[ADF-1949 ] Allow customization of the dropdown menu on document picker (#2660)

* [ADF-1949] Allow customization of the dropdown menu on document picker

- add as input properties the 'dropdownHideMyFiles' and the 'dropdownSiteList' to the destination picker, so the list of sites from the dropdown can be customized, if wanted
- add navigation on '-mysites-' entries on document-list
- use custom dropdown on the copy/move document picker

* [ADF-1949] do not use a custom dropdown on the copy/move document picker on ADF side, because this can be done only on ACA side

* [ADF-1949] handling the node-dblclick event on content-node-selector.component instead of doing it on the document-list component

- and update the sites-dropdown documentation file

* [ADF-1949] changes requested on code review

* [ADF-1949] fix failing tests
This commit is contained in:
suzanadirla 2017-11-20 14:10:38 +02:00 committed by Eugenio Romano
parent 141bc0f8b4
commit edaa442e18
7 changed files with 103 additions and 43 deletions

View File

@ -29,6 +29,7 @@ Displays a dropdown menu to show and interact with the sites of the current user
| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| hideMyFiles | boolean | false | Hide the "My Files" option added to the list by default |
| siteList | any[] | null | A custom list of sites to be displayed by the dropdown. If no value is given, the sites of the current user are displayed by default. A list of objects only with properties 'title' and 'guid' is enough to be able to display the dropdown. |
### Events

View File

@ -28,6 +28,8 @@
<adf-sites-dropdown
(change)="siteChanged($event)"
[hideMyFiles]="dropdownHideMyFiles"
[siteList]="dropdownSiteList"
data-automation-id="content-node-selector-sites-combo"></adf-sites-dropdown>
<adf-toolbar>
@ -60,6 +62,7 @@
[allowDropFiles]="false"
(folderChange)="onFolderChange()"
(ready)="onFolderLoaded($event)"
(node-dblclick)="onNodeDoubleClick($event)"
data-automation-id="content-node-selector-document-list">
<empty-folder-content>
<ng-template>

View File

@ -20,7 +20,7 @@ import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { By } from '@angular/platform-browser';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ContentService, TranslationService, SearchService, SiteModel, SitesApiService, UserPreferencesService } from '@alfresco/adf-core';
import { AlfrescoApiService, ContentService, TranslationService, SearchService, SiteModel, SitesApiService, UserPreferencesService } from '@alfresco/adf-core';
import { DataTableModule } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Rx';
import { MaterialModule } from '../material.module';
@ -83,6 +83,7 @@ describe('ContentNodeSelectorComponent', () => {
ContentNodeSelectorComponent
],
providers: [
AlfrescoApiService,
ContentService,
SitesApiService,
TranslationService,
@ -173,13 +174,13 @@ describe('ContentNodeSelectorComponent', () => {
});
it('should be shown if dialogRef is injected', () => {
const componentInstance = new ContentNodeSelectorComponent(null, null, fakePreference, data, dummyMdDialogRef);
const componentInstance = new ContentNodeSelectorComponent(null, null, null, fakePreference, data, dummyMdDialogRef);
expect(componentInstance.inDialog).toBeTruthy();
});
it('should should call the close method in the injected dialogRef', () => {
spyOn(dummyMdDialogRef, 'close');
const componentInstance = new ContentNodeSelectorComponent(null, null, fakePreference, data, dummyMdDialogRef);
const componentInstance = new ContentNodeSelectorComponent(null, null, null, fakePreference, data, dummyMdDialogRef);
componentInstance.close();

View File

@ -16,7 +16,7 @@
*/
import { Component, EventEmitter, Inject, Input, OnInit, Optional, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { ContentService, HighlightDirective, SiteModel, UserPreferencesService } from '@alfresco/adf-core';
import { AlfrescoApiService, ContentService, HighlightDirective, SiteModel, UserPreferencesService } from '@alfresco/adf-core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api';
import { DocumentListComponent, PaginationStrategy } from '../document-list/components/document-list.component';
@ -28,6 +28,8 @@ import { ContentNodeSelectorService } from './content-node-selector.service';
export interface ContentNodeSelectorComponentData {
title: string;
currentFolderId?: string;
dropdownHideMyFiles?: boolean;
dropdownSiteList?: any[];
rowFilter?: RowFilter;
imageResolver?: ImageResolver;
select: EventEmitter<MinimalNodeEntryEntity[]>;
@ -60,6 +62,12 @@ export class ContentNodeSelectorComponent implements OnInit {
@Input()
currentFolderId: string | null = null;
@Input()
dropdownHideMyFiles: boolean = false;
@Input()
dropdownSiteList: any[] = null;
@Input()
rowFilter: RowFilter = null;
@ -80,6 +88,7 @@ export class ContentNodeSelectorComponent implements OnInit {
constructor(private contentNodeSelectorService: ContentNodeSelectorService,
private contentService: ContentService,
private apiService: AlfrescoApiService,
private preferences: UserPreferencesService,
@Optional() @Inject(MAT_DIALOG_DATA) data?: ContentNodeSelectorComponentData,
@Optional() private containingDialog?: MatDialogRef<ContentNodeSelectorComponent>) {
@ -87,6 +96,8 @@ export class ContentNodeSelectorComponent implements OnInit {
this.title = data.title;
this.select = data.select;
this.currentFolderId = data.currentFolderId;
this.dropdownHideMyFiles = data.dropdownHideMyFiles;
this.dropdownSiteList = data.dropdownSiteList;
this.rowFilter = data.rowFilter;
this.imageResolver = data.imageResolver;
}
@ -295,4 +306,21 @@ export class ContentNodeSelectorComponent implements OnInit {
close(): void {
this.containingDialog.close();
}
onNodeDoubleClick(e: CustomEvent) {
const node: any = e.detail.node.entry;
if (node && node.guid) {
const options = {
maxItems: this.pageSize,
skipCount: this.skipCount,
include: ['path', 'properties', 'allowableOperations']
};
this.apiService.nodesApi.getNode(node.guid, options)
.then(documentLibrary => {
this.documentList.performCustomSourceNavigation(documentLibrary);
});
}
}
}

View File

@ -287,7 +287,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
if (this.folderNode) {
this.loadFolder(merge);
} else if (this.currentFolderId) {
this.loadFolderByNodeId(this.currentFolderId);
this.loadFolderByNodeId(this.currentFolderId, merge);
} else if (this.node) {
this.data.loadPage(this.node);
this.ready.emit(this.node);
@ -376,15 +376,27 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
performNavigation(node: MinimalNodeEntity): boolean {
if (this.canNavigateFolder(node)) {
this.updateFolderData(node);
return true;
}
return false;
}
performCustomSourceNavigation(node: MinimalNodeEntity): boolean {
if (this.isCustomSource(this.currentFolderId)) {
this.updateFolderData(node);
return true;
}
return false;
}
updateFolderData(node: MinimalNodeEntity): void {
this.currentFolderId = node.entry.id;
this.folderNode = node.entry;
this.skipCount = 0;
this.currentNodeAllowableOperations = node.entry['allowableOperations'] ? node.entry['allowableOperations'] : [];
this.loadFolder();
this.folderChange.emit(new NodeEntryEvent(node.entry));
return true;
}
return false;
}
/**
@ -418,28 +430,32 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
}
let nodeId = this.folderNode ? this.folderNode.id : this.currentFolderId;
if (!this.hasCustomLayout) {
this.setupDefaultColumns(nodeId);
}
if (nodeId) {
this.loadFolderNodesByFolderNodeId(nodeId, this.maxItems, this.skipCount, merge).catch(err => this.error.emit(err));
}
}
// gets folder node and its content
loadFolderByNodeId(nodeId: string) {
loadFolderByNodeId(nodeId: string, merge: boolean = false) {
this.loading = true;
this.resetSelection();
if (nodeId === '-trashcan-') {
this.loadTrashcan();
this.loadTrashcan(merge);
} else if (nodeId === '-sharedlinks-') {
this.loadSharedLinks();
this.loadSharedLinks(merge);
} else if (nodeId === '-sites-') {
this.loadSites();
this.loadSites(merge);
} else if (nodeId === '-mysites-') {
this.loadMemberSites();
this.loadMemberSites(merge);
} else if (nodeId === '-favorites-') {
this.loadFavorites();
this.loadFavorites(merge);
} else if (nodeId === '-recent-') {
this.loadRecent();
this.loadRecent(merge);
} else {
this.documentListService
.getFolderNode(nodeId)
@ -448,7 +464,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
this.currentFolderId = node.id;
this.skipCount = 0;
this.currentNodeAllowableOperations = node['allowableOperations'] ? node['allowableOperations'] : [];
return this.loadFolderNodesByFolderNodeId(node.id, this.maxItems, this.skipCount);
return this.loadFolderNodesByFolderNodeId(node.id, this.maxItems, this.skipCount, merge);
})
.catch(err => {
if (JSON.parse(err.message).error.statusCode === 403) {
@ -502,29 +518,29 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
changePage.maxItems.currentValue !== changePage.maxItems.previousValue;
}
private loadTrashcan(): void {
private loadTrashcan(merge: boolean = false): void {
const options = {
include: ['path', 'properties'],
maxItems: this.maxItems,
skipCount: this.skipCount
};
this.apiService.nodesApi.getDeletedNodes(options)
.then((page: DeletedNodesPaging) => this.onPageLoaded(page))
.then((page: DeletedNodesPaging) => this.onPageLoaded(page, merge))
.catch(error => this.error.emit(error));
}
private loadSharedLinks(): void {
private loadSharedLinks(merge: boolean = false): void {
const options = {
include: ['properties', 'allowableOperations', 'path'],
maxItems: this.maxItems,
skipCount: this.skipCount
};
this.apiService.sharedLinksApi.findSharedLinks(options)
.then((page: NodePaging) => this.onPageLoaded(page))
.then((page: NodePaging) => this.onPageLoaded(page, merge))
.catch(error => this.error.emit(error));
}
private loadSites(): void {
private loadSites(merge: boolean = false): void {
const options = {
include: ['properties'],
maxItems: this.maxItems,
@ -532,11 +548,11 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
};
this.apiService.sitesApi.getSites(options)
.then((page: NodePaging) => this.onPageLoaded(page))
.then((page: NodePaging) => this.onPageLoaded(page, merge))
.catch(error => this.error.emit(error));
}
private loadMemberSites(): void {
private loadMemberSites(merge: boolean = false): void {
const options = {
include: ['properties'],
maxItems: this.maxItems,
@ -548,19 +564,22 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
let page: NodePaging = {
list: {
entries: result.list.entries
.map(({ entry: { site } }: any) => ({
.map(({entry: {site}}: any) => {
site.allowableOperations = site.allowableOperations ? site.allowableOperations : [this.CREATE_PERMISSION];
return {
entry: site
})),
};
}),
pagination: result.list.pagination
}
};
this.onPageLoaded(page);
this.onPageLoaded(page, merge);
})
.catch(error => this.error.emit(error));
}
private loadFavorites(): void {
private loadFavorites(merge: boolean = false): void {
const options = {
maxItems: this.maxItems,
skipCount: this.skipCount,
@ -586,12 +605,12 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
pagination: result.list.pagination
}
};
this.onPageLoaded(page);
this.onPageLoaded(page, merge);
})
.catch(error => this.error.emit(error));
}
private loadRecent(): void {
private loadRecent(merge: boolean = false): void {
this.apiService.peopleApi.getPerson('-me-')
.then((person: PersonEntry) => {
const username = person.entry.id;
@ -619,13 +638,13 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
return this.apiService.searchApi.search(query);
})
.then((page: NodePaging) => this.onPageLoaded(page))
.then((page: NodePaging) => this.onPageLoaded(page, merge))
.catch(error => this.error.emit(error));
}
private onPageLoaded(page: NodePaging) {
private onPageLoaded(page: NodePaging, merge: boolean = false) {
if (page) {
this.data.loadPage(page);
this.data.loadPage(page, merge);
this.loading = false;
this.ready.emit(page);
}

View File

@ -10,7 +10,7 @@
(ngModelChange)="selectedSite()">
<mat-option *ngIf="!hideMyFiles" data-automation-id="site-my-files-option" id="default_site_option" [value]="MY_FILES_VALUE">{{'DROPDOWN.MY_FILES_OPTION' | translate}}</mat-option>
<mat-option *ngFor="let site of siteList" [value]="site.guid">
{{ site.title }}
{{ site.title | translate }}
</mat-option>
</mat-select>
</mat-form-field>

View File

@ -28,22 +28,22 @@ export class DropdownSitesComponent implements OnInit {
@Input()
hideMyFiles: boolean = false;
@Input()
siteList: any[] = null;
@Output()
change: EventEmitter<SiteModel> = new EventEmitter();
public MY_FILES_VALUE = 'default';
siteList = [];
public siteSelected: string;
constructor(private sitesService: SitesApiService) {}
ngOnInit() {
this.sitesService.getSites().subscribe((result) => {
this.siteList = result;
},
(error) => {});
if (!this.siteList) {
this.setDefaultSiteList();
}
}
selectedSite() {
@ -56,4 +56,12 @@ export class DropdownSitesComponent implements OnInit {
this.change.emit(siteFound);
}
setDefaultSiteList() {
this.siteList = [];
this.sitesService.getSites().subscribe((result) => {
this.siteList = result;
},
(error) => {});
}
}