diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js index 9f049e490b..5f7f6e9a25 100644 --- a/awx/ui/client/src/controllers/Credentials.js +++ b/awx/ui/client/src/controllers/Credentials.js @@ -288,7 +288,7 @@ CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, $stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt, GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, FormSave, Wait, - $state, CreateSelect2, Authorization, i18n) { + $state, CreateSelect2, Authorization, i18n, OrgAdminLookup) { ClearScope(); @@ -499,6 +499,16 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, setAskCheckboxes(); + if(data.organization) { + OrgAdminLookup.checkForAdminAccess({organization: data.organization}) + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + } + else { + $scope.canEditOrg = true; + } + $scope.$emit('credentialLoaded'); Wait('stop'); }) @@ -626,5 +636,5 @@ CredentialsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices', 'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange', - 'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n', + 'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n', 'OrgAdminLookup' ]; diff --git a/awx/ui/client/src/controllers/Projects.js b/awx/ui/client/src/controllers/Projects.js index 555d4e28ca..759f23be60 100644 --- a/awx/ui/client/src/controllers/Projects.js +++ b/awx/ui/client/src/controllers/Projects.js @@ -439,11 +439,11 @@ export function ProjectsAdd($scope, $rootScope, $compile, $location, $log, 'Do not put the username and key in the URL. ' + 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); break; - case 'insights': - $scope.pathRequired = false; - $scope.scmRequired = false; - $scope.credentialLabel = "Red Hat Insights"; - break; + case 'insights': + $scope.pathRequired = false; + $scope.scmRequired = false; + $scope.credentialLabel = "Red Hat Insights"; + break; default: $scope.credentialLabel = "SCM Credential"; $scope.urlPopover = '

' + i18n._('URL popover text'); @@ -464,7 +464,7 @@ ProjectsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, $stateParams, ProjectsForm, Rest, Alert, ProcessErrors, GenerateForm, Prompt, ClearScope, GetBasePath, GetProjectPath, Authorization, - GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n) { + GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n, OrgAdminLookup) { ClearScope('htmlTemplate'); @@ -595,6 +595,11 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, $scope.scm_type_class = "btn-disabled"; } + OrgAdminLookup.checkForAdminAccess({organization: data.organization}) + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + $scope.project_obj = data; $scope.name = data.name; $scope.$emit('projectLoaded'); @@ -708,6 +713,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, if ($scope.scm_type.value) { switch ($scope.scm_type.value) { case 'git': + $scope.credentialLabel = "SCM Credential"; $scope.urlPopover = '

' + i18n._('Example URLs for GIT SCM include:') + '

' + '

' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' + @@ -715,11 +721,13 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, 'SSH. GIT read only protocol (git://) does not use username or password information.'), '', ''); break; case 'svn': + $scope.credentialLabel = "SCM Credential"; $scope.urlPopover = '

' + i18n._('Example URLs for Subversion SCM include:') + '

' + ''; break; case 'hg': + $scope.credentialLabel = "SCM Credential"; $scope.urlPopover = '

' + i18n._('Example URLs for Mercurial SCM include:') + '

' + '' + @@ -727,12 +735,13 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, 'Do not put the username and key in the URL. ' + 'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '', ''); break; - case 'insights': - $scope.pathRequired = false; - $scope.scmRequired = false; - $scope.credentialLabel = "Red Hat Insights"; + case 'insights': + $scope.pathRequired = false; + $scope.scmRequired = false; + $scope.credentialLabel = "Red Hat Insights"; break; default: + $scope.credentialLabel = "SCM Credential"; $scope.urlPopover = '

