Skip to content

Commit cdbb45f

Browse files
committed
Merge branch 'roles-management-enhancement' into dev
2 parents ae63221 + 21a44c6 commit cdbb45f

21 files changed

+1377
-11
lines changed

src/app/app.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,6 @@ angular.module('supportAdminApp', [
9292
data: { pageTitle: 'User Management' },
9393
resolve: { auth: authenticate }
9494
})
95-
.state('index.permission_management', {
96-
url: '/permission_management',
97-
templateUrl: 'app/permission_management/permission_management.html',
98-
data: { pageTitle: 'Permission Management' },
99-
controller: 'PermissionManagementCtrl',
100-
controllerAs: 'ctrl',
101-
resolve: { auth: authenticate }
102-
})
10395
.state('index.submissions', {
10496
abstract: true,
10597
url: '/submissions',
@@ -303,6 +295,41 @@ angular.module('supportAdminApp', [
303295
data: { pageTitle: 'Add Group Members' },
304296
resolve: { auth: authenticate }
305297
})
298+
.state('index.roles', {
299+
abstract: true,
300+
url: '/roles',
301+
templateUrl: 'app/roles/roles.html',
302+
data: { pageTitle: 'Roles' },
303+
controller: 'permissionmanagement.RolesController'
304+
})
305+
.state('index.roles.list', {
306+
url: '/list',
307+
templateUrl: 'app/roles/roles.list.html',
308+
data: { pageTitle: 'Roles' },
309+
controller: 'permissionmanagement.RolesListController',
310+
controllerAs: 'ctrl',
311+
resolve: { auth: authenticate }
312+
})
313+
.state('index.rolemembers', {
314+
abstract: true,
315+
url: '/rolemembers/:roleId',
316+
templateUrl: 'app/rolemembers/rolemembers.html',
317+
data: { pageTitle: 'Role Members' },
318+
controller: 'permissionmanagement.RoleMembersController'
319+
})
320+
.state('index.rolemembers.list', {
321+
url: '/list',
322+
templateUrl: 'app/rolemembers/rolemembers.list.html',
323+
controller: 'permissionmanagement.RoleMembersListController',
324+
resolve: { auth: authenticate }
325+
})
326+
.state('index.rolemembers.new', {
327+
url: '/new',
328+
templateUrl: 'app/rolemembers/rolemembers.new.html',
329+
controller: 'permissionmanagement.RoleMembersNewController',
330+
data: { pageTitle: 'Add Role Members' },
331+
resolve: { auth: authenticate }
332+
})
306333
.state('index.billingaccounts', {
307334
abstract: true,
308335
url: '/billingaccounts',

src/app/groups/groups.service.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,31 @@ angular.module('supportAdminApp')
5454
}).catch(GroupService.handleError);
5555
};
5656

57+
/**
58+
* Get a groups of the particular member
59+
*
60+
* @param {String} memberId member id
61+
* @param {String} membershipType membership type: 'user' or 'group'
62+
* @return {Promise} promise get a members group list
63+
*/
64+
GroupService.findByMember = function(memberId, membershipType) {
65+
return $http({
66+
method: 'GET',
67+
url: GroupService.getBasePath() + '/groups/?memberId=' + memberId + '&membershipType=' + membershipType,
68+
headers: {
69+
"Content-Type": "application/json"
70+
}
71+
}).then(function (response) {
72+
if (response && response.data && response.data.result) {
73+
return response.data.result.content;
74+
} else {
75+
return $q.reject({
76+
error : 'Cannot find data in response'
77+
})
78+
}
79+
}).catch(GroupService.handleError);
80+
};
81+
5782
/**
5883
* Handle API response error
5984
*
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
3+
var module = angular.module('supportAdminApp');
4+
5+
/**
6+
* The parent controller for the rolemembers states
7+
*/
8+
module.controller('permissionmanagement.RoleMembersController', ['$scope', 'AuthService', '$state',
9+
function ($scope, $authService, $state) {
10+
$scope.$state = $state;
11+
12+
/**
13+
* Validate the user authentication
14+
*/
15+
$scope.authorized = function() {
16+
return $authService.isLoggedIn();
17+
};
18+
}
19+
]);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="row wrapper border-bottom white-bg page-heading">
2+
<div class="col-lg-10">
3+
<h2>{{$state.current.data.pageTitle}}</h2>
4+
</div>
5+
<div class="col-md-10 col-lg-12" ng-include src="'components/alert/alert.html'"></div>
6+
</div>
7+
<div ui-view=""></div>
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
'use strict';
2+
3+
var module = angular.module('supportAdminApp');
4+
5+
module.controller('permissionmanagement.RoleMembersListController', [
6+
'$scope', '$rootScope', 'RoleService', 'IdResolverService', '$stateParams', '$state', '$q', 'Alert', '$timeout',
7+
function ($scope, $rootScope, RoleService, IdResolverService, $stateParams, $state, $q, $alert, $timeout) {
8+
9+
// true if role is loading
10+
$scope.isLoading = false;
11+
12+
// true if we are removing a bulk of entries
13+
$scope.isProcessing = false;
14+
15+
// list of members
16+
$scope.members = [];
17+
18+
// current role object
19+
$scope.role = null;
20+
21+
// true if any members were selected in the list
22+
$scope.hasSelected = false;
23+
24+
// if checkbox in table header is selected
25+
$scope.isAllSelected = false;
26+
27+
// keep the list of members visible on the current page
28+
var currentPageMembers = [];
29+
30+
/* Maps user ids, present in the page, into user handles. */
31+
$scope.users = {};
32+
var loadUser = IdResolverService.getUserResolverFunction($scope.users);
33+
34+
/**
35+
* Return members which are selected in the table by checkboxes
36+
*
37+
* @return {Array} member records
38+
*/
39+
function getSelectedMembers() {
40+
// return only members selected on the current page
41+
// to make 100% sure we never delete members we cannot see
42+
return _.filter(currentPageMembers, { isSelected: true });
43+
}
44+
45+
/**
46+
* Get role with members list
47+
*
48+
* @param {String} roleId role id to get
49+
*/
50+
$scope.loadRole = function(roleId) {
51+
$alert.clear();
52+
$scope.isLoading = true;
53+
54+
RoleService.getRole(roleId, ['id', 'roleName', 'subjects']).then(function(role) {
55+
$scope.role = role;
56+
$scope.members = $scope.role.subjects.map(function(memberId) {
57+
return {
58+
id: memberId
59+
}
60+
});
61+
// if have some members we will redraw table using footable plugin
62+
if ($scope.members.length) {
63+
// make sure changes to scope are applied
64+
// and redraw footable table with current member list
65+
$timeout(function() {
66+
$('.footable').trigger('footable_redraw');
67+
$scope.isLoading = false;
68+
});
69+
} else {
70+
$scope.isLoading = false;
71+
}
72+
}).catch(function (error) {
73+
$scope.isLoading = false;
74+
$alert.error(error.error, $rootScope);
75+
});
76+
};
77+
78+
/**
79+
* Checks if any member records are selected in the table
80+
* and updates $scope.hasSelected value
81+
*/
82+
$scope.checkSelected = function() {
83+
$scope.hasSelected = !!getSelectedMembers().length;
84+
}
85+
86+
/**
87+
* Toggle all selected member records of specified type
88+
*/
89+
$scope.toggleAll = function() {
90+
// toggle checkboxes only for current page
91+
currentPageMembers.forEach(function(member) { member.isSelected = $scope.isAllSelected });
92+
}
93+
94+
/**
95+
* Removes member from the current role
96+
* After removing record from the server, it removes the record from the table
97+
*
98+
* @param {Object} member member record
99+
* @return {Promise} promise to remove member
100+
*/
101+
$scope.removeMember = function(member) {
102+
member.isRemoving = true;
103+
return RoleService.unassignRole($stateParams.roleId, member.id).then(function() {
104+
_.remove($scope.members, { id: member.id });
105+
// we remove row of deleted member from footable table
106+
// which will also triggers footable table redraw
107+
// we don't worry to call it after $scope.members is updated so we don't use $timeout here
108+
var $footable = $('.footable');
109+
var ft = $footable.data('footable');
110+
ft.removeRow($footable.find('tr#' + member.id));
111+
}).catch(function(error) {
112+
member.isRemoving = false;
113+
$alert.error('Cannot remove member with id `' + member.memberId + '`. ' + error.error, $rootScope);
114+
});
115+
}
116+
117+
/**
118+
* Remove all selected member records
119+
*/
120+
$scope.removeSelected = function() {
121+
$alert.clear();
122+
$scope.isProcessing = true;
123+
124+
var selectedMembers = getSelectedMembers();
125+
126+
// for now we remove all members in parallel
127+
// it's preferable, because it's faster
128+
// though if there will be any issues with server overload
129+
// it can be rewritten so requests go one by one
130+
$q.all(selectedMembers.map(function(member) {
131+
return $scope.removeMember(member);
132+
})).then(function() {
133+
// uncheck select all checkbox as we already removed all selected items
134+
$scope.isAllSelected = false;
135+
$scope.checkSelected();
136+
}).catch(function(error) {
137+
$alert.error(error.error, $rootScope);
138+
}).finally(function() {
139+
$scope.isProcessing = false;
140+
});
141+
}
142+
143+
/**
144+
* Uncheck all checkboxes
145+
*/
146+
function uncheckAll() {
147+
$scope.isAllSelected = false;
148+
$scope.members.forEach(function(member) {
149+
member.isSelected = false;
150+
});
151+
$scope.checkSelected();
152+
}
153+
154+
/**
155+
* Updates current page member list
156+
*
157+
* @param {Event} event event which contains ft property
158+
*/
159+
function updateCurrentPage(event) {
160+
var ft = event.ft;
161+
162+
// if pager plugin of footable plugin was completely initialized
163+
if (ft.pageInfo && ft.pageInfo.pages && ft.pageInfo.pages.length) {
164+
// get the list of member on the current page
165+
currentPageMembers = ft.pageInfo.pages[ft.pageInfo.currentPage].map(function(row) {
166+
return _.find($scope.members, { id: row.id });
167+
});
168+
// clear queue of currently loading user handles
169+
loadUser.clearQueue();
170+
// load user handles for members visible on the current page
171+
currentPageMembers.forEach(function(member) {
172+
loadUser(member.id);
173+
});
174+
}
175+
}
176+
177+
angular.element(document).ready(function() {
178+
$('.footable').on({
179+
// we watch footable jquery plugin footable_page_filled event
180+
// to update current page member list when rows on the page are changed
181+
footable_page_filled: updateCurrentPage,
182+
// when changing sort order or page, we uncheck all checkboxes
183+
// to avoid having checked but invisible rows
184+
footable_paging: function() {
185+
$scope.$apply(uncheckAll);
186+
},
187+
footable_sorted: function() {
188+
$scope.$apply(uncheckAll);
189+
}
190+
}).footable();
191+
});
192+
193+
// load role on init
194+
$scope.loadRole($stateParams.roleId);
195+
}
196+
]);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<div class="wrapper wrapper-content animated fadeInRight" ng-show="authorized()">
2+
<!-- add member button row -->
3+
<div class="row">
4+
<div class="col col-md-12 col-lg-12">
5+
<a class="btn btn-primary pull-right m-b" ui-sref="index.roles.list" style="margin-left: 20px">
6+
<strong>Back</strong>
7+
</a>
8+
<a class="btn btn-info pull-right m-b" ui-sref="index.rolemembers.new({roleId: role.id})">
9+
<strong><i class="fa fa-plus"></i> Add Members</strong>
10+
</a>
11+
</div>
12+
</div>
13+
<!-- list members row -->
14+
<div class="row">
15+
<div class="col-lg-12">
16+
<div class="ibox float-e-margins">
17+
<div class="ibox-title">
18+
<h2>
19+
<span ng-if="role">{{role.roleName}}</span>
20+
<span class="text-info" ng-if="!role">loading...</span>
21+
</h2>
22+
</div>
23+
<div class="ibox-content">
24+
<div class="text-center" ng-show="isLoading">
25+
<img src="assets/images/loading.gif" />
26+
</div>
27+
<div ng-show="!isLoading">
28+
<table class="footable table table-stripped toggle-arrow-tiny" ng-show="members.length" data-page-size="20">
29+
<thead>
30+
<tr>
31+
<th data-sort-ignore="true"><input type="checkbox" ng-model="isAllSelected" ng-change="toggleAll(); checkSelected();" /></th>
32+
<th data-type="numeric">User Id</th>
33+
<th>Handle</th>
34+
<th data-sort-ignore="true">&nbsp;</th>
35+
</tr>
36+
</thead>
37+
38+
<tbody>
39+
<tr class="animate-repeat" ng-repeat="member in members" id="{{member.id}}">
40+
<td><input type="checkbox" ng-model="member.isSelected" ng-change="checkSelected();" /></td>
41+
<td>{{member.id}}</td>
42+
<td>
43+
<span ng-if="users[member.id]">{{users[member.id]}}</span>
44+
<span class="text-info" ng-if="member.id && !users[member.id]">loading...</span>
45+
</td>
46+
<td>
47+
<button data-ng-click='removeMember(member)' class="btn btn-sm btn-danger" ng-disabled="member.isRemoving">
48+
<strong>Remove</strong>
49+
</button>
50+
</td>
51+
</tr>
52+
</tbody>
53+
54+
<tfoot>
55+
<tr>
56+
<td colspan="4">
57+
<ul class="pagination pull-right"></ul>
58+
</td>
59+
</tr>
60+
</tfoot>
61+
</table>
62+
<div ng-show="!members.length">No members</div><br/>
63+
64+
<button data-ng-click='removeSelected()' class="btn btn-sm btn-danger" ng-disabled="!hasSelected || isProcessing" ng-show="members.length"><strong>Remove Selected</strong></button>
65+
</div>
66+
</div>
67+
</div>
68+
</div>
69+
</div>
70+
</div>

0 commit comments

Comments
 (0)