diff --git a/lib/content-services/content-node-selector/content-node-dialog.service.spec.ts b/lib/content-services/content-node-selector/content-node-dialog.service.spec.ts index dca9d95a24..d5ca3f3a41 100644 --- a/lib/content-services/content-node-selector/content-node-dialog.service.spec.ts +++ b/lib/content-services/content-node-selector/content-node-dialog.service.spec.ts @@ -15,25 +15,45 @@ * limitations under the License. */ -/*tslint:disable: ban*/ - -import { async, TestBed } from '@angular/core/testing'; -import { MinimalNodeEntryEntity } from 'alfresco-js-api'; -import { AppConfigService } from '@alfresco/adf-core'; +import { async, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { MinimalNodeEntryEntity, SitePaging } from 'alfresco-js-api'; +import { AppConfigService, SitesService } from '@alfresco/adf-core'; import { DocumentListService } from '../document-list/services/document-list.service'; import { ContentNodeDialogService } from './content-node-dialog.service'; import { MatDialog } from '@angular/material'; +import { Observable } from 'rxjs/Observable'; const fakeNode: MinimalNodeEntryEntity = { id: 'fake', name: 'fake-name' }; +const fakeSiteList: SitePaging = { + list: { + pagination: { + count: 1, + hasMoreItems: false, + totalItems: 1, + skipCount: 0, + maxItems: 100 + }, + entries: [ + { + entry: { + id: 'FAKE', + guid: 'FAKE-GUID', + title: 'FAKE-SITE-TITLE' + } + } + ] + } +}; + describe('ContentNodeDialogService', () => { let service: ContentNodeDialogService; - // let documentListService: DocumentListService; - // let contentDialogService: ContentNodeDialogService; + let documentListService: DocumentListService; + let sitesService: SitesService; let materialDialog: MatDialog; beforeEach(async(() => { @@ -42,6 +62,7 @@ describe('ContentNodeDialogService', () => { providers: [ ContentNodeDialogService, DocumentListService, + SitesService, MatDialog ] }).compileComponents(); @@ -52,7 +73,9 @@ describe('ContentNodeDialogService', () => { appConfig.config.ecmHost = 'http://localhost:9876/ecm'; service = TestBed.get(ContentNodeDialogService); + documentListService = TestBed.get(DocumentListService); materialDialog = TestBed.get(MatDialog); + sitesService = TestBed.get(SitesService); spyOn(materialDialog, 'open').and.stub(); spyOn(materialDialog, 'closeAll').and.stub(); @@ -67,7 +90,7 @@ describe('ContentNodeDialogService', () => { expect(materialDialog.open).toHaveBeenCalled(); }); - it('should be able to open the dialog when node has NOT permission', () => { + it('should NOT be able to open the dialog when node has NOT permission', () => { service.openCopyMoveDialog('fake-action', fakeNode, 'noperm').subscribe( () => { }, (error) => { @@ -76,6 +99,21 @@ describe('ContentNodeDialogService', () => { }); }); + it('should be able to open the dialog using a folder id', fakeAsync(() => { + spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(fakeNode)); + service.openFileBrowseDialogByFolderId('fake-folder-id').subscribe(); + tick(); + expect(materialDialog.open).toHaveBeenCalled(); + })); + + it('should be able to open the dialog using the first user site', fakeAsync(() => { + spyOn(sitesService, 'getSites').and.returnValue(Observable.of(fakeSiteList)); + spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(fakeNode)); + service.openFileBrowseDialogBySite().subscribe(); + tick(); + expect(materialDialog.open).toHaveBeenCalled(); + })); + it('should be able to close the material dialog', () => { service.close(); expect(materialDialog.closeAll).toHaveBeenCalled(); diff --git a/lib/content-services/content-node-selector/content-node-dialog.service.ts b/lib/content-services/content-node-selector/content-node-dialog.service.ts index 2bfa6cca2d..20cd9d7f32 100644 --- a/lib/content-services/content-node-selector/content-node-dialog.service.ts +++ b/lib/content-services/content-node-selector/content-node-dialog.service.ts @@ -21,8 +21,8 @@ import { ContentService } from '@alfresco/adf-core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import { ShareDataRow } from '../document-list/data/share-data-row.model'; -import { MinimalNodeEntryEntity } from 'alfresco-js-api'; -import { DataColumn } from '@alfresco/adf-core'; +import { MinimalNodeEntryEntity, SitePaging } from 'alfresco-js-api'; +import { DataColumn, SitesService } from '@alfresco/adf-core'; import { DocumentListService } from '../document-list/services/document-list.service'; import { ContentNodeSelectorComponent } from './content-node-selector.component'; import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface'; @@ -31,11 +31,26 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon export class ContentNodeDialogService { constructor(private dialog: MatDialog, - private contentService?: ContentService, - private documentListService?: DocumentListService) { } + private contentService: ContentService, + private documentListService: DocumentListService, + private siteService: SitesService) { } + + openFileBrowseDialogByFolderId(folderNodeId: string): Observable { + return Observable.fromPromise(this.documentListService.getFolderNode(folderNodeId)) + .switchMap((node: MinimalNodeEntryEntity) => { + return this.openUploadFileDialog('Choose', node); + }); + } + + openFileBrowseDialogBySite(): Observable { + return this.siteService.getSites().switchMap((response: SitePaging) => { + return this.openFileBrowseDialogByFolderId(response.list.entries[0].entry.guid); + }); + } openCopyMoveDialog(action: string, contentEntry: MinimalNodeEntryEntity, permission?: string): Observable { if (this.contentService.hasPermission(contentEntry, permission)) { + const select = new Subject(); select.subscribe({ complete: this.close.bind(this) @@ -45,17 +60,43 @@ export class ContentNodeDialogService { title: `${action} '${contentEntry.name}' to ...`, actionName: action, currentFolderId: contentEntry.parentId, - rowFilter: this.rowFilter.bind(this, contentEntry.id), imageResolver: this.imageResolver.bind(this), + rowFilter : this.rowFilter.bind(this, contentEntry.id), + isSelectionValid: this.hasEntityCreatePermission.bind(this), select: select }; - this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: 'adf-content-node-selector-dialog', width: '630px' }); + + this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px'); + return select; } else { return Observable.throw({ statusCode: 403 }); } } + openUploadFileDialog(action: string, contentEntry: MinimalNodeEntryEntity): Observable { + const select = new Subject(); + select.subscribe({ + complete: this.close.bind(this) + }); + + const data: ContentNodeSelectorComponentData = { + title: `${action} '${contentEntry.name}' to ...`, + actionName: action, + currentFolderId: contentEntry.id, + imageResolver: this.imageResolver.bind(this), + isSelectionValid: this.isNodeFile.bind(this), + select: select + }; + + this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px'); + return select; + } + + private openContentNodeDialog(data: ContentNodeSelectorComponentData, currentPanelClass: string, chosenWidth: string) { + this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: currentPanelClass, width: chosenWidth }); + } + private imageResolver(row: ShareDataRow, col: DataColumn): string | null { const entry: MinimalNodeEntryEntity = row.node.entry; if (!this.contentService.hasPermission(entry, 'create')) { @@ -75,7 +116,16 @@ export class ContentNodeDialogService { } } + private isNodeFile(entry: MinimalNodeEntryEntity): boolean { + return entry.isFile; + } + + private hasEntityCreatePermission(entry: MinimalNodeEntryEntity): boolean { + return this.contentService.hasPermission(entry, 'create'); + } + close() { this.dialog.closeAll(); } + } diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts index 4bffb8248c..b0153bfcb3 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts @@ -74,6 +74,10 @@ describe('ContentNodeSelectorComponent', () => { _observer.next(result); } + function returnAlwaysTrue(entry: MinimalNodeEntryEntity) { + return true; + } + function setupTestbed(plusProviders) { TestBed.configureTestingModule({ imports: [ @@ -597,6 +601,7 @@ describe('ContentNodeSelectorComponent', () => { beforeEach(() => { const alfrescoContentService = TestBed.get(ContentService); spyOn(alfrescoContentService, 'hasPermission').and.callFake(() => hasPermission); + component.isSelectionValid = returnAlwaysTrue.bind(this); }); it('should become enabled after loading node with the necessary permissions', async(() => { @@ -664,14 +669,14 @@ describe('ContentNodeSelectorComponent', () => { fixture.detectChanges(); }); - it('should be disabled when resetting the chosen node', () => { + it('should emit null when the chosenNode is reset', () => { hasPermission = true; component.onNodeSelect({ detail: { node: { entry: {} } } }); fixture.detectChanges(); component.select.subscribe((nodes) => { expect(nodes).toBeDefined(); - expect(nodes).not.toBeNull(); + expect(nodes).toBeNull(); }); component.resetChosenNode(); diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts index 4e814c63e9..1a512375f4 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts @@ -26,7 +26,6 @@ import { } from '@angular/core'; import { AlfrescoApiService, - ContentService, HighlightDirective, UserPreferencesService } from '@alfresco/adf-core'; @@ -38,6 +37,10 @@ import { ImageResolver } from '../document-list/data/image-resolver.model'; import { ContentNodeSelectorService } from './content-node-selector.service'; import { debounceTime } from 'rxjs/operators'; +export type ValidationFunction = (entry: MinimalNodeEntryEntity) => boolean; + +const defaultValidation = () => true; + @Component({ selector: 'adf-content-node-selector-panel', styleUrls: ['./content-node-selector-panel.component.scss'], @@ -46,19 +49,6 @@ import { debounceTime } from 'rxjs/operators'; }) export class ContentNodeSelectorPanelComponent implements OnInit { - nodes: NodePaging | null = null; - siteId: null | string; - searchTerm: string = ''; - showingSearchResults: boolean = false; - loadingSearchResults: boolean = false; - inDialog: boolean = false; - _chosenNode: MinimalNodeEntryEntity = null; - folderIdToShow: string | null = null; - paginationStrategy: PaginationStrategy; - pagination: Pagination; - skipCount: number = 0; - infiniteScroll: boolean = false; - @Input() currentFolderId: string = null; @@ -77,6 +67,9 @@ export class ContentNodeSelectorPanelComponent implements OnInit { @Input() pageSize: number; + @Input() + isSelectionValid: ValidationFunction = defaultValidation; + @Output() select: EventEmitter = new EventEmitter(); @@ -86,12 +79,22 @@ export class ContentNodeSelectorPanelComponent implements OnInit { @ViewChild(HighlightDirective) highlighter: HighlightDirective; + nodes: NodePaging | null = null; + siteId: null | string; + searchTerm: string = ''; + showingSearchResults: boolean = false; + loadingSearchResults: boolean = false; + inDialog: boolean = false; + _chosenNode: MinimalNodeEntryEntity = null; + folderIdToShow: string | null = null; + paginationStrategy: PaginationStrategy; + pagination: Pagination; + skipCount: number = 0; + infiniteScroll: boolean = false; debounceSearch: number= 200; - searchInput: FormControl = new FormControl(); constructor(private contentNodeSelectorService: ContentNodeSelectorService, - private contentService: ContentService, private apiService: AlfrescoApiService, private preferences: UserPreferencesService) { this.searchInput.valueChanges @@ -101,13 +104,16 @@ export class ContentNodeSelectorPanelComponent implements OnInit { .subscribe((searchValue) => { this.search(searchValue); }); - this.pageSize = this.preferences.paginationSize; } set chosenNode(value: MinimalNodeEntryEntity) { this._chosenNode = value; - this.select.next([value]); + let valuesArray = null; + if (value) { + valuesArray = [value]; + } + this.select.next(valuesArray); } get chosenNode() { @@ -270,7 +276,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit { * @param entry */ private attemptNodeSelection(entry: MinimalNodeEntryEntity): void { - if (this.contentService.hasPermission(entry, 'create')) { + if (this.isSelectionValid(entry)) { this.chosenNode = entry; } else { this.resetChosenNode(); diff --git a/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts b/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts index 1a8a48fe4d..e97dccf832 100644 --- a/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts +++ b/lib/content-services/content-node-selector/content-node-selector.component-data.interface.ts @@ -26,5 +26,6 @@ export interface ContentNodeSelectorComponentData { dropdownSiteList?: SitePaging; rowFilter?: any; imageResolver?: any; + isSelectionValid?: (entry: MinimalNodeEntryEntity) => boolean; select: Subject; } diff --git a/lib/content-services/content-node-selector/content-node-selector.component.html b/lib/content-services/content-node-selector/content-node-selector.component.html index 6cd69b4732..5473e7eb2f 100644 --- a/lib/content-services/content-node-selector/content-node-selector.component.html +++ b/lib/content-services/content-node-selector/content-node-selector.component.html @@ -11,6 +11,7 @@ [dropdownSiteList]="dropdownSiteList || data?.dropdownSiteList" [rowFilter]="rowFilter || data?.rowFilter" [imageResolver]="imageResolver || data?.imageResolver" + [isSelectionValid]="data?.isSelectionValid" (select)="onSelect($event)"> diff --git a/lib/content-services/content-node-selector/public-api.ts b/lib/content-services/content-node-selector/public-api.ts index 44f25b54f6..b3736503c3 100644 --- a/lib/content-services/content-node-selector/public-api.ts +++ b/lib/content-services/content-node-selector/public-api.ts @@ -19,3 +19,4 @@ export * from './content-node-selector.component-data.interface'; export * from './content-node-selector-panel.component'; export * from './content-node-selector.component'; export * from './content-node-selector.service'; +export * from './content-node-dialog.service'; diff --git a/lib/content-services/i18n/en.json b/lib/content-services/i18n/en.json index fabe081ec0..ecc798dd9a 100644 --- a/lib/content-services/i18n/en.json +++ b/lib/content-services/i18n/en.json @@ -7,6 +7,7 @@ "IMAGE_NOT_AVAILABLE": "Preview not available" }, "FIELD": { + "SOURCE": "Select source from ", "UPLOAD": "UPLOAD", "REQUIRED": "*Required", "VALIDATOR": { diff --git a/lib/core/form/components/widgets/attach/attach.widget.css b/lib/core/form/components/widgets/attach/attach.widget.css deleted file mode 100644 index 8bb37584c6..0000000000 --- a/lib/core/form/components/widgets/attach/attach.widget.css +++ /dev/null @@ -1,15 +0,0 @@ -.attach-widget { - width:100% -} - -.attach-widget__icon { - float: left; -} - -.attach-widget__file { - margin-top: 4px; -} - -.attach-widget__reset { - margin-top: 4px; -} diff --git a/lib/core/form/components/widgets/attach/attach.widget.html b/lib/core/form/components/widgets/attach/attach.widget.html deleted file mode 100644 index ea31aecc0b..0000000000 --- a/lib/core/form/components/widgets/attach/attach.widget.html +++ /dev/null @@ -1,32 +0,0 @@ -
- -
- {{getLinkedFileName()}} - - -
-
- - -

Select content

-
- -
-
- -
-
diff --git a/lib/core/form/components/widgets/attach/attach.widget.spec.ts b/lib/core/form/components/widgets/attach/attach.widget.spec.ts deleted file mode 100644 index d1fb09e366..0000000000 --- a/lib/core/form/components/widgets/attach/attach.widget.spec.ts +++ /dev/null @@ -1,302 +0,0 @@ -/*! - * @license - * Copyright 2016 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable } from 'rxjs/Observable'; -import { ActivitiContentService } from '../../../services/activiti-alfresco.service'; -import { MaterialModule } from '../../../../material.module'; -import { ExternalContent } from '../core/external-content'; -import { ExternalContentLink } from '../core/external-content-link'; -import { FormFieldTypes } from '../core/form-field-types'; -import { ErrorWidgetComponent } from '../error/error.component'; -import { EcmModelService } from './../../../services/ecm-model.service'; -import { FormService } from './../../../services/form.service'; -import { FormFieldModel } from './../core/form-field.model'; -import { FormModel } from './../core/form.model'; -import { AttachWidgetComponent } from './attach.widget'; - -describe('AttachWidgetComponent', () => { - - let widget: AttachWidgetComponent; - let fixture: ComponentFixture; - let contentService: ActivitiContentService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - MaterialModule - ], - declarations: [ - AttachWidgetComponent, - ErrorWidgetComponent - ], - providers: [ - FormService, - EcmModelService, - ActivitiContentService - ] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AttachWidgetComponent); - contentService = TestBed.get(ActivitiContentService); - - widget = fixture.componentInstance; - }); - - it('should require field value to check file', () => { - widget.field = null; - widget.ngOnInit(); - expect(widget.hasFile()).toBeFalsy(); - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - value: null - }); - widget.ngOnInit(); - expect(widget.hasFile()).toBeFalsy(); - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - value: [{ name: 'file' }] - }); - widget.ngOnInit(); - expect(widget.hasFile()).toBeTruthy(); - }); - - it('should setup with form field', () => { - let nodes: any = [{}]; - spyOn(contentService, 'getAlfrescoNodes').and.returnValue( - Observable.create(observer => { - observer.next(nodes); - observer.complete(); - }) - ); - - let config = { - siteId: '', - site: '', - pathId: '', - accountId: '' - }; - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - params: { - fileSource: { - selectedFolder: config - } - } - }); - widget.ngOnInit(); - - expect(widget.selectedFolderSiteId).toBe(config.siteId); - expect(widget.selectedFolderSiteName).toBe(config.site); - expect(widget.selectedFolderPathId).toBe(config.pathId); - expect(widget.selectedFolderAccountId).toBe(config.accountId); - expect(widget.selectedFolderNodes).toEqual(nodes); - }); - - xit('should link file on select', () => { - let link = {}; - spyOn(contentService, 'linkAlfrescoNode').and.returnValue( - Observable.create(observer => { - observer.next(link); - observer.complete(); - }) - ); - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD - }); - widget.ngOnInit(); - - let node = {}; - widget.selectFile(node, null); - - expect(contentService.linkAlfrescoNode).toHaveBeenCalled(); - expect(widget.selectedFile).toBe(node); - expect(widget.field.value).toEqual([link]); - expect(widget.field.json.value).toEqual([link]); - expect(widget.hasFile()).toBeTruthy(); - }); - - it('should reset', () => { - widget.field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.UPLOAD, - value: [{ name: 'filename' }] - }); - - widget.reset(); - expect(widget.hasFile()).toBeFalsy(); - expect(widget.field.value).toBeNull(); - expect(widget.field.json.value).toBeNull(); - expect(widget.hasFile()).toBeFalsy(); - }); - - it('should close dialog on cancel', () => { - let closed = false; - widget.dialog = { - nativeElement: { - close: function () { - closed = true; - } - } - }; - widget.cancel(); - expect(closed).toBeTruthy(); - }); - - xit('should show modal dialog', () => { - spyOn(contentService, 'getAlfrescoNodes').and.returnValue( - Observable.create(observer => { - observer.next([]); - observer.complete(); - }) - ); - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - params: { - fileSource: { - selectedFolder: {} - } - } - }); - - let modalShown = false; - widget.dialog = { - nativeElement: { - showModal: function () { - modalShown = true; - } - } - }; - - widget.showDialog(); - expect(modalShown).toBeTruthy(); - }); - - it('should select folder and load nodes', () => { - let nodes: any = [{}]; - spyOn(contentService, 'getAlfrescoNodes').and.returnValue( - Observable.create(observer => { - observer.next(nodes); - observer.complete(); - }) - ); - - let node = { id: '' }; - widget.selectFolder(node, null); - - expect(widget.selectedFolderPathId).toBe(node.id); - expect(widget.selectedFolderNodes).toEqual(nodes); - }); - - it('should get linked file name via local variable', () => { - widget.fileName = ''; - widget.selectedFile = null; - widget.field = null; - expect(widget.getLinkedFileName()).toBe(widget.fileName); - }); - - it('should get linked file name via selected file', () => { - widget.fileName = null; - widget.selectedFile = { title: '' }; - widget.field = null; - expect(widget.getLinkedFileName()).toBe(widget.selectedFile.title); - }); - - it('should get linked file name via form field', () => { - widget.fileName = null; - widget.selectedFile = null; - - let name = '<file>'; - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - value: [{ name: name }] - }); - - expect(widget.getLinkedFileName()).toBe(name); - }); - - it('should require form field to setup file browser', () => { - widget.field = null; - widget.setupFileBrowser(); - - expect(widget.selectedFolderPathId).toBeUndefined(); - expect(widget.selectedFolderAccountId).toBeUndefined(); - - const pathId = '<pathId>'; - const accountId = '<accountId>'; - - widget.field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - params: { - fileSource: { - selectedFolder: { - pathId: pathId, - accountId: accountId - } - } - } - }); - widget.setupFileBrowser(); - expect(widget.selectedFolderPathId).toBe(pathId); - expect(widget.selectedFolderAccountId).toBe(accountId); - }); - - it('should get external content nodes', () => { - let nodes: any = [{}]; - spyOn(contentService, 'getAlfrescoNodes').and.returnValue( - Observable.create(observer => { - observer.next(nodes); - observer.complete(); - }) - ); - - const accountId = '<accountId>'; - const pathId = '<pathId>'; - widget.selectedFolderAccountId = accountId; - widget.selectedFolderPathId = pathId; - widget.getExternalContentNodes(); - - expect(contentService.getAlfrescoNodes).toHaveBeenCalledWith(accountId, pathId); - expect(widget.selectedFolderNodes).toEqual(nodes); - }); - - it('should handle error', (done) => { - let error = 'error'; - spyOn(contentService, 'getAlfrescoNodes').and.returnValue( - Observable.throw(error) - ); - - widget.error.subscribe(() => { - done(); - }); - - widget.getExternalContentNodes(); - }); - - it('should require configured dialog to show modal', () => { - widget.dialog = null; - spyOn(widget, 'setupFileBrowser').and.stub(); - spyOn(widget, 'getExternalContentNodes').and.stub(); - expect(widget.showDialog()).toBeFalsy(); - }); -}); diff --git a/lib/core/form/components/widgets/attach/attach.widget.ts b/lib/core/form/components/widgets/attach/attach.widget.ts deleted file mode 100644 index 088a3c764c..0000000000 --- a/lib/core/form/components/widgets/attach/attach.widget.ts +++ /dev/null @@ -1,156 +0,0 @@ -/*! - * @license - * Copyright 2016 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, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ActivitiContentService } from '../../../services/activiti-alfresco.service'; -import { ExternalContent } from '../core/external-content'; -import { ExternalContentLink } from '../core/external-content-link'; -import { FormFieldModel } from '../core/form-field.model'; -import { FormService } from './../../../services/form.service'; -import { baseHost , WidgetComponent } from './../widget.component'; - -@Component({ - selector: 'attach-widget', - templateUrl: './attach.widget.html', - styleUrls: ['./attach.widget.css'], - host: baseHost, - encapsulation: ViewEncapsulation.None -}) -export class AttachWidgetComponent extends WidgetComponent implements OnInit { - - selectedFolderPathId: string; - selectedFolderSiteId: string; - selectedFolderSiteName: string; - selectedFolderAccountId: string; - fileName: string; - selectedFolderNodes: [ExternalContent]; - selectedFile: ExternalContent; - - @Output() - fieldChanged: EventEmitter<FormFieldModel> = new EventEmitter<FormFieldModel>(); - - @Output() - error: EventEmitter<any> = new EventEmitter<any>(); - - @ViewChild('dialog') - dialog: any; - - constructor(public formService: FormService, - private contentService: ActivitiContentService) { - super(formService); - } - - ngOnInit() { - if (this.field) { - let params = this.field.params; - - if (params && - params.fileSource && - params.fileSource.selectedFolder) { - this.selectedFolderSiteId = params.fileSource.selectedFolder.siteId; - this.selectedFolderSiteName = params.fileSource.selectedFolder.site; - this.setupFileBrowser(); - this.getExternalContentNodes(); - } - } - } - - setupFileBrowser() { - if (this.field) { - let params = this.field.params; - this.selectedFolderPathId = params.fileSource.selectedFolder.pathId; - this.selectedFolderAccountId = params.fileSource.selectedFolder.accountId; - } - } - - getLinkedFileName(): string { - let result = this.fileName; - - if (this.selectedFile && - this.selectedFile.title) { - result = this.selectedFile.title; - } - if (this.field && - this.field.value && - this.field.value.length > 0 && - this.field.value[0].name) { - result = this.field.value[0].name; - } - - return result; - } - - getExternalContentNodes() { - this.contentService.getAlfrescoNodes(this.selectedFolderAccountId, this.selectedFolderPathId) - .subscribe( - nodes => this.selectedFolderNodes = nodes, - (err) => { - this.error.emit(err); - } - ); - } - - selectFile(node: ExternalContent, $event: any) { - this.contentService.linkAlfrescoNode(this.selectedFolderAccountId, node, this.selectedFolderSiteId).subscribe( - (link: ExternalContentLink) => { - this.selectedFile = node; - this.field.value = [link]; - this.field.json.value = [link]; - this.closeDialog(); - this.fieldChanged.emit(this.field); - } - ); - } - - selectFolder(node: ExternalContent, $event: any) { - this.selectedFolderPathId = node.id; - this.getExternalContentNodes(); - } - - showDialog(): boolean { - this.setupFileBrowser(); - this.getExternalContentNodes(); - - if (this.dialog) { - // todo: show dialog - return true; - } - return false; - } - - private closeDialog() { - if (this.dialog) { - this.dialog.nativeElement.close(); - } - } - - cancel() { - this.closeDialog(); - } - - reset() { - this.field.value = null; - this.field.json.value = null; - } - - hasFile(): boolean { - return this.field && this.field.value; - } - -} diff --git a/lib/core/form/components/widgets/index.ts b/lib/core/form/components/widgets/index.ts index d793a18cf5..3fa1231187 100644 --- a/lib/core/form/components/widgets/index.ts +++ b/lib/core/form/components/widgets/index.ts @@ -20,7 +20,6 @@ import { TabsWidgetComponent } from './tabs/tabs.widget'; import { UnknownWidgetComponent } from './unknown/unknown.widget'; import { AmountWidgetComponent } from './amount/amount.widget'; -import { AttachWidgetComponent } from './attach/attach.widget'; import { CheckboxWidgetComponent } from './checkbox/checkbox.widget'; import { DateWidgetComponent } from './date/date.widget'; import { DisplayTextWidgetComponentComponent } from './display-text/display-text.widget'; @@ -63,7 +62,6 @@ export * from './hyperlink/hyperlink.widget'; export * from './radio-buttons/radio-buttons.widget'; export * from './display-text/display-text.widget'; export * from './upload/upload.widget'; -export * from './attach/attach.widget'; export * from './typeahead/typeahead.widget'; export * from './functional-group/functional-group.widget'; export * from './people/people.widget'; @@ -95,7 +93,6 @@ export const WIDGET_DIRECTIVES: any[] = [ RadioButtonsWidgetComponent, DisplayTextWidgetComponentComponent, UploadWidgetComponent, - AttachWidgetComponent, TypeaheadWidgetComponent, FunctionalGroupWidgetComponent, PeopleWidgetComponent, diff --git a/lib/core/form/components/widgets/upload/upload.widget.ts b/lib/core/form/components/widgets/upload/upload.widget.ts index fc4d195087..c6a50dee7f 100644 --- a/lib/core/form/components/widgets/upload/upload.widget.ts +++ b/lib/core/form/components/widgets/upload/upload.widget.ts @@ -98,7 +98,7 @@ export class UploadWidgetComponent extends WidgetComponent implements OnInit { }); } - private getMultipleFileParam() { + getMultipleFileParam() { if (this.field && this.field.params && this.field.params.multiple) { diff --git a/lib/core/form/public-api.ts b/lib/core/form/public-api.ts index 5fa6d1cddf..bfc50da1cc 100644 --- a/lib/core/form/public-api.ts +++ b/lib/core/form/public-api.ts @@ -27,5 +27,6 @@ export * from './services/ecm-model.service'; export * from './services/node.service'; export * from './services/form-rendering.service'; export * from './services/process-content.service'; +export * from './services/activiti-alfresco.service'; export * from './events/index'; diff --git a/lib/core/form/services/activiti-alfresco.service.ts b/lib/core/form/services/activiti-alfresco.service.ts index 4b1ae760bb..574bffbb83 100644 --- a/lib/core/form/services/activiti-alfresco.service.ts +++ b/lib/core/form/services/activiti-alfresco.service.ts @@ -18,7 +18,7 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { LogService } from '../../services/log.service'; import { Injectable } from '@angular/core'; -import { AlfrescoApi } from 'alfresco-js-api'; +import { AlfrescoApi, MinimalNodeEntryEntity } from 'alfresco-js-api'; import { Observable } from 'rxjs/Observable'; import { ExternalContent } from '../components/widgets/core/external-content'; import { ExternalContentLink } from '../components/widgets/core/external-content-link'; @@ -49,6 +49,24 @@ export class ActivitiContentService { .catch(err => this.handleError(err)); } + /** + * Returns a list of all the repositories configured + * + * @param accountId + * @param folderId + * @returns {any} + */ + getAlfrescoRepositories(tenantId: number, includeAccount: boolean): Observable<any> { + let apiService: AlfrescoApi = this.apiService.getInstance(); + const opts = { + tenantId: tenantId, + includeAccounts: includeAccount + }; + return Observable.fromPromise(apiService.activiti.alfrescoApi.getRepositories(opts)) + .map(this.toJsonArray) + .catch(err => this.handleError(err)); + } + /** * Returns a list of child nodes below the specified folder * @@ -68,6 +86,20 @@ export class ActivitiContentService { })).map(this.toJson).catch(err => this.handleError(err)); } + applyAlfrescoNode(node: MinimalNodeEntryEntity, siteId: string, accountId: string) { + let apiService: AlfrescoApi = this.apiService.getInstance(); + let params: any = { + source: accountId, + sourceId: node.id + ';1.0@' + siteId, + name: node.name, + link: false + }; + return Observable.fromPromise( + apiService.activiti.contentApi.createTemporaryRelatedContent(params)) + .map(this.toJson) + .catch(err => this.handleError(err)); + } + toJson(res: any) { if (res) { return res || {}; diff --git a/lib/core/form/services/form-rendering.service.spec.ts b/lib/core/form/services/form-rendering.service.spec.ts index eafb18e735..5f0a65ea4b 100644 --- a/lib/core/form/services/form-rendering.service.spec.ts +++ b/lib/core/form/services/form-rendering.service.spec.ts @@ -17,7 +17,6 @@ import { DynamicComponentResolver } from '../../index'; import { - AttachWidgetComponent, FormFieldModel, FormFieldTypes, UnknownWidgetComponent, @@ -33,17 +32,6 @@ describe('FormRenderingService', () => { service = new FormRenderingService(); }); - it('should resolve Upload field as Attach widget', () => { - let field = new FormFieldModel(null, { - type: FormFieldTypes.UPLOAD, - params: { - link: 'link' - } - }); - let type = service.resolveComponentType(field); - expect(type).toBe(AttachWidgetComponent); - }); - it('should resolve Upload field as Upload widget', () => { let field = new FormFieldModel(null, { type: FormFieldTypes.UPLOAD, @@ -55,10 +43,10 @@ describe('FormRenderingService', () => { expect(type).toBe(UploadWidgetComponent); }); - it('should resolve Unknown widget for Upload field', () => { + it('should resolve Upload widget for Upload field', () => { let resolver = service.getComponentTypeResolver(FormFieldTypes.UPLOAD); let type = resolver(null); - expect(type).toBe(UnknownWidgetComponent); + expect(type).toBe(UploadWidgetComponent); }); it('should resolve Uknown widget for unknown field type', () => { @@ -67,12 +55,6 @@ describe('FormRenderingService', () => { expect(type).toBe(UnknownWidgetComponent); }); - it('shoulld resolve custom value for unknown field type', () => { - let resolver = service.getComponentTypeResolver('missing-type', AttachWidgetComponent); - let type = resolver(null); - expect(type).toBe(AttachWidgetComponent); - }); - it('should fallback to default resolver when field type missing', () => { let resolver = service.getComponentTypeResolver(null); let type = resolver(null); @@ -123,7 +105,7 @@ describe('FormRenderingService', () => { }); it('should return custom value when resolving with no field', () => { - expect(service.resolveComponentType(null, AttachWidgetComponent)).toBe(AttachWidgetComponent); + expect(service.resolveComponentType(null, UploadWidgetComponent)).toBe(UploadWidgetComponent); }); }); diff --git a/lib/core/form/services/form-rendering.service.ts b/lib/core/form/services/form-rendering.service.ts index 45c8346ea7..6ad1082e19 100644 --- a/lib/core/form/services/form-rendering.service.ts +++ b/lib/core/form/services/form-rendering.service.ts @@ -20,7 +20,6 @@ import { Injectable, Type } from '@angular/core'; import { AmountWidgetComponent, - AttachWidgetComponent, CheckboxWidgetComponent, ContainerWidgetComponent, DateWidgetComponent, @@ -28,7 +27,6 @@ import { DocumentWidgetComponent, DropdownWidgetComponent, DynamicTableWidgetComponent, - FormFieldModel, FunctionalGroupWidgetComponent, HyperlinkWidgetComponent, MultilineTextWidgetComponentComponent, @@ -63,21 +61,11 @@ export class FormRenderingService extends DynamicComponentMapper { 'dynamic-table': DynamicComponentResolver.fromType(DynamicTableWidgetComponent), 'container': DynamicComponentResolver.fromType(ContainerWidgetComponent), 'group': DynamicComponentResolver.fromType(ContainerWidgetComponent), - 'document': DynamicComponentResolver.fromType(DocumentWidgetComponent) + 'document': DynamicComponentResolver.fromType(DocumentWidgetComponent), + 'upload': DynamicComponentResolver.fromType(UploadWidgetComponent) }; constructor() { super(); - - this.types['upload'] = (field: FormFieldModel): Type<{}> => { - if (field) { - let params = field.params; - if (params && params.link) { - return AttachWidgetComponent; - } - return UploadWidgetComponent; - } - return UnknownWidgetComponent; - }; } } diff --git a/lib/core/services/sites-api.spec.ts b/lib/core/services/sites.service.spec.ts similarity index 100% rename from lib/core/services/sites-api.spec.ts rename to lib/core/services/sites.service.spec.ts diff --git a/lib/core/services/sites.service.ts b/lib/core/services/sites.service.ts index 75b93ed804..e669a1fd53 100644 --- a/lib/core/services/sites.service.ts +++ b/lib/core/services/sites.service.ts @@ -21,6 +21,7 @@ import { Observable } from 'rxjs/Observable'; import { AlfrescoApiService } from './alfresco-api.service'; import 'rxjs/add/observable/fromPromise'; import 'rxjs/add/operator/catch'; +import { SitePaging, SiteEntry } from 'alfresco-js-api'; @Injectable() export class SitesService { @@ -28,7 +29,7 @@ export class SitesService { constructor( private apiService: AlfrescoApiService) { } - getSites(opts: any = {}): any { + getSites(opts: any = {}): Observable<SitePaging> { const defaultOptions = { skipCount: 0, include: ['properties'] @@ -38,23 +39,23 @@ export class SitesService { .catch(this.handleError); } - getSite(siteId: string, opts?: any): any { + getSite(siteId: string, opts?: any): Observable<SiteEntry> { return Observable.fromPromise(this.apiService.getInstance().core.sitesApi.getSite(siteId, opts)) .catch(this.handleError); } - deleteSite(siteId: string, permanentFlag: boolean = true): any { + deleteSite(siteId: string, permanentFlag: boolean = true): Observable<any> { let options: any = {}; options.permanent = permanentFlag; return Observable.fromPromise(this.apiService.getInstance().core.sitesApi.deleteSite(siteId, options) .catch(this.handleError)); } - getSiteContent(siteId: string): Observable<any> { + getSiteContent(siteId: string): Observable<SiteEntry> { return this.getSite(siteId, { relations: ['containers'] }); } - getSiteMembers(siteId: string): Observable<any> { + getSiteMembers(siteId: string): Observable<SiteEntry> { return this.getSite(siteId, { relations: ['members'] }); } diff --git a/lib/ng-package/ng-package-process.json b/lib/ng-package/ng-package-process.json index cbc383f54f..52783e606a 100644 --- a/lib/ng-package/ng-package-process.json +++ b/lib/ng-package/ng-package-process.json @@ -13,6 +13,7 @@ "@angular/material": "@angular/material", "@angular/flex-layout": "@angular/flex-layout", "@alfresco/adf-core": "@alfresco/adf-core", + "@alfresco/adf-content-services": "@alfresco/adf-content-services", "@angular/material-moment-adapter": "@angular/material-moment-adapter", "@angular/animations": "@angular/animations", "@angular/cdk/platform": "@angular/cdk/platform", diff --git a/lib/package.json b/lib/package.json index 91a7dd6f8a..4384723766 100644 --- a/lib/package.json +++ b/lib/package.json @@ -24,7 +24,9 @@ "copy-i18n": "mkdir -p core/dist/bundles/assets/adf-core/i18n && cp -R core/i18n/* core/dist/bundles/assets/adf-core/i18n && mkdir -p content-services/dist/bundles/assets/adf-content-services/i18n && cp -R content-services/i18n/* content-services/dist/bundles/assets/adf-content-services/i18n && mkdir -p process-services/dist/bundles/assets/adf-process-services/i18n && cp -R process-services/i18n/* process-services/dist/bundles/assets/adf-process-services/i18n && mkdir -p insights/dist/bundles/assets/adf-insights/i18n && cp -R insights/i18n/* insights/dist/bundles/assets/adf-insights/i18n", "copy-assets": "cp -R core/assets/* core/dist/bundles/assets && cp -R content-services/assets/* content-services/dist/bundles/assets && cp -R process-services/assets/* process-services/dist/bundles/assets", "new-build": "npm run build-bundles && npm run build-style-new && npm run copy-i18n && npm run copy-assets", - "build-bundles": "npm run build-core && mkdir -p ./node_modules/@alfresco/adf-core/ && cp -R ./core/dist/* ./node_modules/@alfresco/adf-core/ && npm run build-content && npm run build-process && npm run build-insights", + "build-bundles": "npm run build-core && npm run link-core && npm run build-content && npm run link-content && npm run build-process && npm run build-insights", + "link-core": "mkdir -p ./node_modules/@alfresco/adf-core/ && cp -R ./core/dist/* ./node_modules/@alfresco/adf-core/", + "link-content": "mkdir -p ./node_modules/@alfresco/adf-content-services/ && cp -R ./content-services/dist/* ./node_modules/@alfresco/adf-content-services/", "build-core": "ng-packagr -p ./ng-package/ng-package-core.json", "build-content": "ng-packagr -p ./ng-package/ng-package-content.json", "build-process": "ng-packagr -p ./ng-package/ng-package-process.json", diff --git a/lib/process-services/assets/images/alfresco-flower.svg b/lib/process-services/assets/images/alfresco-flower.svg new file mode 100644 index 0000000000..0b796b7d60 --- /dev/null +++ b/lib/process-services/assets/images/alfresco-flower.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="17.2 134.6 562 573.1" enable-background="new 17.2 134.6 562 573.1" xml:space="preserve"> +<path fill="#FFFFFF" d="M298.2,421.3L219,340.4l-2.9-2.9c-45.4-46.5-119.5-46.5-164.7-0.2c-45.6,46.3-45.6,121.5,0,167.8 + c45.2,46.4,119,46.4,164.4,0L298.2,421.3z"/> +<path fill="#FFFFFF" d="M298.2,421.2v114.3l0.2,4.2c0,65.5-52.2,119-116.5,119c-64.3,0-116.5-53.1-116.5-118.6 + c0-65.6,52.4-118.8,116.5-118.8H298.2z"/> +<path fill="#FFFFFF" d="M298.2,421.2v114.3l0.2,4.2c0,65.5-52.2,119-116.5,119c-64.3,0-116.5-53.1-116.5-118.6 + c0-65.6,52.4-118.8,116.5-118.8H298.2z"/> +<path fill="#FFFFFF" d="M298.2,421.2v114.3l0.2,4.2c0,65.5-52.2,119-116.5,119c-64.3,0-116.5-53.1-116.5-118.6 + c0-7.3,0.8-14.4,2-21.2c45.4,32.2,108.2,27.7,148.6-13.7l82.4-83.8V421.2z"/> +<path fill="#8ABF42" d="M181.9,436.1c-56.1,0-101.6,46.5-101.6,103.8c0,57.1,45.6,103.5,101.6,103.5c56.2,0,101.7-46.4,101.7-103.5 + v-4.5v-99.3H182.1H181.9z"/> +<path fill="#FFFFFF" d="M298.2,421.2l79.2,80.8l3.3,2.9c45.2,46.4,45.5,121.8,0,168.1c-45.6,46.3-119.2,46.3-164.8,0 + c-45.4-46.3-45.4-121.5,0-168L298.2,421.2z"/> +<path fill="#8ABF42" d="M226.5,515.6c-39.8,40.5-39.8,106.2,0,146.6c39.6,40.5,104,40.5,143.8,0c39.6-40.4,39.6-106.1,0-146.6 + l-3.3-3l-68.7-70.2l-71.7,73.1V515.6z"/> +<path fill="#FFFFFF" d="M298.2,421.2h112.2l4.1-0.2c64.4,0,116.5,53.2,116.5,118.8c0,65.6-52,118.6-116.3,118.6 + c-64.3,0-116.5-53.1-116.5-118.6V421.2z"/> +<path fill="#8ABF42" d="M313.1,539.8c0,57.3,45.4,103.7,101.6,103.7c56,0,101.4-46.4,101.4-103.7c0-57.3-45.4-103.6-101.4-103.6 + h-4.3h-97.4v103.3V539.8z"/> +<path fill="#FFFFFF" d="M298.2,421.2l79.2-80.9l3.1-3.1c45.4-46.3,119.2-46.5,164.6-0.2c45.4,46.4,45.4,121.5,0,167.9 + c-45.4,46.4-119.2,46.4-164.6,0L298.2,421.2z"/> +<path fill="#8ABF42" d="M391.1,494.3c39.4,40.6,103.8,40.6,143.6,0c39.8-40.4,39.8-106,0-146.5c-39.8-40.5-104.1-40.5-143.6,0 + l-3.3,3.2l-68.6,70.3l71.5,73L391.1,494.3z"/> +<path fill="#FFFFFF" d="M298.2,421.2V306.8v-4.3c0-65.5,51.9-118.8,116.5-118.8c64.2,0,116.1,52.9,116.1,118.6 + c0,65.6-52,118.8-116.1,118.8H298.2z"/> +<path fill="#F89F31" d="M414.7,406.1c56,0,101.4-46.4,101.4-103.7c0-57.1-45.4-103.6-101.4-103.6c-56.3,0-101.8,46.5-101.8,103.6 + l0.2,4.4v99.3h101.1H414.7z"/> +<path fill="#FFFFFF" d="M298.2,421.2L219,340.3l-3.1-2.9c-45.4-46.5-45.4-121.8,0-168.1c45.2-46.4,119.1-46.4,164.6,0 + c45.4,46.3,45.4,121.6,0,167.9L298.2,421.2z"/> +<path fill="#0080C5" d="M370.1,327.6c39.6-40.5,39.6-106.2,0-146.6c-39.8-40.5-104-40.5-143.7,0c-39.8,40.4-39.8,106.1,0,146.6 + l2.9,3.1l69,70.1l71.6-73L370.1,327.6z"/> +<path fill="#FFFFFF" d="M298.2,422.1H186.1l-4.2,0.1c-64.1,0-116.5-53.2-116.5-118.8c0-65.5,52.2-118.7,116.3-118.7 + c64.3,0,116.6,53.2,116.6,118.8V422.1z"/> +<path fill="#0080C5" d="M283.5,303.5c0-57.3-45.4-103.6-101.8-103.6c-56,0-101.6,46.3-101.6,103.5c0,57.2,45.6,103.7,101.6,103.7 + l4.4-0.1h97.4V303.8V303.5z"/> +<path fill="#FFFFFF" d="M298.2,422.1l-281,0.1c0-30.4,11.2-60.8,34.2-84c45.2-46.3,119-46.3,164.4,0L298.2,422.1z"/> +<path fill="#0080C5" d="M205.4,348.9l71.9,73.3h-95.5c-51.6,0-95.5,34.3-110.7,81.9c-3.1-2.7-6.4-5.5-9.3-8.7 + c-39.8-40.5-39.8-106,0-146.5C101.3,308.4,165.8,308.4,205.4,348.9"/> +<path fill="#005DA9" d="M205.6,349l56.9,58l-80.9,0.2c-46.6,0-85.8-31.9-97.8-75.5c38.6-22.1,89.2-16.8,121.6,17.1L205.6,349z"/> +<path fill="#005DA9" d="M283.5,303.7l-0.1,82.1l-57.1-58.2c-32.9-33.6-38.7-84.4-17-123.8c42.7,12.2,74.2,52.2,74.2,99.8V303.7z"/> +<path fill="#005DA9" d="M312.6,386v-82.4c0-47.5,31.5-87.5,74.2-99.8c21.6,39.5,17.6,89-16.4,123.3L312.6,386z"/> +<path fill="#FFDE4F" d="M333.9,406.1l57.1-58.3c32.9-33.6,82.8-39.3,121.4-17.2c-12.2,43.5-49.4,74.8-95.3,75.6L333.9,406.1z"/> +<path fill="#48A64A" d="M333.8,436.1h80.8c46.6,0,85.8,31.7,97.8,75.4c-38.9,22.2-88.7,16.3-121.8-17.2L333.8,436.1z"/> +<path fill="#48A64A" d="M313.1,539.2v-82l57.2,58.2c32.9,33.7,38.5,84.7,16.8,124.1c-42.7-12.2-74-52.4-74-99.9V539.2z"/> +<path fill="#48A64A" d="M226.6,515.4l56.9-58l0.1,82.4c0,47.5-31.3,87.4-74,99.7c-21.6-39.5-15.8-90.4,17-123.9V515.4z"/> +<path fill="#48A64A" d="M182.4,436.1h80.4l-57,58.2c-32.8,33.5-83.1,39.3-121.7,17.3c12.1-43.7,51.4-75.5,98-75.5H182.4z"/> +</svg> diff --git a/lib/process-services/content-widget/attach-file-widget.component.html b/lib/process-services/content-widget/attach-file-widget.component.html new file mode 100644 index 0000000000..e15123e9a1 --- /dev/null +++ b/lib/process-services/content-widget/attach-file-widget.component.html @@ -0,0 +1,83 @@ +<div class="adf-attach-widget {{field.className}}" + [class.adf-invalid]="!field.isValid" + [class.adf-readonly]="field.readOnly"> + <label class="adf-label" [attr.for]="field.id">{{field.name}} + <span *ngIf="isRequired()">*</span> + </label> + <div class="adf-attach-widget-container"> + <div id="adf-attach-widget-simple-upload" *ngIf="isSimpleUploadButton() && isUploadButtonVisible()"> + <a mat-raised-button color="primary"> + {{ 'FORM.FIELD.UPLOAD' | translate }} + <mat-icon>file_upload</mat-icon> + <input #uploadFiles + [multiple]="multipleOption" + type="file" + [id]="field.id" + (change)="onFileChanged($event)" /> + </a> + </div> + <div class="adf-attach-widget__menu-upload" *ngIf="isUploadButtonVisible() && isMultipleSourceUpload()"> + <button mat-raised-button color="primary" [matMenuTriggerFor]="menu" [id]="field.id"> + {{ 'FORM.FIELD.UPLOAD' | translate }} + <mat-icon>attach_file</mat-icon> + </button> + <mat-menu #menu="matMenu" class="adf-attach-widget__menu-content"> + <button mat-menu-item (click)="uploadFile.click()" + id="attach-local-file" + *ngIf="isAllFileSourceSelected()"> + From HD + <mat-icon>file_upload</mat-icon> + <input #uploadFile + class="adf-attach-widget__input-type" + [multiple]="multipleOption" + type="file" + [id]="field.id" + (change)="onFileChanged($event)" /> + </button> + <button mat-menu-item + *ngIf="isDefinedSourceFolder()" + id="attach-{{field.params?.fileSource?.name}}" + (click)="openSelectDialogFromFileSource()"> + From {{field.params?.fileSource?.name}} + <mat-icon> + <img class="adf-attach-widget__image-logo" [src]="alfrescoLogoUrl"> + </mat-icon> + </button> + <div *ngIf="!isDefinedSourceFolder()"> + <button mat-menu-item *ngFor="let repo of repositoryList" + id="attach-{{repo?.name}}" + (click)="openSelectDialog(repo.id, repo.name)"> + From {{repo.name}} + <mat-icon> + <img class="adf-attach-widget__image-logo" [src]="alfrescoLogoUrl"> + </mat-icon> + </button> + </div> + </mat-menu> + </div> + </div> +</div> + +<div id="adf-attach-widget-readonly-list"> + <mat-list *ngIf="hasFile"> + <mat-list-item class="adf-attach-files-row" *ngFor="let file of field.value"> + <img mat-list-icon class="adf-attach-widget__icon" + [id]="'file-'+file.id+'-icon'" + [src]="getIcon(file.mimeType)" + [alt]="mimeTypeIcon" + (click)="fileClicked(file)" + (keyup.enter)="fileClicked(file)" + role="button" + tabindex="0"/> + <span matLine id="{{'file-'+file.id}}" (click)="fileClicked(file)" (keyup.enter)="fileClicked(file)" + role="button" tabindex="0" class="adf-file">{{file.name}}</span> + <button *ngIf="!field.readOnly" mat-icon-button [id]="'file-'+file.id+'-remove'" + (click)="removeFile(file);" (keyup.enter)="removeFile(file);"> + <mat-icon class="mat-24">highlight_off</mat-icon> + </button> + </mat-list-item> + </mat-list> +</div> + +<error-widget [error]="field.validationSummary"></error-widget> +<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget> diff --git a/lib/process-services/content-widget/attach-file-widget.component.scss b/lib/process-services/content-widget/attach-file-widget.component.scss new file mode 100644 index 0000000000..61b904af33 --- /dev/null +++ b/lib/process-services/content-widget/attach-file-widget.component.scss @@ -0,0 +1,73 @@ +.adf { + + &-attach-widget-container { + margin-bottom: 15px; + display: flex; + align-items: center; + + input { + cursor: pointer; + height: 100%; + right: 0; + opacity: 0; + position: absolute; + top: 0; + width: 300px; + z-index: 4; + } + } + + &-attach-widget__menu-upload { + display: flex; + align-items: center; + } + + &-attach-widget__input-type { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; + } + + &-attach-widget__image-logo{ + padding-left: 5px; + } + + &-attach-widget-repo-button{ + padding-left: 10px; + + .mat-button-wrapper { + display: inline; + } + + .mat-mini-fab.mat-accent { + background-color: inherit; + } + } + + &-attach-widget { + width: 100%; + word-break: break-all; + padding: 0.4375em 0; + border-top: 0.84375em solid transparent; + } + + &-attach-widget__icon { + padding: 6px; + float: left; + cursor: pointer; + } + + &-attach-widget__reset { + margin-top: -2px; + } + + &-attach-files-row { + .mat-line { + margin-bottom: 0px; + } + } + +} diff --git a/lib/process-services/content-widget/attach-file-widget.component.ts b/lib/process-services/content-widget/attach-file-widget.component.ts new file mode 100644 index 0000000000..f355ebbbf8 --- /dev/null +++ b/lib/process-services/content-widget/attach-file-widget.component.ts @@ -0,0 +1,142 @@ +/*! + * @license + * Copyright 2016 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, ViewEncapsulation, OnInit } from '@angular/core'; +import { + baseHost, + UploadWidgetComponent, + FormService, + LogService, + ThumbnailService, + ProcessContentService, + ActivitiContentService +} from '@alfresco/adf-core'; +import { ContentNodeDialogService } from '@alfresco/adf-content-services'; +import { MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { Observable } from 'rxjs/Observable'; + +@Component({ + selector: 'attach-widget', + templateUrl: './attach-file-widget.component.html', + styleUrls: ['./attach-file-widget.component.scss'], + host: baseHost, + encapsulation: ViewEncapsulation.None +}) +export class AttachFileWidgetComponent extends UploadWidgetComponent implements OnInit { + + alfrescoLogoUrl: string = '../assets/images/alfresco-flower.svg'; + repositoryList = []; + + constructor( + public formService: FormService, + private logger: LogService, + public thumbnails: ThumbnailService, + public processContentService: ProcessContentService, + private activitiContentService: ActivitiContentService, + private contentDialog: ContentNodeDialogService) { + super(formService, logger, thumbnails, processContentService); + } + + ngOnInit() { + if (this.field && + this.field.value && + this.field.value.length > 0) { + this.hasFile = true; + } + this.getMultipleFileParam(); + this.activitiContentService.getAlfrescoRepositories(null, true).subscribe((repoList) => { + this.repositoryList = repoList; + }); + } + + isFileSourceConfigured(): boolean { + return !!this.field.params && !!this.field.params.fileSource; + } + + isMultipleSourceUpload() { + return !this.field.readOnly && this.isFileSourceConfigured() && !this.isOnlyLocalSourceSelected(); + } + + isAllFileSourceSelected() { + return this.field.params && + this.field.params.fileSource && + this.field.params.fileSource.serviceId === 'all-file-sources'; + } + + isOnlyLocalSourceSelected() { + return this.field.params && + this.field.params.fileSource && + this.field.params.fileSource.serviceId === 'local-file'; + } + + isSimpleUploadButton() { + return this.isUploadButtonVisible() && + !this.isFileSourceConfigured() || + this.isOnlyLocalSourceSelected(); + } + + isUploadButtonVisible() { + return (!this.hasFile || this.multipleOption) && !this.field.readOnly; + } + + isDefinedSourceFolder() { + return !!this.field.params && + !!this.field.params.fileSource && + !!this.field.params.fileSource.selectedFolder; + } + + openSelectDialogFromFileSource() { + let params = this.field.params; + if (this.isDefinedSourceFolder()) { + this.contentDialog.openFileBrowseDialogByFolderId(params.fileSource.selectedFolder.pathId).subscribe( + (selections: MinimalNodeEntryEntity[]) => { + this.uploadFileFromShare(selections, + this.field.params.fileSource.selectedFolder.accountId, + this.field.params.fileSource.selectedFolder.siteId); + }); + } + } + + openSelectDialog(repoId: string, repoName: string) { + const accountIdentifier = 'alfresco-' + repoId + repoName; + this.contentDialog.openFileBrowseDialogBySite().subscribe( + (selections: MinimalNodeEntryEntity[]) => { + this.uploadFileFromShare(selections, accountIdentifier); + }); + } + + private uploadFileFromShare(fileNodeList: MinimalNodeEntryEntity[], accountId: string, siteId?: string) { + let filesSaved = []; + Observable.from(fileNodeList) + .mergeMap(node => + this.activitiContentService.applyAlfrescoNode(node, + siteId, + accountId) + ).subscribe((res) => { + filesSaved.push(res); + }, + (error) => { this.logger.error(error); }, + () => { + this.field.value = filesSaved; + this.field.json.value = filesSaved; + }); + this.hasFile = true; + } + +} diff --git a/lib/process-services/content-widget/attach-file-widget.components.spec.ts b/lib/process-services/content-widget/attach-file-widget.components.spec.ts new file mode 100644 index 0000000000..5f52021e8b --- /dev/null +++ b/lib/process-services/content-widget/attach-file-widget.components.spec.ts @@ -0,0 +1,243 @@ +/*! + * @license + * Copyright 2016 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { AttachFileWidgetComponent } from './attach-file-widget.component'; +import { + FormFieldModel, + FormModel, + FormFieldTypes, + FormService, + ProcessContentService, + ActivitiContentService, + AlfrescoApiService, + LogService, + ThumbnailService, + SitesService, + FormFieldMetadata +} from '@alfresco/adf-core'; +import { ContentNodeDialogService, DocumentListService } from '@alfresco/adf-content-services'; +import { MaterialModule } from '../material.module'; +import { Observable } from 'rxjs/Observable'; +import { MinimalNodeEntryEntity } from 'alfresco-js-api'; + +const fakeRepositoryListAnswer = [ + { + 'authorized': true, + 'serviceId': 'alfresco-9999-SHAREME', + 'metaDataAllowed': true, + 'name': 'SHAREME' + }, + { + 'authorized': true, + 'serviceId': 'alfresco-0000-GOKUSHARE', + 'metaDataAllowed': true, + 'name': 'GOKUSHARE' + }]; + +const onlyLocalParams = { + fileSource : { + serviceId: 'local-file' + } +}; + +const allSourceParams = { + fileSource : { + serviceId: 'all-file-sources' + } +}; + +const definedSourceParams = { + fileSource : { + serviceId: 'goku-sources', + name: 'pippo-baudo', + selectedFolder: { + accountId: 'goku-share-account-id' + } + } +}; + +const fakeMinimalNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> { + id: 'fake', + name: 'fake-name' +}; + +const fakePngAnswer = { + 'id': 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': 'image/png', + 'simpleType': 'image', + 'previewStatus': 'queued', + 'thumbnailStatus': 'queued' +}; + +describe('AttachFileWidgetComponent', () => { + + let widget: AttachFileWidgetComponent; + let fixture: ComponentFixture<AttachFileWidgetComponent>; + let element: HTMLInputElement; + let activitiContentService: ActivitiContentService; + let contentNodeDialogService: ContentNodeDialogService; + let processContentService: ProcessContentService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MaterialModule], + declarations: [AttachFileWidgetComponent], + providers: [ + FormService, + ProcessContentService, + ThumbnailService, + ActivitiContentService, + AlfrescoApiService, + LogService, + SitesService, + DocumentListService, + ContentNodeDialogService + ] + }).compileComponents().then(() => { + fixture = TestBed.createComponent(AttachFileWidgetComponent); + widget = fixture.componentInstance; + element = fixture.nativeElement; + activitiContentService = TestBed.get(ActivitiContentService); + contentNodeDialogService = TestBed.get(ContentNodeDialogService); + processContentService = TestBed.get(ProcessContentService); + }); + })); + + afterEach(() => { + fixture.destroy(); + }); + + it('should be able to create the widget', () => { + expect(widget).not.toBeNull(); + }); + + it('should show up as simple upload when is configured for only local files', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [] + }); + widget.field.id = 'simple-upload-button'; + widget.field.params = <FormFieldMetadata> onlyLocalParams; + fixture.detectChanges(); + expect(element.querySelector('#simple-upload-button')).not.toBeNull(); + }); + + it('should show up all the repository option on menu list', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [] + }); + widget.field.id = 'attach-file-attach'; + widget.field.params = <FormFieldMetadata> allSourceParams; + spyOn(activitiContentService, 'getAlfrescoRepositories').and.returnValue(Observable.of(fakeRepositoryListAnswer)); + fixture.detectChanges(); + + let attachButton: HTMLButtonElement = element.querySelector('#attach-file-attach'); + expect(attachButton).not.toBeNull(); + attachButton.click(); + fixture.detectChanges(); + + expect(fixture.debugElement.queryAll(By.css('#attach-local-file'))).not.toBeNull(); + expect(fixture.debugElement.queryAll(By.css('#attach-local-file'))).not.toBeUndefined(); + expect(fixture.debugElement.queryAll(By.css('#attach-SHAREME'))).not.toBeNull(); + expect(fixture.debugElement.queryAll(By.css('#attach-SHAREME'))).not.toBeUndefined(); + expect(fixture.debugElement.queryAll(By.css('#attach-GOKUSHARE'))).not.toBeNull(); + expect(fixture.debugElement.queryAll(By.css('#attach-GOKUSHARE'))).not.toBeUndefined(); + }); + + it('should be able to upload files coming from content node selector', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [] + }); + widget.field.id = 'attach-file-attach'; + widget.field.params = <FormFieldMetadata> allSourceParams; + spyOn(activitiContentService, 'getAlfrescoRepositories').and.returnValue(Observable.of(fakeRepositoryListAnswer)); + spyOn(activitiContentService, 'applyAlfrescoNode').and.returnValue(Observable.of(fakePngAnswer)); + spyOn(contentNodeDialogService, 'openFileBrowseDialogBySite').and.returnValue(Observable.of([fakeMinimalNode])); + fixture.detectChanges(); + + let attachButton: HTMLButtonElement = element.querySelector('#attach-file-attach'); + expect(attachButton).not.toBeNull(); + attachButton.click(); + fixture.detectChanges(); + fixture.debugElement.query(By.css('#attach-SHAREME')).nativeElement.click(); + fixture.detectChanges(); + + expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + }); + + it('should be able to upload files when a defined folder is selected', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [] + }); + widget.field.id = 'attach-file-attach'; + widget.field.params = <FormFieldMetadata> definedSourceParams; + spyOn(activitiContentService, 'getAlfrescoRepositories').and.returnValue(Observable.of(fakeRepositoryListAnswer)); + spyOn(activitiContentService, 'applyAlfrescoNode').and.returnValue(Observable.of(fakePngAnswer)); + spyOn(contentNodeDialogService, 'openFileBrowseDialogByFolderId').and.returnValue(Observable.of([fakeMinimalNode])); + fixture.detectChanges(); + + let attachButton: HTMLButtonElement = element.querySelector('#attach-file-attach'); + expect(attachButton).not.toBeNull(); + attachButton.click(); + fixture.detectChanges(); + fixture.debugElement.query(By.css('#attach-pippo-baudo')).nativeElement.click(); + fixture.detectChanges(); + + expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + }); + + it('should be able to upload files from local source', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [] + }); + widget.field.id = 'attach-file-attach'; + widget.field.params = <FormFieldMetadata> onlyLocalParams; + spyOn(processContentService, 'createTemporaryRawRelatedContent').and.returnValue(Observable.of(fakePngAnswer)); + fixture.detectChanges(); + + let inputDebugElement = fixture.debugElement.query(By.css('#attach-file-attach')); + inputDebugElement.triggerEventHandler('change', {target: {files: [fakePngAnswer]}}); + fixture.detectChanges(); + + expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + }); + + it('should display file list when field has value', () => { + widget.field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.UPLOAD, + value: [fakePngAnswer] + }); + widget.field.id = 'attach-file-attach'; + widget.field.params = <FormFieldMetadata> onlyLocalParams; + fixture.detectChanges(); + + expect(element.querySelector('#file-1155-icon')).not.toBeNull(); + }); + +}); diff --git a/lib/process-services/content-widget/content-widget.module.ts b/lib/process-services/content-widget/content-widget.module.ts new file mode 100644 index 0000000000..23febdf281 --- /dev/null +++ b/lib/process-services/content-widget/content-widget.module.ts @@ -0,0 +1,48 @@ +/*! + * @license + * Copyright 2016 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 { TranslateModule } from '@ngx-translate/core'; +import { MaterialModule } from '../material.module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { DataColumnModule, DataTableModule, FormModule } from '@alfresco/adf-core'; + +import { AttachFileWidgetComponent } from './attach-file-widget.component'; + +@NgModule({ + imports: [ + DataColumnModule, + DataTableModule, + FormsModule, + ReactiveFormsModule, + MaterialModule, + CommonModule, + TranslateModule, + FormModule + ], + entryComponents: [ + AttachFileWidgetComponent + ], + declarations: [ + AttachFileWidgetComponent + ], + exports: [ + AttachFileWidgetComponent + ] +}) +export class ContentWidgetModule {} diff --git a/lib/process-services/content-widget/index.ts b/lib/process-services/content-widget/index.ts new file mode 100644 index 0000000000..4c6ac1d58f --- /dev/null +++ b/lib/process-services/content-widget/index.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright 2016 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'; diff --git a/lib/process-services/content-widget/public-api.ts b/lib/process-services/content-widget/public-api.ts new file mode 100644 index 0000000000..01d0e88487 --- /dev/null +++ b/lib/process-services/content-widget/public-api.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright 2016 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 './attach-file-widget.component'; diff --git a/lib/process-services/material.module.ts b/lib/process-services/material.module.ts index daac2a4345..966f50cf82 100644 --- a/lib/process-services/material.module.ts +++ b/lib/process-services/material.module.ts @@ -21,7 +21,7 @@ import { MatChipsModule, MatDatepickerModule, MatDialogModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, - MatTooltipModule + MatTooltipModule, MatMenuModule } from '@angular/material'; export function modules() { @@ -30,7 +30,7 @@ export function modules() { MatCheckboxModule, MatDatepickerModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatOptionModule, MatRadioModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatProgressSpinnerModule, MatNativeDateModule, MatRippleModule, MatTooltipModule, - MatChipsModule + MatChipsModule, MatMenuModule ]; } diff --git a/lib/process-services/process-list/components/process-instance-tasks.component.ts b/lib/process-services/process-list/components/process-instance-tasks.component.ts index 71de634c58..710a501080 100644 --- a/lib/process-services/process-list/components/process-instance-tasks.component.ts +++ b/lib/process-services/process-list/components/process-instance-tasks.component.ts @@ -53,9 +53,6 @@ export class ProcessInstanceTasksComponent implements OnInit, OnChanges { message: string; processId: string; - // @ViewChild('dialog') - // dialog: any; - @ViewChild('startDialog') startDialog: any; diff --git a/lib/process-services/process-list/components/start-process.component.ts b/lib/process-services/process-list/components/start-process.component.ts index 230d3c3ca0..f20729e845 100644 --- a/lib/process-services/process-list/components/start-process.component.ts +++ b/lib/process-services/process-list/components/start-process.component.ts @@ -16,11 +16,14 @@ */ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; -import { StartFormComponent } from '@alfresco/adf-core'; +import { StartFormComponent + , FormRenderingService +} from '@alfresco/adf-core'; import { ProcessInstanceVariable } from '../models/process-instance-variable.model'; import { ProcessDefinitionRepresentation } from './../models/process-definition.model'; import { ProcessInstance } from './../models/process-instance.model'; import { ProcessService } from './../services/process.service'; +import { AttachFileWidgetComponent } from '../../content-widget'; @Component({ selector: 'adf-start-process', @@ -56,7 +59,9 @@ export class StartProcessInstanceComponent implements OnChanges { errorMessageId: string = ''; - constructor(private activitiProcess: ProcessService) { + constructor(private activitiProcess: ProcessService, + private formRenderingService: FormRenderingService) { + this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true); } ngOnChanges(changes: SimpleChanges) { diff --git a/lib/process-services/process-list/process-list.module.ts b/lib/process-services/process-list/process-list.module.ts index 86e59a5022..6dc219970d 100644 --- a/lib/process-services/process-list/process-list.module.ts +++ b/lib/process-services/process-list/process-list.module.ts @@ -27,6 +27,7 @@ import { CardViewModule, CardViewUpdateService, DataColumnModule, DataTableModul import { TaskListModule } from '../task-list/task-list.module'; import { PeopleModule } from '../people/people.module'; import { CommentsModule } from '../comments/comments.module'; +import { ContentWidgetModule } from '../content-widget/content-widget.module'; import { ProcessAuditDirective } from './components/process-audit.directive'; import { ProcessFiltersComponent } from './components/process-filters.component'; @@ -55,7 +56,8 @@ import { ProcessFilterService } from './services/process-filter.service'; DataColumnModule, DirectiveModule, PeopleModule, - CommentsModule + CommentsModule, + ContentWidgetModule ], declarations: [ ProcessInstanceListComponent, diff --git a/lib/process-services/task-list/components/no-task-detail-template.directive.spec.ts b/lib/process-services/task-list/components/no-task-detail-template.directive.spec.ts index f44a78655f..f74a7785c2 100644 --- a/lib/process-services/task-list/components/no-task-detail-template.directive.spec.ts +++ b/lib/process-services/task-list/components/no-task-detail-template.directive.spec.ts @@ -17,6 +17,7 @@ import { NoTaskDetailsTemplateDirective } from './no-task-detail-template.directive'; import { TaskDetailsComponent } from './task-details.component'; +import { FormRenderingService } from '@alfresco/adf-core'; describe('NoTaskDetailsTemplateDirective', () => { @@ -24,7 +25,7 @@ describe('NoTaskDetailsTemplateDirective', () => { let detailsComponent: TaskDetailsComponent; beforeEach(() => { - detailsComponent = new TaskDetailsComponent(null, null, null, null, null, null); + detailsComponent = new TaskDetailsComponent(null, null, null, new FormRenderingService(), null, null, null); component = new NoTaskDetailsTemplateDirective(detailsComponent); }); diff --git a/lib/process-services/task-list/components/task-details.component.ts b/lib/process-services/task-list/components/task-details.component.ts index 414838adfa..a77bad3239 100644 --- a/lib/process-services/task-list/components/task-details.component.ts +++ b/lib/process-services/task-list/components/task-details.component.ts @@ -16,7 +16,7 @@ */ import { PeopleProcessService, UserProcessModel } from '@alfresco/adf-core'; -import { AuthenticationService, CardViewUpdateService, ClickNotification, LogService, UpdateNotification } from '@alfresco/adf-core'; +import { AuthenticationService, CardViewUpdateService, ClickNotification, LogService, UpdateNotification, FormRenderingService } from '@alfresco/adf-core'; import { Component, EventEmitter, Input, @@ -35,6 +35,7 @@ import { TaskQueryRequestRepresentationModel } from '../models/filter.model'; import { TaskDetailsModel } from '../models/task-details.model'; import { TaskListService } from './../services/tasklist.service'; import { CommentsComponent } from '../../comments'; +import { AttachFileWidgetComponent } from '../../content-widget'; @Component({ selector: 'adf-task-details', @@ -141,9 +142,12 @@ export class TaskDetailsComponent implements OnInit, OnChanges { constructor(private taskListService: TaskListService, private authService: AuthenticationService, private peopleProcessService: PeopleProcessService, + private formRenderingService: FormRenderingService, private logService: LogService, private cardViewUpdateService: CardViewUpdateService, private dialog: MatDialog) { + + this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true); this.peopleSearch$ = new Observable<UserProcessModel[]>(observer => this.peopleSearchObserver = observer).share(); } diff --git a/lib/process-services/task-list/task-list.module.ts b/lib/process-services/task-list/task-list.module.ts index 7ed4c1bec0..ab8010e789 100644 --- a/lib/process-services/task-list/task-list.module.ts +++ b/lib/process-services/task-list/task-list.module.ts @@ -27,6 +27,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MaterialModule } from '../material.module'; import { PeopleModule } from '../people/people.module'; import { CommentsModule } from '../comments/comments.module'; +import { ContentWidgetModule } from '../content-widget/content-widget.module'; import { TaskUploadService } from './services/task-upload.service'; import { ProcessUploadService } from './services/process-upload.service'; import { TaskListService } from './services/tasklist.service'; @@ -56,7 +57,8 @@ import { TaskListComponent } from './components/task-list.component'; FormsModule, ReactiveFormsModule, PeopleModule, - CommentsModule + CommentsModule, + ContentWidgetModule ], declarations: [ NoTaskDetailsTemplateDirective,