no permission content (#2616)

This commit is contained in:
Cilibiu Bogdan
2017-11-07 18:40:19 +02:00
committed by Eugenio Romano
parent cc821542a2
commit 5fff7fcb29
18 changed files with 375 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -733,8 +733,30 @@ That will give the following output:
![Custom empty folder](docassets/images/empty-folder-template-custom.png) ![Custom empty folder](docassets/images/empty-folder-template-custom.png)
### Custom 'permission denied' template
By default DocumentList provides the following content for permission denied:
![Default no permission](docassets/images/no-permission-default.png)
This can be changed by means of the custom html template:
```html
<adf-document-list ...>
<no-permission-content>
<ng-template>
<h1>You don't have permissions</h1>
</ng-template>
</no-permission-content>
</adf-document-list>
```
That will give the following output:
![Custom no permission](docassets/images/no-permission-custom.png)
<!-- Don't edit the See also section. Edit seeAlsoGraph.json and run config/generateSeeAlso.js --> <!-- Don't edit the See also section. Edit seeAlsoGraph.json and run config/generateSeeAlso.js -->
<!-- seealso start --> <!-- seealso start -->
## See also ## See also
- [Datatable component](datatable.component.md) - [Datatable component](datatable.component.md)

View File

@@ -39,6 +39,7 @@ import { FileSizeCellComponent } from './src/components/datatable/filesize-cell.
import { LocationCellComponent } from './src/components/datatable/location-cell.component'; import { LocationCellComponent } from './src/components/datatable/location-cell.component';
import { LoadingContentTemplateDirective } from './src/directives/loading-template.directive'; import { LoadingContentTemplateDirective } from './src/directives/loading-template.directive';
import { NoContentTemplateDirective } from './src/directives/no-content-template.directive'; import { NoContentTemplateDirective } from './src/directives/no-content-template.directive';
import { NoPermissionTemplateDirective } from './src/directives/no-permission-template.directive';
export function directives() { export function directives() {
return [ return [
@@ -52,6 +53,7 @@ export function directives() {
FileSizeCellComponent, FileSizeCellComponent,
LocationCellComponent, LocationCellComponent,
NoContentTemplateDirective, NoContentTemplateDirective,
NoPermissionTemplateDirective,
LoadingContentTemplateDirective LoadingContentTemplateDirective
]; ];
} }

View File

@@ -34,7 +34,7 @@
</thead> </thead>
<tbody> <tbody>
<ng-container *ngIf="!loading"> <ng-container *ngIf="!loading && !noPermission">
<tr *ngFor="let row of data.getRows(); let idx = index" <tr *ngFor="let row of data.getRows(); let idx = index"
role="button" role="button"
[class.is-selected]="row.isSelected" [class.is-selected]="row.isSelected"
@@ -166,6 +166,14 @@
</td> </td>
</tr> </tr>
</ng-container> </ng-container>
<tr *ngIf="!loading && noPermission" class="adf-no-permission__row">
<td class="adf-no-permission__cell">
<ng-template *ngIf="noPermissionTemplate"
ngFor [ngForOf]="[data]"
[ngForTemplate]="noPermissionTemplate">
</ng-template>
</td>
</tr>
<tr *ngIf="loading"> <tr *ngIf="loading">
<td class="adf-loading-content-container" <td class="adf-loading-content-container"
[attr.colspan]="1 + data.getColumns().length"> [attr.colspan]="1 + data.getColumns().length">

View File

@@ -202,6 +202,17 @@
} }
} }
.adf-no-permission {
&__row:hover {
cursor: default;
background-color: inherit;
}
&__cell {
padding: 0 !important;
}
}
.ellipsis-cell { .ellipsis-cell {
.cell-container { .cell-container {
height: 100%; height: 100%;

View File

@@ -96,7 +96,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck
@Input() @Input()
loading: boolean = false; loading: boolean = false;
@Input()
noPermission: boolean = false;
noContentTemplate: TemplateRef<any>; noContentTemplate: TemplateRef<any>;
noPermissionTemplate: TemplateRef<any>;
loadingTemplate: TemplateRef<any>; loadingTemplate: TemplateRef<any>;
isSelectAllChecked: boolean = false; isSelectAllChecked: boolean = false;

View File

@@ -0,0 +1,64 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { DataTableCellComponent } from '../components/datatable/datatable-cell.component';
import { DataTableComponent } from '../components/datatable/datatable.component';
import { DateCellComponent } from '../components/datatable/date-cell.component';
import { FileSizeCellComponent } from '../components/datatable/filesize-cell.component';
import { LocationCellComponent } from '../components/datatable/location-cell.component';
import { MaterialModule } from '../material.module';
import { NoPermissionTemplateDirective } from './no-permission-template.directive';
describe('NoPermissionTemplateDirective', () => {
let dataTable: DataTableComponent;
let directive: NoPermissionTemplateDirective;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
MaterialModule,
CoreModule
],
declarations: [
DataTableComponent,
DataTableCellComponent,
DateCellComponent,
NoPermissionTemplateDirective,
LocationCellComponent,
FileSizeCellComponent
]
}).compileComponents();
}));
beforeEach(() => {
let fixture = TestBed.createComponent(DataTableComponent);
dataTable = fixture.componentInstance;
directive = new NoPermissionTemplateDirective(dataTable);
});
it('should apply template to the datatable', () => {
const template = {};
directive.template = template;
directive.ngAfterContentInit();
expect(dataTable.noPermissionTemplate).toBe(template);
});
});

