From 38c0c0a89bee892a1df2796114fa4f0edaa642d7 Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Mon, 24 Apr 2017 16:13:48 -0400
Subject: [PATCH 1/6] add credential types to ui
---
awx/ui/client/legacy-styles/ansible-ui.less | 2 +
awx/ui/client/legacy-styles/codemirror.less | 10 +-
awx/ui/client/legacy-styles/forms.less | 14 +-
awx/ui/client/src/app.js | 2 +
.../credential-types/add/add.controller.js | 106 +++++++++++
.../client/src/credential-types/add/main.js | 11 ++
.../credential-types/credential-types.form.js | 113 ++++++++++++
.../credential-types/credential-types.list.js | 82 +++++++++
.../credential-types/edit/edit.controller.js | 172 ++++++++++++++++++
.../client/src/credential-types/edit/main.js | 11 ++
.../credential-types/list/list.controller.js | 122 +++++++++++++
.../client/src/credential-types/list/main.js | 11 ++
awx/ui/client/src/credential-types/main.js | 71 ++++++++
.../src/setup-menu/setup-menu.partial.html | 7 +
awx/ui/client/src/shared/form-generator.js | 5 +-
15 files changed, 730 insertions(+), 9 deletions(-)
create mode 100644 awx/ui/client/src/credential-types/add/add.controller.js
create mode 100644 awx/ui/client/src/credential-types/add/main.js
create mode 100644 awx/ui/client/src/credential-types/credential-types.form.js
create mode 100644 awx/ui/client/src/credential-types/credential-types.list.js
create mode 100644 awx/ui/client/src/credential-types/edit/edit.controller.js
create mode 100644 awx/ui/client/src/credential-types/edit/main.js
create mode 100644 awx/ui/client/src/credential-types/list/list.controller.js
create mode 100644 awx/ui/client/src/credential-types/list/main.js
create mode 100644 awx/ui/client/src/credential-types/main.js
diff --git a/awx/ui/client/legacy-styles/ansible-ui.less b/awx/ui/client/legacy-styles/ansible-ui.less
index c953778157..bf10688c22 100644
--- a/awx/ui/client/legacy-styles/ansible-ui.less
+++ b/awx/ui/client/legacy-styles/ansible-ui.less
@@ -2152,6 +2152,7 @@ tr td button i {
cursor: not-allowed;
opacity: 100;
background: @egrey;
+ border-radius: 5px;
}
.select2-container--default .select2-selection--single {
@@ -2337,6 +2338,7 @@ input[disabled].ui-spinner-input {
margin-right: 0;
overflow: auto !important;
overflow-y: auto !important;
+ border-radius: 5px;
}
.CodeMirror-lines {
diff --git a/awx/ui/client/legacy-styles/codemirror.less b/awx/ui/client/legacy-styles/codemirror.less
index a317585460..8aed0b7e54 100644
--- a/awx/ui/client/legacy-styles/codemirror.less
+++ b/awx/ui/client/legacy-styles/codemirror.less
@@ -39,11 +39,9 @@
// Disabled
textarea[disabled="disabled"] + div[id*="-container"]{
- pointer-events: none;
- cursor: not-allowed;
-
.CodeMirror {
cursor: not-allowed;
+ pointer-events: none;
}
.CodeMirror.cm-s-default,
@@ -68,4 +66,10 @@ textarea[disabled="disabled"] + div[id*="-container"]{
.CodeMirror-cursors {
display: none;
}
+
+ .CodeMirror.cm-s-default {
+ min-height: initial !important;
+ max-height: initial !important;
+ height: initial !important;
+ }
}
diff --git a/awx/ui/client/legacy-styles/forms.less b/awx/ui/client/legacy-styles/forms.less
index 3e5f7f79bf..a16654ba13 100644
--- a/awx/ui/client/legacy-styles/forms.less
+++ b/awx/ui/client/legacy-styles/forms.less
@@ -283,13 +283,13 @@
}
.Button-primary--hollow {
- border: 1px solid @default-link;
+ border: 1px solid @default-link;
color: @default-link;
background: @default-bg;
}
.Button-primary--hollow:hover {
color: @default-link-hov;
- border: 1px solid @default-link-hov;
+ border: 1px solid @default-link-hov;
}
.ui-spinner{
@@ -495,7 +495,8 @@ input[type='radio']:checked:before {
display: block !important;
}
-.Form-inputLabelContainer[for=variables] {
+.Form-inputLabelContainer[for=variables],
+.Form-inputLabelContainer--codeMirror {
width: auto;
display: inline-block !important;
}
@@ -506,9 +507,10 @@ input[type='radio']:checked:before {
.FormToggle {}
.FormToggle-container {
+ float: right;
margin: 0 0 0 10px;
- padding-bottom: 5px;
display: initial;
+ padding-bottom: 5px;
label {
&:first-child {
@@ -529,6 +531,10 @@ input[type='radio']:checked:before {
}
}
+#credential_type_form .FormToggle-container {
+ float: initial;
+}
+
.Form-inputLabelContainer {
width: 100%;
display: block !important;
diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js
index 4b3c5e6ed8..0649023b54 100644
--- a/awx/ui/client/src/app.js
+++ b/awx/ui/client/src/app.js
@@ -41,6 +41,7 @@ import portalMode from './portal-mode/main';
import systemTracking from './system-tracking/main';
import inventories from './inventories/main';
import inventoryScripts from './inventory-scripts/main';
+import credentialTypes from './credential-types/main';
import organizations from './organizations/main';
import managementJobs from './management-jobs/main';
import workflowResults from './workflow-results/main';
@@ -99,6 +100,7 @@ var tower = angular.module('Tower', [
systemTracking.name,
inventories.name,
inventoryScripts.name,
+ credentialTypes.name,
organizations.name,
managementJobs.name,
setupMenu.name,
diff --git a/awx/ui/client/src/credential-types/add/add.controller.js b/awx/ui/client/src/credential-types/add/add.controller.js
new file mode 100644
index 0000000000..d5302bdf61
--- /dev/null
+++ b/awx/ui/client/src/credential-types/add/add.controller.js
@@ -0,0 +1,106 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+export default ['Rest', 'Wait',
+ 'CredentialTypesForm', 'ProcessErrors', 'GetBasePath',
+ 'GenerateForm', '$scope', '$state', 'Alert', 'GetChoices', 'ParseTypeChange', 'ToJSON', 'CreateSelect2',
+ function(Rest, Wait,
+ CredentialTypesForm, ProcessErrors, GetBasePath,
+ GenerateForm, $scope, $state, Alert, GetChoices, ParseTypeChange, ToJSON, CreateSelect2
+ ) {
+ var form = CredentialTypesForm,
+ url = GetBasePath('credential_types');
+
+ init();
+
+ function init() {
+ // Load the list of options for Kind
+ GetChoices({
+ scope: $scope,
+ url: url,
+ field: 'kind',
+ variable: 'credential_kind_options'
+ });
+
+ Rest.setUrl(url);
+ Rest.options()
+ .success(function(data) {
+ if (!data.actions.POST) {
+ $state.go("^");
+ Alert('Permission Error', 'You do not have permission to add a credential type.', 'alert-info');
+ }
+ });
+
+ // apply form definition's default field values
+ GenerateForm.applyDefaults(form, $scope);
+
+ // @issue @jmitchell - this setting probably collides with new RBAC can* implementation?
+ $scope.canEdit = true;
+
+ var callback = function() {
+ // Make sure the form controller knows there was a change
+ $scope[form.name + '_form'].$setDirty();
+ };
+ $scope.parseTypeInputs = 'yaml';
+ $scope.parseTypeInjectors = 'yaml';
+ ParseTypeChange({
+ scope: $scope,
+ field_id: 'credential_type_inputs',
+ variable: 'inputs',
+ onChange: callback,
+ parse_variable: 'parseTypeInputs'
+ });
+ ParseTypeChange({
+ scope: $scope,
+ field_id: 'credential_type_injectors',
+ variable: 'injectors',
+ onChange: callback,
+ parse_variable: 'parseTypeInjectors'
+ });
+
+ CreateSelect2({
+ element: '#credential_type_kind',
+ multiple: false,
+ });
+ }
+
+ // Save
+ $scope.formSave = function() {
+ GenerateForm.clearApiErrors($scope);
+ Wait('start');
+ Rest.setUrl(url);
+ var inputs = ToJSON($scope.parseTypeInputs, $scope.inputs);
+ var injectors = ToJSON($scope.parseTypeInjectors, $scope.injectors);
+ if (inputs === null) {
+ inputs = {};
+ }
+ if (injectors === null) {
+ injectors = {};
+ }
+ Rest.post({
+ name: $scope.name,
+ description: $scope.description,
+ kind: $scope.kind.value,
+ inputs: inputs,
+ injectors: injectors
+ })
+ .success(function(data) {
+ $state.go('credentialTypes.edit', { credential_type_id: data.id }, { reload: true });
+ Wait('stop');
+ })
+ .error(function(data, status) {
+ ProcessErrors($scope, data, status, form, {
+ hdr: 'Error!',
+ msg: 'Failed to add new credential type. PUT returned status: ' + status
+ });
+ });
+ };
+
+ $scope.formCancel = function() {
+ $state.go('^');
+ };
+ }
+];
diff --git a/awx/ui/client/src/credential-types/add/main.js b/awx/ui/client/src/credential-types/add/main.js
new file mode 100644
index 0000000000..9344da0e98
--- /dev/null
+++ b/awx/ui/client/src/credential-types/add/main.js
@@ -0,0 +1,11 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+import controller from './add.controller';
+
+export default
+ angular.module('credentialTypesAdd', [])
+ .controller('CredentialTypesAddController', controller);
diff --git a/awx/ui/client/src/credential-types/credential-types.form.js b/awx/ui/client/src/credential-types/credential-types.form.js
new file mode 100644
index 0000000000..f1fba41b84
--- /dev/null
+++ b/awx/ui/client/src/credential-types/credential-types.form.js
@@ -0,0 +1,113 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+ /**
+ * @ngdoc function
+ * @name forms.function:CredentialType
+ * @description This form is for adding/editing a credential type
+*/
+
+export default ['i18n', function(i18n) {
+ return {
+
+ addTitle: i18n._('NEW CREDENTIAL TYPE'),
+ editTitle: '{{ name }}',
+ name: 'credential_type',
+ basePath: 'credential_types',
+ stateTree: 'credentialTypes',
+ breadcrumbName: i18n._('CREDENTIAL TYPE'),
+ showActions: true,
+
+ // TODO: update fields to be the schema for credential types instead of inventory scripts
+ fields: {
+ name: {
+ label: i18n._('Name'),
+ type: 'text',
+ ngDisabled: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)',
+ required: true,
+ capitalize: false
+ },
+ description: {
+ label: i18n._('Description'),
+ type: 'text',
+ ngDisabled: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ kind: {
+ label: i18n._('Kind'),
+ excludeModal: true,
+ type: 'select',
+ ngOptions: 'kind.label for kind in credential_kind_options track by kind.value',
+ required: true,
+ awPopOver: '\n' +
+ '' + i18n._('Machine') + ' \n' +
+ '' + i18n._('Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
+ 'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
+ 'remote hosts.') + ' ' +
+ '' + i18n._('Network') + ' \n' +
+ '' + i18n._('Authentication for network device access. This can include SSH keys, usernames, passwords, ' +
+ 'and authorize information. Network credentials are used when submitting jobs to run playbooks against ' +
+ 'network devices.') + ' ' +
+ '' + i18n._('Source Control') + ' \n' +
+ '' + i18n._('Used to check out and synchronize playbook repositories with a remote source control ' +
+ 'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
+ 'used by Projects.') + ' \n' +
+ '' + i18n._('Cloud') + ' \n' +
+ '' + i18n._('Usernames, passwords, and access keys for authenticating to the specified cloud or infrastructure ' +
+ 'provider. These are used for dynamic inventory sources and for cloud provisioning and deployment ' +
+ 'in playbook runs.') + ' \n' +
+ ' \n',
+ dataTitle: i18n._('Kind'),
+ dataPlacement: 'right',
+ dataContainer: "body",
+ ngDisabled: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ inputs: {
+ label: i18n._('Input Configuration'),
+ class: 'Form-textAreaLabel Form-formGroup--fullWidth',
+ type: 'textarea',
+ rows: 6,
+ default: '---',
+ showParseTypeToggle: true,
+ parseTypeName: 'parseTypeInputs',
+ awPopOver: 'TODO: input config helper text
',
+ dataTitle: i18n._('Input Configuration'),
+ dataPlacement: 'right',
+ dataContainer: "body",
+ ngDisabled: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ injectors: {
+ label: i18n._('Injector Configuration'),
+ class: 'Form-textAreaLabel Form-formGroup--fullWidth',
+ type: 'textarea',
+ rows: 6,
+ default: '---',
+ showParseTypeToggle: true,
+ parseTypeName: 'parseTypeInjectors',
+ awPopOver: 'TODO: injector config helper text
',
+ dataTitle: i18n._('Injector Configuration'),
+ dataPlacement: 'right',
+ dataContainer: "body",
+ ngDisabled: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ },
+
+ buttons: { //for now always generates tags
+ cancel: {
+ ngClick: 'formCancel()',
+ ngShow: '(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ close: {
+ ngClick: 'formCancel()',
+ ngShow: '!(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ },
+ save: {
+ ngClick: 'formSave()', //$scope.function to call on click, optional
+ ngDisabled: 'credential_type_form.$invalid', //Disable when $invalid, optional
+ ngShow: '(credential_type.summary_fields.user_capabilities.edit || canAdd)'
+ }
+ }
+ };
+}];
diff --git a/awx/ui/client/src/credential-types/credential-types.list.js b/awx/ui/client/src/credential-types/credential-types.list.js
new file mode 100644
index 0000000000..4e9191876b
--- /dev/null
+++ b/awx/ui/client/src/credential-types/credential-types.list.js
@@ -0,0 +1,82 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+
+
+export default ['i18n', function(i18n){
+ return {
+ name: 'credential_types' ,
+ listTitle: i18n._('CREDENTIAL TYPES'),
+ basePath: 'credential_types',
+ iterator: 'credential_type',
+ index: false,
+ hover: false,
+
+ fields: {
+ name: {
+ key: true,
+ label: i18n._('Name'),
+ columnClass: 'col-md-3 col-sm-9 col-xs-9',
+ modalColumnClass: 'col-md-8'
+ },
+ // TODO: update to tooltip on name
+ description: {
+ label: i18n._('Description'),
+ excludeModal: true,
+ columnClass: 'col-md-4 hidden-sm hidden-xs'
+ },
+ kind: {
+ label: i18n._('Type'),
+ ngBind: 'credential_type.kind_label',
+ excludeModal: true,
+ columnClass: 'col-md-2 hidden-sm hidden-xs'
+ },
+ },
+
+ actions: {
+ add: {
+ mode: 'all', // One of: edit, select, all
+ ngClick: 'addCredentialType()',
+ awToolTip: i18n._('Create a new credential type'),
+ actionClass: 'btn List-buttonSubmit',
+ buttonContent: '+ ' + i18n._('ADD'),
+ ngShow: 'canAdd'
+ }
+ },
+
+ fieldActions: {
+
+ columnClass: 'col-md-2 col-sm-3 col-xs-3',
+
+ edit: {
+ ngClick: "editCredentialType(credential_type.id)",
+ icon: 'fa-edit',
+ label: i18n._('Edit'),
+ "class": 'btn-sm',
+ awToolTip: i18n._('Edit credenital type'),
+ dataPlacement: 'top',
+ ngShow: 'credential_type.summary_fields.user_capabilities.edit'
+ },
+ view: {
+ ngClick: "editCredentialType(credential_type.id)",
+ label: i18n._('View'),
+ "class": 'btn-sm',
+ awToolTip: i18n._('View credential type'),
+ dataPlacement: 'top',
+ ngShow: '!credential_type.summary_fields.user_capabilities.edit'
+ },
+ "delete": {
+ ngClick: "deleteCredentialType(credential_type.id, credential_type.name)",
+ icon: 'fa-trash',
+ label: i18n._('Delete'),
+ "class": 'btn-sm',
+ awToolTip: i18n._('Delete credential type'),
+ dataPlacement: 'top',
+ ngShow: 'credential_type.summary_fields.user_capabilities.delete'
+ }
+ }
+ };
+}];
diff --git a/awx/ui/client/src/credential-types/edit/edit.controller.js b/awx/ui/client/src/credential-types/edit/edit.controller.js
new file mode 100644
index 0000000000..8861c647ba
--- /dev/null
+++ b/awx/ui/client/src/credential-types/edit/edit.controller.js
@@ -0,0 +1,172 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+export default ['Rest', 'Wait',
+ 'CredentialTypesForm', 'ProcessErrors', 'GetBasePath',
+ 'GenerateForm', 'credential_typeData',
+ '$scope', '$state', 'GetChoices', 'ParseTypeChange', 'ToJSON', 'ParseVariableString', 'CreateSelect2',
+ function(
+ Rest, Wait, CredentialTypesForm, ProcessErrors, GetBasePath,
+ GenerateForm, credential_typeData,
+ $scope, $state, GetChoices, ParseTypeChange, ToJSON, ParseVariableString, CreateSelect2
+ ) {
+ var generator = GenerateForm,
+ data = credential_typeData,
+ id = credential_typeData.id,
+ form = CredentialTypesForm,
+ master = {},
+ url = GetBasePath('credential_types');
+
+ init();
+
+ function init() {
+ // Load the list of options for Kind
+ GetChoices({
+ scope: $scope,
+ url: url,
+ field: 'kind',
+ variable: 'credential_kind_options',
+ callback: 'choicesReadyCredentialTypes'
+ });
+ }
+
+ if ($scope.removeChoicesReady) {
+ $scope.removeChoicesReady();
+ }
+ $scope.removeChoicesReady = $scope.$on('choicesReadyCredentialTypes',
+ function() {
+ $scope.credential_type = credential_typeData;
+
+ $scope.$watch('credential_type.summary_fields.user_capabilities.edit', function(val) {
+ if (val === false) {
+ $scope.canAdd = false;
+ }
+ });
+
+ function getVars(str){
+
+ // Quick function to test if the host vars are a json-object-string,
+ // by testing if they can be converted to a JSON object w/o error.
+ function IsJsonString(str) {
+ try {
+ JSON.parse(str);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+
+ if(str === ''){
+ return '---';
+ }
+ else if(IsJsonString(str)){
+ str = JSON.parse(str);
+ return jsyaml.safeDump(str);
+ }
+ else if(!IsJsonString(str)){
+ return str;
+ }
+ }
+
+ var fld, i;
+ for (fld in form.fields) {
+ if (data[fld] && fld !== 'inputs' || fld !== 'injectors') {
+ $scope[fld] = data[fld];
+ master[fld] = data[fld];
+ }
+
+ if (fld === "kind") {
+ // Set kind field to the correct option
+ for (i = 0; i < $scope.credential_kind_options.length; i++) {
+ if ($scope.kind === $scope.credential_kind_options[i].value) {
+ $scope.kind = $scope.credential_kind_options[i];
+ master[fld] = $scope.credential_kind_options[i];
+ break;
+ }
+ }
+ }
+ }
+
+ $scope.inputs = ParseVariableString(getVars(data.inputs));
+ $scope.injectors = ParseVariableString(getVars(data.injectors));
+
+ // if ($scope.inputs === "{}") {
+ // $scope.inputs = "---";
+ // }
+ //
+ // if ($scope.injectors === "{}") {
+ // $scope.injectors = "---";
+ // }
+
+ // $scope.inputs = JSON.parse($scope.inputs);
+ // $scope.injectors = JSON.parse($scope.injectors);
+
+ var callback = function() {
+ // Make sure the form controller knows there was a change
+ $scope[form.name + '_form'].$setDirty();
+ };
+ $scope.parseTypeInputs = 'yaml';
+ $scope.parseTypeInjectors = 'yaml';
+
+ ParseTypeChange({
+ scope: $scope,
+ field_id: 'credential_type_inputs',
+ variable: 'inputs',
+ onChange: callback,
+ parse_variable: 'parseTypeInputs'
+ });
+ ParseTypeChange({
+ scope: $scope,
+ field_id: 'credential_type_injectors',
+ variable: 'injectors',
+ onChange: callback,
+ parse_variable: 'parseTypeInjectors'
+ });
+
+ CreateSelect2({
+ element: '#credential_type_kind',
+ multiple: false,
+ });
+ }
+ );
+
+ $scope.formSave = function() {
+ generator.clearApiErrors($scope);
+ Wait('start');
+ Rest.setUrl(url + id + '/');
+ var inputs = ToJSON($scope.parseTypeInputs, $scope.inputs);
+ var injectors = ToJSON($scope.parseTypeInjectors, $scope.injectors);
+ if (inputs === null) {
+ inputs = {};
+ }
+ if (injectors === null) {
+ injectors = {};
+ }
+ Rest.put({
+ name: $scope.name,
+ description: $scope.description,
+ kind: $scope.kind.value,
+ inputs: inputs,
+ injectors: injectors
+ })
+ .success(function() {
+ $state.go($state.current, null, { reload: true });
+ Wait('stop');
+ })
+ .error(function(data, status) {
+ ProcessErrors($scope, data, status, form, {
+ hdr: 'Error!',
+ msg: 'Failed to add new credential type. PUT returned status: ' + status
+ });
+ });
+ };
+
+ $scope.formCancel = function() {
+ $state.go('credentialTypes');
+ };
+
+ }
+];
diff --git a/awx/ui/client/src/credential-types/edit/main.js b/awx/ui/client/src/credential-types/edit/main.js
new file mode 100644
index 0000000000..6b5695ac69
--- /dev/null
+++ b/awx/ui/client/src/credential-types/edit/main.js
@@ -0,0 +1,11 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+import controller from './edit.controller';
+
+export default
+ angular.module('credentialTypesEdit', [])
+ .controller('CredentialTypesEditController', controller);
diff --git a/awx/ui/client/src/credential-types/list/list.controller.js b/awx/ui/client/src/credential-types/list/list.controller.js
new file mode 100644
index 0000000000..c34f8c69e8
--- /dev/null
+++ b/awx/ui/client/src/credential-types/list/list.controller.js
@@ -0,0 +1,122 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
+ 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'Alert',
+ function(
+ $rootScope, $scope, Wait, CredentialTypesList,
+ GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter, Dataset, rbacUiControlService, Alert
+ ) {
+ var defaultUrl = GetBasePath('credential_types'),
+ list = CredentialTypesList;
+
+ init();
+
+ function init() {
+ if (!($rootScope.user_is_superuser || $rootScope.user_is_system_auditor)) {
+ $state.go("setup");
+ Alert('Permission Error', 'You do not have permission to view, edit or create custom credential types.', 'alert-info');
+ }
+
+ $scope.canAdd = false;
+
+ rbacUiControlService.canAdd("credential_types")
+ .then(function(canAdd) {
+ $scope.canAdd = canAdd;
+ });
+
+ // search init
+ $scope.list = list;
+ $scope[`${list.iterator}_dataset`] = Dataset.data;
+ $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
+
+ }
+
+ // @todo what is going on here, and if it needs to happen in this controller make $rootScope var name more explicit
+ if ($rootScope.addedItem) {
+ $scope.addedItem = $rootScope.addedItem;
+ delete $rootScope.addedItem;
+ }
+
+ $scope.editCredentialType = function() {
+ $state.go('credentialTypes.edit', {
+ credential_type_id: this.credential_type.id
+ });
+ };
+
+ $scope.deleteCredentialType = function(id, name) {
+
+ var action = function() {
+ $('#prompt-modal').modal('hide');
+ Wait('start');
+ var url = defaultUrl + id + '/';
+ Rest.setUrl(url);
+ Rest.destroy()
+ .success(function() {
+ if (parseInt($state.params.credential_type_id) === id) {
+ $state.go('^', null, { reload: true });
+ } else {
+ $state.go('.', null, { reload: true });
+ }
+ })
+ .error(function(data, status) {
+ ProcessErrors($scope, data, status, null, {
+ hdr: 'Error!',
+ msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status
+ });
+ });
+ };
+
+ var bodyHtml = 'Are you sure you want to delete the credential type below?
' + $filter('sanitize')(name) + '
';
+ Prompt({
+ hdr: 'Delete',
+ body: bodyHtml,
+ action: action,
+ actionText: 'DELETE'
+ });
+ };
+
+ $scope.addCredentialType = function() {
+ $state.go('credentialTypes.add');
+ };
+
+ // iterate over the list and add fields like type label, after the
+ // OPTIONS request returns, or the list is sorted/paginated/searched
+ function optionsRequestDataProcessing(){
+ if($scope.list.name === 'credential_types'){
+ if ($scope[list.name] !== undefined) {
+ $scope[list.name].forEach(function(item, item_idx) {
+ var itm = $scope[list.name][item_idx];
+
+ // Set the item type label
+ if (list.fields.kind && $scope.options &&
+ $scope.options.hasOwnProperty('kind')) {
+ $scope.options.kind.choices.forEach(function(choice) {
+ if (choice[0] === item.kind) {
+ itm.kind_label = choice[1];
+ }
+ });
+ }
+
+ });
+ }
+ }
+ }
+
+ Rest.setUrl(GetBasePath("credential_types"));
+ Rest.options()
+ .success(function(data) {
+ $scope.options = data.actions.GET;
+ optionsRequestDataProcessing();
+ });
+
+ $scope.$watchCollection(`${$scope.list.name}`, function() {
+ optionsRequestDataProcessing();
+ }
+ );
+
+ }
+];
diff --git a/awx/ui/client/src/credential-types/list/main.js b/awx/ui/client/src/credential-types/list/main.js
new file mode 100644
index 0000000000..a029726ab5
--- /dev/null
+++ b/awx/ui/client/src/credential-types/list/main.js
@@ -0,0 +1,11 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+import CredentialTypesListController from './list.controller';
+
+export default
+angular.module('credenitalTypesList', [])
+ .controller('CredentialTypesListController', CredentialTypesListController);
diff --git a/awx/ui/client/src/credential-types/main.js b/awx/ui/client/src/credential-types/main.js
new file mode 100644
index 0000000000..5215f8e139
--- /dev/null
+++ b/awx/ui/client/src/credential-types/main.js
@@ -0,0 +1,71 @@
+/*************************************************
+ * Copyright (c) 2015 Ansible, Inc.
+ *
+ * All Rights Reserved
+ *************************************************/
+
+import credentialTypesList from './list/main';
+import credentialTypesAdd from './add/main';
+import credentialTypesEdit from './edit/main';
+import list from './credential-types.list';
+import form from './credential-types.form';
+import { N_ } from '../i18n';
+
+export default
+angular.module('credentialTypes', [
+ credentialTypesList.name,
+ credentialTypesAdd.name,
+ credentialTypesEdit.name
+ ])
+ .factory('CredentialTypesList', list)
+ .factory('CredentialTypesForm', form)
+ .config(['$stateProvider', 'stateDefinitionsProvider',
+ function($stateProvider, stateDefinitionsProvider) {
+ let stateDefinitions = stateDefinitionsProvider.$get();
+
+ $stateProvider.state({
+ name: 'credentialTypes',
+ url: '/credential_type',
+ lazyLoad: () => stateDefinitions.generateTree({
+ parent: 'credentialTypes',
+ modes: ['add', 'edit'],
+ list: 'CredentialTypesList',
+ form: 'CredentialTypesForm',
+ controllers: {
+ list: 'CredentialTypesListController',
+ add: 'CredentialTypesAddController',
+ edit: 'CredentialTypesEditController'
+ },
+ resolve: {
+ edit: {
+ credential_typeData: ['$state', '$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors',
+ function($state, $stateParams, rest, getBasePath, ProcessErrors) {
+ var credentialTypeId = $stateParams.credential_type_id;
+ var url = getBasePath('credential_types') + credentialTypeId + '/';
+ rest.setUrl(url);
+ return rest.get()
+ .then(function(data) {
+ return data.data;
+ }).catch(function(response) {
+ ProcessErrors(null, response.data, response.status, null, {
+ hdr: 'Error!',
+ msg: 'Failed to get credential type info. GET returned status: ' +
+ response.status
+ });
+ });
+ }
+ ]
+ }
+ },
+ data: {
+ activityStream: true,
+ activityStreamTarget: 'custom_inventory_script' // TODO: change to 'credential_type'...there's probably more work that needs to be done to hook up activity stream
+ },
+ ncyBreadcrumb: {
+ parent: 'setup',
+ label: N_('CREDENTIAL TYPES')
+ }
+ })
+ });
+ }
+ ]);
diff --git a/awx/ui/client/src/setup-menu/setup-menu.partial.html b/awx/ui/client/src/setup-menu/setup-menu.partial.html
index 9fd4e08b81..aa1bae3eea 100644
--- a/awx/ui/client/src/setup-menu/setup-menu.partial.html
+++ b/awx/ui/client/src/setup-menu/setup-menu.partial.html
@@ -43,6 +43,13 @@
Create templates for sending notifications with Email, HipChat, Slack, and SMS.
+
View Your License
diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js
index 6e6118c7f0..0ce4e72b69 100644
--- a/awx/ui/client/src/shared/form-generator.js
+++ b/awx/ui/client/src/shared/form-generator.js
@@ -646,6 +646,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (field.labelClass) ? field.labelClass : "";
html += `${field.required ? ' prepend-asterisk ' : ''}`;
html += (horizontal) ? " " + getLabelWidth() : "Form-inputLabelContainer ";
+ html += (field.showParseTypeToggle) ? "Form-inputLabelContainer--codeMirror " : "";
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += "for=\"" + fld + '">\n';
@@ -658,7 +659,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (field.awPopOver && !field.awPopOverRight) ? Attr(field, 'awPopOver', fld) : "";
html += (field.hintText) ? "\n\t\t\n\t\t\t\n\t\t\t \n\t\t\tHint: " + field.hintText + "\n\t\t " : "";
// Variable editing
- if (fld === "variables" || fld === "extra_vars" || _.last(fld.split('_')) === 'variables' || fld === 'source_vars') {
+ if (fld === "variables" || fld === "extra_vars" || _.last(fld.split('_')) === 'variables' || fld === 'source_vars' || field.showParseTypeToggle === true) {
let parseTypeId = `${form.name}_${fld}_parse_type`;
let parseTypeName = field.parseTypeName || 'parseType';
let getToggleClass = (primary, secondary) => `{
@@ -674,7 +675,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
YAML
-
+
JSON
From 2b5702706712eea9d33a3acf9c1c9372a61ec00f Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Tue, 25 Apr 2017 18:13:05 -0400
Subject: [PATCH 2/6] deduping options request and possibly fixing choices of
undefined error
---
.../src/credential-types/list/list.controller.js | 15 +++++----------
.../list/credentials-list.controller.js | 4 ++--
.../inventories/add/inventory-add.controller.js | 4 ++--
.../inventories/list/inventory-list.controller.js | 6 +++---
.../manage/groups/groups-add.controller.js | 4 ++--
.../manage/groups/groups-edit.controller.js | 4 ++--
.../manage/groups/groups-list.controller.js | 4 ++--
.../manage/hosts/hosts-add.controller.js | 4 ++--
.../manage/hosts/hosts-list.controller.js | 4 ++--
.../src/inventory-scripts/list/list.controller.js | 4 ++--
.../list.controller.js | 4 ++--
.../list/organizations-list.controller.js | 4 ++--
.../src/projects/list/projects-list.controller.js | 4 ++--
.../src/scheduler/schedulerList.controller.js | 4 ++--
awx/ui/client/src/shared/rbacUiControl.js | 2 +-
.../src/teams/list/teams-list.controller.js | 4 ++--
.../templates/list/templates-list.controller.js | 8 ++++----
.../src/users/list/users-list.controller.js | 4 ++--
18 files changed, 41 insertions(+), 46 deletions(-)
diff --git a/awx/ui/client/src/credential-types/list/list.controller.js b/awx/ui/client/src/credential-types/list/list.controller.js
index c34f8c69e8..27c167923d 100644
--- a/awx/ui/client/src/credential-types/list/list.controller.js
+++ b/awx/ui/client/src/credential-types/list/list.controller.js
@@ -24,8 +24,10 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
$scope.canAdd = false;
rbacUiControlService.canAdd("credential_types")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
+ $scope.options = params.options;
+ optionsRequestDataProcessing();
});
// search init
@@ -93,7 +95,7 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
// Set the item type label
if (list.fields.kind && $scope.options &&
- $scope.options.hasOwnProperty('kind')) {
+ $scope.options.kind) {
$scope.options.kind.choices.forEach(function(choice) {
if (choice[0] === item.kind) {
itm.kind_label = choice[1];
@@ -106,13 +108,6 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
}
}
- Rest.setUrl(GetBasePath("credential_types"));
- Rest.options()
- .success(function(data) {
- $scope.options = data.actions.GET;
- optionsRequestDataProcessing();
- });
-
$scope.$watchCollection(`${$scope.list.name}`, function() {
optionsRequestDataProcessing();
}
diff --git a/awx/ui/client/src/credentials/list/credentials-list.controller.js b/awx/ui/client/src/credentials/list/credentials-list.controller.js
index 06fb2a1ed8..fb0c903b4c 100644
--- a/awx/ui/client/src/credentials/list/credentials-list.controller.js
+++ b/awx/ui/client/src/credentials/list/credentials-list.controller.js
@@ -19,8 +19,8 @@ export default ['$scope', 'Rest', 'CredentialList', 'Prompt', 'ClearScope',
function init() {
rbacUiControlService.canAdd('credentials')
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
diff --git a/awx/ui/client/src/inventories/add/inventory-add.controller.js b/awx/ui/client/src/inventories/add/inventory-add.controller.js
index 4195bd5959..4287cfb404 100644
--- a/awx/ui/client/src/inventories/add/inventory-add.controller.js
+++ b/awx/ui/client/src/inventories/add/inventory-add.controller.js
@@ -17,8 +17,8 @@ function InventoriesAdd($scope, $location,
$scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory'))
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
Rest.setUrl(GetBasePath('inventory'));
diff --git a/awx/ui/client/src/inventories/list/inventory-list.controller.js b/awx/ui/client/src/inventories/list/inventory-list.controller.js
index f538645975..2f43f48100 100644
--- a/awx/ui/client/src/inventories/list/inventory-list.controller.js
+++ b/awx/ui/client/src/inventories/list/inventory-list.controller.js
@@ -23,8 +23,8 @@ function InventoriesList($scope, $rootScope, $location,
$scope.canAdd = false;
rbacUiControlService.canAdd('inventory')
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
$scope.$watchCollection(list.name, function(){
@@ -102,7 +102,7 @@ function InventoriesList($scope, $rootScope, $location,
elem.removeAttr('ng-click');
$compile(elem)($scope);
$scope.triggerPopover(event);
- }
+ }
if ($scope.removeHostSummaryReady) {
$scope.removeHostSummaryReady();
}
diff --git a/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js
index 49d38fba62..a3fadb4ca7 100644
--- a/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js
+++ b/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js
@@ -17,8 +17,8 @@ export default ['$state', '$stateParams', '$scope', 'GroupForm', 'ParseTypeChang
GenerateForm.applyDefaults(form, $scope);
rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/groups")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
$scope.parseType = 'yaml';
$scope.envParseType = 'yaml';
diff --git a/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js
index 1355d5d33f..8be1854c23 100644
--- a/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js
+++ b/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js
@@ -13,8 +13,8 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString', 'rbac
function init() {
rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/groups")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// instantiate expected $scope values from inventorySourceData & groupData
_.assign($scope, { credential: inventorySourceData.credential }, { overwrite: inventorySourceData.overwrite }, { overwrite_vars: inventorySourceData.overwrite_vars }, { update_on_launch: inventorySourceData.update_on_launch }, { update_cache_timeout: inventorySourceData.update_cache_timeout }, { instance_filters: inventorySourceData.instance_filters }, { inventory_script: inventorySourceData.source_script });
diff --git a/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js
index ecbe611588..212d88850a 100644
--- a/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js
+++ b/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js
@@ -21,8 +21,8 @@
$scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/groups")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// Search init
diff --git a/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js b/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js
index 7042800ee2..15d44945ef 100644
--- a/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js
+++ b/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js
@@ -15,8 +15,8 @@ export default ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange
$scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/hosts")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
$scope.parseType = 'yaml';
$scope.host = { enabled: true };
diff --git a/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js b/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js
index ea9d76d9ec..278022e002 100644
--- a/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js
+++ b/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js
@@ -17,8 +17,8 @@
$scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/hosts")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// Search init
diff --git a/awx/ui/client/src/inventory-scripts/list/list.controller.js b/awx/ui/client/src/inventory-scripts/list/list.controller.js
index 24f206d2d0..4aba6d6efb 100644
--- a/awx/ui/client/src/inventory-scripts/list/list.controller.js
+++ b/awx/ui/client/src/inventory-scripts/list/list.controller.js
@@ -19,8 +19,8 @@ export default ['$rootScope', '$scope', 'Wait', 'InventoryScriptsList',
$scope.canAdd = false;
rbacUiControlService.canAdd("inventory_scripts")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
diff --git a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js
index c54df4e598..6d08e74b55 100644
--- a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js
+++ b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js
@@ -23,8 +23,8 @@
$scope.canAdd = false;
rbacUiControlService.canAdd("notification_templates")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
diff --git a/awx/ui/client/src/organizations/list/organizations-list.controller.js b/awx/ui/client/src/organizations/list/organizations-list.controller.js
index b976a4a3f8..0711c21e01 100644
--- a/awx/ui/client/src/organizations/list/organizations-list.controller.js
+++ b/awx/ui/client/src/organizations/list/organizations-list.controller.js
@@ -23,8 +23,8 @@ export default ['$stateParams', '$scope', '$rootScope',
$scope.canAdd = false;
rbacUiControlService.canAdd("organizations")
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
$scope.orgCount = Dataset.data.count;
diff --git a/awx/ui/client/src/projects/list/projects-list.controller.js b/awx/ui/client/src/projects/list/projects-list.controller.js
index 9a8f7d2e02..e70c12f44d 100644
--- a/awx/ui/client/src/projects/list/projects-list.controller.js
+++ b/awx/ui/client/src/projects/list/projects-list.controller.js
@@ -22,8 +22,8 @@ export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert',
$scope.canAdd = false;
rbacUiControlService.canAdd('projects')
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
diff --git a/awx/ui/client/src/scheduler/schedulerList.controller.js b/awx/ui/client/src/scheduler/schedulerList.controller.js
index 454220819d..819615bcb6 100644
--- a/awx/ui/client/src/scheduler/schedulerList.controller.js
+++ b/awx/ui/client/src/scheduler/schedulerList.controller.js
@@ -34,8 +34,8 @@ export default [
scheduleEndpoint = ParentObject.endpoint|| ParentObject.related.schedules || `${ParentObject.related.inventory_source}schedules`;
$scope.canAdd = false;
rbacUiControlService.canAdd(scheduleEndpoint)
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
}
diff --git a/awx/ui/client/src/shared/rbacUiControl.js b/awx/ui/client/src/shared/rbacUiControl.js
index 867fc4a6cb..ee74951d43 100644
--- a/awx/ui/client/src/shared/rbacUiControl.js
+++ b/awx/ui/client/src/shared/rbacUiControl.js
@@ -20,7 +20,7 @@ export default
Rest.options()
.success(function(data) {
if (data.actions.POST) {
- canAddVal.resolve(true);
+ canAddVal.resolve({canAdd: true, options: data});
} else {
canAddVal.reject(false);
}
diff --git a/awx/ui/client/src/teams/list/teams-list.controller.js b/awx/ui/client/src/teams/list/teams-list.controller.js
index 1477452358..2e59cf0b42 100644
--- a/awx/ui/client/src/teams/list/teams-list.controller.js
+++ b/awx/ui/client/src/teams/list/teams-list.controller.js
@@ -21,8 +21,8 @@ export default ['$scope', 'Rest', 'TeamList', 'Prompt', 'ClearScope',
$scope.canAdd = false;
rbacUiControlService.canAdd('teams')
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
$scope.list = list;
diff --git a/awx/ui/client/src/templates/list/templates-list.controller.js b/awx/ui/client/src/templates/list/templates-list.controller.js
index 397351ec6f..5cd54f1347 100644
--- a/awx/ui/client/src/templates/list/templates-list.controller.js
+++ b/awx/ui/client/src/templates/list/templates-list.controller.js
@@ -25,13 +25,13 @@ export default ['$scope', '$rootScope',
$scope.canAdd = false;
rbacUiControlService.canAdd("job_templates")
- .then(function(canAddJobTemplate) {
- $scope.canAddJobTemplate = canAddJobTemplate;
+ .then(function(params) {
+ $scope.canAddJobTemplate = params.canAdd;
});
rbacUiControlService.canAdd("workflow_job_templates")
- .then(function(canAddWorkflowJobTemplate) {
- $scope.canAddWorkflowJobTemplate = canAddWorkflowJobTemplate;
+ .then(function(params) {
+ $scope.canAddWorkflowJobTemplate = params.canAdd;
});
// search init
$scope.list = list;
diff --git a/awx/ui/client/src/users/list/users-list.controller.js b/awx/ui/client/src/users/list/users-list.controller.js
index 3eb05edb83..c443c634b2 100644
--- a/awx/ui/client/src/users/list/users-list.controller.js
+++ b/awx/ui/client/src/users/list/users-list.controller.js
@@ -34,8 +34,8 @@ export default ['$scope', '$rootScope', 'Rest', 'UserList', 'Prompt',
$scope.canAdd = false;
rbacUiControlService.canAdd('users')
- .then(function(canAdd) {
- $scope.canAdd = canAdd;
+ .then(function(params) {
+ $scope.canAdd = params.canAdd;
});
// search init
From 3731c8b72f2d989d72799efbd1024ccf5d141502 Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Tue, 25 Apr 2017 18:58:12 -0400
Subject: [PATCH 3/6] reduce options reqests and get requests further
---
.../credential-types/add/add.controller.js | 20 ++---
.../credential-types/edit/edit.controller.js | 26 ++++---
.../credential-types/list/list.controller.js | 7 +-
awx/ui/client/src/credential-types/main.js | 21 -----
awx/ui/client/src/rest/get-choices.factory.js | 52 ++++++++-----
awx/ui/client/src/shared/Utilities.js | 77 +++++++++++--------
6 files changed, 107 insertions(+), 96 deletions(-)
diff --git a/awx/ui/client/src/credential-types/add/add.controller.js b/awx/ui/client/src/credential-types/add/add.controller.js
index d5302bdf61..c8d2d3d38f 100644
--- a/awx/ui/client/src/credential-types/add/add.controller.js
+++ b/awx/ui/client/src/credential-types/add/add.controller.js
@@ -18,17 +18,17 @@ export default ['Rest', 'Wait',
function init() {
// Load the list of options for Kind
- GetChoices({
- scope: $scope,
- url: url,
- field: 'kind',
- variable: 'credential_kind_options'
- });
+ $scope.$parent.optionsDefer.promise
+ .then(function(options) {
+ GetChoices({
+ scope: $scope,
+ url: url,
+ field: 'kind',
+ variable: 'credential_kind_options',
+ options: options
+ });
- Rest.setUrl(url);
- Rest.options()
- .success(function(data) {
- if (!data.actions.POST) {
+ if (!options.actions.POST) {
$state.go("^");
Alert('Permission Error', 'You do not have permission to add a credential type.', 'alert-info');
}
diff --git a/awx/ui/client/src/credential-types/edit/edit.controller.js b/awx/ui/client/src/credential-types/edit/edit.controller.js
index 8861c647ba..2808ef3249 100644
--- a/awx/ui/client/src/credential-types/edit/edit.controller.js
+++ b/awx/ui/client/src/credential-types/edit/edit.controller.js
@@ -6,14 +6,15 @@
export default ['Rest', 'Wait',
'CredentialTypesForm', 'ProcessErrors', 'GetBasePath',
- 'GenerateForm', 'credential_typeData',
+ 'GenerateForm', 'resourceData',
'$scope', '$state', 'GetChoices', 'ParseTypeChange', 'ToJSON', 'ParseVariableString', 'CreateSelect2',
function(
Rest, Wait, CredentialTypesForm, ProcessErrors, GetBasePath,
- GenerateForm, credential_typeData,
+ GenerateForm, resourceData,
$scope, $state, GetChoices, ParseTypeChange, ToJSON, ParseVariableString, CreateSelect2
) {
- var generator = GenerateForm,
+ var credential_typeData = resourceData.data,
+ generator = GenerateForm,
data = credential_typeData,
id = credential_typeData.id,
form = CredentialTypesForm,
@@ -24,13 +25,17 @@ export default ['Rest', 'Wait',
function init() {
// Load the list of options for Kind
- GetChoices({
- scope: $scope,
- url: url,
- field: 'kind',
- variable: 'credential_kind_options',
- callback: 'choicesReadyCredentialTypes'
- });
+ $scope.$parent.optionsDefer.promise
+ .then(function(options) {
+ GetChoices({
+ scope: $scope,
+ url: url,
+ field: 'kind',
+ variable: 'credential_kind_options',
+ options: options,
+ callback: 'choicesReadyCredentialTypes'
+ });
+ });
}
if ($scope.removeChoicesReady) {
@@ -38,6 +43,7 @@ export default ['Rest', 'Wait',
}
$scope.removeChoicesReady = $scope.$on('choicesReadyCredentialTypes',
function() {
+
$scope.credential_type = credential_typeData;
$scope.$watch('credential_type.summary_fields.user_capabilities.edit', function(val) {
diff --git a/awx/ui/client/src/credential-types/list/list.controller.js b/awx/ui/client/src/credential-types/list/list.controller.js
index 27c167923d..0b16ca23d5 100644
--- a/awx/ui/client/src/credential-types/list/list.controller.js
+++ b/awx/ui/client/src/credential-types/list/list.controller.js
@@ -5,10 +5,10 @@
*************************************************/
export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
- 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'Alert',
+ 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'Alert', '$q',
function(
$rootScope, $scope, Wait, CredentialTypesList,
- GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter, Dataset, rbacUiControlService, Alert
+ GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter, Dataset, rbacUiControlService, Alert, $q
) {
var defaultUrl = GetBasePath('credential_types'),
list = CredentialTypesList;
@@ -16,6 +16,8 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
init();
function init() {
+ $scope.optionsDefer = $q.defer();
+
if (!($rootScope.user_is_superuser || $rootScope.user_is_system_auditor)) {
$state.go("setup");
Alert('Permission Error', 'You do not have permission to view, edit or create custom credential types.', 'alert-info');
@@ -27,6 +29,7 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
.then(function(params) {
$scope.canAdd = params.canAdd;
$scope.options = params.options;
+ $scope.optionsDefer.resolve(params.options);
optionsRequestDataProcessing();
});
diff --git a/awx/ui/client/src/credential-types/main.js b/awx/ui/client/src/credential-types/main.js
index 5215f8e139..2ccb9607ed 100644
--- a/awx/ui/client/src/credential-types/main.js
+++ b/awx/ui/client/src/credential-types/main.js
@@ -36,27 +36,6 @@ angular.module('credentialTypes', [
add: 'CredentialTypesAddController',
edit: 'CredentialTypesEditController'
},
- resolve: {
- edit: {
- credential_typeData: ['$state', '$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors',
- function($state, $stateParams, rest, getBasePath, ProcessErrors) {
- var credentialTypeId = $stateParams.credential_type_id;
- var url = getBasePath('credential_types') + credentialTypeId + '/';
- rest.setUrl(url);
- return rest.get()
- .then(function(data) {
- return data.data;
- }).catch(function(response) {
- ProcessErrors(null, response.data, response.status, null, {
- hdr: 'Error!',
- msg: 'Failed to get credential type info. GET returned status: ' +
- response.status
- });
- });
- }
- ]
- }
- },
data: {
activityStream: true,
activityStreamTarget: 'custom_inventory_script' // TODO: change to 'credential_type'...there's probably more work that needs to be done to hook up activity stream
diff --git a/awx/ui/client/src/rest/get-choices.factory.js b/awx/ui/client/src/rest/get-choices.factory.js
index 33537d5ab4..e9f7e4398d 100644
--- a/awx/ui/client/src/rest/get-choices.factory.js
+++ b/awx/ui/client/src/rest/get-choices.factory.js
@@ -17,27 +17,41 @@
return function (params) {
var scope = params.scope,
url = params.url,
- field = params.field;
+ field = params.field,
+ options = params.options;
- // Auto populate the field if there is only one result
- Rest.setUrl(url);
- return Rest.options()
- .then(function (data) {
- data = data.data;
- var choices = data.actions.GET[field].choices;
+ if (!options) {
+ // Auto populate the field if there is only one result
+ Rest.setUrl(url);
+ return Rest.options()
+ .then(function (data) {
+ data = data.data;
+ var choices = data.actions.GET[field].choices;
- // manually add the adhoc label to the choices object if
- // the permission_type field
- if (field === "permission_type") {
- choices.push(["adhoc",
- data.actions.GET.run_ad_hoc_commands.help_text]);
- }
+ // manually add the adhoc label to the choices object if
+ // the permission_type field
+ if (field === "permission_type") {
+ choices.push(["adhoc",
+ data.actions.GET.run_ad_hoc_commands.help_text]);
+ }
- return choices;
- })
- .catch(function (data, status) {
- ProcessErrors(scope, data, status, null, { hdr: 'Error!',
- msg: 'Failed to get ' + field + ' labels. Options requrest returned status: ' + status });
- });
+ return choices;
+ })
+ .catch(function (data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to get ' + field + ' labels. Options requrest returned status: ' + status });
+ });
+ } else {
+ var choices = options.actions.GET[field].choices;
+
+ // manually add the adhoc label to the choices object if
+ // the permission_type field
+ if (field === "permission_type") {
+ choices.push(["adhoc",
+ options.actions.GET.run_ad_hoc_commands.help_text]);
+ }
+
+ return choices;
+ }
};
}];
diff --git a/awx/ui/client/src/shared/Utilities.js b/awx/ui/client/src/shared/Utilities.js
index 0b7a86970f..de5dad33a3 100644
--- a/awx/ui/client/src/shared/Utilities.js
+++ b/awx/ui/client/src/shared/Utilities.js
@@ -720,7 +720,8 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
field = params.field,
variable = params.variable,
callback = params.callback,
- choice_name = params.choice_name;
+ choice_name = params.choice_name,
+ options = params.options
if (scope[variable]) {
scope[variable].length = 0;
@@ -728,42 +729,50 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
scope[variable] = [];
}
- Rest.setUrl(url);
- Rest.options()
- .success(function(data) {
- var choices, defaultChoice;
- choices = (choice_name) ? data.actions.GET[field][choice_name] : data.actions.GET[field].choices;
- if (data && data.actions && data.actions.POST && data.actions.POST[field]) {
- defaultChoice = data.actions.POST[field].default;
- }
- if (choices) {
- // including 'name' property so list can be used by search
- choices.forEach(function(choice) {
- scope[variable].push({
- label: choice[1],
- value: choice[0],
- name: choice[1]
- });
+ var withOptions = function(options) {
+ var choices, defaultChoice;
+ choices = (choice_name) ? options.actions.GET[field][choice_name] : options.actions.GET[field].choices;
+ if (options && options.actions && options.actions.POST && options.actions.POST[field]) {
+ defaultChoice = options.actions.POST[field].default;
+ }
+ if (choices) {
+ // including 'name' property so list can be used by search
+ choices.forEach(function(choice) {
+ scope[variable].push({
+ label: choice[1],
+ value: choice[0],
+ name: choice[1]
});
- }
- if (defaultChoice !== undefined) {
- var val;
- for (val in scope[variable]) {
- if (scope[variable][val].value === defaultChoice) {
- scope[variable][val].isDefault = true;
- }
+ });
+ }
+ if (defaultChoice !== undefined) {
+ var val;
+ for (val in scope[variable]) {
+ if (scope[variable][val].value === defaultChoice) {
+ scope[variable][val].isDefault = true;
}
}
- if (callback) {
- scope.$emit(callback);
- }
- })
- .error(function(data, status) {
- ProcessErrors(scope, data, status, null, {
- hdr: 'Error!',
- msg: 'Failed to get ' + url + '. GET status: ' + status
- });
- });
+ }
+ if (callback) {
+ scope.$emit(callback);
+ }
+ }
+
+ if (!options) {
+ Rest.setUrl(url);
+ Rest.options()
+ .success(function(data) {
+ withOptions(data);
+ })
+ .error(function(data, status) {
+ ProcessErrors(scope, data, status, null, {
+ hdr: 'Error!',
+ msg: 'Failed to get ' + url + '. GET status: ' + status
+ });
+ });
+ } else {
+ withOptions(options);
+ }
};
}
])
From 3a5a9aac4132fc58fda1d23e2e4a67152044be17 Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Fri, 28 Apr 2017 17:07:54 -0400
Subject: [PATCH 4/6] make select2 elements fit in their alloted space
---
awx/ui/client/legacy-styles/ansible-ui.less | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/awx/ui/client/legacy-styles/ansible-ui.less b/awx/ui/client/legacy-styles/ansible-ui.less
index bf10688c22..52b9d1e524 100644
--- a/awx/ui/client/legacy-styles/ansible-ui.less
+++ b/awx/ui/client/legacy-styles/ansible-ui.less
@@ -2112,6 +2112,11 @@ tr td button i {
box-shadow: none !important;
}
+.select2-container {
+ margin-left: 2px;
+ margin-top: 2px;
+}
+
.form-control + .select2-container--disabled .select2-selection {
background-color: @egrey !important;
}
From 7f48da9114c43788a771820979452108e6dde995 Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Fri, 28 Apr 2017 17:08:18 -0400
Subject: [PATCH 5/6] fix toggle display based on greg's work
---
awx/ui/client/legacy-styles/forms.less | 6 ------
1 file changed, 6 deletions(-)
diff --git a/awx/ui/client/legacy-styles/forms.less b/awx/ui/client/legacy-styles/forms.less
index a16654ba13..dcf9154154 100644
--- a/awx/ui/client/legacy-styles/forms.less
+++ b/awx/ui/client/legacy-styles/forms.less
@@ -507,7 +507,6 @@ input[type='radio']:checked:before {
.FormToggle {}
.FormToggle-container {
- float: right;
margin: 0 0 0 10px;
display: initial;
padding-bottom: 5px;
@@ -531,10 +530,6 @@ input[type='radio']:checked:before {
}
}
-#credential_type_form .FormToggle-container {
- float: initial;
-}
-
.Form-inputLabelContainer {
width: 100%;
display: block !important;
@@ -708,4 +703,3 @@ input[type='radio']:checked:before {
overflow-wrap: break-word;
white-space: normal;
}
-
From 879f98df2a0bd5222f3bbe16a6de067ec371b237 Mon Sep 17 00:00:00 2001
From: John Mitchell
Date: Fri, 28 Apr 2017 17:08:59 -0400
Subject: [PATCH 6/6] updates to credential types forms and lists
---
.../credential-types/credential-types.form.js | 18 ++++++++++++--
.../credential-types/credential-types.list.js | 10 +++-----
.../credential-types/list/list.controller.js | 24 +++++++++----------
awx/ui/client/src/shared/Utilities.js | 4 ++--
awx/ui/client/src/shared/form-generator.js | 2 +-
5 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/awx/ui/client/src/credential-types/credential-types.form.js b/awx/ui/client/src/credential-types/credential-types.form.js
index f1fba41b84..417abbb926 100644
--- a/awx/ui/client/src/credential-types/credential-types.form.js
+++ b/awx/ui/client/src/credential-types/credential-types.form.js
@@ -72,7 +72,14 @@ export default ['i18n', function(i18n) {
default: '---',
showParseTypeToggle: true,
parseTypeName: 'parseTypeInputs',
- awPopOver: 'TODO: input config helper text
',
+ awPopOver: "Enter inputs using either JSON or YAML syntax. Use the " +
+ "radio button to toggle between the two.
" +
+ "JSON: \n" +
+ "{ \"somevar\": \"somevalue\", \"password\": \"magic\" } \n" +
+ "YAML: \n" +
+ "--- somevar: somevalue password: magic \n" +
+ 'View JSON examples at www.json.org
' +
+ 'View YAML examples at docs.ansible.com
',
dataTitle: i18n._('Input Configuration'),
dataPlacement: 'right',
dataContainer: "body",
@@ -86,7 +93,14 @@ export default ['i18n', function(i18n) {
default: '---',
showParseTypeToggle: true,
parseTypeName: 'parseTypeInjectors',
- awPopOver: 'TODO: injector config helper text
',
+ awPopOver: "Enter injectors using either JSON or YAML syntax. Use the " +
+ "radio button to toggle between the two.
" +
+ "JSON: \n" +
+ "{ \"somevar\": \"somevalue\", \"password\": \"magic\" } \n" +
+ "YAML: \n" +
+ "--- somevar: somevalue password: magic \n" +
+ 'View JSON examples at www.json.org
' +
+ 'View YAML examples at docs.ansible.com
',
dataTitle: i18n._('Injector Configuration'),
dataPlacement: 'right',
dataContainer: "body",
diff --git a/awx/ui/client/src/credential-types/credential-types.list.js b/awx/ui/client/src/credential-types/credential-types.list.js
index 4e9191876b..a32be91ece 100644
--- a/awx/ui/client/src/credential-types/credential-types.list.js
+++ b/awx/ui/client/src/credential-types/credential-types.list.js
@@ -20,13 +20,9 @@ export default ['i18n', function(i18n){
key: true,
label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
- modalColumnClass: 'col-md-8'
- },
- // TODO: update to tooltip on name
- description: {
- label: i18n._('Description'),
- excludeModal: true,
- columnClass: 'col-md-4 hidden-sm hidden-xs'
+ modalColumnClass: 'col-md-8',
+ awToolTip: '{{credential_type.description}}',
+ dataPlacement: 'top'
},
kind: {
label: i18n._('Type'),
diff --git a/awx/ui/client/src/credential-types/list/list.controller.js b/awx/ui/client/src/credential-types/list/list.controller.js
index 0b16ca23d5..54ff04c397 100644
--- a/awx/ui/client/src/credential-types/list/list.controller.js
+++ b/awx/ui/client/src/credential-types/list/list.controller.js
@@ -91,24 +91,24 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
// iterate over the list and add fields like type label, after the
// OPTIONS request returns, or the list is sorted/paginated/searched
function optionsRequestDataProcessing(){
- if($scope.list.name === 'credential_types'){
- if ($scope[list.name] !== undefined) {
- $scope[list.name].forEach(function(item, item_idx) {
- var itm = $scope[list.name][item_idx];
-
- // Set the item type label
- if (list.fields.kind && $scope.options &&
- $scope.options.kind) {
- $scope.options.kind.choices.forEach(function(choice) {
+ $scope.optionsDefer.promise.then(function(options) {
+ if($scope.list.name === 'credential_types'){
+ if ($scope[list.name] !== undefined) {
+ $scope[list.name].forEach(function(item, item_idx) {
+ var itm = $scope[list.name][item_idx];
+ // Set the item type label
+ if (list.fields.kind && options && options.actions && options.actions.GET && options.actions.GET.kind) {
+ options.actions.GET.kind.choices.forEach(function(choice) {
if (choice[0] === item.kind) {
itm.kind_label = choice[1];
}
});
- }
+ }
- });
+ });
+ }
}
- }
+ });
}
$scope.$watchCollection(`${$scope.list.name}`, function() {
diff --git a/awx/ui/client/src/shared/Utilities.js b/awx/ui/client/src/shared/Utilities.js
index de5dad33a3..5a49d1e8ff 100644
--- a/awx/ui/client/src/shared/Utilities.js
+++ b/awx/ui/client/src/shared/Utilities.js
@@ -721,7 +721,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
variable = params.variable,
callback = params.callback,
choice_name = params.choice_name,
- options = params.options
+ options = params.options;
if (scope[variable]) {
scope[variable].length = 0;
@@ -756,7 +756,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
if (callback) {
scope.$emit(callback);
}
- }
+ };
if (!options) {
Rest.setUrl(url);
diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js
index 0ce4e72b69..63d813ff12 100644
--- a/awx/ui/client/src/shared/form-generator.js
+++ b/awx/ui/client/src/shared/form-generator.js
@@ -675,7 +675,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
YAML
-
+
JSON