[ADF-2340] add trashcan in demo shell and definitely delete in delete directive (#2976)

* add trashcan in demo shell and definetly delete in delete directive

* fix delte directive test
This commit is contained in:
Eugenio Romano
2018-02-22 12:33:42 +00:00
committed by GitHub
parent 9bb2a0ae19
commit 6e14ff5470
12 changed files with 298 additions and 31 deletions

View File

@@ -23,6 +23,7 @@
"UPLOADER": "Uploader",
"WEBSCRIPT": "Webscript",
"TAG": "Tag",
"TRASHCAN": "Trashcan",
"SOCIAL": "Social",
"SETTINGS": "Settings",
"OVERLAY_VIEWER": "Overlay Viewer",
@@ -33,6 +34,17 @@
"SEARCH_CREATED_BY" : "Created By",
"SEARCH_SERVICE_APPROACH": "Check this to disable the input property and configure using the service"
},
"TRASHCAN" :{
"ACTIONS":{
"DELETE_PERMANENT":"Delete permanent",
"RESTORE":"Restore"
},
"EMPTY_STATE": {
"TITLE": "Trash is empty",
"FIRST_TEXT": "Items you delete are moved to the Trash.",
"SECOND_TEXT": "Empty Trash to permanently delete items."
}
},
"DOCUMENT_LIST": {
"MULTISELECT_CHECKBOXES" :"Multiselect (with checkboxes)",
"MULTIPLE_FILE_UPLOAD" :"Multiple File Upload",
@@ -49,7 +61,10 @@
"TAG": "Tag",
"CREATED_BY": "Created by",
"CREATED_ON": "Created on",
"CREATED": "Created"
"CREATED": "Created",
"SIZE": "Size",
"DELETED_ON": "Deleted",
"DELETED_BY": "Deleted by"
},
"TOOLBAR": {
"CARDVIEW":"Card view mode",

View File

@@ -29,6 +29,7 @@ import { FormViewerComponent } from './components/process-service/form-viewer.co
import { FormNodeViewerComponent } from './components/process-service/form-node-viewer.component';
import { AppsViewComponent } from './components/process-service/apps-view.component';
import { DataTableComponent } from './components/datatable/datatable.component';
import { TrashcanComponent } from './components/trashcan/trashcan.component';
import { FilesComponent } from './components/files/files.component';
import { FileViewComponent } from './components/file-view/file-view.component';
import { WebscriptComponent } from './components/webscript/webscript.component';
@@ -78,6 +79,7 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li
DataTableComponent,
FilesComponent,
FileViewComponent,
TrashcanComponent,
FormComponent,
FormListComponent,
WebscriptComponent,

View File

@@ -30,6 +30,7 @@ import { FormNodeViewerComponent } from './components/process-service/form-node-
import { AppsViewComponent } from './components/process-service/apps-view.component';
import { SearchResultComponent } from './components/search/search-result.component';
import { SearchExtendedComponent } from './components/search/search-extended.component';
import { TrashcanComponent } from './components/trashcan/trashcan.component';
import { DataTableComponent } from './components/datatable/datatable.component';
import { WebscriptComponent } from './components/webscript/webscript.component';
@@ -65,8 +66,12 @@ export const appRoutes: Routes = [
{
path: 'home',
component: HomeComponent
}
,
},
{
path: 'trashcan',
component: TrashcanComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'files',
component: FilesComponent,

View File

@@ -30,6 +30,7 @@ export class AppLayoutComponent {
links: Array<any> = [
{ href: '/home', icon: 'home', title: 'APP_LAYOUT.HOME' },
{ href: '/files', icon: 'folder_open', title: 'APP_LAYOUT.CONTENT_SERVICES' },
{ href: '/trashcan', icon: 'delete', title: 'APP_LAYOUT.TRASHCAN' },
{ href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES' },
{ href: '/login', icon: 'vpn_key', title: 'APP_LAYOUT.LOGIN' },
{ href: '/dl-custom-sources', icon: 'extension', title: 'APP_LAYOUT.CUSTOM_SOURCES' },

View File

@@ -199,7 +199,7 @@
-->
<data-column
key="content.sizeInBytes"
title="Size"
title="{{'DOCUMENT_LIST.COLUMNS.SIZE' | translate}}"
type="fileSize">
</data-column>
<!-- Notes: has performance overhead due to multiple files/folders causing separate HTTP calls to get tags -->

View File

@@ -0,0 +1,96 @@
<div class="inner-layout">
<div class="inner-layout__header">
<adf-breadcrumb [root]="'APP_LAYOUT.TRASHCAN' | translate">
</adf-breadcrumb>
<adf-toolbar class="inline">
<button
mat-icon-button
[adf-delete]="documentList.selection"
(delete)="documentList.reload()"
[permanent]="true"
*ngIf="documentList.selection.length"
title="{{ 'TRASHCAN.ACTIONS.DELETE_PERMANENT' | translate }}">
<mat-icon>delete_forever</mat-icon>
</button>
<button
mat-icon-button
(selection-node-restored)="refresh()"
[adf-restore]="documentList.selection"
*ngIf="documentList.selection.length"
title="{{ 'TRASHCAN.ACTIONS.RESTORE' | translate }}">
<mat-icon>restore</mat-icon>
</button>
</adf-toolbar>
</div>
<div class="inner-layout__content">
<adf-document-list #documentList
[attr.class]="documentList.isEmpty() ? 'empty-list' : ''"
currentFolderId="-trashcan-"
selectionMode="multiple"
[navigate]="false"
locationFormat="/files"
[sorting]="[ 'archivedAt', 'desc' ]"
[contextMenuActions]="true"
[contentActions]="false">
<empty-folder-content>
<ng-template>
<div class="empty-list__block">
<mat-icon>delete</mat-icon>
<p class="empty-list__title">{{ 'TRASHCAN.EMPTY_STATE.TITLE' | translate }}</p>
<p class="empty-list__text">{{ 'TRASHCAN.EMPTY_STATE.FIRST_TEXT' | translate }}</p>
<p class="empty-list__text">{{ 'TRASHCAN.EMPTY_STATE.SECOND_TEXT' | translate }}</p>
</div>
</ng-template>
</empty-folder-content>
<data-columns>
<data-column
key="$thumbnail"
type="image"
[sortable]="false"
class="image-table-cell">
</data-column>
<data-column
class="adf-data-table-cell--ellipsis__name"
key="name"
title="DOCUMENT_LIST.COLUMNS.DISPLAY_NAME">
<ng-template let-value="value" let-context>
<span class="adf-datatable-cell" title="{{ context?.row?.obj | adfNodeNameTooltip }}">{{ value }}</span>
</ng-template>
</data-column>
<data-column
key="content.sizeInBytes"
title="DOCUMENT_LIST.COLUMNS.SIZE"
type="fileSize">
</data-column>
<data-column
key="archivedAt"
title="DOCUMENT_LIST.COLUMNS.DELETED_ON">
<ng-template let-value="value">
<span title="{{ value | date:'medium' }}">{{ value | adfTimeAgo }}</span>
</ng-template>
</data-column>
<data-column
class="adf-data-table-cell--ellipsis"
key="archivedByUser.displayName"
title="DOCUMENT_LIST.COLUMNS.DELETED_BY">
</data-column>
</data-columns>
</adf-document-list>
<adf-pagination [ngClass]="{ 'no-border' : documentList.isEmpty()}"
[target]="documentList">
</adf-pagination>
</div>
</div>

View File

@@ -0,0 +1,35 @@
.empty-list {
.adf-data-table {
height: 100%;
tr:hover, tr:focus {
cursor: default;
}
}
&__block {
display: flex;
flex-direction: column;
align-items: center;
p {
line-height: 0;
}
}
&__title {
font-size: 18px;
font-weight: 600;
}
&__subtitle {
font-size: 14px;
font-weight: 300;
}
&__block > mat-icon {
font-size: 52px;
height: 52px;
width: 52px;
}
}

View File

@@ -0,0 +1,43 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2017 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, ViewChild } from '@angular/core';
import { DocumentListComponent } from '@alfresco/adf-content-services';
@Component({
templateUrl: './trashcan.component.html',
styleUrls: ['trashcan.component.scss']
})
export class TrashcanComponent {
@ViewChild(DocumentListComponent)
documentList;
refresh() {
this.documentList.loadTrashcan();
this.documentList.resetSelection();
}
}

View File

@@ -25,7 +25,7 @@ Deletes multiple files and folders.
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| selection | `MinimalNodeEntity[]` | | Array of nodes to delete. |
| selection | `MinimalNodeEntity[] | DeletedNodeEntity[]` | | Array of nodes to delete. |
| permanent | `boolean` | `false` | If true then the nodes are deleted immediately rather than being put in the trash. |
### Events
@@ -36,7 +36,7 @@ Deletes multiple files and folders.
## Details
See **Demo Shell**
Note it the file is already in the trashcan so is a DeletedNodeEntity the performing of this action will delete the file premanently
## See also

View File

@@ -36,7 +36,8 @@ class TestComponent {
@Component({
template: `
<div [adf-node-permission]="selection" [adf-delete]="selection"
<div [adf-node-permission]="selection"
[adf-delete]="selection"
(delete)="done()">
</div>`
})
@@ -46,13 +47,33 @@ class TestWithPermissionsComponent {
done = jasmine.createSpy('done');
}
@Component({
template: `
delete permanent
<div id="delete-permanent"
[adf-delete]="selection"
[permanent]="permanent"
(delete)="done()">
</div>`
})
class TestDeletePermanentComponent {
selection = [];
permanent = true;
done = jasmine.createSpy('done');
}
describe('NodeDeleteDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let fixtureWithPermissions: ComponentFixture<TestWithPermissionsComponent>;
let fixtureWithPermanentComponent: ComponentFixture<TestDeletePermanentComponent>;
let element: DebugElement;
let elementWithPermissions: DebugElement;
let elementWithPermanentDelete: DebugElement;
let component: TestComponent;
let componentWithPermissions: TestWithPermissionsComponent;
let componentWithPermanentDelete: TestDeletePermanentComponent;
let alfrescoApi: AlfrescoApiService;
let notification: NotificationService;
let nodeApi;
@@ -62,17 +83,23 @@ describe('NodeDeleteDirective', () => {
declarations: [
TestComponent,
TestWithPermissionsComponent
TestWithPermissionsComponent,
TestDeletePermanentComponent
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(TestComponent);
fixtureWithPermissions = TestBed.createComponent(TestWithPermissionsComponent);
fixtureWithPermanentComponent = TestBed.createComponent(TestDeletePermanentComponent);
component = fixture.componentInstance;
componentWithPermissions = fixtureWithPermissions.componentInstance;
componentWithPermanentDelete = fixtureWithPermanentComponent.componentInstance;
element = fixture.debugElement.query(By.directive(NodeDeleteDirective));
elementWithPermissions = fixtureWithPermissions.debugElement.query(By.directive(NodeDeleteDirective));
elementWithPermanentDelete = fixtureWithPermanentComponent.debugElement.query(By.directive(NodeDeleteDirective));
alfrescoApi = TestBed.get(AlfrescoApiService);
nodeApi = alfrescoApi.getInstance().nodes;
@@ -268,5 +295,43 @@ describe('NodeDeleteDirective', () => {
expect(elementWithPermissions.nativeElement.disabled).toEqual(false);
}));
describe('Permanent', () => {
it('should call the api with permamnet delete option if permanent directive input is true', fakeAsync(() => {
let deleteApi = spyOn(nodeApi, 'deleteNode').and.returnValue(Promise.resolve());
fixtureWithPermanentComponent.detectChanges();
componentWithPermanentDelete.selection = [
{ entry: { id: '1', name: 'name1'}
];
fixtureWithPermanentComponent.detectChanges();
elementWithPermanentDelete.triggerEventHandler('click', null);
tick();
expect(deleteApi).toHaveBeenCalledWith('1', { permanent: true });
}));
it('should call the traschan api if permanent directive input is true and the file is already in the trashcan ', fakeAsync(() => {
let deleteApi = spyOn(nodeApi, 'purgeDeletedNode').and.returnValue(Promise.resolve());
fixtureWithPermanentComponent.detectChanges();
componentWithPermanentDelete.selection = [
{ entry: { id: '1', name: 'name1', archivedAt: 'archived' } }
];
fixtureWithPermanentComponent.detectChanges();
elementWithPermanentDelete.triggerEventHandler('click', null);
tick();
expect(deleteApi).toHaveBeenCalledWith('1');
}));
});
});
});

View File

@@ -18,7 +18,7 @@
/* tslint:disable:no-input-rename */
import { Directive, ElementRef, EventEmitter, HostListener, Input, OnChanges, Output } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { MinimalNodeEntity, MinimalNodeEntryEntity, DeletedNodeEntity, DeletedNodeMinimalEntry } from 'alfresco-js-api';
import { Observable } from 'rxjs/Observable';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { NotificationService } from '../services/notification.service';
@@ -28,7 +28,7 @@ import 'rxjs/observable/forkJoin';
import 'rxjs/add/operator/catch';
interface ProcessedNodeData {
entry: MinimalNodeEntryEntity;
entry: MinimalNodeEntryEntity | DeletedNodeMinimalEntry;
status: number;
}
@@ -55,7 +55,7 @@ interface ProcessStatus {
export class NodeDeleteDirective implements OnChanges {
/** Array of nodes to delete. */
@Input('adf-delete')
selection: MinimalNodeEntity[];
selection: MinimalNodeEntity[] | DeletedNodeEntity[];
/** If true then the nodes are deleted immediately rather than being
* put in the trash.
@@ -92,10 +92,8 @@ export class NodeDeleteDirective implements OnChanges {
this.elementRef.nativeElement.disabled = disable;
}
private process(selection: MinimalNodeEntity[]) {
if (!selection.length) {
return;
}
private process(selection: MinimalNodeEntity[] | DeletedNodeEntity[]) {
if (selection && selection.length) {
const batch = this.getDeleteNodesBatch(selection);
@@ -110,15 +108,22 @@ export class NodeDeleteDirective implements OnChanges {
}
});
}
}
private getDeleteNodesBatch(selection: MinimalNodeEntity[]): Observable<ProcessedNodeData>[] {
private getDeleteNodesBatch(selection: any): Observable<ProcessedNodeData>[] {
return selection.map((node) => this.deleteNode(node));
}
private deleteNode(node: MinimalNodeEntity): Observable<ProcessedNodeData> {
private deleteNode(node: MinimalNodeEntity | DeletedNodeEntity): Observable<ProcessedNodeData> {
const id = (<any> node.entry).nodeId || node.entry.id;
const promise = this.alfrescoApiService.getInstance().nodes.deleteNode(id, { permanent: this.permanent });
let promise;
if (node.entry.hasOwnProperty('archivedAt')) {
promise = this.alfrescoApiService.getInstance().nodes.purgeDeletedNode(id);
} else {
promise = this.alfrescoApiService.getInstance().nodes.deleteNode(id, { permanent: this.permanent });
}
return Observable.fromPromise(promise)
.map(() => ({

View File

@@ -23,7 +23,7 @@ import { Observable } from 'rxjs/Observable';
import { ComponentTranslationModel } from '../models/component.model';
import { ObjectUtils } from '../utils/object-utils';
import { LogService } from './log.service';
import { map } from 'rxjs/operators'
import { map } from 'rxjs/operators';
import 'rxjs/observable/forkJoin';
import 'rxjs/add/observable/forkJoin';