From 55b574fa26258d35a783cc395472d8be8b0b6226 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Tue, 18 Feb 2014 03:50:36 -0500 Subject: [PATCH] AC-564 added new method to codemirror wrapper to enable replacing textarea fields with an editor. Implemented in inventory, groups, hosts, and templates. Solved issues with groups related to two potential textareas at the same time. Found and fixed an error in the way ReturnToCaller() utility was being called. Finished implementing angular-md5. Adding or saving a job template now shows a pop-up on save when a callback is enabled. The pop-up shows the callback url and host key. Before user had to save and then re-open the template to get the URL. With the pop-up we're now showing it immmediately on save. --- awx/ui/static/js/controllers/Inventories.js | 7 +- awx/ui/static/js/controllers/JobTemplates.js | 96 +++++++++++++------ awx/ui/static/js/controllers/Projects.js | 5 +- awx/ui/static/js/controllers/Teams.js | 4 +- awx/ui/static/js/controllers/Users.js | 8 +- awx/ui/static/js/helpers/Groups.js | 81 ++++++++++++---- awx/ui/static/js/helpers/Hosts.js | 19 ++-- awx/ui/static/js/helpers/Parse.js | 58 ++++++----- awx/ui/static/js/helpers/inventory.js | 32 +++---- awx/ui/static/js/helpers/md5.js | 6 +- awx/ui/static/less/codemirror.less | 3 + .../static/lib/angular-codemirror/.bower.json | 5 +- .../lib/AngularCodeMirror.js | 92 ++++++++++++++++-- awx/ui/static/lib/ansible/Utilities.js | 17 ++-- awx/ui/static/lib/ansible/form-generator.js | 2 - 15 files changed, 309 insertions(+), 126 deletions(-) diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 32380a0bf9..500591135b 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -236,11 +236,12 @@ function InventoriesAdd($scope, $rootScope, $compile, $location, $log, $routePar generator.inject(form, { mode: 'add', related: false, scope: $scope }); - $scope.inventoryParseType = 'yaml'; - generator.reset(); LoadBreadCrumbs(); - ParseTypeChange( $scope, 'inventory_variables', 'inventoryParseType'); + + $scope.inventoryParseType = 'yaml'; + ParseTypeChange({ scope: $scope, variable: 'inventory_variables', parse_variable: 'inventoryParseType', + field_id: 'inventory_inventory_variables' }); LookUpInit({ scope: $scope, diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index c2b807e8a5..5d1d417dec 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -118,12 +118,18 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa generator = GenerateForm, master = {}, CloudCredentialList = {}, - selectPlaybook, checkSCMStatus; + selectPlaybook, checkSCMStatus, + callback; generator.inject(form, { mode: 'add', related: false, scope: $scope }); + callback = function() { + // Make sure the form controller knows there was a change + $scope[form.name + '_form'].setDirty(); + }; + $scope.parseType = 'yaml'; - ParseTypeChange($scope); + ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback }); $scope.job_type_options = [ { value: 'run', label: 'Run' }, @@ -131,9 +137,9 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa ]; $scope.verbosity_options = [ - { value: '0', label: 'Default' }, - { value: '1', label: 'Verbose' }, - { value: '3', label: 'Debug' } + { value: 0, label: 'Default' }, + { value: 1, label: 'Verbose' }, + { value: 3, label: 'Debug' } ]; $scope.playbook_options = []; @@ -258,6 +264,24 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa field: 'project' }); + function saveCompleted() { + setTimeout(function() { $scope.$apply(function() { $location.path('/job_templates'); }); }, 500); + } + + if ($scope.removeTemplateSaveSuccess) { + $scope.removeTemplateSaveSuccess(); + } + $scope.removeTemplateSaveSuccess = $scope.$on('templateSaveSuccess', function(e, data) { + Wait('stop'); + if (data.related && data.related.callback) { + Alert('Callback URL', '

Host callbacks are enabled for this template. The callback URL is: ' + data.related.callback + + '

The host configuration key is: ' + data.host_config_key + '