View File

@@ -0,0 +1,37 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core';
import { DataTableComponent } from '../components/datatable/datatable.component';
@Directive({
selector: 'no-permission-template'
})
export class NoPermissionTemplateDirective implements AfterContentInit {
@ContentChild(TemplateRef)
template: any;
constructor(private dataTable: DataTableComponent) {
}
ngAfterContentInit() {
if (this.dataTable) {
this.dataTable.noPermissionTemplate = this.template;
}
}
}

View File

@@ -30,6 +30,7 @@ import { ContentColumnComponent } from './src/components/content-column/content-
import { ContentNodeSelectorComponent } from './src/components/content-node-selector/content-node-selector.component'; import { ContentNodeSelectorComponent } from './src/components/content-node-selector/content-node-selector.component';
import { DocumentListComponent } from './src/components/document-list.component'; import { DocumentListComponent } from './src/components/document-list.component';
import { EmptyFolderContentDirective } from './src/components/empty-folder/empty-folder-content.directive'; import { EmptyFolderContentDirective } from './src/components/empty-folder/empty-folder-content.directive';
import { NoPermissionContentDirective } from './src/components/no-permission/no-permission-content.directive';
import { DropdownSitesComponent } from './src/components/site-dropdown/sites-dropdown.component'; import { DropdownSitesComponent } from './src/components/site-dropdown/sites-dropdown.component';
import { VersionListComponent } from './src/components/version-manager/version-list.component'; import { VersionListComponent } from './src/components/version-manager/version-list.component';
import { VersionManagerComponent } from './src/components/version-manager/version-manager.component'; import { VersionManagerComponent } from './src/components/version-manager/version-manager.component';
@@ -51,6 +52,7 @@ export * from './src/components/content-action/content-action.component';
export * from './src/components/content-action/content-action-list.component'; export * from './src/components/content-action/content-action-list.component';
export * from './src/components/content-node-selector/content-node-selector.component'; export * from './src/components/content-node-selector/content-node-selector.component';
export * from './src/components/empty-folder/empty-folder-content.directive'; export * from './src/components/empty-folder/empty-folder-content.directive';
export * from './src/components/no-permission/no-permission-content.directive';
export * from './src/components/breadcrumb/breadcrumb.component'; export * from './src/components/breadcrumb/breadcrumb.component';
export * from './src/components/site-dropdown/sites-dropdown.component'; export * from './src/components/site-dropdown/sites-dropdown.component';
@@ -77,6 +79,7 @@ export const DOCUMENT_LIST_DIRECTIVES: any[] = [
ContentActionComponent, ContentActionComponent,
ContentActionListComponent, ContentActionListComponent,
EmptyFolderContentDirective, EmptyFolderContentDirective,
NoPermissionContentDirective,
BreadcrumbComponent, BreadcrumbComponent,
DropdownSitesComponent, DropdownSitesComponent,
DropdownBreadcrumbComponent, DropdownBreadcrumbComponent,

View File

@@ -246,6 +246,7 @@ describe('ContentNodeSelectorComponent', () => {
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode)); spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test')); spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test'));
spyOn(sitesApiService, 'getSites').and.returnValue(Observable.of([])); spyOn(sitesApiService, 'getSites').and.returnValue(Observable.of([]));
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
component.currentFolderId = 'cat-girl-nuku-nuku'; component.currentFolderId = 'cat-girl-nuku-nuku';
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -347,6 +348,12 @@ describe('ContentNodeSelectorComponent', () => {
} }
beforeEach(() => { beforeEach(() => {
const documentListService = TestBed.get(DocumentListService);
const expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
component.currentFolderId = 'cat-girl-nuku-nuku'; component.currentFolderId = 'cat-girl-nuku-nuku';
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -458,7 +465,7 @@ describe('ContentNodeSelectorComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]')); let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList).not.toBeNull('Document list should be shown'); expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.currentFolderId).toBeNull(); expect(documentList.componentInstance.currentFolderId).toBeUndefined();
}); });
})); }));

