mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-09-17 14:21:29 +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 { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry, SearchEntry } from '@alfresco/js-api';
|
import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry, SearchEntry } from '@alfresco/js-api';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService, AuthenticationService, AppConfigService, AppConfigValues, ContentService, TranslationService,
|
AlfrescoApiService, AuthenticationService, AppConfigService, AppConfigValues, ContentService, TranslationService, FolderCreatedEvent, LogService, NotificationService,
|
||||||
FileUploadEvent, FolderCreatedEvent, LogService, NotificationService,
|
|
||||||
UploadService, DataRow, UserPreferencesService,
|
UploadService, DataRow, UserPreferencesService,
|
||||||
PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent, HighlightDirective,
|
PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent, HighlightDirective,
|
||||||
SharedLinksApiService
|
SharedLinksApiService
|
||||||
@@ -47,7 +46,7 @@ import { VersionManagerDialogAdapterComponent } from './version-manager-dialog-a
|
|||||||
import { MetadataDialogAdapterComponent } from './metadata-dialog-adapter.component';
|
import { MetadataDialogAdapterComponent } from './metadata-dialog-adapter.component';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { PreviewService } from '../../services/preview.service';
|
import { PreviewService } from '../../services/preview.service';
|
||||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
const DEFAULT_FOLDER_TO_SHOW = '-my-';
|
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
|
this.contentService.folderCreated
|
||||||
.pipe(takeUntil(this.onDestroy$))
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
.subscribe(value => this.onFolderCreated(value));
|
.subscribe(value => this.onFolderCreated(value));
|
||||||
@@ -350,12 +338,6 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileUploadEvent(event: FileUploadEvent) {
|
|
||||||
if (event && event.file.options.parentId === this.documentList.currentFolderId) {
|
|
||||||
this.documentList.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFolderCreated(event: FolderCreatedEvent) {
|
onFolderCreated(event: FolderCreatedEvent) {
|
||||||
this.logService.log('FOLDER CREATED');
|
this.logService.log('FOLDER CREATED');
|
||||||
this.logService.log(event);
|
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. |
|
| 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. |
|
| 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
|
## Details
|
||||||
|
|
||||||
|
@@ -36,11 +36,10 @@ class MyComponent {
|
|||||||
- _outcome:_ `string` - [Form](../../../lib/process-services/src/lib/task-list/models/form.model.ts) outcome
|
- _outcome:_ `string` - [Form](../../../lib/process-services/src/lib/task-list/models/form.model.ts) outcome
|
||||||
- _version:_ `number` - of the form
|
- _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
|
- **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` -
|
- _file:_ `any` -
|
||||||
- _nodeId:_ `string` -
|
- _nodeId:_ `string` -
|
||||||
- _contentHost:_ `string` -
|
|
||||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<any>` -
|
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<any>` -
|
||||||
|
|
||||||
- **getBasePath**(appName: `string`): `string`<br/>
|
- **getBasePath**(appName: `string`): `string`<br/>
|
||||||
@@ -58,13 +57,6 @@ class MyComponent {
|
|||||||
- _formKey:_ `string` - key of the target task
|
- _formKey:_ `string` - key of the target task
|
||||||
- _version:_ `number` - (Optional) Version of the form
|
- _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
|
- **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/>
|
- **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
|
Gets details of a task
|
||||||
- _appName:_ `string` - Name of the app
|
- _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",
|
"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",
|
"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",
|
"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"
|
[allowDropFiles]="false"
|
||||||
[sorting]="'server'"
|
[sorting]="'server'"
|
||||||
[where]="where"
|
[where]="where"
|
||||||
(folderChange)="onFolderChange()"
|
(folderChange)="onFolderChange($event)"
|
||||||
(ready)="onFolderLoaded()"
|
(ready)="onFolderLoaded()"
|
||||||
(nodeSelected)="onCurrentSelection($event)"
|
(nodeSelected)="onCurrentSelection($event)"
|
||||||
data-automation-id="content-node-selector-document-list">
|
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 { DocumentListComponent } from '../document-list/components/document-list.component';
|
||||||
import { DropdownSitesComponent } from '../site-dropdown/sites-dropdown.component';
|
import { DropdownSitesComponent } from '../site-dropdown/sites-dropdown.component';
|
||||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
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';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
const ONE_FOLDER_RESULT = {
|
const ONE_FOLDER_RESULT = {
|
||||||
@@ -59,6 +59,8 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
let sitesService: SitesService;
|
let sitesService: SitesService;
|
||||||
let searchSpy: jasmine.Spy;
|
let searchSpy: jasmine.Spy;
|
||||||
let cnSearchSpy: jasmine.Spy;
|
let cnSearchSpy: jasmine.Spy;
|
||||||
|
const fakeNodeEntry = new Node({ id: 'fakeId' });
|
||||||
|
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
|
||||||
|
|
||||||
let _observer: Observer<NodePaging>;
|
let _observer: Observer<NodePaging>;
|
||||||
|
|
||||||
@@ -271,7 +273,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
|
|
||||||
component.onFolderChange();
|
component.onFolderChange(nodeEntryEvent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||||
expect(breadcrumb).not.toBeNull();
|
expect(breadcrumb).not.toBeNull();
|
||||||
@@ -309,7 +311,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onFolderChange();
|
component.onFolderChange(nodeEntryEvent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const chosenNode = <Node> { path: { elements: [] } };
|
const chosenNode = <Node> { path: { elements: [] } };
|
||||||
@@ -700,7 +702,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
tick();
|
tick();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onFolderChange();
|
component.onFolderChange(nodeEntryEvent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.clearSearch).toHaveBeenCalled();
|
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 { ContentNodeSelectorService } from './content-node-selector.service';
|
||||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
||||||
import { ShareDataRow } from '../document-list';
|
import { NodeEntryEvent, ShareDataRow } from '../document-list';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
export type ValidationFunction = (entry: Node) => boolean;
|
export type ValidationFunction = (entry: Node) => boolean;
|
||||||
@@ -186,6 +186,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
@Output()
|
@Output()
|
||||||
select: EventEmitter<Node[]> = new EventEmitter<Node[]>();
|
select: EventEmitter<Node[]> = new EventEmitter<Node[]>();
|
||||||
|
|
||||||
|
/** Emitted when the navigation changes. */
|
||||||
|
@Output()
|
||||||
|
navigationChange: EventEmitter<NodeEntryEvent> = new EventEmitter<NodeEntryEvent>();
|
||||||
|
|
||||||
/** Emitted when the select site changes. */
|
/** Emitted when the select site changes. */
|
||||||
@Output()
|
@Output()
|
||||||
siteChange: EventEmitter<string> = new EventEmitter<string>();
|
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
|
* Sets showingSearchResults state to be able to differentiate between search results or folder results
|
||||||
*/
|
*/
|
||||||
onFolderChange(): void {
|
onFolderChange($event: NodeEntryEvent): void {
|
||||||
this.showingSearchResults = false;
|
this.showingSearchResults = false;
|
||||||
this.infiniteScroll = false;
|
this.infiniteScroll = false;
|
||||||
this.breadcrumbFolderTitle = null;
|
this.breadcrumbFolderTitle = null;
|
||||||
this.clearSearch();
|
this.clearSearch();
|
||||||
|
this.navigationChange.emit($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -36,4 +36,6 @@ export interface ContentNodeSelectorComponentData {
|
|||||||
showSearch?: boolean;
|
showSearch?: boolean;
|
||||||
showFilesInResult?: boolean;
|
showFilesInResult?: boolean;
|
||||||
showDropdownSiteList?: boolean;
|
showDropdownSiteList?: boolean;
|
||||||
|
showLocalUploadButton?: boolean;
|
||||||
|
multipleUpload?: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -21,21 +21,33 @@
|
|||||||
[showDropdownSiteList]="data?.showDropdownSiteList"
|
[showDropdownSiteList]="data?.showDropdownSiteList"
|
||||||
[showFilesInResult]="data?.showFilesInResult"
|
[showFilesInResult]="data?.showFilesInResult"
|
||||||
(select)="onSelect($event)"
|
(select)="onSelect($event)"
|
||||||
(siteChange)="onSiteChange($event)">
|
(siteChange)="onSiteChange($event)"
|
||||||
|
(navigationChange)="onNavigationChange($event)">
|
||||||
</adf-content-node-selector-panel>
|
</adf-content-node-selector-panel>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions>
|
||||||
<button
|
<div>
|
||||||
mat-button
|
<adf-upload-button
|
||||||
(click)="close()"
|
*ngIf="data?.showLocalUploadButton"
|
||||||
data-automation-id="content-node-selector-actions-cancel">{{ 'NODE_SELECTOR.CANCEL' | translate }}
|
[staticTitle]="'FORM.FIELD.UPLOAD' | translate "
|
||||||
</button>
|
[multipleFiles]="isMultipleSelection()"
|
||||||
|
[rootFolderId]="currentDirectoryId"
|
||||||
|
(error)="onError($event)">
|
||||||
|
</adf-upload-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
(click)="close()"
|
||||||
|
data-automation-id="content-node-selector-actions-cancel">{{ 'NODE_SELECTOR.CANCEL' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button mat-button
|
<button mat-button
|
||||||
[disabled]="!hasNodeSelected()"
|
[disabled]="!chosenNode"
|
||||||
class="adf-choose-action"
|
class="adf-choose-action"
|
||||||
(click)="onClick()"
|
(click)="onClick()"
|
||||||
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
@@ -36,7 +36,8 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
background-color: mat-color($background, background);
|
background-color: mat-color($background, background);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
color: mat-color($foreground, text, 0.72);
|
color: mat-color($foreground, text, 0.72);
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@@ -40,7 +40,8 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
|||||||
select: new EventEmitter<Node>(),
|
select: new EventEmitter<Node>(),
|
||||||
rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name',
|
rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name',
|
||||||
imageResolver: () => 'piccolo',
|
imageResolver: () => 'piccolo',
|
||||||
currentFolderId: 'cat-girl-nuku-nuku'
|
currentFolderId: 'cat-girl-nuku-nuku',
|
||||||
|
showLocalUploadButton: true
|
||||||
};
|
};
|
||||||
|
|
||||||
setupTestBed({
|
setupTestBed({
|
||||||
@@ -175,4 +176,22 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
|||||||
expect(titleElement.nativeElement.innerText).toBe('NODE_SELECTOR.CHOOSE_ITEM');
|
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 { Component, Inject, ViewEncapsulation } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
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 { Node } from '@alfresco/js-api';
|
||||||
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
||||||
|
import { NodeEntryEvent } from '../document-list/components/node.event';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-content-node-selector',
|
selector: 'adf-content-node-selector',
|
||||||
@@ -28,17 +29,19 @@ import { ContentNodeSelectorComponentData } from './content-node-selector.compon
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ContentNodeSelectorComponent {
|
export class ContentNodeSelectorComponent {
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
action: string;
|
action: string;
|
||||||
buttonActionName: string;
|
buttonActionName: string;
|
||||||
chosenNode: Node[];
|
chosenNode: Node[];
|
||||||
|
currentDirectoryId: string;
|
||||||
|
|
||||||
constructor(private translation: TranslationService,
|
constructor(private translation: TranslationService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
|
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
|
||||||
this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE';
|
this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE';
|
||||||
this.buttonActionName = `NODE_SELECTOR.${this.action}`;
|
this.buttonActionName = `NODE_SELECTOR.${this.action}`;
|
||||||
this.title = data.title;
|
this.title = data.title;
|
||||||
|
this.currentDirectoryId = data.currentFolderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
@@ -49,14 +52,14 @@ export class ContentNodeSelectorComponent {
|
|||||||
this.chosenNode = nodeList;
|
this.chosenNode = nodeList;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNodeSelected(): boolean {
|
|
||||||
return this.chosenNode?.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSiteChange(siteTitle: string) {
|
onSiteChange(siteTitle: string) {
|
||||||
this.updateTitle(siteTitle);
|
this.updateTitle(siteTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onNavigationChange(pathElement: NodeEntryEvent) {
|
||||||
|
this.currentDirectoryId = pathElement.value.id;
|
||||||
|
}
|
||||||
|
|
||||||
onClick(): void {
|
onClick(): void {
|
||||||
this.data.select.next(this.chosenNode);
|
this.data.select.next(this.chosenNode);
|
||||||
this.data.select.complete();
|
this.data.select.complete();
|
||||||
@@ -71,4 +74,12 @@ export class ContentNodeSelectorComponent {
|
|||||||
getTitleTranslation(action: string, name: string): string {
|
getTitleTranslation(action: string, name: string): string {
|
||||||
return this.translation.instant(`NODE_SELECTOR.${action}_ITEM`, { name: this.translation.instant(name) });
|
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 { CoreModule } from '@alfresco/adf-core';
|
||||||
import { DocumentListModule } from '../document-list/document-list.module';
|
import { DocumentListModule } from '../document-list/document-list.module';
|
||||||
import { NameLocationCellComponent } from './name-location-cell/name-location-cell.component';
|
import { NameLocationCellComponent } from './name-location-cell/name-location-cell.component';
|
||||||
|
import { UploadModule } from '../upload/upload.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -37,7 +38,8 @@ import { NameLocationCellComponent } from './name-location-cell/name-location-ce
|
|||||||
MaterialModule,
|
MaterialModule,
|
||||||
SitesDropdownModule,
|
SitesDropdownModule,
|
||||||
BreadcrumbModule,
|
BreadcrumbModule,
|
||||||
DocumentListModule
|
DocumentListModule,
|
||||||
|
UploadModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ContentNodeSelectorPanelComponent,
|
ContentNodeSelectorPanelComponent,
|
||||||
|
@@ -45,7 +45,8 @@ import {
|
|||||||
RequestPaginationModel,
|
RequestPaginationModel,
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
UserPreferenceValues,
|
UserPreferenceValues,
|
||||||
LockService
|
LockService,
|
||||||
|
UploadService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
|
|
||||||
import { Node, NodeEntry, NodePaging, Pagination } from '@alfresco/js-api';
|
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 { RowFilter } from '../data/row-filter.model';
|
||||||
import { DocumentListService } from '../services/document-list.service';
|
import { DocumentListService } from '../services/document-list.service';
|
||||||
import { DocumentLoaderNode } from '../models/document-folder.model';
|
import { DocumentLoaderNode } from '../models/document-folder.model';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-document-list',
|
selector: 'adf-document-list',
|
||||||
@@ -337,6 +338,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
|||||||
private appConfig: AppConfigService,
|
private appConfig: AppConfigService,
|
||||||
private userPreferencesService: UserPreferencesService,
|
private userPreferencesService: UserPreferencesService,
|
||||||
private contentService: ContentService,
|
private contentService: ContentService,
|
||||||
|
private uploadService: UploadService,
|
||||||
private thumbnailService: ThumbnailService,
|
private thumbnailService: ThumbnailService,
|
||||||
private alfrescoApiService: AlfrescoApiService,
|
private alfrescoApiService: AlfrescoApiService,
|
||||||
private lockService: LockService) {
|
private lockService: LockService) {
|
||||||
@@ -346,6 +348,18 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
|||||||
.subscribe(pagSize => {
|
.subscribe(pagSize => {
|
||||||
this.maxItems = this._pagination.maxItems = 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) {
|
getContextActions(node: NodeEntry) {
|
||||||
|
@@ -80,6 +80,7 @@
|
|||||||
"NODE_SELECTOR": {
|
"NODE_SELECTOR": {
|
||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"CHOOSE": "Select",
|
"CHOOSE": "Select",
|
||||||
|
"ATTACH": "Attach",
|
||||||
"CHOOSE_ITEM": "Select content to attach from '{{ name }}'",
|
"CHOOSE_ITEM": "Select content to attach from '{{ name }}'",
|
||||||
"COPY": "Copy",
|
"COPY": "Copy",
|
||||||
"COPY_ITEM": "Copy '{{ name }}' to...",
|
"COPY_ITEM": "Copy '{{ name }}' to...",
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<!--Single Files Upload-->
|
<!--Single Files Upload-->
|
||||||
<button *ngIf="!multipleFiles"
|
<button *ngIf="!multipleFiles"
|
||||||
[disabled]="isButtonDisabled()"
|
[disabled]="isButtonDisabled()"
|
||||||
mat-raised-button color="primary"
|
mat-button
|
||||||
(click)="uploadSingleFile.click()">
|
(click)="uploadSingleFile.click()">
|
||||||
<mat-icon>file_upload</mat-icon>
|
<mat-icon>file_upload</mat-icon>
|
||||||
<span id="upload-single-file-label"
|
<span id="upload-single-file-label"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<!--Multiple Files Upload-->
|
<!--Multiple Files Upload-->
|
||||||
<button *ngIf="multipleFiles"
|
<button *ngIf="multipleFiles"
|
||||||
[disabled]="isButtonDisabled()"
|
[disabled]="isButtonDisabled()"
|
||||||
mat-raised-button color="primary"
|
mat-button
|
||||||
(click)="uploadMultipleFiles.click()">
|
(click)="uploadMultipleFiles.click()">
|
||||||
<mat-icon>file_upload</mat-icon>
|
<mat-icon>file_upload</mat-icon>
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div *ngIf="uploadFolders">
|
<div *ngIf="uploadFolders">
|
||||||
<button
|
<button
|
||||||
[disabled]="isButtonDisabled()"
|
[disabled]="isButtonDisabled()"
|
||||||
mat-raised-button color="primary"
|
mat-button
|
||||||
(click)="uploadFolders.click()">
|
(click)="uploadFolders.click()">
|
||||||
<mat-icon>file_upload</mat-icon>
|
<mat-icon>file_upload</mat-icon>
|
||||||
<span id="uploadFolder-label"
|
<span id="uploadFolder-label"
|
||||||
|
@@ -24,4 +24,5 @@ export interface FormFieldFileSource {
|
|||||||
name: string;
|
name: string;
|
||||||
selectedFolder: FormFieldSelectedFolder;
|
selectedFolder: FormFieldSelectedFolder;
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
|
destinationFolderPath: string;
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,6 @@ export class FormModel {
|
|||||||
|
|
||||||
json: any;
|
json: any;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
contentHost: string;
|
|
||||||
values: FormValues = {};
|
values: FormValues = {};
|
||||||
tabs: TabModel[] = [];
|
tabs: TabModel[] = [];
|
||||||
fields: FormWidgetModel[] = [];
|
fields: FormWidgetModel[] = [];
|
||||||
|
@@ -36,7 +36,8 @@
|
|||||||
"AT_LEAST_LONG": "Enter at least {{ minLength }} characters",
|
"AT_LEAST_LONG": "Enter at least {{ minLength }} characters",
|
||||||
"NO_LONGER_THAN": "Enter no more than {{ maxLength }} 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": {
|
"FORM_RENDERER": {
|
||||||
"NAMELESS_TASK": "Nameless task"
|
"NAMELESS_TASK": "Nameless task"
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Component, DebugElement, SimpleChange, NgModule, Injector, ComponentFactoryResolver, ViewChild } from '@angular/core';
|
import { Component, DebugElement, SimpleChange, NgModule, Injector, ComponentFactoryResolver, ViewChild } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
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 { Observable, of, throwError } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
AppConfigService,
|
AppConfigService,
|
||||||
@@ -370,45 +370,6 @@ describe('FormCloudComponent', () => {
|
|||||||
expect(formComponent.getFormByTaskId).toHaveBeenCalledWith(appName, taskId, 1);
|
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', () => {
|
it('should reload form definition by form id on binding changes', () => {
|
||||||
spyOn(formComponent, 'getFormById').and.stub();
|
spyOn(formComponent, 'getFormById').and.stub();
|
||||||
const formId = '123';
|
const formId = '123';
|
||||||
|
@@ -28,12 +28,10 @@ import {
|
|||||||
FormOutcomeModel,
|
FormOutcomeModel,
|
||||||
WidgetVisibilityService,
|
WidgetVisibilityService,
|
||||||
FormService,
|
FormService,
|
||||||
NotificationService,
|
|
||||||
FORM_FIELD_VALIDATORS,
|
FORM_FIELD_VALIDATORS,
|
||||||
FormFieldValidator,
|
FormFieldValidator,
|
||||||
FormValues,
|
FormValues,
|
||||||
FormModel,
|
FormModel,
|
||||||
AppConfigService,
|
|
||||||
ContentLinkModel
|
ContentLinkModel
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { FormCloudService } from '../services/form-cloud.service';
|
import { FormCloudService } from '../services/form-cloud.service';
|
||||||
@@ -106,9 +104,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
|
|
||||||
constructor(protected formCloudService: FormCloudService,
|
constructor(protected formCloudService: FormCloudService,
|
||||||
protected formService: FormService,
|
protected formService: FormService,
|
||||||
private notificationService: NotificationService,
|
protected visibilityService: WidgetVisibilityService) {
|
||||||
protected visibilityService: WidgetVisibilityService,
|
|
||||||
private appConfigService: AppConfigService) {
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.formService.formContentClicked
|
this.formService.formContentClicked
|
||||||
@@ -123,7 +119,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
|
|
||||||
if (appName && appName.currentValue) {
|
if (appName && appName.currentValue) {
|
||||||
if (this.taskId) {
|
if (this.taskId) {
|
||||||
this.getFormDefinitionWithFolderTask(this.appName, this.taskId, this.processInstanceId);
|
this.getFormByTaskId(appName.currentValue, this.taskId, this.appVersion);
|
||||||
} else if (this.formId) {
|
} else if (this.formId) {
|
||||||
this.getFormById(appName.currentValue, this.formId, this.appVersion);
|
this.getFormById(appName.currentValue, this.formId, this.appVersion);
|
||||||
}
|
}
|
||||||
@@ -200,7 +196,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
parsedForm.validateForm();
|
parsedForm.validateForm();
|
||||||
this.form = parsedForm;
|
this.form = parsedForm;
|
||||||
this.form.nodeId = '-my-';
|
this.form.nodeId = '-my-';
|
||||||
this.form.contentHost = this.appConfigService.get('ecmHost');
|
|
||||||
this.onFormLoaded(this.form);
|
this.onFormLoaded(this.form);
|
||||||
resolve(this.form);
|
resolve(this.form);
|
||||||
},
|
},
|
||||||
@@ -230,7 +225,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
parsedForm.validateForm();
|
parsedForm.validateForm();
|
||||||
this.form = parsedForm;
|
this.form = parsedForm;
|
||||||
this.form.nodeId = '-my-';
|
this.form.nodeId = '-my-';
|
||||||
this.form.contentHost = this.appConfigService.get('ecmHost');
|
|
||||||
this.onFormLoaded(this.form);
|
this.onFormLoaded(this.form);
|
||||||
},
|
},
|
||||||
(error) => {
|
(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() {
|
saveTaskForm() {
|
||||||
if (this.form && this.appName && this.taskId) {
|
if (this.form && this.appName && this.taskId) {
|
||||||
this.formCloudService
|
this.formCloudService
|
||||||
|
@@ -5,35 +5,11 @@
|
|||||||
<span *ngIf="isRequired()">*</span>
|
<span *ngIf="isRequired()">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="adf-attach-widget-container">
|
<div class="adf-attach-widget-container">
|
||||||
<div id="adf-attach-widget-simple-upload" *ngIf="isSimpleUploadButton() && isUploadButtonVisible()">
|
<div class="adf-attach-widget__menu-upload" *ngIf="isUploadButtonVisible()">
|
||||||
<a mat-raised-button color="primary">
|
<button (click)="openSelectDialog()" mat-raised-button color="primary" [id]="field.id">
|
||||||
{{ 'FORM.FIELD.UPLOAD' | translate }}
|
{{ 'FORM.FIELD.ATTACH' | translate }}
|
||||||
<mat-icon>file_upload</mat-icon>
|
<mat-icon>{{getWidgetIcon()}}</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>
|
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -27,7 +27,8 @@ import {
|
|||||||
FormFieldTypes,
|
FormFieldTypes,
|
||||||
FormFieldMetadata,
|
FormFieldMetadata,
|
||||||
FormService,
|
FormService,
|
||||||
DownloadService
|
DownloadService,
|
||||||
|
NotificationService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
@@ -44,6 +45,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
let element: HTMLInputElement;
|
let element: HTMLInputElement;
|
||||||
let contentCloudNodeSelectorService: ContentCloudNodeSelectorService;
|
let contentCloudNodeSelectorService: ContentCloudNodeSelectorService;
|
||||||
let processCloudContentService: ProcessCloudContentService;
|
let processCloudContentService: ProcessCloudContentService;
|
||||||
|
let notificationService: NotificationService;
|
||||||
let formService: FormService;
|
let formService: FormService;
|
||||||
let downloadService: DownloadService;
|
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> {
|
const fakeMinimalNode: Node = <Node> {
|
||||||
id: 'fake',
|
id: 'fake',
|
||||||
name: 'fake-name',
|
name: 'fake-name',
|
||||||
@@ -88,25 +106,9 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fakeLocalPngAnswer = {
|
const mockNodeId = new Promise(function (resolve) {
|
||||||
id: 1155,
|
resolve('mock-node-id');
|
||||||
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'
|
|
||||||
};
|
|
||||||
|
|
||||||
setupTestBed({
|
setupTestBed({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -128,23 +130,20 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
ContentCloudNodeSelectorService
|
ContentCloudNodeSelectorService
|
||||||
);
|
);
|
||||||
formService = TestBed.inject(FormService);
|
formService = TestBed.inject(FormService);
|
||||||
|
notificationService = TestBed.inject(NotificationService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
fixture.destroy();
|
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(() => {
|
it('should show up as simple upload when is configured for only local files', async(() => {
|
||||||
widget.field = new FormFieldModel(new FormModel(), {
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
type: FormFieldTypes.UPLOAD,
|
type: FormFieldTypes.UPLOAD,
|
||||||
value: []
|
value: []
|
||||||
});
|
});
|
||||||
widget.field.id = 'simple-upload-button';
|
widget.field.id = 'simple-upload-button';
|
||||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
widget.field.params = <FormFieldMetadata> allSourceParams;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
expect(
|
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(
|
spyOn(
|
||||||
contentCloudNodeSelectorService,
|
contentCloudNodeSelectorService,
|
||||||
'openUploadFileDialog'
|
'openUploadFileDialog'
|
||||||
@@ -180,47 +179,108 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
widget.field.id = 'attach-file-alfresco';
|
widget.field.id = 'attach-file-alfresco';
|
||||||
widget.field.params = <FormFieldMetadata> contentSourceParam;
|
widget.field.params = <FormFieldMetadata> contentSourceParam;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
await fixture.whenStable();
|
||||||
const attachButton: HTMLButtonElement = element.querySelector(
|
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||||
'#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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
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(), {
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
type: FormFieldTypes.UPLOAD,
|
type: FormFieldTypes.UPLOAD,
|
||||||
value: []
|
value: []
|
||||||
});
|
});
|
||||||
widget.field.id = 'attach-file-local';
|
widget.field.id = 'attach-file-alfresco';
|
||||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
widget.field.params = <FormFieldMetadata> allSourceParams;
|
||||||
spyOn(
|
|
||||||
processCloudContentService,
|
|
||||||
'createTemporaryRawRelatedContent'
|
|
||||||
).and.returnValue(of(fakeLocalPngAnswer));
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
await fixture.whenStable();
|
||||||
fixture.detectChanges();
|
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||||
const inputDebugElement = fixture.debugElement.query(
|
|
||||||
By.css('#attach-file-local')
|
expect(attachButton).not.toBeNull();
|
||||||
);
|
|
||||||
inputDebugElement.triggerEventHandler('change', {
|
attachButton.click();
|
||||||
target: { files: [fakeLocalPngAnswer] }
|
await fixture.whenStable();
|
||||||
});
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
const attachedFileName = fixture.debugElement.query(By.css('.adf-file'));
|
||||||
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
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(
|
||||||
|
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();
|
||||||
|
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(() => {
|
it('should display file list when field has value', async(() => {
|
||||||
widget.field = new FormFieldModel(new FormModel(), {
|
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', () => {
|
describe('when is readonly', () => {
|
||||||
|
|
||||||
it('should show empty list message when there are no file', async(() => {
|
it('should show empty list message when there are no file', async(() => {
|
||||||
@@ -332,46 +360,44 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when a file is uploaded', () => {
|
describe('when a file is uploaded', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async() => {
|
||||||
|
spyOn(
|
||||||
|
contentCloudNodeSelectorService,
|
||||||
|
'openUploadFileDialog'
|
||||||
|
).and.returnValue(of([fakeMinimalNode]));
|
||||||
widget.field = new FormFieldModel(new FormModel(), {
|
widget.field = new FormFieldModel(new FormModel(), {
|
||||||
type: FormFieldTypes.UPLOAD,
|
type: FormFieldTypes.UPLOAD,
|
||||||
value: []
|
value: []
|
||||||
});
|
});
|
||||||
widget.field.id = 'attach-file-attach';
|
widget.field.id = 'attach-file-alfresco';
|
||||||
widget.field.params = <FormFieldMetadata> onlyLocalParams;
|
widget.field.params = <FormFieldMetadata> contentSourceParam;
|
||||||
spyOn(
|
|
||||||
processCloudContentService,
|
|
||||||
'createTemporaryRawRelatedContent'
|
|
||||||
).and.returnValue(of(fakeLocalPngAnswer));
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
await fixture.whenStable();
|
||||||
const inputDebugElement = fixture.debugElement.query(
|
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
|
||||||
By.css('#attach-file-attach')
|
|
||||||
);
|
expect(attachButton).not.toBeNull();
|
||||||
inputDebugElement.triggerEventHandler('change', {
|
|
||||||
target: { files: [fakeLocalPngAnswer] }
|
attachButton.click();
|
||||||
});
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
await fixture.whenStable();
|
||||||
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
|
});
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should remove file when remove is clicked', (done) => {
|
it('should remove file when remove is clicked', (done) => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(By.css('#file-1155-option-menu'))
|
fixture.debugElement.query(By.css('#file-fake-option-menu'))
|
||||||
.nativeElement
|
.nativeElement
|
||||||
);
|
);
|
||||||
menuButton.click();
|
menuButton.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const removeOption: HTMLButtonElement = <HTMLButtonElement> (
|
const removeOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(By.css('#file-1155-remove'))
|
fixture.debugElement.query(By.css('#file-fake-remove'))
|
||||||
.nativeElement
|
.nativeElement
|
||||||
);
|
);
|
||||||
removeOption.click();
|
removeOption.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenRenderingDone().then(() => {
|
fixture.whenRenderingDone().then(() => {
|
||||||
expect(element.querySelector('#file-1155-icon')).toBeNull();
|
expect(element.querySelector('#file-fake-icon')).toBeNull();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -384,7 +410,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(By.css('#file-1155-option-menu'))
|
fixture.debugElement.query(By.css('#file-fake-option-menu'))
|
||||||
.nativeElement
|
.nativeElement
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -392,7 +418,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const downloadOption: HTMLButtonElement = <HTMLButtonElement> (
|
const downloadOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(By.css('#file-1155-download-file'))
|
fixture.debugElement.query(By.css('#file-fake-download-file'))
|
||||||
.nativeElement
|
.nativeElement
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -409,7 +435,7 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
|
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
|
||||||
formService.formContentClicked.subscribe(
|
formService.formContentClicked.subscribe(
|
||||||
(fileClicked: any) => {
|
(fileClicked: any) => {
|
||||||
expect(fileClicked.nodeId).toBe(1155);
|
expect(fileClicked.nodeId).toBe('fake');
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -417,14 +443,14 @@ describe('AttachFileCloudWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(
|
fixture.debugElement.query(
|
||||||
By.css('#file-1155-option-menu')
|
By.css('#file-fake-option-menu')
|
||||||
).nativeElement
|
).nativeElement
|
||||||
);
|
);
|
||||||
menuButton.click();
|
menuButton.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const showOption: HTMLButtonElement = <HTMLButtonElement> (
|
const showOption: HTMLButtonElement = <HTMLButtonElement> (
|
||||||
fixture.debugElement.query(
|
fixture.debugElement.query(
|
||||||
By.css('#file-1155-show-file')
|
By.css('#file-fake-show-file')
|
||||||
).nativeElement
|
).nativeElement
|
||||||
);
|
);
|
||||||
showOption.click();
|
showOption.click();
|
||||||
|
@@ -23,7 +23,8 @@ import {
|
|||||||
LogService,
|
LogService,
|
||||||
ThumbnailService,
|
ThumbnailService,
|
||||||
NotificationService,
|
NotificationService,
|
||||||
ContentLinkModel
|
ContentLinkModel,
|
||||||
|
TranslationService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { Node, RelatedContentRepresentation } from '@alfresco/js-api';
|
import { Node, RelatedContentRepresentation } from '@alfresco/js-api';
|
||||||
import { ContentCloudNodeSelectorService } from '../../../services/content-cloud-node-selector.service';
|
import { ContentCloudNodeSelectorService } from '../../../services/content-cloud-node-selector.service';
|
||||||
@@ -49,9 +50,9 @@ import { UploadCloudWidgetComponent } from './upload-cloud.widget';
|
|||||||
})
|
})
|
||||||
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
||||||
implements OnInit {
|
implements OnInit {
|
||||||
static ACS_SERVICE = 'alfresco-content';
|
|
||||||
|
|
||||||
typeId = 'AttachFileCloudWidgetComponent';
|
typeId = 'AttachFileCloudWidgetComponent';
|
||||||
|
rootNodeId = '-my-';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
formService: FormService,
|
formService: FormService,
|
||||||
@@ -59,35 +60,18 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
|||||||
thumbnails: ThumbnailService,
|
thumbnails: ThumbnailService,
|
||||||
processCloudContentService: ProcessCloudContentService,
|
processCloudContentService: ProcessCloudContentService,
|
||||||
notificationService: NotificationService,
|
notificationService: NotificationService,
|
||||||
private contentNodeSelectorService: ContentCloudNodeSelectorService
|
private contentNodeSelectorService: ContentCloudNodeSelectorService,
|
||||||
|
private translationService: TranslationService
|
||||||
) {
|
) {
|
||||||
super(formService, thumbnails, processCloudContentService, notificationService, logger);
|
super(formService, thumbnails, processCloudContentService, notificationService, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
isFileSourceConfigured(): boolean {
|
isAlfrescoAndLocal(): boolean {
|
||||||
return !!this.field.params && !!this.field.params.fileSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
isMultipleSourceUpload(): boolean {
|
|
||||||
return (
|
|
||||||
!this.field.readOnly &&
|
|
||||||
this.isFileSourceConfigured() &&
|
|
||||||
!this.isOnlyLocalSourceSelected()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
isOnlyLocalSourceSelected(): boolean {
|
|
||||||
return (
|
return (
|
||||||
this.field.params &&
|
this.field.params &&
|
||||||
this.field.params.fileSource &&
|
this.field.params.fileSource &&
|
||||||
this.field.params.fileSource.serviceId === 'local-file'
|
this.field.params.fileSource.serviceId === 'all-file-sources'
|
||||||
);
|
|| this.field.params.fileSource.serviceId === 'local-file'
|
||||||
}
|
|
||||||
|
|
||||||
isSimpleUploadButton(): boolean {
|
|
||||||
return (
|
|
||||||
(this.isUploadButtonVisible() && !this.isFileSourceConfigured()) ||
|
|
||||||
this.isOnlyLocalSourceSelected()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,23 +79,31 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
|||||||
return (!this.hasFile || this.multipleOption) && !this.field.readOnly;
|
return (!this.hasFile || this.multipleOption) && !this.field.readOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
onAttachFileChanged(event: any) {
|
|
||||||
this.onFileChanged(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemoveAttachFile(file: File | RelatedContentRepresentation | Node) {
|
onRemoveAttachFile(file: File | RelatedContentRepresentation | Node) {
|
||||||
this.removeFile(file);
|
this.removeFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadFileFromCS() {
|
async openSelectDialog() {
|
||||||
this.openSelectDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
openSelectDialog() {
|
|
||||||
const selectedMode = this.field.params.multiple ? 'multiple' : 'single';
|
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
|
this.contentNodeSelectorService
|
||||||
.openUploadFileDialog(this.field.form.contentHost, '-my-', selectedMode)
|
.openUploadFileDialog(this.rootNodeId, selectedMode, this.isAlfrescoAndLocal())
|
||||||
.subscribe((selections: Node[]) => {
|
.subscribe((selections: Node[]) => {
|
||||||
selections.forEach(node => (node['isExternal'] = true));
|
selections.forEach(node => (node['isExternal'] = true));
|
||||||
const selectionWithoutDuplication = this.removeExistingSelection(selections);
|
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[]) {
|
removeExistingSelection(selections: Node[]) {
|
||||||
const existingNode: Node[] = [...this.field.value || []];
|
const existingNode: Node[] = [...this.field.value || []];
|
||||||
return selections.filter(opt => !existingNode.some( (node) => node.id === opt.id));
|
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 {
|
downloadContent(file: Node): void {
|
||||||
this.processCloudContentService.downloadFile(
|
this.processCloudContentService.downloadFile(file.id);
|
||||||
file.id,
|
|
||||||
this.field.form.contentHost
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAttachFileClicked(nodeSelector: any) {
|
onAttachFileClicked(nodeSelector: any) {
|
||||||
nodeSelector.nodeId = nodeSelector.id;
|
nodeSelector.nodeId = nodeSelector.id;
|
||||||
this.fileClicked(new ContentLinkModel(nodeSelector));
|
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> {
|
private uploadRawContent(file: File): Observable<Node> {
|
||||||
return this.processCloudContentService.createTemporaryRawRelatedContent(
|
return this.processCloudContentService.createTemporaryRawRelatedContent(file, this.field.form.nodeId);
|
||||||
file, this.field.form.nodeId, this.field.form.contentHost
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMultipleFileParam() {
|
getMultipleFileParam() {
|
||||||
|
@@ -23,16 +23,3 @@ export class TaskVariableCloud {
|
|||||||
this.value = obj.value || null;
|
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) {
|
private dialog: MatDialog) {
|
||||||
}
|
}
|
||||||
|
|
||||||
openUploadFileDialog(contentHost: string, currentFolderId?: string, selectionMode?: string): Observable<Node[]> {
|
openUploadFileDialog(currentFolderId?: string, selectionMode?: string, showLocalUploadButton?: boolean): Observable<Node[]> {
|
||||||
const changedConfig = this.apiService.lastConfig;
|
|
||||||
changedConfig.provider = 'ALL';
|
|
||||||
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
|
|
||||||
this.apiService.getInstance().setConfig(changedConfig);
|
|
||||||
const select = new Subject<Node[]>();
|
const select = new Subject<Node[]>();
|
||||||
select.subscribe({
|
select.subscribe({
|
||||||
complete: this.close.bind(this)
|
complete: this.close.bind(this)
|
||||||
});
|
});
|
||||||
const data = <ContentNodeSelectorComponentData> {
|
const data = <ContentNodeSelectorComponentData> {
|
||||||
title: 'Select a file',
|
title: 'Select a file',
|
||||||
actionName: 'Choose',
|
actionName: 'Attach',
|
||||||
currentFolderId,
|
currentFolderId,
|
||||||
restrictRootToCurrentFolderId: true,
|
restrictRootToCurrentFolderId: true,
|
||||||
select,
|
select,
|
||||||
selectionMode,
|
selectionMode,
|
||||||
isSelectionValid: (entry: Node) => entry.isFile,
|
isSelectionValid: (entry: Node) => entry.isFile,
|
||||||
showFilesInResult: true
|
showFilesInResult: true,
|
||||||
};
|
showDropdownSiteList: false,
|
||||||
|
showLocalUploadButton
|
||||||
|
};
|
||||||
this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
||||||
return select;
|
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) {
|
private openContentNodeDialog(data: ContentNodeSelectorComponentData, currentPanelClass: string, chosenWidth: string) {
|
||||||
this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: currentPanelClass, width: chosenWidth });
|
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');
|
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('POST');
|
||||||
done();
|
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 { map, switchMap } from 'rxjs/operators';
|
||||||
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
|
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
|
||||||
import { CompleteFormRepresentation } from '@alfresco/js-api';
|
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 { BaseCloudService } from '../../services/base-cloud.service';
|
||||||
import { FormContent } from '../../services/form-fields.interfaces';
|
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.
|
* Gets the variables of a task.
|
||||||
* @param appName Name of the app
|
* @param appName Name of the app
|
||||||
@@ -237,10 +227,4 @@ export class FormCloudService extends BaseCloudService {
|
|||||||
}
|
}
|
||||||
return null;
|
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(
|
createTemporaryRawRelatedContent(
|
||||||
file: File,
|
file: File,
|
||||||
nodeId: string,
|
nodeId: string
|
||||||
contentHost: string
|
|
||||||
): Observable<Node> {
|
): Observable<Node> {
|
||||||
this.updateConfig(contentHost);
|
|
||||||
|
|
||||||
return from(
|
return from(
|
||||||
this.apiService
|
this.apiService
|
||||||
@@ -59,8 +57,7 @@ export class ProcessCloudContentService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRawContentNode(nodeId: string, contentHost: string): Observable<Blob> {
|
getRawContentNode(nodeId: string): Observable<Blob> {
|
||||||
this.updateConfig(contentHost);
|
|
||||||
return this.contentService.getNodeContent(nodeId);
|
return this.contentService.getNodeContent(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +65,7 @@ export class ProcessCloudContentService {
|
|||||||
this.contentService.downloadBlob(blob, fileName);
|
this.contentService.downloadBlob(blob, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadFile(nodeId: string, contentHost: string) {
|
async downloadFile(nodeId: string) {
|
||||||
this.updateConfig(contentHost);
|
|
||||||
|
|
||||||
const ticket = await this.getAuthTicket();
|
const ticket = await this.getAuthTicket();
|
||||||
const url = this.contentService.getContentUrl(nodeId, true, ticket);
|
const url = this.contentService.getContentUrl(nodeId, true, ticket);
|
||||||
@@ -88,18 +84,6 @@ export class ProcessCloudContentService {
|
|||||||
return '';
|
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) {
|
private handleError(error: any) {
|
||||||
this.logService.error(error);
|
this.logService.error(error);
|
||||||
return throwError(error || 'Server error');
|
return throwError(error || 'Server error');
|
||||||
|
@@ -266,6 +266,9 @@
|
|||||||
"CLAIM": "CLAIM",
|
"CLAIM": "CLAIM",
|
||||||
"UNCLAIM": "RELEASE"
|
"UNCLAIM": "RELEASE"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ERROR": {
|
||||||
|
"INVALID_DESTINATION_FOLDER_PATH": "Invalid destination folder path"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user