diff --git a/demo-shell/src/app.config.json b/demo-shell/src/app.config.json
index dca06a5c49..f055130a77 100644
--- a/demo-shell/src/app.config.json
+++ b/demo-shell/src/app.config.json
@@ -1476,5 +1476,9 @@
"ai:labels",
"ai:textLines"
]
+ },
+ "viewer": {
+ "enableFileAutoDownload": true,
+ "fileAutoDownloadSizeThresholdInMB": 15
}
}
diff --git a/demo-shell/src/app/components/files/files.component.html b/demo-shell/src/app/components/files/files.component.html
index 34c8d7a02c..a1de8a994f 100644
--- a/demo-shell/src/app/components/files/files.component.html
+++ b/demo-shell/src/app/components/files/files.component.html
@@ -653,6 +653,18 @@
+
+
+ Enable FileAutoDownload
+
+
+
+
Upload
diff --git a/demo-shell/src/app/components/files/files.component.ts b/demo-shell/src/app/components/files/files.component.ts
index 8be126f59b..6980dcfa62 100644
--- a/demo-shell/src/app/components/files/files.component.ts
+++ b/demo-shell/src/app/components/files/files.component.ts
@@ -251,6 +251,9 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
selectedNodes = [];
+ enableFileAutoDownload: boolean = this.appConfig.get('viewer.enableFileAutoDownload', true);
+ fileAutoDownloadSizeThresholdInMB: number = this.appConfig.get('viewer.fileAutoDownloadSizeThresholdInMB', 15);
+
constructor(private notificationService: NotificationService,
private uploadService: UploadService,
private contentService: ContentService,
@@ -777,4 +780,14 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
this.selectedNodes = [];
}
+ onEnableFileAutoDownloadChange() {
+ const previewConfig = this.appConfig?.config['viewer'];
+ previewConfig['enableFileAutoDownload'] = this.enableFileAutoDownload;
+ }
+
+ onFileAutoDownloadSizeThresholdChange() {
+ const previewConfig = this.appConfig?.config['viewer'];
+ previewConfig['fileAutoDownloadSizeThresholdInMB'] = this.fileAutoDownloadSizeThresholdInMB;
+ }
+
}
diff --git a/docs/content-services/components/document-list.component.md b/docs/content-services/components/document-list.component.md
index c634c97481..baabbfb7c7 100644
--- a/docs/content-services/components/document-list.component.md
+++ b/docs/content-services/components/document-list.component.md
@@ -790,6 +790,27 @@ This will give the following output:

