mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-3110] Move upload button inside the node selector dialog (#5901)
* Open select dialog on all types, fetch destination folder from a relative path * Dialog UI refactor, multiple upload from local * Fix document list should automatically reload after upload * Remove not used ViewChild * Fix imports, read destination folder from form field * support different alias * Remove not needed property, reuse selection mode * Remove unused methods * Fix unit tests * * Added unit tests * Fixed failing unit tests * * Added unit for upload button * Skipped failing e2e * * Removed process-storage related code * * Removed unncessory model and code * * * Removed contentHost from formCloud model * * Skiped content-services e2e * Skip failing process e2e related to attachment Co-authored-by: sivakumar414ram <siva.kumar@muraai.com> Co-authored-by: maurizio vitale <maurizio.vitale@alfresco.com>
This commit is contained in:
@@ -25,8 +25,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry, SearchEntry } from '@alfresco/js-api';
|
||||
import {
|
||||
AlfrescoApiService, AuthenticationService, AppConfigService, AppConfigValues, ContentService, TranslationService,
|
||||
FileUploadEvent, FolderCreatedEvent, LogService, NotificationService,
|
||||
AlfrescoApiService, AuthenticationService, AppConfigService, AppConfigValues, ContentService, TranslationService, FolderCreatedEvent, LogService, NotificationService,
|
||||
UploadService, DataRow, UserPreferencesService,
|
||||
PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent, HighlightDirective,
|
||||
SharedLinksApiService
|
||||
@@ -47,7 +46,7 @@ import { VersionManagerDialogAdapterComponent } from './version-manager-dialog-a
|
||||
import { MetadataDialogAdapterComponent } from './metadata-dialog-adapter.component';
|
||||
import { Subject } from 'rxjs';
|
||||
import { PreviewService } from '../../services/preview.service';
|
||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
const DEFAULT_FOLDER_TO_SHOW = '-my-';
|
||||
|
||||
@@ -272,17 +271,6 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
this.uploadService.fileUploadComplete
|
||||
.pipe(
|
||||
debounceTime(300),
|
||||
takeUntil(this.onDestroy$)
|
||||
)
|
||||
.subscribe(value => this.onFileUploadEvent(value));
|
||||
|
||||
this.uploadService.fileUploadDeleted
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(value => this.onFileUploadEvent(value));
|
||||
|
||||
this.contentService.folderCreated
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(value => this.onFolderCreated(value));
|
||||
@@ -350,12 +338,6 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.errorMessage = null;
|
||||
}
|
||||
|
||||
onFileUploadEvent(event: FileUploadEvent) {
|
||||
if (event && event.file.options.parentId === this.documentList.currentFolderId) {
|
||||
this.documentList.reload();
|
||||
}
|
||||
}
|
||||
|
||||
onFolderCreated(event: FolderCreatedEvent) {
|
||||
this.logService.log('FOLDER CREATED');
|
||||
this.logService.log(event);
|
||||
|
@@ -52,6 +52,7 @@ Opens a [Content Node Selector](content-node-selector.component.md) in its own
|
||||
| ---- | ---- | ----------- |
|
||||
| select | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/Node.md)`[]>` | Emitted when the user has chosen an item. |
|
||||
| siteChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the select site changes. |
|
||||
| navigationChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<NodeEntryEvent>` | Emitted when the navigation changes. |
|
||||
|
||||
## Details
|
||||
|
||||
|
@@ -36,11 +36,10 @@ class MyComponent {
|
||||
- _outcome:_ `string` - [Form](../../../lib/process-services/src/lib/task-list/models/form.model.ts) outcome
|
||||
- _version:_ `number` - of the form
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskDetailsCloudModel`](../../../lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts)`>` - Updated task details
|
||||
- **createTemporaryRawRelatedContent**(file: `any`, nodeId: `string`, contentHost: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<any>`<br/>
|
||||
- **createTemporaryRawRelatedContent**(file: `any`, nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<any>`<br/>
|
||||
|
||||
- _file:_ `any` -
|
||||
- _nodeId:_ `string` -
|
||||
- _contentHost:_ `string` -
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<any>` -
|
||||
|
||||
- **getBasePath**(appName: `string`): `string`<br/>
|
||||
@@ -58,13 +57,6 @@ class MyComponent {
|
||||
- _formKey:_ `string` - key of the target task
|
||||
- _version:_ `number` - (Optional) Version of the form
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`FormContent`](../../../lib/process-services-cloud/src/lib/services/form-fields.interfaces.ts)`>` - Form definition
|
||||
- **getProcessStorageFolderTask**(appName: `string`, taskId: `string`, processInstanceId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ProcessStorageCloudModel`](../../../lib/process-services-cloud/src/lib/form/models/task-variable-cloud.model.ts)`>`<br/>
|
||||
|
||||
- _appName:_ `string` -
|
||||
- _taskId:_ `string` -
|
||||
- _processInstanceId:_ `string` -
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ProcessStorageCloudModel`](../../../lib/process-services-cloud/src/lib/form/models/task-variable-cloud.model.ts)`>` -
|
||||
|
||||
- **getTask**(appName: `string`, taskId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskDetailsCloudModel`](../../../lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts)`>`<br/>
|
||||
Gets details of a task
|
||||
- _appName:_ `string` - Name of the app
|
||||
|
@@ -4,6 +4,19 @@
|
||||
"C362240": "Include once ADF starts using ACS 7 , https://issues.alfresco.com/jira/browse/ADF-5182",
|
||||
"C362241": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182",
|
||||
"C362242": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182",
|
||||
"C362265": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182"
|
||||
"C362265": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182",
|
||||
"C311290": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C310358": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311285": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311287": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311288": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311289": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311292": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311293": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C311295": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C315292": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C260140": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C261160": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177",
|
||||
"C246534": "Include once process storage services removed, https://issues.alfresco.com/jira/browse/AAE-3177"
|
||||
}
|
||||
|
||||
|
@@ -71,7 +71,7 @@
|
||||
[allowDropFiles]="false"
|
||||
[sorting]="'server'"
|
||||
[where]="where"
|
||||
(folderChange)="onFolderChange()"
|
||||
(folderChange)="onFolderChange($event)"
|
||||
(ready)="onFolderLoaded()"
|
||||
(nodeSelected)="onCurrentSelection($event)"
|
||||
data-automation-id="content-node-selector-document-list">
|
||||
|
@@ -29,7 +29,7 @@ import { DocumentListService } from '../document-list/services/document-list.ser
|
||||
import { DocumentListComponent } from '../document-list/components/document-list.component';
|
||||
import { DropdownSitesComponent } from '../site-dropdown/sites-dropdown.component';
|
||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
||||
import { ShareDataRow } from '../document-list';
|
||||
import { NodeEntryEvent, ShareDataRow } from '../document-list';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
const ONE_FOLDER_RESULT = {
|
||||
@@ -59,6 +59,8 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
let sitesService: SitesService;
|
||||
let searchSpy: jasmine.Spy;
|
||||
let cnSearchSpy: jasmine.Spy;
|
||||
const fakeNodeEntry = new Node({ id: 'fakeId' });
|
||||
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
|
||||
|
||||
let _observer: Observer<NodePaging>;
|
||||
|
||||
@@ -271,7 +273,7 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
|
||||
tick(debounceSearch);
|
||||
|
||||
component.onFolderChange();
|
||||
component.onFolderChange(nodeEntryEvent);
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
@@ -309,7 +311,7 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onFolderChange();
|
||||
component.onFolderChange(nodeEntryEvent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const chosenNode = <Node> { path: { elements: [] } };
|
||||
@@ -700,7 +702,7 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onFolderChange();
|
||||
component.onFolderChange(nodeEntryEvent);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.clearSearch).toHaveBeenCalled();
|
||||
|
@@ -33,7 +33,7 @@ import { ImageResolver } from '../document-list/data/image-resolver.model';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
||||
import { ShareDataRow } from '../document-list';
|
||||
import { NodeEntryEvent, ShareDataRow } from '../document-list';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export type ValidationFunction = (entry: Node) => boolean;
|
||||
@@ -186,6 +186,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
select: EventEmitter<Node[]> = new EventEmitter<Node[]>();
|
||||
|
||||
/** Emitted when the navigation changes. */
|
||||
@Output()
|
||||
navigationChange: EventEmitter<NodeEntryEvent> = new EventEmitter<NodeEntryEvent>();
|
||||
|
||||
/** Emitted when the select site changes. */
|
||||
@Output()
|
||||
siteChange: EventEmitter<string> = new EventEmitter<string>();
|
||||
@@ -426,11 +430,12 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* Sets showingSearchResults state to be able to differentiate between search results or folder results
|
||||
*/
|
||||
onFolderChange(): void {
|
||||
onFolderChange($event: NodeEntryEvent): void {
|
||||
this.showingSearchResults = false;
|
||||
this.infiniteScroll = false;
|
||||
this.breadcrumbFolderTitle = null;
|
||||
this.clearSearch();
|
||||
this.navigationChange.emit($event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -36,4 +36,6 @@ export interface ContentNodeSelectorComponentData {
|
||||
showSearch?: boolean;
|
||||
showFilesInResult?: boolean;
|
||||
showDropdownSiteList?: boolean;
|
||||
showLocalUploadButton?: boolean;
|
||||
multipleUpload?: boolean;
|
||||
}
|
||||
|
@@ -21,11 +21,22 @@
|
||||
[showDropdownSiteList]="data?.showDropdownSiteList"
|
||||
[showFilesInResult]="data?.showFilesInResult"
|
||||
(select)="onSelect($event)"
|
||||
(siteChange)="onSiteChange($event)">
|
||||
(siteChange)="onSiteChange($event)"
|
||||
(navigationChange)="onNavigationChange($event)">
|
||||
</adf-content-node-selector-panel>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<mat-dialog-actions>
|
||||
<div>
|
||||
<adf-upload-button
|
||||
*ngIf="data?.showLocalUploadButton"
|
||||
[staticTitle]="'FORM.FIELD.UPLOAD' | translate "
|
||||
[multipleFiles]="isMultipleSelection()"
|
||||
[rootFolderId]="currentDirectoryId"
|
||||
(error)="onError($event)">
|
||||
</adf-upload-button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
mat-button
|
||||
(click)="close()"
|
||||
@@ -33,9 +44,10 @@
|
||||
</button>
|
||||
|
||||
<button mat-button
|
||||
[disabled]="!hasNodeSelected()"
|
||||
[disabled]="!chosenNode"
|
||||
class="adf-choose-action"
|
||||
(click)="onClick()"
|
||||
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
@@ -36,7 +36,8 @@
|
||||
padding: 8px;
|
||||
background-color: mat-color($background, background);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
color: mat-color($foreground, text, 0.72);
|
||||
|
||||
button {
|
||||
|
@@ -40,7 +40,8 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
||||
select: new EventEmitter<Node>(),
|
||||
rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name',
|
||||
imageResolver: () => 'piccolo',
|
||||
currentFolderId: 'cat-girl-nuku-nuku'
|
||||
currentFolderId: 'cat-girl-nuku-nuku',
|
||||
showLocalUploadButton: true
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
@@ -175,4 +176,22 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
||||
expect(titleElement.nativeElement.innerText).toBe('NODE_SELECTOR.CHOOSE_ITEM');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Upload button', () => {
|
||||
|
||||
it('should be able to show upload button if showLocalUploadButton set to true', () => {
|
||||
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button'));
|
||||
|
||||
expect(adfUploadButton).not.toBeNull();
|
||||
expect(adfUploadButton.nativeElement.innerText).toEqual('file_uploadFORM.FIELD.UPLOAD');
|
||||
});
|
||||
|
||||
it('should not be able to show upload button if showLocalUploadButton set to false', () => {
|
||||
component.data.showLocalUploadButton = false;
|
||||
fixture.detectChanges();
|
||||
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button span'));
|
||||
|
||||
expect(adfUploadButton).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -17,9 +17,10 @@
|
||||
|
||||
import { Component, Inject, ViewEncapsulation } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { TranslationService } from '@alfresco/adf-core';
|
||||
import { TranslationService, NotificationService } from '@alfresco/adf-core';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
||||
import { NodeEntryEvent } from '../document-list/components/node.event';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-node-selector',
|
||||
@@ -28,17 +29,19 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ContentNodeSelectorComponent {
|
||||
|
||||
title: string;
|
||||
action: string;
|
||||
buttonActionName: string;
|
||||
chosenNode: Node[];
|
||||
currentDirectoryId: string;
|
||||
|
||||
constructor(private translation: TranslationService,
|
||||
private notificationService: NotificationService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
|
||||
this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE';
|
||||
this.buttonActionName = `NODE_SELECTOR.${this.action}`;
|
||||
this.title = data.title;
|
||||
this.currentDirectoryId = data.currentFolderId;
|
||||
}
|
||||
|
||||
close() {
|
||||
@@ -49,14 +52,14 @@ export class ContentNodeSelectorComponent {
|
||||
this.chosenNode = nodeList;
|
||||
}
|
||||
|
||||
hasNodeSelected(): boolean {
|
||||
return this.chosenNode?.length > 0;
|
||||
}
|
||||
|
||||
onSiteChange(siteTitle: string) {
|
||||
this.updateTitle(siteTitle);
|
||||
}
|
||||
|
||||
onNavigationChange(pathElement: NodeEntryEvent) {
|
||||
this.currentDirectoryId = pathElement.value.id;
|
||||
}
|
||||
|
||||
onClick(): void {
|
||||
this.data.select.next(this.chosenNode);
|
||||
this.data.select.complete();
|
||||
@@ -71,4 +74,12 @@ export class ContentNodeSelectorComponent {
|
||||
getTitleTranslation(action: string, name: string): string {
|
||||
return this.translation.instant(`NODE_SELECTOR.${action}_ITEM`, { name: this.translation.instant(name) });
|
||||
}
|
||||
|
||||
isMultipleSelection(): boolean {
|
||||
return this.data.selectionMode === 'multiple';
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
this.notificationService.showError(error);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import { BreadcrumbModule } from '../breadcrumb/breadcrumb.module';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
import { DocumentListModule } from '../document-list/document-list.module';
|
||||
import { NameLocationCellComponent } from './name-location-cell/name-location-cell.component';
|
||||
import { UploadModule } from '../upload/upload.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -37,7 +38,8 @@ import { NameLocationCellComponent } from './name-location-cell/name-location-ce
|
||||
MaterialModule,
|
||||
SitesDropdownModule,
|
||||
BreadcrumbModule,
|
||||
DocumentListModule
|
||||
DocumentListModule,
|
||||
UploadModule
|
||||
],
|
||||
exports: [
|
||||
ContentNodeSelectorPanelComponent,
|
||||
|
@@ -45,7 +45,8 @@ import {
|
||||
RequestPaginationModel,
|
||||
AlfrescoApiService,
|
||||
UserPreferenceValues,
|
||||
LockService
|
||||
LockService,
|
||||
UploadService
|
||||
} from '@alfresco/adf-core';
|
||||
|
||||
import { Node, NodeEntry, NodePaging, Pagination } from '@alfresco/js-api';
|
||||
@@ -60,7 +61,7 @@ import { NavigableComponentInterface } from '../../breadcrumb/navigable-componen
|
||||
import { RowFilter } from '../data/row-filter.model';
|
||||
import { DocumentListService } from '../services/document-list.service';
|
||||
import { DocumentLoaderNode } from '../models/document-folder.model';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-document-list',
|
||||
@@ -337,6 +338,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
private appConfig: AppConfigService,
|
||||
private userPreferencesService: UserPreferencesService,
|
||||
private contentService: ContentService,
|
||||
private uploadService: UploadService,
|
||||
private thumbnailService: ThumbnailService,
|
||||
private alfrescoApiService: AlfrescoApiService,
|
||||
private lockService: LockService) {
|
||||
@@ -346,6 +348,18 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
.subscribe(pagSize => {
|
||||
this.maxItems = this._pagination.maxItems = pagSize;
|
||||
});
|
||||
|
||||
this.uploadService.fileUploadComplete
|
||||
.pipe(
|
||||
debounceTime(300),
|
||||
takeUntil(this.onDestroy$))
|
||||
.subscribe(() => this.reload());
|
||||
|
||||
this.uploadService.fileUploadDeleted
|
||||
.pipe(
|
||||
debounceTime(300),
|
||||
takeUntil(this.onDestroy$))
|
||||
.subscribe(() => this.reload());
|
||||
}
|
||||
|
||||
getContextActions(node: NodeEntry) {
|
||||
|
@@ -80,6 +80,7 @@
|
||||
"NODE_SELECTOR": {
|
||||
"CANCEL": "Cancel",
|
||||
"CHOOSE": "Select",
|
||||
"ATTACH": "Attach",
|
||||
"CHOOSE_ITEM": "Select content to attach from '{{ name }}'",
|
||||
"COPY": "Copy",
|
||||
"COPY_ITEM": "Copy '{{ name }}' to...",
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<!--Single Files Upload-->
|
||||
<button *ngIf="!multipleFiles"
|
||||
[disabled]="isButtonDisabled()"
|
||||
mat-raised-button color="primary"
|
||||
mat-button
|
||||
(click)="uploadSingleFile.click()">
|
||||
<mat-icon>file_upload</mat-icon>
|
||||
<span id="upload-single-file-label"
|
||||
@@ -27,7 +27,7 @@
|
||||
<!--Multiple Files Upload-->
|
||||
<button *ngIf="multipleFiles"
|
||||
[disabled]="isButtonDisabled()"
|
||||
mat-raised-button color="primary"
|
||||
mat-button
|
||||
(click)="uploadMultipleFiles.click()">
|
||||
<mat-icon>file_upload</mat-icon>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<div *ngIf="uploadFolders">
|
||||
<button
|
||||
[disabled]="isButtonDisabled()"
|
||||
mat-raised-button color="primary"
|
||||
mat-button
|
||||
(click)="uploadFolders.click()">
|
||||
<mat-icon>file_upload</mat-icon>
|
||||
<span id="uploadFolder-label"
|
||||
|
@@ -24,4 +24,5 @@ export interface FormFieldFileSource {
|
||||
name: string;
|
||||
selectedFolder: FormFieldSelectedFolder;
|
||||
serviceId: string;
|
||||
destinationFolderPath: string;
|
||||
}
|
||||
|
@@ -69,7 +69,6 @@ export class FormModel {
|
||||
|
||||
json: any;
|
||||
nodeId: string;
|
||||
contentHost: string;
|
||||
values: FormValues = {};
|
||||
tabs: TabModel[] = [];
|
||||
fields: FormWidgetModel[] = [];
|
||||
|
@@ -36,7 +36,8 @@
|
||||
"AT_LEAST_LONG": "Enter at least {{ minLength }} characters",
|
||||
"NO_LONGER_THAN": "Enter no more than {{ maxLength }} characters"
|
||||
},
|
||||
"FILE_ALREADY_UPLOADED": "A file with the same name is already uploaded."
|
||||
"FILE_ALREADY_UPLOADED": "A file with the same name is already uploaded.",
|
||||
"ATTACH":"Attach"
|
||||
},
|
||||
"FORM_RENDERER": {
|
||||
"NAMELESS_TASK": "Nameless task"
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
import { Component, DebugElement, SimpleChange, NgModule, Injector, ComponentFactoryResolver, ViewChild } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import {
|
||||
AppConfigService,
|
||||
@@ -370,45 +370,6 @@ describe('FormCloudComponent', () => {
|
||||
expect(formComponent.getFormByTaskId).toHaveBeenCalledWith(appName, taskId, 1);
|
||||
});
|
||||
|
||||
it('should call the process storage to retrieve the folder with only the taskId', fakeAsync(() => {
|
||||
spyOn(formCloudService, 'getTaskForm').and.returnValue(of(cloudFormMock));
|
||||
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
|
||||
spyOn(formCloudService, 'getProcessStorageFolderTask')
|
||||
.and.returnValue(of({ nodeId: '123', path: '/a/path/type', type: 'fakeType' }));
|
||||
const taskId = '<task id>';
|
||||
const appName = 'test-app';
|
||||
formComponent.appName = appName;
|
||||
formComponent.taskId = taskId;
|
||||
|
||||
const change = new SimpleChange(null, appName, true);
|
||||
formComponent.ngOnChanges({ 'appName': change });
|
||||
tick();
|
||||
|
||||
expect(formCloudService.getProcessStorageFolderTask).toHaveBeenCalledWith(appName, taskId, undefined);
|
||||
expect(formComponent.form.nodeId).toBe('123');
|
||||
expect(formComponent.form.contentHost).toBe('/a/path/type');
|
||||
}));
|
||||
|
||||
it('should call the process storage to retrieve the folder with taskId and processInstanceId', fakeAsync(() => {
|
||||
spyOn(formCloudService, 'getTaskForm').and.returnValue(of(cloudFormMock));
|
||||
spyOn(formCloudService, 'getTaskVariables').and.returnValue(of([]));
|
||||
spyOn(formCloudService, 'getProcessStorageFolderTask')
|
||||
.and.returnValue(of({ nodeId: '123', path: '/a/path/type', type: 'fakeType' }));
|
||||
const taskId = '<task id>';
|
||||
const processInstanceId = 'i-am-the-process-instance-id';
|
||||
const appName = 'test-app';
|
||||
formComponent.appName = appName;
|
||||
formComponent.taskId = taskId;
|
||||
formComponent.processInstanceId = processInstanceId;
|
||||
|
||||
const change = new SimpleChange(null, 'new-app-name', true);
|
||||
formComponent.ngOnChanges({ 'appName': change });
|
||||
tick();
|
||||
expect(formCloudService.getProcessStorageFolderTask).toHaveBeenCalledWith(appName, taskId, processInstanceId);
|
||||
expect(formComponent.form.nodeId).toBe('123');
|
||||
expect(formComponent.form.contentHost).toBe('/a/path/type');
|
||||
}));
|
||||
|
||||
it('should reload form definition by form id on binding changes', () => {
|
||||
spyOn(formComponent, 'getFormById').and.stub();
|
||||
const formId = '123';
|
||||
|
@@ -28,12 +28,10 @@ import {
|
||||
FormOutcomeModel,
|
||||
WidgetVisibilityService,
|
||||
FormService,
|
||||
NotificationService,
|
||||
FORM_FIELD_VALIDATORS,
|
||||
FormFieldValidator,
|
||||
FormValues,
|
||||
FormModel,
|
||||
AppConfigService,
|
||||
ContentLinkModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { FormCloudService } from '../services/form-cloud.service';
|
||||
@@ -106,9 +104,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
|
||||
constructor(protected formCloudService: FormCloudService,
|
||||
protected formService: FormService,
|
||||
private notificationService: NotificationService,
|
||||
protected visibilityService: WidgetVisibilityService,
|
||||
private appConfigService: AppConfigService) {
|
||||
protected visibilityService: WidgetVisibilityService) {
|
||||
super();
|
||||
|
||||
this.formService.formContentClicked
|
||||
@@ -123,7 +119,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
|
||||
if (appName && appName.currentValue) {
|
||||
if (this.taskId) {
|
||||
this.getFormDefinitionWithFolderTask(this.appName, this.taskId, this.processInstanceId);
|
||||
this.getFormByTaskId(appName.currentValue, this.taskId, this.appVersion);
|
||||
} else if (this.formId) {
|
||||
this.getFormById(appName.currentValue, this.formId, this.appVersion);
|
||||
}
|
||||
@@ -200,7 +196,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
parsedForm.validateForm();
|
||||
this.form = parsedForm;
|
||||
this.form.nodeId = '-my-';
|
||||
this.form.contentHost = this.appConfigService.get('ecmHost');
|
||||
this.onFormLoaded(this.form);
|
||||
resolve(this.form);
|
||||
},
|
||||
@@ -230,7 +225,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
parsedForm.validateForm();
|
||||
this.form = parsedForm;
|
||||
this.form.nodeId = '-my-';
|
||||
this.form.contentHost = this.appConfigService.get('ecmHost');
|
||||
this.onFormLoaded(this.form);
|
||||
},
|
||||
(error) => {
|
||||
@@ -239,31 +233,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
);
|
||||
}
|
||||
|
||||
getFormDefinitionWithFolderTask(appName: string, taskId: string, processInstanceId: string) {
|
||||
this.getFormDefinitionWithFolder(appName, taskId, processInstanceId);
|
||||
}
|
||||
|
||||
async getFormDefinitionWithFolder(appName: string, taskId: string, processInstanceId: string) {
|
||||
try {
|
||||
await this.getFormByTaskId(appName, taskId, this.appVersion);
|
||||
|
||||
const hasUploadWidget = (<any> this.form).hasUpload;
|
||||
if (hasUploadWidget) {
|
||||
try {
|
||||
const processStorageCloudModel = await this.formCloudService.getProcessStorageFolderTask(appName, taskId, processInstanceId).toPromise();
|
||||
this.form.nodeId = processStorageCloudModel.nodeId;
|
||||
this.form.contentHost = processStorageCloudModel.path;
|
||||
} catch (error) {
|
||||
this.notificationService.openSnackMessage('The content repo is not configured');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.notificationService.openSnackMessage('Form service an error occour');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
saveTaskForm() {
|
||||
if (this.form && this.appName && this.taskId) {
|
||||
this.formCloudService
|
||||
|
@@ -5,35 +5,11 @@
|
||||
<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)="onAttachFileChanged($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>
|
||||
<div class="adf-attach-widget__menu-upload" *ngIf="isUploadButtonVisible()">
|
||||
<button (click)="openSelectDialog()" mat-raised-button color="primary" [id]="field.id">
|
||||
{{ 'FORM.FIELD.ATTACH' | translate }}
|
||||
<mat-icon>{{getWidgetIcon()}}</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu" class="adf-attach-widget__menu-content">
|
||||
<div *ngIf="isContentSourceSelected()">
|
||||
<button mat-menu-item
|
||||
id="attach-{{field.params?.fileSource?.name}}"
|
||||
(click)="uploadFileFromCS()">
|
||||
{{field.params?.fileSource?.name}}
|
||||
<mat-icon>
|
||||
<img alt="alfresco" class="adf-attach-widget__image-logo"
|
||||
src="./assets/images/alfresco-flower.svg">
|
||||
</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -27,7 +27,8 @@ import {
|
||||
FormFieldTypes,
|
||||
FormFieldMetadata,
|
||||
FormService,
|
||||
DownloadService
|
||||
DownloadService,
|
||||
NotificationService
|
||||
} from '@alfresco/adf-core';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
@@ -44,6 +45,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
let element: HTMLInputElement;
|
||||
let contentCloudNodeSelectorService: ContentCloudNodeSelectorService;
|
||||
let processCloudContentService: ProcessCloudContentService;
|
||||
let notificationService: NotificationService;
|
||||
let formService: FormService;
|
||||
let downloadService: DownloadService;
|
||||
|
||||
@@ -80,6 +82,22 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const allSourceParams = {
|
||||
fileSource: {
|
||||
name: 'all file sources',
|
||||
serviceId: 'all-file-sources',
|
||||
destinationFolderPath: '-root-/myfiles'
|
||||
}
|
||||
};
|
||||
|
||||
const allSourceParamsWithWrongPath = {
|
||||
fileSource: {
|
||||
name: 'all file sources',
|
||||
serviceId: 'all-file-sources',
|
||||
destinationFolderPath: 'mock-folder'
|
||||
}
|
||||
};
|
||||
|
||||
const fakeMinimalNode: Node = <Node> {
|
||||
id: 'fake',
|
||||
name: 'fake-name',
|
||||
@@ -88,25 +106,9 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const fakeLocalPngAnswer = {
|
||||
id: 1155,
|
||||
nodeId: 1155,
|
||||
name: 'a_png_file.png',
|
||||
created: '2017-07-25T17:17:37.099Z',
|
||||
createdBy: {
|
||||
id: 1001,
|
||||
firstName: 'Admin',
|
||||
lastName: 'admin',
|
||||
email: 'admin'
|
||||
},
|
||||
relatedContent: false,
|
||||
contentAvailable: true,
|
||||
link: false,
|
||||
mimeType: 'image/png',
|
||||
simpleType: 'image',
|
||||
previewStatus: 'queued',
|
||||
thumbnailStatus: 'queued'
|
||||
};
|
||||
const mockNodeId = new Promise(function (resolve) {
|
||||
resolve('mock-node-id');
|
||||
});
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@@ -128,23 +130,20 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
ContentCloudNodeSelectorService
|
||||
);
|
||||
formService = TestBed.inject(FormService);
|
||||
notificationService = TestBed.inject(NotificationService);
|
||||
}));
|
||||
|
||||
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', async(() => {
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
widget.field.id = 'simple-upload-button';
|
||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
||||
widget.field.params = <FormFieldMetadata> allSourceParams;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(
|
||||
@@ -168,7 +167,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be able to attach files coming from content selector', async(() => {
|
||||
it('should be able to attach files coming from content selector', async() => {
|
||||
spyOn(
|
||||
contentCloudNodeSelectorService,
|
||||
'openUploadFileDialog'
|
||||
@@ -180,47 +179,108 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
widget.field.id = 'attach-file-alfresco';
|
||||
widget.field.params = <FormFieldMetadata> contentSourceParam;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const attachButton: HTMLButtonElement = element.querySelector(
|
||||
'#attach-file-alfresco'
|
||||
);
|
||||
expect(attachButton).not.toBeNull();
|
||||
attachButton.click();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement
|
||||
.query(By.css('#attach-mock-alf-content'))
|
||||
.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#file-fake-icon')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
}));
|
||||
await fixture.whenStable();
|
||||
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||
|
||||
it('should be able to upload files from local source', async(() => {
|
||||
expect(attachButton).not.toBeNull();
|
||||
|
||||
attachButton.click();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
const attachedFileName = fixture.debugElement.query(By.css('.adf-file'));
|
||||
const fileIcon = element.querySelector('#file-fake-icon');
|
||||
|
||||
expect(attachedFileName.nativeElement.innerText).toEqual('fake-name');
|
||||
expect(fileIcon).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should be able to attach files coming from all files source', async() => {
|
||||
spyOn(contentCloudNodeSelectorService, 'fetchNodeIdFromRelativePath').and.returnValue(mockNodeId);
|
||||
spyOn(
|
||||
contentCloudNodeSelectorService,
|
||||
'openUploadFileDialog'
|
||||
).and.returnValue(of([fakeMinimalNode]));
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
widget.field.id = 'attach-file-local';
|
||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
||||
widget.field.id = 'attach-file-alfresco';
|
||||
widget.field.params = <FormFieldMetadata> allSourceParams;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||
|
||||
expect(attachButton).not.toBeNull();
|
||||
|
||||
attachButton.click();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
const attachedFileName = fixture.debugElement.query(By.css('.adf-file'));
|
||||
const fileIcon = element.querySelector('#file-fake-icon');
|
||||
|
||||
expect(attachedFileName.nativeElement.innerText).toEqual('fake-name');
|
||||
expect(fileIcon).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should be able to fetch nodeId if destinationFolderPtah defined ', async() => {
|
||||
const fetchNodeIdFromRelativePathSpy = spyOn(contentCloudNodeSelectorService, 'fetchNodeIdFromRelativePath').and.returnValue(mockNodeId);
|
||||
spyOn(
|
||||
processCloudContentService,
|
||||
'createTemporaryRawRelatedContent'
|
||||
).and.returnValue(of(fakeLocalPngAnswer));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const inputDebugElement = fixture.debugElement.query(
|
||||
By.css('#attach-file-local')
|
||||
);
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [fakeLocalPngAnswer] }
|
||||
contentCloudNodeSelectorService,
|
||||
'openUploadFileDialog'
|
||||
).and.returnValue(of([fakeMinimalNode]));
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
widget.field.id = 'attach-file-alfresco';
|
||||
widget.field.params = <FormFieldMetadata> allSourceParams;
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
||||
await fixture.whenStable();
|
||||
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||
|
||||
expect(attachButton).not.toBeNull();
|
||||
|
||||
attachButton.click();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
const alias = '-root-';
|
||||
const opt = { relativePath: '/myfiles' };
|
||||
expect(fetchNodeIdFromRelativePathSpy).toHaveBeenCalledWith(alias, opt);
|
||||
expect(widget.field.params.fileSource.destinationFolderPath).toBe('-root-/myfiles');
|
||||
expect(widget.rootNodeId).toEqual('mock-node-id');
|
||||
});
|
||||
|
||||
it('should be able to show error notification if destinationFolderPtah wrong/undefined', async() => {
|
||||
const fetchNodeIdFromRelativePathSpy = spyOn(contentCloudNodeSelectorService, 'fetchNodeIdFromRelativePath').and.returnValue(mockNodeId);
|
||||
spyOn(
|
||||
contentCloudNodeSelectorService,
|
||||
'openUploadFileDialog'
|
||||
).and.returnValue(of([fakeMinimalNode]));
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
const showErrorSpy = spyOn(notificationService, 'showError').and.callThrough();
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
widget.field.id = 'attach-file-alfresco';
|
||||
widget.field.params = <FormFieldMetadata> allSourceParamsWithWrongPath;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||
|
||||
expect(attachButton).not.toBeNull();
|
||||
|
||||
attachButton.click();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fetchNodeIdFromRelativePathSpy).not.toHaveBeenCalled();
|
||||
expect(showErrorSpy).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display file list when field has value', async(() => {
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
@@ -249,38 +309,6 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to enable multiple file upload', async(() => {
|
||||
const files = [fakeLocalPngAnswer, { ...fakeLocalPngAnswer, id: 1166, nodeId: 1166, name: 'second_png_file.png' }];
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
id: 'attach-file',
|
||||
name: 'Upload',
|
||||
value: [],
|
||||
params: { onlyLocalParams, multiple: true }
|
||||
});
|
||||
spyOn(processCloudContentService, 'createTemporaryRawRelatedContent')
|
||||
.and.returnValues(of(files[0]), of(files[1]));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const inputDebugElement = fixture.debugElement.query(By.css('#attach-file'));
|
||||
expect(inputDebugElement.nativeElement.multiple).toBe(true);
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [ files[0] ] }
|
||||
});
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#file-1155-icon')).toBeDefined();
|
||||
let name: HTMLElement = element.querySelector('span[id="file-1155"]');
|
||||
expect(name.innerText).toEqual('a_png_file.png');
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [ files[1] ] }
|
||||
});
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#file-1166-icon')).toBeDefined();
|
||||
name = element.querySelector('span[id="file-1166"]');
|
||||
expect(name.innerText).toEqual('second_png_file.png');
|
||||
});
|
||||
}));
|
||||
|
||||
describe('when is readonly', () => {
|
||||
|
||||
it('should show empty list message when there are no file', async(() => {
|
||||
@@ -332,46 +360,44 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
});
|
||||
|
||||
describe('when a file is uploaded', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(async() => {
|
||||
spyOn(
|
||||
contentCloudNodeSelectorService,
|
||||
'openUploadFileDialog'
|
||||
).and.returnValue(of([fakeMinimalNode]));
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: []
|
||||
});
|
||||
widget.field.id = 'attach-file-attach';
|
||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
||||
spyOn(
|
||||
processCloudContentService,
|
||||
'createTemporaryRawRelatedContent'
|
||||
).and.returnValue(of(fakeLocalPngAnswer));
|
||||
widget.field.id = 'attach-file-alfresco';
|
||||
widget.field.params = <FormFieldMetadata> contentSourceParam;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const inputDebugElement = fixture.debugElement.query(
|
||||
By.css('#attach-file-attach')
|
||||
);
|
||||
inputDebugElement.triggerEventHandler('change', {
|
||||
target: { files: [fakeLocalPngAnswer] }
|
||||
});
|
||||
await fixture.whenStable();
|
||||
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||
|
||||
expect(attachButton).not.toBeNull();
|
||||
|
||||
attachButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
||||
await fixture.whenStable();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should remove file when remove is clicked', (done) => {
|
||||
fixture.detectChanges();
|
||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(By.css('#file-1155-option-menu'))
|
||||
fixture.debugElement.query(By.css('#file-fake-option-menu'))
|
||||
.nativeElement
|
||||
);
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
const removeOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(By.css('#file-1155-remove'))
|
||||
fixture.debugElement.query(By.css('#file-fake-remove'))
|
||||
.nativeElement
|
||||
);
|
||||
removeOption.click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenRenderingDone().then(() => {
|
||||
expect(element.querySelector('#file-1155-icon')).toBeNull();
|
||||
expect(element.querySelector('#file-fake-icon')).toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -384,7 +410,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(By.css('#file-1155-option-menu'))
|
||||
fixture.debugElement.query(By.css('#file-fake-option-menu'))
|
||||
.nativeElement
|
||||
);
|
||||
|
||||
@@ -392,7 +418,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const downloadOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(By.css('#file-1155-download-file'))
|
||||
fixture.debugElement.query(By.css('#file-fake-download-file'))
|
||||
.nativeElement
|
||||
);
|
||||
|
||||
@@ -409,7 +435,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
|
||||
formService.formContentClicked.subscribe(
|
||||
(fileClicked: any) => {
|
||||
expect(fileClicked.nodeId).toBe(1155);
|
||||
expect(fileClicked.nodeId).toBe('fake');
|
||||
done();
|
||||
}
|
||||
);
|
||||
@@ -417,14 +443,14 @@ describe('AttachFileCloudWidgetComponent', () => {
|
||||
fixture.detectChanges();
|
||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(
|
||||
By.css('#file-1155-option-menu')
|
||||
By.css('#file-fake-option-menu')
|
||||
).nativeElement
|
||||
);
|
||||
menuButton.click();
|
||||
fixture.detectChanges();
|
||||
const showOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||
fixture.debugElement.query(
|
||||
By.css('#file-1155-show-file')
|
||||
By.css('#file-fake-show-file')
|
||||
).nativeElement
|
||||
);
|
||||
showOption.click();
|
||||
|
@@ -23,7 +23,8 @@ import {
|
||||
LogService,
|
||||
ThumbnailService,
|
||||
NotificationService,
|
||||
ContentLinkModel
|
||||
ContentLinkModel,
|
||||
TranslationService
|
||||
} from '@alfresco/adf-core';
|
||||
import { Node, RelatedContentRepresentation } from '@alfresco/js-api';
|
||||
import { ContentCloudNodeSelectorService } from '../../../services/content-cloud-node-selector.service';
|
||||
@@ -49,9 +50,9 @@ import { UploadCloudWidgetComponent } from './upload-cloud.widget';
|
||||
})
|
||||
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
||||
implements OnInit {
|
||||
static ACS_SERVICE = 'alfresco-content';
|
||||
|
||||
typeId = 'AttachFileCloudWidgetComponent';
|
||||
rootNodeId = '-my-';
|
||||
|
||||
constructor(
|
||||
formService: FormService,
|
||||
@@ -59,35 +60,18 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
||||
thumbnails: ThumbnailService,
|
||||
processCloudContentService: ProcessCloudContentService,
|
||||
notificationService: NotificationService,
|
||||
private contentNodeSelectorService: ContentCloudNodeSelectorService
|
||||
private contentNodeSelectorService: ContentCloudNodeSelectorService,
|
||||
private translationService: TranslationService
|
||||
) {
|
||||
super(formService, thumbnails, processCloudContentService, notificationService, logger);
|
||||
}
|
||||
|
||||
isFileSourceConfigured(): boolean {
|
||||
return !!this.field.params && !!this.field.params.fileSource;
|
||||
}
|
||||
|
||||
isMultipleSourceUpload(): boolean {
|
||||
return (
|
||||
!this.field.readOnly &&
|
||||
this.isFileSourceConfigured() &&
|
||||
!this.isOnlyLocalSourceSelected()
|
||||
);
|
||||
}
|
||||
|
||||
isOnlyLocalSourceSelected(): boolean {
|
||||
isAlfrescoAndLocal(): boolean {
|
||||
return (
|
||||
this.field.params &&
|
||||
this.field.params.fileSource &&
|
||||
this.field.params.fileSource.serviceId === 'local-file'
|
||||
);
|
||||
}
|
||||
|
||||
isSimpleUploadButton(): boolean {
|
||||
return (
|
||||
(this.isUploadButtonVisible() && !this.isFileSourceConfigured()) ||
|
||||
this.isOnlyLocalSourceSelected()
|
||||
this.field.params.fileSource.serviceId === 'all-file-sources'
|
||||
|| this.field.params.fileSource.serviceId === 'local-file'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,23 +79,31 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
||||
return (!this.hasFile || this.multipleOption) && !this.field.readOnly;
|
||||
}
|
||||
|
||||
onAttachFileChanged(event: any) {
|
||||
this.onFileChanged(event);
|
||||
}
|
||||
|
||||
onRemoveAttachFile(file: File | RelatedContentRepresentation | Node) {
|
||||
this.removeFile(file);
|
||||
}
|
||||
|
||||
uploadFileFromCS() {
|
||||
this.openSelectDialog();
|
||||
}
|
||||
|
||||
openSelectDialog() {
|
||||
async openSelectDialog() {
|
||||
const selectedMode = this.field.params.multiple ? 'multiple' : 'single';
|
||||
|
||||
if (this.isAlfrescoAndLocal()) {
|
||||
const destinationFolderPath = this.field.params.fileSource.destinationFolderPath;
|
||||
const alias = this.getAliasFromDestinationFolderPath(destinationFolderPath);
|
||||
const opts = {
|
||||
relativePath: this.getRelativePathFromDestinationFolderPath(destinationFolderPath)
|
||||
};
|
||||
|
||||
if (alias && opts && opts.relativePath) {
|
||||
await this.contentNodeSelectorService.fetchNodeIdFromRelativePath(alias, opts).then((nodeId: string) => {
|
||||
this.rootNodeId = nodeId;
|
||||
});
|
||||
} else {
|
||||
const errorMessage = this.translationService.instant('ADF_CLOUD_TASK_FORM.ERROR.INVALID_DESTINATION_FOLDER_PATH');
|
||||
this.notificationService.showError(errorMessage);
|
||||
}
|
||||
}
|
||||
this.contentNodeSelectorService
|
||||
.openUploadFileDialog(this.field.form.contentHost, '-my-', selectedMode)
|
||||
.openUploadFileDialog(this.rootNodeId, selectedMode, this.isAlfrescoAndLocal())
|
||||
.subscribe((selections: Node[]) => {
|
||||
selections.forEach(node => (node['isExternal'] = true));
|
||||
const selectionWithoutDuplication = this.removeExistingSelection(selections);
|
||||
@@ -119,29 +111,31 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
||||
});
|
||||
}
|
||||
|
||||
getAliasFromDestinationFolderPath(destinationFolderPath: string): string {
|
||||
const startOfRelativePathIndex = destinationFolderPath.indexOf('/');
|
||||
return destinationFolderPath.substring(0, startOfRelativePathIndex);
|
||||
}
|
||||
|
||||
getRelativePathFromDestinationFolderPath(destinationFolderPath: string): string {
|
||||
const startOfRelativePathIndex = destinationFolderPath.indexOf('/');
|
||||
return destinationFolderPath.substring(startOfRelativePathIndex, destinationFolderPath.length);
|
||||
}
|
||||
|
||||
removeExistingSelection(selections: Node[]) {
|
||||
const existingNode: Node[] = [...this.field.value || []];
|
||||
return selections.filter(opt => !existingNode.some( (node) => node.id === opt.id));
|
||||
}
|
||||
|
||||
isContentSourceSelected(): boolean {
|
||||
return (
|
||||
this.field.params &&
|
||||
this.field.params.fileSource &&
|
||||
this.field.params.fileSource.serviceId ===
|
||||
AttachFileCloudWidgetComponent.ACS_SERVICE
|
||||
);
|
||||
}
|
||||
|
||||
downloadContent(file: Node): void {
|
||||
this.processCloudContentService.downloadFile(
|
||||
file.id,
|
||||
this.field.form.contentHost
|
||||
);
|
||||
this.processCloudContentService.downloadFile(file.id);
|
||||
}
|
||||
|
||||
onAttachFileClicked(nodeSelector: any) {
|
||||
nodeSelector.nodeId = nodeSelector.id;
|
||||
this.fileClicked(new ContentLinkModel(nodeSelector));
|
||||
}
|
||||
|
||||
getWidgetIcon(): string {
|
||||
return this.isAlfrescoAndLocal() ? 'file_upload' : 'attach_file';
|
||||
}
|
||||
}
|
||||
|
@@ -124,9 +124,7 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
|
||||
}
|
||||
|
||||
private uploadRawContent(file: File): Observable<Node> {
|
||||
return this.processCloudContentService.createTemporaryRawRelatedContent(
|
||||
file, this.field.form.nodeId, this.field.form.contentHost
|
||||
);
|
||||
return this.processCloudContentService.createTemporaryRawRelatedContent(file, this.field.form.nodeId);
|
||||
}
|
||||
|
||||
getMultipleFileParam() {
|
||||
|
@@ -23,16 +23,3 @@ export class TaskVariableCloud {
|
||||
this.value = obj.value || null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ProcessStorageCloudModel {
|
||||
|
||||
nodeId: string;
|
||||
path: string;
|
||||
type: string;
|
||||
|
||||
constructor(obj) {
|
||||
this.nodeId = obj.nodeId || null;
|
||||
this.path = obj.path || null;
|
||||
this.type = obj.type || null;
|
||||
}
|
||||
}
|
||||
|
@@ -32,30 +32,35 @@ export class ContentCloudNodeSelectorService {
|
||||
private dialog: MatDialog) {
|
||||
}
|
||||
|
||||
openUploadFileDialog(contentHost: string, currentFolderId?: string, selectionMode?: string): Observable<Node[]> {
|
||||
const changedConfig = this.apiService.lastConfig;
|
||||
changedConfig.provider = 'ALL';
|
||||
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
|
||||
this.apiService.getInstance().setConfig(changedConfig);
|
||||
openUploadFileDialog(currentFolderId?: string, selectionMode?: string, showLocalUploadButton?: boolean): Observable<Node[]> {
|
||||
const select = new Subject<Node[]>();
|
||||
select.subscribe({
|
||||
complete: this.close.bind(this)
|
||||
});
|
||||
const data = <ContentNodeSelectorComponentData> {
|
||||
title: 'Select a file',
|
||||
actionName: 'Choose',
|
||||
actionName: 'Attach',
|
||||
currentFolderId,
|
||||
restrictRootToCurrentFolderId: true,
|
||||
select,
|
||||
selectionMode,
|
||||
isSelectionValid: (entry: Node) => entry.isFile,
|
||||
showFilesInResult: true
|
||||
showFilesInResult: true,
|
||||
showDropdownSiteList: false,
|
||||
showLocalUploadButton
|
||||
};
|
||||
|
||||
this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
||||
return select;
|
||||
}
|
||||
|
||||
async fetchNodeIdFromRelativePath(alias: string, opts: { relativePath: string }): Promise<string> {
|
||||
let nodeId = '';
|
||||
await this.apiService.getInstance().node.getNode(alias, opts).then(node => {
|
||||
nodeId = node.entry.id;
|
||||
});
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
private openContentNodeDialog(data: ContentNodeSelectorComponentData, currentPanelClass: string, chosenWidth: string) {
|
||||
this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: currentPanelClass, width: chosenWidth });
|
||||
}
|
||||
|
@@ -183,45 +183,6 @@ describe('Form Cloud service', () => {
|
||||
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('POST');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should fetch process storage folder with process instance id and task id', (done) => {
|
||||
oauth2Auth.callCustomApi.and.returnValue(Promise.resolve({
|
||||
nodeId: 'fake-node-id-really-long',
|
||||
path: 'path/to/node/id',
|
||||
type: 'nodeType'
|
||||
}));
|
||||
|
||||
service.getProcessStorageFolderTask(appName, taskId, processInstanceId).subscribe((result) => {
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toBeNull();
|
||||
expect(result.nodeId).toBe('fake-node-id-really-long');
|
||||
expect(result.path).toBe('path/to/node/id');
|
||||
expect(result.type).toBe('nodeType');
|
||||
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/process-storage/v1/folders/${processInstanceId}/${taskId}`)).toBeTruthy();
|
||||
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch process storage folder with task id only', (done) => {
|
||||
oauth2Auth.callCustomApi.and.returnValue(Promise.resolve({
|
||||
nodeId: 'fake-node-id-really-long',
|
||||
path: 'path/to/node/id',
|
||||
type: 'nodeType'
|
||||
}));
|
||||
|
||||
service.getProcessStorageFolderTask(appName, taskId, null).subscribe((result) => {
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toBeNull();
|
||||
expect(result.nodeId).toBe('fake-node-id-really-long');
|
||||
expect(result.path).toBe('path/to/node/id');
|
||||
expect(result.type).toBe('nodeType');
|
||||
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/process-storage/v1/folders/${taskId}`)).toBeTruthy();
|
||||
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -27,7 +27,7 @@ import { Observable, from } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
|
||||
import { CompleteFormRepresentation } from '@alfresco/js-api';
|
||||
import { TaskVariableCloud, ProcessStorageCloudModel } from '../models/task-variable-cloud.model';
|
||||
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
|
||||
import { BaseCloudService } from '../../services/base-cloud.service';
|
||||
import { FormContent } from '../../services/form-fields.interfaces';
|
||||
|
||||
@@ -151,16 +151,6 @@ export class FormCloudService extends BaseCloudService {
|
||||
);
|
||||
}
|
||||
|
||||
getProcessStorageFolderTask(appName: string, taskId: string, processInstanceId: string): Observable<ProcessStorageCloudModel> {
|
||||
const apiUrl = this.buildFolderTask(appName, taskId, processInstanceId);
|
||||
|
||||
return this.get(apiUrl).pipe(
|
||||
map((res: any) => {
|
||||
return new ProcessStorageCloudModel(res);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variables of a task.
|
||||
* @param appName Name of the app
|
||||
@@ -237,10 +227,4 @@ export class FormCloudService extends BaseCloudService {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private buildFolderTask(appName: string, taskId: string, processInstanceId: string): string {
|
||||
return processInstanceId
|
||||
? `${this.getBasePath(appName)}/process-storage/v1/folders/${processInstanceId}/${taskId}`
|
||||
: `${this.getBasePath(appName)}/process-storage/v1/folders/${taskId}`;
|
||||
}
|
||||
}
|
||||
|
@@ -39,10 +39,8 @@ export class ProcessCloudContentService {
|
||||
|
||||
createTemporaryRawRelatedContent(
|
||||
file: File,
|
||||
nodeId: string,
|
||||
contentHost: string
|
||||
nodeId: string
|
||||
): Observable<Node> {
|
||||
this.updateConfig(contentHost);
|
||||
|
||||
return from(
|
||||
this.apiService
|
||||
@@ -59,8 +57,7 @@ export class ProcessCloudContentService {
|
||||
);
|
||||
}
|
||||
|
||||
getRawContentNode(nodeId: string, contentHost: string): Observable<Blob> {
|
||||
this.updateConfig(contentHost);
|
||||
getRawContentNode(nodeId: string): Observable<Blob> {
|
||||
return this.contentService.getNodeContent(nodeId);
|
||||
}
|
||||
|
||||
@@ -68,8 +65,7 @@ export class ProcessCloudContentService {
|
||||
this.contentService.downloadBlob(blob, fileName);
|
||||
}
|
||||
|
||||
async downloadFile(nodeId: string, contentHost: string) {
|
||||
this.updateConfig(contentHost);
|
||||
async downloadFile(nodeId: string) {
|
||||
|
||||
const ticket = await this.getAuthTicket();
|
||||
const url = this.contentService.getContentUrl(nodeId, true, ticket);
|
||||
@@ -88,18 +84,6 @@ export class ProcessCloudContentService {
|
||||
return '';
|
||||
}
|
||||
|
||||
private updateConfig(contentHost: string) {
|
||||
const changedConfig = this.apiService.lastConfig;
|
||||
|
||||
changedConfig.provider = 'ALL';
|
||||
|
||||
if (contentHost) {
|
||||
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
|
||||
}
|
||||
|
||||
this.apiService.getInstance().setConfig(changedConfig);
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
this.logService.error(error);
|
||||
return throwError(error || 'Server error');
|
||||
|
@@ -266,6 +266,9 @@
|
||||
"CLAIM": "CLAIM",
|
||||
"UNCLAIM": "RELEASE"
|
||||
}
|
||||
},
|
||||
"ERROR": {
|
||||
"INVALID_DESTINATION_FOLDER_PATH": "Invalid destination folder path"
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user