diff --git a/docs/docassets/images/no-permission-custom.png b/docs/docassets/images/no-permission-custom.png new file mode 100644 index 0000000000..b551660444 Binary files /dev/null and b/docs/docassets/images/no-permission-custom.png differ diff --git a/docs/docassets/images/no-permission-default.png b/docs/docassets/images/no-permission-default.png new file mode 100644 index 0000000000..12e364d6f3 Binary files /dev/null and b/docs/docassets/images/no-permission-default.png differ diff --git a/docs/document-list.component.md b/docs/document-list.component.md index 37bd3d0b82..c096534101 100644 --- a/docs/document-list.component.md +++ b/docs/document-list.component.md @@ -733,8 +733,30 @@ That will give the following output: ![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 + + + +

You don't have permissions

+
+
+
+``` + +That will give the following output: + +![Custom no permission](docassets/images/no-permission-custom.png) + ## See also - [Datatable component](datatable.component.md) diff --git a/ng2-components/ng2-alfresco-datatable/index.ts b/ng2-components/ng2-alfresco-datatable/index.ts index 16b3193e94..17aabfe106 100644 --- a/ng2-components/ng2-alfresco-datatable/index.ts +++ b/ng2-components/ng2-alfresco-datatable/index.ts @@ -39,6 +39,7 @@ import { FileSizeCellComponent } from './src/components/datatable/filesize-cell. import { LocationCellComponent } from './src/components/datatable/location-cell.component'; import { LoadingContentTemplateDirective } from './src/directives/loading-template.directive'; import { NoContentTemplateDirective } from './src/directives/no-content-template.directive'; +import { NoPermissionTemplateDirective } from './src/directives/no-permission-template.directive'; export function directives() { return [ @@ -52,6 +53,7 @@ export function directives() { FileSizeCellComponent, LocationCellComponent, NoContentTemplateDirective, + NoPermissionTemplateDirective, LoadingContentTemplateDirective ]; } diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html index 40be7d37c8..7f931e0082 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.html @@ -34,7 +34,7 @@ - + + + + + + + diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.scss b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.scss index 2bda6c680e..b06977844a 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.scss +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.scss @@ -202,6 +202,17 @@ } } + .adf-no-permission { + &__row:hover { + cursor: default; + background-color: inherit; + } + + &__cell { + padding: 0 !important; + } + } + .ellipsis-cell { .cell-container { height: 100%; diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.ts b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.ts index fa2e24523d..40d3ee4e40 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.ts +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable/datatable.component.ts @@ -96,7 +96,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck @Input() loading: boolean = false; + @Input() + noPermission: boolean = false; + noContentTemplate: TemplateRef; + noPermissionTemplate: TemplateRef; loadingTemplate: TemplateRef; isSelectAllChecked: boolean = false; diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.spec.ts b/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.spec.ts new file mode 100644 index 0000000000..d8d6cfdaf5 --- /dev/null +++ b/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.spec.ts @@ -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); + }); +}); diff --git a/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.ts b/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.ts new file mode 100644 index 0000000000..878f646df2 --- /dev/null +++ b/ng2-components/ng2-alfresco-datatable/src/directives/no-permission-template.directive.ts @@ -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; + } + } +} diff --git a/ng2-components/ng2-alfresco-documentlist/index.ts b/ng2-components/ng2-alfresco-documentlist/index.ts index a8b23ea3c1..97d3991a8a 100644 --- a/ng2-components/ng2-alfresco-documentlist/index.ts +++ b/ng2-components/ng2-alfresco-documentlist/index.ts @@ -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 { DocumentListComponent } from './src/components/document-list.component'; 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 { VersionListComponent } from './src/components/version-manager/version-list.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-node-selector/content-node-selector.component'; 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/site-dropdown/sites-dropdown.component'; @@ -77,6 +79,7 @@ export const DOCUMENT_LIST_DIRECTIVES: any[] = [ ContentActionComponent, ContentActionListComponent, EmptyFolderContentDirective, + NoPermissionContentDirective, BreadcrumbComponent, DropdownSitesComponent, DropdownBreadcrumbComponent, diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-node-selector/content-node-selector.component.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-node-selector/content-node-selector.component.spec.ts index c26073e6ce..ac9cb2a798 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-node-selector/content-node-selector.component.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-node-selector/content-node-selector.component.spec.ts @@ -246,6 +246,7 @@ describe('ContentNodeSelectorComponent', () => { spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode)); spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test')); spyOn(sitesApiService, 'getSites').and.returnValue(Observable.of([])); + spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve()); component.currentFolderId = 'cat-girl-nuku-nuku'; fixture.detectChanges(); }); @@ -347,6 +348,12 @@ describe('ContentNodeSelectorComponent', () => { } beforeEach(() => { + const documentListService = TestBed.get(DocumentListService); + const expectedDefaultFolderNode = { path: { elements: [] } }; + + spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode)); + spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve()); + component.currentFolderId = 'cat-girl-nuku-nuku'; fixture.detectChanges(); }); @@ -458,7 +465,7 @@ describe('ContentNodeSelectorComponent', () => { fixture.detectChanges(); 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.componentInstance.currentFolderId).toBeNull(); + expect(documentList.componentInstance.currentFolderId).toBeUndefined(); }); })); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html index 0c01856563..253e77e4c2 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.html @@ -9,6 +9,7 @@ [rowStyle]="rowStyle" [rowStyleClass]="rowStyleClass" [loading]="loading" + [noPermission]="noPermission" [showHeader]="!isEmpty()" (showRowContextMenu)="onShowRowContextMenu($event)" (showRowActionsMenu)="onShowRowActionsMenu($event)" @@ -34,6 +35,17 @@ +
+ + +
+ ic_error +

{{ 'ADF-DOCUMENT-LIST.NO_PERMISSION' | translate }}

+
+
+
+
+
diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.scss b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.scss index 70a24f6775..60bfe047ea 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.scss +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.scss @@ -16,6 +16,28 @@ 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 { height: 32px; opacity: 0.26; diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts index 194a646677..a84131d501 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.spec.ts @@ -741,6 +741,25 @@ describe('DocumentList', () => { 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 = > {}; + 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', () => { documentList.emptyFolderTemplate = > {}; documentList.dataTable = new DataTableComponent(null, null); @@ -824,6 +843,44 @@ describe('DocumentList', () => { 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) => { documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692'; documentList.folderNode = new NodeMinimal(); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts index 5b34a7c32c..ddc426fa4e 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.component.ts @@ -116,6 +116,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni supportedPageSizes: number[]; infiniteLoading: boolean = false; + noPermission: boolean = false; selection = new Array(); skipCount: number = 0; @@ -161,6 +162,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni errorMessage; actions: ContentActionModel[] = []; emptyFolderTemplate: TemplateRef; + noPermissionTemplate: TemplateRef; contextActionHandler: Subject = new Subject(); data: ShareDataTableAdapter; @@ -305,6 +307,15 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni return false; } + isNoPermissionTemplateDefined(): boolean { + if (this.dataTable) { + if (this.noPermissionTemplate) { + return true; + } + } + return false; + } + isMobile(): boolean { 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(); } else { this.documentListService - .getFolderNode(nodeId).then(node => { + .getFolderNode(nodeId) + .then(node => { this.folderNode = node; this.currentFolderId = node.id; this.skipCount = 0; 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); + }); } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.spec.ts new file mode 100644 index 0000000000..711a212830 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.spec.ts @@ -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 = ''; + + noPermissionContent.ngAfterContentInit(); + + expect(noPermissionContent.template).toBe(documentList.noPermissionTemplate); + expect(noPermissionContent.template).toBe(documentList.dataTable.noPermissionTemplate); + }); +}); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.ts b/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.ts new file mode 100644 index 0000000000..582843d8d6 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/src/components/no-permission/no-permission-content.directive.ts @@ -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; + } +} diff --git a/ng2-components/ng2-alfresco-documentlist/src/i18n/en.json b/ng2-components/ng2-alfresco-documentlist/src/i18n/en.json index c1b3b5213f..3244b92a65 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/i18n/en.json +++ b/ng2-components/ng2-alfresco-documentlist/src/i18n/en.json @@ -3,6 +3,7 @@ "EMPTY": { "HEADER": "This folder is empty" }, + "NO_PERMISSION": "You don't have permission to view this file or folder.", "LAYOUT": { "CREATED": "Created", "THUMBNAIL": "Thumbnail",