View File

@@ -9,6 +9,7 @@
[rowStyle]="rowStyle" [rowStyle]="rowStyle"
[rowStyleClass]="rowStyleClass" [rowStyleClass]="rowStyleClass"
[loading]="loading" [loading]="loading"
[noPermission]="noPermission"
[showHeader]="!isEmpty()" [showHeader]="!isEmpty()"
(showRowContextMenu)="onShowRowContextMenu($event)" (showRowContextMenu)="onShowRowContextMenu($event)"
(showRowActionsMenu)="onShowRowActionsMenu($event)" (showRowActionsMenu)="onShowRowActionsMenu($event)"
@@ -34,6 +35,17 @@
</no-content-template> </no-content-template>
</div> </div>
<div *ngIf="!isNoPermissionTemplateDefined()">
<no-permission-template>
<ng-template>
<div class="adf-no-permission__template">
<mat-icon>ic_error</mat-icon>
<p class="adf-no-permission__template--text">{{ 'ADF-DOCUMENT-LIST.NO_PERMISSION' | translate }}</p>
</div>
</ng-template>
</no-permission-template>
</div>
<div> <div>
<loading-content-template> <loading-content-template>
<ng-template> <ng-template>

View File

@@ -16,6 +16,28 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.adf-no-permission__template {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
min-height: 300px;
mat-icon {
font-size: 52px;
height: 52px;
width: 52px;
direction: rtl;
}
&--text {
color: mat-color($foreground, text);
font-size: 16px;
}
}
.document-list__this-space-is-empty { .document-list__this-space-is-empty {
height: 32px; height: 32px;
opacity: 0.26; opacity: 0.26;

View File

@@ -741,6 +741,25 @@ describe('DocumentList', () => {
expect(documentList.isEmptyTemplateDefined()).toBeFalsy(); expect(documentList.isEmptyTemplateDefined()).toBeFalsy();
}); });
it('should require dataTable to check no permission template', () => {
documentList.dataTable = null;
expect(documentList.isNoPermissionTemplateDefined()).toBe(false);
});
it('should return true if custom permission template is provided', () => {
documentList.noPermissionTemplate = <TemplateRef<any>> {};
documentList.dataTable = new DataTableComponent(null, null);
expect(documentList.isNoPermissionTemplateDefined()).toBe(true);
});
it('should return false if no custom permission template is provided', () => {
documentList.noPermissionTemplate = null;
documentList.dataTable = new DataTableComponent(null, null);
expect(documentList.isNoPermissionTemplateDefined()).toBe(false);
});
it('should empty folder NOT show the pagination', () => { it('should empty folder NOT show the pagination', () => {
documentList.emptyFolderTemplate = <TemplateRef<any>> {}; documentList.emptyFolderTemplate = <TemplateRef<any>> {};
documentList.dataTable = new DataTableComponent(null, null); documentList.dataTable = new DataTableComponent(null, null);
@@ -824,6 +843,44 @@ describe('DocumentList', () => {
expect(documentList.loadFolderNodesByFolderNodeId).toHaveBeenCalled(); expect(documentList.loadFolderNodesByFolderNodeId).toHaveBeenCalled();
}); });
it('should emit error when getFolderNode fails', (done) => {
const error = { message: '{ "error": { "statusCode": 501 } }' } ;
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.reject(error));
documentList.error.subscribe(val => {
expect(val).toBe(error);
done();
});
documentList.loadFolderByNodeId('123');
});
it('should emit error when loadFolderNodesByFolderNodeId fails', (done) => {
const error = { message: '{ "error": { "statusCode": 501 } }' } ;
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(fakeNodeWithCreatePermission));
spyOn(documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.reject(error));
documentList.error.subscribe(val => {
expect(val).toBe(error);
done();
});
documentList.loadFolderByNodeId('123');
});
it('should set no permision when getFolderNode fails with 403', (done) => {
const error = { message: '{ "error": { "statusCode": 403 } }' } ;
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.reject(error));
documentList.error.subscribe(val => {
expect(val).toBe(error);
expect(documentList.noPermission).toBe(true);
done();
});
documentList.loadFolderByNodeId('123');
});
xit('should load previous page if there are no other elements in multi page table', (done) => { xit('should load previous page if there are no other elements in multi page table', (done) => {
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692'; documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
documentList.folderNode = new NodeMinimal(); documentList.folderNode = new NodeMinimal();

View File

@@ -116,6 +116,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
supportedPageSizes: number[]; supportedPageSizes: number[];
infiniteLoading: boolean = false; infiniteLoading: boolean = false;
noPermission: boolean = false;
selection = new Array<MinimalNodeEntity>(); selection = new Array<MinimalNodeEntity>();
skipCount: number = 0; skipCount: number = 0;
@@ -161,6 +162,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
errorMessage; errorMessage;
actions: ContentActionModel[] = []; actions: ContentActionModel[] = [];
emptyFolderTemplate: TemplateRef<any>; emptyFolderTemplate: TemplateRef<any>;
noPermissionTemplate: TemplateRef<any>;
contextActionHandler: Subject<any> = new Subject(); contextActionHandler: Subject<any> = new Subject();
data: ShareDataTableAdapter; data: ShareDataTableAdapter;
@@ -305,6 +307,15 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
return false; return false;
} }
isNoPermissionTemplateDefined(): boolean {
if (this.dataTable) {
if (this.noPermissionTemplate) {
return true;
}
}
return false;
}
isMobile(): boolean { isMobile(): boolean {
return !!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); return !!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
} }
@@ -440,14 +451,21 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
this.loadRecent(); this.loadRecent();
} else { } else {
this.documentListService this.documentListService
.getFolderNode(nodeId).then(node => { .getFolderNode(nodeId)
.then(node => {
this.folderNode = node; this.folderNode = node;
this.currentFolderId = node.id; this.currentFolderId = node.id;
this.skipCount = 0; this.skipCount = 0;
this.currentNodeAllowableOperations = node['allowableOperations'] ? node['allowableOperations'] : []; this.currentNodeAllowableOperations = node['allowableOperations'] ? node['allowableOperations'] : [];
this.loadFolderNodesByFolderNodeId(node.id, this.pageSize, this.skipCount).catch(err => this.error.emit(err)); return this.loadFolderNodesByFolderNodeId(node.id, this.pageSize, this.skipCount);
}) })
.catch(err => this.error.emit(err)); .catch(err => {
if (JSON.parse(err.message).error.statusCode === 403) {
this.loading = false;
this.noPermission = true;
}
this.error.emit(err);
});
} }
} }