' + i18n._('URL popover text'); } } @@ -756,4 +765,4 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GenerateForm', 'Prompt', 'ClearScope', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty', - 'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n']; + 'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n', 'OrgAdminLookup']; diff --git a/awx/ui/client/src/controllers/Teams.js b/awx/ui/client/src/controllers/Teams.js index 464a10ee10..1db3843bef 100644 --- a/awx/ui/client/src/controllers/Teams.js +++ b/awx/ui/client/src/controllers/Teams.js @@ -154,7 +154,7 @@ TeamsAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Generat export function TeamsEdit($scope, $rootScope, $stateParams, - TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state) { + TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state, OrgAdminLookup) { ClearScope(); @@ -172,6 +172,11 @@ export function TeamsEdit($scope, $rootScope, $stateParams, setScopeFields(data); $scope.organization_name = data.summary_fields.organization.name; + OrgAdminLookup.checkForAdminAccess({organization: data.organization}) + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + $scope.team_obj = data; Wait('stop'); }); @@ -243,5 +248,5 @@ export function TeamsEdit($scope, $rootScope, $stateParams, } TeamsEdit.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', - 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state' + 'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state', 'OrgAdminLookup' ]; diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js index 8242043acb..89af5ea7b9 100644 --- a/awx/ui/client/src/forms/Credentials.js +++ b/awx/ui/client/src/forms/Credentials.js @@ -55,7 +55,8 @@ export default dataTitle: i18n._('Organization') + ' ', dataPlacement: 'bottom', dataContainer: "body", - ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', + awLookupWhen: '(credential_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, kind: { label: i18n._('Type'), diff --git a/awx/ui/client/src/forms/Inventories.js b/awx/ui/client/src/forms/Inventories.js index e010679c7b..08f29d5872 100644 --- a/awx/ui/client/src/forms/Inventories.js +++ b/awx/ui/client/src/forms/Inventories.js @@ -49,7 +49,8 @@ angular.module('InventoryFormDefinition', []) reqExpression: "organizationrequired", init: "true" }, - ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', + awLookupWhen: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, variables: { label: i18n._('Variables'), diff --git a/awx/ui/client/src/forms/Projects.js b/awx/ui/client/src/forms/Projects.js index 3b2a69c044..8a5abc4af9 100644 --- a/awx/ui/client/src/forms/Projects.js +++ b/awx/ui/client/src/forms/Projects.js @@ -50,7 +50,8 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition']) required: true, dataContainer: 'body', dataPlacement: 'right', - ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' + ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', + awLookupWhen: '(project_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, scm_type: { label: i18n._('SCM Type'), diff --git a/awx/ui/client/src/forms/Teams.js b/awx/ui/client/src/forms/Teams.js index 121ae4be1e..7b883ab834 100644 --- a/awx/ui/client/src/forms/Teams.js +++ b/awx/ui/client/src/forms/Teams.js @@ -42,7 +42,8 @@ export default sourceModel: 'organization', basePath: 'organizations', sourceField: 'name', - ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)', + ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg', + awLookupWhen: '(team_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg', required: true, } }, diff --git a/awx/ui/client/src/forms/Workflows.js b/awx/ui/client/src/forms/Workflows.js index 874f176ae3..f0e7c24dc6 100644 --- a/awx/ui/client/src/forms/Workflows.js +++ b/awx/ui/client/src/forms/Workflows.js @@ -54,7 +54,8 @@ export default dataContainer: 'body', dataPlacement: 'right', column: 1, - ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)' + ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate) || !canEditOrg', + awLookupWhen: '(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg' }, labels: { label: i18n._('Labels'), diff --git a/awx/ui/client/src/inventories/edit/inventory-edit.controller.js b/awx/ui/client/src/inventories/edit/inventory-edit.controller.js index c567839d8b..5613e1f627 100644 --- a/awx/ui/client/src/inventories/edit/inventory-edit.controller.js +++ b/awx/ui/client/src/inventories/edit/inventory-edit.controller.js @@ -14,7 +14,7 @@ function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $stateParams, InventoryForm, Rest, Alert, ProcessErrors, ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON, ParseVariableString, Prompt, InitiatePlaybookRun, - TemplatesService, $state) { + TemplatesService, $state, OrgAdminLookup) { // Inject dynamic view var defaultUrl = GetBasePath('inventory'), @@ -77,6 +77,11 @@ function InventoriesEdit($scope, $rootScope, $compile, $location, field_id: 'inventory_variables' }); + OrgAdminLookup.checkForAdminAccess({organization: data.organization}) + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + $scope.inventory_obj = data; $scope.name = data.name; @@ -132,5 +137,5 @@ export default ['$scope', '$rootScope', '$compile', '$location', '$log', '$stateParams', 'InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString', 'Prompt', 'InitiatePlaybookRun', - 'TemplatesService', '$state', InventoriesEdit, + 'TemplatesService', '$state', 'OrgAdminLookup', InventoriesEdit, ]; diff --git a/awx/ui/client/src/inventories/main.js b/awx/ui/client/src/inventories/main.js index 42ea1d6a4e..6fb5916b25 100644 --- a/awx/ui/client/src/inventories/main.js +++ b/awx/ui/client/src/inventories/main.js @@ -46,6 +46,9 @@ angular.module('inventory', [ data: { activityStream: true, activityStreamTarget: 'inventory' + }, + ncyBreadcrumb: { + label: N_('INVENTORIES') } }); diff --git a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html index 0444f0f662..04777cc0f8 100644 --- a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html +++ b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html @@ -1,6 +1,6 @@

\n"; diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js index 171a72ffa0..6879e26552 100644 --- a/awx/ui/client/src/shared/generator-helpers.js +++ b/awx/ui/client/src/shared/generator-helpers.js @@ -86,6 +86,9 @@ angular.module('GeneratorHelpers', [systemStatus.name]) result += value; result += '"'; break; + case 'awLookupWhen': + result = "ng-attr-awlookup=\"" + value + "\" "; + break; default: result = key + "=\"" + value + "\" "; } diff --git a/awx/ui/client/src/shared/main.js b/awx/ui/client/src/shared/main.js index 5b68027dde..934d3c0157 100644 --- a/awx/ui/client/src/shared/main.js +++ b/awx/ui/client/src/shared/main.js @@ -20,6 +20,7 @@ import templateUrl from './template-url/main'; import RestServices from '../rest/main'; import stateDefinitions from './stateDefinitions.factory'; import apiLoader from './api-loader'; +import orgAdminLookup from './org-admin-lookup/main'; import 'angular-duration-format'; export default @@ -36,6 +37,7 @@ angular.module('shared', [listGenerator.name, templateUrl.name, RestServices.name, apiLoader.name, + orgAdminLookup.name, require('angular-cookies'), 'angular-duration-format' ]) diff --git a/awx/ui/client/src/shared/org-admin-lookup/main.js b/awx/ui/client/src/shared/org-admin-lookup/main.js new file mode 100644 index 0000000000..3d4f162f62 --- /dev/null +++ b/awx/ui/client/src/shared/org-admin-lookup/main.js @@ -0,0 +1,11 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import OrgAdminLookupFactory from './org-admin-lookup.factory'; + +export default + angular.module('orgAdminLookup', []) + .service('OrgAdminLookup', OrgAdminLookupFactory); diff --git a/awx/ui/client/src/shared/org-admin-lookup/org-admin-lookup.factory.js b/awx/ui/client/src/shared/org-admin-lookup/org-admin-lookup.factory.js new file mode 100644 index 0000000000..aa200ef984 --- /dev/null +++ b/awx/ui/client/src/shared/org-admin-lookup/org-admin-lookup.factory.js @@ -0,0 +1,35 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default + ['Rest', 'Authorization', 'GetBasePath', '$rootScope', '$q', + function(Rest, Authorization, GetBasePath, $rootScope, $q){ + return { + checkForAdminAccess: function(params) { + // params.organization - id of the organization in question + var deferred = $q.defer(); + if(Authorization.getUserInfo('is_superuser') !== true) { + Rest.setUrl(GetBasePath('users') + $rootScope.current_user.id + '/admin_of_organizations'); + Rest.get({ params: { id: params.organization } }) + .success(function(data) { + if(data.count && data.count > 0) { + deferred.resolve(true); + } + else { + deferred.resolve(false); + } + }); + } + else { + deferred.resolve(true); + } + + return deferred.promise; + } + + }; + } + ]; diff --git a/awx/ui/client/src/shared/stateDefinitions.factory.js b/awx/ui/client/src/shared/stateDefinitions.factory.js index 6a3e530401..8d4a118fad 100644 --- a/awx/ui/client/src/shared/stateDefinitions.factory.js +++ b/awx/ui/client/src/shared/stateDefinitions.factory.js @@ -150,7 +150,7 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto url: url, ncyBreadcrumb: { [params.parent ? 'parent' : null]: `${params.parent}`, - label: i18n.sprintf(i18n._("CREATE %s"), i18n._(`${form.breadcrumbName || form.name}`)) + label: i18n.sprintf(i18n._("CREATE %s"), i18n._(`${form.breadcrumbName || form.name.toUpperCase()}`)) }, views: { 'form': { @@ -376,14 +376,15 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto function buildNotificationState(field) { let state, - list = field.include ? $injector.get(field.include) : field; + list = field.include ? $injector.get(field.include) : field, + breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase(); state = $stateExtender.buildDefinition({ searchPrefix: `${list.iterator}`, name: `${formStateDefinition.name}.${list.iterator}s`, url: `/${list.iterator}s`, ncyBreadcrumb: { parent: `${formStateDefinition.name}`, - label: `${field.iterator}s` + label: `${breadcrumbLabel}` }, params: { [list.iterator + '_search']: { @@ -571,14 +572,14 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto list = field.include ? $injector.get(field.include) : field, // Added this line specifically for Completed Jobs but should be OK // for all the rest of the related tabs - breadcrumbLabel = field.iterator.replace('_', ' '), + breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase(), stateConfig = { searchPrefix: `${list.iterator}`, name: `${formStateDefinition.name}.${list.iterator}s`, url: `/${list.iterator}s`, ncyBreadcrumb: { parent: `${formStateDefinition.name}`, - label: `${breadcrumbLabel}s` + label: `${breadcrumbLabel}` }, params: { [list.iterator + '_search']: { diff --git a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js index c0cbeb417d..e8fba017c8 100644 --- a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js +++ b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js @@ -8,12 +8,12 @@ [ '$scope', '$stateParams', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', '$q', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString', - 'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService', 'ToggleNotification', + 'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService', 'ToggleNotification', 'OrgAdminLookup', function( $scope, $stateParams, WorkflowForm, GenerateForm, Alert, ProcessErrors, ClearScope, GetBasePath, $q, ParseTypeChange, Wait, Empty, ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString, - TemplatesService, OrganizationList, Rest, WorkflowService, ToggleNotification + TemplatesService, OrganizationList, Rest, WorkflowService, ToggleNotification, OrgAdminLookup ) { ClearScope(); @@ -145,6 +145,17 @@ workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; } } + + if(workflowJobTemplateData.organization) { + OrgAdminLookup.checkForAdminAccess({organization: workflowJobTemplateData.organization}) + .then(function(canEditOrg){ + $scope.canEditOrg = canEditOrg; + }); + } + else { + $scope.canEditOrg = true; + } + Wait('stop'); $scope.url = workflowJobTemplateData.url; $scope.survey_enabled = workflowJobTemplateData.survey_enabled;