mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-325] added attach folder widget (#2831)
[ADF-325] added new attach folder widget
This commit is contained in:
@@ -106,7 +106,7 @@ describe('ContentNodeDialogService', () => {
|
||||
expect(materialDialog.open).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should be able to open the dialog using the first user site', fakeAsync(() => {
|
||||
it('should be able to open the dialog for files 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();
|
||||
@@ -114,6 +114,14 @@ describe('ContentNodeDialogService', () => {
|
||||
expect(materialDialog.open).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should be able to open the dialog for folder using the first user site', fakeAsync(() => {
|
||||
spyOn(sitesService, 'getSites').and.returnValue(Observable.of(fakeSiteList));
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(fakeNode));
|
||||
service.openFolderBrowseDialogBySite().subscribe();
|
||||
tick();
|
||||
expect(materialDialog.open).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should be able to close the material dialog', () => {
|
||||
service.close();
|
||||
expect(materialDialog.closeAll).toHaveBeenCalled();
|
||||
|
@@ -48,6 +48,19 @@ export class ContentNodeDialogService {
|
||||
});
|
||||
}
|
||||
|
||||
openFolderBrowseDialogBySite(): Observable<MinimalNodeEntryEntity[]> {
|
||||
return this.siteService.getSites().switchMap((response: SitePaging) => {
|
||||
return this.openFolderBrowseDialogByFolderId(response.list.entries[0].entry.guid);
|
||||
});
|
||||
}
|
||||
|
||||
openFolderBrowseDialogByFolderId(folderNodeId: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
return Observable.fromPromise(this.documentListService.getFolderNode(folderNodeId))
|
||||
.switchMap((node: MinimalNodeEntryEntity) => {
|
||||
return this.openUploadFolderDialog('Choose', node);
|
||||
});
|
||||
}
|
||||
|
||||
openCopyMoveDialog(action: string, contentEntry: MinimalNodeEntryEntity, permission?: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
if (this.contentService.hasPermission(contentEntry, permission)) {
|
||||
|
||||
@@ -74,6 +87,26 @@ export class ContentNodeDialogService {
|
||||
}
|
||||
}
|
||||
|
||||
openUploadFolderDialog(action: string, contentEntry: MinimalNodeEntryEntity): Observable<MinimalNodeEntryEntity[]> {
|
||||
const select = new Subject<MinimalNodeEntryEntity[]>();
|
||||
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.hasPermissionOnNodeFolder.bind(this),
|
||||
rowFilter : this.rowFilter.bind(this, contentEntry.id),
|
||||
select: select
|
||||
};
|
||||
|
||||
this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
||||
return select;
|
||||
}
|
||||
|
||||
openUploadFileDialog(action: string, contentEntry: MinimalNodeEntryEntity): Observable<MinimalNodeEntryEntity[]> {
|
||||
const select = new Subject<MinimalNodeEntryEntity[]>();
|
||||
select.subscribe({
|
||||
@@ -120,6 +153,14 @@ export class ContentNodeDialogService {
|
||||
return entry.isFile;
|
||||
}
|
||||
|
||||
private hasPermissionOnNodeFolder(entry: MinimalNodeEntryEntity): boolean {
|
||||
return this.isNodeFolder(entry) && this.contentService.hasPermission(entry, 'create');
|
||||
}
|
||||
|
||||
private isNodeFolder(entry: MinimalNodeEntryEntity): boolean {
|
||||
return entry.isFolder;
|
||||
}
|
||||
|
||||
private hasEntityCreatePermission(entry: MinimalNodeEntryEntity): boolean {
|
||||
return this.contentService.hasPermission(entry, 'create');
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ import { CardViewUpdateService } from './services/card-view-update.service';
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
MatDatetimepickerModule,
|
||||
MatNativeDatetimeModule
|
||||
MatNativeDatetimeModule
|
||||
],
|
||||
declarations: [
|
||||
CardViewComponent,
|
||||
@@ -69,7 +69,7 @@ import { CardViewUpdateService } from './services/card-view-update.service';
|
||||
CardViewBoolItemComponent,
|
||||
CardViewDateItemComponent,
|
||||
CardViewMapItemComponent,
|
||||
CardViewTextItemComponent
|
||||
CardViewTextItemComponent
|
||||
],
|
||||
exports: [
|
||||
CardViewComponent,
|
||||
|
@@ -455,7 +455,7 @@ export class ViewerComponent implements OnChanges {
|
||||
this.urlFileContent = this.apiService.contentApi.getRenditionUrl(nodeId, 'pdf');
|
||||
} else if (status === 'NOT_CREATED') {
|
||||
try {
|
||||
await this.renditionService.convert(nodeId, 'pdf').toPromise()
|
||||
await this.renditionService.convert(nodeId, 'pdf').toPromise();
|
||||
this.viewerType = 'pdf';
|
||||
this.urlFileContent = this.apiService.contentApi.getRenditionUrl(nodeId, 'pdf');
|
||||
} catch {
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<div class="adf-attach-folder-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-folder-widget-container">
|
||||
<div *ngIf="hasFolder" class="adf-attach-folder-result">
|
||||
<mat-icon>folder</mat-icon>
|
||||
<div class="adf-attach-folder-files-row">
|
||||
<span matLine id="{{'folder-'+field?.id}}"
|
||||
role="button" tabindex="0" class="adf-folder">{{selectedFolderName}}</span>
|
||||
<button *ngIf="!field.readOnly" mat-icon-button [id]="'folder-'+field?.id+'-remove'"
|
||||
(click)="removeFolder();">
|
||||
<mat-icon class="mat-24">highlight_off</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-row" *ngIf="!hasFolder && !field.readOnly">
|
||||
<button mat-raised-button
|
||||
color="primary"
|
||||
(click)="openSelectDialogFromFileSource()"
|
||||
[id]="'folder-'+field?.id+'-button'">
|
||||
{{ 'FORM.FIELD.UPLOAD' | translate }}
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<error-widget [error]="field.validationSummary"></error-widget>
|
||||
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
|
||||
</div>
|
@@ -0,0 +1,39 @@
|
||||
.adf {
|
||||
|
||||
&-attach-folder-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-folder-widget {
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
padding: 0.4375em 0;
|
||||
border-top: 0.84375em solid transparent;
|
||||
}
|
||||
|
||||
&-attach-folder-files-row {
|
||||
padding-left: 8px;
|
||||
.mat-line {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&-attach-folder-result {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,169 @@
|
||||
/*!
|
||||
* @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 { AttachFolderWidgetComponent } from './attach-folder-widget.component';
|
||||
import {
|
||||
FormFieldModel,
|
||||
FormModel,
|
||||
FormService,
|
||||
AlfrescoApiService,
|
||||
LogService,
|
||||
ThumbnailService,
|
||||
SitesService,
|
||||
NodesApiService
|
||||
} 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 fakeMinimalNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {
|
||||
id: 'fake',
|
||||
name: 'fake-name'
|
||||
};
|
||||
|
||||
const definedSourceParams = {
|
||||
folderSource : {
|
||||
serviceId: 'goofy-sources',
|
||||
name: 'pippo-baudo',
|
||||
selectedFolder: {
|
||||
accountId: 'goku-share-account-id',
|
||||
pathId: 'fake-pippo-baudo-id'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('AttachFolderWidgetComponent', () => {
|
||||
|
||||
let widget: AttachFolderWidgetComponent;
|
||||
let fixture: ComponentFixture<AttachFolderWidgetComponent>;
|
||||
let element: HTMLInputElement;
|
||||
let contentNodeDialogService: ContentNodeDialogService;
|
||||
let nodeService: NodesApiService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MaterialModule],
|
||||
declarations: [AttachFolderWidgetComponent],
|
||||
providers: [
|
||||
FormService,
|
||||
ThumbnailService,
|
||||
AlfrescoApiService,
|
||||
LogService,
|
||||
SitesService,
|
||||
DocumentListService,
|
||||
ContentNodeDialogService,
|
||||
NodesApiService
|
||||
]
|
||||
}).compileComponents().then(() => {
|
||||
fixture = TestBed.createComponent(AttachFolderWidgetComponent);
|
||||
widget = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
contentNodeDialogService = TestBed.get(ContentNodeDialogService);
|
||||
nodeService = TestBed.get(NodesApiService);
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should be able to create the widget', () => {
|
||||
expect(widget).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should be rendered correctly', () => {
|
||||
expect(widget).not.toBeNull();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: 'select-folder',
|
||||
id: 'fake-widget',
|
||||
value: null
|
||||
});
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#folder-fake-widget-button')).toBeDefined();
|
||||
expect(element.querySelector('#folder-fake-widget-button')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should show the folder selected by content node', async(() => {
|
||||
spyOn(contentNodeDialogService, 'openFolderBrowseDialogBySite').and.returnValue(Observable.of([fakeMinimalNode]));
|
||||
expect(widget).not.toBeNull();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: 'select-folder',
|
||||
id: 'fake-widget',
|
||||
value: null
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.query(By.css('#folder-fake-widget-button')).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('#folder-fake-widget')).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show the folder selected by content node opening on a configured folder', async(() => {
|
||||
spyOn(contentNodeDialogService, 'openFolderBrowseDialogByFolderId').and.returnValue(Observable.of([fakeMinimalNode]));
|
||||
expect(widget).not.toBeNull();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: 'select-folder',
|
||||
id: 'fake-widget',
|
||||
value: null,
|
||||
params: definedSourceParams
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.query(By.css('#folder-fake-widget-button')).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('#folder-fake-widget')).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should retrieve the node information on init', async(() => {
|
||||
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeMinimalNode));
|
||||
expect(widget).not.toBeNull();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: 'select-folder',
|
||||
id: 'fake-widget',
|
||||
value: 'fake-pippo-baudo-id'
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('#folder-fake-widget')).not.toBeNull();
|
||||
expect(element.querySelector('#folder-fake-widget-button')).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should remove the folder via the remove button', async(() => {
|
||||
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeMinimalNode));
|
||||
expect(widget).not.toBeNull();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: 'select-folder',
|
||||
id: 'fake-widget',
|
||||
value: 'fake-pippo-baudo-id'
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('#folder-fake-widget')).not.toBeNull();
|
||||
expect(element.querySelector('#folder-fake-widget-button')).toBeNull();
|
||||
fixture.debugElement.query(By.css('#folder-fake-widget-remove')).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#folder-fake-widget')).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* @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,
|
||||
WidgetComponent,
|
||||
FormService,
|
||||
NodesApiService
|
||||
} from '@alfresco/adf-core';
|
||||
import { ContentNodeDialogService } from '@alfresco/adf-content-services';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'attach-folder-widget',
|
||||
templateUrl: './attach-folder-widget.component.html',
|
||||
styleUrls: ['./attach-folder-widget.component.scss'],
|
||||
host: baseHost,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AttachFolderWidgetComponent extends WidgetComponent implements OnInit {
|
||||
|
||||
hasFolder: boolean = false;
|
||||
selectedFolderName: string = '';
|
||||
|
||||
constructor(private contentDialog: ContentNodeDialogService,
|
||||
public formService: FormService,
|
||||
private nodeService: NodesApiService) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.field &&
|
||||
this.field.value) {
|
||||
this.hasFolder = true;
|
||||
this.nodeService.getNode(this.field.value).subscribe((node: MinimalNodeEntryEntity) => {
|
||||
this.selectedFolderName = node.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDefinedSourceFolder(): boolean {
|
||||
return !!this.field.params &&
|
||||
!!this.field.params.folderSource &&
|
||||
!!this.field.params.folderSource.selectedFolder;
|
||||
}
|
||||
|
||||
openSelectDialogFromFileSource() {
|
||||
let params = this.field.params;
|
||||
if (this.isDefinedSourceFolder()) {
|
||||
this.contentDialog.openFolderBrowseDialogByFolderId(params.folderSource.selectedFolder.pathId).subscribe(
|
||||
(selections: MinimalNodeEntryEntity[]) => {
|
||||
this.selectedFolderName = selections[0].name;
|
||||
this.field.value = selections[0].id;
|
||||
this.hasFolder = true;
|
||||
});
|
||||
} else {
|
||||
this.contentDialog.openFolderBrowseDialogBySite().subscribe(
|
||||
(selections: MinimalNodeEntryEntity[]) => {
|
||||
this.selectedFolderName = selections[0].name;
|
||||
this.field.value = selections[0].id;
|
||||
this.hasFolder = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
removeFolder() {
|
||||
this.field.value = null;
|
||||
this.selectedFolderName = '';
|
||||
this.hasFolder = false;
|
||||
}
|
||||
|
||||
}
|
@@ -23,6 +23,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { DataColumnModule, DataTableModule, FormModule } from '@alfresco/adf-core';
|
||||
|
||||
import { AttachFileWidgetComponent } from './attach-file-widget.component';
|
||||
import { AttachFolderWidgetComponent } from './attach-folder-widget.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -36,13 +37,16 @@ import { AttachFileWidgetComponent } from './attach-file-widget.component';
|
||||
FormModule
|
||||
],
|
||||
entryComponents: [
|
||||
AttachFileWidgetComponent
|
||||
AttachFileWidgetComponent,
|
||||
AttachFolderWidgetComponent
|
||||
],
|
||||
declarations: [
|
||||
AttachFileWidgetComponent
|
||||
AttachFileWidgetComponent,
|
||||
AttachFolderWidgetComponent
|
||||
],
|
||||
exports: [
|
||||
AttachFileWidgetComponent
|
||||
AttachFileWidgetComponent,
|
||||
AttachFolderWidgetComponent
|
||||
]
|
||||
})
|
||||
export class ContentWidgetModule {}
|
||||
|
@@ -16,3 +16,4 @@
|
||||
*/
|
||||
|
||||
export * from './attach-file-widget.component';
|
||||
export * from './attach-folder-widget.component';
|
||||
|
@@ -16,14 +16,12 @@
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { StartFormComponent
|
||||
, FormRenderingService
|
||||
} 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';
|
||||
import { AttachFileWidgetComponent, AttachFolderWidgetComponent } from '../../content-widget';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-start-process',
|
||||
@@ -62,6 +60,7 @@ export class StartProcessInstanceComponent implements OnChanges {
|
||||
constructor(private activitiProcess: ProcessService,
|
||||
private formRenderingService: FormRenderingService) {
|
||||
this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true);
|
||||
this.formRenderingService.setComponentTypeResolver('select-folder', () => AttachFolderWidgetComponent, true);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
|
@@ -35,7 +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';
|
||||
import { AttachFileWidgetComponent, AttachFolderWidgetComponent } from '../../content-widget';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-task-details',
|
||||
@@ -147,6 +147,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
private dialog: MatDialog) {
|
||||
|
||||
this.formRenderingService.setComponentTypeResolver('select-folder', () => AttachFolderWidgetComponent, true);
|
||||
this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true);
|
||||
this.peopleSearch$ = new Observable<UserProcessModel[]>(observer => this.peopleSearchObserver = observer).share();
|
||||
}
|
||||
|
Reference in New Issue
Block a user