View File

@@ -0,0 +1,66 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { MatProgressSpinnerModule } from '@angular/material';
import { CoreModule } from 'ng2-alfresco-core';
import { DataTableComponent, DataTableModule } from 'ng2-alfresco-datatable';
import { DocumentListService } from '../../services/document-list.service';
import { DocumentListComponent } from './../document-list.component';
import { NoPermissionContentDirective } from './no-permission-content.directive';
describe('NoPermissionContentDirective', () => {
let noPermissionContent: NoPermissionContentDirective;
let documentList: DocumentListComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule,
DataTableModule,
MatProgressSpinnerModule
],
declarations: [
DocumentListComponent
],
providers: [
DocumentListService
]
}).compileComponents();
}));
beforeEach(() => {
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
documentList.dataTable = new DataTableComponent(null, null);
noPermissionContent = new NoPermissionContentDirective(documentList);
});
it('should be defined', () => {
expect(noPermissionContent).toBeDefined();
});
it('should set template', () => {
noPermissionContent.template = '<example>';
noPermissionContent.ngAfterContentInit();
expect(noPermissionContent.template).toBe(documentList.noPermissionTemplate);
expect(noPermissionContent.template).toBe(documentList.dataTable.noPermissionTemplate);
});
});

View File

@@ -0,0 +1,36 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core';
import { DocumentListComponent } from './../document-list.component';
@Directive({
selector: 'no-permission-content'
})
export class NoPermissionContentDirective implements AfterContentInit {
@ContentChild(TemplateRef)
template: any;
constructor(private documentList: DocumentListComponent) {
}
ngAfterContentInit() {
this.documentList.noPermissionTemplate = this.template;
this.documentList.dataTable.noPermissionTemplate = this.template;
}
}

View File

@@ -3,6 +3,7 @@
"EMPTY": { "EMPTY": {
"HEADER": "This folder is empty" "HEADER": "This folder is empty"
}, },
"NO_PERMISSION": "You don't have permission to view this file or folder.",
"LAYOUT": { "LAYOUT": {
"CREATED": "Created", "CREATED": "Created",
"THUMBNAIL": "Thumbnail", "THUMBNAIL": "Thumbnail",