+### File Auto downloading
+
+In case of files exceeding a predefined file size, the document list component can be configured to automatically download those file when trying to preview them.
+This can help in reducing server load, and ensuring quick access to such files. After turning this feature on, whenever the user tries to preview a file with a large
+file size, the Document List component will first preview a dialog, asking for confirmation from the user on whether they want to download the file, or cancel the preview altogether.
+
+In order to configure the Document List to automatically download the files, the following environment variables would need to be set up in app.config.json -
+
+```
+"viewer": {
+ "enableFileAutoDownload": true,
+ "fileAutoDownloadSizeThresholdInMB": 15
+}
+```
+
+Here, `"enableFileAutoDownload": true,` would enable the file auto download feature on the Document List component. Setting this flag to false disables this feature, and always
+triggers a file preview when trying to view a file, regardless of its size.
+
+The second configuration here, `"fileAutoDownloadSizeThresholdInMB": 15` specifies the file size threshold (in MB), after which the Document List component will start downloading the file.
+In the example provided above, any file greater than 15MB in size would trigger the auto download functionality. Files lower than 15MB in size would continue to preview normally.
+
## See also
- [Datatable component](../../core/components/datatable.component.md)
diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
index 124eb7e04b..a958a45ad2 100644
--- a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
+++ b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts
@@ -27,7 +27,7 @@ import {
DataTableModule,
ObjectDataTableAdapter,
ShowHeaderMode,
- ThumbnailService
+ ThumbnailService, AppConfigService
} from '@alfresco/adf-core';
import { ContentService } from '../../common/services/content.service';
@@ -62,6 +62,12 @@ import { ShareDataRow } from '../data/share-data-row.model';
import { DocumentLoaderNode } from '../models/document-folder.model';
import { matIconRegistryMock } from '../../testing/mat-icon-registry-mock';
import { domSanitizerMock } from '../../testing/dom-sanitizer-mock';
+import { MatDialog } from '@angular/material/dialog';
+import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component';
+
+const mockDialog = {
+ open: jasmine.createSpy('open')
+};
describe('DocumentList', () => {
@@ -71,6 +77,7 @@ describe('DocumentList', () => {
let customResourcesService: CustomResourcesService;
let thumbnailService: ThumbnailService;
let contentService: ContentService;
+ let appConfigService: AppConfigService;
let fixture: ComponentFixture;
let element: HTMLElement;
let eventMock: any;
@@ -84,7 +91,10 @@ describe('DocumentList', () => {
TranslateModule.forRoot(),
ContentTestingModule
],
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ providers: [
+ { provide: MatDialog, useValue: mockDialog }
+ ]
});
beforeEach(() => {
@@ -102,6 +112,7 @@ describe('DocumentList', () => {
customResourcesService = TestBed.inject(CustomResourcesService);
thumbnailService = TestBed.inject(ThumbnailService);
contentService = TestBed.inject(ContentService);
+ appConfigService = TestBed.inject(AppConfigService);
spyFolder = spyOn(documentListService, 'getFolder').and.returnValue(of({ list: {} }));
spyFolderNode = spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry({ entry: {} })));
@@ -1567,6 +1578,30 @@ describe('DocumentList', () => {
}), undefined);
});
+ it('should display fileAutoDownload dialog if node size exceeds appConfig.viewer.fileAutoDownloadSizeThresholdInMB', async () => {
+ appConfigService.config = {
+ ...appConfigService.config,
+ 'viewer': {
+ 'enableFileAutoDownload': true,
+ 'fileAutoDownloadSizeThresholdInMB': 10
+ }
+ };
+ documentList.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
+ const node = { entry: {
+ ...mockNode1,
+ content: {
+ ...mockNode1.content,
+ sizeInBytes: 104857600
+ }
+ } };
+ documentList.onNodeClick(node);
+
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(mockDialog.open).toHaveBeenCalledWith(FileAutoDownloadComponent, { disableClose: true, data: node });
+ });
+
describe('Preselect nodes', () => {
beforeEach(() => {
diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.ts b/lib/content-services/src/lib/document-list/components/document-list.component.ts
index 4be8ddbb66..c4947d30dd 100644
--- a/lib/content-services/src/lib/document-list/components/document-list.component.ts
+++ b/lib/content-services/src/lib/document-list/components/document-list.component.ts
@@ -66,6 +66,10 @@ import { LockService } from '../services/lock.service';
import { DocumentLoaderNode } from '../models/document-folder.model';
import { takeUntil } from 'rxjs/operators';
import { ADF_DOCUMENT_PARENT_COMPONENT } from './document-list.token';
+import { MatDialog } from '@angular/material/dialog';
+import { FileAutoDownloadComponent } from './file-auto-download/file-auto-download.component';
+
+const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
@Component({
selector: 'adf-document-list',
@@ -367,7 +371,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
private alfrescoApiService: AlfrescoApiService,
private nodeService: NodesApiService,
private dataTableService: DataTableService,
- private lockService: LockService) {
+ private lockService: LockService,
+ private dialog: MatDialog) {
this.nodeService.nodeUpdated.subscribe((node) => {
this.dataTableService.rowUpdate.next({id: node.id, obj: {entry: node}});
@@ -758,7 +763,16 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
onPreviewFile(node: NodeEntry) {
if (node) {
- this.preview.emit(new NodeEntityEvent(node));
+ const sizeInMB = node.entry?.content?.sizeInBytes / BYTES_TO_MB_CONVERSION_VALUE;
+
+ const fileAutoDownloadFlag: boolean = this.appConfig.get('viewer.enableFileAutoDownload', true);
+ const sizeThreshold: number = this.appConfig.get('viewer.fileAutoDownloadSizeThresholdInMB', 15);
+
+ if (fileAutoDownloadFlag && sizeInMB && sizeInMB > sizeThreshold) {
+ this.dialog.open(FileAutoDownloadComponent, { disableClose: true, data: node });
+ } else {
+ this.preview.emit(new NodeEntityEvent(node));
+ }
}
}
diff --git a/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.html b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.html
new file mode 100644
index 0000000000..ad1aaf6f21
--- /dev/null
+++ b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.html
@@ -0,0 +1,24 @@
+
+
{{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.HEADER' | translate }}
+
+
+ {{ 'ADF-DOCUMENT-LIST.FILE_AUTO_DOWNLOAD_DIALOG.LABEL' | translate }}
+
+
+
+
+
diff --git a/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.spec.ts b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.spec.ts
new file mode 100644
index 0000000000..d7cdbc5e38
--- /dev/null
+++ b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.spec.ts
@@ -0,0 +1,76 @@
+/*!
+ * @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 { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileAutoDownloadComponent } from './file-auto-download.component';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { By } from '@angular/platform-browser';
+import { CoreTestingModule } from '@alfresco/adf-core';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+
+const mockDialog = {
+ close: jasmine.createSpy('close')
+};
+describe('FileAutoDownloadComponent', () => {
+ let matDialogRef: MatDialogRef;
+ let fixture: ComponentFixture;
+
+ const getButton = (buttonId: string) => {
+ return fixture.debugElement.query(By.css(buttonId)).nativeElement;
+ };
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [FileAutoDownloadComponent],
+ imports: [
+ TranslateModule.forRoot(),
+ CoreTestingModule
+ ],
+ schemas: [NO_ERRORS_SCHEMA],
+ providers: [
+ { provide: MatDialogRef, useValue: mockDialog },
+ { provide: MAT_DIALOG_DATA, useValue: null }
+ ]
+ });
+
+ fixture = TestBed.createComponent(FileAutoDownloadComponent);
+ matDialogRef = TestBed.inject(MatDialogRef);
+ fixture.detectChanges();
+ });
+
+ it('should emit FileAutoDownloadActionsEnum.CANCEL and close dialog when clicking on the cancel button', async () => {
+ const waitButton = getButton('#cancelButton');
+ waitButton.dispatchEvent(new Event('click'));
+
+ await fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(matDialogRef.close).toHaveBeenCalled();
+ });
+
+ it('should emit FileAutoDownloadActionsEnum.DOWNLOAD and close dialog when clicking on the wait button', async () => {
+ const waitButton = getButton('#downloadButton');
+ waitButton.dispatchEvent(new Event('click'));
+
+ await fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(matDialogRef.close).toHaveBeenCalled();
+ });
+});
diff --git a/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts
new file mode 100644
index 0000000000..09ad0ae64f
--- /dev/null
+++ b/lib/content-services/src/lib/document-list/components/file-auto-download/file-auto-download.component.ts
@@ -0,0 +1,28 @@
+/*!
+ * @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 { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { NodeEntry } from '@alfresco/js-api';
+
+@Component({
+ selector: 'adf-file-auto-download',
+ templateUrl: './file-auto-download.component.html'
+})
+export class FileAutoDownloadComponent {
+ constructor(@Inject(MAT_DIALOG_DATA) public node: NodeEntry) {}
+}
diff --git a/lib/content-services/src/lib/document-list/document-list.module.ts b/lib/content-services/src/lib/document-list/document-list.module.ts
index c0d9fbb17a..4db8c771dd 100644
--- a/lib/content-services/src/lib/document-list/document-list.module.ts
+++ b/lib/content-services/src/lib/document-list/document-list.module.ts
@@ -35,6 +35,8 @@ import { LibraryRoleColumnComponent } from './components/library-role-column/lib
import { LibraryNameColumnComponent } from './components/library-name-column/library-name-column.component';
import { NameColumnComponent } from './components/name-column/name-column.component';
import { FilterHeaderComponent } from './components/filter-header/filter-header.component';
+import { FileAutoDownloadComponent } from './components/file-auto-download/file-auto-download.component';
+import { ContentDirectiveModule } from '../directives/content-directive.module';
@NgModule({
imports: [
@@ -45,7 +47,8 @@ import { FilterHeaderComponent } from './components/filter-header/filter-header.
MaterialModule,
UploadModule,
EditJsonDialogModule,
- SearchModule
+ SearchModule,
+ ContentDirectiveModule
],
declarations: [
DocumentListComponent,
@@ -56,7 +59,8 @@ import { FilterHeaderComponent } from './components/filter-header/filter-header.
NameColumnComponent,
ContentActionComponent,
ContentActionListComponent,
- FilterHeaderComponent
+ FilterHeaderComponent,
+ FileAutoDownloadComponent
],
exports: [
DocumentListComponent,
diff --git a/lib/content-services/src/lib/document-list/public-api.ts b/lib/content-services/src/lib/document-list/public-api.ts
index d516b99a3a..6b9b77313a 100644
--- a/lib/content-services/src/lib/document-list/public-api.ts
+++ b/lib/content-services/src/lib/document-list/public-api.ts
@@ -25,6 +25,7 @@ export * from './components/library-status-column/library-status-column.componen
export * from './components/name-column/name-column.component';
export * from './components/filter-header/filter-header.component';
export * from './components/trashcan-name-column/trashcan-name-column.component';
+export * from './components/file-auto-download/file-auto-download.component';
// data
export * from './data/share-datatable-adapter';
diff --git a/lib/content-services/src/lib/i18n/en.json b/lib/content-services/src/lib/i18n/en.json
index e94428de0b..4e297df3cb 100644
--- a/lib/content-services/src/lib/i18n/en.json
+++ b/lib/content-services/src/lib/i18n/en.json
@@ -73,7 +73,15 @@
"REMOVE": "Remove",
"DOWNLOAD": "Download"
},
- "LOADER_LABEL": "Document list loader"
+ "LOADER_LABEL": "Document list loader",
+ "FILE_AUTO_DOWNLOAD_DIALOG": {
+ "HEADER": "Preview loading unresponsive",
+ "LABEL": "Download to view this document, or cancel to continue without preview.",
+ "ACTIONS": {
+ "CANCEL": "Cancel",
+ "DOWNLOAD": "Download"
+ }
+ }
},
"ALFRESCO_DOCUMENT_LIST": {
"BUTTON": {