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