From fde08bb5738cfda59092cfee1ec7ec0c0f040065 Mon Sep 17 00:00:00 2001 From: Maurizio Vitale Date: Mon, 27 Mar 2017 14:33:07 +0100 Subject: [PATCH] Integration beetwen Viewer and Activiti Content (#1765) * Provide a FormService event when you click on the uploaded content * Add a formContentClick event to the Form component * Provide a way to pass Blob to viewer * Provide a way to use the viewer using a Blob - Fix the pdf viewer - Fix the image viewer - Fix the media viewer * Update the viewer documentation * Use the ContentService provided by the core * Fix and improve unit test * Add unit test blob viewer --- .../activiti/activiti-demo.component.html | 11 ++- .../activiti/activiti-demo.component.ts | 15 ++++ .../components/activiti-content.component.ts | 11 ++- .../src/components/activiti-form.component.ts | 8 ++ .../widgets/core/content-link.model.ts | 1 + .../src/services/form.service.ts | 2 + .../activiti-task-details.component.html | 1 + .../activiti-task-details.component.ts | 9 +- ng2-components/ng2-alfresco-viewer/README.md | 4 +- .../src/components/imgViewer.component.html | 3 +- .../components/imgViewer.component.spec.ts | 32 +++++-- .../src/components/imgViewer.component.ts | 21 +++-- .../components/mediaPlayer.component.spec.ts | 33 +++++-- .../src/components/mediaPlayer.component.ts | 23 +++-- .../components/pdfViewer.component.spec.ts | 88 +++++++++++++++++-- .../src/components/pdfViewer.component.ts | 65 ++++++++------ .../src/components/viewer.component.html | 6 +- .../src/components/viewer.component.ts | 17 +++- 18 files changed, 283 insertions(+), 67 deletions(-) diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html index 74a40a6d7b..5fb15efc40 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.html +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.html @@ -37,7 +37,7 @@ [sort]="taskFilter.filter.sort" [data]="dataTasks" [landingTaskId]="currentTaskId" - (rowClick)="onTaskRowClick($event)" + (rowClick)="onTaskRowClick($event)" (onSuccess)="onSuccessTaskList($event)" (row-click)="onRowClick($event)" (row-dblclick)="onRowDblClick($event)" @@ -55,6 +55,7 @@ @@ -142,3 +143,11 @@ + +
+ + +
diff --git a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts index f883fe09c5..81c6f54b41 100644 --- a/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts +++ b/demo-shell-ng2/app/components/activiti/activiti-demo.component.ts @@ -78,6 +78,11 @@ export class ActivitiDemoComponent implements AfterViewInit { @Input() appId: number = null; + fileShowed: boolean = false; + + content: Blob; + contentName: string; + layoutType: string; currentTaskId: string; currentProcessInstanceId: string; @@ -92,6 +97,8 @@ export class ActivitiDemoComponent implements AfterViewInit { processFilter: FilterProcessRepresentationModel; sub: Subscription; + blobFile: any; + flag: boolean = true; dataTasks: ObjectDataTableAdapter; dataProcesses: ObjectDataTableAdapter; @@ -113,6 +120,7 @@ export class ActivitiDemoComponent implements AfterViewInit { ); this.dataProcesses.setSorting(new DataSorting('started', 'desc')); + // Uncomment this line to replace all 'text' field editors with custom component // formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true); @@ -126,6 +134,7 @@ export class ActivitiDemoComponent implements AfterViewInit { formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => { console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`); }); + } ngOnInit() { @@ -242,6 +251,12 @@ export class ActivitiDemoComponent implements AfterViewInit { this.currentTaskId = null; } + onFormContentClick(content: any) { + this.fileShowed = true; + this.content = content.contentBlob; + this.contentName = content.name; + } + onTaskCreated(data: any) { this.currentTaskId = data.parentTaskId; this.activititasklist.reload(); diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts index 5b5f55517d..14b1073067 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts @@ -94,8 +94,15 @@ export class ActivitiContent implements OnChanges { } openViewer(content: ContentLinkModel): void { - this.contentClick.emit(content); - this.logService.info('Content clicked' + content.id); + this.formService.getFileRawContent(content.id).subscribe( + (blob: Blob) => { + content.contentBlob = blob; + this.contentClick.emit(content); + this.logService.info('Content clicked' + content.id); + this.formService.formContentClicked.next(content); + }, + error => this.logService.error(error) + ); } /** diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts index a591ee9a0b..e3ea619a73 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-form.component.ts @@ -21,6 +21,7 @@ import { EcmModelService } from './../services/ecm-model.service'; import { FormService } from './../services/form.service'; import { NodeService } from './../services/node.service'; import { FormModel, FormOutcomeModel, FormValues, FormFieldModel, FormOutcomeEvent } from './widgets/core/index'; +import { ContentLinkModel } from './widgets/core/content-link.model'; import { FormEvent, FormErrorEvent } from './../events/index'; import { WidgetVisibilityService } from './../services/widget-visibility.service'; @@ -134,6 +135,9 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges { @Output() formCompleted: EventEmitter = new EventEmitter(); + @Output() + formContentClicked: EventEmitter = new EventEmitter(); + @Output() formLoaded: EventEmitter = new EventEmitter(); @@ -202,6 +206,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges { } ngOnInit() { + this.formService.formContentClicked.subscribe((content: ContentLinkModel) => { + this.formContentClicked.emit(content); + }); + if (this.nodeId) { this.loadFormForEcmNode(); } else { diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/content-link.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/content-link.model.ts index 0e8e47c0e7..6db04842a6 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/content-link.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/content-link.model.ts @@ -29,6 +29,7 @@ export class ContentLinkModel { simpleType: string; thumbnailUrl: string; contentRawUrl: string; + contentBlob: Blob; thumbnailStatus: string; constructor(obj?: any) { diff --git a/ng2-components/ng2-activiti-form/src/services/form.service.ts b/ng2-components/ng2-activiti-form/src/services/form.service.ts index fb2a4a14db..070d0510be 100644 --- a/ng2-components/ng2-activiti-form/src/services/form.service.ts +++ b/ng2-components/ng2-activiti-form/src/services/form.service.ts @@ -24,6 +24,7 @@ import { EcmModelService } from './ecm-model.service'; import { GroupModel } from './../components/widgets/core/group.model'; import { GroupUserModel } from './../components/widgets/core/group-user.model'; import { FormEvent, FormErrorEvent, FormFieldEvent } from './../events/index'; +import { ContentLinkModel } from './../components/widgets/core/content-link.model'; @Injectable() export class FormService { @@ -37,6 +38,7 @@ export class FormService { taskCompletedError: Subject = new Subject(); taskSaved: Subject = new Subject(); taskSavedError: Subject = new Subject(); + formContentClicked: Subject = new Subject(); constructor(private ecmModelService: EcmModelService, private apiService: AlfrescoApiService, diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.html b/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.html index 28c74fdd1b..9fbb193a4f 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.html +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.html @@ -53,6 +53,7 @@ [readOnly]="readOnlyForm" (formSaved)='onFormSaved($event)' (formCompleted)='onFormCompleted($event)' + (formContentClicked)='onFormContentClick($event)' (formLoaded)='onFormLoaded($event)' (onError)='onFormError($event)' (executeOutcome)='onFormExecuteOutcome($event)'> diff --git a/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.ts b/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.ts index fe0919a4c9..88b0b24d7a 100644 --- a/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.ts +++ b/ng2-components/ng2-activiti-tasklist/src/components/activiti-task-details.component.ts @@ -20,7 +20,7 @@ import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core'; import { ActivitiTaskListService } from './../services/activiti-tasklist.service'; import { TaskDetailsModel } from '../models/task-details.model'; import { User } from '../models/user.model'; -import { FormService, FormModel, FormOutcomeEvent } from 'ng2-activiti-form'; +import { FormService, FormModel, FormOutcomeEvent, ContentLinkModel } from 'ng2-activiti-form'; import { TaskQueryRequestRepresentationModel } from '../models/filter.model'; @Component({ @@ -87,6 +87,9 @@ export class ActivitiTaskDetails implements OnInit, OnChanges { @Output() formCompleted: EventEmitter = new EventEmitter(); + @Output() + formContentClicked: EventEmitter = new EventEmitter(); + @Output() formLoaded: EventEmitter = new EventEmitter(); @@ -228,6 +231,10 @@ export class ActivitiTaskDetails implements OnInit, OnChanges { ); } + onFormContentClick(content: ContentLinkModel) { + this.formContentClicked.emit(content); + } + onFormSaved(form: FormModel) { this.formSaved.emit(form); } diff --git a/ng2-components/ng2-alfresco-viewer/README.md b/ng2-components/ng2-alfresco-viewer/README.md index 41bfe6bae1..039e8ef82b 100644 --- a/ng2-components/ng2-alfresco-viewer/README.md +++ b/ng2-components/ng2-alfresco-viewer/README.md @@ -210,10 +210,12 @@ platformBrowserDynamic().bootstrapModule(AppModule); Attribute | Options | Default | Description | Mandatory --- | --- | --- | --- | --- `fileNodeId` | *string* | | node Id of the file to load the file | -`urlFile` | *string* | | If you want laod an external file that not comes from the ECM you can use this Url where to load the file | +`urlFile` | *string* | | If you want load an external file that not comes from the ECM you can use this Url where to load the file | +`urlBlob` | *Blob* | | If you want load a Blob File | `overlayMode` | *boolean* | `false` | if true Show the Viewer full page over the present content otherwise will fit the parent div | `showViewer` | *boolean* | `true` | Hide or show the viewer | `showToolbar` | *boolean* | `true` | Hide or show the toolbars | +`displayName` | *string* | | You can specify the name of the file| #### Supported file formats diff --git a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.html b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.html index 5f4d71b098..2a65bf5dac 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.html +++ b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.html @@ -1,8 +1,7 @@
- {{nameFile}} +
- diff --git a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.spec.ts b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.spec.ts index cd0911947a..3458199c2f 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.spec.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.spec.ts @@ -17,21 +17,28 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ImgViewerComponent } from './imgViewer.component'; -import { DebugElement } from '@angular/core'; +import { DebugElement, SimpleChange } from '@angular/core'; import { AlfrescoAuthenticationService, AlfrescoSettingsService, AlfrescoApiService, - CoreModule + CoreModule, + ContentService } from 'ng2-alfresco-core'; describe('Test ng2-alfresco-viewer Img viewer component ', () => { let component: ImgViewerComponent; + let service: ContentService; let fixture: ComponentFixture; let debug: DebugElement; let element: HTMLElement; + function createFakeBlob() { + let data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); + return new Blob([data], {type: 'image/png'}); + } + beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ @@ -44,6 +51,7 @@ describe('Test ng2-alfresco-viewer Img viewer component ', () => { AlfrescoApiService ] }).compileComponents(); + service = TestBed.get(ContentService); })); beforeEach(() => { @@ -55,17 +63,18 @@ describe('Test ng2-alfresco-viewer Img viewer component ', () => { fixture.detectChanges(); }); - it('If no url is passed should thrown an error', () => { + it('If no url or blob are passed should thrown an error', () => { + let change = new SimpleChange(null, null); expect(() => { - component.ngOnChanges(null); - }).toThrow(new Error('Attribute urlFile is required')); + component.ngOnChanges({ 'blobFile': change }); + }).toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('If url is passed should not thrown an error', () => { component.urlFile = 'fake-url'; expect(() => { component.ngOnChanges(null); - }).not.toThrow(new Error('Attribute urlFile is required')); + }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('The file Name should be present in the alt attribute', () => { @@ -73,4 +82,15 @@ describe('Test ng2-alfresco-viewer Img viewer component ', () => { fixture.detectChanges(); expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name'); }); + + it('If blob is passed should not thrown an error', () => { + let blob = createFakeBlob(); + + spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url'); + let change = new SimpleChange(null, blob); + expect(() => { + component.ngOnChanges({ 'blobFile': change }); + }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); + expect(component.urlFile).toEqual('fake-blob-url'); + }); }); diff --git a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.ts b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.ts index a41b696646..d222a5e7da 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/imgViewer.component.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { ContentService } from 'ng2-alfresco-core'; @Component({ moduleId: module.id, @@ -23,17 +24,27 @@ import { Component, Input } from '@angular/core'; templateUrl: './imgViewer.component.html', styleUrls: ['./imgViewer.component.css'] }) -export class ImgViewerComponent { +export class ImgViewerComponent implements OnChanges { @Input() urlFile: string; + @Input() + blobFile: any; + @Input() nameFile: string; - ngOnChanges(changes) { - if (!this.urlFile) { - throw new Error('Attribute urlFile is required'); + constructor(private contentService: ContentService) {} + + ngOnChanges(changes: SimpleChanges) { + let blobFile = changes['blobFile']; + if (blobFile && blobFile.currentValue) { + this.urlFile = this.contentService.createTrustedUrl(this.blobFile); + return; + } + if (!this.urlFile && !this.blobFile) { + throw new Error('Attribute urlFile or blobFile is required'); } } } diff --git a/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.spec.ts b/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.spec.ts index 8093cb51d6..097721afbb 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.spec.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.spec.ts @@ -17,7 +17,9 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { MediaPlayerComponent } from './mediaPlayer.component'; -import { DebugElement } from '@angular/core'; +import { DebugElement, SimpleChange } from '@angular/core'; +import { ContentService } from 'ng2-alfresco-core'; + import { AlfrescoAuthenticationService, AlfrescoSettingsService, @@ -28,10 +30,16 @@ import { describe('Test ng2-alfresco-viewer Media player component ', () => { let component: MediaPlayerComponent; + let service: ContentService; let fixture: ComponentFixture; let debug: DebugElement; let element: HTMLElement; + function createFakeBlob() { + let data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); + return new Blob([data], {type: 'image/png'}); + } + beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ @@ -44,6 +52,7 @@ describe('Test ng2-alfresco-viewer Media player component ', () => { AlfrescoApiService ] }).compileComponents(); + service = TestBed.get(ContentService); })); beforeEach(() => { @@ -55,23 +64,35 @@ describe('Test ng2-alfresco-viewer Media player component ', () => { fixture.detectChanges(); }); - it('If no url is passed should thrown an error', () => { + it('If no url or no blob are passed should thrown an error', () => { + let change = new SimpleChange(null, null); expect(() => { - component.ngOnChanges(null); - }).toThrow(new Error('Attribute urlFile is required')); + component.ngOnChanges({ 'blobFile': change }); + }).toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('If url is passed should not thrown an error', () => { component.urlFile = 'fake-url'; expect(() => { component.ngOnChanges(null); - }).not.toThrow(new Error('Attribute urlFile is required')); + }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('If url is passed should not thrown an error', () => { component.urlFile = 'fake-url'; expect(() => { component.ngOnChanges(null); - }).not.toThrow(new Error('Attribute urlFile is required')); + }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); + }); + + it('If blob is passed should not thrown an error', () => { + let blob = createFakeBlob(); + + spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url'); + let change = new SimpleChange(null, blob); + expect(() => { + component.ngOnChanges({ 'blobFile': change }); + }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); + expect(component.urlFile).toEqual('fake-blob-url'); }); }); diff --git a/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.ts b/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.ts index 734f0be58e..c7185f92ef 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/mediaPlayer.component.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { ContentService } from 'ng2-alfresco-core'; @Component({ moduleId: module.id, @@ -23,21 +24,31 @@ import { Component, Input } from '@angular/core'; templateUrl: './mediaPlayer.component.html', styleUrls: ['./mediaPlayer.component.css'] }) -export class MediaPlayerComponent { +export class MediaPlayerComponent implements OnChanges { @Input() urlFile: string; + @Input() + blobFile: Blob; + @Input() mimeType: string; @Input() nameFile: string; - ngOnChanges(changes) { - if (!this.urlFile) { - throw new Error('Attribute urlFile is required'); + constructor(private contentService: ContentService ) {} + + ngOnChanges(changes: SimpleChanges) { + let blobFile = changes['blobFile']; + if (blobFile && blobFile.currentValue) { + this.urlFile = this.contentService.createTrustedUrl(this.blobFile); + return; + } + + if (!this.urlFile && !this.blobFile) { + throw new Error('Attribute urlFile or blobFile is required'); } } - } diff --git a/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.spec.ts b/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.spec.ts index e81ee5b622..eb8771872a 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.spec.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.spec.ts @@ -49,6 +49,24 @@ describe('Test ng2-alfresco-viewer PdfViewer component', () => { }).compileComponents(); })); + function createFakeBlob(): Blob { + let pdfData = atob( + 'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' + + 'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' + + 'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' + + 'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' + + 'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' + + 'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' + + 'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' + + 'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' + + 'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' + + 'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' + + 'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' + + 'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' + + 'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G'); + return new Blob([pdfData], {type: 'application/pdf'}); + } + beforeEach(() => { fixture = TestBed.createComponent(PdfViewerComponent); @@ -57,21 +75,71 @@ describe('Test ng2-alfresco-viewer PdfViewer component', () => { component = fixture.componentInstance; component.showToolbar = true; - component.urlFile = 'base/src/assets/fake-test-file.pdf'; - fixture.detectChanges(); }); - describe('View', () => { + describe('View with url file', () => { + beforeEach(() => { + component.urlFile = 'base/src/assets/fake-test-file.pdf'; + fixture.detectChanges(); + }); - it('If urlfile is not present should not be thrown any error ', () => { + it('If urlfile is not present should thrown an error ', () => { component.urlFile = undefined; fixture.detectChanges(); expect(() => { component.ngOnChanges(null); - }).toThrow(); + }).toThrow(new Error('Attribute urlFile or blobFile is required')); + }); + + it('Canvas should be present', () => { + expect(element.querySelector('#viewer-viewerPdf')).not.toBeNull(); + expect(element.querySelector('#viewer-pdf-container')).not.toBeNull(); + }); + + it('Loader should be present', () => { + expect(element.querySelector('#loader-container')).not.toBeNull(); + }); + + it('Next an Previous Buttons should be present', () => { + expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull(); + expect(element.querySelector('#viewer-next-page-button')).not.toBeNull(); + }); + + it('Input Page elements should be present', () => { + expect(element.querySelector('#viewer-pagenumber-input')).toBeDefined(); + expect(element.querySelector('#viewer-total-pages')).toBeDefined(); + + expect(element.querySelector('#viewer-previous-page-button')).not.toBeNull(); + expect(element.querySelector('#viewer-next-page-button')).not.toBeNull(); + }); + + it('Toolbar should be hide if showToolbar is false', () => { + component.showToolbar = false; + + fixture.detectChanges(); + + expect(element.querySelector('#viewer-toolbar-command')).toBeNull(); + expect(element.querySelector('#viewer-toolbar-pagination')).toBeNull(); + }); + }); + + describe('View with blob file', () => { + + beforeEach(() => { + component.urlFile = undefined; + component.blobFile = createFakeBlob(); + + fixture.detectChanges(); + }); + + it('If blobFile is not present should thrown an error ', () => { + component.blobFile = undefined; + expect(() => { + component.ngOnChanges(null); + }).toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('Canvas should be present', () => { @@ -109,6 +177,8 @@ describe('Test ng2-alfresco-viewer PdfViewer component', () => { describe('User interaction', () => { beforeEach(() => { + component.urlFile = 'base/src/assets/fake-test-file.pdf'; + fixture.detectChanges(); component.inputPage('1'); }); @@ -244,6 +314,10 @@ describe('Test ng2-alfresco-viewer PdfViewer component', () => { }); describe('Resize interaction', () => { + beforeEach(() => { + component.urlFile = 'base/src/assets/fake-test-file.pdf'; + component.inputPage('1'); + }); it('resize event should trigger setScaleUpdatePages', (done) => { component.ngOnChanges(null).then(() => { fixture.detectChanges(); @@ -256,6 +330,10 @@ describe('Test ng2-alfresco-viewer PdfViewer component', () => { }); describe('scroll interaction', () => { + beforeEach(() => { + component.urlFile = 'base/src/assets/fake-test-file.pdf'; + fixture.detectChanges(); + }); it('scroll page should return the current page', (done) => { component.ngOnChanges(null).then(() => { fixture.detectChanges(); diff --git a/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.ts b/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.ts index d2f1a5d58c..e9b5177b1f 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/pdfViewer.component.ts @@ -33,6 +33,9 @@ export class PdfViewerComponent { @Input() urlFile: string; + @Input() + blobFile: Blob; + @Input() nameFile: string; @@ -58,40 +61,52 @@ export class PdfViewerComponent { } ngOnChanges(changes) { - if (!this.urlFile) { - throw new Error('Attribute urlFile is required'); + if (!this.urlFile && !this.blobFile) { + throw new Error('Attribute urlFile or blobFile is required'); } if (this.urlFile) { return new Promise((resolve, reject) => { - let loadingTask = this.getPDFJS().getDocument(this.urlFile); - - loadingTask.onProgress = (progressData) => { - let level = progressData.loaded / progressData.total; - this.laodingPercent = Math.round(level * 100); + this.executePdf(this.urlFile, resolve, reject); + }); + } else { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.onload = () => { + this.executePdf(reader.result, resolve, reject); }; - - loadingTask.then((pdfDocument) => { - this.currentPdfDocument = pdfDocument; - this.totalPages = pdfDocument.numPages; - this.page = 1; - this.displayPage = 1; - this.initPDFViewer(this.currentPdfDocument); - - this.currentPdfDocument.getPage(1).then(() => { - this.scalePage('auto'); - resolve(); - }, (error) => { - reject(error); - }); - - }, (error) => { - reject(error); - }); + reader.readAsArrayBuffer(this.blobFile); }); } } + executePdf(src, resolve, reject) { + let loadingTask = this.getPDFJS().getDocument(src); + + loadingTask.onProgress = (progressData) => { + let level = progressData.loaded / progressData.total; + this.laodingPercent = Math.round(level * 100); + }; + + loadingTask.then((pdfDocument) => { + this.currentPdfDocument = pdfDocument; + this.totalPages = pdfDocument.numPages; + this.page = 1; + this.displayPage = 1; + this.initPDFViewer(this.currentPdfDocument); + + this.currentPdfDocument.getPage(1).then(() => { + this.scalePage('auto'); + resolve(); + }, (error) => { + reject(error); + }); + + }, (error) => { + reject(error); + }); + } + /** * return the PDFJS global object (exist to facilitate the mock of PDFJS in the test) * diff --git a/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.html b/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.html index af5dd646cb..67e47c4b6b 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.html +++ b/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.html @@ -41,14 +41,14 @@
-
- +
-
diff --git a/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.ts b/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.ts index d3e6309de6..c9dda5fcf9 100644 --- a/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.ts +++ b/ng2-components/ng2-alfresco-viewer/src/components/viewer.component.ts @@ -31,6 +31,9 @@ export class ViewerComponent { @Input() urlFile: string = ''; + @Input() + blobFile: Blob; + @Input() fileNodeId: string = null; @@ -43,6 +46,9 @@ export class ViewerComponent { @Input() showToolbar: boolean = true; + @Input() + displayName: string; + @Output() showViewerChange: EventEmitter = new EventEmitter(); @@ -55,7 +61,6 @@ export class ViewerComponent { urlFileContent: string; otherMenu: any; - displayName: string; extension: string; mimeType: string; loaded: boolean = false; @@ -70,12 +75,16 @@ export class ViewerComponent { if (this.showViewer) { this.hideOtherHeaderBar(); this.blockOtherScrollBar(); - if (!this.urlFile && !this.fileNodeId) { - throw new Error('Attribute urlFile or fileNodeId is required'); + if (!this.urlFile && !this.blobFile && !this.fileNodeId) { + throw new Error('Attribute urlFile or fileNodeId or blobFile is required'); } return new Promise((resolve, reject) => { let alfrescoApi = this.apiService.getInstance(); - if (this.urlFile) { + if (this.blobFile) { + this.mimeType = this.blobFile.type; + this.extensionChange.emit(this.mimeType); + resolve(); + } else if (this.urlFile) { let filenameFromUrl = this.getFilenameFromUrl(this.urlFile); this.displayName = filenameFromUrl ? filenameFromUrl : ''; this.extension = this.getFileExtension(filenameFromUrl);