Merge Request #34
← To merge requests
From
article-actions-permission
into
master
Hide article buttons when user doesn't have permission to execute it
Commits (1)
Showing
10 changed files
Show diff stats
src/app/article/article-default-view-component.spec.ts
| @@ -65,16 +65,36 @@ describe("Components", () => { | @@ -65,16 +65,36 @@ describe("Components", () => { | ||
| 65 | expect(state.transitionTo).toHaveBeenCalled(); | 65 | expect(state.transitionTo).toHaveBeenCalled(); |
| 66 | }); | 66 | }); |
| 67 | 67 | ||
| 68 | + it("hide button to delete article when user doesn't have permission", () => { | ||
| 69 | + expect(helper.find(".article-toolbar .delete-article").attr('style')).toEqual("display: none; "); | ||
| 70 | + }); | ||
| 71 | + | ||
| 72 | + it("hide button to edit article when user doesn't have permission", () => { | ||
| 73 | + expect(helper.find(".article-toolbar .edit-article").attr('style')).toEqual("display: none; "); | ||
| 74 | + }); | ||
| 75 | + | ||
| 76 | + it("show button to edit article when user has permission", () => { | ||
| 77 | + (<any>helper.component['article'])['permissions'] = ['allow_edit']; | ||
| 78 | + helper.detectChanges(); | ||
| 79 | + expect(helper.find(".article-toolbar .edit-article").attr('style')).toEqual(''); | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + it("show button to delete article when user has permission", () => { | ||
| 83 | + (<any>helper.component['article'])['permissions'] = ['allow_delete']; | ||
| 84 | + helper.detectChanges(); | ||
| 85 | + expect(helper.find(".article-toolbar .delete-article").attr('style')).toEqual(''); | ||
| 86 | + }); | ||
| 87 | + | ||
| 68 | /** | 88 | /** |
| 69 | * Execute the delete method on the target component | 89 | * Execute the delete method on the target component |
| 70 | */ | 90 | */ |
| 71 | function doDeleteArticle() { | 91 | function doDeleteArticle() { |
| 72 | // Create a mock for the notification service confirmation | 92 | // Create a mock for the notification service confirmation |
| 73 | - spyOn(helper.component.notificationService, 'confirmation').and.callFake(function (params: Function) { | 93 | + spyOn(helper.component.notificationService, 'confirmation').and.callFake(function(params: Function) { |
| 74 | 94 | ||
| 75 | }); | 95 | }); |
| 76 | // Create a mock for the ArticleService removeArticle method | 96 | // Create a mock for the ArticleService removeArticle method |
| 77 | - spyOn(helper.component.articleService, 'remove').and.callFake(function (param: noosfero.Article) { | 97 | + spyOn(helper.component.articleService, 'remove').and.callFake(function(param: noosfero.Article) { |
| 78 | 98 | ||
| 79 | return { | 99 | return { |
| 80 | catch: () => { } | 100 | catch: () => { } |
src/app/article/article-default-view.component.ts
| @@ -6,6 +6,7 @@ import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot | @@ -6,6 +6,7 @@ import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot | ||
| 6 | import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; | 6 | import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; |
| 7 | import {ArticleService} from "./../../lib/ng-noosfero-api/http/article.service"; | 7 | import {ArticleService} from "./../../lib/ng-noosfero-api/http/article.service"; |
| 8 | import { NotificationService } from "./../shared/services/notification.service"; | 8 | import { NotificationService } from "./../shared/services/notification.service"; |
| 9 | +import {PermissionDirective} from '../shared/components/permission/permission.directive'; | ||
| 9 | 10 | ||
| 10 | /** | 11 | /** |
| 11 | * @ngdoc controller | 12 | * @ngdoc controller |
| @@ -16,7 +17,8 @@ import { NotificationService } from "./../shared/services/notification.service"; | @@ -16,7 +17,8 @@ import { NotificationService } from "./../shared/services/notification.service"; | ||
| 16 | */ | 17 | */ |
| 17 | @Component({ | 18 | @Component({ |
| 18 | selector: 'noosfero-default-article', | 19 | selector: 'noosfero-default-article', |
| 19 | - templateUrl: 'app/article/article.html' | 20 | + templateUrl: 'app/article/article.html', |
| 21 | + directives: [PermissionDirective] | ||
| 20 | }) | 22 | }) |
| 21 | @Inject("$state", ArticleService, NotificationService) | 23 | @Inject("$state", ArticleService, NotificationService) |
| 22 | export class ArticleDefaultViewComponent { | 24 | export class ArticleDefaultViewComponent { |
src/app/article/article.html
| @@ -4,13 +4,15 @@ | @@ -4,13 +4,15 @@ | ||
| 4 | </div> | 4 | </div> |
| 5 | 5 | ||
| 6 | <div class="sub-header clearfix"> | 6 | <div class="sub-header clearfix"> |
| 7 | - <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> | ||
| 8 | - <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} | ||
| 9 | - </a> | ||
| 10 | - <a href="#" class="btn btn-default btn-xs" ng-click="ctrl.delete()"> | ||
| 11 | - <i class="fa fa-trash-o fa-fw fa-lg" ng-click="ctrl.delete()"></i> {{"article.actions.delete" | translate}} | ||
| 12 | - </a> | ||
| 13 | - <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | 7 | + <div class="article-toolbar"> |
| 8 | + <a href="#" permission="ctrl.article.permissions" permission-action="allow_edit" class="btn btn-default btn-xs edit-article" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> | ||
| 9 | + <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} | ||
| 10 | + </a> | ||
| 11 | + <a href="#" permission="ctrl.article.permissions" permission-action="allow_delete" class="btn btn-default btn-xs delete-article" ng-click="ctrl.delete()"> | ||
| 12 | + <i class="fa fa-trash-o fa-fw fa-lg" ng-click="ctrl.delete()"></i> {{"article.actions.delete" | translate}} | ||
| 13 | + </a> | ||
| 14 | + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | ||
| 15 | + </div> | ||
| 14 | <div class="page-info pull-right small text-muted"> | 16 | <div class="page-info pull-right small text-muted"> |
| 15 | <span class="time"> | 17 | <span class="time"> |
| 16 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | 18 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> |
src/app/article/content-viewer/navbar-actions.html
| 1 | -<ul class="nav navbar-nav"> | 1 | +<ul class="nav navbar-nav" permission="vm.profile.permissions" permission-action="allow_edit"> |
| 2 | <li class="dropdown profile-menu" uib-dropdown> | 2 | <li class="dropdown profile-menu" uib-dropdown> |
| 3 | <a class="btn dropdown-toggle" data-toggle="dropdown" uib-dropdown-toggle> | 3 | <a class="btn dropdown-toggle" data-toggle="dropdown" uib-dropdown-toggle> |
| 4 | {{"navbar.content_viewer_actions.new_item" | translate}} | 4 | {{"navbar.content_viewer_actions.new_item" | translate}} |
| @@ -19,4 +19,3 @@ | @@ -19,4 +19,3 @@ | ||
| 19 | </li> | 19 | </li> |
| 20 | 20 | ||
| 21 | </ul> | 21 | </ul> |
| 22 | - |
src/app/main/main.component.ts
| @@ -39,7 +39,7 @@ import {SidebarComponent} from "../layout/sidebar/sidebar.component"; | @@ -39,7 +39,7 @@ import {SidebarComponent} from "../layout/sidebar/sidebar.component"; | ||
| 39 | 39 | ||
| 40 | import {MainBlockComponent} from "../layout/blocks/main/main-block.component"; | 40 | import {MainBlockComponent} from "../layout/blocks/main/main-block.component"; |
| 41 | import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component"; | 41 | import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component"; |
| 42 | - | 42 | +import {PermissionDirective} from "../shared/components/permission/permission.directive"; |
| 43 | 43 | ||
| 44 | /** | 44 | /** |
| 45 | * @ngdoc controller | 45 | * @ngdoc controller |
| @@ -100,7 +100,7 @@ export class EnvironmentContent { | @@ -100,7 +100,7 @@ export class EnvironmentContent { | ||
| 100 | LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent, | 100 | LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent, |
| 101 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, | 101 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, |
| 102 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent, | 102 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent, |
| 103 | - LoginBlockComponent, CustomContentComponent | 103 | + LoginBlockComponent, CustomContentComponent, PermissionDirective |
| 104 | ].concat(plugins.mainComponents).concat(plugins.hotspots), | 104 | ].concat(plugins.mainComponents).concat(plugins.hotspots), |
| 105 | 105 | ||
| 106 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService] | 106 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService] |
src/app/profile/custom-content/custom-content.component.spec.ts
| @@ -73,5 +73,16 @@ describe("Components", () => { | @@ -73,5 +73,16 @@ describe("Components", () => { | ||
| 73 | helper.component.save(); | 73 | helper.component.save(); |
| 74 | expect(helper.component['notificationService'].success).toHaveBeenCalled(); | 74 | expect(helper.component['notificationService'].success).toHaveBeenCalled(); |
| 75 | }); | 75 | }); |
| 76 | + | ||
| 77 | + it("hide button to edit content when user doesn't have the permission", () => { | ||
| 78 | + helper.detectChanges(); | ||
| 79 | + expect(helper.find(".actions").attr('style')).toEqual('display: none; '); | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + it("show button to edit content when user has the permission", () => { | ||
| 83 | + (<any>helper.component['profile'])['permissions'] = ['allow_edit']; | ||
| 84 | + helper.detectChanges(); | ||
| 85 | + expect(helper.find(".actions").attr('style')).toEqual(''); | ||
| 86 | + }); | ||
| 76 | }); | 87 | }); |
| 77 | }); | 88 | }); |
src/app/profile/custom-content/custom-content.component.ts
| 1 | import {Component, Input, Inject} from 'ng-forward'; | 1 | import {Component, Input, Inject} from 'ng-forward'; |
| 2 | import {ProfileService} from '../../../lib/ng-noosfero-api/http/profile.service'; | 2 | import {ProfileService} from '../../../lib/ng-noosfero-api/http/profile.service'; |
| 3 | import {NotificationService} from '../../shared/services/notification.service'; | 3 | import {NotificationService} from '../../shared/services/notification.service'; |
| 4 | +import {PermissionDirective} from '../../shared/components/permission/permission.directive'; | ||
| 4 | 5 | ||
| 5 | @Component({ | 6 | @Component({ |
| 6 | selector: 'custom-content', | 7 | selector: 'custom-content', |
| 7 | templateUrl: "app/profile/custom-content/custom-content.html", | 8 | templateUrl: "app/profile/custom-content/custom-content.html", |
| 9 | + directives: [PermissionDirective] | ||
| 8 | }) | 10 | }) |
| 9 | @Inject("$uibModal", "$scope", ProfileService, NotificationService) | 11 | @Inject("$uibModal", "$scope", ProfileService, NotificationService) |
| 10 | export class CustomContentComponent { | 12 | export class CustomContentComponent { |
src/app/profile/custom-content/custom-content.html
| 1 | <div class="custom-content"> | 1 | <div class="custom-content"> |
| 2 | - <div class="actions"> | 2 | + <div class="actions" permission="ctrl.profile.permissions" permission-action="allow_edit"> |
| 3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button> | 3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button> |
| 4 | </div> | 4 | </div> |
| 5 | <div class="content" ng-bind-html="ctrl.content"></div> | 5 | <div class="content" ng-bind-html="ctrl.content"></div> |
src/app/shared/components/permission/permission.directive.spec.ts
0 → 100644
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +import {Input, provide, Component} from 'ng-forward'; | ||
| 2 | +import {PermissionDirective} from "./permission.directive"; | ||
| 3 | +import * as helpers from "../../../../spec/helpers"; | ||
| 4 | + | ||
| 5 | +const htmlTemplate: string = '<div permission="ctrl.permissions" permission-action="action"></div>'; | ||
| 6 | + | ||
| 7 | +describe("Permission directive", () => { | ||
| 8 | + | ||
| 9 | + let element = jasmine.createSpyObj("$element", ["css"]); | ||
| 10 | + let scope = jasmine.createSpyObj("$scope", ["$watch", "$eval"]); | ||
| 11 | + scope.$watch = (param: string, f: Function) => { f(); }; | ||
| 12 | + scope.$eval = (param: any) => { return param; }; | ||
| 13 | + | ||
| 14 | + it("hide element when there is no permission action in the permissions array", (done: Function) => { | ||
| 15 | + let attrs: any = { permission: [], permissionAction: 'action' }; | ||
| 16 | + let directive = new PermissionDirective(<any>attrs, scope, element); | ||
| 17 | + expect(element.css).toHaveBeenCalledWith('display', 'none'); | ||
| 18 | + done(); | ||
| 19 | + }); | ||
| 20 | + | ||
| 21 | + it("show element when the permission action exists in the permissions array", (done: Function) => { | ||
| 22 | + let attrs = { permission: ['action'], permissionAction: 'action' }; | ||
| 23 | + let directive = new PermissionDirective(<any>attrs, scope, element); | ||
| 24 | + expect(element.css).toHaveBeenCalledWith('display', ''); | ||
| 25 | + done(); | ||
| 26 | + }); | ||
| 27 | +}); |
src/app/shared/components/permission/permission.directive.ts
0 → 100644
| @@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
| 1 | +import {Directive, Inject, Input} from "ng-forward"; | ||
| 2 | + | ||
| 3 | +@Directive({ | ||
| 4 | + selector: '[permission]' | ||
| 5 | +}) | ||
| 6 | +@Inject('$attrs', '$scope', '$element') | ||
| 7 | +export class PermissionDirective { | ||
| 8 | + | ||
| 9 | + constructor($attrs: ng.IAttributes, $scope: ng.IScope, $element: any) { | ||
| 10 | + $scope.$watch($attrs['permission'], () => { | ||
| 11 | + let permissions = $scope.$eval($attrs['permission']); | ||
| 12 | + let permissionAction = $attrs['permissionAction']; | ||
| 13 | + if (!permissions || permissions.indexOf(permissionAction) < 0) { | ||
| 14 | + $element.css("display", "none"); | ||
| 15 | + } else { | ||
| 16 | + $element.css("display", ""); | ||
| 17 | + } | ||
| 18 | + }); | ||
| 19 | + } | ||
| 20 | +} |
-
Reassigned to @carloseugenio
-
Assignee removed
-
Reassigned to @carloseugenio
-
mentioned in commit b6a7edb744689b21a1c0f81e21a3eb0f2a05ef70
-
Assignee removed