mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-5316] - Content Type (#6602)
* [ADF-5316] - added content type editing for nodes * [ADF-5316] - added content type editing for nodes * [ADF-5316] - fix unit test #1 * [ADF-5316] - fix unit test #2 * [ADF-5316] - fix unit test - final * Removed failing lint word * [ADF-5316] - added alfresco api real calls * Build fixed * [ADF-5316] - fixed second loop trigger on model * [ADF-5316] - fixed unit tests * [ADF-5316] - removed unused stream * [ADF-5316] - fixed package.json * [ADF-5316] - added missing unit tests * [ADF-5316] - fixed wrong import Co-authored-by: Vito Albano <vitoalbano@vitoalbano-mbp-0120.local>
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
Title: Content Type Dialog component
|
||||||
|
Added: v2.0.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2021-01-20
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Content Type Dialog component](../../../lib/content-services/src/lib/content-type/content-type-dialog.component.ts "Defined in content-type-dialog.component.ts")
|
||||||
|
|
||||||
|
Confirm dialog when user changes content type of a node.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The [Content Type Dialog component](content-type-dialog.component.md) works as a dialog showing a confirm message when the user changes the conten type of a node. It is showing the properties of the new content type selected.
|
||||||
|
|
||||||
|
### Showing the dialog
|
||||||
|
|
||||||
|
Unlike most components, the [Content Type Dialog component](content-type-dialog.component.md) is typically shown in a dialog box
|
||||||
|
rather than the main page and you are responsible for opening the dialog yourself. You can use the
|
||||||
|
[Angular Material Dialog](https://material.angular.io/components/dialog/overview) for this,
|
||||||
|
as shown in the usage example. ADF provides the [`ContentTypeDialogComponentData`](../../../lib/content-services/src/lib/content-type/content-type-metadata.interface.ts) interface
|
||||||
|
to work with the Dialog's
|
||||||
|
[data option](https://material.angular.io/components/dialog/overview#sharing-data-with-the-dialog-component-):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ContentTypeDialogComponentData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
confirmMessage: string;
|
||||||
|
select: Subject<boolean>;
|
||||||
|
nodeType?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The properties are described in the table below:
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| title | `string` | "" | Dialog title |
|
||||||
|
| description | `string` | "" | Text to appear as description under the dialog title |
|
||||||
|
| confirmMessage | `string` | "" | Text that will be showed on the top of properties list accordion |
|
||||||
|
| select | [`Subject<Node>`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md) | | Event emitted when apply button is clicked |
|
||||||
|
| nodeType | `string` | "" | current prefixed name of the content type selected |
|
||||||
|
|
||||||
|
If you don't want to manage the dialog yourself then it is easier to use the
|
||||||
|
methods of the Content Type Property Service, which create
|
||||||
|
the dialog for you.
|
||||||
|
|
||||||
|
### Usage example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { AspectListDialogComponentData, AspectListDialogComponent} from '@adf/content-services'
|
||||||
|
import { Subject } from 'rxjs/Subject';
|
||||||
|
...
|
||||||
|
constructor(dialog: MatDialog ... ) {}
|
||||||
|
openSelectorDialog() {
|
||||||
|
const data: ContentTypeDialogComponentData = {
|
||||||
|
title: 'CORE.METADATA.CONTENT_TYPE.DIALOG.TITLE',
|
||||||
|
description: 'CORE.METADATA.CONTENT_TYPE.DIALOG.DESCRIPTION',
|
||||||
|
confirmMessage: 'CORE.METADATA.CONTENT_TYPE.DIALOG.CONFIRM',
|
||||||
|
select: select,
|
||||||
|
nodeType
|
||||||
|
};
|
||||||
|
this.dialog.open(
|
||||||
|
ContentTypeDialogComponent,
|
||||||
|
{
|
||||||
|
data, panelClass: 'adf-content-type-dialog',
|
||||||
|
width: '630px'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
data.select.subscribe((selections: Node[]) => {
|
||||||
|
// Use or store selection...
|
||||||
|
},
|
||||||
|
(error)=>{
|
||||||
|
//your error handling
|
||||||
|
},
|
||||||
|
()=>{
|
||||||
|
//action called when an action or cancel is clicked on the dialog
|
||||||
|
this.dialog.closeAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All the results will be streamed to the select [subject](http://reactivex.io/rxjs/manual/overview.html#subject) present in the [`ContentTypeDialogData`](../../../lib/content-services/src/lib/content-type/content-type-metadata.interface.ts) object passed to the dialog.
|
||||||
|
When the dialog action is selected by clicking, the `data.select` stream will be completed.
|
@@ -24,11 +24,14 @@ import { setupTestBed, AllowableOperationsEnum } from '@alfresco/adf-core';
|
|||||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||||
import { SimpleChange } from '@angular/core';
|
import { SimpleChange } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
describe('ContentMetadataCardComponent', () => {
|
describe('ContentMetadataCardComponent', () => {
|
||||||
|
|
||||||
let component: ContentMetadataCardComponent;
|
let component: ContentMetadataCardComponent;
|
||||||
let fixture: ComponentFixture<ContentMetadataCardComponent>;
|
let fixture: ComponentFixture<ContentMetadataCardComponent>;
|
||||||
|
let contentMetadataService: ContentMetadataService;
|
||||||
let node: Node;
|
let node: Node;
|
||||||
const preset = 'custom-preset';
|
const preset = 'custom-preset';
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ContentMetadataCardComponent);
|
fixture = TestBed.createComponent(ContentMetadataCardComponent);
|
||||||
|
contentMetadataService = TestBed.inject(ContentMetadataService);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
node = <Node> {
|
node = <Node> {
|
||||||
aspectNames: [],
|
aspectNames: [],
|
||||||
@@ -53,6 +57,7 @@ describe('ContentMetadataCardComponent', () => {
|
|||||||
|
|
||||||
component.node = node;
|
component.node = node;
|
||||||
component.preset = preset;
|
component.preset = preset;
|
||||||
|
spyOn(contentMetadataService, 'getContentTypeProperty').and.returnValue(of([]));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
|
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<adf-card-view
|
<adf-card-view
|
||||||
(keydown)="keyDown($event)"
|
(keydown)="keyDown($event)"
|
||||||
[properties]="basicProperties$ | async"
|
[properties]="basicProperties$ | async"
|
||||||
|
@@ -58,7 +58,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
node = <Node> {
|
node = <Node> {
|
||||||
id: 'node-id',
|
id: 'node-id',
|
||||||
aspectNames: [],
|
aspectNames: [],
|
||||||
nodeType: '',
|
nodeType: 'cm:node',
|
||||||
content: {},
|
content: {},
|
||||||
properties: {},
|
properties: {},
|
||||||
createdByUser: {},
|
createdByUser: {},
|
||||||
@@ -75,6 +75,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
|
|
||||||
component.node = node;
|
component.node = node;
|
||||||
component.preset = preset;
|
component.preset = preset;
|
||||||
|
spyOn(contentMetadataService, 'getContentTypeProperty').and.returnValue(of([]));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -169,10 +170,33 @@ describe('ContentMetadataComponent', () => {
|
|||||||
saveButton.nativeElement.click();
|
saveButton.nativeElement.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should open the confirm dialog when content type is changed', fakeAsync(() => {
|
||||||
|
component.editable = true;
|
||||||
|
const property = <CardViewBaseItemModel> { key: 'nodeType', value: 'ft:sbiruli' };
|
||||||
|
const expectedNode = Object.assign({}, node, { nodeType: 'ft:sbiruli' });
|
||||||
|
spyOn(contentMetadataService, 'openConfirmDialog').and.returnValue(of(true));
|
||||||
|
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
|
||||||
|
return of(expectedNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateService.update(property, 'ft:poppoli');
|
||||||
|
tick(600);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(100);
|
||||||
|
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
|
||||||
|
saveButton.nativeElement.click();
|
||||||
|
|
||||||
|
tick(100);
|
||||||
|
expect(component.node).toEqual(expectedNode);
|
||||||
|
expect(contentMetadataService.openConfirmDialog).toHaveBeenCalledWith({nodeType: 'ft:poppoli'});
|
||||||
|
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reseting', () => {
|
describe('Reseting', () => {
|
||||||
it('should reset changedProperties on reset click', async(async () => {
|
it('should reset changedProperties on reset click', async () => {
|
||||||
component.changedProperties = { properties: { 'property-key': 'updated-value' } };
|
component.changedProperties = { properties: { 'property-key': 'updated-value' } };
|
||||||
component.hasMetadataChanged = true;
|
component.hasMetadataChanged = true;
|
||||||
component.editable = true;
|
component.editable = true;
|
||||||
@@ -189,7 +213,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(component.changedProperties).toEqual({});
|
expect(component.changedProperties).toEqual({});
|
||||||
expect(nodesApiService.updateNode).not.toHaveBeenCalled();
|
expect(nodesApiService.updateNode).not.toHaveBeenCalled();
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Properties loading', () => {
|
describe('Properties loading', () => {
|
||||||
@@ -205,6 +229,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
|
|
||||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||||
|
|
||||||
|
expect(contentMetadataService.getContentTypeProperty).toHaveBeenCalledWith(node.nodeType);
|
||||||
expect(contentMetadataService.getBasicProperties).toHaveBeenCalledWith(expectedNode);
|
expect(contentMetadataService.getBasicProperties).toHaveBeenCalledWith(expectedNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -221,7 +246,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
component.basicProperties$.subscribe(() => {
|
component.basicProperties$.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const basicPropertiesComponent = fixture.debugElement.query(By.directive(CardViewComponent)).componentInstance;
|
const basicPropertiesComponent = fixture.debugElement.query(By.directive(CardViewComponent)).componentInstance;
|
||||||
expect(basicPropertiesComponent.properties).toBe(expectedProperties);
|
expect(basicPropertiesComponent.properties.length).toBe(expectedProperties.length);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||||
import { Node } from '@alfresco/js-api';
|
import { Node } from '@alfresco/js-api';
|
||||||
import { Observable, Subject, of } from 'rxjs';
|
import { Observable, Subject, of, zip } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
CardViewItem,
|
CardViewItem,
|
||||||
NodesApiService,
|
NodesApiService,
|
||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||||
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
|
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
|
||||||
import { takeUntil, debounceTime, catchError } from 'rxjs/operators';
|
import { takeUntil, debounceTime, catchError, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-content-metadata',
|
selector: 'adf-content-metadata',
|
||||||
@@ -155,11 +155,18 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
|
|
||||||
private loadProperties(node: Node) {
|
private loadProperties(node: Node) {
|
||||||
if (node) {
|
if (node) {
|
||||||
this.basicProperties$ = this.contentMetadataService.getBasicProperties(node);
|
this.basicProperties$ = this.getProperties(node);
|
||||||
this.groupedProperties$ = this.contentMetadataService.getGroupedProperties(node, this.preset);
|
this.groupedProperties$ = this.contentMetadataService.getGroupedProperties(node, this.preset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getProperties(node: Node) {
|
||||||
|
const properties$ = this.contentMetadataService.getBasicProperties(node);
|
||||||
|
const contentTypeProperty$ = this.contentMetadataService.getContentTypeProperty(node.nodeType);
|
||||||
|
return zip(properties$, contentTypeProperty$)
|
||||||
|
.pipe(map(([properties, contentTypeProperty]) => [...properties, ...contentTypeProperty]));
|
||||||
|
}
|
||||||
|
|
||||||
updateChanges(updatedNodeChanges) {
|
updateChanges(updatedNodeChanges) {
|
||||||
Object.keys(updatedNodeChanges).map((propertyGroup: string) => {
|
Object.keys(updatedNodeChanges).map((propertyGroup: string) => {
|
||||||
if (typeof updatedNodeChanges[propertyGroup] === 'object') {
|
if (typeof updatedNodeChanges[propertyGroup] === 'object') {
|
||||||
@@ -174,6 +181,16 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveChanges() {
|
saveChanges() {
|
||||||
|
if (this.hasContentTypeChanged(this.changedProperties)) {
|
||||||
|
this.contentMetadataService.openConfirmDialog(this.changedProperties).subscribe(() => {
|
||||||
|
this.updateNode();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.updateNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateNode() {
|
||||||
this.nodesApiService.updateNode(this.node.id, this.changedProperties).pipe(
|
this.nodesApiService.updateNode(this.node.id, this.changedProperties).pipe(
|
||||||
catchError((err) => {
|
catchError((err) => {
|
||||||
this.cardViewUpdateService.updateElement(this.targetProperty);
|
this.cardViewUpdateService.updateElement(this.targetProperty);
|
||||||
@@ -189,6 +206,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasContentTypeChanged(changedProperties): boolean {
|
||||||
|
return !!changedProperties?.nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
revertChanges() {
|
revertChanges() {
|
||||||
this.changedProperties = {};
|
this.changedProperties = {};
|
||||||
this.hasMetadataChanged = false;
|
this.hasMetadataChanged = false;
|
||||||
|
@@ -22,6 +22,7 @@ export * from './services/content-metadata.service';
|
|||||||
export * from './services/property-descriptors.service';
|
export * from './services/property-descriptors.service';
|
||||||
export * from './services/property-groups-translator.service';
|
export * from './services/property-groups-translator.service';
|
||||||
export * from './services/config/content-metadata-config.factory';
|
export * from './services/config/content-metadata-config.factory';
|
||||||
|
export * from './services/content-type-property.service';
|
||||||
|
|
||||||
export * from './services/config/indifferent-config.service';
|
export * from './services/config/indifferent-config.service';
|
||||||
export * from './services/config/layout-oriented-config.service';
|
export * from './services/config/layout-oriented-config.service';
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Node } from '@alfresco/js-api';
|
import { Node } from '@alfresco/js-api';
|
||||||
import { CardViewDateItemModel, CardViewTextItemModel, FileSizePipe } from '@alfresco/adf-core';
|
import { CardViewDateItemModel, CardViewTextItemModel, FileSizePipe } from '@alfresco/adf-core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
@@ -18,17 +18,19 @@
|
|||||||
import { AlfrescoApiService, AppConfigService, setupTestBed } from '@alfresco/adf-core';
|
import { AlfrescoApiService, AppConfigService, setupTestBed } from '@alfresco/adf-core';
|
||||||
import { ClassesApi, Node } from '@alfresco/js-api';
|
import { ClassesApi, Node } from '@alfresco/js-api';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
|
||||||
import { ContentMetadataService } from './content-metadata.service';
|
import { ContentMetadataService } from './content-metadata.service';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { PropertyGroup } from '../interfaces/property-group.interface';
|
import { PropertyGroup } from '../interfaces/property-group.interface';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ContentTypePropertiesService } from './content-type-property.service';
|
||||||
|
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||||
|
|
||||||
describe('ContentMetaDataService', () => {
|
describe('ContentMetaDataService', () => {
|
||||||
|
|
||||||
let service: ContentMetadataService;
|
let service: ContentMetadataService;
|
||||||
let classesApi: ClassesApi;
|
let classesApi: ClassesApi;
|
||||||
let appConfig: AppConfigService;
|
let appConfig: AppConfigService;
|
||||||
|
let contentPropertyService: ContentTypePropertiesService;
|
||||||
|
|
||||||
const exifResponse: PropertyGroup = {
|
const exifResponse: PropertyGroup = {
|
||||||
name: 'exif:exif',
|
name: 'exif:exif',
|
||||||
@@ -64,6 +66,7 @@ describe('ContentMetaDataService', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = TestBed.inject(ContentMetadataService);
|
service = TestBed.inject(ContentMetadataService);
|
||||||
|
contentPropertyService = TestBed.inject(ContentTypePropertiesService);
|
||||||
const alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
const alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||||
classesApi = alfrescoApiService.classesApi;
|
classesApi = alfrescoApiService.classesApi;
|
||||||
appConfig = TestBed.inject(AppConfigService);
|
appConfig = TestBed.inject(AppConfigService);
|
||||||
@@ -89,6 +92,28 @@ describe('ContentMetaDataService', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the content type property', () => {
|
||||||
|
spyOn(contentPropertyService, 'getContentTypeCardItem').and.returnValue(of({ label: 'hello i am a weird content type'}));
|
||||||
|
|
||||||
|
service.getContentTypeProperty('fn:fakenode').subscribe(
|
||||||
|
(res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.label).toBe('hello i am a weird content type');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger the opening of the content type dialog', () => {
|
||||||
|
spyOn(contentPropertyService, 'openContentTypeDialogConfirm').and.returnValue(of());
|
||||||
|
|
||||||
|
service.openConfirmDialog('fn:fakenode').subscribe(
|
||||||
|
() => {
|
||||||
|
expect(contentPropertyService.openContentTypeDialogConfirm).toHaveBeenCalledWith('fn:fakenode');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe('AspectOriented preset', () => {
|
describe('AspectOriented preset', () => {
|
||||||
|
|
||||||
it('should return response with exif property', (done) => {
|
it('should return response with exif property', (done) => {
|
||||||
|
@@ -25,7 +25,7 @@ import { CardViewGroup, OrganisedPropertyGroup } from '../interfaces/content-met
|
|||||||
import { ContentMetadataConfigFactory } from './config/content-metadata-config.factory';
|
import { ContentMetadataConfigFactory } from './config/content-metadata-config.factory';
|
||||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { ContentTypePropertiesService } from './content-type-property.service';
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@@ -36,13 +36,22 @@ export class ContentMetadataService {
|
|||||||
constructor(private basicPropertiesService: BasicPropertiesService,
|
constructor(private basicPropertiesService: BasicPropertiesService,
|
||||||
private contentMetadataConfigFactory: ContentMetadataConfigFactory,
|
private contentMetadataConfigFactory: ContentMetadataConfigFactory,
|
||||||
private propertyGroupTranslatorService: PropertyGroupTranslatorService,
|
private propertyGroupTranslatorService: PropertyGroupTranslatorService,
|
||||||
private propertyDescriptorsService: PropertyDescriptorsService) {
|
private propertyDescriptorsService: PropertyDescriptorsService,
|
||||||
|
private contentTypePropertyService: ContentTypePropertiesService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBasicProperties(node: Node): Observable<CardViewItem[]> {
|
getBasicProperties(node: Node): Observable<CardViewItem[]> {
|
||||||
return of(this.basicPropertiesService.getProperties(node));
|
return of(this.basicPropertiesService.getProperties(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getContentTypeProperty(nodeType: string): Observable<CardViewItem[]> {
|
||||||
|
return this.contentTypePropertyService.getContentTypeCardItem(nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmDialog(changedProperties): Observable<any> {
|
||||||
|
return this.contentTypePropertyService.openContentTypeDialogConfirm(changedProperties.nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
getGroupedProperties(node: Node, presetName: string = 'default'): Observable<CardViewGroup[]> {
|
getGroupedProperties(node: Node, presetName: string = 'default'): Observable<CardViewGroup[]> {
|
||||||
let groupedProperties = of([]);
|
let groupedProperties = of([]);
|
||||||
|
|
||||||
|
@@ -0,0 +1,86 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Node } from '@alfresco/js-api';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { ContentMetadataService } from './content-metadata.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ContentTypePropertiesService } from './content-type-property.service';
|
||||||
|
import { setupTestBed } from 'core';
|
||||||
|
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
describe('ContentMetaDataService', () => {
|
||||||
|
|
||||||
|
let service: ContentMetadataService;
|
||||||
|
let contentPropertyService: ContentTypePropertiesService;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = TestBed.inject(ContentMetadataService);
|
||||||
|
contentPropertyService = TestBed.inject(ContentTypePropertiesService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return all the properties of the node', () => {
|
||||||
|
const fakeNode: Node = <Node> {
|
||||||
|
name: 'Node',
|
||||||
|
id: 'fake-id',
|
||||||
|
isFile: true,
|
||||||
|
aspectNames: ['exif:exif'],
|
||||||
|
createdByUser: {displayName: 'test-user'},
|
||||||
|
modifiedByUser: {displayName: 'test-user-modified'}
|
||||||
|
};
|
||||||
|
|
||||||
|
service.getBasicProperties(fakeNode).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect(res.length).toEqual(10);
|
||||||
|
expect(res[0].value).toEqual('Node');
|
||||||
|
expect(res[1].value).toBeFalsy();
|
||||||
|
expect(res[2].value).toBe('test-user');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the content type property', () => {
|
||||||
|
spyOn(contentPropertyService, 'getContentTypeCardItem').and.returnValue(of({ label: 'hello i am a weird content type'}));
|
||||||
|
|
||||||
|
service.getContentTypeProperty('fn:fakenode').subscribe(
|
||||||
|
(res: any) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.label).toBe('hello i am a weird content type');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger the opening of the content type dialog', () => {
|
||||||
|
spyOn(contentPropertyService, 'openContentTypeDialogConfirm').and.returnValue(of());
|
||||||
|
|
||||||
|
service.openConfirmDialog('fn:fakenode').subscribe(
|
||||||
|
() => {
|
||||||
|
expect(contentPropertyService.openContentTypeDialogConfirm).toHaveBeenCalledWith('fn:fakenode');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,106 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { CardViewItem, CardViewSelectItemModel, CardViewSelectItemOption } from '@alfresco/adf-core';
|
||||||
|
import { Observable, of, Subject, zip } from 'rxjs';
|
||||||
|
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||||
|
import { ContentTypeDialogComponent } from '../../content-type/content-type-dialog.component';
|
||||||
|
import { ContentTypeDialogComponentData } from '../../content-type/content-type-metadata.interface';
|
||||||
|
import { ContentTypeService } from '../../content-type/content-type.service';
|
||||||
|
import { TypeEntry } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ContentTypePropertiesService {
|
||||||
|
|
||||||
|
constructor(private contentTypeService: ContentTypeService, private dialog: MatDialog) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getContentTypeCardItem(nodeType: string): Observable<CardViewItem[]> {
|
||||||
|
return this.contentTypeService.getContentTypeByPrefix(nodeType).
|
||||||
|
pipe(
|
||||||
|
map((contentType) => {
|
||||||
|
const contentTypesOptions$ = this.getContentTypesAsSelectOption(contentType);
|
||||||
|
const contentTypeCard = this.buildContentTypeSelectCardModel(contentType.entry.id, contentTypesOptions$);
|
||||||
|
return [contentTypeCard];
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildContentTypeSelectCardModel(currentValue: string, options$: Observable<CardViewSelectItemOption<string>[]>): CardViewSelectItemModel<string> {
|
||||||
|
const contentTypeCard = new CardViewSelectItemModel({
|
||||||
|
label: 'CORE.METADATA.BASIC.CONTENT_TYPE',
|
||||||
|
value: currentValue,
|
||||||
|
key: 'nodeType',
|
||||||
|
editable: true,
|
||||||
|
options$: options$
|
||||||
|
});
|
||||||
|
|
||||||
|
return contentTypeCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getContentTypesAsSelectOption(currentType: TypeEntry): Observable<CardViewSelectItemOption<string>[]> {
|
||||||
|
const childrenTypes$ = this.contentTypeService.getContentTypeChildren(currentType.entry.id);
|
||||||
|
return zip(childrenTypes$, of(currentType)).pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
map(([contentTypesEntries, currentContentType]) => {
|
||||||
|
const updatedTypes = this.appendCurrentType(currentContentType, contentTypesEntries);
|
||||||
|
return updatedTypes.map((contentType) => <CardViewSelectItemOption<string>> { key: contentType.entry.id, label: contentType.entry.title ?? contentType.entry.id});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private appendCurrentType(currentType: TypeEntry, contentTypesEntries: TypeEntry[]): TypeEntry[] {
|
||||||
|
const resultTypes = contentTypesEntries;
|
||||||
|
if (contentTypesEntries.indexOf(currentType) === -1) {
|
||||||
|
resultTypes.push(currentType);
|
||||||
|
}
|
||||||
|
return resultTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
openContentTypeDialogConfirm(nodeType): Observable<boolean> {
|
||||||
|
const select = new Subject<boolean>();
|
||||||
|
select.subscribe({
|
||||||
|
complete: this.close.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: ContentTypeDialogComponentData = {
|
||||||
|
title: 'CORE.METADATA.CONTENT_TYPE.DIALOG.TITLE',
|
||||||
|
description: 'CORE.METADATA.CONTENT_TYPE.DIALOG.DESCRIPTION',
|
||||||
|
confirmMessage: 'CORE.METADATA.CONTENT_TYPE.DIALOG.CONFIRM',
|
||||||
|
select: select,
|
||||||
|
nodeType
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openDialog(data, 'adf-content-type-dialog', '600px');
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.dialog.closeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private openDialog(data: ContentTypeDialogComponentData, panelClass: string, width: string) {
|
||||||
|
this.dialog.open(ContentTypeDialogComponent, {
|
||||||
|
data,
|
||||||
|
panelClass,
|
||||||
|
width,
|
||||||
|
disableClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
<div class="adf-content-type-dialog">
|
||||||
|
<h2 mat-dialog-title class="adf-content-type-dialog-title" data-automation-id="content-type-dialog-title">{{title |
|
||||||
|
translate}}</h2>
|
||||||
|
<mat-dialog-content class="mat-typography" class="adf-content-type-dialog-content"
|
||||||
|
data-automation-id="content-type-dialog-content">
|
||||||
|
<h4 data-automation-id="content-type-dialog-description">{{description | translate}}</h4>
|
||||||
|
<p data-automation-id="content-type-dialog-confirm-message">{{confirmMessage | translate}}</p>
|
||||||
|
<mat-accordion>
|
||||||
|
<mat-expansion-panel class="adf-content-type-accordion">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>
|
||||||
|
{{'CORE.METADATA.CONTENT_TYPE.DIALOG.VIEW_DETAILS' | translate}}
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<table mat-table [dataSource]="currentContentType?.entry?.properties"
|
||||||
|
*ngIf="currentContentType?.entry?.properties?.length > 0" class="adf-content-type-table">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'CORE.METADATA.CONTENT_TYPE.DIALOG.PROPERTY.NAME' |
|
||||||
|
translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.id}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
{{'CORE.METADATA.CONTENT_TYPE.DIALOG.PROPERTY.DESCRIPTION' | translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.title}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="dataType">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'CORE.METADATA.CONTENT_TYPE.DIALOG.PROPERTY.DATA_TYPE'
|
||||||
|
| translate}} </th>
|
||||||
|
<td mat-cell *matCellDef="let property"> {{property.dataType}} </td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="propertyColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: propertyColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button mat-dialog-close
|
||||||
|
id="conten-type-dialog-actions-cancel">{{'CORE.METADATA.CONTENT_TYPE.DIALOG.CANCEL' | translate }}</button>
|
||||||
|
<button mat-button class="adf-content-type-dialog-apply-button" id="content-type-dialog-apply-button"
|
||||||
|
[mat-dialog-close]="true" cdkFocusInitial (click)="onApply()">{{'CORE.METADATA.CONTENT_TYPE.DIALOG.APPLY' |
|
||||||
|
translate}}</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
</div>
|
@@ -0,0 +1,41 @@
|
|||||||
|
@mixin adf-content-type-dialog-theme($theme) {
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
$warn: map-get($theme, warn);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
|
||||||
|
.adf {
|
||||||
|
|
||||||
|
&-content-type-dialog {
|
||||||
|
.mat-expansion-panel {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-type-accordion {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-type-dialog-title {
|
||||||
|
font-size: large;
|
||||||
|
font-weight: 200;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-type-dialog-description {
|
||||||
|
font-size: small;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-type-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content-type-dialog-apply-button {
|
||||||
|
color: mat-color($primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,158 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of, Subject } from 'rxjs';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { ContentTypeDialogComponent } from './content-type-dialog.component';
|
||||||
|
import { ContentTypeService } from './content-type.service';
|
||||||
|
import { ContentTypeDialogComponentData } from './content-type-metadata.interface';
|
||||||
|
import { TypeEntry } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
const elementCustom: TypeEntry = {
|
||||||
|
entry: {
|
||||||
|
id: 'ck:pippobaudo',
|
||||||
|
title: 'PIPPO-BAUDO',
|
||||||
|
description: 'Doloro reaepgfihawpefih peahfa powfj p[qwofhjaq[ fq[owfj[qowjf[qowfgh[qowh f[qowhfj [qwohf',
|
||||||
|
parentId: 'cm:content',
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'ck:PropA',
|
||||||
|
dataType: 'ck:propA',
|
||||||
|
defaultValue: 'HERE I AM',
|
||||||
|
description: 'A property',
|
||||||
|
isMandatory: false,
|
||||||
|
isMandatoryEnforced: false,
|
||||||
|
isMultiValued: false,
|
||||||
|
title: 'PropertyA'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ck:PropB',
|
||||||
|
dataType: 'ck:propB',
|
||||||
|
defaultValue: 'HERE I AM',
|
||||||
|
description: 'A property',
|
||||||
|
|
||||||
|
isMandatory: false,
|
||||||
|
isMandatoryEnforced: false,
|
||||||
|
isMultiValued: false,
|
||||||
|
title: 'PropertyB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ck:PropC',
|
||||||
|
dataType: 'ck:propC',
|
||||||
|
defaultValue: 'HERE I AM',
|
||||||
|
description: 'A property',
|
||||||
|
isMandatory: false,
|
||||||
|
isMandatoryEnforced: false,
|
||||||
|
isMultiValued: false,
|
||||||
|
title: 'PropertyC'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Content Type Dialog Component', () => {
|
||||||
|
let fixture: ComponentFixture<ContentTypeDialogComponent>;
|
||||||
|
let contentTypeService: ContentTypeService;
|
||||||
|
let data: ContentTypeDialogComponentData;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
data = <ContentTypeDialogComponentData> {
|
||||||
|
title: 'Title',
|
||||||
|
description: 'Description that can be longer or shorter',
|
||||||
|
nodeType: 'fk:fakeNode',
|
||||||
|
confirmMessage: 'Do you want to jump? Y/N',
|
||||||
|
select: new Subject<boolean>()
|
||||||
|
};
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule,
|
||||||
|
MatDialogModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||||
|
{
|
||||||
|
provide: MatDialogRef,
|
||||||
|
useValue: {
|
||||||
|
keydownEvents: () => of(null),
|
||||||
|
backdropClick: () => of(null),
|
||||||
|
close: jasmine.createSpy('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
contentTypeService = TestBed.inject(ContentTypeService);
|
||||||
|
spyOn(contentTypeService, 'getContentTypeByPrefix').and.returnValue(of(elementCustom));
|
||||||
|
fixture = TestBed.createComponent(ContentTypeDialogComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show basic information for the dialog', () => {
|
||||||
|
const dialogTitle = fixture.nativeElement.querySelector('[data-automation-id="content-type-dialog-title"]');
|
||||||
|
expect(dialogTitle).not.toBeNull();
|
||||||
|
expect(dialogTitle.innerText).toBe(data.title);
|
||||||
|
|
||||||
|
const description = fixture.nativeElement.querySelector('[data-automation-id="content-type-dialog-description"]');
|
||||||
|
expect(description).not.toBeNull();
|
||||||
|
expect(description.innerText).toBe(data.description);
|
||||||
|
|
||||||
|
const confirmMessage = fixture.nativeElement.querySelector('[data-automation-id="content-type-dialog-confirm-message"]');
|
||||||
|
expect(confirmMessage).not.toBeNull();
|
||||||
|
expect(confirmMessage.innerText).toBe(data.confirmMessage);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete the select stream Cancel button is clicked', (done) => {
|
||||||
|
data.select.subscribe(() => { }, () => { }, () => done());
|
||||||
|
const cancelButton: HTMLButtonElement = fixture.nativeElement.querySelector('#conten-type-dialog-actions-cancel');
|
||||||
|
expect(cancelButton).toBeDefined();
|
||||||
|
cancelButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the property of the aspect', async () => {
|
||||||
|
const showPropertyAccordon: HTMLButtonElement = fixture.nativeElement.querySelector('.adf-content-type-accordion .mat-expansion-panel-header');
|
||||||
|
expect(showPropertyAccordon).toBeDefined();
|
||||||
|
showPropertyAccordon.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const propertyShowed: NodeList = fixture.nativeElement.querySelectorAll('.adf-content-type-table .mat-row');
|
||||||
|
expect(propertyShowed.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit true when apply is clicked', (done) => {
|
||||||
|
data.select.subscribe((value) => {
|
||||||
|
expect(value).toBe(true);
|
||||||
|
}, () => { }, () => done());
|
||||||
|
const applyButton: HTMLButtonElement = fixture.nativeElement.querySelector('#content-type-dialog-apply-button');
|
||||||
|
expect(applyButton).toBeDefined();
|
||||||
|
applyButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,72 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TypeEntry } from '@alfresco/js-api';
|
||||||
|
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { ContentTypeDialogComponentData } from './content-type-metadata.interface';
|
||||||
|
import { ContentTypeService } from './content-type.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-content-type-dialog',
|
||||||
|
templateUrl: './content-type-dialog.component.html',
|
||||||
|
styleUrls: ['./content-type-dialog.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ContentTypeDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
nodeType: string;
|
||||||
|
confirmMessage: string;
|
||||||
|
|
||||||
|
currentContentType: TypeEntry;
|
||||||
|
|
||||||
|
propertyColumns: string[] = ['name', 'title', 'dataType'];
|
||||||
|
|
||||||
|
constructor(private dialog: MatDialogRef<ContentTypeDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: ContentTypeDialogComponentData,
|
||||||
|
private contentTypeService: ContentTypeService) {
|
||||||
|
this.title = data.title;
|
||||||
|
this.description = data.description;
|
||||||
|
this.confirmMessage = data.confirmMessage;
|
||||||
|
this.nodeType = data.nodeType;
|
||||||
|
|
||||||
|
this.contentTypeService.getContentTypeByPrefix(this.nodeType).subscribe((contentTypeEntry) => {
|
||||||
|
this.currentContentType = contentTypeEntry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dialog.backdropClick().subscribe(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.data.select.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onApply() {
|
||||||
|
this.data.select.next(true);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
export interface ContentTypeDialogComponentData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
confirmMessage: string;
|
||||||
|
select: Subject<boolean>;
|
||||||
|
nodeType?: string;
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { ContentTypeDialogComponent } from './content-type-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
TranslateModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatButtonModule
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
ContentTypeDialogComponent
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ContentTypeDialogComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ContentTypeModule { }
|
@@ -0,0 +1,66 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TypeEntry } from '@alfresco/js-api';
|
||||||
|
import { AlfrescoApiService } from 'core';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { ContentTypeService } from './content-type.service';
|
||||||
|
|
||||||
|
describe('ContentTypeService', () => {
|
||||||
|
|
||||||
|
const fakeEntryMock: TypeEntry = {
|
||||||
|
entry: {
|
||||||
|
id : 'fake-type-id',
|
||||||
|
title: 'fake-title',
|
||||||
|
description: 'optional-fake-description',
|
||||||
|
parentId: 'cm:parent',
|
||||||
|
properties: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockTypesApi = jasmine.createSpyObj('TypesApi', ['getType', 'listTypes']);
|
||||||
|
const alfrescoApiService: AlfrescoApiService = new AlfrescoApiService(null, null);
|
||||||
|
const contentTypeService: ContentTypeService = new ContentTypeService(alfrescoApiService);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnProperty(alfrescoApiService, 'typesApi').and.returnValue(mockTypesApi);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a node type info', (done) => {
|
||||||
|
mockTypesApi.getType.and.returnValue(of(fakeEntryMock));
|
||||||
|
contentTypeService.getContentTypeByPrefix('whatever-whenever').subscribe((result) => {
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result.entry.id).toBe('fake-type-id');
|
||||||
|
expect(mockTypesApi.getType).toHaveBeenCalledWith('whatever-whenever');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the list of children types', (done) => {
|
||||||
|
mockTypesApi.listTypes.and.returnValue(of({ list: {entries: [fakeEntryMock]}}));
|
||||||
|
contentTypeService.getContentTypeChildren('whatever-whenever').subscribe((results: TypeEntry[]) => {
|
||||||
|
expect(results).toBeDefined();
|
||||||
|
expect(results).not.toBeNull();
|
||||||
|
expect(results.length).toBe(1);
|
||||||
|
expect(results[0].entry.id).toBe('fake-type-id');
|
||||||
|
expect(mockTypesApi.listTypes).toHaveBeenCalledWith({ where: '(parentIds in (\'whatever-whenever\') and not namespaceUri matches(\'http://www.alfresco.org/model.*\'))' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TypeEntry, TypePaging } from '@alfresco/js-api';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ContentTypeService {
|
||||||
|
|
||||||
|
constructor(private alfrescoApiService: AlfrescoApiService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getContentTypeByPrefix(prefixedType: string): Observable<TypeEntry> {
|
||||||
|
return from(this.alfrescoApiService.typesApi.getType(prefixedType));
|
||||||
|
}
|
||||||
|
|
||||||
|
getContentTypeChildren(nodeType: string): Observable<TypeEntry[]> {
|
||||||
|
const opts = {where : `(parentIds in ('${nodeType}') and not namespaceUri matches('http://www.alfresco.org/model.*'))`};
|
||||||
|
return from(this.alfrescoApiService.typesApi.listTypes(opts)).pipe(
|
||||||
|
map((result: TypePaging) => result.list.entries)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
23
lib/content-services/src/lib/content-type/index.ts
Normal file
23
lib/content-services/src/lib/content-type/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './content-type.service';
|
||||||
|
// export * from './content-type.model';
|
||||||
|
export * from './content-type-metadata.interface';
|
||||||
|
export * from './content-type-dialog.component';
|
||||||
|
|
||||||
|
export * from './content-type.module';
|
18
lib/content-services/src/lib/content-type/public-api.ts
Normal file
18
lib/content-services/src/lib/content-type/public-api.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './public-api';
|
@@ -39,6 +39,7 @@ import { FolderDirectiveModule } from './folder-directive/folder-directive.modul
|
|||||||
import { ContentMetadataModule } from './content-metadata/content-metadata.module';
|
import { ContentMetadataModule } from './content-metadata/content-metadata.module';
|
||||||
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
|
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
|
||||||
import { TreeViewModule } from './tree-view/tree-view.module';
|
import { TreeViewModule } from './tree-view/tree-view.module';
|
||||||
|
import { ContentTypeModule } from './content-type/content-type.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -63,7 +64,8 @@ import { TreeViewModule } from './tree-view/tree-view.module';
|
|||||||
ContentDirectiveModule,
|
ContentDirectiveModule,
|
||||||
PermissionManagerModule,
|
PermissionManagerModule,
|
||||||
VersionManagerModule,
|
VersionManagerModule,
|
||||||
TreeViewModule
|
TreeViewModule,
|
||||||
|
ContentTypeModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -92,7 +94,8 @@ import { TreeViewModule } from './tree-view/tree-view.module';
|
|||||||
ContentDirectiveModule,
|
ContentDirectiveModule,
|
||||||
PermissionManagerModule,
|
PermissionManagerModule,
|
||||||
VersionManagerModule,
|
VersionManagerModule,
|
||||||
TreeViewModule
|
TreeViewModule,
|
||||||
|
ContentTypeModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ContentModule {
|
export class ContentModule {
|
||||||
|
@@ -334,7 +334,23 @@
|
|||||||
"CREATOR": "Creator",
|
"CREATOR": "Creator",
|
||||||
"CREATED_DATE": "Created Date",
|
"CREATED_DATE": "Created Date",
|
||||||
"MODIFIER": "Modifier",
|
"MODIFIER": "Modifier",
|
||||||
"MODIFIED_DATE": "Modified Date"
|
"MODIFIED_DATE": "Modified Date",
|
||||||
|
"CONTENT_TYPE": "Content Type"
|
||||||
|
},
|
||||||
|
"CONTENT_TYPE": {
|
||||||
|
"DIALOG" :{
|
||||||
|
"TITLE" : "Change content type",
|
||||||
|
"DESCRIPTION": "Making this change to the content type will permanently add some properties and stored metadata to the document.",
|
||||||
|
"CONFIRM": "Are you sure you want to change the content type?",
|
||||||
|
"CANCEL": "CANCEL",
|
||||||
|
"APPLY": "SAVE CHANGES",
|
||||||
|
"VIEW_DETAILS": "View details",
|
||||||
|
"PROPERTY" :{
|
||||||
|
"NAME" : "Name",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"DATA_TYPE": "Data type"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ERRORS": {
|
"ERRORS": {
|
||||||
"GENERIC": "Error updating property",
|
"GENERIC": "Error updating property",
|
||||||
|
@@ -28,7 +28,6 @@ export const mockContentModelTextProperty = {
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
mandatoryEnforced: false,
|
mandatoryEnforced: false,
|
||||||
indexed: false,
|
indexed: false,
|
||||||
facetable: 'FALSE',
|
|
||||||
indexTokenisationMode: '',
|
indexTokenisationMode: '',
|
||||||
constraints: []
|
constraints: []
|
||||||
};
|
};
|
||||||
@@ -44,7 +43,6 @@ export const mockContentModelDateProperty = {
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
mandatoryEnforced: false,
|
mandatoryEnforced: false,
|
||||||
indexed: false,
|
indexed: false,
|
||||||
facetable: 'FALSE',
|
|
||||||
indexTokenisationMode: '',
|
indexTokenisationMode: '',
|
||||||
constraints: []
|
constraints: []
|
||||||
};
|
};
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
@import '../permission-manager/components/add-permission/add-permission-panel.component';
|
@import '../permission-manager/components/add-permission/add-permission-panel.component';
|
||||||
@import '../tree-view/components/tree-view.component';
|
@import '../tree-view/components/tree-view.component';
|
||||||
@import '../version-manager/version-comparison.component';
|
@import '../version-manager/version-comparison.component';
|
||||||
|
@import '../content-type/content-type-dialog.component';
|
||||||
|
|
||||||
@mixin adf-content-services-theme($theme) {
|
@mixin adf-content-services-theme($theme) {
|
||||||
@include adf-breadcrumb-theme($theme);
|
@include adf-breadcrumb-theme($theme);
|
||||||
@@ -52,4 +53,5 @@
|
|||||||
@include adf-search-filter-theme($theme);
|
@include adf-search-filter-theme($theme);
|
||||||
@include adf-search-chip-list-theme($theme);
|
@include adf-search-chip-list-theme($theme);
|
||||||
@include adf-version-comparison-theme($theme);
|
@include adf-version-comparison-theme($theme);
|
||||||
|
@include adf-content-type-dialog-theme($theme);
|
||||||
}
|
}
|
||||||
|
@@ -33,5 +33,6 @@ export * from './lib/permission-manager/index';
|
|||||||
export * from './lib/content-node-share/index';
|
export * from './lib/content-node-share/index';
|
||||||
export * from './lib/tree-view/index';
|
export * from './lib/tree-view/index';
|
||||||
export * from './lib/group/index';
|
export * from './lib/group/index';
|
||||||
|
export * from './lib/content-type/index';
|
||||||
|
|
||||||
export * from './lib/content.module';
|
export * from './lib/content.module';
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)"></adf-select-filter-input>
|
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)"></adf-select-filter-input>
|
||||||
|
|
||||||
<mat-option *ngIf="showNoneOption()">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
<mat-option *ngIf="showNoneOption()">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
||||||
<mat-option *ngFor="let option of getList() | async"
|
<mat-option *ngFor="let option of list$ | async"
|
||||||
[value]="option.key">
|
[value]="option.key">
|
||||||
{{ option.label | translate }}
|
{{ option.label | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import { Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
import { Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
||||||
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
||||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||||
import { Observable, Subject } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
|
||||||
import { CardViewSelectItemOption } from '../../interfaces/card-view.interfaces';
|
import { CardViewSelectItemOption } from '../../interfaces/card-view.interfaces';
|
||||||
import { MatSelectChange } from '@angular/material/select';
|
import { MatSelectChange } from '@angular/material/select';
|
||||||
import { BaseCardView } from '../base-card-view';
|
import { BaseCardView } from '../base-card-view';
|
||||||
@@ -44,11 +44,13 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
displayEmpty: boolean = true;
|
displayEmpty: boolean = true;
|
||||||
|
|
||||||
value: string | number;
|
value: string | number;
|
||||||
filter: string = '';
|
filter$: BehaviorSubject<string> = new BehaviorSubject('');
|
||||||
showInputFilter: boolean = false;
|
showInputFilter: boolean = false;
|
||||||
|
|
||||||
private onDestroy$ = new Subject<void>();
|
private onDestroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
list$: Observable<CardViewSelectItemOption<string | number>[]> = null;
|
||||||
|
|
||||||
constructor(cardViewUpdateService: CardViewUpdateService, private appConfig: AppConfigService) {
|
constructor(cardViewUpdateService: CardViewUpdateService, private appConfig: AppConfigService) {
|
||||||
super(cardViewUpdateService);
|
super(cardViewUpdateService);
|
||||||
}
|
}
|
||||||
@@ -63,10 +65,12 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
.subscribe((options: CardViewSelectItemOption<string>[]) => {
|
.subscribe((options: CardViewSelectItemOption<string>[]) => {
|
||||||
this.showInputFilter = options.length > this.optionsLimit;
|
this.showInputFilter = options.length > this.optionsLimit;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.list$ = this.getList();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterInputChange(value: string) {
|
onFilterInputChange(value: string) {
|
||||||
this.filter = value.toString();
|
this.filter$.next(value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditable(): boolean {
|
isEditable(): boolean {
|
||||||
@@ -77,12 +81,12 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
return this.options$ || this.property.options$;
|
return this.options$ || this.property.options$;
|
||||||
}
|
}
|
||||||
|
|
||||||
getList(): Observable<CardViewSelectItemOption<string>[]> {
|
getList(): Observable<CardViewSelectItemOption<string | number>[]> {
|
||||||
return this.getOptions()
|
return combineLatest([this.getOptions(), this.filter$])
|
||||||
.pipe(
|
.pipe(
|
||||||
map((items: CardViewSelectItemOption<string>[]) => items.filter(
|
map(([items, filter]) => items.filter((item: CardViewSelectItemOption<string>) =>
|
||||||
(item: CardViewSelectItemOption<string>) =>
|
filter ? item.label.toLowerCase().includes(filter.toLowerCase())
|
||||||
item.label.toLowerCase().includes(this.filter.toLowerCase()))),
|
: true)),
|
||||||
takeUntil(this.onDestroy$)
|
takeUntil(this.onDestroy$)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -26,18 +26,25 @@ export class CardViewSelectItemModel<T> extends CardViewBaseItemModel implements
|
|||||||
type: string = 'select';
|
type: string = 'select';
|
||||||
options$: Observable<CardViewSelectItemOption<T>[]>;
|
options$: Observable<CardViewSelectItemOption<T>[]>;
|
||||||
|
|
||||||
|
valueFetch$: Observable<string> = null;
|
||||||
|
|
||||||
constructor(cardViewSelectItemProperties: CardViewSelectItemProperties<T>) {
|
constructor(cardViewSelectItemProperties: CardViewSelectItemProperties<T>) {
|
||||||
super(cardViewSelectItemProperties);
|
super(cardViewSelectItemProperties);
|
||||||
|
|
||||||
this.options$ = cardViewSelectItemProperties.options$;
|
this.options$ = cardViewSelectItemProperties.options$;
|
||||||
}
|
|
||||||
|
|
||||||
get displayValue() {
|
this.valueFetch$ = this.options$.pipe(
|
||||||
return this.options$.pipe(
|
|
||||||
switchMap((options) => {
|
switchMap((options) => {
|
||||||
const option = options.find((o) => o.key === this.value?.toString());
|
const option = options.find((o) => o.key === this.value?.toString());
|
||||||
return of(option ? option.label : '');
|
return of(option ? option.label : '');
|
||||||
})
|
}));
|
||||||
);
|
}
|
||||||
|
|
||||||
|
get displayValue() {
|
||||||
|
return this.valueFetch$;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value: any) {
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -199,7 +199,23 @@
|
|||||||
"CREATOR": "Creator",
|
"CREATOR": "Creator",
|
||||||
"CREATED_DATE": "Created Date",
|
"CREATED_DATE": "Created Date",
|
||||||
"MODIFIER": "Modifier",
|
"MODIFIER": "Modifier",
|
||||||
"MODIFIED_DATE": "Modified Date"
|
"MODIFIED_DATE": "Modified Date",
|
||||||
|
"CONTENT_TYPE": "Content Type"
|
||||||
|
},
|
||||||
|
"CONTENT_TYPE": {
|
||||||
|
"DIALOG" :{
|
||||||
|
"TITLE" : "Change content type",
|
||||||
|
"DESCRIPTION": "Making this change to the content type will permanently add some properties and stored metadata to the document.",
|
||||||
|
"CONFIRM": "Are you sure you want to change the content type?",
|
||||||
|
"CANCEL": "CANCEL",
|
||||||
|
"APPLY": "SAVE CHANGES",
|
||||||
|
"VIEW_DETAILS": "View details",
|
||||||
|
"PROPERTY" :{
|
||||||
|
"NAME" : "Name",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"DATA_TYPE": "Data type"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"EDIT": "Edit",
|
"EDIT": "Edit",
|
||||||
|
@@ -23,7 +23,7 @@ import {
|
|||||||
SearchApi,
|
SearchApi,
|
||||||
Node,
|
Node,
|
||||||
GroupsApi,
|
GroupsApi,
|
||||||
AlfrescoApiCompatibility, AlfrescoApiConfig
|
AlfrescoApiCompatibility, AlfrescoApiConfig, TypesApi
|
||||||
} from '@alfresco/js-api';
|
} from '@alfresco/js-api';
|
||||||
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
|
||||||
import { Subject, Observable, BehaviorSubject } from 'rxjs';
|
import { Subject, Observable, BehaviorSubject } from 'rxjs';
|
||||||
@@ -102,6 +102,10 @@ export class AlfrescoApiService {
|
|||||||
return new GroupsApi(this.getInstance());
|
return new GroupsApi(this.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get typesApi(): TypesApi {
|
||||||
|
return new TypesApi(this.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected appConfig: AppConfigService,
|
protected appConfig: AppConfigService,
|
||||||
protected storageService: StorageService) {
|
protected storageService: StorageService) {
|
||||||
|
Reference in New Issue
Block a user