From 6e653c29e08bd27edf84e48c419ab33a2261346e Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 2 Sep 2016 11:31:18 -0400 Subject: [PATCH] ui implementation of crud-based ui hiding --- .../client/src/access/roleList.partial.html | 6 +- awx/ui/client/src/app.js | 6 ++ awx/ui/client/src/controllers/Credentials.js | 33 ++++++++ awx/ui/client/src/controllers/Projects.js | 32 ++++++- awx/ui/client/src/controllers/Teams.js | 32 ++++++- awx/ui/client/src/controllers/Users.js | 32 ++++++- awx/ui/client/src/forms/Credentials.js | 83 +++++++++++++------ awx/ui/client/src/forms/Inventories.js | 25 ++++-- awx/ui/client/src/forms/JobTemplates.js | 79 ++++++++++++------ awx/ui/client/src/forms/Organizations.js | 19 +++-- awx/ui/client/src/forms/Projects.js | 50 +++++++---- awx/ui/client/src/forms/Teams.js | 22 +++-- awx/ui/client/src/forms/Users.js | 35 +++++--- awx/ui/client/src/helpers/Parse.js | 2 +- awx/ui/client/src/helpers/ProjectPath.js | 14 ++-- .../add/inventory-add.controller.js | 9 ++ .../edit/inventory-edit.controller.js | 11 ++- .../list/inventory-list.controller.js | 12 +++ .../inventory-scripts/add/add.controller.js | 13 ++- .../inventory-scripts/edit/edit.controller.js | 11 +++ .../inventory-scripts.form.js | 17 +++- .../inventory-scripts.list.js | 17 +++- .../inventory-scripts/list/list.controller.js | 12 +++ .../add/job-templates-add.controller.js | 9 ++ .../edit/job-templates-edit.controller.js | 10 +++ .../list/job-templates-list.controller.js | 13 ++- .../survey-maker/survey-maker.block.less | 6 ++ awx/ui/client/src/lists/AllJobs.js | 6 +- awx/ui/client/src/lists/CompletedJobs.js | 5 +- awx/ui/client/src/lists/Credentials.js | 18 +++- awx/ui/client/src/lists/Inventories.js | 16 +++- awx/ui/client/src/lists/InventoryGroups.js | 18 ++-- awx/ui/client/src/lists/JobTemplates.js | 16 +++- awx/ui/client/src/lists/Projects.js | 23 +++-- awx/ui/client/src/lists/Schedules.js | 6 +- awx/ui/client/src/lists/Teams.js | 18 +++- awx/ui/client/src/lists/Users.js | 18 +++- .../src/notifications/notifications.list.js | 3 +- .../add/organizations-add.controller.js | 9 ++ .../edit/organizations-edit.controller.js | 10 +++ .../list/organizations-list.controller.js | 13 +++ .../list/organizations-list.partial.html | 11 +++ .../src/partials/survey-maker-modal.html | 17 ++-- awx/ui/client/src/shared/form-generator.js | 15 ++++ 44 files changed, 669 insertions(+), 163 deletions(-) diff --git a/awx/ui/client/src/access/roleList.partial.html b/awx/ui/client/src/access/roleList.partial.html index 1478c9dc37..365a20f061 100644 --- a/awx/ui/client/src/access/roleList.partial.html +++ b/awx/ui/client/src/access/roleList.partial.html @@ -1,13 +1,13 @@
-
{{ entry.name }} diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index d9101f6d22..512ad43595 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -840,6 +840,12 @@ var tower = angular.module('Tower', [ // If browser refresh, set the user_is_superuser value $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser'); $rootScope.user_is_system_auditor = Authorization.getUserInfo('is_system_auditor'); + + Rest.setUrl($rootScope.current_user.related.admin_of_organizations); + Rest.get() + .success(function(data) { + $rootScope.current_user_admin_orgs = data.results.map(i => i.name); + }); // state the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket) if (!_.contains($location.$$url, '/login')) { ConfigService.getConfig().then(function() { diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js index 66b016dc50..53c4c001b4 100644 --- a/awx/ui/client/src/controllers/Credentials.js +++ b/awx/ui/client/src/controllers/Credentials.js @@ -17,6 +17,18 @@ export function CredentialsList($scope, $rootScope, $location, $log, SelectionInit, GetChoices, Wait, $state, $filter) { ClearScope(); + $scope.canAdd = false; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('credentials')); + Rest.options() + .success(function(data) { + if (data.actions.POST) { + $scope.canAdd = true; + $scope.canEdit = true; + } + }); + Wait('start'); var list = CredentialList, @@ -139,6 +151,15 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log, LookUpInit, OrganizationList, GetBasePath, GetChoices, Empty, KindChange, OwnerChange, FormSave, $state, CreateSelect2) { + Rest.setUrl(GetBasePath('credentials')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add a credential.', 'alert-info'); + } + }); + ClearScope(); // Inject dynamic view @@ -337,6 +358,7 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, } ClearScope(); + var defaultUrl = GetBasePath('credentials'), generator = GenerateForm, form = CredentialForm, @@ -344,6 +366,17 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log, master = {}, id = $stateParams.credential_id, relatedSets = {}; + + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('credentials') + id); + Rest.options() + .success(function(data) { + if (data.actions.PUT) { + $scope.canEdit = true; + } + }); + generator.inject(form, { mode: 'edit', related: true, scope: $scope }); generator.reset(); $scope.id = id; diff --git a/awx/ui/client/src/controllers/Projects.js b/awx/ui/client/src/controllers/Projects.js index 60692d581e..5c0803c3ce 100644 --- a/awx/ui/client/src/controllers/Projects.js +++ b/awx/ui/client/src/controllers/Projects.js @@ -16,9 +16,20 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate, Refresh, Wait, GetChoices, Empty, Find, GetProjectIcon, GetProjectToolTip, $filter, $state) { - ClearScope(); + $scope.canAdd = false; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('projects')); + Rest.options() + .success(function(data) { + if (data.actions.POST) { + $scope.canAdd = true; + $scope.canEdit = true; + } + }); + Wait('start'); var list = ProjectList, @@ -379,6 +390,15 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l OrganizationList, CredentialList, GetChoices, DebugForm, Wait, $state, CreateSelect2) { + Rest.setUrl(GetBasePath('projects')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add a project.', 'alert-info'); + } + }); + ClearScope(); // Inject dynamic view @@ -568,6 +588,16 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log, id = $stateParams.id, relatedSets = {}; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('projects') + id); + Rest.options() + .success(function(data) { + if (data.actions.PUT) { + $scope.canEdit = true; + } + }); + // remove "type" field from search options CredentialList = _.cloneDeep(CredentialList); CredentialList.fields.kind.noSearch = true; diff --git a/awx/ui/client/src/controllers/Teams.js b/awx/ui/client/src/controllers/Teams.js index a8eed62bc5..f78a5f13c9 100644 --- a/awx/ui/client/src/controllers/Teams.js +++ b/awx/ui/client/src/controllers/Teams.js @@ -15,9 +15,20 @@ export function TeamsList($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, TeamList, GenerateList, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit, Wait, $state, Refresh, $filter) { - ClearScope(); + $scope.canAdd = false; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('teams')); + Rest.options() + .success(function(data) { + if (data.actions.POST) { + $scope.canAdd = true; + $scope.canEdit = true; + } + }); + var list = TeamList, defaultUrl = GetBasePath('teams'), generator = GenerateList, @@ -137,6 +148,15 @@ export function TeamsAdd($scope, $rootScope, $compile, $location, $log, ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //$scope. + Rest.setUrl(GetBasePath('teams')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add a team.', 'alert-info'); + } + }); + // Inject dynamic view var defaultUrl = GetBasePath('teams'), form = TeamForm, @@ -206,6 +226,16 @@ export function TeamsEdit($scope, $rootScope, $location, relatedSets = {}, set; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('teams') + id); + Rest.options() + .success(function(data) { + if (data.actions.PUT) { + $scope.canEdit = true; + } + }); + $scope.team_id = id; diff --git a/awx/ui/client/src/controllers/Users.js b/awx/ui/client/src/controllers/Users.js index 103e1d1c84..5e76269983 100644 --- a/awx/ui/client/src/controllers/Users.js +++ b/awx/ui/client/src/controllers/Users.js @@ -35,9 +35,20 @@ export function UsersList($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, UserList, GenerateList, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit, Wait, $state, Refresh, $filter) { - ClearScope(); + $scope.canAdd = false; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('users')); + Rest.options() + .success(function(data) { + if (data.actions.POST) { + $scope.canAdd = true; + $scope.canEdit = true; + } + }); + var list = UserList, defaultUrl = GetBasePath('users'), generator = GenerateList, @@ -148,6 +159,15 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log, ReturnToCaller, ClearScope, GetBasePath, LookUpInit, OrganizationList, ResetForm, Wait, CreateSelect2, $state) { + Rest.setUrl(GetBasePath('users')); + Rest.options() + .success(function(data) { + if (!data.actions.POST) { + $state.go("^"); + Alert('Permission Error', 'You do not have permission to add a user.', 'alert-info'); + } + }); + ClearScope(); // Inject dynamic view @@ -272,6 +292,16 @@ export function UsersEdit($scope, $rootScope, $location, relatedSets = {}, set; + $scope.canEdit = false; + + Rest.setUrl(GetBasePath('users') + id); + Rest.options() + .success(function(data) { + if (data.actions.PUT) { + $scope.canEdit = true; + } + }); + generator.inject(form, { mode: 'edit', related: true, scope: $scope }); generator.reset(); diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js index d503f53905..4f2b262359 100644 --- a/awx/ui/client/src/forms/Credentials.js +++ b/awx/ui/client/src/forms/Credentials.js @@ -32,13 +32,15 @@ export default type: 'text', addRequired: true, editRequired: true, - autocomplete: false + autocomplete: false, + ngDisabled: '!canEdit' }, description: { label: 'Description', type: 'text', addRequired: false, - editRequired: false + editRequired: false, + ngDisabled: '!canEdit' }, organization: { addRequired: false, @@ -52,7 +54,8 @@ export default awPopOver: "

If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.

", dataTitle: 'Organization ', dataPlacement: 'bottom', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, kind: { label: 'Type', @@ -83,7 +86,8 @@ export default dataTitle: 'Type', dataPlacement: 'right', dataContainer: "body", - hasSubForm: true + hasSubForm: true, + ngDisabled: '!canEdit' }, access_key: { label: 'Access Key', @@ -96,12 +100,13 @@ export default autocomplete: false, apiField: 'username', subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, secret_key: { label: 'Secret Key', type: 'sensitive', ngShow: "kind.value == 'aws'", - ngDisabled: "secret_key_ask", + ngDisabled: "secret_key_ask || !canEdit", awRequiredWhen: { reqExpression: "aws_required", init: false @@ -123,7 +128,8 @@ export default dataTitle: 'STS Token', dataPlacement: 'right', dataContainer: "body", - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "host": { labelBind: 'hostLabel', @@ -139,7 +145,8 @@ export default reqExpression: 'host_required', init: false }, - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "subscription": { label: "Subscription ID", @@ -156,7 +163,8 @@ export default dataTitle: 'Subscription ID', dataPlacement: 'right', dataContainer: "body", - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "username": { labelBind: 'usernameLabel', @@ -168,7 +176,8 @@ export default init: false }, autocomplete: false, - subForm: "credentialSubForm" + subForm: "credentialSubForm", + ngDisabled: '!canEdit' }, "email_address": { labelBind: 'usernameLabel', @@ -183,7 +192,8 @@ export default dataTitle: 'Email', dataPlacement: 'right', dataContainer: "body", - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "api_key": { label: 'API Key', @@ -196,7 +206,8 @@ export default autocomplete: false, hasShowInputButton: true, clear: false, - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "password": { labelBind: 'passwordLabel', @@ -209,13 +220,14 @@ export default reqExpression: "password_required", init: false }, - subForm: "credentialSubForm" + subForm: "credentialSubForm", + ngDisabled: '!canEdit' }, "ssh_password": { label: 'Password', type: 'sensitive', ngShow: "kind.value == 'ssh'", - ngDisabled: "ssh_password_ask", + ngDisabled: "ssh_password_ask || !canEdit", addRequired: false, editRequired: false, subCheckbox: { @@ -247,7 +259,8 @@ export default dataTitle: 'Private Key', dataPlacement: 'right', dataContainer: "body", - subForm: "credentialSubForm" + subForm: "credentialSubForm", + ngDisabled: '!canEdit' }, "ssh_key_unlock": { label: 'Private Key Passphrase', @@ -255,7 +268,7 @@ export default ngShow: "kind.value == 'ssh' || kind.value == 'scm'", addRequired: false, editRequired: false, - ngDisabled: "keyEntered === false || ssh_key_unlock_ask", + ngDisabled: "keyEntered === false || ssh_key_unlock_ask || !canEdit", subCheckbox: { variable: 'ssh_key_unlock_ask', ngShow: "kind.value == 'ssh'", @@ -278,7 +291,8 @@ export default "sudo | su | pbrun | pfexec | runas
(defaults to sudo)

", dataPlacement: 'right', dataContainer: "body", - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "become_username": { labelBind: 'becomeUsernameLabel', @@ -287,13 +301,14 @@ export default addRequired: false, editRequired: false, autocomplete: false, - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "become_password": { labelBind: 'becomePasswordLabel', type: 'sensitive', ngShow: "(kind.value == 'ssh' && (become_method && become_method.value)) ", - ngDisabled: "become_password_ask", + ngDisabled: "become_password_ask || !canEdit", addRequired: false, editRequired: false, subCheckbox: { @@ -309,7 +324,8 @@ export default type: 'text', label: 'Client ID', subForm: 'credentialSubForm', - ngShow: "kind.value === 'azure_rm'" + ngShow: "kind.value === 'azure_rm'", + ngDisabled: '!canEdit' }, secret:{ type: 'sensitive', @@ -317,20 +333,23 @@ export default autocomplete: false, label: 'Client Secret', subForm: 'credentialSubForm', - ngShow: "kind.value === 'azure_rm'" + ngShow: "kind.value === 'azure_rm'", + ngDisabled: '!canEdit' }, tenant: { type: 'text', label: 'Tenant ID', subForm: 'credentialSubForm', - ngShow: "kind.value === 'azure_rm'" + ngShow: "kind.value === 'azure_rm'", + ngDisabled: '!canEdit' }, authorize: { label: 'Authorize', type: 'checkbox', ngChange: "toggleCallback('host_config_key')", subForm: 'credentialSubForm', - ngShow: "kind.value === 'net'" + ngShow: "kind.value === 'net'", + ngDisabled: '!canEdit' }, authorize_password: { label: 'Authorize Password', @@ -339,6 +358,7 @@ export default autocomplete: false, subForm: 'credentialSubForm', ngShow: "authorize && authorize !== 'false'", + ngDisabled: '!canEdit' }, "project": { labelBind: 'projectLabel', @@ -355,7 +375,8 @@ export default reqExpression: 'project_required', init: false }, - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "domain": { labelBind: 'domainLabel', @@ -371,13 +392,14 @@ export default dataContainer: "body", addRequired: false, editRequired: false, - subForm: 'credentialSubForm' + subForm: 'credentialSubForm', + ngDisabled: '!canEdit' }, "vault_password": { label: "Vault Password", type: 'sensitive', ngShow: "kind.value == 'ssh'", - ngDisabled: "vault_password_ask", + ngDisabled: "vault_password_ask || !canEdit", addRequired: false, editRequired: false, subCheckbox: { @@ -394,11 +416,17 @@ export default buttons: { cancel: { ngClick: 'formCancel()', + ngShow: 'canEdit' + }, + close: { + ngClick: 'formCancel()', + ngShow: '!canEdit' }, save: { label: 'Save', ngClick: 'formSave()', //$scope.function to call on click, optional - ngDisabled: true //Disable when $pristine or $invalid, optional + ngDisabled: true, + ngShow: 'canEdit' //Disable when $pristine or $invalid, optional } }, @@ -421,7 +449,8 @@ export default label: 'Add', awToolTip: 'Add a permission', actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD' + buttonContent: '+ ADD', + ngShow: 'canEdit' } }, diff --git a/awx/ui/client/src/forms/Inventories.js b/awx/ui/client/src/forms/Inventories.js index 6467bf28d3..c1d3c172d0 100644 --- a/awx/ui/client/src/forms/Inventories.js +++ b/awx/ui/client/src/forms/Inventories.js @@ -26,14 +26,16 @@ export default type: 'text', addRequired: true, editRequired: true, - capitalize: false + capitalize: false, + ngDisabled: '!canEdit' }, inventory_description: { realName: 'description', label: 'Description', type: 'text', addRequired: false, - editRequired: false + editRequired: false, + ngDisabled: '!canEdit' }, organization: { label: 'Organization', @@ -44,7 +46,8 @@ export default awRequiredWhen: { reqExpression: "organizationrequired", init: "true" - } + }, + ngDisabled: '!canEdit' }, variables: { label: 'Variables', @@ -63,17 +66,24 @@ export default '

View YAML examples at docs.ansible.com

', dataTitle: 'Inventory Variables', dataPlacement: 'right', - dataContainer: 'body' + dataContainer: 'body', + ngDisabled: '!canEdit' // TODO: get working } }, buttons: { cancel: { - ngClick: 'formCancel()' + ngClick: 'formCancel()', + ngShow: 'canEdit' + }, + close: { + ngClick: 'formCancel()', + ngHide: 'canEdit' }, save: { ngClick: 'formSave()', - ngDisabled: true + ngDisabled: true, + ngShow: 'canEdit' } }, @@ -94,7 +104,8 @@ export default label: 'Add', awToolTip: 'Add a permission', actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD' + buttonContent: '+ ADD', + ngShow: 'canEdit' } }, diff --git a/awx/ui/client/src/forms/JobTemplates.js b/awx/ui/client/src/forms/JobTemplates.js index 5d56907c88..d7a6c81bfa 100644 --- a/awx/ui/client/src/forms/JobTemplates.js +++ b/awx/ui/client/src/forms/JobTemplates.js @@ -27,14 +27,16 @@ export default type: 'text', addRequired: true, editRequired: true, - column: 1 + column: 1, + ngDisabled: '!canEdit' }, description: { label: 'Description', type: 'text', addRequired: false, editRequired: false, - column: 1 + column: 1, + ngDisabled: '!canEdit' }, job_type: { label: 'Job Type', @@ -56,7 +58,8 @@ export default variable: 'ask_job_type_on_launch', ngShow: "!job_type.value || job_type.value !== 'scan'", text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, inventory: { label: 'Inventory', @@ -78,7 +81,8 @@ export default variable: 'ask_inventory_on_launch', ngShow: "!job_type.value || job_type.value !== 'scan'", text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, project: { label: 'Project', @@ -100,12 +104,13 @@ export default dataTitle: 'Project', dataPlacement: 'right', dataContainer: "body", + ngDisabled: '!canEdit' }, playbook: { label: 'Playbook', type:'select', ngOptions: 'book for book in playbook_options track by book', - ngDisabled: "job_type.value === 'scan' && project_name === 'Default'", + ngDisabled: "(job_type.value === 'scan' && project_name === 'Default') || !canEdit", id: 'playbook-select', awRequiredWhen: { reqExpression: "playbookrequired", @@ -138,7 +143,8 @@ export default subCheckbox: { variable: 'ask_credential_on_launch', text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, cloud_credential: { label: 'Cloud Credential', @@ -153,7 +159,8 @@ export default "running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.

", dataTitle: 'Cloud Credential', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, network_credential: { label: 'Network Credential', @@ -167,7 +174,8 @@ export default awPopOver: "

Network credentials are used by Ansible networking modules to connect to and manage networking devices.

", dataTitle: 'Network Credential', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, forks: { label: 'Forks', @@ -186,7 +194,8 @@ export default ' target=\"_blank\">ansible configuration file.

', dataTitle: 'Forks', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' // TODO: get working }, limit: { label: 'Limit', @@ -203,7 +212,8 @@ export default subCheckbox: { variable: 'ask_limit_on_launch', text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, verbosity: { label: 'Verbosity', @@ -216,7 +226,8 @@ export default awPopOver: "

Control the level of output ansible will produce as the playbook executes.

", dataTitle: 'Verbosity', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, job_tags: { label: 'Job Tags', @@ -235,7 +246,8 @@ export default subCheckbox: { variable: 'ask_tags_on_launch', text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, skip_tags: { label: 'Skip Tags', @@ -254,7 +266,8 @@ export default subCheckbox: { variable: 'ask_skip_tags_on_launch', text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' }, checkbox_group: { label: 'Options', @@ -270,7 +283,8 @@ export default dataPlacement: 'right', dataTitle: 'Become Privilege Escalation', dataContainer: "body", - labelClass: 'stack-inline' + labelClass: 'stack-inline', + ngDisabled: '!canEdit' }, { name: 'allow_callbacks', label: 'Allow Provisioning Callbacks', @@ -284,7 +298,8 @@ export default dataPlacement: 'right', dataTitle: 'Allow Provisioning Callbacks', dataContainer: "body", - labelClass: 'stack-inline' + labelClass: 'stack-inline', + ngDisabled: '!canEdit' }] }, callback_url: { @@ -299,7 +314,8 @@ export default awPopOverWatch: "callback_help", dataPlacement: 'top', dataTitle: 'Provisioning Callback URL', - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, host_config_key: { label: 'Host Config Key', @@ -312,7 +328,8 @@ export default awPopOverWatch: "callback_help", dataPlacement: 'right', dataTitle: "Host Config Key", - dataContainer: "body" + dataContainer: "body", + ngDisabled: '!canEdit' }, labels: { label: 'Labels', @@ -325,7 +342,8 @@ export default dataTitle: 'Labels', dataPlacement: 'right', awPopOver: "

Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.

", - dataContainer: 'body' + dataContainer: 'body', + ngDisabled: '!canEdit' }, variables: { label: 'Extra Variables', @@ -348,14 +366,15 @@ export default subCheckbox: { variable: 'ask_variables_on_launch', text: 'Prompt on launch' - } + }, + ngDisabled: '!canEdit' // TODO: get working } }, buttons: { //for now always generates @@ -31,13 +32,23 @@
+
-
+
-
+
-
+
PREVIEW
@@ -56,13 +56,13 @@ {{question.question_description}}
- +   -
+
@@ -80,9 +80,10 @@
- - - + + + +
diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 66c4f750b8..821857e3f6 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -865,6 +865,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += ">"; html += "