', 'alert-info', saveCompleted); + } + else { + saveCompleted(); + } + }); + // Save $scope.formSave = function () { generator.clearApiErrors(); @@ -293,16 +317,10 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa Rest.setUrl(defaultUrl); Rest.post(data) - .success(function () { - Wait('stop'); - var base = $location.path().replace(/^\//, '').split('/')[0]; - if (base === 'job_templates') { - ReturnToCaller(); - } - ReturnToCaller(1); + .success(function(data) { + $scope.$emit('templateSaveSuccess', data); }) .error(function (data, status) { - Wait('stop'); ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new job template. POST returned status: ' + status }); @@ -347,13 +365,12 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP master = {}, id = $routeParams.id, relatedSets = {}, - checkSCMStatus, getPlaybooks; + checkSCMStatus, getPlaybooks, callback; generator.inject(form, { mode: 'edit', related: true, scope: $scope }); $scope.parseType = 'yaml'; - ParseTypeChange($scope); - + // Our job type options $scope.job_type_options = [ { value: 'run', label: 'Run' }, @@ -361,10 +378,15 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP ]; $scope.verbosity_options = [ - { value: '0', label: 'Default' }, - { value: '1', label: 'Verbose' }, - { value: '3', label: 'Debug' } + { value: 0, label: 'Default' }, + { value: 1, label: 'Verbose' }, + { value: 3, label: 'Debug' } ]; + + callback = function() { + // Make sure the form controller knows there was a change + $scope[form.name + '_form'].$setDirty(); + }; $scope.playbook_options = null; $scope.playbook = null; @@ -524,6 +546,8 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP default_val: dft }); + ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback }); + if (related_cloud_credential) { Rest.setUrl(related_cloud_credential); Rest.get() @@ -642,6 +666,24 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP }); }); + function saveCompleted() { + setTimeout(function() { $scope.$apply(function() { $location.path('/job_templates'); }); }, 500); + } + + if ($scope.removeTemplateSaveSuccess) { + $scope.removeTemplateSaveSuccess(); + } + $scope.removeTemplateSaveSuccess = $scope.$on('templateSaveSuccess', function(e, data) { + Wait('stop'); + if (Empty(master.callback_url) && data.related && data.related.callback) { + Alert('Callback URL', '

Host callbacks are enabled for this template. The callback URL is: ' + data.related.callback + + '

The host configuration key is: ' + data.host_config_key + '

