diff --git a/awx/ui/client/src/forms/Groups.js b/awx/ui/client/src/forms/Groups.js index 83b2af3aab..06d717e532 100644 --- a/awx/ui/client/src/forms/Groups.js +++ b/awx/ui/client/src/forms/Groups.js @@ -45,6 +45,7 @@ export default 'default': '---', dataTitle: 'Group Variables', dataPlacement: 'right', + parseTypeName: 'parseType', awPopOver: "

Variables defined here apply to all child groups and hosts.

" + "

Enter variables using either JSON or YAML syntax. Use the " + "radio button to toggle between the two.

" + @@ -158,7 +159,7 @@ export default addRequired: false, editRequired: false, rows: 6, - 'default': null, + 'default': '---', parseTypeName: 'envParseType', dataTitle: "Environment Variables", dataPlacement: 'right', @@ -181,7 +182,7 @@ export default addRequired: false, editRequird: false, rows: 6, - 'default': null, + 'default': '---', parseTypeName: 'envParseType', dataTitle: "Source Variables", dataPlacement: 'right', @@ -207,7 +208,7 @@ export default class: 'Form-textAreaLabel Form-formGroup--fullWidth', editRequird: false, rows: 6, - 'default': null, + 'default': '---', parseTypeName: 'envParseType', dataTitle: "Source Variables", dataPlacement: 'right', @@ -233,7 +234,7 @@ export default class: 'Form-textAreaLabel Form-formGroup--fullWidth', editRequird: false, rows: 6, - 'default': null, + 'default': '---', parseTypeName: 'envParseType', dataTitle: "Source Variables", dataPlacement: 'right', diff --git a/awx/ui/client/src/helpers/Parse.js b/awx/ui/client/src/helpers/Parse.js index a421b96c0b..15db47f0d5 100644 --- a/awx/ui/client/src/helpers/Parse.js +++ b/awx/ui/client/src/helpers/Parse.js @@ -27,51 +27,39 @@ export default onReady = params.onReady, onChange = params.onChange; - function removeField() { + function removeField(fld) { //set our model to the last change in CodeMirror and then destroy CodeMirror - scope[fld] = scope.codeMirror.getValue(); - - // codeMirror.destroy looks for anything with a CodeMirror class and destroys it, so if there are multiple codeMirror editor instances, it will delete them all, - // // which was the case if launching a job from the job template form. I had to add a check to see if there were multiple instances and only remove the second one found on the modal. - // if( $(".CodeMirror").length >1) { - // var self = scope.codeMirror; - // $('.CodeMirror:eq(1)').empty().remove(); - // if (self.element) { - // self.element.show(); - // } - // } - // else - scope.codeMirror.destroy(); + scope[fld] = scope[fld + 'codeMirror'].getValue(); + $('#cm-' + fld + '-container > .CodeMirror').empty().remove(); } - function createField(onChange, onReady) { + function createField(onChange, onReady, fld) { //hide the textarea and show a fresh CodeMirror with the current mode (json or yaml) - $rootScope.loginConfig.promise.then(function () { - scope.codeMirror = AngularCodeMirror(); - scope.codeMirror.addModes($AnsibleConfig.variable_edit_modes); - scope.codeMirror.showTextArea({ - scope: scope, - model: fld, - element: field_id, - lineNumbers: true, - mode: scope[pfld], - onReady: onReady, - onChange: onChange - }); + + scope[fld + 'codeMirror'] = AngularCodeMirror(); + scope[fld + 'codeMirror'].addModes($AnsibleConfig.variable_edit_modes); + scope[fld + 'codeMirror'].showTextArea({ + scope: scope, + model: fld, + element: field_id, + lineNumbers: true, + mode: scope[pfld], + onReady: onReady, + onChange: onChange }); } // Hide the textarea and show a CodeMirror editor - createField(onChange, onReady); + createField(onChange, onReady, fld); // Toggle displayed variable string between JSON and YAML - scope.parseTypeChange = function() { + scope.parseTypeChange = function(model, fld) { var json_obj; - if (scope[pfld] === 'json') { + if (scope[model] === 'json') { // converting yaml to json try { - removeField(); + removeField(fld); json_obj = jsyaml.load(scope[fld]); if ($.isEmptyObject(json_obj)) { scope[fld] = "{}"; @@ -79,17 +67,17 @@ export default else { scope[fld] = JSON.stringify(json_obj, null, " "); } - createField(); + createField(onReady, onChange, fld); } catch (e) { Alert('Parse Error', 'Failed to parse valid YAML. ' + e.message); - setTimeout( function() { scope.$apply( function() { scope[pfld] = 'yaml'; createField(); }); }, 500); + setTimeout( function() { scope.$apply( function() { scope[model] = 'yaml'; createField(); }); }, 500); } } else { // convert json to yaml try { - removeField(); + removeField(fld); json_obj = JSON.parse(scope[fld]); if ($.isEmptyObject(json_obj)) { scope[fld] = '---'; @@ -97,11 +85,11 @@ export default else { scope[fld] = jsyaml.safeDump(json_obj); } - createField(); + createField(onReady, onChange, fld); } catch (e) { Alert('Parse Error', 'Failed to parse valid JSON. ' + e.message); - setTimeout( function() { scope.$apply( function() { scope[pfld] = 'json'; createField(); }); }, 500 ); + setTimeout( function() { scope.$apply( function() { scope[model] = 'json'; createField(); }); }, 500 ); } } }; 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 b435709351..2c476c7d26 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 @@ -28,7 +28,7 @@ // inventory_source fields params = { instance_filters: $scope.instance_filters, - source_vars: $scope[$scope.source.value + '_variables'] === null ? null : $scope[$scope.source.value + '_variables'], + source_vars: $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables'], source_script: $scope.inventory_script, source: $scope.source.value, credential: $scope.credential, @@ -114,6 +114,14 @@ input_type: "radio" }); } + if (source === 'ec2' || source === 'custom' || source === 'vmware' || source === 'openstack'){ + ParseTypeChange({ + scope: $scope, + field_id: source + '_variables', + variable: source + '_variables', + parse_variable: 'envParseType' + }); + } // reset fields $scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null; // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint @@ -203,7 +211,7 @@ }); var init = function(){ $scope.parseType = 'yaml'; - $scope.variables = '---'; + $scope.envParseType = 'yaml'; generator.inject(form, {mode: 'add', related: false, id: 'Inventory-groupManage--panel', scope: $scope}); ParseTypeChange({ scope: $scope, 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 66fddb124d..9f1fe28ee4 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 @@ -5,10 +5,10 @@ *************************************************/ export default - ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ToggleNotification', + ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ToggleNotification', 'ParseVariableString', 'ParseTypeChange', 'GenerateForm', 'LookUpInit', 'RelatedSearchInit', 'RelatedPaginateInit', 'NotificationsListInit', 'GroupManageService','GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'groupData', 'inventorySourceData', - function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ToggleNotification, + function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ToggleNotification, ParseVariableString, ParseTypeChange, GenerateForm, LookUpInit, RelatedSearchInit, RelatedPaginateInit, NotificationsListInit, GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, groupData, inventorySourceData){ var generator = GenerateForm, @@ -41,7 +41,7 @@ group_by: _.map($scope.group_by, 'value').join(','), source_regions: _.map($scope.source_regions, 'value').join(','), instance_filters: $scope.instance_filters, - source_vars: $scope[$scope.source.value + '_variables'] === '' ? null : $scope[$scope.source.value + '_variables'] + source_vars: $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables'] }; source = $scope.source.value; } @@ -112,6 +112,16 @@ input_type: "radio" }); } + if (source.value === 'ec2' || source.value === 'custom' || + source.value === 'vmware' || source.value === 'openstack'){ + $scope[source.value + '_variables'] = $scope[source.value + '_variables'] === null ? '---' : $scope[source.value + '_variables']; + ParseTypeChange({ + scope: $scope, + field_id: source.value + '_variables', + variable: source.value + '_variables', + parse_variable: 'envParseType', + }); + } // reset fields // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint $scope.source_region_choices = source.value === 'azure_rm' ? $scope.azure_regions : $scope[source.value + '_regions']; @@ -122,6 +132,7 @@ $scope.credential_name = null; initRegionSelect(); }; + var initRegionSelect = function(){ CreateSelect2({ element: '#group_source_regions', @@ -138,6 +149,19 @@ element: '#group_source', multiple: false }); + // After the source is set, conditional fields will be visible + // CodeMirror is buggy if you instantiate it in a not-visible element + // So we initialize it here instead of the init() routine + if(inventorySourceData.source === 'ec2' || inventorySourceData.source === 'openstack' || + inventorySourceData.source === 'custom' || inventorySourceData.source === 'vmware'){ + $scope[inventorySourceData.source + '_variables'] = inventorySourceData.source_vars === null || inventorySourceData.source_vars === '' ? '---' : ParseVariableString(inventorySourceData.source_vars); + ParseTypeChange({ + scope: $scope, + field_id: inventorySourceData.source + '_variables', + variable: inventorySourceData.source + '_variables', + parse_variable: 'envParseType', + }); + } }; var initRegionData = function(){ var source = $scope.source.value === 'azure_rm' ? 'azure' : $scope.source.value; @@ -230,15 +254,11 @@ {instance_filters: inventorySourceData.instance_filters}, {inventory_script: inventorySourceData.source_script} ); - if(inventorySourceData.source === ('ec2' || 'openstack' || 'custom' || 'vmware')){ - $scope[inventorySourceData.source + '_variables'] = inventorySourceData.source_vars; - } if (inventorySourceData.credential){ GroupManageService.getCredential(inventorySourceData.credential).then(res => $scope.credential_name = res.data.name); } $scope = angular.extend($scope, groupData); - $scope.variables = $scope.variables === (null || '') ? '---' : $scope.variables; - $scope.parseType = 'yaml'; + // instantiate lookup fields if (inventorySourceData.source !== 'custom'){ LookUpInit({ @@ -272,11 +292,17 @@ input_type: "radio" }); } + // init codemirror(s) + $scope.variables = $scope.variables === null || $scope.variables === '' ? '---' : ParseVariableString($scope.variables); + $scope.parseType = 'yaml'; + $scope.envParseType = 'yaml'; + ParseTypeChange({ scope: $scope, field_id: 'group_variables', variable: 'variables', }); + NotificationsListInit({ scope: $scope, url: GetBasePath('inventory_sources'), diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index f0ce685e8b..9e14e28d32 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -729,14 +729,14 @@ 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" || fld === 'inventory_variables' || fld === 'source_vars') { + if (fld === "variables" || fld === "extra_vars" || _.last(fld.split('_')) === 'variables' || fld === 'source_vars') { html += "
" + " YAML\n"; + html += "\" value=\"yaml\" ng-change=\"parseTypeChange('" + ((field.parseTypeName) ? field.parseTypeName : 'parseType') + "', '" + fld + "'" + ")\"> YAML\n"; html += " JSON\n"; + html += "\" value=\"json\" ng-change=\"parseTypeChange('" + ((field.parseTypeName) ? field.parseTypeName : 'parseType')+ "', '" + fld + "'" + ")\"> JSON\n"; html += "
\n"; }