diff --git a/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.spec.ts b/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.spec.ts new file mode 100644 index 000000000000..7f28c1a3920a --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.spec.ts @@ -0,0 +1,109 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { CoreWebService } from '@dotcms/dotcms-js'; + +import { DotHttpErrorManagerService } from '@services/dot-http-error-manager/dot-http-error-manager.service'; +import { + DotCategoriesService, + CATEGORY_API_URL, + CATEGORY_CHILDREN_API_URL +} from './dot-categories.service'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { of } from 'rxjs'; +import { + CATEGORY_SOURCE, + DotCategory +} from '@dotcms/app/shared/models/dot-categories/dot-categories.model'; + +const mockCategory: DotCategory = { + categoryId: '1222', + categoryName: 'Test', + key: 'adsdsd', + sortOrder: 1, + deleted: false, + categoryVelocityVarName: 'sdsdsds', + friendlyName: 'asas', + identifier: '1222', + inode: '2121', + name: 'Test', + type: 'ggg', + source: CATEGORY_SOURCE.DB +}; + +describe('DotCategorysService', () => { + let service: DotCategoriesService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DotCategoriesService, + { + provide: DotHttpErrorManagerService, + useValue: { + handle() { + return of({}); + } + } + }, + { + provide: CoreWebService, + useClass: CoreWebServiceMock + } + ], + imports: [HttpClientTestingModule] + }); + service = TestBed.inject(DotCategoriesService); + + httpMock = TestBed.inject(HttpTestingController); + }); + + it('should get a categories list', () => { + service + .getCategories({ + first: 0, + rows: 40, + sortOrder: 1, + filters: {}, + globalFilter: null + }) + .subscribe((categories: DotCategory[]) => { + expect(categories).toEqual([mockCategory]); + }); + + const req = httpMock.expectOne(`${CATEGORY_API_URL}?direction=ASC&per_page=40`); + + expect(req.request.method).toBe('GET'); + + req.flush({ + entity: [mockCategory] + }); + }); + + it('should get a children categories list', () => { + service + .getChildrenCategories({ + first: 0, + rows: 40, + sortOrder: 1, + filters: { + inode: { value: '123' } + }, + globalFilter: null + }) + .subscribe((categories: DotCategory[]) => { + expect(categories).toEqual([mockCategory]); + }); + + const req = httpMock.expectOne( + `${CATEGORY_CHILDREN_API_URL}?direction=ASC&per_page=40&inode=123` + ); + + expect(req.request.method).toBe('GET'); + + req.flush({ + entity: [mockCategory] + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.ts b/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.ts new file mode 100644 index 000000000000..521d27f92855 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@angular/core'; +import { DotCategory } from '@dotcms/app/shared/models/dot-categories/dot-categories.model'; +import { CoreWebService } from '@dotcms/dotcms-js'; +import { LazyLoadEvent } from 'primeng/api'; +import { Observable } from 'rxjs'; +import { OrderDirection, PaginatorService } from '../paginator'; + +export const CATEGORY_API_URL = 'v1/categories'; + +export const CATEGORY_CHILDREN_API_URL = 'v1/categories/children'; + +@Injectable() +export class DotCategoriesService extends PaginatorService { + constructor(coreWebService: CoreWebService) { + super(coreWebService); + this.url = CATEGORY_API_URL; + } + + updatePaginationService(event: LazyLoadEvent) { + const { sortField, sortOrder, filters } = event; + this.setExtraParams('inode', filters?.inode?.value || null); + this.filter = event?.filters?.global?.value || ''; + this.sortField = sortField; + this.sortOrder = sortOrder === 1 ? OrderDirection.ASC : OrderDirection.DESC; + } + + /** + * Get categories according to pagination and search + * @param {LazyLoadEvent} [event] + * @return {*} {Observable} + * @memberof DotCategoriesService + */ + getCategories(event: LazyLoadEvent): Observable { + this.url = CATEGORY_API_URL; + this.updatePaginationService(event); + const page = parseInt(String(event.first / this.paginationPerPage), 10) + 1; + + return this.getPage(page); + } + + /** + * Get children categories according to pagination and search + * @param {LazyLoadEvent} [event] + * @return {*} {Observable} + * @memberof DotCategoriesService + */ + getChildrenCategories(event: LazyLoadEvent): Observable { + this.url = CATEGORY_CHILDREN_API_URL; + this.updatePaginationService(event); + const page = parseInt(String(event.first / this.paginationPerPage), 10) + 1; + + return this.getPage(page); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.spec.ts b/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.spec.ts new file mode 100644 index 000000000000..931c1a78fe23 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.spec.ts @@ -0,0 +1,225 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { CoreWebService } from '@dotcms/dotcms-js'; + +import { DotHttpErrorManagerService } from '@services/dot-http-error-manager/dot-http-error-manager.service'; +import { DotContainersService, CONTAINER_API_URL } from './dot-containers.service'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { DotActionBulkResult } from '@models/dot-action-bulk-result/dot-action-bulk-result.model'; +import { of } from 'rxjs'; +import { + CONTAINER_SOURCE, + DotContainerEntity, + DotContainerPayload +} from '@models/container/dot-container.model'; + +const mockBulkResponseSuccess: DotActionBulkResult = { + skippedCount: 0, + successCount: 1, + fails: [] +}; + +const mockContainer: DotContainerEntity = { + container: { + archived: false, + categoryId: '6e07301c-e6d2-4c1f-9e8e-fcc4a31947d3', + deleted: false, + friendlyName: '', + identifier: '1234', + live: true, + name: 'movie', + parentPermissionable: { + hostname: 'default' + }, + path: null, + source: CONTAINER_SOURCE.DB, + title: 'movie', + type: 'containers', + working: true + }, + contentTypes: [] +}; + +describe('DotContainersService', () => { + let service: DotContainersService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DotContainersService, + { + provide: DotHttpErrorManagerService, + useValue: { + handle() { + return of({}); + } + } + }, + { + provide: CoreWebService, + useClass: CoreWebServiceMock + } + ], + imports: [HttpClientTestingModule] + }); + service = TestBed.inject(DotContainersService); + + httpMock = TestBed.inject(HttpTestingController); + }); + + it('should get a list of containers', () => { + service.get().subscribe((container: DotContainerEntity[]) => { + expect(container).toEqual([mockContainer]); + }); + + const req = httpMock.expectOne(CONTAINER_API_URL); + + expect(req.request.method).toBe('GET'); + + req.flush({ + entity: [mockContainer] + }); + }); + + it('should get a container by id', () => { + service.getById('123').subscribe((containerEntity: DotContainerEntity) => { + expect(containerEntity).toEqual(mockContainer); + }); + + const req = httpMock.expectOne(`${CONTAINER_API_URL}working?containerId=123`); + + expect(req.request.method).toBe('GET'); + + req.flush({ + entity: { + ...mockContainer + } + }); + }); + + it('should get a containers by filter', () => { + service.getFiltered('123').subscribe((container: DotContainerEntity[]) => { + expect(container).toEqual([mockContainer]); + }); + + const req = httpMock.expectOne(`${CONTAINER_API_URL}?filter=123`); + + expect(req.request.method).toBe('GET'); + + req.flush({ + entity: [mockContainer] + }); + }); + + it('should post to create a container', () => { + service + .create({ + title: '', + friendlyName: '' + } as DotContainerPayload) + .subscribe((container: DotContainerEntity) => { + expect(container).toEqual(mockContainer); + }); + + const req = httpMock.expectOne(`${CONTAINER_API_URL}`); + + expect(req.request.method).toBe('POST'); + expect(req.request.body.title).toEqual(''); + expect(req.request.body.friendlyName).toEqual(''); + + req.flush({ + entity: mockContainer + }); + }); + + it('should put to update a container', () => { + service + .update({ + title: '', + friendlyName: '' + } as DotContainerPayload) + .subscribe((container) => { + expect(container).toEqual(mockContainer); + }); + + const req = httpMock.expectOne(CONTAINER_API_URL); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body.title).toEqual(''); + expect(req.request.body.friendlyName).toEqual(''); + + req.flush({ + entity: mockContainer + }); + }); + it('should put to save and publish a container', () => { + service + .saveAndPublish({ + container: { name: '', friendlyName: '' }, + contentTypes: [] + } as DotContainerEntity) + .subscribe((container: DotContainerEntity) => { + expect(container).toEqual(mockContainer); + }); + + const req = httpMock.expectOne(`${CONTAINER_API_URL}_savepublish`); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body.container.name).toEqual(''); + expect(req.request.body.container.friendlyName).toEqual(''); + + req.flush({ + entity: mockContainer + }); + }); + it('should delete a container', () => { + service.delete(['testId01']).subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}_bulkdelete`); + + expect(req.request.method).toBe('DELETE'); + expect(req.request.body).toEqual(['testId01']); + req.flush(mockBulkResponseSuccess); + }); + it('should unArchive a container', () => { + service.unArchive(['testId01']).subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}_bulkunarchive`); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body).toEqual(['testId01']); + req.flush(mockBulkResponseSuccess); + }); + it('should archive a container', () => { + service.archive(['testId01']).subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}_bulkarchive`); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body).toEqual(['testId01']); + req.flush(mockBulkResponseSuccess); + }); + it('should unPublish a container', () => { + service.unPublish(['testId01']).subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}_bulkunpublish`); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body).toEqual(['testId01']); + req.flush(mockBulkResponseSuccess); + }); + it('should publish a container', () => { + const identifier = 'testId01'; + service.publish([identifier]).subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}_bulkpublish`); + + expect(req.request.method).toBe('PUT'); + expect(req.request.body).toEqual([identifier]); + req.flush(mockBulkResponseSuccess); + }); + it('should copy a container', () => { + service.copy('testId01').subscribe(); + const req = httpMock.expectOne(`${CONTAINER_API_URL}testId01/_copy`); + + expect(req.request.method).toBe('PUT'); + req.flush(mockContainer); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.ts b/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.ts new file mode 100644 index 000000000000..e9202f3ee7b9 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/api/services/dot-containers/dot-containers.service.ts @@ -0,0 +1,207 @@ +import { catchError, map, pluck, take } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; + +import { Observable } from 'rxjs'; +import { CoreWebService, DotRequestOptionsArgs } from '@dotcms/dotcms-js'; + +import { DotHttpErrorManagerService } from '@services/dot-http-error-manager/dot-http-error-manager.service'; +import { DotActionBulkResult } from '@models/dot-action-bulk-result/dot-action-bulk-result.model'; +import { + DotContainer, + DotContainerEntity, + DotContainerPayload +} from '@dotcms/app/shared/models/container/dot-container.model'; + +export const CONTAINER_API_URL = '/api/v1/containers/'; + +/** + * Provide util methods to handle containers in the system. + * @export + * @class DotContainersService + */ +@Injectable() +export class DotContainersService { + constructor( + private coreWebService: CoreWebService, + private httpErrorManagerService: DotHttpErrorManagerService + ) {} + + /** + * Return a list of containers. + * @returns Observable + * @memberof DotContainersService + */ + get(): Observable { + return this.request({ url: CONTAINER_API_URL }); + } + + /** + * Get the container, pass the version default working, pass the includeContentType default false + * @param {string} id + * @param {string} version + * @param {boolean} includeContentType + * @returns {Observable} + * @memberof DotContainersService + */ + getById( + id: string, + version = 'working', + includeContentType = false + ): Observable { + const url = `${CONTAINER_API_URL}${version}?containerId=${id}${ + includeContentType ? `&includeContentType=${includeContentType}` : '' + }`; + + return this.request({ + url + }); + } + + /** + * Get the container filtered by tittle or inode . + * + * @param {string} filter + * @returns {Observable} + * @memberof DotContainersService + */ + getFiltered(filter: string): Observable { + const url = `${CONTAINER_API_URL}?filter=${filter}`; + + return this.request({ + url + }); + } + + /** + * Creates a container + * @param {DotContainerRequest} values + * @returns Observable + * @memberof DotContainersService + */ + + create(values: DotContainerPayload): Observable { + return this.request({ + method: 'POST', + url: CONTAINER_API_URL, + body: values + }); + } + + /** + * Updates a container + * + * @param {DotContainerPayload} values + * @returns Observable + * @memberof DotContainersService + */ + update(values: DotContainerPayload): Observable { + return this.request({ + method: 'PUT', + url: CONTAINER_API_URL, + body: values + }); + } + + /** + * Save and Publish a container + * @param {DotContainer} values + * @returns Observable + * @memberof DotContainersService + */ + saveAndPublish(values: DotContainerEntity): Observable { + return this.request({ + method: 'PUT', + url: `${CONTAINER_API_URL}_savepublish`, + body: values + }); + } + + /** + * Delete a container + * @param {string[]} identifiers + * @returns Observable + * @memberof DotContainersService + */ + delete(identifiers: string[]): Observable { + return this.request({ + method: 'DELETE', + url: `${CONTAINER_API_URL}_bulkdelete`, + body: identifiers + }); + } + + /** + * Unarchive a container + * @param {string[]} identifiers + * @returns Observable + * @memberof DotContainersService + */ + unArchive(identifiers: string[]): Observable { + const url = `${CONTAINER_API_URL}_bulkunarchive`; + + return this.request({ method: 'PUT', url, body: identifiers }); + } + + /** + * Archive a container + * @param {string[]} identifiers + * @returns Observable + * @memberof DotContainersService + */ + archive(identifiers: string[]): Observable { + const url = `${CONTAINER_API_URL}_bulkarchive`; + + return this.request({ method: 'PUT', url, body: identifiers }); + } + + /** + * Unpublish a container00 + * @param {string[]} identifiers + * @returns Observable + * @memberof DotContainersService + */ + unPublish(identifiers: string[]): Observable { + const url = `${CONTAINER_API_URL}_bulkunpublish`; + + return this.request({ method: 'PUT', url, body: identifiers }); + } + + /** + * Publish a container + * @param {string[]} identifiers + * @returns Observable + * @memberof DotContainersService + */ + publish(identifiers: string[]): Observable { + const url = `${CONTAINER_API_URL}_bulkpublish`; + + return this.request({ method: 'PUT', url, body: identifiers }); + } + + /** + * Copy a container + * @param {string} identifier + * @returns Observable + * @memberof DotContainersService + */ + copy(identifier: string): Observable { + const url = `${CONTAINER_API_URL}${identifier}/_copy`; + + return this.request({ method: 'PUT', url }); + } + + private request(options: DotRequestOptionsArgs): Observable { + const response$ = this.coreWebService.requestView(options); + + return response$.pipe( + pluck('entity'), + catchError((error: HttpErrorResponse) => { + return this.httpErrorManagerService.handle(error).pipe( + take(1), + map(() => null) + ); + }) + ); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/api/services/dot-router/dot-router.service.ts b/core-web/apps/dotcms-ui/src/app/api/services/dot-router/dot-router.service.ts index 635a2dede5b6..06d45c7846f2 100644 --- a/core-web/apps/dotcms-ui/src/app/api/services/dot-router/dot-router.service.ts +++ b/core-web/apps/dotcms-ui/src/app/api/services/dot-router/dot-router.service.ts @@ -197,6 +197,29 @@ export class DotRouterService { ]); } + /** + * Redirects to the create container page + * @returns {void} + * @memberof DotRouterService + */ + goToCreateContainer(): void { + this.router.navigate(['/containers/create']); + } + + /** + * Redirect to edit the container. + * If the inode is passed, load a specific version of the container + * + * @param {string} id + * @param {string} inode + * @memberof DotRouterService + */ + goToEditContainer(id: string, inode?: string): void { + this.router.navigate([ + inode ? `/containers/edit/${id}/inode/${inode}` : `/containers/edit/${id}` + ]); + } + goToURL(url: string): void { this.router.navigate([url]); } diff --git a/core-web/apps/dotcms-ui/src/app/api/services/paginator/paginator.service.ts b/core-web/apps/dotcms-ui/src/app/api/services/paginator/paginator.service.ts index 949784b1a83b..96f5f9652999 100644 --- a/core-web/apps/dotcms-ui/src/app/api/services/paginator/paginator.service.ts +++ b/core-web/apps/dotcms-ui/src/app/api/services/paginator/paginator.service.ts @@ -260,7 +260,7 @@ export class PaginatorService { } private setLinks(linksString: string): void { - const linkSplit = linksString.split(','); + const linkSplit = linksString?.split(',') || []; linkSplit.forEach((linkRel) => { const linkrealSplit = linkRel.split(';'); diff --git a/core-web/apps/dotcms-ui/src/app/app-routing.module.ts b/core-web/apps/dotcms-ui/src/app/app-routing.module.ts index f6856df22b48..78c3471e6e0c 100644 --- a/core-web/apps/dotcms-ui/src/app/app-routing.module.ts +++ b/core-web/apps/dotcms-ui/src/app/app-routing.module.ts @@ -16,6 +16,20 @@ import { DotIframePortletLegacyResolver } from '@components/_common/iframe/servi import { DotCustomReuseStrategyService } from '@shared/dot-custom-reuse-strategy/dot-custom-reuse-strategy.service'; const PORTLETS_ANGULAR = [ + { + path: 'containers', + loadChildren: () => + import('@dotcms/app/portlets/dot-containers/dot-containers.module').then( + (m) => m.DotContainersModule + ) + }, + { + path: 'categories', + loadChildren: () => + import('@dotcms/app/portlets/dot-categories/dot-categories.module').then( + (m) => m.DotCategoriesModule + ) + }, { path: 'templates', canActivate: [MenuGuardService], diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-apps/dot-apps-list/dot-apps-list.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-apps/dot-apps-list/dot-apps-list.component.html index 10da5ebb49d2..cb258bdecfb3 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/dot-apps/dot-apps-list/dot-apps-list.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-apps/dot-apps-list/dot-apps-list.component.html @@ -3,34 +3,34 @@ >
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit-routing.module.ts new file mode 100644 index 000000000000..2f80edc88f91 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DotCategoriesCreateEditComponent } from './dot-categories-create-edit.component'; + +const routes: Routes = [ + { + path: '', + component: DotCategoriesCreateEditComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DotCategoriesCreateEditRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.html new file mode 100644 index 000000000000..c2d7daeab9f4 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.html @@ -0,0 +1,21 @@ + + + + + + + + + Properties works + + + + + + + + diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.scss new file mode 100644 index 000000000000..aff3f3e88593 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.scss @@ -0,0 +1,17 @@ +@use "variables" as *; + +:host ::ng-deep { + .p-tabview-nav { + padding: 0 $spacing-4; + background: $white; + } + + .p-tabview-panel { + padding: 0 $spacing-4; + padding-top: $spacing-4; + } +} + +dot-portlet-base { + overflow-x: auto; +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.spec.ts new file mode 100644 index 000000000000..4cb34ff01c41 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.spec.ts @@ -0,0 +1,33 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DotCategoriesCreateEditComponent } from './dot-categories-create-edit.component'; + +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { DotCategoriesListingModule } from '../dot-categories-list/dot-categories-list.module'; +import { TabViewModule } from 'primeng/tabview'; + +@Pipe({ name: 'dm' }) +class MockPipe implements PipeTransform { + transform(value: string): string { + return value; + } +} +describe('CategoriesCreateEditComponent', () => { + let component: DotCategoriesCreateEditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DotCategoriesCreateEditComponent, MockPipe], + imports: [DotPortletBaseModule, DotCategoriesListingModule, TabViewModule] + }).compileComponents(); + + fixture = TestBed.createComponent(DotCategoriesCreateEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.ts new file mode 100644 index 000000000000..3597fd0a6f68 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.ts @@ -0,0 +1,23 @@ +import { Component } from '@angular/core'; +import { MenuItem } from 'primeng/api'; +import { DotCategoriesCreateEditStore } from './store/dot-categories-create-edit.store'; +@Component({ + selector: 'dot-categories-create-edit-list', + templateUrl: './dot-categories-create-edit.component.html', + styleUrls: ['./dot-categories-create-edit.component.scss'], + providers: [DotCategoriesCreateEditStore] +}) +export class DotCategoriesCreateEditComponent { + vm$ = this.store.vm$; + constructor(private store: DotCategoriesCreateEditStore) {} + + /** + * The function takes a category object as a parameter, and then calls the updateCategory function + * in the store service, passing the category object as a parameter + * @param {MenuItem} category + * @memberof DotCategoriesCreateEditComponent + */ + updateCategory(category: MenuItem) { + this.store.updateCategory(category); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.module.ts new file mode 100644 index 000000000000..5a74758b5388 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; + +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { DotCategoriesCreateEditRoutingModule } from './dot-categories-create-edit-routing.module'; +import { DotCategoriesCreateEditComponent } from './dot-categories-create-edit.component'; +import { DotCategoriesListModule } from '../dot-categories-list/dot-categories-list.module'; +import { DotCategoriesPermissionsModule } from '../dot-categories-permissions/dot-categories-permissions.module'; +import { TabViewModule } from 'primeng/tabview'; + +@NgModule({ + declarations: [DotCategoriesCreateEditComponent], + exports: [DotCategoriesCreateEditComponent], + imports: [ + CommonModule, + DotMessagePipeModule, + TabViewModule, + DotCategoriesListModule, + DotCategoriesCreateEditRoutingModule, + DotPortletBaseModule, + DotCategoriesPermissionsModule + ] +}) +export class DotCategoriesCreateEditModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/store/dot-categories-create-edit.store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/store/dot-categories-create-edit.store.ts new file mode 100644 index 000000000000..d4b914e0acb5 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/store/dot-categories-create-edit.store.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { ComponentStore } from '@ngrx/component-store'; +import { MenuItem } from 'primeng/api'; + +export interface DotCategoriesCreateEditState { + category: MenuItem; +} + +@Injectable() +export class DotCategoriesCreateEditStore extends ComponentStore { + constructor() { + super({ category: { label: 'Top', id: '', tabindex: '0' } }); + } + + readonly vm$ = this.select(({ category }: DotCategoriesCreateEditState) => { + return { + category + }; + }); + + readonly updateCategory = this.updater( + (state: DotCategoriesCreateEditState, category: MenuItem) => { + return { + ...state, + category + }; + } + ); +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list-routing.module.ts new file mode 100644 index 000000000000..80820755014d --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DotCategoriesListComponent } from './dot-categories-list.component'; + +const routes: Routes = [ + { + path: '', + component: DotCategoriesListComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DotCategoriesListRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.html new file mode 100644 index 000000000000..c0199dc06848 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.html @@ -0,0 +1,188 @@ + +
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + + +
+
+
+ + + + + + + + {{ col.header }} + + + + + + + + + + + + + + + + + + + + + + + {{ category.sortOrder }} + + + + + + + + + + + + {{ category[col.fieldName] }} + + + + + + + +
+ {{ 'No-Results-Found' | dm }} +
+ + + + + + +
+
+
+
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.scss new file mode 100644 index 000000000000..70ddbe966380 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.scss @@ -0,0 +1,24 @@ +@use "variables" as *; + +dot-portlet-base { + overflow-y: scroll; +} + +.category_listing ::ng-deep { + .tableHeader { + padding-top: $spacing-4; + padding-bottom: $spacing-4; + } + + .category_listing__sortOrder__field { + width: 60px; + border: none; + } + + .category_listing-datatable__empty { + display: flex; + justify-content: center; + font-size: $spacing-9; + font-size: $font-size-xx-large; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.spec.ts new file mode 100644 index 000000000000..5a08fa5d2670 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.spec.ts @@ -0,0 +1,167 @@ +/* eslint-disable no-console */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { By } from '@angular/platform-browser'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { TableModule } from 'primeng/table'; +import { Component, DebugElement } from '@angular/core'; +import { DotCategoriesListComponent } from './dot-categories-list.component'; +import { DotMenuModule } from '../../../view/components/_common/dot-menu/dot-menu.module'; +import { SharedModule } from 'primeng/api'; +import { MenuModule } from 'primeng/menu'; +import { CoreWebService } from '@dotcms/dotcms-js'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { DotPipesModule } from '@pipes/dot-pipes.module'; +import { BreadcrumbModule } from 'primeng/breadcrumb'; +import { InplaceModule } from 'primeng/inplace'; +import { DotCategoriesService } from '@dotcms/app/api/services/dot-categories/dot-categories.service'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { CheckboxModule } from 'primeng/checkbox'; +import { DotMessagePipeModule } from '@dotcms/app/view/pipes/dot-message/dot-message-pipe.module'; +import { DotActionMenuButtonModule } from '@components/_common/dot-action-menu-button/dot-action-menu-button.module'; +import { PaginatorModule } from 'primeng/paginator'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { of } from 'rxjs'; +import { DotCategory } from '@dotcms/app/shared/models/dot-categories/dot-categories.model'; +import { MockDotMessageService } from '@dotcms/app/test/dot-message-service.mock'; +import { DotMessageService } from '@dotcms/app/api/services/dot-message/dot-messages.service'; +import { DotEmptyStateModule } from '@components/_common/dot-empty-state/dot-empty-state.module'; + +@Component({ + selector: 'dot-test-host-component', + template: `` +}) +class TestHostComponent {} + +describe('DotCategoriesListingTableComponent', () => { + let hostFixture: ComponentFixture; + let de: DebugElement; + let el: HTMLElement; + let coreWebService: CoreWebService; + const items: DotCategory[] = [ + { + categoryId: '9e882f2a-ada2-47e3-a441-bdf9a7254216', + categoryName: 'Age or Gender', + categoryVelocityVarName: 'ageOrGender', + identifier: '1212', + inode: '9e882f2a-ada2-47e3-a441-bdf9a7254216', + key: 'ageOrGender', + live: false, + sortOrder: 1, + working: false, + name: 'dsdsd', + friendlyName: 'dfdf', + type: 'ASD' + }, + { + categoryId: '9e882f2a-ada2-47e3-a441-bdf9a7254216', + categoryName: 'Age or Gender', + categoryVelocityVarName: 'ageOrGender', + identifier: '1212', + inode: '9e882f2a-ada2-47e3-a441-bdf9a7254216', + key: 'ageOrGender', + live: false, + sortOrder: 1, + working: false, + name: 'dsdsd', + friendlyName: 'dfdf', + type: 'ASD' + } + ]; + beforeEach(() => { + const messageServiceMock = new MockDotMessageService({ + 'message.category.search': 'Type to Filter', + 'No-Results-Found': 'No Results Found', + 'message.category.empty.title': 'Your category list is empty', + 'message.category.empty.content': + 'You have not added anything yet, start by clicking the button below', + 'message.category.empty.button.label': 'Add New Category' + }); + TestBed.configureTestingModule({ + declarations: [DotCategoriesListComponent, TestHostComponent], + imports: [ + SharedModule, + MenuModule, + DotMenuModule, + HttpClientTestingModule, + DotPipesModule, + BreadcrumbModule, + DotPortletBaseModule, + ButtonModule, + InputTextModule, + TableModule, + PaginatorModule, + InplaceModule, + InputNumberModule, + DotActionMenuButtonModule, + DotMessagePipeModule, + CheckboxModule, + DotEmptyStateModule + ], + providers: [ + { provide: CoreWebService, useClass: CoreWebServiceMock }, + { provide: DotMessageService, useValue: messageServiceMock }, + DotCategoriesService + ] + }); + + hostFixture = TestBed.createComponent(TestHostComponent); + coreWebService = TestBed.inject(CoreWebService); + }); + + it('should have default attributes', fakeAsync(() => { + hostFixture.detectChanges(); + tick(); + hostFixture.detectChanges(); + de = hostFixture.debugElement.query(By.css('p-table')); + expect(de.componentInstance.lazy).toBe(true); + expect(de.componentInstance.paginator).toBe(true); + expect(de.componentInstance.reorderableColumns).toBe(true); + expect(de.componentInstance.rows).toBe(40); + })); + + it('renderer basic datatable component', fakeAsync(() => { + setRequestSpy(items); + + hostFixture.detectChanges(); + tick(); + hostFixture.detectChanges(); + de = hostFixture.debugElement.query(By.css('p-table')); + el = de.nativeElement; + const rows = el.querySelectorAll('[data-testId="testTableRow"]'); + expect(items.length).toEqual(rows.length); + + const headRow = el.querySelector('[data-testId="testHeadTableRow"]'); + const headers = headRow.querySelectorAll('th'); + expect(8).toEqual(headers.length); + + headers.forEach((_col, index) => { + const sortableIcon = headers[index].querySelector('p-sortIcon'); + index === 0 || index === headers.length - 1 + ? expect(sortableIcon).toBeNull() + : expect(sortableIcon).toBeDefined(); + }); + })); + + it('should renders the dot empty state component if items array is empty', fakeAsync(() => { + setRequestSpy([]); + hostFixture.detectChanges(); + tick(); + hostFixture.detectChanges(); + de = hostFixture.debugElement.query(By.css('p-table')); + const emptyState = de.query(By.css('[data-testid="title"]')); + expect(emptyState.nativeElement.innerText).toBe('Your category list is empty'); + })); + + function setRequestSpy(response: any): void { + spyOn(coreWebService, 'requestView').and.returnValue( + of({ + entity: response, + header: (type) => (type === 'Link' ? 'test;test=test' : '40') + }) + ); + } +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.ts new file mode 100644 index 000000000000..73cbd987c138 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.ts @@ -0,0 +1,85 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { DotCategory } from '@dotcms/app/shared/models/dot-categories/dot-categories.model'; +import { LazyLoadEvent } from 'primeng/api'; +import { Table } from 'primeng/table'; +import { Observable } from 'rxjs'; +import { DotCategoriesListStore, DotCategoriesListState } from './store/dot-categories-list-store'; + +@Component({ + selector: 'dot-categories-list', + templateUrl: './dot-categories-list.component.html', + styleUrls: ['./dot-categories-list.component.scss'], + providers: [DotCategoriesListStore] +}) +export class DotCategoriesListComponent { + vm$: Observable = this.store.vm$; + selectedCategories: DotCategory[] = []; + breadCrumbHome = { icon: 'pi pi-home' }; + isContentFiltered = false; + @ViewChild('dataTable') + dataTable: Table; + @ViewChild('gf') + globalSearch: ElementRef; + constructor(private store: DotCategoriesListStore) {} + + /** + * Add category in breadcrumb + * @param {DotCategory} category + * @memberof DotCategoriesListComponent + */ + addBreadCrumb(category: DotCategory) { + this.dataTable.filter(category.inode, 'inode', null); + this.dataTable.filter(null, 'global', null); + this.store.addCategoriesBreadCrumb({ label: category.categoryName, id: category.inode }); + } + + /**5 + * Update categories breadcrumb in store + * @param {*} event + * @memberof DotCategoriesListComponent + */ + updateBreadCrumb(event) { + const { item } = event; + this.store.updateCategoriesBreadCrumb(item); + // for getting child categories need to pass category ID + this.dataTable.filter(item.id || null, 'inode', null); + this.dataTable.filter(null, 'global', null); + } + + /** + * Update selected categories in store + * @memberof DotCategoriesListComponent + */ + handleRowCheck(): void { + this.store.updateSelectedCategories(this.selectedCategories); + } + + /** + * Check if display results are filtered. + * @memberof DotCategoriesListComponent + */ + handleFilter(): void { + this.isContentFiltered = Object.prototype.hasOwnProperty.call( + this.dataTable.filters, + 'global' + ); + } + + /** + * get records according to pagination + * @param {LazyLoadEvent} event + * @memberof DotCategoriesListComponent + */ + loadCategories(event: LazyLoadEvent) { + if (event?.filters?.inode) { + this.store.getChildrenCategories(event); + } else { + this.store.getCategories(event); + } + + // for reset search field + if (!event.globalFilter && this.globalSearch) { + this.globalSearch.nativeElement.value = ''; + } + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.module.ts new file mode 100644 index 000000000000..10043464b219 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DotCategoriesListRoutingModule } from './dot-categories-list-routing.module'; +import { DotCategoriesListComponent } from './dot-categories-list.component'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { MenuModule } from 'primeng/menu'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; +import { TableModule } from 'primeng/table'; +import { PaginatorModule } from 'primeng/paginator'; +import { DotActionMenuButtonModule } from '@components/_common/dot-action-menu-button/dot-action-menu-button.module'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { InplaceModule } from 'primeng/inplace'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { BreadcrumbModule } from 'primeng/breadcrumb'; +import { CheckboxModule } from 'primeng/checkbox'; +import { DotCategoriesService } from '@dotcms/app/api/services/dot-categories/dot-categories.service'; +import { DotEmptyStateModule } from '@components/_common/dot-empty-state/dot-empty-state.module'; + +@NgModule({ + declarations: [DotCategoriesListComponent], + imports: [ + CommonModule, + DotCategoriesListRoutingModule, + DotPortletBaseModule, + MenuModule, + ButtonModule, + InputTextModule, + TableModule, + PaginatorModule, + InplaceModule, + InputNumberModule, + DotActionMenuButtonModule, + DotMessagePipeModule, + CheckboxModule, + BreadcrumbModule, + DotEmptyStateModule + ], + providers: [DotCategoriesService] +}) +export class DotCategoriesListingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.spec.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.ts new file mode 100644 index 000000000000..a18083ee0aaa --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.ts @@ -0,0 +1,272 @@ +import { Injectable } from '@angular/core'; +import { ComponentStore } from '@ngrx/component-store'; +import { LazyLoadEvent, MenuItem } from 'primeng/api'; +import { DataTableColumn } from '@models/data-table'; +import { DotMessageService } from '@services/dot-message/dot-messages.service'; +import { DotCategory } from '@dotcms/app/shared/models/dot-categories/dot-categories.model'; +import { map, take } from 'rxjs/operators'; +import { DotActionMenuItem } from '@dotcms/app/shared/models/dot-action-menu/dot-action-menu-item.model'; +import { DotCategoriesService } from '@dotcms/app/api/services/dot-categories/dot-categories.service'; +import { Observable } from 'rxjs'; +import { OrderDirection } from '@dotcms/app/api/services/paginator'; + +export interface DotCategoriesListState { + categoriesBulkActions: MenuItem[]; + categoriesActions: DotActionMenuItem[]; + selectedCategories: DotCategory[]; + tableColumns: DataTableColumn[]; + categories: DotCategory[]; + categoryBreadCrumbs: MenuItem[]; + paginationPerPage: number; + currentPage: number; + totalRecords: number; + sortField: string; + sortOrder: OrderDirection; +} + +@Injectable() +export class DotCategoriesListStore extends ComponentStore { + constructor( + private dotMessageService: DotMessageService, + private categoryService: DotCategoriesService + ) { + super(); + this.setState({ + categoriesBulkActions: this.getCategoriesBulkActions(), + categoriesActions: this.getCategoriesActions(), + tableColumns: this.getCategoriesColumns(), + selectedCategories: [], + categories: [], + categoryBreadCrumbs: [], + currentPage: this.categoryService.currentPage, + paginationPerPage: this.categoryService.paginationPerPage, + totalRecords: this.categoryService.totalRecords, + sortField: null, + sortOrder: OrderDirection.ASC + }); + } + readonly vm$ = this.select((state: DotCategoriesListState) => state); + + /** + * Get categories breadcrumbs + * @memberof DotCategoriesListStore + */ + readonly categoryBreadCrumbSselector$ = this.select( + ({ categoryBreadCrumbs }: DotCategoriesListState) => { + return { + categoryBreadCrumbs + }; + } + ); + + /** + * A function that updates the state of the store. + * @param state DotCategoryListState + * @param selectedCategories DotCategory + * @memberof DotCategoriesListStore + */ + readonly updateSelectedCategories = this.updater( + (state: DotCategoriesListState, selectedCategories: DotCategory[]) => { + return { + ...state, + selectedCategories + }; + } + ); + + /** A function that updates the state of the store. + * @param state DotCategoryListState + * @param categories DotCategory[] + * @memberof DotCategoriesListStore + */ + readonly setCategories = this.updater( + (state: DotCategoriesListState, categories: DotCategory[]) => { + return { + ...state, + paginationPerPage: this.categoryService.paginationPerPage, + currentPage: this.categoryService.currentPage, + totalRecords: this.categoryService.totalRecords, + sortField: this.categoryService.sortField, + sortOrder: this.categoryService.sortOrder, + categories + }; + } + ); + + /** + * Add cateogry in breadcrumb + * @memberof DotCategoriesListStore + */ + readonly addCategoriesBreadCrumb = this.updater( + (state: DotCategoriesListState, categoryBreadCrumb: MenuItem) => { + return { + ...state, + categoryBreadCrumbs: [ + ...state.categoryBreadCrumbs, + { ...categoryBreadCrumb, tabindex: state.categoryBreadCrumbs.length.toString() } + ] + }; + } + ); + /** + * Update categories in store + * @memberof DotCategoriesListStore + */ + readonly updateCategoriesBreadCrumb = this.updater( + (state: DotCategoriesListState, categoryBreadCrumb: MenuItem) => { + let { categoryBreadCrumbs } = this.get(); + categoryBreadCrumbs = categoryBreadCrumbs.filter( + ({ tabindex }: MenuItem) => Number(tabindex) <= Number(categoryBreadCrumb.tabindex) + ); + + return { + ...state, + categoryBreadCrumbs: categoryBreadCrumbs + }; + } + ); + + // EFFECTS + + /** + * > This function returns an observable of an array of DotCategory objects + * @returns Observable + * @memberof DotCategoriesListStore + */ + + readonly getCategories = this.effect((filters: Observable) => { + return filters.pipe( + map((filters: LazyLoadEvent) => { + this.categoryService + .getCategories(filters) + .pipe(take(1)) + .subscribe((items: DotCategory[]) => { + this.setCategories(items); + }); + }) + ); + }); + + /** + * > This function returns an observable of an array of DotCategory objects + * @returns Observable + * @memberof DotCategoriesListStore + */ + + readonly getChildrenCategories = this.effect((filters: Observable) => { + return filters.pipe( + map((filters: LazyLoadEvent) => { + this.categoryService + .getChildrenCategories(filters) + .pipe(take(1)) + .subscribe((items: DotCategory[]) => { + this.setCategories(items); + }); + }) + ); + }); + + /** + * It returns an array of objects with a label property + * @returns An array of objects with a label property. + */ + private getCategoriesBulkActions(): { label: string }[] { + return [ + { + label: this.dotMessageService.get('Add-To-Bundle') + }, + { + label: this.dotMessageService.get('Delete') + } + ]; + } + + private getCategoriesActions(): DotActionMenuItem[] { + return [ + { + menuItem: { + label: this.dotMessageService.get('Edit') + } + }, + { + menuItem: { + label: this.dotMessageService.get('View Children'), + command: (event) => { + this.getChildrenCategories({ + sortOrder: 1, + filters: { + inode: { + value: event.inode, + matchMode: null + } + } + }); + this.addCategoriesBreadCrumb({ + label: event.categoryName, + id: event.inode + }); + } + } + }, + { + menuItem: { + label: this.dotMessageService.get('Permissions') + } + }, + { + menuItem: { + label: this.dotMessageService.get('Add-To-Bundle') + } + }, + { + menuItem: { + label: this.dotMessageService.get('Delete') + } + } + ]; + } + + /** + * It returns an array of objects that describe the columns of the table + * @returns An array of DataTableColumn objects. + */ + private getCategoriesColumns(): DataTableColumn[] { + return [ + { + fieldName: 'categoryName', + header: this.dotMessageService.get('message.category.fieldName.Name'), + width: '30%', + sortable: true + }, + { + fieldName: 'key', + header: this.dotMessageService.get('message.category.fieldName.Key'), + width: '20%', + sortable: true + }, + { + fieldName: 'categoryVelocityVarName', + header: this.dotMessageService.get('message.category.fieldName.Variable'), + width: '20%', + sortable: true + }, + { + fieldName: 'childrens', + header: this.dotMessageService.get('message.category.fieldName.Childrens'), + width: '15%', + sortable: true + }, + { + fieldName: 'sortOrder', + width: '10%', + header: this.dotMessageService.get('message.category.fieldName.Order'), + sortable: true + }, + { + fieldName: 'Actions', + width: '5%', + header: '' + } + ]; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.html new file mode 100644 index 000000000000..d20d89547c2a --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.html @@ -0,0 +1,3 @@ + + + diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.spec.ts new file mode 100644 index 000000000000..8a9569d76121 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.spec.ts @@ -0,0 +1,55 @@ +import { Component, DebugElement, ElementRef, Input, SimpleChange, ViewChild } from '@angular/core'; +import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DotCategoriesPermissionsComponent } from './dot-categories-permissions.component'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; + +@Component({ + selector: 'dot-iframe', + template: '' +}) +export class IframeMockComponent { + @Input() src: string; + @ViewChild('iframeElement') iframeElement: ElementRef; +} + +describe('CategoriesPermissionsComponent', () => { + let component: DotCategoriesPermissionsComponent; + let fixture: ComponentFixture; + let de: DebugElement; + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DotCategoriesPermissionsComponent, IframeMockComponent], + imports: [DotPortletBaseModule], + providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }] + }).compileComponents(); + + fixture = TestBed.createComponent(DotCategoriesPermissionsComponent); + component = fixture.componentInstance; + de = fixture.debugElement; + component.categoryId = '123'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('permissions', () => { + beforeEach(() => { + component.categoryId = '123'; + component.ngOnChanges({ + categoryId: new SimpleChange(null, component.categoryId, true) + }); + fixture.detectChanges(); + de = fixture.debugElement; + }); + + it('should set iframe permissions url', () => { + const permissions = de.query(By.css('[data-testId="permissionsIframe"]')); + expect(permissions.componentInstance.src).toBe( + '/html/categories/permissions.jsp?categoryId=123' + ); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.ts new file mode 100644 index 000000000000..aa53e91b8673 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.ts @@ -0,0 +1,14 @@ +import { OnChanges, Component, Input, SimpleChanges } from '@angular/core'; +@Component({ + selector: 'dot-categories-permissions', + templateUrl: './dot-categories-permissions.component.html', + styleUrls: ['./dot-categories-permissions.component.scss'] +}) +export class DotCategoriesPermissionsComponent implements OnChanges { + @Input() categoryId: string; + permissionsUrl = ''; + + ngOnChanges(changes: SimpleChanges) { + this.permissionsUrl = `/html/categories/permissions.jsp?categoryId=${changes.categoryId.currentValue}`; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.module.ts new file mode 100644 index 000000000000..267fb549bace --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DotCategoriesPermissionsComponent } from './dot-categories-permissions.component'; +import { IFrameModule } from '@components/_common/iframe'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; + +@NgModule({ + declarations: [DotCategoriesPermissionsComponent], + exports: [DotCategoriesPermissionsComponent], + imports: [CommonModule, DotPortletBaseModule, IFrameModule] +}) +export class DotCategoriesPermissionsModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-routing.module.ts new file mode 100644 index 000000000000..8f3737276eb1 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-routing.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +const routes: Routes = [ + { + path: '', + loadChildren: () => + import('./dot-categories-list/dot-categories-list.module').then( + (m) => m.DotCategoriesListingModule + ) + } +]; + +@NgModule({ + declarations: [], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DotCategoriesRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories.module.ts new file mode 100644 index 000000000000..46594992c98f --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories.module.ts @@ -0,0 +1,9 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DotCategoriesRoutingModule } from './dot-categories-routing.module'; + +@NgModule({ + imports: [CommonModule, DotCategoriesRoutingModule] +}) +export class DotCategoriesModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list-routing.module.ts new file mode 100644 index 000000000000..6d20cfdc9f35 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ContainerListComponent } from './container-list.component'; +import { DotContainerListResolver } from '@portlets/dot-containers/container-list/dot-container-list-resolver.service'; + +const routes: Routes = [ + { + path: '', + component: ContainerListComponent, + resolve: { + dotContainerListResolverData: DotContainerListResolver + } + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ContainerListRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.html new file mode 100644 index 000000000000..ee8d7283a75d --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.html @@ -0,0 +1,94 @@ +
+ + + + +
+
+ + +
+ +
+ + + +
+
+ + + + + + + + {{ rowData.name }} + + + + + + + {{ rowData.friendlyName }} + + + {{ rowData.modDate }} + + + + + + +
+ + +
+
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.scss new file mode 100644 index 000000000000..63cbab4ae2f9 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.scss @@ -0,0 +1,23 @@ +@use "variables" as *; + +.container-listing__header-options { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 $spacing-3; + + .container-listing__filter-controls { + display: flex; + gap: $spacing-3; + } +} + +.container-listing ::ng-deep { + overflow-y: auto; + height: 100%; +} + +dot-base-type-selector { + margin-right: $spacing-3; +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.spec.ts new file mode 100644 index 000000000000..c2dd51de7727 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.spec.ts @@ -0,0 +1,236 @@ +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; + +import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog'; +import { of } from 'rxjs'; +import { ContainerListComponent } from './container-list.component'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; +import { DotListingDataTableComponent } from '@components/dot-listing-data-table/dot-listing-data-table.component'; +import { DotPushPublishDialogService } from '@dotcms/dotcms-js'; +import { DotSiteBrowserService } from '@services/dot-site-browser/dot-site-browser.service'; +import { DotAlertConfirmService } from '@services/dot-alert-confirm'; +import { CoreWebService } from '@dotcms/dotcms-js'; +import { MockDotMessageService } from '@tests/dot-message-service.mock'; +import { DotMessageDisplayService } from '@components/dot-message-display/services'; +import { DotMessageService } from '@services/dot-message/dot-messages.service'; +import { ActivatedRoute } from '@angular/router'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { DotEventsSocketURL } from '@dotcms/dotcms-js'; +import { dotEventSocketURLFactory } from '@tests/dot-test-bed'; +import { StringUtils } from '@dotcms/dotcms-js'; +import { DotHttpErrorManagerService } from '@services/dot-http-error-manager/dot-http-error-manager.service'; +import { ConfirmationService, SharedModule } from 'primeng/api'; +import { LoginService } from '@dotcms/dotcms-js'; +import { DotcmsEventsService } from '@dotcms/dotcms-js'; +import { DotEventsSocket } from '@dotcms/dotcms-js'; +import { DotcmsConfigService } from '@dotcms/dotcms-js'; +import { DotFormatDateService } from '@services/dot-format-date-service'; +import { DotFormatDateServiceMock } from '@tests/format-date-service.mock'; +import { DotListingDataTableModule } from '@components/dot-listing-data-table'; +import { CommonModule } from '@angular/common'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { CheckboxModule } from 'primeng/checkbox'; +import { MenuModule } from 'primeng/menu'; +import { ButtonModule } from 'primeng/button'; +import { DotActionButtonModule } from '@components/_common/dot-action-button/dot-action-button.module'; +import { DotActionMenuButtonModule } from '@components/_common/dot-action-menu-button/dot-action-menu-button.module'; +import { DotAddToBundleModule } from '@components/_common/dot-add-to-bundle'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { CONTAINER_SOURCE, DotContainer } from '@models/container/dot-container.model'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; + +const containersMock: DotContainer[] = [ + { + archived: false, + categoryId: '6e07301c-e6d2-4c1f-9e8e-fcc4a31947d3', + deleted: false, + friendlyName: '', + identifier: 'f17f87c0e571060732923ec92d071b73', + live: true, + name: 'movie', + parentPermissionable: { + hostname: 'default' + }, + path: null, + source: CONTAINER_SOURCE.DB, + title: 'movie', + type: 'containers', + working: true + }, + { + archived: false, + categoryId: 'a443d26e-0e92-4a9e-a2ab-90a44fd1eb8d', + deleted: false, + friendlyName: '', + identifier: '282685c94eb370a7820766d6aa1d0136', + live: true, + name: 'test', + parentPermissionable: { + hostname: 'default' + }, + path: null, + source: CONTAINER_SOURCE.DB, + title: 'test', + type: 'containers', + working: true + } +]; + +const columnsMock = [ + { + fieldName: 'title', + header: 'Name', + sortable: true + }, + { + fieldName: 'status', + header: 'Status', + width: '8%' + }, + { + fieldName: 'friendlyName', + header: 'Description' + }, + { + fieldName: 'modDate', + format: 'date', + header: 'Last Edit', + sortable: true + } +]; + +const messages = { + 'Add-To-Bundle': 'Add To Bundle', + 'Remote-Publish': 'Push Publish', + 'code-container': 'Advanced Container', + 'contenttypes.content.push_publish': 'Push Publish', + 'design-container': 'Container Designer', + 'message.container.confirm.delete.container': + 'Are you sure you want to delete this Container? (This operation cannot be undone)', + 'message.container.copy': 'Container copied', + 'message.container.delete': 'Container archived', + 'message.container.full_delete': 'Container deleted', + 'message.container.undelete': 'Container unarchived', + 'message.container.unpublished': 'Container unpublished', + 'message.container_list.published': 'Containers published', + 'message.containers.fieldName.description': 'Description', + 'message.containers.fieldName.lastEdit': 'Last Edit', + 'message.containers.fieldName.name': 'Name', + 'message.containers.fieldName.status': 'Status', + 'Delete-Container': 'Delete Container', + Archive: 'Archive', + Archived: 'Archived', + Copy: 'Copy', + Delete: 'Delete', + Draft: 'Draft', + Publish: 'Publish', + Published: 'Published', + Results: 'Results', + Revision: 'Revision', + Unarchive: 'Unarchive', + Unpublish: 'Unpublish', + edit: 'Edit', + publish: 'Publish' +}; + +const routeDataMock = { + dotContainerListResolverData: [true, true] +}; + +class ActivatedRouteMock { + get data() { + return of(routeDataMock); + } +} + +describe('ContainerListComponent', () => { + let fixture: ComponentFixture; + let dotListingDataTable: DotListingDataTableComponent; + let dotPushPublishDialogService: DotPushPublishDialogService; + let coreWebService: CoreWebService; + + const messageServiceMock = new MockDotMessageService(messages); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ContainerListComponent], + providers: [ + { provide: DotMessageService, useValue: messageServiceMock }, + { + provide: ActivatedRoute, + useClass: ActivatedRouteMock + }, + { provide: CoreWebService, useClass: CoreWebServiceMock }, + { provide: DotEventsSocketURL, useFactory: dotEventSocketURLFactory }, + { + provide: DotRouterService, + useValue: { + gotoPortlet: jasmine.createSpy(), + goToEditContainer: jasmine.createSpy(), + goToSiteBrowser: jasmine.createSpy() + } + }, + StringUtils, + DotHttpErrorManagerService, + DotAlertConfirmService, + ConfirmationService, + LoginService, + DotcmsEventsService, + DotEventsSocket, + DotcmsConfigService, + DotMessageDisplayService, + DialogService, + DotSiteBrowserService, + DotContainersService, + { provide: DotFormatDateService, useClass: DotFormatDateServiceMock } + ], + imports: [ + DotListingDataTableModule, + CommonModule, + DotMessagePipeModule, + SharedModule, + CheckboxModule, + MenuModule, + ButtonModule, + DotActionButtonModule, + DotActionMenuButtonModule, + DotAddToBundleModule, + HttpClientTestingModule, + DynamicDialogModule, + BrowserAnimationsModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents(); + fixture = TestBed.createComponent(ContainerListComponent); + dotPushPublishDialogService = TestBed.inject(DotPushPublishDialogService); + coreWebService = TestBed.inject(CoreWebService); + }); + + describe('with data', () => { + beforeEach(fakeAsync(() => { + spyOn(coreWebService, 'requestView').and.returnValue( + of({ + entity: containersMock, + header: (type) => (type === 'Link' ? 'test;test=test' : '10') + }) + ); + fixture.detectChanges(); + tick(2); + fixture.detectChanges(); + dotListingDataTable = fixture.debugElement.query( + By.css('dot-listing-data-table') + ).componentInstance; + spyOn(dotPushPublishDialogService, 'open'); + })); + + it('should set attributes of dotListingDataTable', () => { + expect(dotListingDataTable.columns).toEqual(columnsMock); + expect(dotListingDataTable.url).toEqual('v1/containers?system=true'); + expect(dotListingDataTable.actions).toEqual([]); + expect(dotListingDataTable.checkbox).toEqual(true); + expect(dotListingDataTable.dataKey).toEqual('inode'); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.ts new file mode 100644 index 000000000000..d8ad842eadec --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.component.ts @@ -0,0 +1,150 @@ +import { Component, OnDestroy, ViewChild } from '@angular/core'; +import { DotListingDataTableComponent } from '@components/dot-listing-data-table/dot-listing-data-table.component'; +import { DotContainer } from '@models/container/dot-container.model'; +import { DotContentState } from '@dotcms/dotcms-models'; +import { DotContainerListStore } from '@portlets/dot-containers/container-list/store/dot-container-list.store'; +import { DotActionMenuItem } from '@models/dot-action-menu/dot-action-menu-item.model'; +import { + DotActionBulkResult, + DotBulkFailItem +} from '@models/dot-action-bulk-result/dot-action-bulk-result.model'; +import { DotMessageSeverity, DotMessageType } from '@components/dot-message-display/model'; +import { DotBulkInformationComponent } from '@components/_common/dot-bulk-information/dot-bulk-information.component'; +import { DotMessageService } from '@services/dot-message/dot-messages.service'; +import { DotMessageDisplayService } from '@components/dot-message-display/services'; +import { DialogService } from 'primeng/dynamicdialog'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; + +@Component({ + selector: 'dot-container-list', + templateUrl: './container-list.component.html', + styleUrls: ['./container-list.component.scss'], + providers: [DotContainerListStore] +}) +export class ContainerListComponent implements OnDestroy { + vm$ = this.store.vm$; + notify$ = this.store.notify$; + + @ViewChild('listing', { static: false }) + listing: DotListingDataTableComponent; + + private destroy$: Subject = new Subject(); + + constructor( + private store: DotContainerListStore, + private dotMessageService: DotMessageService, + private dotMessageDisplayService: DotMessageDisplayService, + private dialogService: DialogService, + private dotRouterService: DotRouterService + ) { + this.notify$.pipe(takeUntil(this.destroy$)).subscribe(({ payload, message, failsInfo }) => { + this.notifyResult(payload, failsInfo, message); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } + + /** + * Get the attributes that define the state of a container. + * @param {DotContainer} { live, working, deleted, hasLiveVersion} + * @returns DotContentState + * @memberof ContainerListComponent + */ + getContainerState({ live, working, deleted }: DotContainer): DotContentState { + return { live, working, deleted, hasLiveVersion: live }; + } + + /** + * Handle filter for hide / show archive containers + * @param {boolean} checked + * + * @memberof ContainerListComponent + */ + handleArchivedFilter(checked: boolean): void { + checked + ? this.listing.paginatorService.setExtraParams('archive', checked) + : this.listing.paginatorService.deleteExtraParams('archive'); + this.listing.loadFirstPage(); + } + + /** + * Keep updated the selected containers in the grid + * @param {DotContainer[]} containers + * + * @memberof ContainerListComponent + */ + updateSelectedContainers(containers: DotContainer[]): void { + this.store.updateSelectedContainers(containers); + } + + /** + * Reset bundle state to null + * + * @memberof ContainerListComponent + */ + resetBundleIdentifier(): void { + this.store.updateBundleIdentifier(null); + } + + setContainerActions(container: DotContainer): DotActionMenuItem[] { + return this.store.getContainerActions(container); + } + + /** + * Return a list of containers with disableInteraction in system items. + * @param {DotContainer[]} containers + * @returns DotContainer[] + * @memberof DotContainerListComponent + */ + getContainersWithDisabledEntities(containers: DotContainer[]): DotContainer[] { + return containers.map((container) => { + container.disableInteraction = + container.identifier.includes('/') || container.identifier === 'SYSTEM_CONTAINER'; + + return container; + }); + } + + private notifyResult( + response: DotActionBulkResult | DotContainer, + failsInfo: DotBulkFailItem[], + message: string + ): void { + if ('fails' in response && failsInfo?.length) { + this.showErrorDialog({ + ...response, + fails: failsInfo, + action: message + }); + } else if (message) { + this.showToastNotification(message); + } + + this.listing?.clearSelection(); + this.listing?.loadCurrentPage(); + } + + private showToastNotification(message: string): void { + this.dotMessageDisplayService.push({ + life: 3000, + message: message, + severity: DotMessageSeverity.SUCCESS, + type: DotMessageType.SIMPLE_MESSAGE + }); + } + + private showErrorDialog(result: DotActionBulkResult): void { + this.dialogService.open(DotBulkInformationComponent, { + header: this.dotMessageService.get('Results'), + width: '40rem', + contentStyle: { 'max-height': '500px', overflow: 'auto' }, + baseZIndex: 10000, + data: result + }); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.module.ts new file mode 100644 index 000000000000..3741fcd0d60b --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/container-list.module.ts @@ -0,0 +1,44 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ContainerListRoutingModule } from './container-list-routing.module'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { ContainerListComponent } from './container-list.component'; +import { DotListingDataTableModule } from '@components/dot-listing-data-table'; +import { DotBaseTypeSelectorModule } from '@components/dot-base-type-selector'; +import { ButtonModule } from 'primeng/button'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { CheckboxModule } from 'primeng/checkbox'; +import { MenuModule } from 'primeng/menu'; +import { DotEmptyStateModule } from '@components/_common/dot-empty-state/dot-empty-state.module'; +import { DotContainerListResolver } from '@portlets/dot-containers/container-list/dot-container-list-resolver.service'; +import { DotAddToBundleModule } from '@components/_common/dot-add-to-bundle'; +import { DotSiteBrowserService } from '@services/dot-site-browser/dot-site-browser.service'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; +import { DialogService } from 'primeng/dynamicdialog'; +import { DotActionMenuButtonModule } from '@components/_common/dot-action-menu-button/dot-action-menu-button.module'; + +@NgModule({ + declarations: [ContainerListComponent], + imports: [ + CommonModule, + ContainerListRoutingModule, + DotPortletBaseModule, + DotListingDataTableModule, + DotBaseTypeSelectorModule, + DotMessagePipeModule, + ButtonModule, + CheckboxModule, + MenuModule, + DotEmptyStateModule, + DotAddToBundleModule, + DotActionMenuButtonModule + ], + providers: [ + DotContainerListResolver, + DotSiteBrowserService, + DotContainersService, + DialogService + ] +}) +export class ContainerListModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.spec.ts new file mode 100644 index 000000000000..a1a016559e7b --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.spec.ts @@ -0,0 +1,75 @@ +import { TestBed } from '@angular/core/testing'; +import { DotContainerListResolver } from './dot-container-list-resolver.service'; +import { + ApiRoot, + CoreWebService, + DotcmsConfigService, + LoggerService, + StringUtils, + UserModel +} from '@dotcms/dotcms-js'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { DotLicenseService } from '@services/dot-license/dot-license.service'; +import { PushPublishService } from '@services/push-publish/push-publish.service'; +import { take } from 'rxjs/operators'; +import { DotCurrentUserService } from '@services/dot-current-user/dot-current-user.service'; +import { DotFormatDateService } from '@services/dot-format-date-service'; + +describe('DotContainerListResolverService', () => { + let service: DotContainerListResolver; + let pushPublishService: PushPublishService; + let dotLicenseService: DotLicenseService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DotLicenseService, + PushPublishService, + ApiRoot, + UserModel, + LoggerService, + StringUtils, + DotCurrentUserService, + DotContainerListResolver, + { provide: CoreWebService, useClass: CoreWebServiceMock }, + DotFormatDateService, + { + provide: DotcmsConfigService, + useValue: { + getSystemTimeZone: () => + of({ + id: 'America/Costa_Rica', + label: 'Central Standard Time (America/Costa_Rica)', + offset: -21600000 + }) + } + } + ] + }); + service = TestBed.inject(DotContainerListResolver); + pushPublishService = TestBed.inject(PushPublishService); + dotLicenseService = TestBed.inject(DotLicenseService); + }); + + it('should set pagination params, get first page, check license and publish environments', () => { + spyOn(dotLicenseService, 'isEnterprise').and.returnValue(of(true)); + spyOn(pushPublishService, 'getEnvironments').and.returnValue( + of([ + { + id: '1', + name: 'environment' + } + ]) + ); + service + .resolve() + .pipe(take(1)) + .subscribe(([isEnterPrise, hasEnvironments]) => { + expect(isEnterPrise).toEqual(true); + expect(hasEnvironments).toEqual(true); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.ts new file mode 100644 index 000000000000..41f3f5b65421 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/dot-container-list-resolver.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { forkJoin, Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { DotEnvironment } from '@models/dot-environment/dot-environment'; +import { DotLicenseService } from '@services/dot-license/dot-license.service'; +import { PushPublishService } from '@services/push-publish/push-publish.service'; + +@Injectable() +export class DotContainerListResolver implements Resolve<[boolean, boolean]> { + constructor( + public dotLicenseService: DotLicenseService, + public pushPublishService: PushPublishService + ) {} + + resolve(): Observable<[boolean, boolean]> { + return forkJoin([ + this.dotLicenseService.isEnterprise(), + this.pushPublishService.getEnvironments().pipe( + map((environments: DotEnvironment[]) => !!environments.length), + take(1) + ) + ]); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/store/dot-container-list.store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/store/dot-container-list.store.ts new file mode 100644 index 000000000000..214f064ffed3 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/store/dot-container-list.store.ts @@ -0,0 +1,555 @@ +import { Injectable } from '@angular/core'; +import { ComponentStore } from '@ngrx/component-store'; +import { MenuItem } from 'primeng/api'; +import { DotContainer } from '@models/container/dot-container.model'; +import { ActionHeaderOptions } from '@models/action-header'; +import { DataTableColumn } from '@models/data-table'; +import { ActivatedRoute } from '@angular/router'; +import { pluck, take } from 'rxjs/operators'; +import { DotMessageService } from '@services/dot-message/dot-messages.service'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; +import { DotPushPublishDialogService } from '@dotcms/dotcms-js'; +import { DotActionMenuItem } from '@models/dot-action-menu/dot-action-menu-item.model'; +import { DotSiteBrowserService } from '@services/dot-site-browser/dot-site-browser.service'; +import { DotAlertConfirmService } from '@services/dot-alert-confirm'; +import { + DotActionBulkResult, + DotBulkFailItem +} from '@models/dot-action-bulk-result/dot-action-bulk-result.model'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; +import { DotListingDataTableComponent } from '@components/dot-listing-data-table/dot-listing-data-table.component'; + +export interface DotContainerListState { + containerBulkActions: MenuItem[]; + selectedContainers: DotContainer[]; + addToBundleIdentifier: string; + actionHeaderOptions: ActionHeaderOptions; + tableColumns: DataTableColumn[]; + isEnterprise: boolean; + hasEnvironments: boolean; + stateLabels: { [key: string]: string }; + listing: DotListingDataTableComponent; + notifyMessages: DotNotifyMessages; +} + +export interface DotNotifyMessages { + payload: DotActionBulkResult | DotContainer; + message: string; + failsInfo?: DotBulkFailItem[]; +} + +@Injectable() +export class DotContainerListStore extends ComponentStore { + constructor( + private route: ActivatedRoute, + private dotMessageService: DotMessageService, + private dotRouterService: DotRouterService, + private dotPushPublishDialogService: DotPushPublishDialogService, + private dotSiteBrowserService: DotSiteBrowserService, + private dotAlertConfirmService: DotAlertConfirmService, + private dotContainersService: DotContainersService + ) { + super(null); + + this.route.data + .pipe(pluck('dotContainerListResolverData'), take(1)) + .subscribe(([isEnterprise, hasEnvironments]: [boolean, boolean]) => { + this.setState({ + containerBulkActions: this.getContainerBulkActions( + hasEnvironments, + isEnterprise + ), + tableColumns: this.getContainerColumns(), + stateLabels: this.getStateLabels(), + isEnterprise: isEnterprise, + hasEnvironments: hasEnvironments, + addToBundleIdentifier: '', + selectedContainers: [], + actionHeaderOptions: this.getActionHeaderOptions(), + listing: {} as DotListingDataTableComponent, + notifyMessages: { + payload: {}, + message: null, + failsInfo: [] + } as DotNotifyMessages + }); + }); + } + + readonly vm$ = this.select( + ({ + containerBulkActions, + addToBundleIdentifier, + actionHeaderOptions, + tableColumns, + stateLabels, + selectedContainers + }: DotContainerListState) => { + return { + containerBulkActions, + addToBundleIdentifier, + actionHeaderOptions, + tableColumns, + stateLabels, + selectedContainers + }; + } + ); + + readonly notify$ = this.select(({ notifyMessages }: DotContainerListState) => { + return notifyMessages; + }); + + readonly updateBundleIdentifier = this.updater( + (state: DotContainerListState, addToBundleIdentifier: string) => { + return { + ...state, + addToBundleIdentifier + }; + } + ); + + readonly updateSelectedContainers = this.updater( + (state: DotContainerListState, selectedContainers: DotContainer[]) => { + return { + ...state, + selectedContainers + }; + } + ); + + readonly updateListing = this.updater( + (state: DotContainerListState, listing: DotListingDataTableComponent) => { + return { + ...state, + listing + }; + } + ); + + readonly updateNotifyMessages = this.updater( + (state: DotContainerListState, notifyMessages: DotNotifyMessages) => { + const { payload } = notifyMessages; + + if ('fails' in payload && payload.fails.length) { + notifyMessages.failsInfo = this.getFailsInfo(payload.fails); + } + + return { + ...state, + notifyMessages + }; + } + ); + + /** + * Set the actions of each container based o current state. + * @param {DotContainer} container + ** @returns DotActionMenuItem[] + * @memberof DotContainerListComponent + */ + getContainerActions(container: DotContainer): DotActionMenuItem[] { + let options: DotActionMenuItem[]; + if (container.deleted) { + options = this.setArchiveContainerActions(container); + } else { + options = this.setBaseContainerOptions(container); + options = [...options, ...this.setCopyContainerOptions(container)]; + + if (!container.live) { + options = [ + ...options, + ...this.getLicenseAndRemotePublishContainerOptions(container), + ...this.getUnPublishAndArchiveContainerOptions(container) + ]; + } + } + + return options; + } + + private getContainerBulkActions(hasEnvironments = false, isEnterprise = false) { + return [ + { + label: this.dotMessageService.get('Publish'), + command: () => { + const { selectedContainers } = this.get(); + this.publishContainer( + selectedContainers.map((container) => container.identifier) + ); + } + }, + ...this.getLicenseAndRemotePublishContainerBulkOptions(hasEnvironments, isEnterprise), + { + label: this.dotMessageService.get('Unpublish'), + command: () => { + const { selectedContainers } = this.get(); + this.unPublishContainer( + selectedContainers.map((container) => container.identifier) + ); + } + }, + { + label: this.dotMessageService.get('Archive'), + command: () => { + const { selectedContainers } = this.get(); + this.archiveContainers( + selectedContainers.map((container) => container.identifier) + ); + } + }, + { + label: this.dotMessageService.get('Unarchive'), + command: () => { + const { selectedContainers } = this.get(); + this.unArchiveContainer( + selectedContainers.map((container) => container.identifier) + ); + } + }, + { + label: this.dotMessageService.get('Delete'), + command: () => { + const { selectedContainers } = this.get(); + this.deleteContainer( + selectedContainers.map((container) => container.identifier) + ); + } + } + ]; + } + + private getContainerColumns(): DataTableColumn[] { + return [ + { + fieldName: 'title', + header: this.dotMessageService.get('message.containers.fieldName.name'), + sortable: true + }, + { + fieldName: 'status', + header: this.dotMessageService.get('message.containers.fieldName.status'), + width: '8%' + }, + { + fieldName: 'friendlyName', + header: this.dotMessageService.get('message.containers.fieldName.description') + }, + { + fieldName: 'modDate', + format: 'date', + header: this.dotMessageService.get('message.containers.fieldName.lastEdit'), + sortable: true + } + ]; + } + + private getActionHeaderOptions(): ActionHeaderOptions { + return { + primary: { + command: () => { + this.dotRouterService.gotoPortlet(`/containers/create`); + } + } + }; + } + + /** + * set the labels of dot-state-icon. + * @returns { [key: string]: string } + * @memberof DotContainerListStore + */ + private getStateLabels(): { [key: string]: string } { + return { + archived: this.dotMessageService.get('Archived'), + published: this.dotMessageService.get('Published'), + revision: this.dotMessageService.get('Revision'), + draft: this.dotMessageService.get('Draft') + }; + } + + private getLicenseAndRemotePublishContainerBulkOptions( + hasEnvironments: boolean, + isEnterprise: boolean + ): MenuItem[] { + const bulkOptions: MenuItem[] = []; + if (hasEnvironments) { + bulkOptions.push({ + label: this.dotMessageService.get('Remote-Publish'), + command: () => { + const { selectedContainers } = this.get(); + this.dotPushPublishDialogService.open({ + assetIdentifier: selectedContainers + .map((container) => container.identifier) + .toString(), + title: this.dotMessageService.get('contenttypes.content.push_publish') + }); + } + }); + } + + if (isEnterprise) { + bulkOptions.push({ + label: this.dotMessageService.get('Add-To-Bundle'), + command: () => { + const { selectedContainers } = this.get(); + this.updateBundleIdentifier( + selectedContainers.map((container) => container.identifier).toString() + ); + } + }); + } + + return bulkOptions; + } + + private getUnPublishAndArchiveContainerOptions(container: DotContainer): DotActionMenuItem[] { + const options: DotActionMenuItem[] = []; + if (container.live) { + options.push({ + menuItem: { + label: this.dotMessageService.get('Unpublish'), + command: () => { + this.unPublishContainer([container.identifier]); + } + } + }); + } else { + options.push({ + menuItem: { + label: this.dotMessageService.get('Archive'), + command: () => { + this.archiveContainers([container.identifier]); + } + } + }); + } + + return options; + } + + private getLicenseAndRemotePublishContainerOptions( + container: DotContainer + ): DotActionMenuItem[] { + const options: DotActionMenuItem[] = []; + const { hasEnvironments, isEnterprise } = this.get(); + if (hasEnvironments) { + options.push({ + menuItem: { + label: this.dotMessageService.get('Remote-Publish'), + command: () => { + this.dotPushPublishDialogService.open({ + assetIdentifier: container.identifier, + title: this.dotMessageService.get('contenttypes.content.push_publish') + }); + } + } + }); + } + + if (isEnterprise) { + options.push({ + menuItem: { + label: this.dotMessageService.get('Add-To-Bundle'), + command: () => { + this.updateBundleIdentifier(container.identifier); + } + } + }); + } + + return options; + } + + private setCopyContainerOptions(container: DotContainer): DotActionMenuItem[] { + return !container.locked + ? [ + { + menuItem: { + label: this.dotMessageService.get('Duplicate'), + command: () => { + this.copyContainer(container.identifier); + } + } + } + ] + : []; + } + + private setBaseContainerOptions(container: DotContainer): DotActionMenuItem[] { + const options: DotActionMenuItem[] = []; + + if (!container.locked) { + options.push({ + menuItem: { + label: this.dotMessageService.get('edit'), + command: () => { + this.editContainer(container); + } + } + }); + } + + if (!container.live) { + options.push({ + menuItem: { + label: this.dotMessageService.get('publish'), + command: () => { + this.publishContainer([container.identifier]); + } + } + }); + } + + return options; + } + + private setArchiveContainerActions(container: DotContainer): DotActionMenuItem[] { + const options: DotActionMenuItem[] = []; + if (!container.live) { + options.push({ + menuItem: { + label: this.dotMessageService.get('Unarchive'), + command: () => { + this.unArchiveContainer([container.identifier]); + } + } + }); + } + + if (!container.locked) { + options.push({ + menuItem: { + label: this.dotMessageService.get('Delete'), + command: () => { + this.deleteContainer([container.identifier]); + } + } + }); + } + + return options; + } + + /** + * Identify if is a container as File based on the identifier path. + * @param {DotContainer} identifier + * @returns boolean + * @memberof DotContainerListComponent + */ + private isContainerAsFile({ identifier }: DotContainer): boolean { + return identifier.includes('/'); + } + + /** + * Handle selected container. + * + * @param {DotContainer} container + * @memberof DotContainerListComponent + */ + private editContainer(container: DotContainer): void { + this.isContainerAsFile(container) + ? this.dotSiteBrowserService + .setSelectedFolder(container.identifier) + .pipe(take(1)) + .subscribe(() => { + this.dotRouterService.goToSiteBrowser(); + }) + : this.dotRouterService.goToEditContainer(container.identifier); + } + + private deleteContainer(identifiers: string[]): void { + this.dotAlertConfirmService.confirm({ + accept: () => { + this.dotContainersService + .delete(identifiers) + .pipe(take(1)) + .subscribe((payload: DotActionBulkResult) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container.full_delete') + }); + }); + }, + reject: () => { + // + }, + header: this.dotMessageService.get('Delete-Container'), + message: this.dotMessageService.get('message.container.confirm.delete.container') + }); + } + + private publishContainer(identifiers: string[]): void { + this.dotContainersService + .publish(identifiers) + .pipe(take(1)) + .subscribe((payload: DotActionBulkResult) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container_list.published') + }); + }); + } + + private copyContainer(identifier: string): void { + this.dotContainersService + .copy(identifier) + .pipe(take(1)) + .subscribe((payload: DotContainer) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container_list.published') + }); + }); + } + + private unPublishContainer(identifiers: string[]): void { + this.dotContainersService + .unPublish(identifiers) + .pipe(take(1)) + .subscribe((payload: DotActionBulkResult) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container.unpublished') + }); + }); + } + + private unArchiveContainer(identifiers: string[]): void { + this.dotContainersService + .unArchive(identifiers) + .pipe(take(1)) + .subscribe((payload: DotActionBulkResult) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container.undelete') + }); + }); + } + + private archiveContainers(identifiers: string[]): void { + this.dotContainersService + .archive(identifiers) + .pipe(take(1)) + .subscribe((payload: DotActionBulkResult) => { + this.updateNotifyMessages({ + payload, + message: this.dotMessageService.get('message.container.delete') + }); + }); + } + + private getFailsInfo(items: DotBulkFailItem[]): DotBulkFailItem[] { + return items.map((item: DotBulkFailItem) => { + return { ...item, description: this.getContainerName(item.element) }; + }); + } + + private getContainerName(identifier: string): string { + const { selectedContainers } = this.get(); + + return selectedContainers.find((container: DotContainer) => { + return container.identifier === identifier; + }).name; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.html new file mode 100644 index 000000000000..a07180cac681 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.html @@ -0,0 +1,22 @@ + + +
+
+
+
{{ variable?.name }}
+
+ {{ variable?.fieldTypeLabel }} +
+
+
+ +
+
+
+
+
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.ts new file mode 100644 index 000000000000..0e852001b7c3 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { DynamicDialogConfig } from 'primeng/dynamicdialog'; +import { DotAddVariableStore } from '@dotcms/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/store/dot-add-variable.store'; + +@Component({ + selector: 'dot-add-variable', + templateUrl: './dot-add-variable.component.html', + providers: [DotAddVariableStore] +}) +export class DotAddVariableComponent implements OnInit { + vm$ = this.store.vm$; + + constructor(private store: DotAddVariableStore, private config: DynamicDialogConfig) {} + + ngOnInit() { + this.store.getVariables(this.config.data?.contentTypeVariable); + } + + /** + * handle save button + * @param {string} variable + * @returns void + * @memberof DotAddVariableComponent + */ + onSave(variable: string): void { + this.config.data?.onSave?.(this.applyMask(variable)); + } + + /** + * Applies variable mask to string + * + * @param {string} variable + * @private + * @returns string + * @memberof DotAddVariableComponent + */ + private applyMask(variable: string): string { + return `$!{${variable}}`; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.module.ts new file mode 100644 index 000000000000..fb70e8203732 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/dot-add-variable.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DotMessagePipeModule } from '@dotcms/app/view/pipes/dot-message/dot-message-pipe.module'; +import { ButtonModule } from 'primeng/button'; +import { DataViewModule } from 'primeng/dataview'; +import { DotAddVariableComponent } from './dot-add-variable.component'; + +@NgModule({ + declarations: [DotAddVariableComponent], + imports: [CommonModule, DotMessagePipeModule, ButtonModule, DataViewModule], + exports: [DotAddVariableComponent] +}) +export class DotAddVariableModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/store/dot-add-variable.store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/store/dot-add-variable.store.ts new file mode 100644 index 000000000000..bfea31726b84 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-add-variable/store/dot-add-variable.store.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@angular/core'; +import { ComponentStore } from '@ngrx/component-store'; +import { DotCMSContentType, DotCMSContentTypeField } from '@dotcms/dotcms-models'; +import { catchError, switchMap, tap } from 'rxjs/operators'; +import { DotContentTypeService } from '@dotcms/app/api/services/dot-content-type'; +import { Observable, of } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; +import { DotGlobalMessageService } from '@components/_common/dot-global-message/dot-global-message.service'; +import { DotHttpErrorManagerService } from '@services/dot-http-error-manager/dot-http-error-manager.service'; + +export interface DotAddVariableState { + variables: DotCMSContentTypeField[]; +} + +@Injectable() +export class DotAddVariableStore extends ComponentStore { + constructor( + private dotContentTypeService: DotContentTypeService, + private dotGlobalMessageService: DotGlobalMessageService, + private dotHttpErrorManagerService: DotHttpErrorManagerService + ) { + super({ + variables: [] + }); + } + + readonly vm$ = this.select(({ variables }) => { + return { + variables + }; + }); + + readonly updateVariables = this.updater( + (state: DotAddVariableState, variables: DotCMSContentTypeField[]) => { + return { + ...state, + variables + }; + } + ); + + readonly getVariables = this.effect((origin$: Observable) => { + return origin$.pipe( + switchMap((containerVariable) => { + return this.dotContentTypeService.getContentType(containerVariable); + }), + tap((contentType: DotCMSContentType) => { + this.updateVariables(contentType.fields); + }), + catchError((err: HttpErrorResponse) => { + this.dotGlobalMessageService.error(err.statusText); + this.dotHttpErrorManagerService.handle(err); + + return of(null); + }) + ); + }); +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.html new file mode 100644 index 000000000000..31c6c0fd8842 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.html @@ -0,0 +1,45 @@ +
+ + + + + + + + + + + + + +
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.scss new file mode 100644 index 000000000000..cb1450dbd2de --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.scss @@ -0,0 +1,61 @@ +@use "variables" as *; + +.dot-container-code__content-tab-container { + padding: $spacing-4; +} + +.dot-container-code__variable-btn { + margin-bottom: $spacing-2; +} + +.dot-container-code__content-tab-container ::ng-deep { + .p-tabview { + height: 100%; + } + .p-tabview-panels { + height: 100%; + flex-grow: 1; + flex-basis: 0; + overflow: auto; + } + .p-tabview-nav { + padding: 0; + background: $white; + border: 1px solid $input-border-color; + border-bottom: none; + } + + .p-tabview-panel { + height: 100%; + padding: 0; + padding-top: 0; + border: 1px solid $input-border-color; + border-top: none; + } + + .p-tabview-nav-next { + width: 40px; + background-color: #f1f3f4; + border: 1px solid $input-border-color; + border-left: none; + border-bottom: none; + } + + .p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link { + border-bottom: 2px solid var(--color-sec); + } + + .p-tabview .p-tabview-nav li .p-tabview-nav-link { + padding-left: $spacing-4; + padding-right: $spacing-4; + } + + .p-tabview .tab-panel-btn a.p-tabview-nav-link { + padding: 0; + } + + .p-tabview-title { + display: inline-block; + margin-right: $spacing-2; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.spec.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.ts new file mode 100644 index 000000000000..371226e55839 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.component.ts @@ -0,0 +1,185 @@ +import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { DialogService } from 'primeng/dynamicdialog'; +import { DotAddVariableComponent } from './dot-add-variable/dot-add-variable.component'; +import { DotMessageService } from '@dotcms/app/api/services/dot-message/dot-messages.service'; +import { DotCMSContentType } from '@dotcms/dotcms-models'; +import { Subject } from 'rxjs'; +import { ControlValueAccessor, FormArray, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MenuItem } from 'primeng/api'; + +interface DotContainerContent extends DotCMSContentType { + code?: string; +} + +@Component({ + selector: 'dot-container-code', + templateUrl: './dot-container-code.component.html', + styleUrls: ['./dot-container-code.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DotContentEditorComponent), + multi: true + } + ] +}) +export class DotContentEditorComponent implements ControlValueAccessor, OnInit { + @Input() contentTypes: DotContainerContent[]; + @Output() valueChange = new EventEmitter(); + + public readonly containerContents = new FormArray([] as FormControl[]); + + menuItems: MenuItem[]; + activeTabIndex = 1; + monacoEditors = {}; + + private destroy$: Subject = new Subject(); + + constructor( + private dialogService: DialogService, + private dotMessageService: DotMessageService + ) {} + + ngOnInit() { + this.containerContents.valueChanges.subscribe((fieldVal) => { + this._onChange(fieldVal); + this.onTouched(); + }); + + this.init(); + } + + public writeValue(value: DotContainerContent[] | null): void { + value = value ?? []; + this.containerContents.setValue(value); + } + + public setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.containerContents.disable(); + } else { + this.containerContents.enable(); + } + } + + private _onChange = (_value: DotContainerContent[] | null) => undefined; + + public registerOnChange(fn: (value: DotContainerContent[] | null) => void): void { + this._onChange = fn; + } + + public onTouched = () => undefined; + + public registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + /** + * If the index is null or 0, prevent the default action and stop propagation. Otherwise, update the active tab index + * and push the container content + * @param {MouseEvent} e - MouseEvent - The event object that was triggered by the click. + * @param {number} [index=null] - number = null + * @returns false + */ + public handleTabClick(e: MouseEvent, index: number = null): boolean { + if (index === null || index === 0) { + e.preventDefault(); + e.stopPropagation(); + } else { + this.updateActiveTabIndex(index); + } + + return false; + } + + /** + * It takes a string as an argument and sets the value of the active tab to that string + * @param {string} text - The text to be updated in the textarea + * @memberof DotContentEditorComponent + */ + updateContentTypeText(text: string): void { + const control = this.containerContents.controls[this.activeTabIndex - 1].value; + this.containerContents.controls[this.activeTabIndex - 1].setValue({ + ...control, + code: text + }); + } + + /** + * It removes the form control at the index of the form array, and then closes the modal + * @param {number} [index=null] - number = null + * @param close - This is the function that closes the modal. + * @memberof DotContentEditorComponent + */ + handleClose(index: number = null, close: () => void): void { + this.containerContents.removeAt(index - 1); + close(); + } + + /** + * It opens a dialog with a form to add a variable to the container + * @param {DotContainerContent} contentType - DotContainerContent - The content type object that contains + * the variables. + * @returns {void} + * @param {number} index - The index of the tab that was clicked. + * @memberof DotContentEditorComponent + */ + handleAddVariable(contentType: DotContainerContent) { + this.dialogService.open(DotAddVariableComponent, { + header: this.dotMessageService.get('containers.properties.add.variable.title'), + width: '50rem', + data: { + contentTypeVariable: contentType.variable, + onSave: (variable) => { + const editor = this.monacoEditors[contentType.variable].getModel(); + this.monacoEditors[contentType.variable] + .getModel() + .setValue(editor.getValue() + `${variable}`); + } + } + }); + } + + /** + * It pushes the monaco instance into the monacoEditor array. + * @param monacoInstance - The monaco instance that is created by the component. + * @memberof DotContentEditorComponent + */ + monacoInit(monacoEditor) { + this.monacoEditors[monacoEditor.name] = monacoEditor.editor; + } + + private init(): void { + this.menuItems = this.mapMenuItems(this.contentTypes); + } + + /** + * It updates the activeTabIndex property with the index of the tab that was clicked + * @param {number} index - number - The index of the tab that was clicked. + * @memberof DotContentEditorComponent + */ + private updateActiveTabIndex(index: number): void { + this.activeTabIndex = index; + } + + private mapMenuItems(contentTypes: DotContainerContent[]): MenuItem[] { + return contentTypes.map((contentType) => { + return { + label: contentType.name, + command: () => { + if (!this.checkIfAlreadyExists(contentType)) { + this.containerContents.push( + new FormControl(contentType) + ); + } + } + }; + }); + } + + private checkIfAlreadyExists(contentType: DotContainerContent): boolean { + return this.containerContents.controls.some( + (control) => control.value.variable === contentType.variable + ); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.module.ts new file mode 100644 index 000000000000..824e6f1904a3 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DotContentEditorComponent } from './dot-container-code.component'; +import { TabViewModule } from 'primeng/tabview'; +import { MenuModule } from 'primeng/menu'; +import { DotTextareaContentModule } from '@components/_common/dot-textarea-content/dot-textarea-content.module'; +import { DotMessagePipeModule } from '@dotcms/app/view/pipes/dot-message/dot-message-pipe.module'; +import { ReactiveFormsModule } from '@angular/forms'; +import { ButtonModule } from 'primeng/button'; +import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog'; +import { DotAddVariableModule } from './dot-add-variable/dot-add-variable.module'; + +@NgModule({ + declarations: [DotContentEditorComponent], + imports: [ + CommonModule, + TabViewModule, + MenuModule, + DotTextareaContentModule, + DotMessagePipeModule, + ReactiveFormsModule, + ButtonModule, + DynamicDialogModule, + DotAddVariableModule + ], + exports: [DotContentEditorComponent], + providers: [DialogService] +}) +export class DotContentEditorModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/store/dot-content-editor.store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/store/dot-content-editor.store.ts new file mode 100644 index 000000000000..fcacf91ca96e --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-code/store/dot-content-editor.store.ts @@ -0,0 +1,148 @@ +import { ComponentStore } from '@ngrx/component-store'; +import { take } from 'rxjs/operators'; +import { DotContentTypeService } from '@dotcms/app/api/services/dot-content-type'; +import { Injectable } from '@angular/core'; +import { MenuItem } from 'primeng/api'; +import { DotCMSContentType } from '@dotcms/dotcms-models'; +import { DotContainerStructure } from '@models/container/dot-container.model'; + +export interface DotContentEditorState { + activeTabIndex: number; + contentTypes: MenuItem[]; + selectedContentTypes: MenuItem[]; + contentTypesData: MenuItem[]; +} + +@Injectable() +export class DotContentEditorStore extends ComponentStore { + constructor(private dotContentTypeService: DotContentTypeService) { + super({ + activeTabIndex: 1, + contentTypes: [], + selectedContentTypes: [], + contentTypesData: [] + }); + + this.dotContentTypeService + .getContentTypes({ page: 999 }) //TODO: Add call to get all contentTypes + .pipe(take(1)) + .subscribe((contentTypes) => { + const mappedContentTypes = this.mapActions(contentTypes); + this.setState({ + activeTabIndex: 1, + contentTypes: mappedContentTypes, + selectedContentTypes: [mappedContentTypes[0]], + contentTypesData: [mappedContentTypes[0]] + }); + }); + } + + readonly vm$ = this.select((state: DotContentEditorState) => state); + readonly contentTypeData$ = this.select( + ({ contentTypesData }: DotContentEditorState) => contentTypesData + ); + readonly contentTypes$ = this.select(({ contentTypes }: DotContentEditorState) => contentTypes); + + updateActiveTabIndex = this.updater((state, activeTabIndex) => { + return { + ...state, + activeTabIndex + }; + }); + + updateClosedTab = this.updater((state, closedTabIndex) => { + const { selectedContentTypes, contentTypesData } = this.get(); + + const updatedSelectedContentTypes = selectedContentTypes.filter( + (val, index) => index !== closedTabIndex + ); + + const updatedContentTypesData = contentTypesData.filter( + (val, index) => index !== closedTabIndex + ); + + return { + ...state, + selectedContentTypes: updatedSelectedContentTypes, + contentTypesData: updatedContentTypesData + }; + }); + + updateSelectedContentType = this.updater((state, selectedContentType) => { + return { + ...state, + contentTypesData: [...state.contentTypesData, selectedContentType], + selectedContentTypes: [...state.selectedContentTypes, selectedContentType] + }; + }); + + updateSelectedContentTypeContent = this.updater((state, code) => { + const { contentTypesData, activeTabIndex } = this.get(); + const contentTypes = [...contentTypesData]; + const currentContent = contentTypes[activeTabIndex - 1]; + + contentTypes[activeTabIndex - 1] = { + ...currentContent, + state: { ...currentContent.state, code: code || currentContent?.state?.code || '' } + }; + + return { + ...state, + contentTypesData: contentTypes + }; + }); + + updateRetrievedContentTypes = this.updater( + (state, containerStructures) => { + const { contentTypes, selectedContentTypes } = this.get(); + const availableselectedContentTypes = []; + // if containerStructures strcuture available we don't need to add default contentType + if (containerStructures.length === 0) { + availableselectedContentTypes.push(...selectedContentTypes); + } + + contentTypes.forEach((contentType) => { + const foundSelectedContentType = containerStructures.find( + (content) => content.contentTypeVar === contentType.state.contentType.variable + ); + if (foundSelectedContentType) { + availableselectedContentTypes.push({ + ...contentType, + state: { ...contentType.state, ...foundSelectedContentType } + }); + } + }); + + return { + ...state, + selectedContentTypes: availableselectedContentTypes, + contentTypesData: availableselectedContentTypes + }; + } + ); + + private mapActions(contentTypes: DotCMSContentType[]): MenuItem[] { + return contentTypes.map((contentType) => { + const menuItem = { + label: contentType.name, + state: { + code: '', + contentType + }, + command: () => { + if (!this.checkIfAlreadyExists(menuItem.label)) { + this.updateSelectedContentType(menuItem); + } + } + }; + + return menuItem; + }); + } + + private checkIfAlreadyExists(label: string): boolean { + const { selectedContentTypes } = this.get(); + + return selectedContentTypes.some((contentType) => label === contentType.label); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create-routing.module.ts new file mode 100644 index 000000000000..80d14cc82cca --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create-routing.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DotContainerCreateComponent } from './dot-container-create.component'; +import { DotContainerEditResolver } from './resolvers/dot-container-edit.resolver'; + +const routes: Routes = [ + { + path: '', + component: DotContainerCreateComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [DotContainerEditResolver] +}) +export class DotContainerCreateRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.html new file mode 100644 index 000000000000..ac153bdc598a --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.html @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + +
+ +
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.scss new file mode 100644 index 000000000000..a0af3536a5bb --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.scss @@ -0,0 +1,33 @@ +@use "variables" as *; + +.container-create__tab-container ::ng-deep { + .p-tabview { + height: 100%; + } + .p-tabview-panels { + height: 100%; + flex-grow: 1; + flex-basis: 0; + overflow: auto; + } + .p-tabview-nav { + padding: 0 $spacing-4; + background: $white; + } + + .p-tabview-panel { + height: 100%; + padding: 0 $spacing-4; + padding-top: $spacing-2; + } +} + +.container-create__tab-container { + display: flex; + flex-direction: column; + height: 100%; +} + +.container-create__tabs { + height: 100%; +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.spec.ts new file mode 100644 index 000000000000..bd1b52e5f5ef --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.spec.ts @@ -0,0 +1,69 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DotContainerCreateComponent } from './dot-container-create.component'; +import { CoreWebService } from '@dotcms/dotcms-js'; +import { CoreWebServiceMock } from '@tests/core-web.service.mock'; +import { ActivatedRoute } from '@angular/router'; +import { DotRouterService } from '@dotcms/app/api/services/dot-router/dot-router.service'; +import { Pipe, PipeTransform } from '@angular/core'; +import { of } from 'rxjs'; +import { CONTAINER_SOURCE } from '@dotcms/app/shared/models/container/dot-container.model'; + +@Pipe({ + name: 'dm' +}) +class DotMessageMockPipe implements PipeTransform { + transform(): string { + return 'Required'; + } +} +describe('ContainerCreateComponent', () => { + let component: DotContainerCreateComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DotContainerCreateComponent, DotMessageMockPipe], + providers: [ + { provide: CoreWebService, useClass: CoreWebServiceMock }, + { + provide: ActivatedRoute, + useValue: { + data: of({ + container: { + container: { + archived: false, + live: true, + working: true, + locked: false, + identifier: '', + name: '', + type: '', + source: CONTAINER_SOURCE.DB, + parentPermissionable: { + hostname: 'dotcms.com' + } + }, + containerStructures: [] + } + }), + snapshot: { + params: { + id: '123' + } + } + } + }, + DotRouterService + ] + }).compileComponents(); + + fixture = TestBed.createComponent(DotContainerCreateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.ts new file mode 100644 index 000000000000..6d15ac5e2563 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { DotRouterService } from '@dotcms/app/api/services/dot-router/dot-router.service'; +import { DotContainerEntity } from '@dotcms/app/shared/models/container/dot-container.model'; +import { pluck, take } from 'rxjs/operators'; + +@Component({ + selector: 'dot-container-create', + templateUrl: './dot-container-create.component.html', + styleUrls: ['./dot-container-create.component.scss'] +}) +export class DotContainerCreateComponent implements OnInit { + containerId = ''; + constructor( + private activatedRoute: ActivatedRoute, + private dotRouterService: DotRouterService + ) {} + + ngOnInit() { + this.activatedRoute.data + .pipe(pluck('container'), take(1)) + .subscribe((container: DotContainerEntity) => { + if (container?.container) this.containerId = container.container.identifier; + else this.dotRouterService.goToCreateContainer(); + }); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.module.ts new file mode 100644 index 000000000000..655bf70d6b6c --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DotContainerCreateRoutingModule } from './dot-container-create-routing.module'; +import { DotContainerCreateComponent } from './dot-container-create.component'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { TabViewModule } from 'primeng/tabview'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { DotContainerPropertiesModule } from '@portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.module'; +import { DotContainerPermissionsModule } from '@dotcms/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.module'; +import { DotContainerHistoryModule } from '@dotcms/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.module'; +import { DotGlobalMessageModule } from '@components/_common/dot-global-message/dot-global-message.module'; + +@NgModule({ + declarations: [DotContainerCreateComponent], + imports: [ + CommonModule, + DotContainerCreateRoutingModule, + DotPortletBaseModule, + TabViewModule, + DotMessagePipeModule, + DotContainerPropertiesModule, + DotContainerPermissionsModule, + DotContainerHistoryModule, + DotGlobalMessageModule + ] +}) +export class DotContainerCreateModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.html new file mode 100644 index 000000000000..b831cfd7b43f --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.html @@ -0,0 +1,3 @@ + + + diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.scss new file mode 100644 index 000000000000..5080387a357d --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.scss @@ -0,0 +1,8 @@ +@use "variables" as *; + +dot-portlet-box { + height: 100%; + overflow-x: auto; + margin: 0; + padding: $spacing-4; +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.spec.ts new file mode 100644 index 000000000000..eb5cda92702b --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.spec.ts @@ -0,0 +1,56 @@ +import { Component, DebugElement, ElementRef, Input, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DotContainerHistoryComponent } from './dot-container-history.component'; +import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module'; + +@Component({ + selector: 'dot-iframe', + template: '' +}) +export class IframeMockComponent { + @Input() src: string; + @ViewChild('iframeElement') iframeElement: ElementRef; +} + +@Component({ + selector: `dot-host-component`, + template: `` +}) +class DotTestHostComponent { + containerId = ''; +} + +describe('ContainerHistoryComponent', () => { + let hostComponent: DotTestHostComponent; + let fixture: ComponentFixture; + let de: DebugElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DotContainerHistoryComponent, IframeMockComponent, DotTestHostComponent], + imports: [DotPortletBoxModule] + }).compileComponents(); + + fixture = TestBed.createComponent(DotTestHostComponent); + de = fixture.debugElement; + hostComponent = fixture.componentInstance; + hostComponent.containerId = '123'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(hostComponent).toBeTruthy(); + }); + + describe('history', () => { + it('should set iframe history url', () => { + hostComponent.containerId = '123'; + fixture.detectChanges(); + const permissions = de.query(By.css('[data-testId="historyIframe"]')); + expect(permissions.componentInstance.src).toBe( + '/html/containers/push_history.jsp?containerId=123&popup=true' + ); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.ts new file mode 100644 index 000000000000..9cdfcf35ee0d --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.component.ts @@ -0,0 +1,20 @@ +import { Component, Input, OnChanges, ViewChild } from '@angular/core'; +import { IframeComponent } from '@components/_common/iframe/iframe-component'; + +@Component({ + selector: 'dot-container-history', + templateUrl: './dot-container-history.component.html', + styleUrls: ['./dot-container-history.component.scss'] +}) +export class DotContainerHistoryComponent implements OnChanges { + @Input() containerId: string; + @ViewChild('historyIframe') historyIframe: IframeComponent; + historyUrl = '/html/containers/push_history.jsp'; + + ngOnChanges(): void { + this.historyUrl = `/html/containers/push_history.jsp?containerId=${this.containerId}&popup=true`; + if (this.historyIframe) { + this.historyIframe.iframeElement.nativeElement.contentWindow.location.reload(); + } + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.module.ts new file mode 100644 index 000000000000..87a1af3ab377 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-history/dot-container-history.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DotContainerHistoryComponent } from './dot-container-history.component'; +import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module'; +import { IFrameModule } from '@components/_common/iframe'; + +@NgModule({ + declarations: [DotContainerHistoryComponent], + exports: [DotContainerHistoryComponent], + imports: [CommonModule, DotPortletBoxModule, IFrameModule] +}) +export class DotContainerHistoryModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.html new file mode 100644 index 000000000000..0670edad6981 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.html @@ -0,0 +1,3 @@ + + + diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.scss new file mode 100644 index 000000000000..d7f0fe40d9d0 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.scss @@ -0,0 +1,5 @@ +dot-portlet-box { + height: 100%; + overflow-x: auto; + margin: 0; +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.spec.ts new file mode 100644 index 000000000000..bd6fb0ed757e --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.spec.ts @@ -0,0 +1,60 @@ +import { Component, DebugElement, ElementRef, Input, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DotContainerPermissionsComponent } from './dot-container-permissions.component'; +import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module'; + +@Component({ + selector: 'dot-iframe', + template: '' +}) +export class IframeMockComponent { + @Input() src: string; + @ViewChild('iframeElement') iframeElement: ElementRef; +} + +@Component({ + selector: `dot-host-component`, + template: `` +}) +class DotTestHostComponent { + containerId = ''; +} + +describe('ContainerPermissionsComponent', () => { + let hostComponent: DotTestHostComponent; + let fixture: ComponentFixture; + let de: DebugElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + DotContainerPermissionsComponent, + IframeMockComponent, + DotTestHostComponent + ], + imports: [DotPortletBoxModule] + }).compileComponents(); + + fixture = TestBed.createComponent(DotContainerPermissionsComponent); + de = fixture.debugElement; + hostComponent = fixture.componentInstance; + hostComponent.containerId = '123'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(hostComponent).toBeTruthy(); + }); + + describe('permissions', () => { + it('should set iframe permissions url', () => { + hostComponent.containerId = '123'; + fixture.detectChanges(); + const permissions = de.query(By.css('[data-testId="permissionsIframe"]')); + expect(permissions.componentInstance.src).toBe( + '/html/containers/permissions.jsp?containerId=123&popup=true' + ); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.ts new file mode 100644 index 000000000000..13a1c443b136 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.component.ts @@ -0,0 +1,14 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'dot-container-permissions', + templateUrl: './dot-container-permissions.component.html', + styleUrls: ['./dot-container-permissions.component.scss'] +}) +export class DotContainerPermissionsComponent implements OnInit { + @Input() containerId: string; + permissionsUrl = '/html/containers/permissions.jsp'; + ngOnInit() { + this.permissionsUrl = `/html/containers/permissions.jsp?containerId=${this.containerId}&popup=true`; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.module.ts new file mode 100644 index 000000000000..2663853b84aa --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-permissions/dot-container-permissions.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DotContainerPermissionsComponent } from './dot-container-permissions.component'; +import { DotPortletBoxModule } from '@components/dot-portlet-base/components/dot-portlet-box/dot-portlet-box.module'; +import { IFrameModule } from '@components/_common/iframe'; + +@NgModule({ + declarations: [DotContainerPermissionsComponent], + exports: [DotContainerPermissionsComponent], + imports: [CommonModule, DotPortletBoxModule, IFrameModule] +}) +export class DotContainerPermissionsModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.html new file mode 100644 index 000000000000..40decbf9d1ef --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.html @@ -0,0 +1,118 @@ + +
+
+ + +
+
+ + + + + + + + + {{ 'message.containers.create.click_to_edit' | dm }} + {{ + form?.value?.title + }} + + + + +
+
+ + +
+
+ + + + +
+ +
+ + + +
+
+ + +
+
+
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.scss new file mode 100644 index 000000000000..9706f8c44d00 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.scss @@ -0,0 +1,80 @@ +@use "variables" as *; + +.dot-container-properties__button-container { + position: absolute; + top: 5.1rem; + right: $spacing-4; + + button { + margin: 0 calc($spacing-1 / 2); + } +} + +.dot-container-properties__title-container { + display: flex; + flex-direction: row; + gap: $spacing-2; + padding: $spacing-2 0; + + ::ng-deep .p-inplace.p-component { + display: flex; + align-items: center; + justify-content: center; + } + + ::ng-deep .p-inplace-display { + padding: 0.67rem $spacing-3; + } +} + +.dot-container-properties__title-text { + display: inline-block; + padding-right: $spacing-2; + font-size: $font-size-large; +} + +.dot-container-properties__title-input { + width: 20rem; +} + +.dot-container-properties__description-input { + width: 20rem; +} + +.dot-container-properties__max-contents-input { + width: 7rem; + margin-right: $spacing-2; +} + +.p-button-info.dot-container-properties__button-clear, +.p-button-info.dot-container-properties__button-clear:hover { + background-color: $gray; + color: $white; + padding: 0.82rem $spacing-3; +} + +.dot-container-properties__code-loop-container { + background: $white; + border: 1px solid $input-border-color; + padding-bottom: 4rem; +} + +:host ::ng-deep { + .tab-panel-btn { + background: $brand-background; + + a.p-tabview-nav-link { + padding: 0; + } + + i { + color: $white; + padding: 1.2rem $spacing-6 1.35rem; + } + } + + .p-tabview-panel { + padding: 0; + padding-top: $spacing-1; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.spec.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.ts new file mode 100644 index 000000000000..100f8a62114f --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component.ts @@ -0,0 +1,190 @@ +import { Component, OnInit } from '@angular/core'; +import { + FormArray, + FormControl, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + DotContainerPropertiesStore, + DotContainerPropertiesState +} from '@portlets/dot-containers/dot-container-create/dot-container-properties/store/dot-container-properties.store'; +import { MonacoEditor } from '@models/monaco-editor'; +import { DotAlertConfirmService } from '@dotcms/app/api/services/dot-alert-confirm'; +import { DotMessageService } from '@dotcms/app/api/services/dot-message/dot-messages.service'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; +import { take, takeUntil } from 'rxjs/operators'; +import { MenuItem } from 'primeng/api'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'dot-container-properties', + templateUrl: './dot-container-properties.component.html', + styleUrls: ['./dot-container-properties.component.scss'], + providers: [DotContainerPropertiesStore] +}) +export class DotContainerPropertiesComponent implements OnInit { + vm$ = this.store.vm$; + editor: MonacoEditor; + form: UntypedFormGroup; + private destroy$: Subject = new Subject(); + + constructor( + private store: DotContainerPropertiesStore, + private dotMessageService: DotMessageService, + private fb: UntypedFormBuilder, + private dotAlertConfirmService: DotAlertConfirmService, + private dotRouterService: DotRouterService + ) { + // + } + + ngOnInit(): void { + this.store.containerAndStructure$ + .pipe(take(1)) + .subscribe((state: DotContainerPropertiesState) => { + const { container, containerStructures } = state; + this.form = this.fb.group({ + identifier: new FormControl(container?.identifier ?? ''), + title: new FormControl(container?.title ?? '', [Validators.required]), + friendlyName: new FormControl(container?.friendlyName ?? ''), + maxContentlets: new FormControl(container?.maxContentlets ?? 0, [ + Validators.required + ]), + code: new FormControl( + container?.code ?? '', + containerStructures.length === 0 ? [Validators.required] : null + ), + preLoop: container?.preLoop ?? '', + postLoop: container?.postLoop ?? '', + containerStructures: this.fb.array( + containerStructures ?? [], + containerStructures.length ? [Validators.minLength(1)] : null + ) + }); + }); + this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((values) => { + this.store.updateIsContentTypeButtonEnabled(values.maxContentlets > 0); + }); + } + + /** + * This method shows the Pre- and Post-Loop Inputs. + * + * @return void + * @memberof DotContainerPropertiesComponent + */ + showLoopInput(): void { + this.store.updatePrePostLoopInputVisibility(true); + } + + /** + * This method shows the Content type inputs. + * @return void + * @memberof DotContainerPropertiesComponent + */ + showContentTypeAndCode(): void { + const values = this.form.value; + if (values.maxContentlets > 0) { + this.form.get('code').clearValidators(); + this.form.get('code').reset(); + this.form.get('containerStructures').setValidators(Validators.minLength(1)); + this.store.loadContentTypesAndUpdateVisibility(); + } else { + this.form.get('code').setValidators(Validators.required); + this.form.get('containerStructures').clearValidators(); + } + + this.form.updateValueAndValidity(); + } + + /** + * Updates or Saves the container based on the identifier form value. + * @return void + * @memberof DotContainerPropertiesComponent + */ + save(): void { + const formValues = this.form.value; + if (formValues.identifier) { + this.store.editContainer(formValues); + } else { + delete formValues.identifier; + this.store.saveContainer(formValues); + } + } + + /** + * Updates containerStructures based on tab data + * + * @param {MenuItem[]} containerStructures + * @return {void} + * @memberof DotContainerPropertiesComponent + */ + updateContainerStructure(containerStructures: MenuItem[]): void { + const addInContainerStructure = this.form.get('containerStructures') as FormArray; + // clear containerStructures array + (this.form.get('containerStructures') as FormArray).clear(); + containerStructures.forEach(({ state }: MenuItem) => { + addInContainerStructure.push( + this.fb.group({ + structureId: new FormControl(state.contentType.variable ?? '', [ + Validators.required + ]), + code: new FormControl(state?.code || '', [ + Validators.required, + Validators.minLength(2) + ]) + }) + ); + }); + this.form.updateValueAndValidity(); + } + + /** + * This method navigates the user back to previous page. + * @return void + * @memberof DotContainerPropertiesComponent + */ + cancel(): void { + this.dotRouterService.goToURL('/containers'); + } + + /** + * Opens modal on clear content button click. + * @return void + * @memberof DotContainerPropertiesComponent + */ + clearContent(): void { + this.dotAlertConfirmService.confirm({ + accept: () => { + this.store.updateContentTypeVisibility(false); + this.form.reset(); + this.form.get('containerStructures').clearValidators(); + this.form.get('containerStructures').reset(); + // clear containerStructures array + (this.form.get('containerStructures') as FormArray).clear(); + this.form.get('code').addValidators(Validators.required); + this.form.updateValueAndValidity(); + }, + reject: () => { + // + }, + header: this.dotMessageService.get( + 'message.container.properties.confirm.clear.content.title' + ), + message: this.dotMessageService.get( + 'message.container.properties.confirm.clear.content.message' + ) + }); + } + + /** + * It returns the form control with the given name + * @param {string} controlName - The name of the control you want to get. + * @returns {FormControl} A FormControl + */ + getFormControl(controlName: string): FormControl { + return this.form.get(controlName) as FormControl; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.module.ts new file mode 100644 index 000000000000..9f4bd1988256 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { DotContainerPropertiesComponent } from '@portlets/dot-containers/dot-container-create/dot-container-properties/dot-container-properties.component'; +import { CommonModule } from '@angular/common'; +import { InplaceModule } from 'primeng/inplace'; +import { SharedModule } from 'primeng/api'; +import { InputTextModule } from 'primeng/inputtext'; +import { DotPortletBaseModule } from '@components/dot-portlet-base/dot-portlet-base.module'; +import { CardModule } from 'primeng/card'; +import { DotTextareaContentModule } from '@components/_common/dot-textarea-content/dot-textarea-content.module'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TabViewModule } from 'primeng/tabview'; +import { MenuModule } from 'primeng/menu'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { DotLoopEditorModule } from '@portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.module'; +import { DotContentEditorModule } from '@portlets/dot-containers/dot-container-create/dot-container-code/dot-container-code.module'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; +import { DotApiLinkModule } from '@components/dot-api-link/dot-api-link.module'; + +@NgModule({ + declarations: [DotContainerPropertiesComponent], + exports: [DotContainerPropertiesComponent], + imports: [ + CommonModule, + InplaceModule, + SharedModule, + InputTextModule, + DotPortletBaseModule, + CardModule, + DotTextareaContentModule, + ReactiveFormsModule, + TabViewModule, + MenuModule, + DotMessagePipeModule, + DotLoopEditorModule, + DotContentEditorModule, + DotApiLinkModule + ], + providers: [DotContainersService] +}) +export class DotContainerPropertiesModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/store/dot-container-properties.store.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/store/dot-container-properties.store.ts new file mode 100644 index 000000000000..dcb46b5dec77 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/store/dot-container-properties.store.ts @@ -0,0 +1,241 @@ +import { Injectable } from '@angular/core'; +import { ComponentStore } from '@ngrx/component-store'; +import { Observable, of, pipe } from 'rxjs'; +import { catchError, filter, pluck, switchMap, take, tap } from 'rxjs/operators'; +import { HttpErrorResponse } from '@angular/common/http'; +import { + DotContainer, + DotContainerEntity, + DotContainerPayload, + DotContainerStructure +} from '@dotcms/app/shared/models/container/dot-container.model'; +import { DotMessageService } from '@services/dot-message/dot-messages.service'; +import { DotGlobalMessageService } from '@components/_common/dot-global-message/dot-global-message.service'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; +import { DotHttpErrorManagerService } from '@dotcms/app/api/services/dot-http-error-manager/dot-http-error-manager.service'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; +import { ActivatedRoute } from '@angular/router'; +import { DotCMSContentType } from '@dotcms/dotcms-models'; +import { DotContentTypeService } from '@dotcms/app/api/services/dot-content-type'; + +export interface DotContainerPropertiesState { + showPrePostLoopInput: boolean; + isContentTypeVisible: boolean; + isContentTypeButtonEnabled: boolean; + container: DotContainer; + containerStructures: DotContainerStructure[]; + contentTypes: DotCMSContentType[]; + apiLink: string; +} + +@Injectable() +export class DotContainerPropertiesStore extends ComponentStore { + constructor( + private dotMessageService: DotMessageService, + private dotGlobalMessageService: DotGlobalMessageService, + private dotContainersService: DotContainersService, + private dotHttpErrorManagerService: DotHttpErrorManagerService, + private activatedRoute: ActivatedRoute, + private dotRouterService: DotRouterService, + private dotContentTypeService: DotContentTypeService + ) { + super({ + showPrePostLoopInput: false, + isContentTypeVisible: false, + isContentTypeButtonEnabled: false, + containerStructures: [], + contentTypes: [], + container: null, + apiLink: '' + }); + this.activatedRoute.data + .pipe( + pluck('container'), + take(1), + filter((containerEntity) => !!containerEntity) + ) + .subscribe((containerEntity: DotContainerEntity) => { + const { container, contentTypes } = containerEntity; + if (container && (container.preLoop || container.postLoop)) { + this.updatePrePostLoopAndContentTypeVisibility({ + showPrePostLoopInput: true, + isContentTypeVisible: true, + container: container, + containerStructures: contentTypes ?? [] + }); + } else { + this.updateContainerState(containerEntity); + } + + this.updateApiLink(container.identifier); + }); + } + + readonly vm$ = this.select((state: DotContainerPropertiesState) => { + return state; + }); + + readonly containerAndStructure$ = this.select( + ({ container, containerStructures }: DotContainerPropertiesState) => { + return { + container, + containerStructures + }; + } + ); + + readonly updatePrePostLoopAndContentTypeVisibility = this.updater<{ + showPrePostLoopInput: boolean; + isContentTypeVisible: boolean; + container: DotContainer; + containerStructures: DotContainerStructure[]; + }>( + ( + state: DotContainerPropertiesState, + { + showPrePostLoopInput, + isContentTypeVisible, + container, + containerStructures + }: DotContainerPropertiesState + ) => { + return { + ...state, + showPrePostLoopInput, + isContentTypeVisible, + container, + containerStructures + }; + } + ); + + readonly updatePrePostLoopInputVisibility = this.updater( + (state: DotContainerPropertiesState, showPrePostLoopInput: boolean) => { + return { + ...state, + showPrePostLoopInput + }; + } + ); + + readonly updateIsContentTypeButtonEnabled = this.updater( + (state: DotContainerPropertiesState, isContentTypeButtonEnabled: boolean) => { + return { + ...state, + isContentTypeButtonEnabled + }; + } + ); + + readonly updateContentTypeVisibility = this.updater( + (state: DotContainerPropertiesState, isContentTypeVisible: boolean) => { + return { + ...state, + isContentTypeVisible + }; + } + ); + + readonly updateContainerState = this.updater( + (state: DotContainerPropertiesState, container: DotContainerEntity) => { + return { + ...state, + container: container.container, + containerStructures: container.contentTypes + }; + } + ); + + readonly updateApiLink = this.updater( + (state: DotContainerPropertiesState, apiLink: string) => { + return { + ...state, + apiLink + }; + } + ); + + readonly updateContentTypes = this.updater( + (state: DotContainerPropertiesState, contentTypes: DotCMSContentType[]) => { + return { + ...state, + contentTypes + }; + } + ); + + readonly loadContentTypesAndUpdateVisibility = this.effect( + pipe( + switchMap(() => { + this.dotGlobalMessageService.loading(this.dotMessageService.get('loading')); + + return this.dotContentTypeService.getContentTypes({ page: 999 }); + }), + tap((contentTypes: DotCMSContentType[]) => { + this.dotGlobalMessageService.success(this.dotMessageService.get('loaded')); + this.updateContentTypes(contentTypes); + this.updateContentTypeVisibility(true); + }), + catchError((err: HttpErrorResponse) => { + this.dotGlobalMessageService.error(err.statusText); + this.dotHttpErrorManagerService.handle(err); + + return of(null); + }) + ) + ); + + readonly saveContainer = this.effect((origin$: Observable) => { + return origin$.pipe( + switchMap((container: DotContainerPayload) => { + this.dotGlobalMessageService.loading(this.dotMessageService.get('publishing')); + + return this.dotContainersService.create(container); + }), + tap((container: DotContainerEntity) => { + this.dotGlobalMessageService.success( + this.dotMessageService.get('message.container.published') + ); + this.updateContainerState(container); + this.dotRouterService.goToURL('/containers'); + }), + catchError((err: HttpErrorResponse) => { + this.dotGlobalMessageService.error(err.statusText); + this.dotHttpErrorManagerService.handle(err); + + return of(null); + }) + ); + }); + + readonly editContainer = this.effect((origin$: Observable) => { + return origin$.pipe( + switchMap((container: DotContainerPayload) => { + this.dotGlobalMessageService.loading(this.dotMessageService.get('update')); + + return this.dotContainersService.update(container); + }), + tap((container: DotContainerEntity) => { + this.dotGlobalMessageService.success( + this.dotMessageService.get('message.container.updated') + ); + this.updateContainerState(container); + }), + catchError((err: HttpErrorResponse) => { + this.dotGlobalMessageService.error(err.statusText); + this.dotHttpErrorManagerService.handle(err); + + return of(null); + }) + ); + }); + + /** + * It returns a string that is the URL to the working directory of the container + * @param {string} identifier - The container identifier. + * @returns The API link for the container. + */ + private getApiLink(identifier: string): string { + return identifier ? `/api/v1/containers/${identifier}/working` : ''; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.html new file mode 100644 index 000000000000..ef7964887e74 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.html @@ -0,0 +1,21 @@ +
+
+ +
+
+ + +
+
diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.scss b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.scss new file mode 100644 index 000000000000..b01685d25c37 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.scss @@ -0,0 +1,23 @@ +@use "variables" as *; + +:host { + .dot-loop-editor__pre-post-loop { + margin: $spacing-4; + } + + .dot-loop-editor__pre-post-loop-inplace { + display: flex; + align-items: center; + justify-content: center; + padding: $spacing-6; + background: #e9ebfc; + } + + dot-textarea-content { + height: 20vh !important; + } + + ::ng-deep .textarea-content__code-field { + height: 20vh !important; + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.ts new file mode 100644 index 000000000000..534bc9b043fd --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component.ts @@ -0,0 +1,67 @@ +import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'dot-loop-editor', + templateUrl: './dot-loop-editor.component.html', + styleUrls: ['./dot-loop-editor.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DotLoopEditorComponent), + multi: true + } + ] +}) +export class DotLoopEditorComponent implements ControlValueAccessor, OnInit { + @Input() isEditorVisible = false; + @Output() buttonClick = new EventEmitter(); + + public readonly loopControl = new FormControl(''); + + constructor() { + // + } + + public ngOnInit(): void { + this.loopControl.valueChanges.subscribe((fieldVal) => { + this._onChange(fieldVal); + this.onTouched(); + }); + } + + public writeValue(value: string | null): void { + value = value ?? ''; + this.loopControl.setValue(value); + } + + public setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.loopControl.disable(); + } else { + this.loopControl.enable(); + } + } + + private _onChange = (_value: string | null) => undefined; + + public registerOnChange(fn: (value: string | null) => void): void { + this._onChange = fn; + } + + public onTouched = () => undefined; + + public registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + /** + * This method shows the Loop Editor Input + * + * @return void + * @memberof DotLoopEditorComponent + */ + handleClick(): void { + this.buttonClick.emit(); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.module.ts new file mode 100644 index 000000000000..53f867378953 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { DotLoopEditorComponent } from '@portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.component'; +import { DotTextareaContentModule } from '@components/_common/dot-textarea-content/dot-textarea-content.module'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; +import { ReactiveFormsModule } from '@angular/forms'; + +@NgModule({ + declarations: [DotLoopEditorComponent], + imports: [ + DotTextareaContentModule, + DotMessagePipeModule, + CommonModule, + ButtonModule, + ReactiveFormsModule + ], + exports: [DotLoopEditorComponent] +}) +export class DotLoopEditorModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.spec.component.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.spec.component.ts new file mode 100644 index 000000000000..102ca05d7af3 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-loop-editor/dot-loop-editor.spec.component.ts @@ -0,0 +1,23 @@ +import { DotLoopEditorComponent } from './dot-loop-editor.component'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DotMessagePipeModule } from '@pipes/dot-message/dot-message-pipe.module'; + +describe('DotLoopEditorComponent', () => { + let component: DotLoopEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DotLoopEditorComponent], + imports: [DotMessagePipeModule] + }).compileComponents(); + + fixture = TestBed.createComponent(DotLoopEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.spec.ts new file mode 100644 index 000000000000..2bb0a66e98c0 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.spec.ts @@ -0,0 +1,58 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { DotContainerEditResolver } from './dot-container-edit.resolver'; +import { DotRouterService } from '@services/dot-router/dot-router.service'; +import { MockDotRouterService } from '@tests/dot-router-service.mock'; +import { DotContainersService } from '@dotcms/app/api/services/dot-containers/dot-containers.service'; + +describe('DotContainerService', () => { + let service: DotContainerEditResolver; + let containersService: DotContainersService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DotContainerEditResolver, + { provide: DotRouterService, useClass: MockDotRouterService }, + { + provide: DotContainersService, + useValue: { + getById: jasmine.createSpy().and.returnValue( + of({ + this: { + is: 'a page' + } + }) + ), + getFiltered: () => { + // + } + } + } + ] + }); + service = TestBed.inject(DotContainerEditResolver); + containersService = TestBed.inject(DotContainersService); + }); + + it('should return page by id from router', (done) => { + service + .resolve( + { + paramMap: { + get(param) { + return param === 'inode' ? null : 'ID'; + } + } + } as any, + null + ) + .subscribe((res) => { + expect(containersService.getById).toHaveBeenCalledWith('ID', 'working', true); + expect(res).toEqual({ this: { is: 'a page' } }); + done(); + }); + }); +}); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.ts new file mode 100644 index 000000000000..0fb234aa028c --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/resolvers/dot-container-edit.resolver.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; + +import { DotContainerEntity } from '@models/container/dot-container.model'; +import { DotContainersService } from '@services/dot-containers/dot-containers.service'; + +@Injectable() +export class DotContainerEditResolver implements Resolve { + constructor(private service: DotContainersService) {} + + resolve( + route: ActivatedRouteSnapshot, + _state: RouterStateSnapshot + ): Observable { + return this.service.getById(route.paramMap.get('id'), 'working', true); + } +} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers-routing.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers-routing.module.ts new file mode 100644 index 000000000000..d61673a71aa8 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers-routing.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DotContainerEditResolver } from './dot-container-create/resolvers/dot-container-edit.resolver'; + +const routes: Routes = [ + { + path: '', + loadChildren: () => + import('./container-list/container-list.module').then((m) => m.ContainerListModule) + }, + { + path: 'create', + loadChildren: () => + import('./dot-container-create/dot-container-create.module').then( + (m) => m.DotContainerCreateModule + ) + }, + { + path: 'edit/:id', + loadChildren: () => + import('./dot-container-create/dot-container-create.module').then( + (m) => m.DotContainerCreateModule + ), + resolve: { + container: DotContainerEditResolver + } + } +]; + +@NgModule({ + declarations: [], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [DotContainerEditResolver] +}) +export class DotContainersRoutingModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers.module.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers.module.ts new file mode 100644 index 000000000000..dc2292a9a9a7 --- /dev/null +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-containers.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DotContainersRoutingModule } from './dot-containers-routing.module'; +import { DotContainersService } from '@dotcms/app/api/services/dot-containers/dot-containers.service'; + +@NgModule({ + imports: [CommonModule, DotContainersRoutingModule], + providers: [DotContainersService] +}) +export class DotContainersModule {} diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-favorite-page/dot-favorite-page.component.spec.ts b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-favorite-page/dot-favorite-page.component.spec.ts index fbd68e543712..0d2d69271e4f 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-favorite-page/dot-favorite-page.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-favorite-page/dot-favorite-page.component.spec.ts @@ -326,11 +326,13 @@ describe('DotFavoritePageComponent', () => { fixture.detectChanges(); expect(dialogRef.close).toHaveBeenCalledWith(true); }); - + it('should call onSave ref event when actionState event is executed from store with Saved value', () => { - spyOnProperty(store, 'actionState$', 'get').and.returnValue(of(DotFavoritePageActionState.SAVED)); + spyOnProperty(store, 'actionState$', 'get').and.returnValue( + of(DotFavoritePageActionState.SAVED) + ); fixture.detectChanges(); expect(dialogConfig.data.onSave).toHaveBeenCalledTimes(1); }); - }) + }); }); diff --git a/core-web/apps/dotcms-ui/src/app/portlets/dot-templates/dot-template-list/components/dot-template-selector/dot-template-selector.component.html b/core-web/apps/dotcms-ui/src/app/portlets/dot-templates/dot-template-list/components/dot-template-selector/dot-template-selector.component.html index 758b9fdb64c4..14a69ea9c3ed 100644 --- a/core-web/apps/dotcms-ui/src/app/portlets/dot-templates/dot-template-list/components/dot-template-selector/dot-template-selector.component.html +++ b/core-web/apps/dotcms-ui/src/app/portlets/dot-templates/dot-template-list/components/dot-template-selector/dot-template-selector.component.html @@ -2,11 +2,11 @@
-
+
@@ -97,9 +97,9 @@ formControlName="environment" >
-
+
diff --git a/core-web/apps/dotcms-ui/src/app/view/components/_common/iframe/service/dot-iframe-porlet-legacy-resolver.service.spec.ts b/core-web/apps/dotcms-ui/src/app/view/components/_common/iframe/service/dot-iframe-porlet-legacy-resolver.service.spec.ts index c672a1c1bf87..61a718e5219c 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/_common/iframe/service/dot-iframe-porlet-legacy-resolver.service.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/_common/iframe/service/dot-iframe-porlet-legacy-resolver.service.spec.ts @@ -31,39 +31,37 @@ describe('DotIframePorletLegacyResolver', () => { let resolver: DotIframePortletLegacyResolver; let dotLicenseService: DotLicenseService; - beforeEach( - waitForAsync(() => { - const testbed = DOTTestBed.configureTestingModule({ - providers: [ - DotPageStateService, - DotIframePortletLegacyResolver, - DotPageRenderService, - DotContentletLockerService, - DotLicenseService, - DotESContentService, - { - provide: ActivatedRouteSnapshot, - useValue: route - }, - { - provide: RouterStateSnapshot, - useValue: state - }, - { - provide: LoginService, - useClass: LoginServiceMock - } - ], - imports: [RouterTestingModule] - }); + beforeEach(waitForAsync(() => { + const testbed = DOTTestBed.configureTestingModule({ + providers: [ + DotPageStateService, + DotIframePortletLegacyResolver, + DotPageRenderService, + DotContentletLockerService, + DotLicenseService, + DotESContentService, + { + provide: ActivatedRouteSnapshot, + useValue: route + }, + { + provide: RouterStateSnapshot, + useValue: state + }, + { + provide: LoginService, + useClass: LoginServiceMock + } + ], + imports: [RouterTestingModule] + }); - dotPageStateService = testbed.get(DotPageStateService); - dotPageStateServiceRequestPageSpy = spyOn(dotPageStateService, 'requestPage'); - resolver = testbed.get(DotIframePortletLegacyResolver); - dotLicenseService = testbed.get(DotLicenseService); - state.url = '/rules'; - }) - ); + dotPageStateService = testbed.get(DotPageStateService); + dotPageStateServiceRequestPageSpy = spyOn(dotPageStateService, 'requestPage'); + resolver = testbed.get(DotIframePortletLegacyResolver); + dotLicenseService = testbed.get(DotLicenseService); + state.url = '/rules'; + })); it('should return if user can access url to be rendered with current license', () => { const mock = new DotPageRenderState(mockUser(), new DotPageRender(mockDotRenderedPage())); diff --git a/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.html b/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.html index 4c96e2da7493..1d97f8cea655 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.html +++ b/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.html @@ -1,4 +1,4 @@ - + - +
-
+
{ it('should allow keyboad nav on filter Input - ArrowDown', () => { hostFixture.detectChanges(); const searchInput = de.query(By.css('[data-testid="searchInput"]')); - const keyboardEvent = new KeyboardEvent('keyup', {key: 'ArrowDown'}); + const keyboardEvent = new KeyboardEvent('keyup', { key: 'ArrowDown' }); searchInput.nativeElement.dispatchEvent(keyboardEvent); expect(comp.selectedOptionIndex).toBe(1); @@ -488,10 +488,10 @@ describe('SearchableDropdownComponent', () => { it('should allow keyboad nav on filter Input - ArrowUp', () => { comp.selectedOptionIndex = 3; - + hostFixture.detectChanges(); const searchInput = de.query(By.css('[data-testid="searchInput"]')); - const keyboardEvent = new KeyboardEvent('keyup', {key: 'ArrowUp'}); + const keyboardEvent = new KeyboardEvent('keyup', { key: 'ArrowUp' }); searchInput.nativeElement.dispatchEvent(keyboardEvent); expect(comp.selectedOptionIndex).toBe(2); @@ -501,13 +501,13 @@ describe('SearchableDropdownComponent', () => { it('should allow keyboad nav on filter Input - Enter', () => { comp.selectedOptionIndex = 3; spyOn(comp, 'handleClick'); - + hostFixture.detectChanges(); const searchInput = de.query(By.css('[data-testid="searchInput"]')); - const keyboardEvent = new KeyboardEvent('keyup', {key: 'Enter'}); + const keyboardEvent = new KeyboardEvent('keyup', { key: 'Enter' }); searchInput.nativeElement.dispatchEvent(keyboardEvent); - expect(comp.handleClick).toHaveBeenCalledWith(data[3]) + expect(comp.handleClick).toHaveBeenCalledWith(data[3]); }); it('should render external listItem template', () => { diff --git a/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.ts b/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.ts index 1113aad67192..de0f692ffe35 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/_common/searchable-dropdown/component/searchable-dropdown.component.ts @@ -204,7 +204,6 @@ export class SearchableDropdownComponent }); } - /** * Emits hide event and clears any value on filter's input * diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.spec.ts b/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.spec.ts index 6d40e2e4eaa3..f9ec3591f3fb 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.spec.ts @@ -32,9 +32,7 @@ class DotMessageMockPipe implements PipeTransform { } @Component({ - template: `
Hello World
` + template: `
Hello World
` }) class TestHostComponent {} diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.ts b/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.ts index a5aea166dcfb..4922716de287 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-form-dialog/dot-form-dialog.component.ts @@ -1,4 +1,12 @@ -import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { + Component, + ElementRef, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output +} from '@angular/core'; import { DynamicDialogRef } from 'primeng/dynamicdialog'; import { fromEvent, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -21,8 +29,7 @@ export class DotFormDialogComponent implements OnInit, OnDestroy { @Output() cancel: EventEmitter = new EventEmitter(null); - constructor(private dynamicDialog: DynamicDialogRef, - private el: ElementRef) {} + constructor(private dynamicDialog: DynamicDialogRef, private el: ElementRef) {} ngOnInit(): void { const content = document.querySelector('p-dynamicdialog .p-dialog-content'); diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.html b/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.html index ea14da5c840c..93d2f56295d5 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.html +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.html @@ -1,9 +1,10 @@ + @@ -48,22 +49,22 @@
{{ 'No-Results-Found' | dm }} @@ -101,7 +102,7 @@ -
+
{{ col.textContent || rowData[col.fieldName] }}
@@ -109,10 +110,10 @@ {{ rowData[col.fieldName] }} {{ col.textContent @@ -123,9 +124,10 @@ diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.ts b/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.ts index b6b282e08838..34bf1f84165c 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-listing-data-table/dot-listing-data-table.component.ts @@ -48,7 +48,7 @@ export class DotListingDataTableComponent implements OnInit { @Input() multipleSelection = false; @Input() paginationPerPage = 40; @Input() paginatorExtraParams: { [key: string]: string } = {}; - @Input() actions: DotActionMenuItem[]; + @Input() actions: DotActionMenuItem[] = []; @Input() dataKey = ''; @Input() checkbox = false; @Input() mapItems: []>(item: T) => T; @@ -65,6 +65,7 @@ export class DotListingDataTableComponent implements OnInit { @ContentChildren(PrimeTemplate) templates: QueryList; @ContentChild('rowTemplate') rowTemplate: TemplateRef; + @ContentChild('beforeSearchTemplate') beforeSearchTemplate: TemplateRef; @ContentChild('headerTemplate') headerTemplate: TemplateRef; readonly DATE_FORMAT = 'date'; @@ -111,6 +112,16 @@ export class DotListingDataTableComponent implements OnInit { this.handleRowCheck(); } + /** + * It clears the global search filter and reloads the current page + * @memberof DotListingDataTableComponent + */ + clearGlobalSearch(): void { + this.filter = ''; + this.paginatorService.filter = ''; + this.loadCurrentPage(); + } + /** * Emit selected row * @param {any} rowData diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-persona-selector/dot-persona-selector.component.html b/core-web/apps/dotcms-ui/src/app/view/components/dot-persona-selector/dot-persona-selector.component.html index bdf6d417fa2b..4b181a740d47 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-persona-selector/dot-persona-selector.component.html +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-persona-selector/dot-persona-selector.component.html @@ -1,18 +1,18 @@ - + { const siteServiceMock = new SiteServiceMock(); - beforeEach( - waitForAsync(() => { - DOTTestBed.configureTestingModule({ - declarations: [DotPersonaSelectorComponent, HostTestComponent], - imports: [ - BrowserAnimationsModule, - SearchableDropDownModule, - DotPersonaSelectedItemModule, - DotPersonaSelectorOptionModule, - DotAddPersonaDialogModule, - TooltipModule, - DotPipesModule - ], - providers: [ - IframeOverlayService, - { - provide: DotMessageService, - useValue: messageServiceMock - }, - { provide: PaginatorService, useClass: TestPaginatorService }, - { provide: LoginService, useClass: LoginServiceMock }, - { provide: SiteService, useValue: siteServiceMock } - ] - }); - }) - ); + beforeEach(waitForAsync(() => { + DOTTestBed.configureTestingModule({ + declarations: [DotPersonaSelectorComponent, HostTestComponent], + imports: [ + BrowserAnimationsModule, + SearchableDropDownModule, + DotPersonaSelectedItemModule, + DotPersonaSelectorOptionModule, + DotAddPersonaDialogModule, + TooltipModule, + DotPipesModule + ], + providers: [ + IframeOverlayService, + { + provide: DotMessageService, + useValue: messageServiceMock + }, + { provide: PaginatorService, useClass: TestPaginatorService }, + { provide: LoginService, useClass: LoginServiceMock }, + { provide: SiteService, useValue: siteServiceMock } + ] + }); + })); beforeEach(() => { hostFixture = DOTTestBed.createComponent(HostTestComponent); diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.html b/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.html index 76c0d22f1227..326da53a420b 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.html +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.html @@ -1,4 +1,4 @@ - +
@@ -55,19 +55,19 @@ diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.spec.ts b/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.spec.ts index ed80e5e4aa16..00fe9bd24d5f 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.spec.ts +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-theme-selector-dropdown/dot-theme-selector-dropdown.component.spec.ts @@ -3,7 +3,12 @@ import { DebugElement, Input } from '@angular/core'; import { Component } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { UntypedFormBuilder, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { + UntypedFormBuilder, + UntypedFormGroup, + FormsModule, + ReactiveFormsModule +} from '@angular/forms'; import { of } from 'rxjs'; import { SiteService } from '@dotcms/dotcms-js'; @@ -236,7 +241,6 @@ describe('DotThemeSelectorDropdownComponent', () => { const searchableButton = de.query(By.css('dot-searchable-dropdown button')); searchableButton.nativeElement.click(); - }); it('should system to true', () => { @@ -274,7 +278,7 @@ describe('DotThemeSelectorDropdownComponent', () => { fixture.detectChanges(); await fixture.whenStable(); const input = de.query(By.css('[data-testId="searchInput"]')).nativeElement; - const event = new KeyboardEvent('keyup', {key: 'ArrowDown'}); + const event = new KeyboardEvent('keyup', { key: 'ArrowDown' }); input.dispatchEvent(event); await fixture.whenStable(); expect(component.selectedOptionIndex).toBe(1); @@ -285,7 +289,7 @@ describe('DotThemeSelectorDropdownComponent', () => { fixture.detectChanges(); await fixture.whenStable(); const input = de.query(By.css('[data-testId="searchInput"]')).nativeElement; - const event = new KeyboardEvent('keyup', {key: 'ArrowUp'}); + const event = new KeyboardEvent('keyup', { key: 'ArrowUp' }); input.dispatchEvent(event); await fixture.whenStable(); expect(component.selectedOptionIndex).toBe(0); @@ -297,11 +301,10 @@ describe('DotThemeSelectorDropdownComponent', () => { fixture.detectChanges(); await fixture.whenStable(); const input = de.query(By.css('[data-testId="searchInput"]')).nativeElement; - const event = new KeyboardEvent('keyup', {key: 'Enter'}); + const event = new KeyboardEvent('keyup', { key: 'Enter' }); input.dispatchEvent(event); await fixture.whenStable(); - expect(component.onChange).toHaveBeenCalledWith(mockDotThemes[0]) - + expect(component.onChange).toHaveBeenCalledWith(mockDotThemes[0]); }); }); }); diff --git a/core-web/apps/dotcms-ui/src/app/view/pipes/dot-message/dot-message-pipe.module.ts b/core-web/apps/dotcms-ui/src/app/view/pipes/dot-message/dot-message-pipe.module.ts index a910bf0620fd..9ccf98422048 100644 --- a/core-web/apps/dotcms-ui/src/app/view/pipes/dot-message/dot-message-pipe.module.ts +++ b/core-web/apps/dotcms-ui/src/app/view/pipes/dot-message/dot-message-pipe.module.ts @@ -4,7 +4,7 @@ import { DotMessagePipe } from './dot-message.pipe'; @NgModule({ imports: [CommonModule], - declarations: [DotMessagePipe], - exports: [DotMessagePipe] + declarations: [DotMessagePipe, DotMessagePipe], + exports: [DotMessagePipe, DotMessagePipe] }) export class DotMessagePipeModule {} diff --git a/core-web/libs/block-editor/src/lib/extensions/bubble-link-form/bubble-link-form.component.html b/core-web/libs/block-editor/src/lib/extensions/bubble-link-form/bubble-link-form.component.html index 9639e46af9a2..edeaf28916a5 100644 --- a/core-web/libs/block-editor/src/lib/extensions/bubble-link-form/bubble-link-form.component.html +++ b/core-web/libs/block-editor/src/lib/extensions/bubble-link-form/bubble-link-form.component.html @@ -1,28 +1,28 @@
@@ -39,8 +39,8 @@
diff --git a/core-web/libs/dot-primeng-theme-styles/src/scss/dotcms-theme/components/_paginator.scss b/core-web/libs/dot-primeng-theme-styles/src/scss/dotcms-theme/components/_paginator.scss index 050e96d2de39..29b65449667f 100644 --- a/core-web/libs/dot-primeng-theme-styles/src/scss/dotcms-theme/components/_paginator.scss +++ b/core-web/libs/dot-primeng-theme-styles/src/scss/dotcms-theme/components/_paginator.scss @@ -83,3 +83,7 @@ border-color: transparent; color: $text-color-hover; } + +.p-paginator-bottom { + border: none; +} diff --git a/core-web/libs/dotcms-webcomponents/src/components.d.ts b/core-web/libs/dotcms-webcomponents/src/components.d.ts index 4d30bd8d9aaf..d919592bd9ee 100644 --- a/core-web/libs/dotcms-webcomponents/src/components.d.ts +++ b/core-web/libs/dotcms-webcomponents/src/components.d.ts @@ -4,1097 +4,1112 @@ * This is an autogenerated file created by the Stencil compiler. * It contains typing information for all components that exist in this project. */ -import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; -import { DotCMSContentlet, DotCMSContentTypeLayoutColumn, DotCMSContentTypeLayoutRow, DotContentState, DotHttpErrorResponse } from "@dotcms/dotcms-models"; -import { DotBinaryFileEvent, DotFieldStatusEvent, DotFieldValueEvent, DotInputCalendarStatusEvent, DotKeyValueField } from "./models"; -import { DotCardContentletEvent, DotCardContentletItem } from "./models/dot-card-contentlet.model"; -import { DotContentletItem } from "./models/dot-contentlet-item.model"; -import { DotContextMenuOption } from "./models/dot-context-menu.model"; -import { DotContextMenuAction } from "./models/dot-context-menu-action.model"; -import { DotSelectButtonOption } from "./models/dotSelectButtonOption"; +import { HTMLStencilElement, JSXBase } from '@stencil/core/internal'; +import { + DotCMSContentlet, + DotCMSContentTypeLayoutColumn, + DotCMSContentTypeLayoutRow, + DotContentState, + DotHttpErrorResponse +} from '@dotcms/dotcms-models'; +import { + DotBinaryFileEvent, + DotFieldStatusEvent, + DotFieldValueEvent, + DotInputCalendarStatusEvent, + DotKeyValueField +} from './models'; +import { DotCardContentletEvent, DotCardContentletItem } from './models/dot-card-contentlet.model'; +import { DotContentletItem } from './models/dot-contentlet-item.model'; +import { DotContextMenuOption } from './models/dot-context-menu.model'; +import { DotContextMenuAction } from './models/dot-context-menu-action.model'; +import { DotSelectButtonOption } from './models/dotSelectButtonOption'; export namespace Components { interface DotAssetDropZone { /** - * Allowed file extensions + * Allowed file extensions */ - "acceptTypes": string[]; + acceptTypes: string[]; /** - * Legend to be shown when creating dotAssets + * Legend to be shown when creating dotAssets */ - "createAssetsText": string; - "customUploadFiles": (props: { - files: File[], - onSuccess: () => void, - updateProgress: (progress: number) => void, - onError: (header: string, message: string) => void - }) => Promise; + createAssetsText: string; + customUploadFiles: (props: { + files: File[]; + onSuccess: () => void; + updateProgress: (progress: number) => void; + onError: (header: string, message: string) => void; + }) => Promise; /** - * Labels to be shown in error dialog + * Labels to be shown in error dialog */ - "dialogLabels": { closeButton: string; uploadErrorHeader: string; dotAssetErrorHeader: string; errorHeader: string; }; - "displayIndicator": boolean; + dialogLabels: { + closeButton: string; + uploadErrorHeader: string; + dotAssetErrorHeader: string; + errorHeader: string; + }; + displayIndicator: boolean; /** - * URL to endpoint to create dotAssets + * URL to endpoint to create dotAssets */ - "dotAssetsURL": string; + dotAssetsURL: string; /** - * Legend to be shown when dropping files + * Legend to be shown when dropping files */ - "dropFilesText": string; + dropFilesText: string; /** - * Specify the the folder where the dotAssets will be placed + * Specify the the folder where the dotAssets will be placed */ - "folder": string; + folder: string; /** - * Specify the max size of each file to be uploaded + * Specify the max size of each file to be uploaded */ - "maxFileSize": string; + maxFileSize: string; /** - * Error to be shown when try to upload a bigger size file than allowed + * Error to be shown when try to upload a bigger size file than allowed */ - "multiMaxSizeErrorLabel": string; + multiMaxSizeErrorLabel: string; /** - * Error to be shown when try to upload a bigger size file than allowed + * Error to be shown when try to upload a bigger size file than allowed */ - "singeMaxSizeErrorLabel": string; + singeMaxSizeErrorLabel: string; /** - * Allowed file extensions + * Allowed file extensions */ - "typesErrorLabel": string; + typesErrorLabel: string; /** - * Error to be shown when an error happened on the uploading process + * Error to be shown when an error happened on the uploading process */ - "uploadErrorLabel": string; + uploadErrorLabel: string; /** - * Legend to be shown when uploading files + * Legend to be shown when uploading files */ - "uploadFileText": string; + uploadFileText: string; } interface DotAutocomplete { /** - * Function or array of string to get the data to use for the autocomplete search + * Function or array of string to get the data to use for the autocomplete search */ - "data": () => Promise | string[]; + data: () => Promise | string[]; /** - * (optional) Duraction in ms to start search into the autocomplete + * (optional) Duraction in ms to start search into the autocomplete */ - "debounce": number; + debounce: number; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Max results to show after a autocomplete search + * (optional) Max results to show after a autocomplete search */ - "maxResults": number; + maxResults: number; /** - * (optional) text to show when no value is set + * (optional) text to show when no value is set */ - "placeholder": string; + placeholder: string; /** - * (optional) Min characters to start search in the autocomplete input + * (optional) Min characters to start search in the autocomplete input */ - "threshold": number; + threshold: number; } interface DotBadge { - "bgColor": string; - "bordered": boolean; - "color": string; - "size": string; + bgColor: string; + bordered: boolean; + color: string; + size: string; } interface DotBinaryFile { /** - * (optional) Text that be shown when the URL is not valid + * (optional) Text that be shown when the URL is not valid */ - "URLValidationMessage": string; + URLValidationMessage: string; /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept": string; + accept: string; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "buttonLabel": string; + buttonLabel: string; /** - * Clear value of selected file, when the endpoint fails. + * Clear value of selected file, when the endpoint fails. */ - "clearValue": () => Promise; + clearValue: () => Promise; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "errorMessage": string; + errorMessage: string; /** - * (optional) Text that be shown when the file size is not valid + * (optional) Text that be shown when the file size is not valid */ - "fileSizeValidationMessage": string; + fileSizeValidationMessage: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) Set the max file size limit + * (optional) Set the max file size limit */ - "maxFileLength": string; + maxFileLength: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder": string; + placeholder: string; /** - * (optional) Name of the file uploaded + * (optional) Name of the file uploaded */ - "previewImageName": string; + previewImageName: string; /** - * (optional) URL of the file uploaded + * (optional) URL of the file uploaded */ - "previewImageUrl": string; + previewImageUrl: string; /** - * (optional) Determine if it is required + * (optional) Determine if it is required */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage": string; + validationMessage: string; } interface DotBinaryFilePreview { /** - * (optional) Delete button's label + * (optional) Delete button's label */ - "deleteLabel": string; + deleteLabel: string; /** - * file name to be displayed + * file name to be displayed */ - "fileName": string; + fileName: string; /** - * (optional) file URL to be displayed + * (optional) file URL to be displayed */ - "previewUrl": string; + previewUrl: string; } interface DotBinaryTextField { /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept": string; + accept: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder": string; + placeholder: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * Value specifies the value of the element + * Value specifies the value of the element */ - "value": any; + value: any; } interface DotBinaryUploadButton { /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept": string; + accept: string; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "buttonLabel": string; + buttonLabel: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Set the max file size limit + * (optional) Set the max file size limit */ - "maxFileLength": string; + maxFileLength: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; - } - interface DotCard { + required: boolean; } + interface DotCard {} interface DotCardContentlet { - "checked": boolean; - "hideMenu": () => Promise; - "iconSize": string; - "item": DotCardContentletItem; - "showMenu": (x: number, y: number) => Promise; - "thumbnailSize": string; + checked: boolean; + hideMenu: () => Promise; + iconSize: string; + item: DotCardContentletItem; + showMenu: (x: number, y: number) => Promise; + thumbnailSize: string; } interface DotCardView { - "clearValue": () => Promise; - "getValue": () => Promise; - "items": DotCardContentletItem[]; - "value": string; + clearValue: () => Promise; + getValue: () => Promise; + items: DotCardContentletItem[]; + value: string; } interface DotCheckbox { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * Value/Label checkbox options separated by comma, to be formatted as: Value|Label + * Value/Label checkbox options separated by comma, to be formatted as: Value|Label */ - "options": string; + options: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. - * @memberof DotSelectComponent + * Reset properties of the field, clear value and emit events. + * @memberof DotSelectComponent */ - "reset": () => Promise; + reset: () => Promise; /** - * Value set from the checkbox option + * Value set from the checkbox option */ - "value": string; + value: string; } interface DotChip { /** - * (optional) Delete button's label + * (optional) Delete button's label */ - "deleteLabel": string; + deleteLabel: string; /** - * (optional) If is true disabled the delete button + * (optional) If is true disabled the delete button */ - "disabled": boolean; + disabled: boolean; /** - * Chip's label + * Chip's label */ - "label": string; + label: string; } interface DotContentletIcon { - "icon": string; - "size": string; + icon: string; + size: string; } interface DotContentletLockIcon { - "locked": boolean; - "size": string; + locked: boolean; + size: string; } interface DotContentletThumbnail { - "alt": string; - "contentlet": DotContentletItem; - "height": string; - "iconSize": string; - "width": string; + alt: string; + contentlet: DotContentletItem; + height: string; + iconSize: string; + width: string; } interface DotContextMenu { - "fontSize": string; - "hide": () => Promise; - "options": DotContextMenuOption[]; - "show": (x: number, y: number, position?: string) => Promise; + fontSize: string; + hide: () => Promise; + options: DotContextMenuOption[]; + show: (x: number, y: number, position?: string) => Promise; } interface DotDataViewButton { - "value": string; + value: string; } interface DotDate { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd + * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd */ - "max": string; + max: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd + * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd */ - "min": string; + min: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step": string; + step: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage": string; + validationMessage: string; /** - * Value format yyyy-mm-dd e.g., 2005-12-01 + * Value format yyyy-mm-dd e.g., 2005-12-01 */ - "value": string; + value: string; } interface DotDateRange { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Date format used by the field when displayed + * (optional) Date format used by the field when displayed */ - "displayFormat": string; + displayFormat: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) Max value that the field will allow to set + * (optional) Max value that the field will allow to set */ - "max": string; + max: string; /** - * (optional) Min value that the field will allow to set + * (optional) Min value that the field will allow to set */ - "min": string; + min: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Text to be rendered next to presets field + * (optional) Text to be rendered next to presets field */ - "presetLabel": string; + presetLabel: string; /** - * (optional) Array of date presets formatted as [{ label: 'PRESET_LABEL', days: NUMBER }] + * (optional) Array of date presets formatted as [{ label: 'PRESET_LABEL', days: NUMBER }] */ - "presets": { label: string; days: number; }[]; + presets: { label: string; days: number }[]; /** - * (optional) Determine if it is needed + * (optional) Determine if it is needed */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Value formatted with start and end date splitted with a comma + * (optional) Value formatted with start and end date splitted with a comma */ - "value": string; + value: string; } interface DotDateTime { /** - * (optional) The string to use in the date label field + * (optional) The string to use in the date label field */ - "dateLabel": string; + dateLabel: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss + * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss */ - "max": string; + max: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss + * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss */ - "min": string; + min: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the filed, clear value and emit events. + * Reset properties of the filed, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Step specifies the legal number intervals for the input fields date && time e.g., 2,10 + * (optional) Step specifies the legal number intervals for the input fields date && time e.g., 2,10 */ - "step": string; + step: string; /** - * (optional) The string to use in the time label field + * (optional) The string to use in the time label field */ - "timeLabel": string; + timeLabel: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage": string; + validationMessage: string; /** - * Value format yyyy-mm-dd hh:mm:ss e.g., 2005-12-01 15:22:00 + * Value format yyyy-mm-dd hh:mm:ss e.g., 2005-12-01 15:22:00 */ - "value": string; - } - interface DotErrorMessage { + value: string; } + interface DotErrorMessage {} interface DotForm { /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + fieldsToShow: string; /** - * Layout metada to be rendered + * Layout metada to be rendered */ - "layout": DotCMSContentTypeLayoutRow[]; + layout: DotCMSContentTypeLayoutRow[]; /** - * (optional) Text to be rendered on Reset button + * (optional) Text to be rendered on Reset button */ - "resetLabel": string; + resetLabel: string; /** - * (optional) Text to be rendered on Submit button + * (optional) Text to be rendered on Submit button */ - "submitLabel": string; + submitLabel: string; /** - * Content type variable name + * Content type variable name */ - "variable": string; + variable: string; } interface DotFormColumn { /** - * Fields metada to be rendered + * Fields metada to be rendered */ - "column": DotCMSContentTypeLayoutColumn; + column: DotCMSContentTypeLayoutColumn; /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + fieldsToShow: string; } interface DotFormRow { /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow": string; + fieldsToShow: string; /** - * Fields metada to be rendered + * Fields metada to be rendered */ - "row": DotCMSContentTypeLayoutRow; + row: DotCMSContentTypeLayoutRow; } interface DotHtmlToImage { - "height": string; - "value": string; - "width": string; + height: string; + value: string; + width: string; } interface DotInputCalendar { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Max, maximum value that the field will allow to set, expect a Date Format + * (optional) Max, maximum value that the field will allow to set, expect a Date Format */ - "max": string; + max: string; /** - * (optional) Min, minimum value that the field will allow to set, expect a Date Format. + * (optional) Min, minimum value that the field will allow to set, expect a Date Format. */ - "min": string; + min: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step": string; + step: string; /** - * type specifies the type of input element to display + * type specifies the type of input element to display */ - "type": string; + type: string; /** - * Value specifies the value of the input element + * Value specifies the value of the input element */ - "value": string; + value: string; } interface DotKeyValue { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "duplicatedKeyMessage": string; + duplicatedKeyMessage: string; /** - * (optional) Label for the add button in the key-value-form + * (optional) Label for the add button in the key-value-form */ - "formAddButtonLabel": string; + formAddButtonLabel: string; /** - * (optional) The string to use in the key label in the key-value-form + * (optional) The string to use in the key label in the key-value-form */ - "formKeyLabel": string; + formKeyLabel: string; /** - * (optional) Placeholder for the key input text in the key-value-form + * (optional) Placeholder for the key input text in the key-value-form */ - "formKeyPlaceholder": string; + formKeyPlaceholder: string; /** - * (optional) The string to use in the value label in the key-value-form + * (optional) The string to use in the value label in the key-value-form */ - "formValueLabel": string; + formValueLabel: string; /** - * (optional) Placeholder for the value input text in the key-value-form + * (optional) Placeholder for the value input text in the key-value-form */ - "formValuePlaceholder": string; + formValuePlaceholder: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) The string to use in the delete button of a key/value item + * (optional) The string to use in the delete button of a key/value item */ - "listDeleteLabel": string; + listDeleteLabel: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Allows unique keys only + * (optional) Allows unique keys only */ - "uniqueKeys": boolean; + uniqueKeys: boolean; /** - * Value of the field + * Value of the field */ - "value": string; + value: string; /** - * (optional) The string containing the value to be parsed for whitelist key/value + * (optional) The string containing the value to be parsed for whitelist key/value */ - "whiteList": string; + whiteList: string; /** - * (optional) The string to use in the empty option of whitelist dropdown key/value item + * (optional) The string to use in the empty option of whitelist dropdown key/value item */ - "whiteListEmptyOptionLabel": string; + whiteListEmptyOptionLabel: string; } interface DotLabel { /** - * (optional) Text to be rendered + * (optional) Text to be rendered */ - "label": string; + label: string; /** - * (optional) Field name + * (optional) Field name */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; } interface DotMaterialIconPicker { /** - * Label set for the input color + * Label set for the input color */ - "colorLabel": string; + colorLabel: string; /** - * Color value set from the input + * Color value set from the input */ - "colorValue": string; + colorValue: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * Value for input placeholder + * Value for input placeholder */ - "placeholder": string; + placeholder: string; /** - * Show/Hide color picker + * Show/Hide color picker */ - "showColor": string; + showColor: string; /** - * Size value set for font-size + * Size value set for font-size */ - "size": string; + size: string; /** - * Values that the auto-complete textbox should search for + * Values that the auto-complete textbox should search for */ - "suggestionlist": string[]; + suggestionlist: string[]; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value": string; + value: string; } interface DotMultiSelect { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * Value/Label dropdown options separated by comma, to be formatted as: Value|Label + * Value/Label dropdown options separated by comma, to be formatted as: Value|Label */ - "options": string; + options: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. - * @memberof DotSelectComponent + * Reset properties of the field, clear value and emit events. + * @memberof DotSelectComponent */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Size number of the multi-select dropdown (default=3) + * (optional) Size number of the multi-select dropdown (default=3) */ - "size": string; + size: string; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value": string; + value: string; } interface DotProgressBar { /** - * indicates the progress to be show, a value 1 to 100 + * indicates the progress to be show, a value 1 to 100 */ - "progress": number; + progress: number; /** - * text to be show bellow the progress bar + * text to be show bellow the progress bar */ - "text": string; + text: string; } interface DotRadio { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * Value/Label ratio options separated by comma, to be formatted as: Value|Label + * Value/Label ratio options separated by comma, to be formatted as: Value|Label */ - "options": string; + options: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * Value set from the ratio option + * Value set from the ratio option */ - "value": string; + value: string; } interface DotSelect { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * Value/Label dropdown options separated by comma, to be formatted as: Value|Label + * Value/Label dropdown options separated by comma, to be formatted as: Value|Label */ - "options": string; + options: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. - * @memberof DotSelectComponent + * Reset properties of the field, clear value and emit events. + * @memberof DotSelectComponent */ - "reset": () => Promise; + reset: () => Promise; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value": string; + value: string; } interface DotSelectButton { - "options": DotSelectButtonOption[]; - "value": string; + options: DotSelectButtonOption[]; + value: string; } interface DotStateIcon { - "labels": { archived: string; published: string; revision: string; draft: string; }; - "size": string; - "state": DotContentState; + labels: { archived: string; published: string; revision: string; draft: string }; + size: string; + state: DotContentState; } interface DotTags { /** - * Function or array of string to get the data to use for the autocomplete search + * Function or array of string to get the data to use for the autocomplete search */ - "data": () => Promise | string[]; + data: () => Promise | string[]; /** - * Duraction in ms to start search into the autocomplete + * Duraction in ms to start search into the autocomplete */ - "debounce": number; + debounce: number; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) text to show when no value is set + * (optional) text to show when no value is set */ - "placeholder": string; + placeholder: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and value is not set + * (optional) Text that be shown when required is set and value is not set */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the filed, clear value and emit events. + * Reset properties of the filed, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * Min characters to start search in the autocomplete input + * Min characters to start search in the autocomplete input */ - "threshold": number; + threshold: number; /** - * Value formatted splitted with a comma, for example: tag-1,tag-2 + * Value formatted splitted with a comma, for example: tag-1,tag-2 */ - "value": string; + value: string; } interface DotTextarea { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to textarea element + * (optional) Text to be rendered next to textarea element */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Regular expresion that is checked against the value to determine if is valid + * (optional) Regular expresion that is checked against the value to determine if is valid */ - "regexCheck": string; + regexCheck: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. - * @memberof DotTextareaComponent + * Reset properties of the field, clear value and emit events. + * @memberof DotTextareaComponent */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage": string; + validationMessage: string; /** - * Value specifies the value of the textarea element + * Value specifies the value of the textarea element */ - "value": string; + value: string; } interface DotTextfield { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder": string; + placeholder: string; /** - * (optional) Regular expresion that is checked against the value to determine if is valid + * (optional) Regular expresion that is checked against the value to determine if is valid */ - "regexCheck": string; + regexCheck: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * type specifies the type of input element to display + * type specifies the type of input element to display */ - "type": string; + type: string; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage": string; + validationMessage: string; /** - * Value specifies the value of the input element + * Value specifies the value of the input element */ - "value": string; + value: string; } interface DotTime { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint": string; + hint: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label": string; + label: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be hh:mm:ss + * (optional) Max, maximum value that the field will allow to set. Format should be hh:mm:ss */ - "max": string; + max: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be hh:mm:ss + * (optional) Min, minimum value that the field will allow to set. Format should be hh:mm:ss */ - "min": string; + min: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name": string; + name: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required": boolean; + required: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage": string; + requiredMessage: string; /** - * Reset properties of the field, clear value and emit events. + * Reset properties of the field, clear value and emit events. */ - "reset": () => Promise; + reset: () => Promise; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step": string; + step: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage": string; + validationMessage: string; /** - * Value format hh:mm:ss e.g., 15:22:00 + * Value format hh:mm:ss e.g., 15:22:00 */ - "value": string; + value: string; } interface DotTooltip { - "content": string; - "delay": number; - "for": string; - "position": string; + content: string; + delay: number; + for: string; + position: string; } interface KeyValueForm { /** - * (optional) Label for the add item button + * (optional) Label for the add item button */ - "addButtonLabel": string; + addButtonLabel: string; /** - * (optional) Disables all form interaction + * (optional) Disables all form interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Label for the empty option in white-list select + * (optional) Label for the empty option in white-list select */ - "emptyDropdownOptionLabel": string; + emptyDropdownOptionLabel: string; /** - * (optional) The string to use in the key input label + * (optional) The string to use in the key input label */ - "keyLabel": string; + keyLabel: string; /** - * (optional) Placeholder for the key input text + * (optional) Placeholder for the key input text */ - "keyPlaceholder": string; + keyPlaceholder: string; /** - * (optional) The string to use in the value input label + * (optional) The string to use in the value input label */ - "valueLabel": string; + valueLabel: string; /** - * (optional) Placeholder for the value input text + * (optional) Placeholder for the value input text */ - "valuePlaceholder": string; + valuePlaceholder: string; /** - * (optional) The string to use for white-list key/values + * (optional) The string to use for white-list key/values */ - "whiteList": string; + whiteList: string; } interface KeyValueTable { /** - * (optional) Label for the delete button in each item list + * (optional) Label for the delete button in each item list */ - "buttonLabel": string; + buttonLabel: string; /** - * (optional) Disables all form interaction + * (optional) Disables all form interaction */ - "disabled": boolean; + disabled: boolean; /** - * (optional) Message to show when the list of items is empty + * (optional) Message to show when the list of items is empty */ - "emptyMessage": string; + emptyMessage: string; /** - * (optional) Items to render in the list of key value + * (optional) Items to render in the list of key value */ - "items": DotKeyValueField[]; + items: DotKeyValueField[]; } } export interface DotAssetDropZoneCustomEvent extends CustomEvent { @@ -1210,1485 +1225,1501 @@ export interface KeyValueTableCustomEvent extends CustomEvent { target: HTMLKeyValueTableElement; } declare global { - interface HTMLDotAssetDropZoneElement extends Components.DotAssetDropZone, HTMLStencilElement { - } + interface HTMLDotAssetDropZoneElement extends Components.DotAssetDropZone, HTMLStencilElement {} var HTMLDotAssetDropZoneElement: { prototype: HTMLDotAssetDropZoneElement; new (): HTMLDotAssetDropZoneElement; }; - interface HTMLDotAutocompleteElement extends Components.DotAutocomplete, HTMLStencilElement { - } + interface HTMLDotAutocompleteElement extends Components.DotAutocomplete, HTMLStencilElement {} var HTMLDotAutocompleteElement: { prototype: HTMLDotAutocompleteElement; new (): HTMLDotAutocompleteElement; }; - interface HTMLDotBadgeElement extends Components.DotBadge, HTMLStencilElement { - } + interface HTMLDotBadgeElement extends Components.DotBadge, HTMLStencilElement {} var HTMLDotBadgeElement: { prototype: HTMLDotBadgeElement; new (): HTMLDotBadgeElement; }; - interface HTMLDotBinaryFileElement extends Components.DotBinaryFile, HTMLStencilElement { - } + interface HTMLDotBinaryFileElement extends Components.DotBinaryFile, HTMLStencilElement {} var HTMLDotBinaryFileElement: { prototype: HTMLDotBinaryFileElement; new (): HTMLDotBinaryFileElement; }; - interface HTMLDotBinaryFilePreviewElement extends Components.DotBinaryFilePreview, HTMLStencilElement { - } + interface HTMLDotBinaryFilePreviewElement + extends Components.DotBinaryFilePreview, + HTMLStencilElement {} var HTMLDotBinaryFilePreviewElement: { prototype: HTMLDotBinaryFilePreviewElement; new (): HTMLDotBinaryFilePreviewElement; }; - interface HTMLDotBinaryTextFieldElement extends Components.DotBinaryTextField, HTMLStencilElement { - } + interface HTMLDotBinaryTextFieldElement + extends Components.DotBinaryTextField, + HTMLStencilElement {} var HTMLDotBinaryTextFieldElement: { prototype: HTMLDotBinaryTextFieldElement; new (): HTMLDotBinaryTextFieldElement; }; - interface HTMLDotBinaryUploadButtonElement extends Components.DotBinaryUploadButton, HTMLStencilElement { - } + interface HTMLDotBinaryUploadButtonElement + extends Components.DotBinaryUploadButton, + HTMLStencilElement {} var HTMLDotBinaryUploadButtonElement: { prototype: HTMLDotBinaryUploadButtonElement; new (): HTMLDotBinaryUploadButtonElement; }; - interface HTMLDotCardElement extends Components.DotCard, HTMLStencilElement { - } + interface HTMLDotCardElement extends Components.DotCard, HTMLStencilElement {} var HTMLDotCardElement: { prototype: HTMLDotCardElement; new (): HTMLDotCardElement; }; - interface HTMLDotCardContentletElement extends Components.DotCardContentlet, HTMLStencilElement { - } + interface HTMLDotCardContentletElement + extends Components.DotCardContentlet, + HTMLStencilElement {} var HTMLDotCardContentletElement: { prototype: HTMLDotCardContentletElement; new (): HTMLDotCardContentletElement; }; - interface HTMLDotCardViewElement extends Components.DotCardView, HTMLStencilElement { - } + interface HTMLDotCardViewElement extends Components.DotCardView, HTMLStencilElement {} var HTMLDotCardViewElement: { prototype: HTMLDotCardViewElement; new (): HTMLDotCardViewElement; }; - interface HTMLDotCheckboxElement extends Components.DotCheckbox, HTMLStencilElement { - } + interface HTMLDotCheckboxElement extends Components.DotCheckbox, HTMLStencilElement {} var HTMLDotCheckboxElement: { prototype: HTMLDotCheckboxElement; new (): HTMLDotCheckboxElement; }; - interface HTMLDotChipElement extends Components.DotChip, HTMLStencilElement { - } + interface HTMLDotChipElement extends Components.DotChip, HTMLStencilElement {} var HTMLDotChipElement: { prototype: HTMLDotChipElement; new (): HTMLDotChipElement; }; - interface HTMLDotContentletIconElement extends Components.DotContentletIcon, HTMLStencilElement { - } + interface HTMLDotContentletIconElement + extends Components.DotContentletIcon, + HTMLStencilElement {} var HTMLDotContentletIconElement: { prototype: HTMLDotContentletIconElement; new (): HTMLDotContentletIconElement; }; - interface HTMLDotContentletLockIconElement extends Components.DotContentletLockIcon, HTMLStencilElement { - } + interface HTMLDotContentletLockIconElement + extends Components.DotContentletLockIcon, + HTMLStencilElement {} var HTMLDotContentletLockIconElement: { prototype: HTMLDotContentletLockIconElement; new (): HTMLDotContentletLockIconElement; }; - interface HTMLDotContentletThumbnailElement extends Components.DotContentletThumbnail, HTMLStencilElement { - } + interface HTMLDotContentletThumbnailElement + extends Components.DotContentletThumbnail, + HTMLStencilElement {} var HTMLDotContentletThumbnailElement: { prototype: HTMLDotContentletThumbnailElement; new (): HTMLDotContentletThumbnailElement; }; - interface HTMLDotContextMenuElement extends Components.DotContextMenu, HTMLStencilElement { - } + interface HTMLDotContextMenuElement extends Components.DotContextMenu, HTMLStencilElement {} var HTMLDotContextMenuElement: { prototype: HTMLDotContextMenuElement; new (): HTMLDotContextMenuElement; }; - interface HTMLDotDataViewButtonElement extends Components.DotDataViewButton, HTMLStencilElement { - } + interface HTMLDotDataViewButtonElement + extends Components.DotDataViewButton, + HTMLStencilElement {} var HTMLDotDataViewButtonElement: { prototype: HTMLDotDataViewButtonElement; new (): HTMLDotDataViewButtonElement; }; - interface HTMLDotDateElement extends Components.DotDate, HTMLStencilElement { - } + interface HTMLDotDateElement extends Components.DotDate, HTMLStencilElement {} var HTMLDotDateElement: { prototype: HTMLDotDateElement; new (): HTMLDotDateElement; }; - interface HTMLDotDateRangeElement extends Components.DotDateRange, HTMLStencilElement { - } + interface HTMLDotDateRangeElement extends Components.DotDateRange, HTMLStencilElement {} var HTMLDotDateRangeElement: { prototype: HTMLDotDateRangeElement; new (): HTMLDotDateRangeElement; }; - interface HTMLDotDateTimeElement extends Components.DotDateTime, HTMLStencilElement { - } + interface HTMLDotDateTimeElement extends Components.DotDateTime, HTMLStencilElement {} var HTMLDotDateTimeElement: { prototype: HTMLDotDateTimeElement; new (): HTMLDotDateTimeElement; }; - interface HTMLDotErrorMessageElement extends Components.DotErrorMessage, HTMLStencilElement { - } + interface HTMLDotErrorMessageElement extends Components.DotErrorMessage, HTMLStencilElement {} var HTMLDotErrorMessageElement: { prototype: HTMLDotErrorMessageElement; new (): HTMLDotErrorMessageElement; }; - interface HTMLDotFormElement extends Components.DotForm, HTMLStencilElement { - } + interface HTMLDotFormElement extends Components.DotForm, HTMLStencilElement {} var HTMLDotFormElement: { prototype: HTMLDotFormElement; new (): HTMLDotFormElement; }; - interface HTMLDotFormColumnElement extends Components.DotFormColumn, HTMLStencilElement { - } + interface HTMLDotFormColumnElement extends Components.DotFormColumn, HTMLStencilElement {} var HTMLDotFormColumnElement: { prototype: HTMLDotFormColumnElement; new (): HTMLDotFormColumnElement; }; - interface HTMLDotFormRowElement extends Components.DotFormRow, HTMLStencilElement { - } + interface HTMLDotFormRowElement extends Components.DotFormRow, HTMLStencilElement {} var HTMLDotFormRowElement: { prototype: HTMLDotFormRowElement; new (): HTMLDotFormRowElement; }; - interface HTMLDotHtmlToImageElement extends Components.DotHtmlToImage, HTMLStencilElement { - } + interface HTMLDotHtmlToImageElement extends Components.DotHtmlToImage, HTMLStencilElement {} var HTMLDotHtmlToImageElement: { prototype: HTMLDotHtmlToImageElement; new (): HTMLDotHtmlToImageElement; }; - interface HTMLDotInputCalendarElement extends Components.DotInputCalendar, HTMLStencilElement { - } + interface HTMLDotInputCalendarElement extends Components.DotInputCalendar, HTMLStencilElement {} var HTMLDotInputCalendarElement: { prototype: HTMLDotInputCalendarElement; new (): HTMLDotInputCalendarElement; }; - interface HTMLDotKeyValueElement extends Components.DotKeyValue, HTMLStencilElement { - } + interface HTMLDotKeyValueElement extends Components.DotKeyValue, HTMLStencilElement {} var HTMLDotKeyValueElement: { prototype: HTMLDotKeyValueElement; new (): HTMLDotKeyValueElement; }; - interface HTMLDotLabelElement extends Components.DotLabel, HTMLStencilElement { - } + interface HTMLDotLabelElement extends Components.DotLabel, HTMLStencilElement {} var HTMLDotLabelElement: { prototype: HTMLDotLabelElement; new (): HTMLDotLabelElement; }; - interface HTMLDotMaterialIconPickerElement extends Components.DotMaterialIconPicker, HTMLStencilElement { - } + interface HTMLDotMaterialIconPickerElement + extends Components.DotMaterialIconPicker, + HTMLStencilElement {} var HTMLDotMaterialIconPickerElement: { prototype: HTMLDotMaterialIconPickerElement; new (): HTMLDotMaterialIconPickerElement; }; - interface HTMLDotMultiSelectElement extends Components.DotMultiSelect, HTMLStencilElement { - } + interface HTMLDotMultiSelectElement extends Components.DotMultiSelect, HTMLStencilElement {} var HTMLDotMultiSelectElement: { prototype: HTMLDotMultiSelectElement; new (): HTMLDotMultiSelectElement; }; - interface HTMLDotProgressBarElement extends Components.DotProgressBar, HTMLStencilElement { - } + interface HTMLDotProgressBarElement extends Components.DotProgressBar, HTMLStencilElement {} var HTMLDotProgressBarElement: { prototype: HTMLDotProgressBarElement; new (): HTMLDotProgressBarElement; }; - interface HTMLDotRadioElement extends Components.DotRadio, HTMLStencilElement { - } + interface HTMLDotRadioElement extends Components.DotRadio, HTMLStencilElement {} var HTMLDotRadioElement: { prototype: HTMLDotRadioElement; new (): HTMLDotRadioElement; }; - interface HTMLDotSelectElement extends Components.DotSelect, HTMLStencilElement { - } + interface HTMLDotSelectElement extends Components.DotSelect, HTMLStencilElement {} var HTMLDotSelectElement: { prototype: HTMLDotSelectElement; new (): HTMLDotSelectElement; }; - interface HTMLDotSelectButtonElement extends Components.DotSelectButton, HTMLStencilElement { - } + interface HTMLDotSelectButtonElement extends Components.DotSelectButton, HTMLStencilElement {} var HTMLDotSelectButtonElement: { prototype: HTMLDotSelectButtonElement; new (): HTMLDotSelectButtonElement; }; - interface HTMLDotStateIconElement extends Components.DotStateIcon, HTMLStencilElement { - } + interface HTMLDotStateIconElement extends Components.DotStateIcon, HTMLStencilElement {} var HTMLDotStateIconElement: { prototype: HTMLDotStateIconElement; new (): HTMLDotStateIconElement; }; - interface HTMLDotTagsElement extends Components.DotTags, HTMLStencilElement { - } + interface HTMLDotTagsElement extends Components.DotTags, HTMLStencilElement {} var HTMLDotTagsElement: { prototype: HTMLDotTagsElement; new (): HTMLDotTagsElement; }; - interface HTMLDotTextareaElement extends Components.DotTextarea, HTMLStencilElement { - } + interface HTMLDotTextareaElement extends Components.DotTextarea, HTMLStencilElement {} var HTMLDotTextareaElement: { prototype: HTMLDotTextareaElement; new (): HTMLDotTextareaElement; }; - interface HTMLDotTextfieldElement extends Components.DotTextfield, HTMLStencilElement { - } + interface HTMLDotTextfieldElement extends Components.DotTextfield, HTMLStencilElement {} var HTMLDotTextfieldElement: { prototype: HTMLDotTextfieldElement; new (): HTMLDotTextfieldElement; }; - interface HTMLDotTimeElement extends Components.DotTime, HTMLStencilElement { - } + interface HTMLDotTimeElement extends Components.DotTime, HTMLStencilElement {} var HTMLDotTimeElement: { prototype: HTMLDotTimeElement; new (): HTMLDotTimeElement; }; - interface HTMLDotTooltipElement extends Components.DotTooltip, HTMLStencilElement { - } + interface HTMLDotTooltipElement extends Components.DotTooltip, HTMLStencilElement {} var HTMLDotTooltipElement: { prototype: HTMLDotTooltipElement; new (): HTMLDotTooltipElement; }; - interface HTMLKeyValueFormElement extends Components.KeyValueForm, HTMLStencilElement { - } + interface HTMLKeyValueFormElement extends Components.KeyValueForm, HTMLStencilElement {} var HTMLKeyValueFormElement: { prototype: HTMLKeyValueFormElement; new (): HTMLKeyValueFormElement; }; - interface HTMLKeyValueTableElement extends Components.KeyValueTable, HTMLStencilElement { - } + interface HTMLKeyValueTableElement extends Components.KeyValueTable, HTMLStencilElement {} var HTMLKeyValueTableElement: { prototype: HTMLKeyValueTableElement; new (): HTMLKeyValueTableElement; }; interface HTMLElementTagNameMap { - "dot-asset-drop-zone": HTMLDotAssetDropZoneElement; - "dot-autocomplete": HTMLDotAutocompleteElement; - "dot-badge": HTMLDotBadgeElement; - "dot-binary-file": HTMLDotBinaryFileElement; - "dot-binary-file-preview": HTMLDotBinaryFilePreviewElement; - "dot-binary-text-field": HTMLDotBinaryTextFieldElement; - "dot-binary-upload-button": HTMLDotBinaryUploadButtonElement; - "dot-card": HTMLDotCardElement; - "dot-card-contentlet": HTMLDotCardContentletElement; - "dot-card-view": HTMLDotCardViewElement; - "dot-checkbox": HTMLDotCheckboxElement; - "dot-chip": HTMLDotChipElement; - "dot-contentlet-icon": HTMLDotContentletIconElement; - "dot-contentlet-lock-icon": HTMLDotContentletLockIconElement; - "dot-contentlet-thumbnail": HTMLDotContentletThumbnailElement; - "dot-context-menu": HTMLDotContextMenuElement; - "dot-data-view-button": HTMLDotDataViewButtonElement; - "dot-date": HTMLDotDateElement; - "dot-date-range": HTMLDotDateRangeElement; - "dot-date-time": HTMLDotDateTimeElement; - "dot-error-message": HTMLDotErrorMessageElement; - "dot-form": HTMLDotFormElement; - "dot-form-column": HTMLDotFormColumnElement; - "dot-form-row": HTMLDotFormRowElement; - "dot-html-to-image": HTMLDotHtmlToImageElement; - "dot-input-calendar": HTMLDotInputCalendarElement; - "dot-key-value": HTMLDotKeyValueElement; - "dot-label": HTMLDotLabelElement; - "dot-material-icon-picker": HTMLDotMaterialIconPickerElement; - "dot-multi-select": HTMLDotMultiSelectElement; - "dot-progress-bar": HTMLDotProgressBarElement; - "dot-radio": HTMLDotRadioElement; - "dot-select": HTMLDotSelectElement; - "dot-select-button": HTMLDotSelectButtonElement; - "dot-state-icon": HTMLDotStateIconElement; - "dot-tags": HTMLDotTagsElement; - "dot-textarea": HTMLDotTextareaElement; - "dot-textfield": HTMLDotTextfieldElement; - "dot-time": HTMLDotTimeElement; - "dot-tooltip": HTMLDotTooltipElement; - "key-value-form": HTMLKeyValueFormElement; - "key-value-table": HTMLKeyValueTableElement; + 'dot-asset-drop-zone': HTMLDotAssetDropZoneElement; + 'dot-autocomplete': HTMLDotAutocompleteElement; + 'dot-badge': HTMLDotBadgeElement; + 'dot-binary-file': HTMLDotBinaryFileElement; + 'dot-binary-file-preview': HTMLDotBinaryFilePreviewElement; + 'dot-binary-text-field': HTMLDotBinaryTextFieldElement; + 'dot-binary-upload-button': HTMLDotBinaryUploadButtonElement; + 'dot-card': HTMLDotCardElement; + 'dot-card-contentlet': HTMLDotCardContentletElement; + 'dot-card-view': HTMLDotCardViewElement; + 'dot-checkbox': HTMLDotCheckboxElement; + 'dot-chip': HTMLDotChipElement; + 'dot-contentlet-icon': HTMLDotContentletIconElement; + 'dot-contentlet-lock-icon': HTMLDotContentletLockIconElement; + 'dot-contentlet-thumbnail': HTMLDotContentletThumbnailElement; + 'dot-context-menu': HTMLDotContextMenuElement; + 'dot-data-view-button': HTMLDotDataViewButtonElement; + 'dot-date': HTMLDotDateElement; + 'dot-date-range': HTMLDotDateRangeElement; + 'dot-date-time': HTMLDotDateTimeElement; + 'dot-error-message': HTMLDotErrorMessageElement; + 'dot-form': HTMLDotFormElement; + 'dot-form-column': HTMLDotFormColumnElement; + 'dot-form-row': HTMLDotFormRowElement; + 'dot-html-to-image': HTMLDotHtmlToImageElement; + 'dot-input-calendar': HTMLDotInputCalendarElement; + 'dot-key-value': HTMLDotKeyValueElement; + 'dot-label': HTMLDotLabelElement; + 'dot-material-icon-picker': HTMLDotMaterialIconPickerElement; + 'dot-multi-select': HTMLDotMultiSelectElement; + 'dot-progress-bar': HTMLDotProgressBarElement; + 'dot-radio': HTMLDotRadioElement; + 'dot-select': HTMLDotSelectElement; + 'dot-select-button': HTMLDotSelectButtonElement; + 'dot-state-icon': HTMLDotStateIconElement; + 'dot-tags': HTMLDotTagsElement; + 'dot-textarea': HTMLDotTextareaElement; + 'dot-textfield': HTMLDotTextfieldElement; + 'dot-time': HTMLDotTimeElement; + 'dot-tooltip': HTMLDotTooltipElement; + 'key-value-form': HTMLKeyValueFormElement; + 'key-value-table': HTMLKeyValueTableElement; } } declare namespace LocalJSX { interface DotAssetDropZone { /** - * Allowed file extensions + * Allowed file extensions */ - "acceptTypes"?: string[]; + acceptTypes?: string[]; /** - * Legend to be shown when creating dotAssets + * Legend to be shown when creating dotAssets */ - "createAssetsText"?: string; - "customUploadFiles"?: (props: { - files: File[], - onSuccess: () => void, - updateProgress: (progress: number) => void, - onError: (header: string, message: string) => void - }) => Promise; + createAssetsText?: string; + customUploadFiles?: (props: { + files: File[]; + onSuccess: () => void; + updateProgress: (progress: number) => void; + onError: (header: string, message: string) => void; + }) => Promise; /** - * Labels to be shown in error dialog + * Labels to be shown in error dialog */ - "dialogLabels"?: { closeButton: string; uploadErrorHeader: string; dotAssetErrorHeader: string; errorHeader: string; }; - "displayIndicator"?: boolean; + dialogLabels?: { + closeButton: string; + uploadErrorHeader: string; + dotAssetErrorHeader: string; + errorHeader: string; + }; + displayIndicator?: boolean; /** - * URL to endpoint to create dotAssets + * URL to endpoint to create dotAssets */ - "dotAssetsURL"?: string; + dotAssetsURL?: string; /** - * Legend to be shown when dropping files + * Legend to be shown when dropping files */ - "dropFilesText"?: string; + dropFilesText?: string; /** - * Specify the the folder where the dotAssets will be placed + * Specify the the folder where the dotAssets will be placed */ - "folder"?: string; + folder?: string; /** - * Specify the max size of each file to be uploaded + * Specify the max size of each file to be uploaded */ - "maxFileSize"?: string; + maxFileSize?: string; /** - * Error to be shown when try to upload a bigger size file than allowed + * Error to be shown when try to upload a bigger size file than allowed */ - "multiMaxSizeErrorLabel"?: string; + multiMaxSizeErrorLabel?: string; /** - * Emit an array of Contentlets just created or array of errors + * Emit an array of Contentlets just created or array of errors */ - "onUploadComplete"?: (event: DotAssetDropZoneCustomEvent) => void; + onUploadComplete?: ( + event: DotAssetDropZoneCustomEvent + ) => void; /** - * Error to be shown when try to upload a bigger size file than allowed + * Error to be shown when try to upload a bigger size file than allowed */ - "singeMaxSizeErrorLabel"?: string; + singeMaxSizeErrorLabel?: string; /** - * Allowed file extensions + * Allowed file extensions */ - "typesErrorLabel"?: string; + typesErrorLabel?: string; /** - * Error to be shown when an error happened on the uploading process + * Error to be shown when an error happened on the uploading process */ - "uploadErrorLabel"?: string; + uploadErrorLabel?: string; /** - * Legend to be shown when uploading files + * Legend to be shown when uploading files */ - "uploadFileText"?: string; + uploadFileText?: string; } interface DotAutocomplete { /** - * Function or array of string to get the data to use for the autocomplete search + * Function or array of string to get the data to use for the autocomplete search */ - "data"?: () => Promise | string[]; + data?: () => Promise | string[]; /** - * (optional) Duraction in ms to start search into the autocomplete + * (optional) Duraction in ms to start search into the autocomplete */ - "debounce"?: number; + debounce?: number; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Max results to show after a autocomplete search + * (optional) Max results to show after a autocomplete search */ - "maxResults"?: number; - "onEnter"?: (event: DotAutocompleteCustomEvent) => void; - "onLostFocus"?: (event: DotAutocompleteCustomEvent) => void; - "onSelection"?: (event: DotAutocompleteCustomEvent) => void; + maxResults?: number; + onEnter?: (event: DotAutocompleteCustomEvent) => void; + onLostFocus?: (event: DotAutocompleteCustomEvent) => void; + onSelection?: (event: DotAutocompleteCustomEvent) => void; /** - * (optional) text to show when no value is set + * (optional) text to show when no value is set */ - "placeholder"?: string; + placeholder?: string; /** - * (optional) Min characters to start search in the autocomplete input + * (optional) Min characters to start search in the autocomplete input */ - "threshold"?: number; + threshold?: number; } interface DotBadge { - "bgColor"?: string; - "bordered"?: boolean; - "color"?: string; - "size"?: string; + bgColor?: string; + bordered?: boolean; + color?: string; + size?: string; } interface DotBinaryFile { /** - * (optional) Text that be shown when the URL is not valid + * (optional) Text that be shown when the URL is not valid */ - "URLValidationMessage"?: string; + URLValidationMessage?: string; /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept"?: string; + accept?: string; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "buttonLabel"?: string; + buttonLabel?: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "errorMessage"?: string; + errorMessage?: string; /** - * (optional) Text that be shown when the file size is not valid + * (optional) Text that be shown when the file size is not valid */ - "fileSizeValidationMessage"?: string; + fileSizeValidationMessage?: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) Set the max file size limit + * (optional) Set the max file size limit */ - "maxFileLength"?: string; + maxFileLength?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotBinaryFileCustomEvent) => void; - "onDotValueChange"?: (event: DotBinaryFileCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotBinaryFileCustomEvent) => void; + onDotValueChange?: (event: DotBinaryFileCustomEvent) => void; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder"?: string; + placeholder?: string; /** - * (optional) Name of the file uploaded + * (optional) Name of the file uploaded */ - "previewImageName"?: string; + previewImageName?: string; /** - * (optional) URL of the file uploaded + * (optional) URL of the file uploaded */ - "previewImageUrl"?: string; + previewImageUrl?: string; /** - * (optional) Determine if it is required + * (optional) Determine if it is required */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage"?: string; + validationMessage?: string; } interface DotBinaryFilePreview { /** - * (optional) Delete button's label + * (optional) Delete button's label */ - "deleteLabel"?: string; + deleteLabel?: string; /** - * file name to be displayed + * file name to be displayed */ - "fileName"?: string; + fileName?: string; /** - * Emit when the file is deleted + * Emit when the file is deleted */ - "onDelete"?: (event: DotBinaryFilePreviewCustomEvent) => void; + onDelete?: (event: DotBinaryFilePreviewCustomEvent) => void; /** - * (optional) file URL to be displayed + * (optional) file URL to be displayed */ - "previewUrl"?: string; + previewUrl?: string; } interface DotBinaryTextField { /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept"?: string; + accept?: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; - "onFileChange"?: (event: DotBinaryTextFieldCustomEvent) => void; - "onLostFocus"?: (event: DotBinaryTextFieldCustomEvent) => void; + hint?: string; + onFileChange?: (event: DotBinaryTextFieldCustomEvent) => void; + onLostFocus?: (event: DotBinaryTextFieldCustomEvent) => void; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder"?: string; + placeholder?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * Value specifies the value of the element + * Value specifies the value of the element */ - "value"?: any; + value?: any; } interface DotBinaryUploadButton { /** - * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg + * (optional) Describes a type of file that may be selected by the user, separated by comma eg: .pdf,.jpg */ - "accept"?: string; + accept?: string; /** - * (optional) Text that be shown in the browse file button + * (optional) Text that be shown in the browse file button */ - "buttonLabel"?: string; + buttonLabel?: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Set the max file size limit + * (optional) Set the max file size limit */ - "maxFileLength"?: string; + maxFileLength?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onFileChange"?: (event: DotBinaryUploadButtonCustomEvent) => void; + name?: string; + onFileChange?: (event: DotBinaryUploadButtonCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; - } - interface DotCard { + required?: boolean; } + interface DotCard {} interface DotCardContentlet { - "checked"?: boolean; - "iconSize"?: string; - "item"?: DotCardContentletItem; - "onCheckboxChange"?: (event: DotCardContentletCustomEvent) => void; - "onContextMenuClick"?: (event: DotCardContentletCustomEvent) => void; - "thumbnailSize"?: string; + checked?: boolean; + iconSize?: string; + item?: DotCardContentletItem; + onCheckboxChange?: (event: DotCardContentletCustomEvent) => void; + onContextMenuClick?: (event: DotCardContentletCustomEvent) => void; + thumbnailSize?: string; } interface DotCardView { - "items"?: DotCardContentletItem[]; - "onCardClick"?: (event: DotCardViewCustomEvent) => void; - "onSelected"?: (event: DotCardViewCustomEvent) => void; - "value"?: string; + items?: DotCardContentletItem[]; + onCardClick?: (event: DotCardViewCustomEvent) => void; + onSelected?: (event: DotCardViewCustomEvent) => void; + value?: string; } interface DotCheckbox { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotCheckboxCustomEvent) => void; - "onDotValueChange"?: (event: DotCheckboxCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotCheckboxCustomEvent) => void; + onDotValueChange?: (event: DotCheckboxCustomEvent) => void; /** - * Value/Label checkbox options separated by comma, to be formatted as: Value|Label + * Value/Label checkbox options separated by comma, to be formatted as: Value|Label */ - "options"?: string; + options?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * Value set from the checkbox option + * Value set from the checkbox option */ - "value"?: string; + value?: string; } interface DotChip { /** - * (optional) Delete button's label + * (optional) Delete button's label */ - "deleteLabel"?: string; + deleteLabel?: string; /** - * (optional) If is true disabled the delete button + * (optional) If is true disabled the delete button */ - "disabled"?: boolean; + disabled?: boolean; /** - * Chip's label + * Chip's label */ - "label"?: string; - "onRemove"?: (event: DotChipCustomEvent) => void; + label?: string; + onRemove?: (event: DotChipCustomEvent) => void; } interface DotContentletIcon { - "icon"?: string; - "size"?: string; + icon?: string; + size?: string; } interface DotContentletLockIcon { - "locked"?: boolean; - "size"?: string; + locked?: boolean; + size?: string; } interface DotContentletThumbnail { - "alt"?: string; - "contentlet"?: DotContentletItem; - "height"?: string; - "iconSize"?: string; - "width"?: string; + alt?: string; + contentlet?: DotContentletItem; + height?: string; + iconSize?: string; + width?: string; } interface DotContextMenu { - "fontSize"?: string; - "options"?: DotContextMenuOption[]; + fontSize?: string; + options?: DotContextMenuOption[]; } interface DotDataViewButton { - "value"?: string; + value?: string; } interface DotDate { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd + * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd */ - "max"?: string; + max?: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd + * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd */ - "min"?: string; + min?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotDateCustomEvent) => void; - "onDotValueChange"?: (event: DotDateCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotDateCustomEvent) => void; + onDotValueChange?: (event: DotDateCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step"?: string; + step?: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage"?: string; + validationMessage?: string; /** - * Value format yyyy-mm-dd e.g., 2005-12-01 + * Value format yyyy-mm-dd e.g., 2005-12-01 */ - "value"?: string; + value?: string; } interface DotDateRange { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Date format used by the field when displayed + * (optional) Date format used by the field when displayed */ - "displayFormat"?: string; + displayFormat?: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) Max value that the field will allow to set + * (optional) Max value that the field will allow to set */ - "max"?: string; + max?: string; /** - * (optional) Min value that the field will allow to set + * (optional) Min value that the field will allow to set */ - "min"?: string; + min?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotDateRangeCustomEvent) => void; - "onDotValueChange"?: (event: DotDateRangeCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotDateRangeCustomEvent) => void; + onDotValueChange?: (event: DotDateRangeCustomEvent) => void; /** - * (optional) Text to be rendered next to presets field + * (optional) Text to be rendered next to presets field */ - "presetLabel"?: string; + presetLabel?: string; /** - * (optional) Array of date presets formatted as [{ label: 'PRESET_LABEL', days: NUMBER }] + * (optional) Array of date presets formatted as [{ label: 'PRESET_LABEL', days: NUMBER }] */ - "presets"?: { label: string; days: number; }[]; + presets?: { label: string; days: number }[]; /** - * (optional) Determine if it is needed + * (optional) Determine if it is needed */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Value formatted with start and end date splitted with a comma + * (optional) Value formatted with start and end date splitted with a comma */ - "value"?: string; + value?: string; } interface DotDateTime { /** - * (optional) The string to use in the date label field + * (optional) The string to use in the date label field */ - "dateLabel"?: string; + dateLabel?: string; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss + * (optional) Max, maximum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss */ - "max"?: string; + max?: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss + * (optional) Min, minimum value that the field will allow to set. Format should be yyyy-mm-dd hh:mm:ss | yyyy-mm-dd | hh:mm:ss */ - "min"?: string; + min?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotDateTimeCustomEvent) => void; - "onDotValueChange"?: (event: DotDateTimeCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotDateTimeCustomEvent) => void; + onDotValueChange?: (event: DotDateTimeCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Step specifies the legal number intervals for the input fields date && time e.g., 2,10 + * (optional) Step specifies the legal number intervals for the input fields date && time e.g., 2,10 */ - "step"?: string; + step?: string; /** - * (optional) The string to use in the time label field + * (optional) The string to use in the time label field */ - "timeLabel"?: string; + timeLabel?: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage"?: string; + validationMessage?: string; /** - * Value format yyyy-mm-dd hh:mm:ss e.g., 2005-12-01 15:22:00 + * Value format yyyy-mm-dd hh:mm:ss e.g., 2005-12-01 15:22:00 */ - "value"?: string; - } - interface DotErrorMessage { + value?: string; } + interface DotErrorMessage {} interface DotForm { /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow"?: string; + fieldsToShow?: string; /** - * Layout metada to be rendered + * Layout metada to be rendered */ - "layout"?: DotCMSContentTypeLayoutRow[]; + layout?: DotCMSContentTypeLayoutRow[]; /** - * Emit when submit the form + * Emit when submit the form */ - "onSubmit"?: (event: DotFormCustomEvent) => void; + onSubmit?: (event: DotFormCustomEvent) => void; /** - * (optional) Text to be rendered on Reset button + * (optional) Text to be rendered on Reset button */ - "resetLabel"?: string; + resetLabel?: string; /** - * (optional) Text to be rendered on Submit button + * (optional) Text to be rendered on Submit button */ - "submitLabel"?: string; + submitLabel?: string; /** - * Content type variable name + * Content type variable name */ - "variable"?: string; + variable?: string; } interface DotFormColumn { /** - * Fields metada to be rendered + * Fields metada to be rendered */ - "column"?: DotCMSContentTypeLayoutColumn; + column?: DotCMSContentTypeLayoutColumn; /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow"?: string; + fieldsToShow?: string; } interface DotFormRow { /** - * (optional) List of fields (variableName) separated by comma, to be shown + * (optional) List of fields (variableName) separated by comma, to be shown */ - "fieldsToShow"?: string; + fieldsToShow?: string; /** - * Fields metada to be rendered + * Fields metada to be rendered */ - "row"?: DotCMSContentTypeLayoutRow; + row?: DotCMSContentTypeLayoutRow; } interface DotHtmlToImage { - "height"?: string; - "onPageThumbnail"?: (event: DotHtmlToImageCustomEvent<{ - file: File; - error?: string; - }>) => void; - "value"?: string; - "width"?: string; + height?: string; + onPageThumbnail?: ( + event: DotHtmlToImageCustomEvent<{ + file: File; + error?: string; + }> + ) => void; + value?: string; + width?: string; } interface DotInputCalendar { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Max, maximum value that the field will allow to set, expect a Date Format + * (optional) Max, maximum value that the field will allow to set, expect a Date Format */ - "max"?: string; + max?: string; /** - * (optional) Min, minimum value that the field will allow to set, expect a Date Format. + * (optional) Min, minimum value that the field will allow to set, expect a Date Format. */ - "min"?: string; + min?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "on_dotStatusChange"?: (event: DotInputCalendarCustomEvent) => void; - "on_dotValueChange"?: (event: DotInputCalendarCustomEvent) => void; + name?: string; + on_dotStatusChange?: ( + event: DotInputCalendarCustomEvent + ) => void; + on_dotValueChange?: (event: DotInputCalendarCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step"?: string; + step?: string; /** - * type specifies the type of input element to display + * type specifies the type of input element to display */ - "type"?: string; + type?: string; /** - * Value specifies the value of the input element + * Value specifies the value of the input element */ - "value"?: string; + value?: string; } interface DotKeyValue { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "duplicatedKeyMessage"?: string; + duplicatedKeyMessage?: string; /** - * (optional) Label for the add button in the key-value-form + * (optional) Label for the add button in the key-value-form */ - "formAddButtonLabel"?: string; + formAddButtonLabel?: string; /** - * (optional) The string to use in the key label in the key-value-form + * (optional) The string to use in the key label in the key-value-form */ - "formKeyLabel"?: string; + formKeyLabel?: string; /** - * (optional) Placeholder for the key input text in the key-value-form + * (optional) Placeholder for the key input text in the key-value-form */ - "formKeyPlaceholder"?: string; + formKeyPlaceholder?: string; /** - * (optional) The string to use in the value label in the key-value-form + * (optional) The string to use in the value label in the key-value-form */ - "formValueLabel"?: string; + formValueLabel?: string; /** - * (optional) Placeholder for the value input text in the key-value-form + * (optional) Placeholder for the value input text in the key-value-form */ - "formValuePlaceholder"?: string; + formValuePlaceholder?: string; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) The string to use in the delete button of a key/value item + * (optional) The string to use in the delete button of a key/value item */ - "listDeleteLabel"?: string; + listDeleteLabel?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotKeyValueCustomEvent) => void; - "onDotValueChange"?: (event: DotKeyValueCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotKeyValueCustomEvent) => void; + onDotValueChange?: (event: DotKeyValueCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Allows unique keys only + * (optional) Allows unique keys only */ - "uniqueKeys"?: boolean; + uniqueKeys?: boolean; /** - * Value of the field + * Value of the field */ - "value"?: string; + value?: string; /** - * (optional) The string containing the value to be parsed for whitelist key/value + * (optional) The string containing the value to be parsed for whitelist key/value */ - "whiteList"?: string; + whiteList?: string; /** - * (optional) The string to use in the empty option of whitelist dropdown key/value item + * (optional) The string to use in the empty option of whitelist dropdown key/value item */ - "whiteListEmptyOptionLabel"?: string; + whiteListEmptyOptionLabel?: string; } interface DotLabel { /** - * (optional) Text to be rendered + * (optional) Text to be rendered */ - "label"?: string; + label?: string; /** - * (optional) Field name + * (optional) Field name */ - "name"?: string; + name?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; } interface DotMaterialIconPicker { /** - * Label set for the input color + * Label set for the input color */ - "colorLabel"?: string; + colorLabel?: string; /** - * Color value set from the input + * Color value set from the input */ - "colorValue"?: string; + colorValue?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotValueChange"?: (event: DotMaterialIconPickerCustomEvent<{ name: string; value: string; colorValue: string }>) => void; + name?: string; + onDotValueChange?: ( + event: DotMaterialIconPickerCustomEvent<{ + name: string; + value: string; + colorValue: string; + }> + ) => void; /** - * Value for input placeholder + * Value for input placeholder */ - "placeholder"?: string; + placeholder?: string; /** - * Show/Hide color picker + * Show/Hide color picker */ - "showColor"?: string; + showColor?: string; /** - * Size value set for font-size + * Size value set for font-size */ - "size"?: string; + size?: string; /** - * Values that the auto-complete textbox should search for + * Values that the auto-complete textbox should search for */ - "suggestionlist"?: string[]; + suggestionlist?: string[]; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value"?: string; + value?: string; } interface DotMultiSelect { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotMultiSelectCustomEvent) => void; - "onDotValueChange"?: (event: DotMultiSelectCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotMultiSelectCustomEvent) => void; + onDotValueChange?: (event: DotMultiSelectCustomEvent) => void; /** - * Value/Label dropdown options separated by comma, to be formatted as: Value|Label + * Value/Label dropdown options separated by comma, to be formatted as: Value|Label */ - "options"?: string; + options?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Size number of the multi-select dropdown (default=3) + * (optional) Size number of the multi-select dropdown (default=3) */ - "size"?: string; + size?: string; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value"?: string; + value?: string; } interface DotProgressBar { /** - * indicates the progress to be show, a value 1 to 100 + * indicates the progress to be show, a value 1 to 100 */ - "progress"?: number; + progress?: number; /** - * text to be show bellow the progress bar + * text to be show bellow the progress bar */ - "text"?: string; + text?: string; } interface DotRadio { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotRadioCustomEvent) => void; - "onDotValueChange"?: (event: DotRadioCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotRadioCustomEvent) => void; + onDotValueChange?: (event: DotRadioCustomEvent) => void; /** - * Value/Label ratio options separated by comma, to be formatted as: Value|Label + * Value/Label ratio options separated by comma, to be formatted as: Value|Label */ - "options"?: string; + options?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * Value set from the ratio option + * Value set from the ratio option */ - "value"?: string; + value?: string; } interface DotSelect { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotSelectCustomEvent) => void; - "onDotValueChange"?: (event: DotSelectCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotSelectCustomEvent) => void; + onDotValueChange?: (event: DotSelectCustomEvent) => void; /** - * Value/Label dropdown options separated by comma, to be formatted as: Value|Label + * Value/Label dropdown options separated by comma, to be formatted as: Value|Label */ - "options"?: string; + options?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that will be shown when required is set and condition is not met + * (optional) Text that will be shown when required is set and condition is not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * Value set from the dropdown option + * Value set from the dropdown option */ - "value"?: string; + value?: string; } interface DotSelectButton { - "onSelected"?: (event: DotSelectButtonCustomEvent) => void; - "options"?: DotSelectButtonOption[]; - "value"?: string; + onSelected?: (event: DotSelectButtonCustomEvent) => void; + options?: DotSelectButtonOption[]; + value?: string; } interface DotStateIcon { - "labels"?: { archived: string; published: string; revision: string; draft: string; }; - "size"?: string; - "state"?: DotContentState; + labels?: { archived: string; published: string; revision: string; draft: string }; + size?: string; + state?: DotContentState; } interface DotTags { /** - * Function or array of string to get the data to use for the autocomplete search + * Function or array of string to get the data to use for the autocomplete search */ - "data"?: () => Promise | string[]; + data?: () => Promise | string[]; /** - * Duraction in ms to start search into the autocomplete + * Duraction in ms to start search into the autocomplete */ - "debounce"?: number; + debounce?: number; /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotTagsCustomEvent) => void; - "onDotValueChange"?: (event: DotTagsCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotTagsCustomEvent) => void; + onDotValueChange?: (event: DotTagsCustomEvent) => void; /** - * (optional) text to show when no value is set + * (optional) text to show when no value is set */ - "placeholder"?: string; + placeholder?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and value is not set + * (optional) Text that be shown when required is set and value is not set */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * Min characters to start search in the autocomplete input + * Min characters to start search in the autocomplete input */ - "threshold"?: number; + threshold?: number; /** - * Value formatted splitted with a comma, for example: tag-1,tag-2 + * Value formatted splitted with a comma, for example: tag-1,tag-2 */ - "value"?: string; + value?: string; } interface DotTextarea { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to textarea element + * (optional) Text to be rendered next to textarea element */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotTextareaCustomEvent) => void; - "onDotValueChange"?: (event: DotTextareaCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotTextareaCustomEvent) => void; + onDotValueChange?: (event: DotTextareaCustomEvent) => void; /** - * (optional) Regular expresion that is checked against the value to determine if is valid + * (optional) Regular expresion that is checked against the value to determine if is valid */ - "regexCheck"?: string; + regexCheck?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage"?: string; + validationMessage?: string; /** - * Value specifies the value of the textarea element + * Value specifies the value of the textarea element */ - "value"?: string; + value?: string; } interface DotTextfield { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotTextfieldCustomEvent) => void; - "onDotValueChange"?: (event: DotTextfieldCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotTextfieldCustomEvent) => void; + onDotValueChange?: (event: DotTextfieldCustomEvent) => void; /** - * (optional) Placeholder specifies a short hint that describes the expected value of the input field + * (optional) Placeholder specifies a short hint that describes the expected value of the input field */ - "placeholder"?: string; + placeholder?: string; /** - * (optional) Regular expresion that is checked against the value to determine if is valid + * (optional) Regular expresion that is checked against the value to determine if is valid */ - "regexCheck"?: string; + regexCheck?: string; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * type specifies the type of input element to display + * type specifies the type of input element to display */ - "type"?: string; + type?: string; /** - * (optional) Text that be shown when the Regular Expression condition not met + * (optional) Text that be shown when the Regular Expression condition not met */ - "validationMessage"?: string; + validationMessage?: string; /** - * Value specifies the value of the input element + * Value specifies the value of the input element */ - "value"?: string; + value?: string; } interface DotTime { /** - * (optional) Disables field's interaction + * (optional) Disables field's interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Hint text that suggest a clue of the field + * (optional) Hint text that suggest a clue of the field */ - "hint"?: string; + hint?: string; /** - * (optional) Text to be rendered next to input field + * (optional) Text to be rendered next to input field */ - "label"?: string; + label?: string; /** - * (optional) Max, maximum value that the field will allow to set. Format should be hh:mm:ss + * (optional) Max, maximum value that the field will allow to set. Format should be hh:mm:ss */ - "max"?: string; + max?: string; /** - * (optional) Min, minimum value that the field will allow to set. Format should be hh:mm:ss + * (optional) Min, minimum value that the field will allow to set. Format should be hh:mm:ss */ - "min"?: string; + min?: string; /** - * Name that will be used as ID + * Name that will be used as ID */ - "name"?: string; - "onDotStatusChange"?: (event: DotTimeCustomEvent) => void; - "onDotValueChange"?: (event: DotTimeCustomEvent) => void; + name?: string; + onDotStatusChange?: (event: DotTimeCustomEvent) => void; + onDotValueChange?: (event: DotTimeCustomEvent) => void; /** - * (optional) Determine if it is mandatory + * (optional) Determine if it is mandatory */ - "required"?: boolean; + required?: boolean; /** - * (optional) Text that be shown when required is set and condition not met + * (optional) Text that be shown when required is set and condition not met */ - "requiredMessage"?: string; + requiredMessage?: string; /** - * (optional) Step specifies the legal number intervals for the input field + * (optional) Step specifies the legal number intervals for the input field */ - "step"?: string; + step?: string; /** - * (optional) Text that be shown when min or max are set and condition not met + * (optional) Text that be shown when min or max are set and condition not met */ - "validationMessage"?: string; + validationMessage?: string; /** - * Value format hh:mm:ss e.g., 15:22:00 + * Value format hh:mm:ss e.g., 15:22:00 */ - "value"?: string; + value?: string; } interface DotTooltip { - "content"?: string; - "delay"?: number; - "for"?: string; - "position"?: string; + content?: string; + delay?: number; + for?: string; + position?: string; } interface KeyValueForm { /** - * (optional) Label for the add item button + * (optional) Label for the add item button */ - "addButtonLabel"?: string; + addButtonLabel?: string; /** - * (optional) Disables all form interaction + * (optional) Disables all form interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Label for the empty option in white-list select + * (optional) Label for the empty option in white-list select */ - "emptyDropdownOptionLabel"?: string; + emptyDropdownOptionLabel?: string; /** - * (optional) The string to use in the key input label + * (optional) The string to use in the key input label */ - "keyLabel"?: string; + keyLabel?: string; /** - * (optional) Placeholder for the key input text + * (optional) Placeholder for the key input text */ - "keyPlaceholder"?: string; + keyPlaceholder?: string; /** - * Emit the added value, key/value pair + * Emit the added value, key/value pair */ - "onAdd"?: (event: KeyValueFormCustomEvent) => void; + onAdd?: (event: KeyValueFormCustomEvent) => void; /** - * Emit when key is changed + * Emit when key is changed */ - "onKeyChanged"?: (event: KeyValueFormCustomEvent) => void; + onKeyChanged?: (event: KeyValueFormCustomEvent) => void; /** - * Emit when any of the input is blur + * Emit when any of the input is blur */ - "onLostFocus"?: (event: KeyValueFormCustomEvent) => void; + onLostFocus?: (event: KeyValueFormCustomEvent) => void; /** - * (optional) The string to use in the value input label + * (optional) The string to use in the value input label */ - "valueLabel"?: string; + valueLabel?: string; /** - * (optional) Placeholder for the value input text + * (optional) Placeholder for the value input text */ - "valuePlaceholder"?: string; + valuePlaceholder?: string; /** - * (optional) The string to use for white-list key/values + * (optional) The string to use for white-list key/values */ - "whiteList"?: string; + whiteList?: string; } interface KeyValueTable { /** - * (optional) Label for the delete button in each item list + * (optional) Label for the delete button in each item list */ - "buttonLabel"?: string; + buttonLabel?: string; /** - * (optional) Disables all form interaction + * (optional) Disables all form interaction */ - "disabled"?: boolean; + disabled?: boolean; /** - * (optional) Message to show when the list of items is empty + * (optional) Message to show when the list of items is empty */ - "emptyMessage"?: string; + emptyMessage?: string; /** - * (optional) Items to render in the list of key value + * (optional) Items to render in the list of key value */ - "items"?: DotKeyValueField[]; + items?: DotKeyValueField[]; /** - * Emit the index of the item deleted from the list + * Emit the index of the item deleted from the list */ - "onDelete"?: (event: KeyValueTableCustomEvent) => void; + onDelete?: (event: KeyValueTableCustomEvent) => void; /** - * Emit the notification of list reordered + * Emit the notification of list reordered */ - "onReorder"?: (event: KeyValueTableCustomEvent) => void; + onReorder?: (event: KeyValueTableCustomEvent) => void; } interface IntrinsicElements { - "dot-asset-drop-zone": DotAssetDropZone; - "dot-autocomplete": DotAutocomplete; - "dot-badge": DotBadge; - "dot-binary-file": DotBinaryFile; - "dot-binary-file-preview": DotBinaryFilePreview; - "dot-binary-text-field": DotBinaryTextField; - "dot-binary-upload-button": DotBinaryUploadButton; - "dot-card": DotCard; - "dot-card-contentlet": DotCardContentlet; - "dot-card-view": DotCardView; - "dot-checkbox": DotCheckbox; - "dot-chip": DotChip; - "dot-contentlet-icon": DotContentletIcon; - "dot-contentlet-lock-icon": DotContentletLockIcon; - "dot-contentlet-thumbnail": DotContentletThumbnail; - "dot-context-menu": DotContextMenu; - "dot-data-view-button": DotDataViewButton; - "dot-date": DotDate; - "dot-date-range": DotDateRange; - "dot-date-time": DotDateTime; - "dot-error-message": DotErrorMessage; - "dot-form": DotForm; - "dot-form-column": DotFormColumn; - "dot-form-row": DotFormRow; - "dot-html-to-image": DotHtmlToImage; - "dot-input-calendar": DotInputCalendar; - "dot-key-value": DotKeyValue; - "dot-label": DotLabel; - "dot-material-icon-picker": DotMaterialIconPicker; - "dot-multi-select": DotMultiSelect; - "dot-progress-bar": DotProgressBar; - "dot-radio": DotRadio; - "dot-select": DotSelect; - "dot-select-button": DotSelectButton; - "dot-state-icon": DotStateIcon; - "dot-tags": DotTags; - "dot-textarea": DotTextarea; - "dot-textfield": DotTextfield; - "dot-time": DotTime; - "dot-tooltip": DotTooltip; - "key-value-form": KeyValueForm; - "key-value-table": KeyValueTable; + 'dot-asset-drop-zone': DotAssetDropZone; + 'dot-autocomplete': DotAutocomplete; + 'dot-badge': DotBadge; + 'dot-binary-file': DotBinaryFile; + 'dot-binary-file-preview': DotBinaryFilePreview; + 'dot-binary-text-field': DotBinaryTextField; + 'dot-binary-upload-button': DotBinaryUploadButton; + 'dot-card': DotCard; + 'dot-card-contentlet': DotCardContentlet; + 'dot-card-view': DotCardView; + 'dot-checkbox': DotCheckbox; + 'dot-chip': DotChip; + 'dot-contentlet-icon': DotContentletIcon; + 'dot-contentlet-lock-icon': DotContentletLockIcon; + 'dot-contentlet-thumbnail': DotContentletThumbnail; + 'dot-context-menu': DotContextMenu; + 'dot-data-view-button': DotDataViewButton; + 'dot-date': DotDate; + 'dot-date-range': DotDateRange; + 'dot-date-time': DotDateTime; + 'dot-error-message': DotErrorMessage; + 'dot-form': DotForm; + 'dot-form-column': DotFormColumn; + 'dot-form-row': DotFormRow; + 'dot-html-to-image': DotHtmlToImage; + 'dot-input-calendar': DotInputCalendar; + 'dot-key-value': DotKeyValue; + 'dot-label': DotLabel; + 'dot-material-icon-picker': DotMaterialIconPicker; + 'dot-multi-select': DotMultiSelect; + 'dot-progress-bar': DotProgressBar; + 'dot-radio': DotRadio; + 'dot-select': DotSelect; + 'dot-select-button': DotSelectButton; + 'dot-state-icon': DotStateIcon; + 'dot-tags': DotTags; + 'dot-textarea': DotTextarea; + 'dot-textfield': DotTextfield; + 'dot-time': DotTime; + 'dot-tooltip': DotTooltip; + 'key-value-form': KeyValueForm; + 'key-value-table': KeyValueTable; } } export { LocalJSX as JSX }; -declare module "@stencil/core" { +declare module '@stencil/core' { export namespace JSX { interface IntrinsicElements { - "dot-asset-drop-zone": LocalJSX.DotAssetDropZone & JSXBase.HTMLAttributes; - "dot-autocomplete": LocalJSX.DotAutocomplete & JSXBase.HTMLAttributes; - "dot-badge": LocalJSX.DotBadge & JSXBase.HTMLAttributes; - "dot-binary-file": LocalJSX.DotBinaryFile & JSXBase.HTMLAttributes; - "dot-binary-file-preview": LocalJSX.DotBinaryFilePreview & JSXBase.HTMLAttributes; - "dot-binary-text-field": LocalJSX.DotBinaryTextField & JSXBase.HTMLAttributes; - "dot-binary-upload-button": LocalJSX.DotBinaryUploadButton & JSXBase.HTMLAttributes; - "dot-card": LocalJSX.DotCard & JSXBase.HTMLAttributes; - "dot-card-contentlet": LocalJSX.DotCardContentlet & JSXBase.HTMLAttributes; - "dot-card-view": LocalJSX.DotCardView & JSXBase.HTMLAttributes; - "dot-checkbox": LocalJSX.DotCheckbox & JSXBase.HTMLAttributes; - "dot-chip": LocalJSX.DotChip & JSXBase.HTMLAttributes; - "dot-contentlet-icon": LocalJSX.DotContentletIcon & JSXBase.HTMLAttributes; - "dot-contentlet-lock-icon": LocalJSX.DotContentletLockIcon & JSXBase.HTMLAttributes; - "dot-contentlet-thumbnail": LocalJSX.DotContentletThumbnail & JSXBase.HTMLAttributes; - "dot-context-menu": LocalJSX.DotContextMenu & JSXBase.HTMLAttributes; - "dot-data-view-button": LocalJSX.DotDataViewButton & JSXBase.HTMLAttributes; - "dot-date": LocalJSX.DotDate & JSXBase.HTMLAttributes; - "dot-date-range": LocalJSX.DotDateRange & JSXBase.HTMLAttributes; - "dot-date-time": LocalJSX.DotDateTime & JSXBase.HTMLAttributes; - "dot-error-message": LocalJSX.DotErrorMessage & JSXBase.HTMLAttributes; - "dot-form": LocalJSX.DotForm & JSXBase.HTMLAttributes; - "dot-form-column": LocalJSX.DotFormColumn & JSXBase.HTMLAttributes; - "dot-form-row": LocalJSX.DotFormRow & JSXBase.HTMLAttributes; - "dot-html-to-image": LocalJSX.DotHtmlToImage & JSXBase.HTMLAttributes; - "dot-input-calendar": LocalJSX.DotInputCalendar & JSXBase.HTMLAttributes; - "dot-key-value": LocalJSX.DotKeyValue & JSXBase.HTMLAttributes; - "dot-label": LocalJSX.DotLabel & JSXBase.HTMLAttributes; - "dot-material-icon-picker": LocalJSX.DotMaterialIconPicker & JSXBase.HTMLAttributes; - "dot-multi-select": LocalJSX.DotMultiSelect & JSXBase.HTMLAttributes; - "dot-progress-bar": LocalJSX.DotProgressBar & JSXBase.HTMLAttributes; - "dot-radio": LocalJSX.DotRadio & JSXBase.HTMLAttributes; - "dot-select": LocalJSX.DotSelect & JSXBase.HTMLAttributes; - "dot-select-button": LocalJSX.DotSelectButton & JSXBase.HTMLAttributes; - "dot-state-icon": LocalJSX.DotStateIcon & JSXBase.HTMLAttributes; - "dot-tags": LocalJSX.DotTags & JSXBase.HTMLAttributes; - "dot-textarea": LocalJSX.DotTextarea & JSXBase.HTMLAttributes; - "dot-textfield": LocalJSX.DotTextfield & JSXBase.HTMLAttributes; - "dot-time": LocalJSX.DotTime & JSXBase.HTMLAttributes; - "dot-tooltip": LocalJSX.DotTooltip & JSXBase.HTMLAttributes; - "key-value-form": LocalJSX.KeyValueForm & JSXBase.HTMLAttributes; - "key-value-table": LocalJSX.KeyValueTable & JSXBase.HTMLAttributes; + 'dot-asset-drop-zone': LocalJSX.DotAssetDropZone & + JSXBase.HTMLAttributes; + 'dot-autocomplete': LocalJSX.DotAutocomplete & + JSXBase.HTMLAttributes; + 'dot-badge': LocalJSX.DotBadge & JSXBase.HTMLAttributes; + 'dot-binary-file': LocalJSX.DotBinaryFile & + JSXBase.HTMLAttributes; + 'dot-binary-file-preview': LocalJSX.DotBinaryFilePreview & + JSXBase.HTMLAttributes; + 'dot-binary-text-field': LocalJSX.DotBinaryTextField & + JSXBase.HTMLAttributes; + 'dot-binary-upload-button': LocalJSX.DotBinaryUploadButton & + JSXBase.HTMLAttributes; + 'dot-card': LocalJSX.DotCard & JSXBase.HTMLAttributes; + 'dot-card-contentlet': LocalJSX.DotCardContentlet & + JSXBase.HTMLAttributes; + 'dot-card-view': LocalJSX.DotCardView & JSXBase.HTMLAttributes; + 'dot-checkbox': LocalJSX.DotCheckbox & JSXBase.HTMLAttributes; + 'dot-chip': LocalJSX.DotChip & JSXBase.HTMLAttributes; + 'dot-contentlet-icon': LocalJSX.DotContentletIcon & + JSXBase.HTMLAttributes; + 'dot-contentlet-lock-icon': LocalJSX.DotContentletLockIcon & + JSXBase.HTMLAttributes; + 'dot-contentlet-thumbnail': LocalJSX.DotContentletThumbnail & + JSXBase.HTMLAttributes; + 'dot-context-menu': LocalJSX.DotContextMenu & + JSXBase.HTMLAttributes; + 'dot-data-view-button': LocalJSX.DotDataViewButton & + JSXBase.HTMLAttributes; + 'dot-date': LocalJSX.DotDate & JSXBase.HTMLAttributes; + 'dot-date-range': LocalJSX.DotDateRange & + JSXBase.HTMLAttributes; + 'dot-date-time': LocalJSX.DotDateTime & JSXBase.HTMLAttributes; + 'dot-error-message': LocalJSX.DotErrorMessage & + JSXBase.HTMLAttributes; + 'dot-form': LocalJSX.DotForm & JSXBase.HTMLAttributes; + 'dot-form-column': LocalJSX.DotFormColumn & + JSXBase.HTMLAttributes; + 'dot-form-row': LocalJSX.DotFormRow & JSXBase.HTMLAttributes; + 'dot-html-to-image': LocalJSX.DotHtmlToImage & + JSXBase.HTMLAttributes; + 'dot-input-calendar': LocalJSX.DotInputCalendar & + JSXBase.HTMLAttributes; + 'dot-key-value': LocalJSX.DotKeyValue & JSXBase.HTMLAttributes; + 'dot-label': LocalJSX.DotLabel & JSXBase.HTMLAttributes; + 'dot-material-icon-picker': LocalJSX.DotMaterialIconPicker & + JSXBase.HTMLAttributes; + 'dot-multi-select': LocalJSX.DotMultiSelect & + JSXBase.HTMLAttributes; + 'dot-progress-bar': LocalJSX.DotProgressBar & + JSXBase.HTMLAttributes; + 'dot-radio': LocalJSX.DotRadio & JSXBase.HTMLAttributes; + 'dot-select': LocalJSX.DotSelect & JSXBase.HTMLAttributes; + 'dot-select-button': LocalJSX.DotSelectButton & + JSXBase.HTMLAttributes; + 'dot-state-icon': LocalJSX.DotStateIcon & + JSXBase.HTMLAttributes; + 'dot-tags': LocalJSX.DotTags & JSXBase.HTMLAttributes; + 'dot-textarea': LocalJSX.DotTextarea & JSXBase.HTMLAttributes; + 'dot-textfield': LocalJSX.DotTextfield & + JSXBase.HTMLAttributes; + 'dot-time': LocalJSX.DotTime & JSXBase.HTMLAttributes; + 'dot-tooltip': LocalJSX.DotTooltip & JSXBase.HTMLAttributes; + 'key-value-form': LocalJSX.KeyValueForm & + JSXBase.HTMLAttributes; + 'key-value-table': LocalJSX.KeyValueTable & + JSXBase.HTMLAttributes; } } } diff --git a/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx b/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx index e75090cb67b4..76a58c99f13a 100644 --- a/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx +++ b/core-web/libs/dotcms-webcomponents/src/elements/dot-material-icon-picker/dot-material-icon-picker.tsx @@ -242,7 +242,8 @@ export class DotMaterialIcon { private scrollIntoSelectedOption(index: number) { const optionsList = this.element.querySelectorAll('.dot-material-icon__option'); optionsList[index].scrollIntoView({ - behavior: 'smooth', block: 'nearest' + behavior: 'smooth', + block: 'nearest' }); } diff --git a/dotCMS/src/curl-test/Category.postman_collection.json b/dotCMS/src/curl-test/Category.postman_collection.json new file mode 100644 index 000000000000..572f73bb0de9 --- /dev/null +++ b/dotCMS/src/curl-test/Category.postman_collection.json @@ -0,0 +1,2533 @@ +{ + "info": { + "_postman_id": "d44023d0-b70c-4003-b122-ff6d56ab1f53", + "name": "Category", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Delete", + "item": [ + { + "name": "Create Parent Category without children", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Parent category name\");", + "});", + "", + "pm.collectionVariables.set(\"deleteParentCategoryId\", jsonData.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Parent category key\",\n \"categoryName\" : \"Parent category name\",\n \"keywords\":\"This is a parent category\",\n \"categoryVelocityVarName\" : \"ParentCategoryKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Delete Parent Category without children", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Delete Category Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(1);", + "});", + "", + "pm.test(\"Failed To Delete Category Check\", function () {", + " pm.expect(jsonData.entity.fails.length).to.eql(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "[\"{{deleteParentCategoryId}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Parent Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Parent category name\");", + "});", + "", + "pm.collectionVariables.set(\"deleteParentCategoryId\", jsonData.inode);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Parent category key\",\n \"categoryName\" : \"Parent category name\",\n \"keywords\":\"This is a parent category\",\n \"categoryVelocityVarName\" : \"ParentCategoryKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Child Category 1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Child 1 category name\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child 1 category key\",\n \"categoryName\" : \"Child 1 category name\",\n \"keywords\":\"This is a child 1 category\",\n \"categoryVelocityVarName\" : \"Child1CategoryKey\",\n \"parent\" : \"{{deleteParentCategoryId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Child Category 2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Child 2 category name\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child 2 category key\",\n \"categoryName\" : \"Child 2 category name\",\n \"keywords\":\"This is a child 2 category\",\n \"categoryVelocityVarName\" : \"Child2CategoryKey\",\n \"parent\" : \"{{deleteParentCategoryId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Delete Parent Category with children", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Delete Category Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(1);", + "});", + "", + "pm.test(\"Failed To Delete Category Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "[\"{{deleteParentCategoryId}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Delete Category Failed - NotFound", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Delete Category Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Delete Category Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Sort order", + "item": [ + { + "name": "Create top level category 1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Top level category 1\");", + "});", + "", + "pm.collectionVariables.set(\"categoryToSortId1\", jsonData.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Top level category 1 key\",\n \"categoryName\" : \"Top level category 1\",\n \"keywords\":\"This is a test category\",\n \"categoryVelocityVarName\" : \"TLC1\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create top level category 2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Top level category 2\");", + "});", + "", + "pm.collectionVariables.set(\"categoryToSortId2\", jsonData.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Top level category 2 key\",\n \"categoryName\" : \"Top level category 2\",\n \"keywords\":\"This is a test category\",\n \"categoryVelocityVarName\" : \"TLC2\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Update top level category failed - InvalidData", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Empty categories data message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eql(\"The body must send a collection of category inode and sortOrder\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + }, + { + "name": "Update top level category failed - NotFound", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Not found categories data message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eql(\"Category with Id: 12345678-abcd-1234-abcd-123456789abc does not exist\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"\",\n \"categoryData\" : {\"12345678-abcd-1234-abcd-123456789abc\" : 1 }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + }, + { + "name": "Update top level category for sort order - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Update Category Check\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"\",\n \"categoryData\" : {\"{{categoryToSortId1}}\" : 1,\n \"{{categoryToSortId2}}\" : 2 }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + }, + { + "name": "Create Parent Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Parent category name\");", + "});", + "", + "pm.collectionVariables.set(\"parentCategoryId\", jsonData.inode);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Parent category key\",\n \"categoryName\" : \"Parent category name\",\n \"keywords\":\"This is a parent category\",\n \"categoryVelocityVarName\" : \"ParentCategoryKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Child Category 1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Child 1 category name\");", + "});", + "", + "pm.collectionVariables.set(\"categoryToSortId1\", jsonData.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child 1 category key\",\n \"categoryName\" : \"Child 1 category name\",\n \"keywords\":\"This is a child 1 category\",\n \"categoryVelocityVarName\" : \"Child1CategoryKey\",\n \"parent\" : \"{{parentCategoryId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Child Category 2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Child 2 category name\");", + "});", + "", + "pm.collectionVariables.set(\"categoryToSortId2\", jsonData.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child 2 category key\",\n \"categoryName\" : \"Child 2 category name\",\n \"keywords\":\"This is a child 2 category\",\n \"categoryVelocityVarName\" : \"Child2CategoryKey\",\n \"parent\" : \"{{parentCategoryId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Update category failed - InvalidData", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Empty categories data message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eql(\"The body must send a collection of category inode and sortOrder\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"{{parentCategoryId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + }, + { + "name": "Update category failed - NotFound", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Not found categories data message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eql(\"Category with Id: NOTFOUNDCONTAINER does not exist\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"{{parentCategoryId}}\",\n \"categoryData\" : {\"NOTFOUNDCONTAINER\" : 1 }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + }, + { + "name": "Update children categories for sort order - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Update Category Check\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"filter\" : \"\",\n \"page\" : 0,\n \"perPage\" : 10,\n \"direction\" : \"ASC\",\n \"parentInode\" : \"{{parentCategoryId}}\",\n \"categoryData\" : {\"{{categoryToSortId1}}\" : 1,\n \"{{categoryToSortId2}}\" : 2 }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_sort", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_sort" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Import", + "item": [ + { + "name": "Import - merge - success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "filter", + "value": "", + "type": "default" + }, + { + "key": "exportType", + "value": "merge", + "type": "default" + }, + { + "key": "contextInode", + "value": "", + "type": "default" + }, + { + "key": "file", + "type": "file", + "src": "/home/hmb-g8/Desktop/categories_10_6_2022.csv" + } + ], + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_import", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_import" + ] + } + }, + "response": [] + }, + { + "name": "Import - replace - success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "filter", + "value": "", + "type": "default" + }, + { + "key": "exportType", + "value": "replace", + "type": "default" + }, + { + "key": "contextInode", + "value": "", + "type": "default" + }, + { + "key": "file", + "type": "file", + "src": "/home/hmb-g8/Desktop/categories_10_6_2022.csv" + } + ], + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories/_import", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_import" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Export", + "item": [ + { + "name": "export", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories/_export?contextInode=&filter=", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "_export" + ], + "query": [ + { + "key": "contextInode", + "value": "" + }, + { + "key": "filter", + "value": "" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Create New Category without parent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Test Category\");", + "});", + "", + "pm.collectionVariables.set(\"inode\", jsonData.inode);", + "pm.collectionVariables.set(\"categoryINodeId\", jsonData.inode);", + "pm.collectionVariables.set(\"categoryINodeIdToUpdate\", jsonData.inode);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Test key\",\n \"categoryName\" : \"Test Category\",\n \"keywords\":\"This is a test category\",\n \"categoryVelocityVarName\" : \"TestKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create New Category with parent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Child Test Category\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child test key\",\n \"categoryName\" : \"Child Test Category\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"ChildTestKey\",\n \"parent\" : \"{{categoryINodeId}}\"\n}\n\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create New Category without name BadRequest", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Category name required message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eq(\"The category name is required\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child test key\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"ChildTestKey\",\n \"parent\" : \"{{categoryINodeId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Get children categories by inode (aka Parent id) - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories/children?filter=&page=0&per_page=5&ordeby=&direction=&inode={{categoryINodeId}}", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "children" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "ordeby", + "value": "" + }, + { + "key": "direction", + "value": "" + }, + { + "key": "inode", + "value": "{{categoryINodeId}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get children categories without inode - BadRequest", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch successfully with error message\", function () {", + " pm.expect(jsonData.message).to.eq(\"The inode is required\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories/children?filter=&page=0&per_page=5&ordeby=&direction=&inode=", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories", + "children" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "ordeby", + "value": "" + }, + { + "key": "direction", + "value": "" + }, + { + "key": "inode", + "value": "" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Category without parent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.categoryName).to.eql(\"Updated name\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"inode\" : \"{{categoryINodeIdToUpdate}}\",\n \"key\":\"Updated key\",\n \"categoryName\" : \"Updated name\",\n \"keywords\":\"Updated keywords\",\n \"categoryVelocityVarName\" : \"TestKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Update Category without inode", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Category inode required message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eq(\"The inode is required\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Updated key\",\n \"categoryName\" : \"Updated name\",\n \"keywords\":\"Updated keywords\",\n \"categoryVelocityVarName\" : \"TestKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Update Category invalid inode - NotFound", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 404\", function () {", + " pm.response.to.have.status(404);", + "});", + "", + "pm.test(\"Category inode required message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eq(\"Category with inode: 12345678-abcd-1234-abcd-123456789abc does not exist\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"inode\" : \"12345678-abcd-1234-abcd-123456789abc\",\n \"key\":\"Updated key\",\n \"categoryName\" : \"Updated name\",\n \"keywords\":\"Updated keywords\",\n \"categoryVelocityVarName\" : \"TestKey\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Get categories - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=&page=0&per_page=5&ordeby=&direction=", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "ordeby", + "value": "" + }, + { + "key": "direction", + "value": "" + } + ] + } + }, + "response": [] + }, + { + "name": "Get categories with showChildrenCount=true - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch childrenCount successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + " ", + " for(var i=0; i < jsonData.entity.length; i++){", + " pm.expect(jsonData.entity[i]).to.have.property('childrenCount');", + " }", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=&page=0&per_page=5&ordeby=category_name&direction=ASC&showChildrenCount=true", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "ordeby", + "value": "category_name" + }, + { + "key": "direction", + "value": "ASC" + }, + { + "key": "showChildrenCount", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "Get categories with showChildrenCount=false - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch childrenCount successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + " ", + " for(var i=0; i < jsonData.entity.length; i++){", + " pm.expect(jsonData.entity[i]).to.not.have.property('childrenCount');", + " }", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=&page=0&per_page=5&ordeby=category_name&direction=ASC&showChildrenCount=false", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "ordeby", + "value": "category_name" + }, + { + "key": "direction", + "value": "ASC" + }, + { + "key": "showChildrenCount", + "value": "false" + } + ] + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "templateIdToEdit", + "value": "" + }, + { + "key": "inode", + "value": "" + }, + { + "key": "categoryINodeId", + "value": "" + }, + { + "key": "categoryINodeIdToUpdate", + "value": "" + }, + { + "key": "deleteParentCategoryId", + "value": "" + }, + { + "key": "categoryToSortId1", + "value": "" + }, + { + "key": "categoryToSortId2", + "value": "" + }, + { + "key": "parentCategoryId", + "value": "" + }, + { + "key": "parentCategoryToSortId", + "value": "" + } + ] +} diff --git a/dotCMS/src/curl-test/Containers.postman_collection.json b/dotCMS/src/curl-test/Containers.postman_collection.json index 5b9ff405a23b..915ea74043bf 100644 --- a/dotCMS/src/curl-test/Containers.postman_collection.json +++ b/dotCMS/src/curl-test/Containers.postman_collection.json @@ -1,11 +1,2911 @@ { "info": { - "_postman_id": "4fc60ba5-b23a-4bc0-a910-34bbee3f021d", + "_postman_id": "98343aa6-b3da-4fde-8ea5-454c58117b23", "name": "Containers", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "4500400" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ + { + "name": "Bulk Delete", + "item": [ + { + "name": "Create New Container 1 To Delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeDeletedContainer1\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToDeleteId1\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeDeletedContainer1\",\n \"friendlyName\":\"ToBeDeletedContainer1 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Create New Container 2 To Delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeDeletedContainer2\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToDeleteId2\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeDeletedContainer2\",\n \"friendlyName\":\"ToBeDeletedContainer2 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Delete Container Failed To Delete Container Id Not Exist", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Delete Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Delete Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkdelete", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkdelete" + ] + }, + "description": "Tries to delete a template but the id passed does not belong to any template so it fails." + }, + "response": [] + }, + { + "name": "Delete Container Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Delete Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Delete Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "[\"{{containerToDeleteId1}}\",\"{{containerToDeleteId2}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkdelete", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkdelete" + ] + }, + "description": "Delete a template successfully" + }, + "response": [] + } + ] + }, + { + "name": "Bulk Publish", + "item": [ + { + "name": "Create New Container 1 To Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBePublishedContainer1\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToPublishId1\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBePublishedContainer1\",\n \"friendlyName\":\"ToBePublishedContainer1 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Create New Container 2 To Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBePublishedContainer2\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToPublishId2\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBePublishedContainer2\",\n \"friendlyName\":\"ToBePublishedContainer2 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Publish Invalid ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Publish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Publish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\" \",\" \"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkpublish" + ] + }, + "description": "try to unpublish an invalid container" + }, + "response": [] + }, + { + "name": "Publish Not Found ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Publish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Publish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkpublish" + ] + }, + "description": "Try to unpublish a non existing container" + }, + "response": [] + }, + { + "name": "Publish Container Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Publish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Publish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToPublishId1}}\",\"{{containerToPublishId2}}\"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkpublish" + ] + }, + "description": "Publish a container" + }, + "response": [] + } + ] + }, + { + "name": "Bulk Unpublish", + "item": [ + { + "name": "Create New Container 1 To Unpublish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeUnpublishedContainer1\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToUnpublishId1\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeUnpublishedContainer1\",\n \"friendlyName\":\"ToBeUnpublishedContainer1 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Create New Container 2 To Unpublish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeUnpublishedContainer2\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToUnpublishId2\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeUnpublishedContainer2\",\n \"friendlyName\":\"ToBeUnpublishedContainer2 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Unpublish Invalid ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Unpublish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Unpublish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\" \",\" \"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunpublish" + ] + }, + "description": "try to unpublish an invalid container" + }, + "response": [] + }, + { + "name": "Unpublish Not Found ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Unpublish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Unpublish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunpublish" + ] + }, + "description": "Try to unpublish a non existing container" + }, + "response": [] + }, + { + "name": "Publish Container Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Publish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Publish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToUnpublishId1}}\",\"{{containerToUnpublishId2}}\"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkpublish" + ] + }, + "description": "Publish a container" + }, + "response": [] + }, + { + "name": "Unpublish Container Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Publish Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Publish Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToUnpublishId1}}\",\"{{containerToUnpublishId2}}\"]" + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunpublish", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunpublish" + ] + }, + "description": "Publish a container" + }, + "response": [] + } + ] + }, + { + "name": "Bulk Archive", + "item": [ + { + "name": "Create New Container 1 To Archive", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeArchivedContainer1\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToArchiveId1\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeArchivedContainer1\",\n \"friendlyName\":\"ToBeArchivedContainer1 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Create New Container 2 To Archive", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeArchivedContainer2\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToArchiveId2\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeArchivedContainer2\",\n \"friendlyName\":\"ToBeArchivedContainer2 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Archive Container Invalid ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Archive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Archive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\" \",\" \"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkarchive" + ] + }, + "description": "Archive an invalid container" + }, + "response": [] + }, + { + "name": "Archive Container Not Found ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Archive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Archive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkarchive" + ] + }, + "description": "Archive a non existing container" + }, + "response": [] + }, + { + "name": "Archive Container", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Archive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Archive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToArchiveId1}}\",\"{{containerToArchiveId2}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkarchive" + ] + }, + "description": "archive a container" + }, + "response": [] + } + ] + }, + { + "name": "Bulk Unarchive", + "item": [ + { + "name": "Create New Container 1 To Unarchive", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeUnarchivedContainer1\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToUnarchiveId1\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeUnarchivedContainer1\",\n \"friendlyName\":\"ToBeUnarchivedContainer1 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Create New Container 2 To Unarchive", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"ToBeUnarchivedContainer2\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "pm.collectionVariables.set(\"containerToUnarchiveId2\", jsonData.entity.identifier);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"ToBeUnarchivedContainer2\",\n \"friendlyName\":\"ToBeUnarchivedContainer2 description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Unarchive Container Invalid ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\" \",\" \"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunarchive" + ] + }, + "description": "Archive an invalid container" + }, + "response": [] + }, + { + "name": "Unarchive Container Not Found ContainerId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(0);", + "});", + "", + "pm.test(\"Failed To Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(2);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"NOTFOUNDCONTAINER\",\"12345678-abcd-1234-abcd-123456789abc\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunarchive" + ] + }, + "description": "Archive a non existing container" + }, + "response": [] + }, + { + "name": "Archive Container", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Archive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Archive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToUnarchiveId1}}\",\"{{containerToUnarchiveId2}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkarchive" + ] + }, + "description": "archive a container" + }, + "response": [] + }, + { + "name": "Unarchive Container", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.successCount).to.be.eql(2);", + "});", + "", + "pm.test(\"Failed To Unarchive Container Check\", function () {", + " pm.expect(jsonData.entity.fails).to.have.lengthOf(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{containerToUnarchiveId1}}\",\"{{containerToUnarchiveId2}}\"]", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/_bulkunarchive", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "_bulkunarchive" + ] + }, + "description": "archive a container" + }, + "response": [] + } + ] + }, + { + "name": "Copy", + "item": [ + { + "name": "CopyContainerCreateNewContainer", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"abcd\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "", + "pm.collectionVariables.set(\"containerToCopyId\", jsonData.entity.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"abcd\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "CopyContainer", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [] + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers/{{containerToCopyId}}/_copy", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "{{containerToCopyId}}", + "_copy" + ] + }, + "description": "Deletes the container" + }, + "response": [] + }, + { + "name": "CopyContainerNotFoundContainer", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Not found container message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.contains(\"Can't find Container:12345678-abcd-1234-abcd-123456789abc\"); ", + "});", + "", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{serverURL}}/api/v1/containers/12345678-abcd-1234-abcd-123456789abc/_copy", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "12345678-abcd-1234-abcd-123456789abc", + "_copy" + ] + }, + "description": "Deletes the container" + }, + "response": [] + }, + { + "name": "CopyContainerEmptyId", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Empty container id message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.eql(\"The container id is required\"); ", + "});", + "", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{serverURL}}/api/v1/containers/ /_copy", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + " ", + "_copy" + ] + }, + "description": "Deletes the container" + }, + "response": [] + } + ] + }, + { + "name": "Save new", + "item": [ + { + "name": "CreateNewContainer", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "CreateNewContainer with more than 1 containerStructures", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":2,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "CreateNewContainer with maxContentlets 0", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":0,\n \"code\" : \"code\",\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + } + ] + }, + { + "name": "get working", + "item": [ + { + "name": "Add Container with containerStructures", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", + " ", + " ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"containderIdentifierToGetDetails\", jsonData.entity.identifier);", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":2,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Get working with content types- Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{serverURL}}/api/v1/containers/working?containerId={{containderIdentifierToGetDetails}}&includeContentType=true", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "working" + ], + "query": [ + { + "key": "containerId", + "value": "{{containderIdentifierToGetDetails}}" + }, + { + "key": "includeContentType", + "value": "true" + } + ] + }, + "description": "Get the working version of a container" + }, + "response": [] + }, + { + "name": "Get details failed - NotFound", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});", + "", + "pm.test(\"Not found container message\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.message).to.contains(\"Can't find Container:12345678-abcd-1234-abcd-123456789abc\"); ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{serverURL}}/api/v1/containers/working?containerId=12345678-abcd-1234-abcd-123456789abc", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "working" + ], + "query": [ + { + "key": "containerId", + "value": "12345678-abcd-1234-abcd-123456789abc" + } + ] + }, + "description": "Get the working version of a container" + }, + "response": [] + } + ] + }, + { + "name": "Update Container", + "item": [ + { + "name": "CreateNewContainer with maxContentlets 0", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"Test Container to update\"); ", + "});", + "", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set(\"identifierForUpdateContainer\", jsonData.entity.identifier);", + "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\":\"Test Container to update\",\n \"friendlyName\":\"Test Container to update description\",\n \"maxContentlets\":0,\n \"code\" : \"code\",\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Create a container" + }, + "response": [] + }, + { + "name": "Edit Containe with code - success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes 1\");", + " ", + " ", + "});", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"identifier\":\"{{identifierForUpdateContainer}}\",\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":0,\n \"code\" : \"new code\",\n \"notes\":\"Notes 1\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/containers", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers" + ] + }, + "description": "Edit the existing container" + }, + "response": [] + } + ] + }, { "name": "pre_ImportBundleWithApplicationContainerFolder", "event": [ @@ -1276,7 +4176,7 @@ "pm.test(\"Information Saved Correctly\", function () {", " ", " var jsonData = pm.response.json();", - " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.title).to.eql(\"abcd\");", " pm.expect(jsonData.entity.notes).to.eql(\"Notes\");", " ", " ", @@ -1286,7 +4186,7 @@ "pm.collectionVariables.set(\"identifier\", jsonData.entity.identifier);", "pm.collectionVariables.set(\"inode\", jsonData.entity.inode);", "", - "", + "pm.collectionVariables.set(\"containerToCopyId\", jsonData.entity.identifier);", "" ], "type": "text/javascript" @@ -1298,13 +4198,13 @@ "type": "basic", "basic": [ { - "key": "username", - "value": "admin@dotcms.com", + "key": "password", + "value": "admin", "type": "string" }, { - "key": "password", - "value": "admin", + "key": "username", + "value": "admin@dotcms.com", "type": "string" } ] @@ -1320,7 +4220,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"title\":\"TestContainer\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", + "raw": "{\n \"title\":\"abcd\",\n \"friendlyName\":\"TestContainer description\",\n \"maxContentlets\":1,\n \"notes\":\"Notes\",\n \"preLoop\":\"preLoop xxxx\",\n \"postLoop\":\"postLoop xxxx\",\n \"containerStructures\":[\n {\n \"structureId\":\"webPageContent\",\n \"code\":\" code xxxx\"\n },\n {\n \"structureId\":\"DotAsset\",\n \"code\":\" tags: $!{tags}\"\n }\n ]\n\n}", "options": { "raw": { "language": "json" @@ -1499,6 +4399,82 @@ } } ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{serverURL}}/api/v1/containers/working?containerId={{identifier}}", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "containers", + "working" + ], + "query": [ + { + "key": "containerId", + "value": "{{identifier}}" + } + ] + }, + "description": "Get the working version of a container" + }, + "response": [] + }, + { + "name": "Get containers", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"No errors\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.errors.length).to.eql(0);", + "});", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.entity.title).to.eql(\"TestContainer\");", + " pm.expect(jsonData.entity.notes).to.eql(\"Notes 1\");", + " ", + " ", + "});", + "", + "", + "" + ], + "type": "text/javascript" + } + } + ], "request": { "auth": { "type": "basic", @@ -2504,6 +5480,62 @@ { "key": "folderinode", "value": "" + }, + { + "key": "containerToCopyId", + "value": "" + }, + { + "key": "containerToDeleteId", + "value": "" + }, + { + "key": "containerToDeleteId1", + "value": "" + }, + { + "key": "containerToDeleteId2", + "value": "" + }, + { + "key": "containerToPublishId1", + "value": "" + }, + { + "key": "containerToPublishId2", + "value": "" + }, + { + "key": "containerToUnpublishId1", + "value": "" + }, + { + "key": "containerToUnpublishId2", + "value": "" + }, + { + "key": "containerToArchiveId1", + "value": "" + }, + { + "key": "containerToArchiveId2", + "value": "" + }, + { + "key": "containerToUnarchiveId1", + "value": "" + }, + { + "key": "containerToUnarchiveId2", + "value": "" + }, + { + "key": "containderIdentifierToGetDetails", + "value": "" + }, + { + "key": "identifierForUpdateContainer", + "value": "" } ] -} \ No newline at end of file +} diff --git a/dotCMS/src/integration-test/resources/messages/Language_en_US.properties b/dotCMS/src/integration-test/resources/messages/Language_en_US.properties index b305d3b51ee3..4c8e40a12984 100644 --- a/dotCMS/src/integration-test/resources/messages/Language_en_US.properties +++ b/dotCMS/src/integration-test/resources/messages/Language_en_US.properties @@ -1485,6 +1485,12 @@ message.template.undelete=Template Un-archived message.template.unlocked=Template Un-locked template-name=Template name +## Containers Manager +message.container.delete.version=Are you sure you would like to delete this container version? + +## Categories Manager +message.category.delete.version=Are you sure you would like to delete this category version? + ## HTML Pages Manager Filter-By-Template=Filter By Template End-Date=End Date diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java index a482dfd267b9..6e46fe180f06 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java @@ -1,65 +1,173 @@ package com.dotcms.rest.api.v1.categories; +import com.dotcms.business.WrapInTransaction; import com.dotcms.exception.ExceptionUtil; import com.dotcms.repackage.com.google.common.annotations.VisibleForTesting; import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.ResponseEntityView; import com.dotcms.rest.WebResource; import com.dotcms.rest.annotation.NoCache; +import com.dotcms.rest.api.BulkResultView; +import com.dotcms.rest.api.FailedResultView; +import com.dotcms.rest.api.v1.DotObjectMapperProvider; import com.dotcms.rest.exception.ForbiddenException; import com.dotcms.rest.exception.mapper.ExceptionMapperUtil; +import com.dotcms.util.CloseUtils; +import com.dotcms.util.DotPreconditions; import com.dotcms.util.PaginationUtil; import com.dotcms.util.pagination.CategoriesPaginator; +import com.dotcms.util.pagination.CategoryListDTOPaginator; +import com.dotmarketing.beans.Host; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.business.PermissionAPI; +import com.dotmarketing.business.VersionableAPI; +import com.dotmarketing.business.web.HostWebAPI; +import com.dotmarketing.business.web.WebAPILocator; +import com.dotmarketing.exception.DoesNotExistException; +import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.portlets.categories.business.CategoryAPI; +import com.dotmarketing.portlets.categories.business.PaginatedCategories; +import com.dotmarketing.portlets.categories.model.Category; +import com.dotmarketing.util.ActivityLogger; import com.dotmarketing.util.Logger; +import com.dotmarketing.util.PageMode; +import com.dotmarketing.util.PaginatedArrayList; +import com.dotmarketing.util.UtilMethods; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.liferay.portal.model.User; +import com.liferay.util.StringPool; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import org.apache.commons.beanutils.BeanUtils; +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataMultiPart; +import org.glassfish.jersey.media.multipart.FormDataParam; +import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.server.JSONP; /** - * This resource provides all the different end-points associated to information - * and actions that the front-end can perform on the Categories. + * This resource provides all the different end-points associated to information and actions that + * the front-end can perform on the Categories. */ @Path("/v1/categories") public class CategoriesResource { private final WebResource webResource; private final PaginationUtil paginationUtil; + private final PaginationUtil extendedPaginationUtil; + + private final CategoryAPI categoryAPI; + private final VersionableAPI versionableAPI; + + private final HostWebAPI hostWebAPI; + private final PermissionAPI permissionAPI; + private final CategoryHelper categoryHelper; + public CategoriesResource() { - this(new WebResource(), new PaginationUtil( new CategoriesPaginator() ) ); + this(new WebResource(), new PaginationUtil(new CategoriesPaginator()), + new PaginationUtil(new CategoryListDTOPaginator()), + APILocator.getCategoryAPI(), + APILocator.getVersionableAPI(), + WebAPILocator.getHostWebAPI(), + APILocator.getPermissionAPI(), + new CategoryHelper(APILocator.getCategoryAPI())); } @VisibleForTesting - public CategoriesResource(final WebResource webresource, final PaginationUtil paginationUtil) { + public CategoriesResource(final WebResource webresource, final PaginationUtil paginationUtil, + final PaginationUtil extendedPaginationUtil, + final CategoryAPI categoryAPI, final VersionableAPI versionableAPI, + final HostWebAPI hostWebAPI, final PermissionAPI permissionAPI, + final CategoryHelper categoryHelper) { this.webResource = webresource; this.paginationUtil = paginationUtil; + this.extendedPaginationUtil = extendedPaginationUtil; + this.categoryAPI = categoryAPI; + this.versionableAPI = versionableAPI; + this.hostWebAPI = hostWebAPI; + this.permissionAPI = permissionAPI; + this.categoryHelper = new CategoryHelper(categoryAPI); } + /** + * Returns a response of ResponseEntityView with PaginatedArrayList of categories + * syntax:. + *

+ * Url syntax: + * api/v1/categories?filter=filter-string&page=page-number&per_page=per-page&orderby=order-field-name&direction=order-direction&showChildrenCount=true + *

+ * where: + * + *

    + *
  • filter-string: just return Category whose content this pattern into its name
  • + *
  • page: page to return
  • + *
  • per_page: limit of items to return
  • + *
  • ordeby: field to order by
  • + *
  • direction: asc for upward order and desc for downward order
  • + *
  • showChildrenCount: true for including children categories count and false to exclude it
  • + *
+ *

+ * Url example: /api/v1/categories?filter=&page=0&per_page=5&ordeby=category_name&direction=ASC&showChildrenCount=true + * + * @param httpRequest + * @return + */ @GET @JSONP @NoCache - @Produces({ MediaType.APPLICATION_JSON, "application/javascript" }) + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public final Response getCategories(@Context final HttpServletRequest httpRequest, - @Context final HttpServletResponse httpResponse, - @QueryParam(PaginationUtil.FILTER) final String filter, - @QueryParam(PaginationUtil.PAGE) final int page, - @QueryParam(PaginationUtil.PER_PAGE) final int perPage) { + @Context final HttpServletResponse httpResponse, + @QueryParam(PaginationUtil.FILTER) final String filter, + @QueryParam(PaginationUtil.PAGE) final int page, + @QueryParam(PaginationUtil.PER_PAGE) final int perPage, + @DefaultValue("category_name") @QueryParam(PaginationUtil.ORDER_BY) final String orderBy, + @DefaultValue("ASC") @QueryParam(PaginationUtil.DIRECTION) final String direction, + @QueryParam("showChildrenCount") final boolean showChildrenCount) { - final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, null); + final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, + null); Response response = null; final User user = initData.getUser(); + Logger.debug(this, () -> "Getting the List of categories. " + String.format( + "Request query parameters are : {filter : %s, page : %s, perPage : %s}", filter, + page, perPage)); + try { - response = this.paginationUtil.getPage( httpRequest, user, filter, page, perPage ); + response = showChildrenCount == false ? this.paginationUtil.getPage(httpRequest, user, filter, page, perPage) : this.extendedPaginationUtil.getPage(httpRequest, user, filter, page, perPage); } catch (Exception e) { Logger.error(this, e.getMessage(), e); if (ExceptionUtil.causedBy(e, DotSecurityException.class)) { @@ -70,4 +178,601 @@ public final Response getCategories(@Context final HttpServletRequest httpReques return response; } + + /** + * Return a list of {@link com.dotmarketing.portlets.categories.model.Category}, entity response + * syntax:. + * + * { contentTypes: array of Category total: total number of Categories } + *

+ * Url syntax: + * api/v1/categories/children?filter=filter-string&page=page-number&per_page=per-page&orderby=order-field-name&direction=order-direction&inode=parentId + *

+ * where: + * + *

    + *
  • filter-string: just return Category whose content this pattern into its name
  • + *
  • page: page to return
  • + *
  • per_page: limit of items to return
  • + *
  • ordeby: field to order by
  • + *
  • direction: asc for upward order and desc for downward order
  • + *
+ *

+ * Url example: v1/categories/children?filter=test&page=2&orderby=categoryName + * + * @param httpRequest + * @return + */ + @GET + @Path(("/children")) + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response getChildren(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + @QueryParam(PaginationUtil.FILTER) final String filter, + @QueryParam(PaginationUtil.PAGE) final int page, + @QueryParam(PaginationUtil.PER_PAGE) final int perPage, + @DefaultValue("categoryName") @QueryParam(PaginationUtil.ORDER_BY) final String orderBy, + @DefaultValue("ASC") @QueryParam(PaginationUtil.DIRECTION) final String direction, + @QueryParam("inode") final String inode) throws DotDataException, DotSecurityException { + + final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, + null); + + Response response = null; + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(httpRequest); + + Logger.debug(this, () -> "Getting the List of children categories. " + String.format( + "Request query parameters are : {filter : %s, page : %s, perPage : %s, orderBy : %s, direction : %s, inode : %s}", + filter, page, perPage, orderBy, direction, inode)); + + DotPreconditions.checkArgument(UtilMethods.isSet(inode), + "The inode is required"); + + PaginatedCategories list = this.categoryAPI.findChildren(user, inode, + pageMode.respectAnonPerms, page, perPage, + filter, direction); + + return getPage(list.getCategories(), list.getTotalCount(), page, perPage); + } + + /** + * Saves a new working version of a category. + * + * @param httpRequest + * @param httpResponse + * @param categoryForm + * @return CategoryView + * @throws DotDataException + * @throws DotSecurityException + */ + @POST + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final CategoryView saveNew(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + final CategoryForm categoryForm) + throws DotDataException, DotSecurityException { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(httpRequest, httpResponse).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final Host host = this.categoryHelper.getHost(categoryForm.getSiteId(), + () -> this.hostWebAPI.getCurrentHostNoThrow(httpRequest)); + final PageMode pageMode = PageMode.get(httpRequest); + + Logger.debug(this, () -> "Saving category. Request payload is : " + getObjectToJsonString( + categoryForm)); + + DotPreconditions.checkArgument(UtilMethods.isSet(categoryForm.getCategoryName()), + "The category name is required"); + + try { + return this.categoryHelper.toCategoryView( + this.fillAndSave(categoryForm, user, host, pageMode, new Category()), user); + } catch (InvocationTargetException | IllegalAccessException e) { + Logger.error(this, e.getMessage(), e); + throw new RuntimeException(e); + } + } + + /** + * Update a working version of an existing category. The categoryForm must contain the inode of + * the category. + * + * @param httpRequest {@link HttpServletRequest} + * @param httpResponse {@link HttpServletResponse} + * @param categoryForm {@link CategoryForm} + * @return CategoryView + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final CategoryView save(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + final CategoryForm categoryForm) throws DotDataException, DotSecurityException { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(httpRequest, httpResponse).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final Host host = this.categoryHelper.getHost(categoryForm.getSiteId(), + () -> this.hostWebAPI.getCurrentHostNoThrow(httpRequest)); + final PageMode pageMode = PageMode.get(httpRequest); + + Logger.debug(this, () -> "Saving category. Request payload is : " + getObjectToJsonString( + categoryForm)); + + DotPreconditions.checkArgument(UtilMethods.isSet(categoryForm.getInode()), + "The inode is required"); + + final Category oldCategory = this.categoryAPI.find(categoryForm.getInode(), user, + pageMode.respectAnonPerms); + + if (null == oldCategory) { + throw new DoesNotExistException( + "Category with inode: " + categoryForm.getInode() + " does not exist"); + } + + try { + return this.categoryHelper.toCategoryView( + this.fillAndSave(categoryForm, user, host, pageMode, oldCategory, + new Category()), user); + } catch (InvocationTargetException | IllegalAccessException e) { + Logger.error(this, e.getMessage(), e); + throw new RuntimeException(e); + } + } + + /** + * Update a working version of an existing category for sortOrder. The categoryEditDTO must + * contain the inode and sortOrder of the category. + * + * @param httpRequest {@link HttpServletRequest} + * @param httpResponse {@link HttpServletResponse} + * @param categoryEditForm {@link CategoryForm} + * @return CategoryView + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @Path("/_sort") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response save(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + final CategoryEditForm categoryEditForm + ) throws DotDataException, DotSecurityException { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(httpRequest, httpResponse).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final Host host = this.categoryHelper.getHost(categoryEditForm.getSiteId(), + () -> this.hostWebAPI.getCurrentHostNoThrow(httpRequest)); + final PageMode pageMode = PageMode.get(httpRequest); + + Logger.debug(this, + () -> "Saving category sortOrder. Request payload is : " + getObjectToJsonString( + categoryEditForm)); + + DotPreconditions.checkArgument(UtilMethods.isSet(categoryEditForm.getCategoryData()), + "The body must send a collection of category inode and sortOrder"); + + Category parentCategory = null; + + if (UtilMethods.isSet(categoryEditForm.getParentInode())) { + parentCategory = this.categoryAPI.find(categoryEditForm.getParentInode(), user, + pageMode.respectAnonPerms); + } + + updateSortOrder(categoryEditForm, user, host, pageMode, parentCategory); + + return parentCategory == null + ? this.paginationUtil.getPage(httpRequest, user, categoryEditForm.getFilter(), + categoryEditForm.getPage(), categoryEditForm.getPerPage()) + : this.getChildren(httpRequest, httpResponse, categoryEditForm.getFilter(), + categoryEditForm.getPage(), + categoryEditForm.getPerPage(), "", categoryEditForm.getDirection(), + categoryEditForm.getParentInode()); + } + + /** + * Deletes Categories. + *

+ * This method receives a list of inodes and deletes all the children and the parent categories. + * To delete a category successfully the user needs to have Edit Permissions over it. + * + * @param httpRequest {@link HttpServletRequest} + * @param httpResponse {@link HttpServletResponse} + * @param categoriesToDelete {@link String} category inode to look for and then delete it + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @DELETE + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response delete(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + final List categoriesToDelete) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(httpRequest, httpResponse).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(httpRequest); + final List failedToDelete = new ArrayList<>(); + List deletedIds = new ArrayList(); + + DotPreconditions.checkArgument(UtilMethods.isSet(categoriesToDelete), + "The body must send a collection of category inode such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + try { + HashMap undeletedCategoryList = this.categoryAPI.deleteCategoryAndChildren( + categoriesToDelete, user, pageMode.respectAnonPerms); + List undeletedIds = undeletedCategoryList.entrySet().stream() + .map(k -> k.getKey()).collect( + Collectors.toUnmodifiableList()); + deletedIds = new ArrayList<>(categoriesToDelete); + deletedIds.removeAll(undeletedIds); + + ActivityLogger.logInfo(this.getClass(), "Delete Category Action", "User " + + user.getPrimaryKey() + " deleted category list: [" + String.join(",", + deletedIds) + "]"); + + if (!undeletedCategoryList.isEmpty()) { + for (final String categoryInode : undeletedIds) { + Logger.error(this, "Category with Id: " + categoryInode + " does not exist"); + failedToDelete.add(new FailedResultView(categoryInode, + "Category does not exist or failed to remove child category")); + } + } + } catch (Exception e) { + Logger.debug(this, e.getMessage(), e); + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(Long.valueOf(deletedIds.size()), 0L, failedToDelete))) + .build(); + } + + private Category fillAndSave(final CategoryForm categoryForm, + final User user, + final Host host, + final PageMode pageMode, + final Category category) + throws DotSecurityException, DotDataException, InvocationTargetException, IllegalAccessException { + + Category parentCategory = null; + + Logger.debug(this, () -> "Filling category entity"); + + if (UtilMethods.isSet(categoryForm.getParent())) { + parentCategory = this.categoryAPI.find(categoryForm.getParent(), user, + pageMode.respectAnonPerms); + } + + BeanUtils.copyProperties(category, categoryForm); + + category.setModDate(new Date()); + + Logger.debug(this, () -> "Saving category entity : " + getObjectToJsonString(category)); + this.categoryAPI.save(parentCategory, category, user, pageMode.respectAnonPerms); + Logger.debug(this, () -> "Saved category entity : " + getObjectToJsonString(category)); + + ActivityLogger.logInfo(this.getClass(), "Saved Category", "User " + user.getPrimaryKey() + + "Category: " + category.getCategoryName(), + host.getTitle() != null ? host.getTitle() : "default"); + + return category; + } + + private Category fillAndSave(final CategoryForm categoryForm, + final User user, + final Host host, + final PageMode pageMode, + final Category oldCategory, + final Category updatedCategory) + throws DotSecurityException, DotDataException, InvocationTargetException, IllegalAccessException { + + Category parentCategory = null; + + Logger.debug(this, () -> "Filling category entity"); + + if (UtilMethods.isSet(categoryForm.getParent())) { + parentCategory = this.categoryAPI.find(categoryForm.getParent(), user, + pageMode.respectAnonPerms); + } + + BeanUtils.copyProperties(updatedCategory, oldCategory); + + updatedCategory.setCategoryName(categoryForm.getCategoryName()); + updatedCategory.setKey(categoryForm.getKey()); + updatedCategory.setKeywords(categoryForm.getKeywords()); + updatedCategory.setModDate(new Date()); + + Logger.debug(this, + () -> "Saving category entity : " + getObjectToJsonString(updatedCategory)); + this.categoryAPI.save(parentCategory, updatedCategory, user, pageMode.respectAnonPerms); + Logger.debug(this, + () -> "Saved category entity : " + getObjectToJsonString(updatedCategory)); + + ActivityLogger.logInfo(this.getClass(), "Saved Category", "User " + user.getPrimaryKey() + + "Category: " + updatedCategory.getCategoryName(), + host.getTitle() != null ? host.getTitle() : "default"); + + return updatedCategory; + } + + /** + * Return a list of {@link com.dotmarketing.portlets.categories.model.Category}, entity response + * syntax: + * { contentTypes: array of Category total: total number of Categories } + *

+ * Url syntax: api/v1/categories/_export?contextInode=inode&filter=filter-string + *

+ * where: + * + *

    + *
  • filter-string: just return Category whose content this pattern into its name
  • + *
  • contextInode: category inode
  • + *
+ *

+ * Url example: v1/categories/_export?contextInode=inode&filter=test + * + * @param httpRequest + * @return + */ + @GET + @Path("/_export") + @JSONP + @NoCache + @Produces({"text/csv"}) + public final void export(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + @QueryParam("contextInode") final String contextInode, + @QueryParam(PaginationUtil.FILTER) final String filter) + throws DotDataException, DotSecurityException, IOException { + + final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, + null); + + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(httpRequest); + + httpResponse.setCharacterEncoding("UTF-8"); + httpResponse.setContentType("application/octet-stream"); + httpResponse.setHeader("Content-Disposition", + "attachment; filename=\"categories_" + UtilMethods.dateToHTMLDate(new Date(), + "M_d_yyyy") + ".csv\""); + + Logger.debug(this, () -> "Exporting the list of categories. " + String.format( + "Request query parameters are : {contextInode : %s, filter : %s}", contextInode, + filter)); + + final PrintWriter output = httpResponse.getWriter(); + + try { + List categories = + UtilMethods.isSet(contextInode) ? this.categoryAPI.findChildren(user, + contextInode, false, filter) : + this.categoryAPI.findTopLevelCategories(user, false, filter); + + if (!categories.isEmpty()) { + output.print("\"name\",\"key\",\"variable\",\"sort\""); + output.print("\r\n"); + + for (Category category : categories) { + String catName = category.getCategoryName(); + String catKey = category.getKey(); + String catVar = category.getCategoryVelocityVarName(); + String catSort = Integer.toString(category.getSortOrder()); + catName = catName == null ? "" : catName; + catKey = catKey == null ? "" : catKey; + catVar = catVar == null ? "" : catVar; + + catName = "\"" + catName + "\""; + catKey = "\"" + catKey + "\""; + catVar = "\"" + catVar + "\""; + + output.print(catName + "," + catKey + "," + catVar + "," + catSort); + output.print("\r\n"); + } + + } else { + output.print("There are no Categories to show"); + output.print("\r\n"); + } + } catch (Exception e) { + Logger.error(this, "Error exporting categories", e); + } finally { + output.flush(); + output.close(); + } + } + + /** + * Exports a list of categories. + * + * @param httpRequest + * @param httpResponse + * @param uploadedFile + * @param multiPart + * @param fileDetail + * @param filter + * @param exportType + * @param contextInode + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @POST + @Path("/_import") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + @Consumes({MediaType.MULTIPART_FORM_DATA}) + public Response importCategories(@Context final HttpServletRequest httpRequest, + @Context final HttpServletResponse httpResponse, + @FormDataParam("file") final File uploadedFile, + FormDataMultiPart multiPart, + @FormDataParam("file") FormDataContentDisposition fileDetail, + @FormDataParam("filter") String filter, + @FormDataParam("exportType") String exportType, + @FormDataParam("contextInode") String contextInode) throws IOException { + + return processImport(httpRequest, httpResponse, uploadedFile, multiPart, fileDetail, filter, + exportType, contextInode); + + } + + private String getContentFromFile(String path) throws IOException { + + String content = StringPool.BLANK; + + try (InputStream file = java.nio.file.Files.newInputStream(java.nio.file.Path.of(path))) { + byte[] uploadedFileBytes = file.readAllBytes(); + + content = new String(uploadedFileBytes); + } + return content; + } + + @WrapInTransaction + private Response processImport(final HttpServletRequest httpRequest, + final HttpServletResponse httpResponse, + final File uploadedFile, + final FormDataMultiPart multiPart, + final FormDataContentDisposition fileDetail, + final String filter, + final String exportType, + final String contextInode) throws IOException { + + List unableToDeleteCats = null; + final List failedToDelete = new ArrayList<>(); + + StringReader stringReader = null; + BufferedReader bufferedReader = null; + + try { + final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, + null); + + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(httpRequest); + + Logger.debug(this, () -> "Importing the list of categories. " + String.format( + "Request payload is : {contextInode : %s, filter : %s, exportType : %s}", + contextInode, + filter, exportType)); + + String content = getContentFromFile(uploadedFile.getPath()); + + stringReader = new StringReader(content); + bufferedReader = new BufferedReader(stringReader); + + if (exportType.equals("replace")) { + Logger.debug(this, () -> "Replacing categories"); + if (UtilMethods.isSet(contextInode)) { + Category contextCat = this.categoryAPI.find(contextInode, user, false); + unableToDeleteCats = this.categoryAPI.removeAllChildren(contextCat, user, + false); + if (!unableToDeleteCats.isEmpty()) { + for (final Category category : unableToDeleteCats) { + Logger.error(this, "Category with Id: " + category.getInode() + + " unable to delete"); + failedToDelete.add(new FailedResultView(category.getInode(), + "Category with id: " + category.getInode() + + " unable to delete")); + } + } + } else { + Logger.debug(this, () -> "Deleting all the categories"); + categoryAPI.deleteAll(user, false); + Logger.debug(this, () -> "Deleted all the categories"); + } + + this.categoryHelper.addOrUpdateCategory(user, contextInode, bufferedReader, false); + } else if (exportType.equals("merge")) { + Logger.debug(this, () -> "Merging categories"); + this.categoryHelper.addOrUpdateCategory(user, contextInode, bufferedReader, true); + } + + } catch (Exception e) { + Logger.error(this, "Error importing categories", e); + } finally { + CloseUtils.closeQuietly(stringReader, bufferedReader); + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(Long.valueOf(UtilMethods.isSet(unableToDeleteCats) ? 1 : 0), 0L, + failedToDelete))) + .build(); + } + + @WrapInTransaction + private void updateSortOrder(final CategoryEditForm categoryEditForm, final User user, + final Host host, + final PageMode pageMode, final Category parentCategory) + throws DotDataException, DotSecurityException { + + Iterator iterator = categoryEditForm.getCategoryData().entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry entry = (Map.Entry) iterator.next(); + String key = (String) entry.getKey(); + Integer value = (Integer) entry.getValue(); + + final Category category = this.categoryAPI.find(key, user, pageMode.respectAnonPerms); + + if (null == category) { + Logger.error(this, "Category with Id: " + key + " does not exist"); + throw new IllegalArgumentException("Category with Id: " + key + " does not exist"); + } else { + + category.setSortOrder(value); + + Logger.debug(this, + () -> "Saving category entity : " + getObjectToJsonString(category)); + this.categoryAPI.save(parentCategory, category, user, + pageMode.respectAnonPerms); + Logger.debug(this, + () -> "Saved category entity : " + getObjectToJsonString(category)); + + ActivityLogger.logInfo(this.getClass(), "Saved Category", + "User " + user.getPrimaryKey() + + "Category: " + category.getCategoryName(), + host.getTitle() != null ? host.getTitle() : "default"); + } + } + } + + private Response getPage(final List list, final int totalCount, final int page, + final int perPage) { + + return Response. + ok(new ResponseEntityView((Object) list)) + .header("X-Pagination-Per-Page", perPage) + .header("X-Pagination-Current-Page", page) + .header("X-Pagination-Total-Entries", totalCount) + .build(); + } + + private String getObjectToJsonString(final Object object) { + ObjectMapper mapper = DotObjectMapperProvider.getInstance().getDefaultObjectMapper(); + try { + final String json = mapper.writeValueAsString(object); + return json; + } catch (JsonProcessingException e) { + Logger.error(this, e.getMessage(), e); + } + return StringPool.BLANK; + } } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryDTO.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryDTO.java new file mode 100644 index 000000000000..360725d12fc2 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryDTO.java @@ -0,0 +1,51 @@ +package com.dotcms.rest.api.v1.categories; + +import java.util.Date; + +/** + * Category import data transfer object + * + * @author Hassan Mustafa Baig + */ +public class CategoryDTO { + + private final String categoryName; + private final String key; + private final String sortOrder; + private final String keywords; + private final String categoryVelocityVarName; + + public CategoryDTO(final String categoryName, + final String categoryVelocityVarName, + final String key, + final String keywords, + final String sortOrder) { + + this.categoryName = categoryName; + this.categoryVelocityVarName = categoryVelocityVarName; + this.key = key; + this.keywords = keywords; + this.sortOrder = sortOrder; + } + + + public String getCategoryName() { + return categoryName; + } + + public String getKey() { + return key; + } + + public String getSortOrder() { + return sortOrder; + } + + public String getKeywords() { + return keywords; + } + + public String getCategoryVelocityVarName() { + return categoryVelocityVarName; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryEditForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryEditForm.java new file mode 100644 index 000000000000..70e5c9a4a2c7 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryEditForm.java @@ -0,0 +1,147 @@ +package com.dotcms.rest.api.v1.categories; + +import com.dotcms.rest.api.Validated; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; + +/** + * Category Input Form + * + * @author Hassan Mustafa Baig + */ +@JsonDeserialize(builder = CategoryEditForm.Builder.class) +public class CategoryEditForm extends Validated { + + private String filter; + private int page; + private int perPage; + private String direction; + private String orderBy; + private String siteId; + private String parentInode; + private HashMap categoryData; + + private CategoryEditForm(final Builder builder) { + this.filter = builder.filter; + this.page = builder.page; + this.perPage = builder.perPage; + this.direction = builder.direction; + this.orderBy = builder.orderBy; + this.siteId = builder.siteId; + this.parentInode = builder.parentInode; + this.categoryData = builder.categoryData; + } + + public String getFilter() { + return filter; + } + + public int getPage() { + return page; + } + + public int getPerPage() { + return perPage; + } + + public String getDirection() { + return direction; + } + + public String getOrderBy() { + return orderBy; + } + + public String getSiteId() { + return siteId; + } + + public String getParentInode() { + return parentInode; + } + + public HashMap getCategoryData() { + return categoryData; + } + + public static final class Builder { + + @JsonProperty + private String filter; + @JsonProperty + private int page; + @JsonProperty + private int perPage; + @JsonProperty + private String direction; + @JsonProperty + private String orderBy; + @JsonProperty + private String siteId; + @JsonProperty + private String inode; + @JsonProperty + private int sortOrder; + @JsonProperty + private String parentInode; + @JsonProperty + private HashMap categoryData; + + + public Builder filter(final String filter) { + this.filter = filter; + return this; + } + + public Builder page(final int page) { + this.page = page; + return this; + } + + public Builder perPage(final int perPage) { + this.perPage = perPage; + return this; + } + + public Builder direction(final String direction) { + this.direction = direction; + return this; + } + + public Builder orderBy(final String orderBy) { + this.orderBy = orderBy; + return this; + } + + public Builder siteId(final String siteId) { + this.siteId = siteId; + return this; + } + + public Builder inode(final String inode) { + this.inode = inode; + return this; + } + + public Builder sortOrder(final int sortOrder) { + this.sortOrder = sortOrder; + return this; + } + + public Builder parentInode(final String parentInode) { + this.parentInode = parentInode; + return this; + } + + public Builder categoryData(final HashMap categoryData) { + this.categoryData = categoryData; + return this; + } + + public CategoryEditForm build() { + + return new CategoryEditForm(this); + } + } +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryForm.java new file mode 100644 index 000000000000..2112977436f4 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryForm.java @@ -0,0 +1,161 @@ +package com.dotcms.rest.api.v1.categories; + +import com.dotcms.repackage.javax.validation.constraints.NotNull; +import com.dotcms.rest.api.Validated; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +/** + * Category Input Form + * + * @author Hassan Mustafa Baig + */ +@JsonDeserialize(builder = CategoryForm.Builder.class) +public class CategoryForm extends Validated { + + private String siteId; + private String inode; + private String parent; + private String description; + private String keywords; + private String key; + @NotNull + private String categoryName; + private boolean active; + private int sortOrder; + private String categoryVelocityVarName; + + private CategoryForm(final Builder builder) { + + this.siteId = builder.siteId; + this.inode = builder.inode; + this.parent = builder.parent; + this.description = builder.description; + this.keywords = builder.keywords; + this.key = builder.key; + this.categoryName = builder.categoryName; + this.active = builder.active; + this.sortOrder = builder.sortOrder; + this.categoryVelocityVarName = builder.categoryVelocityVarName; + } + + public String getSiteId() { + return siteId; + } + + public String getInode() { + return inode; + } + + public String getParent() { + return parent; + } + + public String getDescription() { + return description; + } + + public String getKeywords() { + return keywords; + } + + public String getKey() { + return key; + } + + public String getCategoryName() { + return categoryName; + } + + public boolean isActive() { + return active; + } + + public int getSortOrder() { + return sortOrder; + } + + public String getCategoryVelocityVarName() { + return categoryVelocityVarName; + } + + public static final class Builder { + + @JsonProperty + private String siteId; + @JsonProperty + private String inode; + @JsonProperty + private String parent; + @JsonProperty + private String description; + @JsonProperty + private String keywords; + @JsonProperty + private String key; + @JsonProperty + private String categoryName; + @JsonProperty + private boolean active; + @JsonProperty + private int sortOrder; + @JsonProperty + private String categoryVelocityVarName; + + public Builder siteId(final String siteId) { + + this.siteId = siteId; + return this; + } + + public Builder setInode(final String inode) { + this.inode = inode; + return this; + } + + public Builder setParent(final String parent) { + this.parent = parent; + return this; + } + + public Builder setDescription(final String description) { + this.description = description; + return this; + } + + public Builder setKeywords(final String keywords) { + this.keywords = keywords; + return this; + } + + public Builder setKey(final String key) { + this.key = key; + return this; + } + + public Builder setCategoryName(final String categoryName) { + this.categoryName = categoryName; + return this; + } + + public Builder setActive(final boolean active) { + this.active = active; + return this; + } + + public Builder setSortOrder(final int sortOrder) { + this.sortOrder = sortOrder; + return this; + } + + public Builder setCategoryVelocityVarName(final String categoryVelocityVarName) { + this.categoryVelocityVarName = categoryVelocityVarName; + return this; + } + + public CategoryForm build() { + + return new CategoryForm(this); + } + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryHelper.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryHelper.java new file mode 100644 index 000000000000..7b87c371c6f8 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryHelper.java @@ -0,0 +1,155 @@ +package com.dotcms.rest.api.v1.categories; + +import com.dotmarketing.beans.Host; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.portlets.categories.business.CategoryAPI; +import com.dotmarketing.portlets.categories.model.Category; +import com.dotmarketing.util.InodeUtils; +import com.dotmarketing.util.Logger; +import com.dotmarketing.util.StringUtils; +import com.dotmarketing.util.UtilMethods; +import com.dotmarketing.util.VelocityUtil; +import com.google.common.annotations.VisibleForTesting; +import com.liferay.portal.model.User; +import io.vavr.control.Try; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.function.Supplier; + +/** + * Helper for categories + * + * @author Hassan Mustafa Baig + */ +public class CategoryHelper { + + private final CategoryAPI categoryAPI; + + public CategoryHelper() { + this(APILocator.getCategoryAPI()); + } + + @VisibleForTesting + public CategoryHelper(final CategoryAPI categoryAPI) { + + this.categoryAPI = categoryAPI; + } + + public Host getHost(final String hostId, final Supplier hostSupplier) { + + if (UtilMethods.isSet(hostId)) { + + return Try.of( + () -> APILocator.getHostAPI().find(hostId, APILocator.systemUser(), false)) + .getOrElse(hostSupplier); + } + + return hostSupplier.get(); + } + + public CategoryView toCategoryView(final Category category, final User user) { + + return new CategoryView.Builder() + .inode(category.getInode()) + .description(category.getDescription()) + .keywords(category.getKeywords()) + .key(category.getKey()) + .categoryName(category.getCategoryName()) + .active(category.isActive()) + .sortOrder(category.getSortOrder()) + .categoryVelocityVarName(category.getCategoryVelocityVarName()) + .build(); + } + + public void addOrUpdateCategory(final User user, final String contextInode, + final BufferedReader bufferedReader, final Boolean merge) + throws IOException, Exception { + + for (final CategoryDTO categoryDTO : CategoryImporter.from(bufferedReader)) { + addOrUpdateCategory(user, true, contextInode, categoryDTO.getCategoryName(), + categoryDTO.getCategoryVelocityVarName(), categoryDTO.getKey(), null, categoryDTO.getSortOrder(), + merge); + } + } + + private void addOrUpdateCategory(final User user, final Boolean isSave, final String inode, + final String name, final String var, final String key, final String keywords, + final String sort, final boolean isMerge) + throws Exception { + + Category parent = null; + Category category = new Category(); + category.setCategoryName(name); + category.setKey(key); + category.setCategoryVelocityVarName(var); + category.setSortOrder(sort); + category.setKeywords(keywords); + + if (UtilMethods.isSet(inode)) { + if (!isSave) {//edit + category.setInode(inode); + final Category finalCat = category;//this is to be able to use the try.of + parent = Try.of(() -> categoryAPI.getParents(finalCat, user, false).get(0)) + .getOrNull(); + } else {//save + parent = categoryAPI.find(inode, user, false); + } + } + + setVelocityVarName(category, var, name); + + if (isMerge) { // add/edit + + if (isSave) { // Importing + if (UtilMethods.isSet(key)) { + category = categoryAPI.findByKey(key, user, false); + if (category == null) { + category = new Category(); + category.setKey(key); + } + + category.setCategoryName(name); + setVelocityVarName(category, var, name); + category.setSortOrder(sort); + } + } else { // Editing + category = categoryAPI.find(inode, user, false); + category.setCategoryName(name); + setVelocityVarName(category, var, name); + category.setKeywords(keywords); + category.setKey(key); + } + + } else { // replace + category.setCategoryName(name); + setVelocityVarName(category, var, name); + category.setSortOrder(sort); + category.setKey(key); + } + + try { + categoryAPI.save(parent, category, user, false); + } catch (DotSecurityException e) { + Logger.error(this, + "Error trying to save/update the category " + category.getInode(), e); + } + } + + private void setVelocityVarName(Category cat, String catvelvar, String catName) + throws DotDataException, DotSecurityException { + Boolean Proceed = false; + if (!UtilMethods.isSet(catvelvar)) { + catvelvar = StringUtils.camelCaseLower(catName); + Proceed = true; + } + if (!InodeUtils.isSet(cat.getInode()) || Proceed) { + if (VelocityUtil.isNotAllowedVelocityVariableName(catvelvar)) { + catvelvar = catvelvar + "Field"; + } + catvelvar = categoryAPI.suggestVelocityVarName(catvelvar); + cat.setCategoryVelocityVarName(catvelvar); + } + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryImporter.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryImporter.java new file mode 100644 index 000000000000..f84e2052550d --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryImporter.java @@ -0,0 +1,44 @@ +package com.dotcms.rest.api.v1.categories; + +import com.dotcms.repackage.com.csvreader.CsvReader; +import com.dotcms.util.CloseUtils; +import com.dotmarketing.util.Logger; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class CategoryImporter { + + + public static List from(final BufferedReader bufferedReader) throws IOException { + List result = new ArrayList<>(); + + CsvReader csvreader = null; + try { + csvreader = new CsvReader(bufferedReader); + csvreader.setSafetySwitch(false); + csvreader.readHeaders(); + String[] csvLine; + + while (csvreader.readRecord()) { + csvLine = csvreader.getValues(); + try { + result.add(new CategoryDTO(csvLine[0], csvLine[2], + csvLine[1], null, csvLine[3])); + + } catch (Exception e) { + Logger.error(CategoryImporter.class, + "Error trying to parse the categories csv row: name=" + csvLine[0] + + ", variable=" + csvLine[2] + ", key=" + csvLine[1] + ", sort=" + + csvLine[3], e); + } + } + } finally { + CloseUtils.closeQuietly(bufferedReader); + csvreader.close(); + } + + return result; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryListDTO.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryListDTO.java new file mode 100644 index 000000000000..f6f71574412d --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryListDTO.java @@ -0,0 +1,117 @@ +package com.dotcms.rest.api.v1.categories; + +import java.util.Date; + +/** + * Category listing data transfer object + * + * @author Hassan Mustafa Baig + */ + +public class CategoryListDTO { + + private final String categoryName; + private final String key; + private final Integer sortOrder; + private final String keywords; + private final String categoryVelocityVarName; + private final String description; + + private final boolean active; + private final java.util.Date modDate; + private final java.util.Date iDate; + private final String type; + private final String owner; + private final String inode; + private final String identifier; + + private final Integer childrenCount; + + public CategoryListDTO(final String categoryName, + final String categoryVelocityVarName, + final String key, + final String keywords, + final Integer sortOrder, + final String description, + final boolean active, + final java.util.Date modDate, + final java.util.Date iDate, + final String type, + final String owner, + final String inode, + final String identifier, + final Integer childrenCount) { + + this.categoryName = categoryName; + this.categoryVelocityVarName = categoryVelocityVarName; + this.key = key; + this.keywords = keywords; + this.sortOrder = sortOrder; + this.description = description; + this.active = active; + this.modDate = modDate; + this.iDate = iDate; + this.type = type; + this.owner = owner; + this.inode = inode; + this.identifier = identifier; + this.childrenCount = childrenCount; + } + + + public String getCategoryName() { + return categoryName; + } + + public String getKey() { + return key; + } + + public Integer getSortOrder() { + return sortOrder; + } + + public String getKeywords() { + return keywords; + } + + public String getCategoryVelocityVarName() { + return categoryVelocityVarName; + } + + public String getDescription() { + return description; + } + + public boolean isActive() { + return active; + } + + public Date getModDate() { + return modDate; + } + + public Date getiDate() { + return iDate; + } + + public String getType() { + return type; + } + + public String getOwner() { + return owner; + } + + public String getInode() { + return inode; + } + + public String getIdentifier() { + return identifier; + } + + public Integer getChildrenCount() { + return childrenCount; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryView.java new file mode 100644 index 000000000000..ba70c853a15a --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoryView.java @@ -0,0 +1,141 @@ +package com.dotcms.rest.api.v1.categories; + +import java.util.Date; + +public class CategoryView { + + private final String inode; + private final String parent; + private final String description; + private final String keywords; + private final String key; + private final String categoryName; + private final boolean active; + private final int sortOrder; + private final String categoryVelocityVarName; + private final Date modDate; + + + private CategoryView(final Builder builder) { + + this.inode = builder.inode; + this.parent = builder.parent; + this.description = builder.description; + this.keywords = builder.keywords; + this.key = builder.key; + this.categoryName = builder.categoryName; + this.active = builder.active; + this.sortOrder = builder.sortOrder; + this.categoryVelocityVarName = builder.categoryVelocityVarName; + this.modDate = builder.modDate; + } + + public String getInode() { + return inode; + } + + public String getParent() { + return parent; + } + + public String getDescription() { + return description; + } + + public String getKeywords() { + return keywords; + } + + public String getKey() { + return key; + } + + public String getCategoryName() { + return categoryName; + } + + public boolean isActive() { + return active; + } + + public int getSortOrder() { + return sortOrder; + } + + public String getCategoryVelocityVarName() { + return categoryVelocityVarName; + } + + public Date getModDate() { + return modDate; + } + + public static final class Builder { + + private String inode; + private String parent; + private String description; + private String keywords; + private String key; + private String categoryName; + private boolean active; + private int sortOrder; + private String categoryVelocityVarName; + private Date modDate; + + public Builder inode(final String inode) { + this.inode = inode; + return this; + } + + public Builder parent(final String parent) { + this.parent = parent; + return this; + } + + public Builder description(final String description) { + this.description = description; + return this; + } + + public Builder keywords(final String keywords) { + this.keywords = keywords; + return this; + } + + public Builder key(final String key) { + this.key = key; + return this; + } + + public Builder categoryName(final String categoryName) { + this.categoryName = categoryName; + return this; + } + + public Builder active(final boolean active) { + this.active = active; + return this; + } + + public Builder sortOrder(final int sortOrder) { + this.sortOrder = sortOrder; + return this; + } + + public Builder categoryVelocityVarName(final String categoryVelocityVarName) { + this.categoryVelocityVarName = categoryVelocityVarName; + return this; + } + + public Builder modDate(final Date modDate) { + this.modDate = modDate; + return this; + } + + public CategoryView build() { + + return new CategoryView(this); + } + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerForm.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerForm.java index 07153c08b3de..2929b81f168f 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerForm.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerForm.java @@ -4,7 +4,6 @@ import com.dotmarketing.beans.ContainerStructure; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - import java.util.List; /** diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResource.java index 296512dd7e03..65fa2d657717 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResource.java @@ -10,18 +10,22 @@ import com.dotcms.rest.ResponseEntityView; import com.dotcms.rest.WebResource; import com.dotcms.rest.annotation.NoCache; +import com.dotcms.rest.api.BulkResultView; +import com.dotcms.rest.api.FailedResultView; import com.dotcms.rest.exception.ForbiddenException; import com.dotcms.rest.exception.mapper.ExceptionMapperUtil; +import com.dotcms.util.DotPreconditions; +import com.dotcms.util.JsonUtil; import com.dotcms.util.PaginationUtil; import com.dotcms.util.pagination.ContainerPaginator; import com.dotcms.util.pagination.OrderDirection; import com.dotcms.uuid.shorty.ShortType; import com.dotcms.uuid.shorty.ShortyId; import com.dotcms.uuid.shorty.ShortyIdAPI; +import com.dotmarketing.beans.ContainerStructure; import com.dotmarketing.beans.Host; import com.dotmarketing.beans.MultiTree; import com.dotmarketing.business.APILocator; -import com.dotmarketing.business.PermissionAPI; import com.dotmarketing.business.PermissionLevel; import com.dotmarketing.business.VersionableAPI; import com.dotmarketing.business.web.WebAPILocator; @@ -45,16 +49,21 @@ import com.dotmarketing.util.Logger; import com.dotmarketing.util.PageMode; import com.dotmarketing.util.UtilMethods; -import com.dotmarketing.util.WebKeys; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.liferay.portal.model.User; -import org.apache.commons.io.IOUtils; -import org.apache.velocity.exception.MethodInvocationException; -import org.apache.velocity.exception.ResourceNotFoundException; -import org.glassfish.jersey.server.JSONP; - +import java.io.IOException; +import java.io.Serializable; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; @@ -70,14 +79,10 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.io.IOException; -import java.io.Serializable; -import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.glassfish.jersey.server.JSONP; /** * This resource provides all the different end-points associated to information and actions that @@ -650,9 +655,11 @@ public final Response saveNew(@Context final HttpServletRequest request, final PageMode pageMode = PageMode.get(request); Container container = new Container(); - ActivityLogger.logInfo(this.getClass(), "Save Container", - "User " + user.getPrimaryKey() + " saved " + container.getTitle(), host.getHostname()); + Logger.debug(this, + () -> "Adding container. Request payload is : " + JsonUtil.getJsonStringFromObject(containerForm)); + + container.setCode(containerForm.getCode()); container.setMaxContentlets(containerForm.getMaxContentlets()); container.setNotes(containerForm.getNotes()); container.setPreLoop(containerForm.getPreLoop()); @@ -667,8 +674,15 @@ public final Response saveNew(@Context final HttpServletRequest request, container.setShowOnMenu(containerForm.isShowOnMenu()); container.setTitle(containerForm.getTitle()); + if(containerForm.getMaxContentlets() == 0){ + container.setCode(containerForm.getCode()); + } + this.containerAPI.save(container, containerForm.getContainerStructures(), host, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Save Container", + "User " + user.getPrimaryKey() + " saved " + container.getTitle(), host.getHostname()); + Logger.debug(this, ()-> "The container: " + container.getIdentifier() + " has been saved"); return Response.ok(new ResponseEntityView(new ContainerView(container))).build(); @@ -708,6 +722,7 @@ public final Response update(@Context final HttpServletRequest request, ActivityLogger.logInfo(this.getClass(), "Upate Container: " + containerForm.getIdentifier(), "User " + user.getPrimaryKey() + " saved " + container.getTitle(), host.getHostname()); + container.setCode(containerForm.getCode()); container.setMaxContentlets(containerForm.getMaxContentlets()); container.setNotes(containerForm.getNotes()); container.setPreLoop(containerForm.getPreLoop()); @@ -722,6 +737,10 @@ public final Response update(@Context final HttpServletRequest request, container.setShowOnMenu(containerForm.isShowOnMenu()); container.setTitle(containerForm.getTitle()); + if(containerForm.getMaxContentlets() == 0){ + container.setCode(containerForm.getCode()); + } + this.containerAPI.save(container, containerForm.getContainerStructures(), host, user, pageMode.respectAnonPerms); Logger.error(this, "The container: " + container.getIdentifier() + " has been updated"); @@ -747,7 +766,8 @@ public final Response update(@Context final HttpServletRequest request, @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public final Response getLiveById(@Context final HttpServletRequest httpRequest, @Context final HttpServletResponse httpResponse, - @QueryParam("containerId") final String containerId) throws DotSecurityException, DotDataException { + @QueryParam("containerId") final String containerId, + @QueryParam("includeContentType") final boolean includeContentType) throws DotSecurityException, DotDataException { final InitDataObject initData = new WebResource.InitBuilder(webResource) .requestAndResponse(httpRequest, httpResponse).rejectWhenNoUser(true).init(); @@ -762,6 +782,11 @@ public final Response getLiveById(@Context final HttpServletRequest httpRequest throw new DoesNotExistException("Live Version of the Container with Id: " + containerId + " does not exist"); } + if(includeContentType){ + List structures = this.containerAPI.getContainerStructures(container); + return Response.ok(new ResponseEntityView(ContainerResourceHelper.getInstance().toResponseEntityContainerWithContentTypesView(container, structures))).build(); + } + return Response.ok(new ResponseEntityView(new ContainerView(container))).build(); } @@ -783,7 +808,8 @@ public final Response getLiveById(@Context final HttpServletRequest httpRequest @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) public final Response getWorkingById(@Context final HttpServletRequest request, @Context final HttpServletResponse httpResponse, - @QueryParam("containerId") final String containerId) throws DotSecurityException, DotDataException { + @QueryParam("containerId") final String containerId, + @QueryParam("includeContentType") final boolean includeContentType) throws DotSecurityException, DotDataException { final InitDataObject initData = new WebResource.InitBuilder(webResource) .requestAndResponse(request, httpResponse).rejectWhenNoUser(true).init(); @@ -799,6 +825,11 @@ public final Response getWorkingById(@Context final HttpServletRequest request, throw new DoesNotExistException("Working Version of the Container with Id: " + containerId + " does not exist"); } + if(includeContentType){ + List structures = this.containerAPI.getContainerStructures(container); + return Response.ok(new ResponseEntityView(ContainerResourceHelper.getInstance().toResponseEntityContainerWithContentTypesView(container, structures))).build(); + } + return Response.ok(new ResponseEntityView(new ContainerView(container))).build(); } @@ -1068,4 +1099,358 @@ public final Response delete(@Context final HttpServletRequest request, } } -} + /** + * Copies container to the specified host + * + * @param request + * @param response + * @param id id identifier to copy. + * @return + * @throws DotDataException + * @throws DotSecurityException + */ + @POST + @Path("/{id}/_copy") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public ResponseEntityContainerView copy(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + @PathParam("id") final String id) throws DotDataException, DotSecurityException { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).requiredBackendUser(true) + .rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final Host host = WebAPILocator.getHostWebAPI().getCurrentHostNoThrow(request); + final PageMode pageMode = PageMode.get(request); + + DotPreconditions.checkArgument(UtilMethods.isSet(id), + "The container id is required"); + + final Container sourceContainer = this.getContainerWorking(id, user, + WebAPILocator.getHostWebAPI().getHost(request)); + + if (null != sourceContainer && InodeUtils.isSet(sourceContainer.getInode())) { + + ActivityLogger.logInfo(this.getClass(), "Copy Container", + "User " + user.getPrimaryKey() + " saved " + sourceContainer.getTitle(), + host.getHostname()); + + Container copiedContainer = this.containerAPI.copy(sourceContainer, host, user, + pageMode.respectAnonPerms); + + Logger.debug(this, + () -> "The container: " + sourceContainer.getIdentifier() + " has been copied"); + + return new ResponseEntityContainerView(Collections.singletonList(copiedContainer)); + } else { + + Logger.error(this, "Container with Id: " + id + " does not exist"); + throw new DoesNotExistException("Container with Id: " + id + " does not exist"); + } + } + + /** + * Deletes Container(s). + * + * This method receives a list of identifiers and deletes the containers. + * To delete a container successfully the user needs to have Edit Permissions over it. + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param containersToDelete {@link String} container identifier to look for and then delete it + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @DELETE + @Path("_bulkdelete") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response bulkDelete(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final List containersToDelete) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(request); + Long deletedContainersCount = 0L; + final List failedToDelete = new ArrayList<>(); + + Logger.debug(this, + () -> "Deleting containers in bulk. Request payload is : {" + String.join(",", containersToDelete) + "}"); + + DotPreconditions.checkArgument(UtilMethods.isSet(containersToDelete), + "The body must send a collection of container identifier such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + for(final String containerId : containersToDelete){ + try{ + final Container container = this.getContainerWorking(containerId, user, + WebAPILocator.getHostWebAPI().getHost(request)); + + if (null != container && InodeUtils.isSet(container.getInode())){ + this.containerAPI.delete(container, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Delete Container Action", "User " + + user.getPrimaryKey() + " deleted template: " + container.getIdentifier()); + deletedContainersCount++; + } else { + Logger.error(this, "Container with Id: " + containerId + " does not exist"); + failedToDelete.add(new FailedResultView(containerId,"Container does not exist")); + } + } catch(Exception e){ + Logger.debug(this,e.getMessage(),e); + failedToDelete.add(new FailedResultView(containerId,e.getMessage())); + } + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(deletedContainersCount,0L,failedToDelete))) + .build(); + } + + + /** + * Publishes Container(s) + * + * This method receives a list of identifiers and publishes the containers. + * To publish a container successfully the user needs to have Publish Permissions + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param containersToPublish {@link List} list of container ids to publish + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @Path("/_bulkpublish") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response bulkPublish(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final List containersToPublish){ + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(request); + Long publishedContainersCount = 0L; + final List failedToPublish = new ArrayList<>(); + + Logger.debug(this, + () -> "Publishing containers in bulk. Request payload is : {" + String.join(",", containersToPublish) + "}"); + + DotPreconditions.checkArgument(UtilMethods.isSet(containersToPublish), + "The body must send a collection of container identifier such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + for (final String containerId : containersToPublish) { + try{ + final Container container = this.getContainerWorking(containerId, user, + WebAPILocator.getHostWebAPI().getHost(request)); + if (null != container && InodeUtils.isSet(container.getInode())){ + this.containerAPI.publish(container, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Publish Container Action", "User " + + user.getPrimaryKey() + " published container: " + container.getIdentifier()); + publishedContainersCount++; + } else { + Logger.error(this, "Container with Id: " + containerId + " does not exist"); + failedToPublish.add(new FailedResultView(containerId,"Container does not exist")); + } + } catch(Exception e) { + Logger.debug(this, e.getMessage(), e); + failedToPublish.add(new FailedResultView(containerId,e.getMessage())); + } + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(publishedContainersCount,0L,failedToPublish))) + .build(); + } + + /** + * Unpublishes Container(s) + * + * This method receives a list of identifiers and unpublishes the containers. + * To publish a container successfully the user needs to have Publish Permissions. + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param containersToUnpublish {@link List} list of container ids to unpublish + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @Path("/_bulkunpublish") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response bulkUnpublish(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final List containersToUnpublish) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(request); + Long unpublishedContainersCount = 0L; + final List failedToUnpublish = new ArrayList<>(); + + Logger.debug(this, + () -> "Unpublishing containers in bulk. Request payload is : {" + String.join(",", containersToUnpublish) + "}"); + + DotPreconditions.checkArgument(UtilMethods.isSet(containersToUnpublish), + "The body must send a collection of container identifier such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + for (final String containerId : containersToUnpublish) { + try{ + final Container container = this.getContainerWorking(containerId, user, + WebAPILocator.getHostWebAPI().getHost(request)); + if (null != container && InodeUtils.isSet(container.getInode())){ + this.containerAPI.unpublish(container, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Unpublish Container Action", "User " + + user.getPrimaryKey() + " unpublished container: " + container.getIdentifier()); + unpublishedContainersCount++; + } else { + Logger.error(this, "Container with Id: " + containerId + " does not exist"); + failedToUnpublish.add(new FailedResultView(containerId,"Container does not exist")); + } + } catch(Exception e) { + Logger.debug(this, e.getMessage(), e); + failedToUnpublish.add(new FailedResultView(containerId,e.getMessage())); + } + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(unpublishedContainersCount,0L,failedToUnpublish))) + .build(); + } + + /** + * Archives container(s). + * + * This method receives a list of identifiers and archives the containers. + * To archive a container successfully the user needs to have Edit Permissions. + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param containersToArchive {@link List} containers identifier to archive. + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @Path("/_bulkarchive") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response bulkArchive(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final List containersToArchive) { + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(request); + Long archivedContainersCount = 0L; + final List failedToArchive = new ArrayList<>(); + + Logger.debug(this, + () -> "Archiving containers in bulk. Request payload is : {" + String.join(",", containersToArchive) + "}"); + + DotPreconditions.checkArgument(UtilMethods.isSet(containersToArchive), + "The body must send a collection of container identifier such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + + for(final String containerId : containersToArchive){ + try{ + final Container container = this.getContainerWorking(containerId, user, + WebAPILocator.getHostWebAPI().getHost(request)); + if (null != container && InodeUtils.isSet(container.getInode())){ + this.containerAPI.archive(container, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Archive Container Action", "User " + + user.getPrimaryKey() + " archived container: " + container.getIdentifier()); + archivedContainersCount++; + } else { + Logger.error(this, "Container with Id: " + containerId + " does not exist"); + failedToArchive.add(new FailedResultView(containerId,"Container does not exist")); + } + } catch(Exception e) { + Logger.debug(this,e.getMessage(),e); + failedToArchive.add(new FailedResultView(containerId,e.getMessage())); + } + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(archivedContainersCount,0L,failedToArchive))) + .build(); + } + + /** + * Unarchives container(s). + * + * This method receives a list of identifiers and unarchives the containers. + * To unarchive a container successfully the user needs to have Edit Permissions and the container + * needs to be archived. + * + * @param request {@link HttpServletRequest} + * @param response {@link HttpServletResponse} + * @param containersToUnarchive {@link List} containers identifier to unarchive. + * @return Response + * @throws DotDataException + * @throws DotSecurityException + */ + @PUT + @Path("/_bulkunarchive") + @JSONP + @NoCache + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public final Response bulkUnarchive(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final List containersToUnarchive){ + + final InitDataObject initData = new WebResource.InitBuilder(webResource) + .requestAndResponse(request, response).rejectWhenNoUser(true).init(); + final User user = initData.getUser(); + final PageMode pageMode = PageMode.get(request); + Long unarchivedContainersCount = 0L; + final List failedToUnarchive = new ArrayList<>(); + + Logger.debug(this, + () -> "Unarchiving containers in bulk. Request payload is : {" + String.join(",", containersToUnarchive) + "}"); + + DotPreconditions.checkArgument(UtilMethods.isSet(containersToUnarchive), + "The body must send a collection of container identifier such as: " + + "[\"dd60695c-9e0f-4a2e-9fd8-ce2a4ac5c27d\",\"cc59390c-9a0f-4e7a-9fd8-ca7e4ec0c77d\"]"); + + for(final String containerId : containersToUnarchive){ + try{ + final Container container = this.getContainerWorking(containerId, user, + WebAPILocator.getHostWebAPI().getHost(request)); + if (null != container && InodeUtils.isSet(container.getInode())){ + this.containerAPI.unarchive(container, user, pageMode.respectAnonPerms); + ActivityLogger.logInfo(this.getClass(), "Unarchive Container Action", "User " + + user.getPrimaryKey() + " unarchived container: " + container.getIdentifier()); + unarchivedContainersCount++; + } else { + Logger.error(this, "Container with Id: " + containerId + " does not exist"); + failedToUnarchive.add(new FailedResultView(containerId,"Container does not exist")); + } + } catch(Exception e) { + Logger.debug(this, e.getMessage(), e); + failedToUnarchive.add(new FailedResultView(containerId,e.getMessage())); + } + } + + return Response.ok(new ResponseEntityView( + new BulkResultView(unarchivedContainersCount,0L,failedToUnarchive))) + .build(); + } +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResourceHelper.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResourceHelper.java index 00bb3e8f5403..24caa4267ce0 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResourceHelper.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerResourceHelper.java @@ -1,6 +1,7 @@ package com.dotcms.rest.api.v1.container; import com.dotcms.rendering.velocity.directive.DotParse; +import com.dotmarketing.beans.ContainerStructure; import com.dotmarketing.business.APILocator; import com.dotmarketing.portlets.containers.model.Container; import com.dotmarketing.portlets.containers.model.FileAssetContainer; @@ -8,10 +9,9 @@ import com.dotmarketing.portlets.languagesmanager.model.Language; import com.dotmarketing.util.UtilMethods; import com.dotmarketing.util.WebKeys; - -import javax.servlet.http.HttpServletRequest; import java.io.Serializable; import java.util.List; +import javax.servlet.http.HttpServletRequest; public class ContainerResourceHelper implements Serializable { @@ -70,6 +70,9 @@ public void setContainerLanguage(final Container container, final HttpServletReq } } } - } // setContainerLanguage. + } + public ContainerWithContentTypesView toResponseEntityContainerWithContentTypesView(final Container container, final List containerStructures) { + return new ContainerWithContentTypesView(container, containerStructures); + } } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerWithContentTypesView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerWithContentTypesView.java new file mode 100644 index 000000000000..8cf86fd80816 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ContainerWithContentTypesView.java @@ -0,0 +1,33 @@ +package com.dotcms.rest.api.v1.container; + +import com.dotmarketing.beans.ContainerStructure; +import com.dotmarketing.portlets.containers.model.Container; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.List; + +/** + * It represents a container with its structure/content type. It contents a List of the + * {@link com.dotmarketing.beans.ContainerStructure} for each Structure + * and a container {@link com.dotmarketing.portlets.containers.model.Container} + */ + +public class ContainerWithContentTypesView { + + private final Container container; + private final List contentTypes; + + @JsonCreator + public ContainerWithContentTypesView(final Container container, + final List contentTypes) { + this.container = container; + this.contentTypes = contentTypes; + } + + public Container getContainer() { + return container; + } + + public List getContentTypes() { + return contentTypes; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ResponseEntityContainerView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ResponseEntityContainerView.java new file mode 100644 index 000000000000..1853ef96676d --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/container/ResponseEntityContainerView.java @@ -0,0 +1,11 @@ +package com.dotcms.rest.api.v1.container; + +import com.dotcms.rest.ResponseEntityView; +import com.dotmarketing.portlets.containers.model.Container; +import java.util.List; + +public class ResponseEntityContainerView extends ResponseEntityView> { + public ResponseEntityContainerView(final List entity) { + super(entity); + } +} diff --git a/dotCMS/src/main/java/com/dotcms/util/JsonUtil.java b/dotCMS/src/main/java/com/dotcms/util/JsonUtil.java index ce263edf8123..969385f1c8ef 100644 --- a/dotCMS/src/main/java/com/dotcms/util/JsonUtil.java +++ b/dotCMS/src/main/java/com/dotcms/util/JsonUtil.java @@ -1,9 +1,12 @@ package com.dotcms.util; +import com.dotcms.rest.api.v1.DotObjectMapperProvider; import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.liferay.util.StringPool; +import io.vavr.control.Try; import java.io.File; import java.io.IOException; import java.net.URL; @@ -55,4 +58,10 @@ public static boolean isValidJSON(final String fieldValue) { } } + public static String getJsonStringFromObject(final Object object) { + final String json = Try.of( + () -> JSON_MAPPER.writeValueAsString(object)).getOrElse(StringPool.BLANK); + + return json; + } } diff --git a/dotCMS/src/main/java/com/dotcms/util/pagination/CategoryListDTOPaginator.java b/dotCMS/src/main/java/com/dotcms/util/pagination/CategoryListDTOPaginator.java new file mode 100644 index 000000000000..5510bc58042a --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/util/pagination/CategoryListDTOPaginator.java @@ -0,0 +1,66 @@ +package com.dotcms.util.pagination; + +import com.dotcms.repackage.com.google.common.annotations.VisibleForTesting; +import com.dotcms.rest.api.v1.categories.CategoryListDTO; +import com.dotmarketing.business.APILocator; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.exception.DotRuntimeException; +import com.dotmarketing.exception.DotSecurityException; +import com.dotmarketing.portlets.categories.business.CategoryAPI; +import com.dotmarketing.portlets.categories.business.PaginatedCategories; +import com.dotmarketing.portlets.categories.model.Category; +import com.dotmarketing.util.PageMode; +import com.dotmarketing.util.PaginatedArrayList; +import com.liferay.portal.model.User; +import java.util.List; +import java.util.Map; + +/** + * Category paginator + */ +public class CategoryListDTOPaginator implements PaginatorOrdered { + + private final CategoryAPI categoryAPI; + + @VisibleForTesting + public CategoryListDTOPaginator(final CategoryAPI categoryAPI){ + this.categoryAPI = categoryAPI; + } + + public CategoryListDTOPaginator(){ + this(APILocator.getCategoryAPI()); + } + + @Override + public PaginatedArrayList getItems(final User user, final String filter, final int limit, final int offset, + final String orderby, final OrderDirection direction, final Map extraParams) { + try { + String categoriesSort = null; + + if (orderby != null) { + categoriesSort = direction == OrderDirection.DESC ? "-" + orderby : orderby; + } + + final PaginatedArrayList result = new PaginatedArrayList<>(); + final PaginatedCategories topLevelCategories = categoryAPI.findTopLevelCategories(user, false, offset, limit, filter, categoriesSort); + result.setTotalResults(topLevelCategories.getTotalCount()); + + final List categories = topLevelCategories.getCategories(); + + if (categories != null) { + for(var category : categories) { + CategoryListDTO categoryListDTO = new CategoryListDTO(category.getCategoryName(),category.getCategoryVelocityVarName(), category.getKey(), + category.getKeywords(), category.getSortOrder(), category.getDescription(),category.isActive(),category.getModDate(), + category.getIDate(),category.getType(),category.getOwner(),category.getInode(),category.getIdentifier(), + this.categoryAPI.findChildren(user, category.getInode(), false, offset, limit,filter, direction.toString()).getTotalCount()); + + result.add(categoryListDTO); + } + } + + return result; + } catch (DotDataException | DotSecurityException e) { + throw new DotRuntimeException(e); + } + } +} diff --git a/dotCMS/src/main/java/com/dotcms/util/pagination/Paginator.java b/dotCMS/src/main/java/com/dotcms/util/pagination/Paginator.java index 1f0153d1521d..5a5856bc3dc4 100644 --- a/dotCMS/src/main/java/com/dotcms/util/pagination/Paginator.java +++ b/dotCMS/src/main/java/com/dotcms/util/pagination/Paginator.java @@ -2,10 +2,7 @@ import com.dotmarketing.util.PaginatedArrayList; import com.liferay.portal.model.User; - -import java.io.Serializable; import java.util.Map; -import static com.dotcms.util.CollectionsUtils.map; /** diff --git a/dotCMS/src/main/java/com/dotcms/util/pagination/PaginatorOrdered.java b/dotCMS/src/main/java/com/dotcms/util/pagination/PaginatorOrdered.java index f4d4c35de8ae..43a88d2818a4 100644 --- a/dotCMS/src/main/java/com/dotcms/util/pagination/PaginatorOrdered.java +++ b/dotCMS/src/main/java/com/dotcms/util/pagination/PaginatorOrdered.java @@ -2,11 +2,8 @@ import com.dotmarketing.util.PaginatedArrayList; import com.liferay.portal.model.User; - import java.util.Map; -import static com.dotcms.util.CollectionsUtils.map; - /** * It is a {@link Paginator} with Order and Filter */ diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPI.java b/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPI.java index 0e3dd3d8f47c..1e286448a066 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPI.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPI.java @@ -7,6 +7,7 @@ import com.dotmarketing.portlets.categories.model.Category; import com.dotmarketing.portlets.contentlet.model.Contentlet; import com.liferay.portal.model.User; +import java.util.HashMap; import java.util.List; /** * @@ -389,6 +390,13 @@ public interface CategoryAPI { * @return The List of categories that could not be deleted */ List removeAllChildren(Category parentCategory, User user, boolean respectFrontendRoles) throws DotDataException, DotSecurityException; + /** + * Recursive Method that deletes all the parent categories along with their children + * @param categoriesToDelete + * @return The List of parent categories that could not be deleted + */ + HashMap deleteCategoryAndChildren(final List categoriesToDelete, final User user, + final boolean respectFrontendRoles) throws DotDataException, DotSecurityException; /** * Retrieves a list all the line of parent categories of the given child category * a final fake top category is added at the beginning of the list to represent the top of diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPIImpl.java b/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPIImpl.java index 9e172cdbe128..33cf2ff75c55 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPIImpl.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/categories/business/CategoryAPIImpl.java @@ -6,6 +6,7 @@ import com.dotcms.contenttype.model.field.Field; import com.dotcms.contenttype.model.type.ContentType; import com.dotcms.contenttype.transform.field.LegacyFieldTransformer; +import com.dotcms.rest.api.FailedResultView; import com.dotcms.util.CollectionsUtils; import com.dotmarketing.business.APILocator; import com.dotmarketing.business.CacheLocator; @@ -17,6 +18,7 @@ import com.dotmarketing.exception.DotSecurityException; import com.dotmarketing.portlets.categories.model.Category; import com.dotmarketing.portlets.contentlet.model.Contentlet; +import com.dotmarketing.util.ActivityLogger; import com.dotmarketing.util.InodeUtils; import com.dotmarketing.util.Logger; import com.dotmarketing.util.UtilMethods; @@ -25,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -559,6 +562,62 @@ public List getAllChildren(final Category category, final User user, } return categoryTree; } + @WrapInTransaction + public HashMap deleteCategoryAndChildren(final List categoriesToDelete, final User user, + final boolean respectFrontendRoles) + throws DotDataException, DotSecurityException { + + final HashMap parentCategoryUnableToDelete = new HashMap<>(); + + for(final String parentCategoryInode : categoriesToDelete) { + + final Category parentCategory = categoryFactory.find(parentCategoryInode); + + if(parentCategory != null) { + if (!permissionAPI.doesUserHavePermission(parentCategory, + PermissionAPI.PERMISSION_EDIT, + user, respectFrontendRoles)) { + throw new DotSecurityException( + String.format("User '%s' doesn't have permission to edit Category '%s'", + null != user ? user.getUserId() : null, + parentCategory.getInode())); + } + + final List childrenCategoriesToDelete = getChildren(parentCategory, user, + false); + childrenCategoriesToDelete.forEach((category) -> { + try { + delete(category, user, false); + } catch (final DotDataException | DotSecurityException e) { + Logger.error(this, String.format( + "Child Category '%s' has dependencies. It couldn't be removed from " + + + "parent Category '%s'", category.getInode(), + parentCategory.getInode())); + parentCategoryUnableToDelete.put(parentCategory.getInode(),parentCategory); + } + }); + + try { + if (!parentCategoryUnableToDelete.containsKey(parentCategory.getInode())) { + categoryFactory.delete(parentCategory); + } + } catch (final DotDataException e) { + Logger.error(this, String.format( + "Parent Category '%s' couldn't be removed", parentCategory.getInode(), + parentCategory.getInode())); + parentCategoryUnableToDelete.put(parentCategory.getInode(), parentCategory); + } + } + else{ + Category notFound = new Category(); + notFound.setInode(parentCategoryInode); + parentCategoryUnableToDelete.put(parentCategoryInode, notFound); + } + } + + return parentCategoryUnableToDelete; + } @CloseDBIfOpened public List removeAllChildren(final Category parentCategory, final User user, diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPI.java b/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPI.java index e74dc97cf20a..019f9b852d18 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPI.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPI.java @@ -1,6 +1,7 @@ package com.dotmarketing.portlets.containers.business; import com.dotcms.contenttype.model.type.ContentType; +import com.dotcms.rest.api.v1.container.ContainerForm; import com.dotmarketing.beans.ContainerStructure; import com.dotmarketing.beans.Host; import com.dotmarketing.business.DotStateException; @@ -315,7 +316,7 @@ void deleteContainerContentTypesByContainerInode(final Container container) thro * @throws DotDataException * @throws DotSecurityException */ - Container save(Container container, List containerStructureList, Host host, User user, boolean respectFrontendRoles) throws DotDataException, DotSecurityException; + Container save(final Container container,final List containerStructureList,final Host host,final User user,final boolean respectFrontendRoles) throws DotDataException, DotSecurityException; /** * Deletes the template version by inode diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPIImpl.java b/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPIImpl.java index db6b49a47797..649fffc7e990 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPIImpl.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/containers/business/ContainerAPIImpl.java @@ -10,6 +10,7 @@ import com.dotcms.contenttype.transform.contenttype.StructureTransformer; import com.dotcms.rendering.velocity.services.ContainerLoader; import com.dotcms.rendering.velocity.services.TemplateLoader; +import com.dotcms.rest.api.v1.container.ContainerForm; import com.dotcms.system.event.local.model.Subscriber; import com.dotcms.util.CollectionsUtils; import com.dotcms.util.transform.TransformerLocator; @@ -700,7 +701,7 @@ public List findContainersUnder(Host parentPermissionable) throws Dot @WrapInTransaction @Override @SuppressWarnings("unchecked") - public Container save(Container container, List containerStructureList, Host host, User user, boolean respectFrontendRoles) throws DotDataException, DotSecurityException { + public Container save(final Container container,final List containerStructureList,final Host host,final User user,final boolean respectFrontendRoles) throws DotDataException, DotSecurityException { if (Container.SYSTEM_CONTAINER.equals(container.getIdentifier())) { Logger.debug(this, "System Container cannot be saved/updated."); throw new IllegalArgumentException("System Container and its associated data cannot be saved."); @@ -750,16 +751,22 @@ public Container save(Container container, List containerStr container.getName())); } - for (ContainerStructure cs : containerStructureList) { - Structure st = CacheLocator.getContentTypeCache().getStructureByInode(cs.getStructureId()); - if((st != null && !existingInode) && !permissionAPI.doesUserHavePermission(st, PermissionAPI.PERMISSION_READ, user, respectFrontendRoles)) { - throw new DotSecurityException( - String.format("User '%s' does not have WRITE permission on Content Type '%s'", user.getUserId(), - st.getName())); + if(containerStructureList != null) { + + for (ContainerStructure cs : containerStructureList) { + Structure st = CacheLocator.getContentTypeCache() + .getStructureByInode(cs.getStructureId()); + if ((st != null && !existingInode) && !permissionAPI.doesUserHavePermission(st, + PermissionAPI.PERMISSION_READ, user, respectFrontendRoles)) { + throw new DotSecurityException( + String.format( + "User '%s' does not have WRITE permission on Content Type '%s'", + user.getUserId(), + st.getName())); + } } } - if(!permissionAPI.doesUserHavePermission(host, PermissionAPI.PERMISSION_WRITE, user, respectFrontendRoles)) { throw new DotSecurityException( String.format("User '%s' does not have WRITE permission on Site '%s'", user.getUserId(), host)); @@ -778,6 +785,7 @@ public Container save(Container container, List containerStr APILocator.getIdentifierAPI().createNew(container, host); container.setIdentifier(ident.getId()); } + if(existingInode){ save(container, container.getInode()); } @@ -799,12 +807,14 @@ public Container save(Container container, List containerStr } } - // save the container-structure relationships , issue-2093 - for (ContainerStructure cs : containerStructureList) { - cs.setContainerId(container.getIdentifier()); - cs.setContainerInode(container.getInode()); + if(containerStructureList != null) { + // save the container-structure relationships , issue-2093 + for (ContainerStructure cs : containerStructureList) { + cs.setContainerId(container.getIdentifier()); + cs.setContainerInode(container.getInode()); + } + saveContainerStructures(containerStructureList); } - saveContainerStructures(containerStructureList); // saves to working folder under velocity new ContainerLoader().invalidate(container); diff --git a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties index f43b3d6f798a..70f66cb3cc60 100644 --- a/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties +++ b/dotCMS/src/main/webapp/WEB-INF/messages/Language.properties @@ -2026,6 +2026,9 @@ message.campaigns.deleted.error = Error Deleting Campaigns message.campaigns.deleted = Campaigns Deleted message.cannot.lock.content = Cannot lock content message.cannot.unlock.content.for.editing = Cannot unlock content +message.category.empty.title = Your category list is empty +message.category.empty.content = You haven't added anything yet, start by clicking the button below +message.category.empty.button.label = Add New Category message.category.add = Category saved, add another? message.category.addchild = Child Category added message.category.confirm.delete.categorygroup = Are you sure you want to delete this Category Group? (This operation cannot be undone) @@ -2035,6 +2038,7 @@ message.category.delete.category = Are you sure you want to delete this Category message.category.delete.failed.has.dependencies = Delete Categories failed due to Category dependencies. message.category.delete = Category deleted message.category.deleting = Deleting Categories. This operation cannot be cancelled or undone. +message.category.delete.version = Are you sure you want to delete this version of the Category? (This operation cannot be undone) message.category.error.import = An error occurred while importing Categories. message.category.existing.field = A Category field using the same Top-Level Category already exists. message.category.haschildren = This Category has child Categories @@ -2044,6 +2048,14 @@ message.category.permission.error = You cannot save the Category because you don message.category.reorder = Categories reordered message.category.toplevel.na = Not available for Top Level Categories message.category.update = Category updated +message.category.search = Type to Filter +message.category.import = IMPORT +message.category.export = EXPORT +message.category.fieldName.Name = Name +message.category.fieldName.Key = Key +message.category.fieldName.Variable = Variable +message.category.fieldName.Childrens = Childrens +message.category.fieldName.Order = Order message.categorygroup.depricated = Note: Category Groups have been deprecated and will no longer be supported in future releases. message.challenge_question.answer_failure = Your challenge answer is invalid. message.challenge_question.answer_successful = Your new password has been emailed to {0}. @@ -2074,7 +2086,23 @@ message.containers.confirm.replace.version = Are you sure you want to replace th message.containers.confirm.spellcheck.confirm = Spelling check finished; do you want to check the next field? message.containers.content.type.already.added = This Content Type was already added to the Container message.containers.copy = Container copied +message.containers.create.click_to_edit = Click to Edit +message.containers.create.description = Description +message.containers.create.max_contents = Max Contents +message.containers.create.clear = Clear +message.containers.create.content_type_code = Content Type Code +message.containers.create.add_pre_post = ADD PRE AND POST LOOP CODE +message.containers.create.pre_loop = Pre-loop +message.containers.create.post_loop = Post-loop +message.containers.create.content_type_code = Content Type Code message.containers.delete = Container archived +message.containers.empty.title = Your container list is empty +message.containers.empty.content = You haven't added anything yet, start by clicking the button below +message.containers.empty.button.label = Add New Container +message.containers.fieldName.description = Description +message.containers.fieldName.lastEdit = Mod. Date +message.containers.fieldName.name = Title +message.containers.fieldName.status = Status message.containers.full_delete.error = Error deleting the Container message.containers.full_delete = Container deleted message.containers.hints.actual.page = The actual page of content shown (beginning with 1). This is a string variable, and should be assigned or converted appropriately if necessary. @@ -2091,9 +2119,13 @@ message.containers.hints.total.pages = Total pages of content. message.containers.published = Container published message.containers.save = Container saved message.containers.select.content.type = You must select a Content Type +message.containers.tab.properties = Properties +message.containers.tab.permissions = Permissions +message.containers.tab.history = History message.containers.undelete = Container unarchived message.containers.unlocked = Container unlocked message.containers.unpublished = Container unpublished +message.container.delete.version = Are you sure you want to delete this version of the Container? (This operation cannot be undone) message.content.locked = Content locked message.content.saved = Content saved message.contentlet.add.parents = Content saved diff --git a/dotCMS/src/main/webapp/html/categories/permissions.jsp b/dotCMS/src/main/webapp/html/categories/permissions.jsp new file mode 100644 index 000000000000..c3b9dc807eb4 --- /dev/null +++ b/dotCMS/src/main/webapp/html/categories/permissions.jsp @@ -0,0 +1,23 @@ +<%@page import="javax.servlet.http.HttpServletRequest"%> +<%@page import="com.liferay.portal.util.WebKeys"%> +<%@page import="com.dotmarketing.business.APILocator"%> +<%@page import="com.liferay.portal.model.User"%> +<%@page import="com.dotcms.contenttype.business.ContentTypeAPI"%> +<%@page import="com.dotcms.contenttype.model.type.ContentType"%> +<%@page import="com.dotmarketing.util.Config"%> +<%@page import="com.liferay.portal.util.ReleaseInfo"%> + +<%@ include file="/html/common/init.jsp" %> +<%@ include file="/html/common/top_inc.jsp" %> +<%@ include file="/html/common/messages_inc.jsp" %> + +<% + final HttpServletRequest httpServletRequest = ((HttpServletRequest) request); + final String categoryId = httpServletRequest.getParameter("categoryId"); + Category category = APILocator.getCategoryAPI().findByKey(categoryId, user, true); + + request.setAttribute(com.dotmarketing.util.WebKeys.PERMISSIONABLE_EDIT, category); +%> + +<%@ include file="/html/portlet/ext/common/edit_permissions_tab_inc.jsp" %> + diff --git a/dotCMS/src/main/webapp/html/categories/push_history.jsp b/dotCMS/src/main/webapp/html/categories/push_history.jsp new file mode 100644 index 000000000000..5b872fe31180 --- /dev/null +++ b/dotCMS/src/main/webapp/html/categories/push_history.jsp @@ -0,0 +1,46 @@ +<%@page import="javax.servlet.http.HttpServletRequest"%> +<%@ page import="com.dotmarketing.util.InodeUtils"%> +<%@page import="com.liferay.portal.util.WebKeys"%> +<%@page import="com.dotmarketing.business.APILocator"%> +<%@page import="com.liferay.portal.model.User"%> +<%@page import="com.dotcms.contenttype.business.ContentTypeAPI"%> +<%@page import="com.dotcms.contenttype.model.type.ContentType"%> +<%@page import="com.dotmarketing.util.Config"%> +<%@page import="com.liferay.portal.util.ReleaseInfo"%> +<%@page import="com.dotmarketing.portlets.categories.model.Category"%> + + +<%@ include file="/html/common/init.jsp" %> +<%@ include file="/html/common/top_inc.jsp" %> + +<% + final HttpServletRequest httpServletRequest = ((HttpServletRequest) request); + final String categoryId = httpServletRequest.getParameter("categoryId"); + Category category = APILocator.getCategoryAPI().findByKey(categoryId, user, true); + request.setAttribute(com.dotmarketing.util.WebKeys.VERSIONS_INODE_EDIT, category); + request.setAttribute("hideBringBack", true); +%> + <%@ include file="/html/portlet/ext/common/edit_versions_inc.jsp" %> + + diff --git a/dotCMS/src/main/webapp/html/containers/permissions.jsp b/dotCMS/src/main/webapp/html/containers/permissions.jsp new file mode 100644 index 000000000000..7894ea3f7a51 --- /dev/null +++ b/dotCMS/src/main/webapp/html/containers/permissions.jsp @@ -0,0 +1,23 @@ +<%@page import="javax.servlet.http.HttpServletRequest"%> +<%@page import="com.liferay.portal.util.WebKeys"%> +<%@page import="com.dotmarketing.business.APILocator"%> +<%@page import="com.liferay.portal.model.User"%> +<%@page import="com.dotcms.contenttype.business.ContentTypeAPI"%> +<%@page import="com.dotcms.contenttype.model.type.ContentType"%> +<%@page import="com.dotmarketing.util.Config"%> +<%@page import="com.liferay.portal.util.ReleaseInfo"%> + +<%@ include file="/html/common/init.jsp" %> +<%@ include file="/html/common/top_inc.jsp" %> +<%@ include file="/html/common/messages_inc.jsp" %> + +<% + final HttpServletRequest httpServletRequest = ((HttpServletRequest) request); + final String containerId = httpServletRequest.getParameter("containerId"); + Container container = APILocator.getContainerAPI().getWorkingContainerById(containerId, user, true); + + request.setAttribute(com.dotmarketing.util.WebKeys.PERMISSIONABLE_EDIT, container); +%> + +<%@ include file="/html/portlet/ext/common/edit_permissions_tab_inc.jsp" %> + diff --git a/dotCMS/src/main/webapp/html/containers/push_history.jsp b/dotCMS/src/main/webapp/html/containers/push_history.jsp new file mode 100644 index 000000000000..58cefa47a1bf --- /dev/null +++ b/dotCMS/src/main/webapp/html/containers/push_history.jsp @@ -0,0 +1,46 @@ +<%@page import="javax.servlet.http.HttpServletRequest"%> +<%@ page import="com.dotmarketing.util.InodeUtils"%> +<%@page import="com.liferay.portal.util.WebKeys"%> +<%@page import="com.dotmarketing.business.APILocator"%> +<%@page import="com.liferay.portal.model.User"%> +<%@page import="com.dotcms.contenttype.business.ContentTypeAPI"%> +<%@page import="com.dotcms.contenttype.model.type.ContentType"%> +<%@page import="com.dotmarketing.util.Config"%> +<%@page import="com.liferay.portal.util.ReleaseInfo"%> +<%@page import="com.dotmarketing.portlets.containers.model.Container"%> + + +<%@ include file="/html/common/init.jsp" %> +<%@ include file="/html/common/top_inc.jsp" %> + +<% + final HttpServletRequest httpServletRequest = ((HttpServletRequest) request); + final String containerId = httpServletRequest.getParameter("containerId"); + Container container = APILocator.getContainerAPI().getWorkingContainerById(containerId, user, true); + request.setAttribute(com.dotmarketing.util.WebKeys.VERSIONS_INODE_EDIT, container); + request.setAttribute("hideBringBack", true); +%> + <%@ include file="/html/portlet/ext/common/edit_versions_inc.jsp" %> + +