Commit b6a7edb744689b21a1c0f81e21a3eb0f2a05ef70
Exists in
master
and in
14 other branches
Merge branch 'article-actions-permission' into 'master'
Hide article buttons when user doesn't have permission to execute it See merge request !34
Showing
10 changed files
with
98 additions
and
15 deletions
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 | +} |