', 'alert-info', saveCompleted); + } + else { + saveCompleted(); + } + }); + // Save changes to the parent $scope.formSave = function () { generator.clearApiErrors(); @@ -677,19 +719,12 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP Rest.setUrl(defaultUrl + id + '/'); Rest.put(data) - .success(function () { - Wait('stop'); - var base = $location.path().replace(/^\//, '').split('/')[0]; - if (base === 'job_templates') { - ReturnToCaller(); - } - ReturnToCaller(1); + .success(function (data) { + $scope.$emit('templateSaveSuccess', data); }) .error(function (data, status) { - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to update job template. PUT returned status: ' + status - }); + ProcessErrors($scope, data, status, form, { hdr: 'Error!', + msg: 'Failed to update job template. PUT returned status: ' + status }); }); } catch (err) { @@ -711,6 +746,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP $scope[fld] = master[fld]; } $scope.parseType = 'yaml'; + ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback }); $('#forks-slider').slider("option", "value", $scope.forks); }; diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index 31509867c1..c474bf70fb 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -444,10 +444,11 @@ function ProjectsAdd($scope, $rootScope, $compile, $location, $log, $routeParams if (base === 'projects') { ReturnToCaller(); } - ReturnToCaller(1); + else { + ReturnToCaller(1); + } }) .error(function (data, status) { - Wait('stop'); ProcessErrors($scope, data, status, ProjectsForm, { hdr: 'Error!', msg: 'Failed to add organization to project. POST returned status: ' + status }); }); diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index fe897dd8ef..01353d9a8c 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -285,7 +285,9 @@ function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, if (base === 'teams') { ReturnToCaller(); } - ReturnToCaller(1); + else { + ReturnToCaller(1); + } }) .error(function (data, status) { Wait('stop'); diff --git a/awx/ui/static/js/controllers/Users.js b/awx/ui/static/js/controllers/Users.js index 0680fae82e..33326753a6 100644 --- a/awx/ui/static/js/controllers/Users.js +++ b/awx/ui/static/js/controllers/Users.js @@ -166,7 +166,9 @@ function UsersAdd($scope, $rootScope, $compile, $location, $log, $routeParams, U $rootScope.flashMessage = 'New user successfully created!'; $location.path('/users/' + data.id); } - ReturnToCaller(1); + else { + ReturnToCaller(1); + } }) .error(function (data, status) { ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new user. POST returned status: ' + status }); @@ -307,7 +309,9 @@ function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeParams, if (base === 'users') { ReturnToCaller(); } - ReturnToCaller(1); + else { + ReturnToCaller(1); + } }) .error(function (data, status) { ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update users: ' + $routeParams.id + diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index b0cf82a2c3..1c358ac180 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -178,13 +178,13 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G } ]) -.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', - function (GetBasePath, CredentialList, LookUpInit, Empty) { +.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', 'Wait', 'ParseTypeChange', + function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, ParseTypeChange) { return function (params) { var scope = params.scope, form = params.form, - kind, url; + kind, url, callback; if (!Empty(scope.source)) { if (scope.source.value === 'file') { @@ -220,6 +220,13 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G list: CredentialList, field: 'credential' }); + + if ($('#group_tabs .active a').text() === 'Source' && scope.source.value === 'ec2') { + callback = function(){ Wait('stop'); }; + Wait('start'); + ParseTypeChange({ scope: scope, variable: 'source_vars', parse_variable: form.fields.source_vars.parseTypeName, + field_id: 'group_source_vars', onReady: callback }); + } } } }; @@ -314,10 +321,10 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G ]) .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'Wait', 'GetChoices', + 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'Wait', 'GetChoices', 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', 'WatchInventoryWindowResize', function ($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, ParseTypeChange, GroupsEdit, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree, + GetBasePath, ParseTypeChange, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree, SourceChange, WatchInventoryWindowResize) { return function (params) { @@ -333,12 +340,28 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G scope.formModalActionLabel = 'Save'; scope.formModalCancelShow = true; - scope.parseType = 'yaml'; scope.source = null; - ParseTypeChange(scope); - generator.reset(); + scope[form.fields.source_vars.parseTypeName] = 'yaml'; + scope.parseType = 'yaml'; + ParseTypeChange({ scope: scope, field_id: 'group_variables' }); + + $('#group_tabs a[data-toggle="tab"]').on('show.bs.tab', function (e) { + var callback = function(){ Wait('stop'); }; + if ($(e.target).text() === 'Properties') { + Wait('start'); + ParseTypeChange({ scope: scope, field_id: 'group_variables', onReady: callback }); + } + else { + if (scope.source && scope.source.value === 'ec2') { + Wait('start'); + ParseTypeChange({ scope: scope, variable: 'source_vars', parse_variable: form.fields.source_vars.parseTypeName, + field_id: 'group_source_vars', onReady: callback }); + } + } + }); + if (scope.removeAddTreeRefreshed) { scope.removeAddTreeRefreshed(); } @@ -586,12 +609,33 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G scope.formModalHeader = 'Group'; scope.formModalCancelShow = true; scope.source = form.fields.source['default']; - scope.parseType = 'yaml'; - scope[form.fields.source_vars.parseTypeName] = 'yaml'; scope.sourcePathRequired = false; - ParseTypeChange(scope); - ParseTypeChange(scope, 'source_vars', form.fields.source_vars.parseTypeName); + $('#group_tabs a[data-toggle="tab"]').on('show.bs.tab', function (e) { + var callback = function(){ Wait('stop'); }; + if ($(e.target).text() === 'Properties') { + Wait('start'); + ParseTypeChange({ scope: scope, field_id: 'group_variables', onReady: callback }); + } + else { + if (scope.source && scope.source.value === 'ec2') { + Wait('start'); + ParseTypeChange({ scope: scope, variable: 'source_vars', parse_variable: form.fields.source_vars.parseTypeName, + field_id: 'group_source_vars', onReady: callback }); + } + } + }); + + scope[form.fields.source_vars.parseTypeName] = 'yaml'; + scope.parseType = 'yaml'; + + if (scope.groupVariablesLoadedRemove) { + scope.groupVariablesLoadedRemove(); + } + scope.groupVariablesLoadedRemove = scope.$on('groupVariablesLoaded', function () { + var callback = function() { Wait('stop'); }; + ParseTypeChange({ scope: scope, field_id: 'group_variables', onReady: callback }); + }); // After the group record is loaded, retrieve related data if (scope.groupLoadedRemove) { @@ -608,18 +652,16 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G } else { scope.variables = jsyaml.safeDump(data); } - Wait('stop'); + scope.$emit('groupVariablesLoaded'); }) .error(function (data, status) { scope.variables = null; - Wait('stop'); - ProcessErrors(scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to retrieve group variables. GET returned status: ' + status - }); + ProcessErrors(scope, data, status, form, { hdr: 'Error!', + msg: 'Failed to retrieve group variables. GET returned status: ' + status }); }); } else { scope.variables = "---"; + scope.$emit('groupVariablesLoaded'); } master.variables = scope.variables; @@ -714,11 +756,10 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G }]; } scope.group_update_url = data.related.update; - Wait('stop'); + //Wait('stop'); }) .error(function (data, status) { scope.source = ""; - Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to retrieve inventory source. GET status: ' + status }); }); diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index 104bd7e0ec..eb0c5587bc 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -336,8 +336,9 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener scope.formModalActionLabel = 'Save'; scope.formModalHeader = 'Create New Host'; scope.formModalCancelShow = true; + scope.parseType = 'yaml'; - ParseTypeChange(scope); + ParseTypeChange({ scope: scope, field_id: 'host_variables' }); if (scope.removeHostsReload) { scope.removeHostsReload(); @@ -466,7 +467,15 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener scope.formModalHeader = 'Host Properties'; scope.formModalCancelShow = true; scope.parseType = 'yaml'; - ParseTypeChange(scope); + + if (scope.hostVariablesLoadedRemove) { + scope.hostVariablesLoadedRemove(); + } + scope.hostVariablesLoadedRemove = scope.$on('hostVariablesLoaded', function() { + var callback = function() { Wait('stop'); }; + $('#form-modal').modal('show'); + ParseTypeChange({ scope: scope, field_id: 'host_variables', onReady: callback }); + }); if (scope.hostLoadedRemove) { scope.hostLoadedRemove(); @@ -483,8 +492,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener else { scope.variables = jsyaml.safeDump(data); } - Wait('stop'); - $('#form-modal').modal('show'); + scope.$emit('hostVariablesLoaded'); }) .error( function(data, status) { scope.variables = null; @@ -494,8 +502,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener } else { scope.variables = "---"; - Wait('stop'); - $('#form-modal').modal('show'); + scope.$emit('hostVariablesLoaded'); } master.variables = scope.variables; }); diff --git a/awx/ui/static/js/helpers/Parse.js b/awx/ui/static/js/helpers/Parse.js index a5cd1ecb39..432de6d8b0 100644 --- a/awx/ui/static/js/helpers/Parse.js +++ b/awx/ui/static/js/helpers/Parse.js @@ -3,8 +3,8 @@ * * ParseHelper * - * Routines for parsing variable data and toggling - * between JSON and YAML. + * Show the CodeMirror variable editor and allow + * toggle between JSON and YAML * */ @@ -12,32 +12,41 @@ angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule']) .factory('ParseTypeChange', ['Alert', 'AngularCodeMirror', function (Alert, AngularCodeMirror) { - return function (scope, varName, parseTypeName) { + return function (params) { + + var scope = params.scope, + field_id = params.field_id, + fld = (params.variable) ? params.variable : 'variables', + pfld = (params.parse_variable) ? params.parse_variable : 'parseType', + onReady = params.onReady, + onChange = params.onChange, + codeMirror; + + function removeField() { + //set our model to the last change in CodeMirror and then destroy CodeMirror + scope[fld] = codeMirror.getValue(); + codeMirror.destroy(); + } + + function createField(onChange, onReady) { + //hide the textarea and show a fresh CodeMirror with the current mode (json or yaml) + codeMirror = AngularCodeMirror(); + codeMirror.addModes($AnsibleConfig.variable_edit_modes); + codeMirror.showTextArea({ scope: scope, model: fld, element: field_id, mode: scope[pfld], onReady: onReady, onChange: onChange }); + } + + + // Hide the textarea and show a CodeMirror editor + createField(onChange, onReady); + // Toggle displayed variable string between JSON and YAML - - var fld = (varName) ? varName : 'variables', - pfld = (parseTypeName) ? parseTypeName : 'parseType', - codeMirror = AngularCodeMirror(); - codeMirror.addModes($AnsibleConfig.variable_edit_modes); - - scope.showCodeEditor = function() { - var title = 'Edit ' + scope[pfld].toUpperCase(), - container = document.getElementById('main-view'); - codeMirror.show({ - scope: scope, - container: container, - mode: scope[pfld], - model: fld, - title: title - }); - }; - scope.parseTypeChange = function() { var json_obj; if (scope[pfld] === 'json') { // converting yaml to json try { + removeField(); json_obj = jsyaml.load(scope[fld]); if ($.isEmptyObject(json_obj)) { scope[fld] = "{}"; @@ -45,15 +54,17 @@ angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule']) else { scope[fld] = JSON.stringify(json_obj, null, " "); } + createField(); } catch (e) { Alert('Parse Error', 'Failed to parse valid YAML. ' + e.message); - setTimeout( function() { scope.$apply( function() { scope[pfld] = 'yaml'; }); }, 500); + setTimeout( function() { scope.$apply( function() { scope[pfld] = 'yaml'; createField(); }); }, 500); } } else { // convert json to yaml try { + removeField(); json_obj = JSON.parse(scope[fld]); if ($.isEmptyObject(json_obj)) { scope[fld] = '---'; @@ -61,10 +72,11 @@ angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule']) else { scope[fld] = jsyaml.safeDump(json_obj); } + createField(); } catch (e) { Alert('Parse Error', 'Failed to parse valid JSON. ' + e.message); - setTimeout( function() { scope.$apply( function() { scope[pfld] = 'json'; }); }, 500 ); + setTimeout( function() { scope.$apply( function() { scope[pfld] = 'json'; createField(); }); }, 500 ); } } }; diff --git a/awx/ui/static/js/helpers/inventory.js b/awx/ui/static/js/helpers/inventory.js index 7ec7ef82d7..0a9f618a55 100644 --- a/awx/ui/static/js/helpers/inventory.js +++ b/awx/ui/static/js/helpers/inventory.js @@ -8,8 +8,6 @@ * */ - /* globals console:false */ - 'use strict'; angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService', @@ -131,8 +129,14 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis /* Reset form properties. Otherwise it screws up future requests of the Inventories detail page */ form.well = true; - ParseTypeChange(scope, 'inventory_variables', 'inventoryParseType'); - scope.inventoryParseType = 'yaml'; + scope.$on('inventoryPropertiesLoaded', function() { + var callback = function() { Wait('stop'); }; + $('#form-modal').modal('show'); + scope.inventoryParseType = 'yaml'; + ParseTypeChange({ scope: scope, variable: 'inventory_variables', parse_variable: 'inventoryParseType', + field_id: 'inventory_inventory_variables', onReady: callback }); + }); + scope.formModalActionLabel = 'Save'; scope.formModalCancelShow = true; scope.formModalInfo = false; @@ -156,11 +160,6 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis } catch (err) { Alert('Variable Parse Error', 'Attempted to parse variables for inventory: ' + inventory_id + '. Parse returned: ' + err); - if (console) { - console.log(err); - console.log('data:'); - console.log(data.variables); - } scope.inventory_variables = '---'; } } @@ -193,15 +192,12 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis field: 'organization' }); - Wait('stop'); - $('#form-modal').modal('show'); + scope.$emit('inventoryPropertiesLoaded'); }) .error(function (data, status) { - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status - }); + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); }); if (scope.removeInventorySaved) { @@ -242,11 +238,9 @@ angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationLis }; scope.formModalAction = function () { - parent_scope.inventory_id = inventory_id; + scope.inventory_id = inventory_id; parent_scope.inventory_name = scope.inventory_name; - SaveInventory({ - scope: scope - }); + SaveInventory({ scope: scope }); }; }; diff --git a/awx/ui/static/js/helpers/md5.js b/awx/ui/static/js/helpers/md5.js index 8939039ace..b200449f32 100644 --- a/awx/ui/static/js/helpers/md5.js +++ b/awx/ui/static/js/helpers/md5.js @@ -10,8 +10,8 @@ 'use strict'; -angular.module('md5Helper', ['RestServices', 'Utilities']) - .factory('md5Setup', [ function () { +angular.module('md5Helper', ['RestServices', 'Utilities', 'angular-md5']) + .factory('md5Setup', ['md5', function (md5) { return function (params) { var scope = params.scope, @@ -24,7 +24,7 @@ angular.module('md5Helper', ['RestServices', 'Utilities']) scope.genMD5 = function (fld) { var now = new Date(); - scope[fld] = $.md5('AnsibleWorks' + now.getTime()); + scope[fld] = md5.createHash('AnsibleWorks' + now.getTime()); }; scope.toggleCallback = function (fld) { diff --git a/awx/ui/static/less/codemirror.less b/awx/ui/static/less/codemirror.less index 9c01f50542..785d50d4a9 100644 --- a/awx/ui/static/less/codemirror.less +++ b/awx/ui/static/less/codemirror.less @@ -8,6 +8,9 @@ .CodeMirror { height: auto; + overflow-x: scroll; + overflow-y: hidden; + border: 1px solid #ccc; } .CodeMirror-activeline-background { diff --git a/awx/ui/static/lib/angular-codemirror/.bower.json b/awx/ui/static/lib/angular-codemirror/.bower.json index f00a892537..194a61b513 100644 --- a/awx/ui/static/lib/angular-codemirror/.bower.json +++ b/awx/ui/static/lib/angular-codemirror/.bower.json @@ -20,6 +20,7 @@ "commit": "94b7aac548b036f4fbd94e56129ed9574e472616" }, "_source": "git://github.com/chouseknecht/angular-codemirror.git", - "_target": "~1.0.0", - "_originalSource": "angular-codemirror" + "_target": "~1.0.2", + "_originalSource": "angular-codemirror", + "_direct": true } \ No newline at end of file diff --git a/awx/ui/static/lib/angular-codemirror/lib/AngularCodeMirror.js b/awx/ui/static/lib/angular-codemirror/lib/AngularCodeMirror.js index 557eca31ee..e4b9e5e02e 100644 --- a/awx/ui/static/lib/angular-codemirror/lib/AngularCodeMirror.js +++ b/awx/ui/static/lib/angular-codemirror/lib/AngularCodeMirror.js @@ -32,16 +32,94 @@ angular.module('AngularCodeMirrorModule', []) .factory('AngularCodeMirror', [ function() { return function() { var fn = function() { - - this.show = function(params) { + + this.myCodeMirror = null; + this.element = null; + + this.showTextArea = function(params) { + var self = this, + element = (typeof params.element === "object") ? params.element : document.getElementById(params.element), + scope = params.scope, + model = params.model, + mode = params.mode, + onReady = params.onReady, + onChange = params.onChange, + height = 0; + + self.element = $(element); - var scope = params.scope, + // We don't want to touch the original textarea. Angular likely has a model and other listeners + // attached to it. In prior iterations attaching CodeMirror to it seemed to go bad, so we'll insert a + //
under it, hide the textarea and let CodeMirror attach to the
. + if ($('#cm-' + model + '-container').length > 0) { + $('#cm-' + model + '-container').empty(); + } + else { + self.element.after("
"); + } + + // Calc the height of the text area- our CodeMirror should match. + height += self.element.attr('rows') * parseInt($(self.element).css('line-height').replace(/px/,''),10); + height += parseInt(self.element.css('padding-top').replace(/px|%/,''),10) + + parseInt(self.element.css('padding-bottom').replace(/px|%/,''),10); + height += 2; //for the border + + // hide + self.element.hide(); + + // Initialize CodeMirror + self.modes[mode].value = scope[model]; + self.myCodeMirror = CodeMirror(document.getElementById('cm-' + model + '-container'), self.modes[mode]); + + // Adjust the height + $('.CodeMirror').css({ 'min-height': height, 'max-height': height }); + self.myCodeMirror.setSize(null, height); + + // This doesn't work without a setTimeout + setTimeout(function() { + self.myCodeMirror.refresh(); + if (onReady) { + onReady(); + } + }, 500); + + // Update the model on change + self.myCodeMirror.on('change', function() { + setTimeout(function() { + scope.$apply(function(){ + scope[model] = self.myCodeMirror.getValue(); + if (onChange) { + onChange(); + } + }); + }, 500); + }); + }; + + this.getValue = function() { + var self = this; + return self.myCodeMirror.getValue(); + }; + + this.destroy = function() { + // Intended for use with showTextArea. This will get ride of CM and put the + // textarea back to normal + var self = this; + $('.CodeMirror').empty().remove(); + if (self.element) { + self.element.show(); + } + }; + + this.showModal = function(params) { + + var self = this, + scope = params.scope, target = (typeof params.container === "string") ? document.getElementById(params.container) : params.container, mode = params.mode, model = params.model, title = params.title || 'Code Editor', - modes = this.modes, - myCodeMirror; + modes = self.modes; this.html = "
\n
\n"; if ($('#af-code-editor-modal').length === 0) { @@ -66,7 +144,7 @@ angular.module('AngularCodeMirrorModule', []) { text: "Cancel", id: "af-code-edit-cancel", click: function() { $(this).dialog('close'); } }, { text: "OK", id: "af-code-edit-ok", click: function() { - scope.$apply(function() { scope[model] = myCodeMirror.getValue(); }); + scope.$apply(function() { scope[model] = self.myCodeMirror.getValue(); }); $(this).dialog('close'); } } @@ -87,7 +165,7 @@ angular.module('AngularCodeMirrorModule', []) // initialize CodeMirror options = modes[mode]; options.value = scope[model]; - myCodeMirror = CodeMirror(document.getElementById('af-code'), options); + self.myCodeMirror = CodeMirror(document.getElementById('af-code'), options); } }); }; diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index f805c17c7a..c05b81c5b9 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -85,7 +85,7 @@ angular.module('Utilities', ['RestServices', 'Utilities']) }); $rootScope.disableButtons2 = (disableButtons) ? true : false; if (action) { - $('#alert-modal2').on('hidden', function () { + $('#alert-modal2').on('hidden.bs.modal', function () { action(); }); } @@ -107,6 +107,14 @@ angular.module('Utilities', ['RestServices', 'Utilities']) backdrop: 'static' }); + $('#alert-modal').on('hidden.bs.modal', function () { + console.log('modal closed'); + if (action) { + console.log('attempting callback'); + action(); + } + }); + $(document).bind('keydown', function (e) { if (e.keyCode === 27) { $('#alert-modal').modal('hide'); @@ -117,11 +125,7 @@ angular.module('Utilities', ['RestServices', 'Utilities']) }); $rootScope.disableButtons = (disableButtons) ? true : false; - if (action) { - $('#alert-modal').on('hidden', function () { - action(); - }); - } + } }; } @@ -630,6 +634,7 @@ angular.module('Utilities', ['RestServices', 'Utilities']) var form = params.form, scope = params.scope, fld; + console.log('here'); for (fld in form.fields) { if (scope[form.name + '_form'][fld]) { console.log(fld + ' valid: ' + scope[form.name + '_form'][fld].$valid); diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index e901fa6c42..a17f941303 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -688,8 +688,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) html += " JSON\n"; - html += " Editor"; html += "
\n"; }