Commit 22c795d6dcb9a4e296a2e860dced7a2b52e25f76

Authored by Victor Costa
1 parent 0456e67c

Hide action buttons related to articles when user doesn't have permission to execute it

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 &quot;../hotspot/article-toolbar-hotspot @@ -6,6 +6,7 @@ import {ArticleToolbarHotspotComponent} from &quot;../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 &quot;./../shared/services/notification.service&quot;; @@ -16,7 +17,8 @@ import { NotificationService } from &quot;./../shared/services/notification.service&quot;;
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 &quot;../layout/sidebar/sidebar.component&quot;; @@ -39,7 +39,7 @@ import {SidebarComponent} from &quot;../layout/sidebar/sidebar.component&quot;;
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(&quot;Components&quot;, () =&gt; { @@ -73,5 +73,16 @@ describe(&quot;Components&quot;, () =&gt; {
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 +}