diff --git a/awx/ui/client/src/access/add-rbac-user-team/rbac-selected-list.directive.js b/awx/ui/client/src/access/add-rbac-user-team/rbac-selected-list.directive.js
index 131a060523..99e2cb451f 100644
--- a/awx/ui/client/src/access/add-rbac-user-team/rbac-selected-list.directive.js
+++ b/awx/ui/client/src/access/add-rbac-user-team/rbac-selected-list.directive.js
@@ -17,7 +17,6 @@ export default ['$compile','templateUrl', 'i18n', 'generateList',
selected: "="
},
link: function(scope, element, attrs) {
- console.log(scope.resourceType)
let listMap, list, list_html;
listMap = {
@@ -28,12 +27,12 @@ export default ['$compile','templateUrl', 'i18n', 'generateList',
credentials: CredentialList
};
- list = _.cloneDeep(listMap[scope.resourceType])
+ list = _.cloneDeep(listMap[scope.resourceType]);
list.fieldActions = {
remove: {
ngClick: `removeSelection(${list.iterator}, resourceType)`,
- icon: 'fa-remove',
+ iconClass: 'fa fa-times-circle',
awToolTip: i18n._(`Remove ${list.iterator}`),
label: i18n._('Remove'),
class: 'btn-sm'
@@ -43,6 +42,8 @@ export default ['$compile','templateUrl', 'i18n', 'generateList',
list.listTitleBadge = false;
+ // @issue - fix field.columnClass values for this view
+
switch(scope.resourceType){
case 'projects':
@@ -77,8 +78,7 @@ export default ['$compile','templateUrl', 'i18n', 'generateList',
description: list.fields.description
};
break;
-
- default:
+ case 'credentials':
list.fields = {
name: list.fields.name,
description: list.fields.description
@@ -93,12 +93,31 @@ export default ['$compile','templateUrl', 'i18n', 'generateList',
related: false,
title: false,
showSearch: false,
+ showEmptyPanel: false,
paginate: false
});
scope.list = list;
- scope[`${list.iterator}_dataset`] = scope.collection;
- scope[list.name] = scope.collection;
+
+ scope.$watchCollection('collection', function(selected){
+ scope[`${list.iterator}_dataset`] = scope.collection;
+ scope[list.name] = _.values(scope.collection);
+ });
+
+ scope.removeSelection = function(resource, type){
+ let multiselect_scope, deselectedIdx;
+
+ delete scope.collection[resource.id];
+ delete scope.selected[type][resource.id];
+
+ // a quick & dirty hack
+ // section 1 and section 2 elements produce sibling scopes
+ // This means events propogated from section 2 are not received in section 1
+ // The following code directly accesses the right scope by list table id
+ multiselect_scope = angular.element('#AddPermissions-body').find(`#${type}_table`).scope()
+ deselectedIdx = _.findIndex(multiselect_scope[type], {id: resource.id});
+ multiselect_scope[type][deselectedIdx].isSelected = false;
+ };
element.append(list_html);
$compile(element.contents())(scope);
diff --git a/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.controller.js b/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.controller.js
index 3e41460487..665f5f6c26 100644
--- a/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.controller.js
+++ b/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.controller.js
@@ -11,8 +11,8 @@
* Controller for handling permissions adding
*/
-export default ['$rootScope', '$scope', '$state', 'GetBasePath', 'Rest', '$q', 'Wait', 'ProcessErrors',
-function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
+export default ['$rootScope', '$scope', '$state', 'i18n', 'CreateSelect2', 'GetBasePath', 'Rest', '$q', 'Wait', 'ProcessErrors',
+function(rootScope, scope, $state, i18n, CreateSelect2, GetBasePath, Rest, $q, Wait, ProcessErrors) {
init();
@@ -24,12 +24,20 @@ function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
// selected - keyed by type of resource
// selected[type] - keyed by each resource object's id
// selected[type][id] === { roles: [ ... ], ... }
+
+ // collection of resources selected in section 1
scope.selected = {};
- _.each(resources, (resource) => scope.selected[resource] = {});
+ _.each(resources, (type) => scope.selected[type] = {});
+ // collection of assignable roles per type of resource
scope.keys = {};
- _.each(resources, (resource) => scope.keys[resource] = {});
+ _.each(resources, (type) => scope.keys[type] = {});
+ // collection of currently-selected role to assign in section 2
+ scope.roleSelection = {};
+ _.each(resources, (type) => scope.roleSelection[type] = null);
+
+ // tracks currently-selected tabs, initialized with the job templates tab open
scope.tab = {
job_templates: true,
workflow_templates: false,
@@ -37,13 +45,39 @@ function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
inventories: false,
credentials: false
};
+
+ // initializes select2 per select field
+ // html snippet:
+ /*
+
+
+
+ */
+ _.each(resources, (type) => buildSelect2(type));
+
+ function buildSelect2(type){
+ CreateSelect2({
+ element: `#${type}-role-select`,
+ multiple: false,
+ placeholder: i18n._('Select a role')
+ });
+ }
+
scope.showKeyPane = false;
+
+ // the user or team being assigned permissions
scope.owner = scope.resolve.resourceData.data;
}
// aggregate name/descriptions for each available role, based on resource type
+ // reasoning:
function aggregateKey(item, type){
- _.merge(scope.keys[type], item.summary_fields.object_roles);
+ _.merge(scope.keys[type], _.omit(item.summary_fields.object_roles, 'read_role'));
}
scope.closeModal = function() {
@@ -66,11 +100,6 @@ function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
return Object.keys(scope.selected[tab]).length > 0;
};
- scope.removeSelection = function(resource, type){
- delete scope.selected[type][resource.id];
- resource.isSelected = false;
- };
-
// handle form tab changes
scope.selectTab = function(selected){
_.each(scope.tab, (value, key, collection) => {
@@ -78,7 +107,7 @@ function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
});
};
- // pop/push into unified collection of selected users & teams
+ // pop/push into unified collection of selected resourcesf
scope.$on("selectedOrDeselected", function(e, value) {
let resourceType = scope.currentTab(),
item = value.value;
@@ -94,15 +123,20 @@ function(rootScope, scope, $state, GetBasePath, Rest, $q, Wait, ProcessErrors) {
// post roles to api
scope.saveForm = function() {
- Wait('start');
- // scope.selected => { n: {id: n}, ... } => [ {id: n}, ... ]
- let requests = _(scope.selected).map((type) => {
- return _.map(type, (resource) => resource.roles);
+ //Wait('start');
+
+ // builds an array of role entities to apply to current user or team
+ let roles = _(scope.selected).map( (resources, type) =>{
+ return _.map(resources, (resource) => {
+ return resource.summary_fields.object_roles[scope.roleSelection[type]]
+ });
}).flattenDeep().value();
+
+ debugger;
Rest.setUrl(scope.owner.related.roles);
- $q.all( _.map(requests, (entity) => Rest.post({id: entity.id})) )
+ $q.all( _.map(roles, (entity) => Rest.post({id: entity.id})) )
.then( () =>{
Wait('stop');
scope.closeModal();
diff --git a/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.partial.html b/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.partial.html
index dc330bc035..de79fe03f0 100644
--- a/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.partial.html
+++ b/awx/ui/client/src/access/add-rbac-user-team/rbac-user-team.partial.html
@@ -20,7 +20,7 @@
-
+
@@ -65,7 +65,7 @@
-
-
+
+
+
+
@@ -149,7 +157,8 @@
+ selected="selected"
+ ng-show="tab[type]">
@@ -167,7 +176,7 @@
diff --git a/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-list.directive.js b/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-list.directive.js
index d94411ff0f..855c15e093 100644
--- a/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-list.directive.js
+++ b/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-list.directive.js
@@ -70,7 +70,13 @@ export default ['addPermissionsTeamsList', 'addPermissionsUsersList', 'TemplateL
description: list.fields.description
};
break;
-
+ case 'Users':
+ list.fields = {
+ username: list.fields.username,
+ first_name: list.fields.first_name,
+ last_name: list.fields.last_name
+ }
+ break;
default:
list.fields = {
name: list.fields.name,
diff --git a/awx/ui/client/src/access/rbac-role-column/roleList.directive.js b/awx/ui/client/src/access/rbac-role-column/roleList.directive.js
index 2ee33b8652..97682d0bc9 100644
--- a/awx/ui/client/src/access/rbac-role-column/roleList.directive.js
+++ b/awx/ui/client/src/access/rbac-role-column/roleList.directive.js
@@ -5,7 +5,7 @@ export default
return {
restrict: 'E',
scope: true,
- templateUrl: templateUrl('access/rbac-role-list/roleList'),
+ templateUrl: templateUrl('access/rbac-role-column/roleList'),
link: function(scope, element, attrs) {
// given a list of roles (things like "project
// auditor") which are pulled from two different
diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js
index b1a74db51a..cac1e129aa 100644
--- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js
+++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js
@@ -241,9 +241,11 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon',
}
// Show the "no items" box when loading is done and the user isn't actively searching and there are no results
- html += `
`;
- html += (list.emptyListText) ? list.emptyListText : i18n._("PLEASE ADD ITEMS TO THIS LIST");
- html += "
";
+ if (options.showEmptyPanel === undefined || options.showEmptyPanel === true){
+ html += `
`;
+ html += (list.emptyListText) ? list.emptyListText : i18n._("PLEASE ADD ITEMS TO THIS LIST");
+ html += "
";
+ }
// Add a title and optionally a close button (used on Inventory->Groups)
if (options.mode !== 'lookup' && list.showTitle) {