mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-6165] Add capability in the Attach File of displaying metadata fields in addition to file name (#7324)
* [AAE-6165] Add capability in the Attach File of displaying metadata fields in addition to file name * Minor css styling adjustments * [AAE-6165] Changes done for date datatype and table css * [AAE-6165] Changes done as per the comments * [AAE-6165] Changes done as per comments on PR * Resolved failing lints * Updated CSS for attach file widget * Updated css * Updated UT * Resolved e2e failures * Resolved e2e errors Co-authored-by: amohammedalfresco <abdul.mohammed@alfresco.com>
This commit is contained in:
@@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable:component-selector */
|
||||||
|
|
||||||
|
export interface DisplayableCMProperties {
|
||||||
|
name?: string;
|
||||||
|
prefixedName?: string;
|
||||||
|
title?: string;
|
||||||
|
dataType?: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
/* tslint:disable:component-selector */
|
/* tslint:disable:component-selector */
|
||||||
|
|
||||||
|
import { DisplayableCMProperties } from './displayable-cm-properties.model';
|
||||||
import { FormFieldFileSource } from './form-field-file-source';
|
import { FormFieldFileSource } from './form-field-file-source';
|
||||||
|
|
||||||
export interface FormFieldMetadata {
|
export interface FormFieldMetadata {
|
||||||
@@ -35,4 +36,5 @@ export interface FormFieldMetadata {
|
|||||||
retrieveMetadata?: boolean,
|
retrieveMetadata?: boolean,
|
||||||
remove?: boolean
|
remove?: boolean
|
||||||
};
|
};
|
||||||
|
displayableCMProperties?: DisplayableCMProperties[];
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
"REMOVE_FILE": "Remove",
|
"REMOVE_FILE": "Remove",
|
||||||
"UPLOAD": "UPLOAD",
|
"UPLOAD": "UPLOAD",
|
||||||
"REQUIRED": "*Required",
|
"REQUIRED": "*Required",
|
||||||
|
"FILE_NAME": "File Name",
|
||||||
"NO_FILE_ATTACHED" : "No file attached",
|
"NO_FILE_ATTACHED" : "No file attached",
|
||||||
"VALIDATOR": {
|
"VALIDATOR": {
|
||||||
"INVALID_NUMBER": "Use a different number format",
|
"INVALID_NUMBER": "Use a different number format",
|
||||||
|
@@ -37,7 +37,10 @@ module.exports = function (config) {
|
|||||||
'/assets/adf-core/i18n/en-GB.json': '/base/lib/core/i18n/en.json',
|
'/assets/adf-core/i18n/en-GB.json': '/base/lib/core/i18n/en.json',
|
||||||
'/assets/adf-process-services-cloud/i18n/en.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
'/assets/adf-process-services-cloud/i18n/en.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
||||||
'/assets/adf-process-services-cloud/i18n/en-GB.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
'/assets/adf-process-services-cloud/i18n/en-GB.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
|
||||||
'/app.config.json': '/base/lib/config/app.config.json'
|
'/app.config.json': '/base/lib/config/app.config.json',
|
||||||
|
'/base/lib/process-services-cloud/assets/images/ft_ic_raster_image.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_raster_image.svg',
|
||||||
|
'/base/lib/process-services-cloud/assets/images/ft_ic_miscellaneous.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_miscellaneous.svg',
|
||||||
|
'/base/lib/process-services-cloud/assets/images/ft_ic_pdf.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_pdf.svg',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine-ajax'),
|
require('karma-jasmine-ajax'),
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<div class="adf-attach-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
|
<div class="adf-attach-file-widget-container">
|
||||||
|
<div class="adf-attach-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
|
||||||
[class.adf-readonly]="field.readOnly">
|
[class.adf-readonly]="field.readOnly">
|
||||||
<label class="adf-label" [attr.for]="field.id">{{field.name}}
|
<label class="adf-label" [attr.for]="field.id">{{field.name}}
|
||||||
<span *ngIf="isRequired()">*</span>
|
<span *ngIf="isRequired()">*</span>
|
||||||
@@ -12,53 +13,28 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="adf-attach-widget-readonly-list">
|
<div id="adf-attach-widget-readonly-list" class="adf-attach-widget-readonly-table">
|
||||||
<mat-list *ngIf="hasFile">
|
<adf-cloud-file-properties-table
|
||||||
<mat-list-item
|
[uploadedFiles]="uploadedFiles"
|
||||||
[ngClass]="{'adf-attach-files-row': true, 'adf-attach-selected-file-row': displayMenuOption('retrieveMetadata') && selectedNode && file.id === selectedNode.id}"
|
[hasFile]="hasFile"
|
||||||
*ngFor="let file of uploadedFiles">
|
[selectedNode]="selectedNode"
|
||||||
<mat-icon mat-list-icon class="adf-datatable-selected" *ngIf="selectedNode && file.id === selectedNode.id" (click)="onRowClicked(file)">
|
[field]="field"
|
||||||
check_circle
|
[displayedColumns]="displayedColumns"
|
||||||
</mat-icon>
|
[mimeTypeIcon]="mimeTypeIcon"
|
||||||
<img mat-list-icon class="adf-attach-widget__icon" *ngIf="!selectedNode || file.id !== selectedNode.id" [id]="'file-'+file?.id+'-icon'" (click)="onRowClicked(file)"
|
(rowClick)="onRowClicked($event)"
|
||||||
[src]="file.content ? getIcon(file.content.mimeType) : getIcon(file['mimeType'])" [alt]="mimeTypeIcon"
|
(attachFileClick)="onAttachFileClicked($event)"
|
||||||
role="button" tabindex="0" />
|
(downloadFile)="downloadContent($event)"
|
||||||
<span matLine id="{{'file-'+file?.id}}" role="button" tabindex="0" class="adf-file" (click)="onRowClicked(file)">{{file.name}}</span>
|
(contentModelFileHandler)="contentModelFormFileHandler($event)"
|
||||||
<button id="{{'file-'+file?.id+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu" *ngIf="!!file.content?.mimeType">
|
(removeAttachFile)="onRemoveAttachFile($event)"
|
||||||
<mat-icon>more_vert</mat-icon>
|
></adf-cloud-file-properties-table>
|
||||||
</button>
|
|
||||||
<mat-menu #fileActionMenu="matMenu" xPosition="before">
|
|
||||||
<button *ngIf="displayMenuOption('show') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-show-file'}}"
|
|
||||||
mat-menu-item (click)="onAttachFileClicked(file)">
|
|
||||||
<mat-icon>visibility</mat-icon>
|
|
||||||
<span>{{ 'FORM.FIELD.VIEW_FILE' | translate }}</span>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="displayMenuOption('download') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-download-file'}}"
|
|
||||||
mat-menu-item (click)="downloadContent(file)">
|
|
||||||
<mat-icon>file_download</mat-icon>
|
|
||||||
<span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="displayMenuOption('retrieveMetadata') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-retrieve-file-metadata'}}"
|
|
||||||
mat-menu-item (click)="contentModelFormFileHandler(file)">
|
|
||||||
<mat-icon class="mat-24">low_priority</mat-icon>
|
|
||||||
<span>{{ 'ADF_CLOUD_FORM_COMPONENT.RETRIEVE_METADATA' | translate }}</span>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="!field.readOnly && displayMenuOption('remove')" id="{{'file-'+file?.id+'-remove-file'}}"
|
|
||||||
mat-menu-item [id]="'file-'+file?.id+'-remove'"
|
|
||||||
(click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);">
|
|
||||||
<mat-icon class="mat-24">highlight_off</mat-icon>
|
|
||||||
<span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
</mat-list-item>
|
|
||||||
</mat-list>
|
|
||||||
<div *ngIf="!hasFile && field.readOnly" id="{{'adf-attach-empty-list-'+field.id}}">
|
<div *ngIf="!hasFile && field.readOnly" id="{{'adf-attach-empty-list-'+field.id}}">
|
||||||
{{ 'FORM.FIELD.NO_FILE_ATTACHED' | translate }}
|
{{ 'FORM.FIELD.NO_FILE_ATTACHED' | translate }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<error-widget [error]="field.validationSummary"></error-widget>
|
<error-widget [error]="field.validationSummary"></error-widget>
|
||||||
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
|
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
|
||||||
|
</div>
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
.adf {
|
.adf {
|
||||||
&-attach-widget-container {
|
&-attach-widget-container {
|
||||||
margin-bottom: 15px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@@ -19,6 +18,26 @@
|
|||||||
&-attach-widget__menu-upload {
|
&-attach-widget__menu-upload {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
.mat-raised-button {
|
||||||
|
line-height: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button span {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
line-height: 12px;
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-attach-widget__input-type {
|
&-attach-widget__input-type {
|
||||||
@@ -47,10 +66,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-attach-widget {
|
&-attach-widget {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
padding: 0.4375em 0;
|
padding: 0.4375em;
|
||||||
border-top: 0.84375em solid transparent;
|
border-bottom: none;
|
||||||
|
background: var(--theme-colors-mat-grey);
|
||||||
|
min-height: 27px;
|
||||||
|
|
||||||
|
.adf-label {
|
||||||
|
width: 32px;
|
||||||
|
font-size: var(--theme-caption-font-size);
|
||||||
|
line-height: var(--theme-headline-line-height);
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-attach-widget__icon {
|
&-attach-widget__icon {
|
||||||
@@ -83,4 +114,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-attach-file-widget-container {
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,8 @@ import {
|
|||||||
FormService,
|
FormService,
|
||||||
DownloadService,
|
DownloadService,
|
||||||
AppConfigService,
|
AppConfigService,
|
||||||
UploadWidgetContentLinkModel
|
UploadWidgetContentLinkModel,
|
||||||
|
LocalizedDatePipe
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import {
|
import {
|
||||||
allSourceParams,
|
allSourceParams,
|
||||||
@@ -54,7 +55,9 @@ import {
|
|||||||
processVariables,
|
processVariables,
|
||||||
mockAllFileSourceWithRenamedFolderVariablePathType,
|
mockAllFileSourceWithRenamedFolderVariablePathType,
|
||||||
allSourceParamsWithRelativePath,
|
allSourceParamsWithRelativePath,
|
||||||
fakeLocalPhysicalRecordResponse
|
fakeLocalPhysicalRecordResponse,
|
||||||
|
displayableCMParams,
|
||||||
|
fakeLocalPngHavingCMProperties
|
||||||
} from '../../../mocks/attach-file-cloud-widget.mock';
|
} from '../../../mocks/attach-file-cloud-widget.mock';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
@@ -79,6 +82,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
let updateFormSpy: jasmine.Spy;
|
let updateFormSpy: jasmine.Spy;
|
||||||
let contentClickedSpy: jasmine.Spy;
|
let contentClickedSpy: jasmine.Spy;
|
||||||
let openUploadFileDialogSpy: jasmine.Spy;
|
let openUploadFileDialogSpy: jasmine.Spy;
|
||||||
|
let localizedDataPipe: LocalizedDatePipe;
|
||||||
|
|
||||||
function createUploadWidgetField(form: FormModel, fieldId: string, value?: any, params?: any, multiple?: boolean, name?: string, readOnly?: boolean) {
|
function createUploadWidgetField(form: FormModel, fieldId: string, value?: any, params?: any, multiple?: boolean, name?: string, readOnly?: boolean) {
|
||||||
widget.field = new FormFieldModel(form, {
|
widget.field = new FormFieldModel(form, {
|
||||||
@@ -122,6 +126,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
formService = TestBed.inject(FormService);
|
formService = TestBed.inject(FormService);
|
||||||
contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService);
|
contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService);
|
||||||
openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeMinimalNode]));
|
openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeMinimalNode]));
|
||||||
|
localizedDataPipe = new LocalizedDatePipe();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -197,6 +202,45 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
expect(contentNodeSelectorPanelService.customModels).toEqual([]);
|
expect(contentNodeSelectorPanelService.customModels).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Upload widget with displayable ContentModel properties', () => {
|
||||||
|
|
||||||
|
it('should display CM Properties if the file contains value', async() => {
|
||||||
|
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], displayableCMParams);
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
||||||
|
expect(element.querySelector('#fileProperty-1155-name').textContent).toBe('Alex');
|
||||||
|
expect(element.querySelector('#fileProperty-1155-age').textContent).toBe('34');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display defaultValue if the file does not contain value for respective displayableCMProperties', async() => {
|
||||||
|
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngResponse], displayableCMParams);
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(element.querySelector('#fileProperty-1155-name').textContent).toBe('Bob');
|
||||||
|
expect(element.querySelector('#fileProperty-1155-age').textContent).toBe('--');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display CM Properties in table if the field does not contain displayableCMProperties', async() => {
|
||||||
|
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], allSourceParams);
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(element.querySelector('#fileProperty-1155-name')).toBeNull();
|
||||||
|
expect(element.querySelector('#fileProperty-1155-age')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display date property in converted form based on dataType', async() => {
|
||||||
|
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], displayableCMParams);
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(element.querySelector('#fileProperty-1155-dob').textContent).toBe(localizedDataPipe.transform(new Date()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('destinationFolderPath', () => {
|
describe('destinationFolderPath', () => {
|
||||||
|
|
||||||
it('should be able to fetch nodeId if destinationFolderPath is defined', async () => {
|
it('should be able to fetch nodeId if destinationFolderPath is defined', async () => {
|
||||||
|
@@ -74,6 +74,7 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
|
|||||||
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
|
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
|
||||||
return this._nodesApi;
|
return this._nodesApi;
|
||||||
}
|
}
|
||||||
|
displayedColumns = ['icon', 'fileName', 'action'];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
formService: FormService,
|
formService: FormService,
|
||||||
@@ -95,6 +96,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
|
|||||||
const files = this.field.value || this.field.form.values[this.field.id];
|
const files = this.field.value || this.field.form.values[this.field.id];
|
||||||
this.contentModelFormFileHandler(files[0]);
|
this.contentModelFormFileHandler(files[0]);
|
||||||
}
|
}
|
||||||
|
this.field.params.displayableCMProperties = this.field.params.displayableCMProperties ?? [];
|
||||||
|
this.displayedColumns.splice(2, 0, ...this.field.params.displayableCMProperties?.map(property => property?.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
isPathStaticType(): boolean {
|
isPathStaticType(): boolean {
|
||||||
@@ -221,10 +224,6 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
|
|||||||
return this.isAlfrescoAndLocal() ? 'file_upload' : 'attach_file';
|
return this.isAlfrescoAndLocal() ? 'file_upload' : 'attach_file';
|
||||||
}
|
}
|
||||||
|
|
||||||
displayMenuOption(option: string): boolean {
|
|
||||||
return this.field?.params?.menuOptions ? this.field.params.menuOptions[option] : option !== AttachFileCloudWidgetComponent.RETRIEVE_METADATA_OPTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowClicked(file?: Node) {
|
onRowClicked(file?: Node) {
|
||||||
if (this.selectedNode?.id === file?.id) {
|
if (this.selectedNode?.id === file?.id) {
|
||||||
this.selectedNode = null;
|
this.selectedNode = null;
|
||||||
|
@@ -0,0 +1,72 @@
|
|||||||
|
<div class="adf-file-properties-table">
|
||||||
|
<table mat-table [dataSource]="uploadedFiles" class="mat-elevation-z0" *ngIf="hasFile">
|
||||||
|
<ng-container matColumnDef="icon">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<mat-icon mat-list-icon class="adf-datatable-selected"
|
||||||
|
*ngIf="selectedNode && element.id === selectedNode.id" (click)="onRowClicked(element)">
|
||||||
|
check_circle
|
||||||
|
</mat-icon>
|
||||||
|
<img mat-list-icon class="adf-attach-widget__icon"
|
||||||
|
*ngIf="!selectedNode || element.id !== selectedNode.id" [id]="'file-'+element?.id+'-icon'"
|
||||||
|
(click)="onRowClicked(element)"
|
||||||
|
[src]="element.content ? getIcon(element.content.mimeType) : getIcon(element['mimeType'])"
|
||||||
|
[alt]="mimeTypeIcon" role="button" tabindex="0" />
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="fileName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'FORM.FIELD.FILE_NAME' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<span matLine id="{{'file-'+element?.id}}" role="button" tabindex="0" class="adf-file"
|
||||||
|
(click)="onRowClicked(element)">{{element.name}}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngFor="let columnName of field?.params?.displayableCMProperties" [matColumnDef]="columnName.name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ columnName.title ? columnName.title : columnName.name | titlecase
|
||||||
|
}}</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<span matLine id="{{'fileProperty-'+row?.id+'-'+columnName?.name}}" role="button" tabindex="0"
|
||||||
|
(click)="onRowClicked(row)">{{ getColumnValue(row, columnName) }}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="action">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<button id="{{'file-'+element?.id+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu"
|
||||||
|
*ngIf="!!element.content?.mimeType">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #fileActionMenu="matMenu" xPosition="before">
|
||||||
|
<button *ngIf="displayMenuOption('show') && !!element.content?.mimeType"
|
||||||
|
id="{{'file-'+element?.id+'-show-file'}}" mat-menu-item (click)="onAttachFileClicked(element)">
|
||||||
|
<mat-icon>visibility</mat-icon>
|
||||||
|
<span>{{ 'FORM.FIELD.VIEW_FILE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="displayMenuOption('download') && !!element.content?.mimeType"
|
||||||
|
id="{{'file-'+element?.id+'-download-file'}}" mat-menu-item (click)="downloadContent(element)">
|
||||||
|
<mat-icon>file_download</mat-icon>
|
||||||
|
<span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="displayMenuOption('retrieveMetadata') && !!element.content?.mimeType"
|
||||||
|
id="{{'file-'+element?.id+'-retrieve-file-metadata'}}" mat-menu-item
|
||||||
|
(click)="contentModelFormFileHandler(element)">
|
||||||
|
<mat-icon class="mat-24">low_priority</mat-icon>
|
||||||
|
<span>{{ 'ADF_CLOUD_FORM_COMPONENT.RETRIEVE_METADATA' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="!field.readOnly && displayMenuOption('remove')"
|
||||||
|
id="{{'file-'+element?.id+'-remove-file'}}" mat-menu-item [id]="'file-'+element?.id+'-remove'"
|
||||||
|
(click)="onRemoveAttachFile(element);" (keyup.enter)="onRemoveAttachFile(element);">
|
||||||
|
<mat-icon class="mat-24">highlight_off</mat-icon>
|
||||||
|
<span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
@@ -0,0 +1,34 @@
|
|||||||
|
.adf-file-properties-table {
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--theme-border-color);
|
||||||
|
box-shadow: none;
|
||||||
|
.adf-datatable-selected {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.mat-header-row {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
max-width: 50px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.mat-cell:first-of-type,
|
||||||
|
td.mat-footer-cell:first-of-type,
|
||||||
|
td.mat-cell:last-of-type,
|
||||||
|
td.mat-footer-cell:last-of-type {
|
||||||
|
width: 18% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,114 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable:component-selector */
|
||||||
|
|
||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { LocalizedDatePipe, ThumbnailService } from '@alfresco/adf-core';
|
||||||
|
import { Node } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-cloud-file-properties-table',
|
||||||
|
templateUrl: './file-properties-table-cloud.component.html',
|
||||||
|
styleUrls: ['./file-properties-table-cloud.component.scss']
|
||||||
|
})
|
||||||
|
export class FilePropertiesTableCloudComponent {
|
||||||
|
|
||||||
|
static RETRIEVE_METADATA_OPTION = 'retrieveMetadata';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
uploadedFiles;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
hasFile: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
selectedNode: Node;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
field;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayedColumns;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
mimeTypeIcon;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
rowClick: EventEmitter<Node> = new EventEmitter<Node>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
attachFileClick: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
downloadFile: EventEmitter<Node> = new EventEmitter<Node>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
contentModelFileHandler: EventEmitter<any> = new EventEmitter<Node>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
removeAttachFile: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor(private localizedDatePipe: LocalizedDatePipe, private thumbnailService: ThumbnailService) {}
|
||||||
|
|
||||||
|
onRowClicked(file?: Node) {
|
||||||
|
this.rowClick.emit(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAttachFileClicked(nodeSelector: any) {
|
||||||
|
this.attachFileClick.emit(nodeSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadContent(file: Node) {
|
||||||
|
this.downloadFile.emit(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentModelFormFileHandler(file?: any) {
|
||||||
|
this.contentModelFileHandler.emit(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveAttachFile(file: any) {
|
||||||
|
this.removeAttachFile.emit(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIcon(mimeType: string): string {
|
||||||
|
return this.thumbnailService.getMimeTypeIcon(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumnValue(file, displayableCMProperty): string {
|
||||||
|
if (!file.properties[displayableCMProperty.prefixedName]) {
|
||||||
|
const fieldProperty = this.field.params.displayableCMProperties?.find(property => property.name === displayableCMProperty.name);
|
||||||
|
return fieldProperty.defaultValue ? this.checkDateTypeAndTransform(displayableCMProperty.dataType, fieldProperty.defaultValue) : '--' ;
|
||||||
|
}
|
||||||
|
return file.properties[displayableCMProperty.prefixedName] ?
|
||||||
|
this.checkDateTypeAndTransform(displayableCMProperty.dataType, file.properties[displayableCMProperty.prefixedName]) :
|
||||||
|
'--' ;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDateTypeAndTransform(dataType, value): string {
|
||||||
|
if (dataType === 'd:date') {
|
||||||
|
return this.localizedDatePipe.transform(value);
|
||||||
|
} else if (dataType === 'd:datetime') {
|
||||||
|
return this.localizedDatePipe.transform(value, 'medium');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayMenuOption(option: string): boolean {
|
||||||
|
return this.field?.params?.menuOptions ? this.field.params.menuOptions[option] : option !== FilePropertiesTableCloudComponent.RETRIEVE_METADATA_OPTION;
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,7 @@ import { GroupCloudModule } from '../group/group-cloud.module';
|
|||||||
import { PropertiesViewerWidgetComponent } from './components/widgets/properties-viewer/properties-viewer.widget';
|
import { PropertiesViewerWidgetComponent } from './components/widgets/properties-viewer/properties-viewer.widget';
|
||||||
import { PropertiesViewerWrapperComponent } from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
|
import { PropertiesViewerWrapperComponent } from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
|
||||||
import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
|
||||||
|
import { FilePropertiesTableCloudComponent } from './components/widgets/attach-file/file-properties-table-cloud.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -64,7 +65,8 @@ import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-but
|
|||||||
PeopleCloudWidgetComponent,
|
PeopleCloudWidgetComponent,
|
||||||
GroupCloudWidgetComponent,
|
GroupCloudWidgetComponent,
|
||||||
PropertiesViewerWrapperComponent,
|
PropertiesViewerWrapperComponent,
|
||||||
PropertiesViewerWidgetComponent
|
PropertiesViewerWidgetComponent,
|
||||||
|
FilePropertiesTableCloudComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
FormCloudComponent,
|
FormCloudComponent,
|
||||||
|
@@ -69,6 +69,34 @@ export const fakeLocalPhysicalRecordResponse = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fakeLocalPngHavingCMProperties = {
|
||||||
|
id: 1155,
|
||||||
|
nodeId: 1155,
|
||||||
|
name: 'a_png_file.png',
|
||||||
|
created: '2017-07-25T17:17:37.099Z',
|
||||||
|
createdBy: {
|
||||||
|
id: 1001,
|
||||||
|
firstName: 'Admin',
|
||||||
|
lastName: 'admin',
|
||||||
|
email: 'admin'
|
||||||
|
},
|
||||||
|
relatedContent: false,
|
||||||
|
contentAvailable: true,
|
||||||
|
link: false,
|
||||||
|
mimeType: null,
|
||||||
|
simpleType: 'image',
|
||||||
|
previewStatus: 'queued',
|
||||||
|
thumbnailStatus: 'queued',
|
||||||
|
properties: {
|
||||||
|
'pfx:property_one': 'testValue',
|
||||||
|
'pfx:property_two': true,
|
||||||
|
'a:name': 'Alex',
|
||||||
|
'a:age': '34',
|
||||||
|
'a:dob': new Date(),
|
||||||
|
'a:doj': new Date()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const onlyLocalParams = {
|
export const onlyLocalParams = {
|
||||||
fileSource: {
|
fileSource: {
|
||||||
serviceId: 'local-file'
|
serviceId: 'local-file'
|
||||||
@@ -117,6 +145,47 @@ export const allSourceParams = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const displayableCMParams = {
|
||||||
|
fileSource: {
|
||||||
|
name: 'all file sources',
|
||||||
|
serviceId: FileSourceTypes.ALL_FILE_SOURCES_SERVICE_ID,
|
||||||
|
destinationFolderPath: {
|
||||||
|
value: '-root-/myfiles',
|
||||||
|
type: DestinationFolderPathType.STATIC_TYPE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
displayableCMProperties: [
|
||||||
|
{
|
||||||
|
'name': 'name',
|
||||||
|
'prefixedName': 'a:name',
|
||||||
|
'title': '',
|
||||||
|
'dataType': 'd:text',
|
||||||
|
'defaultValue': 'Bob'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'age',
|
||||||
|
'prefixedName': 'a:age',
|
||||||
|
'title': 'Age',
|
||||||
|
'dataType': 'd:text',
|
||||||
|
'defaultValue': ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'dob',
|
||||||
|
'prefixedName': 'a:dob',
|
||||||
|
'title': 'Date of Birth',
|
||||||
|
'dataType': 'd:date',
|
||||||
|
'defaultValue': ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'doj',
|
||||||
|
'prefixedName': 'a:doj',
|
||||||
|
'title': 'Date of Joining',
|
||||||
|
'dataType': 'd:datetime',
|
||||||
|
'defaultValue': ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
export const allSourceWithStringTypeEmptyValue = {
|
export const allSourceWithStringTypeEmptyValue = {
|
||||||
fileSource: {
|
fileSource: {
|
||||||
name: 'all file sources',
|
name: 'all file sources',
|
||||||
|
@@ -40,6 +40,7 @@ import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model'
|
|||||||
import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
|
import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
|
||||||
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
|
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
|
||||||
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
||||||
|
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||||
|
|
||||||
describe('EditTaskFilterCloudComponent', () => {
|
describe('EditTaskFilterCloudComponent', () => {
|
||||||
let component: EditTaskFilterCloudComponent;
|
let component: EditTaskFilterCloudComponent;
|
||||||
@@ -67,7 +68,8 @@ describe('EditTaskFilterCloudComponent', () => {
|
|||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
ProcessServiceCloudTestingModule,
|
ProcessServiceCloudTestingModule,
|
||||||
TaskFiltersCloudModule,
|
TaskFiltersCloudModule,
|
||||||
PeopleCloudModule
|
PeopleCloudModule,
|
||||||
|
MatIconTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
MatDialog,
|
MatDialog,
|
||||||
|
@@ -29,8 +29,8 @@ export class AttachFileWidgetCloudPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFileAttachedLocatorByContainingText = async(text: string): Promise<ElementFinder> => {
|
getFileAttachedLocatorByContainingText = async(text: string): Promise<ElementFinder> => {
|
||||||
const filesListLocator = 'div[id="adf-attach-widget-readonly-list"]';
|
const filesListLocator = 'div[class="adf-file-properties-table"]';
|
||||||
return this.widget.$(filesListLocator).element(by.cssContainingText('mat-list-item span ', text));
|
return this.widget.$(filesListLocator).element(by.cssContainingText('table tbody tr td span ', text));
|
||||||
}
|
}
|
||||||
|
|
||||||
assignWidget(fieldId: string): void {
|
assignWidget(fieldId: string): void {
|
||||||
|
Reference in New Issue
Block a user