From e61a966464f3ab11dc3509f43a7105cdcc1ddbf7 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Mon, 27 May 2013 13:18:15 -0400 Subject: [PATCH] Added tooltips to host/group/extra variables using TB popover. Fixed bugs on Inventory detail page causing js errors. Added JSON validation to extra_vars on Job Templates add/edit page. Always grey-out/disable fields on Jobs detail page because upading a Pending job throws a 405 error -will investigate with CC later. --- ansibleworks/ui/static/js/app.js | 1 - .../ui/static/js/controllers/Inventories.js | 7 -- .../ui/static/js/controllers/JobTemplates.js | 84 +++++++++++-------- ansibleworks/ui/static/js/controllers/Jobs.js | 8 +- ansibleworks/ui/static/js/forms/Groups.js | 9 +- ansibleworks/ui/static/js/forms/Hosts.js | 9 +- .../ui/static/js/forms/JobTemplates.js | 10 ++- .../ui/static/lib/ansible/directives.js | 40 ++++++++- .../ui/static/lib/ansible/form-generator.js | 18 +++- ansibleworks/ui/static/lib/ansible/tooltip.js | 23 ----- ansibleworks/ui/templates/ui/index.html | 1 - 11 files changed, 133 insertions(+), 77 deletions(-) delete mode 100644 ansibleworks/ui/static/lib/ansible/tooltip.js diff --git a/ansibleworks/ui/static/js/app.js b/ansibleworks/ui/static/js/app.js index 1873c7b574..fcc82e6a05 100644 --- a/ansibleworks/ui/static/js/app.js +++ b/ansibleworks/ui/static/js/app.js @@ -17,7 +17,6 @@ angular.module('ansible', [ 'OrganizationListDefinition', 'UserListDefinition', 'ListGenerator', - 'AWToolTip', 'PromptDialog', 'ApiLoader', 'RelatedSearchHelper', diff --git a/ansibleworks/ui/static/js/controllers/Inventories.js b/ansibleworks/ui/static/js/controllers/Inventories.js index b9988649af..46d44131d7 100644 --- a/ansibleworks/ui/static/js/controllers/Inventories.js +++ b/ansibleworks/ui/static/js/controllers/Inventories.js @@ -218,13 +218,6 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP var id = $routeParams.id; var relatedSets = {}; - // After inventory is loaded, retrieve each related set and any lookups - scope.$on('inventoryLoaded', function() { - for (var set in relatedSets) { - scope.search(relatedSets[set].iterator); - } - }); - // Retrieve detail record and prepopulate the form Rest.setUrl(defaultUrl + ':id/'); Rest.get({ params: {id: id} }) diff --git a/ansibleworks/ui/static/js/controllers/JobTemplates.js b/ansibleworks/ui/static/js/controllers/JobTemplates.js index 73ef50c12c..2687e9a90a 100644 --- a/ansibleworks/ui/static/js/controllers/JobTemplates.js +++ b/ansibleworks/ui/static/js/controllers/JobTemplates.js @@ -216,25 +216,32 @@ function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeP // Save scope.formSave = function() { - Rest.setUrl(defaultUrl); - var data = {} - for (var fld in form.fields) { - if (form.fields[fld].type == 'select' && fld != 'playbook') { - data[fld] = scope[fld].value; + try { + // Make sure we have valid JSON + var myjson = JSON.parse(scope.extra_vars); + Rest.setUrl(defaultUrl); + var data = {} + for (var fld in form.fields) { + if (form.fields[fld].type == 'select' && fld != 'playbook') { + data[fld] = scope[fld].value; + } + else { + data[fld] = scope[fld]; + } } - else { - data[fld] = scope[fld]; - } + Rest.post(data) + .success( function(data, status, headers, config) { + var base = $location.path().replace(/^\//,'').split('/')[0]; + (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to add new project. POST returned status: ' + status }); + }); + } + catch(err) { + Alert("Error", "Error parsing extra variables. Expecting valid JSON. Parser returned " + err); } - Rest.post(data) - .success( function(data, status, headers, config) { - var base = $location.path().replace(/^\//,'').split('/')[0]; - (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to add new project. POST returned status: ' + status }); - }); }; // Reset @@ -389,25 +396,32 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route // Save changes to the parent scope.formSave = function() { - Rest.setUrl(defaultUrl + $routeParams.id + '/'); - var data = {} - for (var fld in form.fields) { - if (form.fields[fld].type == 'select' && fld != 'playbook') { - data[fld] = scope[fld].value; - } - else { - data[fld] = scope[fld]; - } - } - Rest.put(data) - .success( function(data, status, headers, config) { - var base = $location.path().replace(/^\//,'').split('/')[0]; - (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.id + '. PUT status: ' + status }); + try { + // Make sure we have valid JSON + var myjson = JSON.parse(scope.extra_vars); + Rest.setUrl(defaultUrl + $routeParams.id + '/'); + var data = {} + for (var fld in form.fields) { + if (form.fields[fld].type == 'select' && fld != 'playbook') { + data[fld] = scope[fld].value; + } + else { + data[fld] = scope[fld]; + } + } + Rest.put(data) + .success( function(data, status, headers, config) { + var base = $location.path().replace(/^\//,'').split('/')[0]; + (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.id + '. PUT status: ' + status }); }); + } + catch(err) { + Alert("Error", "Error parsing extra variables. Expecting valid JSON. Parser returned " + err); + } }; // Cancel diff --git a/ansibleworks/ui/static/js/controllers/Jobs.js b/ansibleworks/ui/static/js/controllers/Jobs.js index 26561136db..90154869e5 100644 --- a/ansibleworks/ui/static/js/controllers/Jobs.js +++ b/ansibleworks/ui/static/js/controllers/Jobs.js @@ -227,11 +227,9 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, } } - if (data.status != 'new') { - $('input[type="text"], textarea').attr('readonly','readonly'); - $('select').prop('disabled', 'disabled'); - $('.lookup-btn').prop('disabled', 'disabled'); - } + $('input[type="text"], textarea').attr('readonly','readonly'); + $('select').prop('disabled', 'disabled'); + $('.lookup-btn').prop('disabled', 'disabled'); scope.url = data.url; var related = data.related; diff --git a/ansibleworks/ui/static/js/forms/Groups.js b/ansibleworks/ui/static/js/forms/Groups.js index 7acf50d6da..fb16f4b2a2 100644 --- a/ansibleworks/ui/static/js/forms/Groups.js +++ b/ansibleworks/ui/static/js/forms/Groups.js @@ -35,7 +35,14 @@ angular.module('GroupFormDefinition', []) editRequird: false, rows: 10, class: 'span12', - default: "\{\}" + default: "\{\}", + dataTitle: 'Group Variables', + dataPlacement: 'right', + awPopOver: '

Enter variables as JSON. Both the key and value must be wrapped in double quotes. ' + + 'Separate variables with commas, and wrap the entire string with { }. ' + + ' For example:

{"ntp_server": "ntp.example.com",
' + + '"proxy": "proxy.example.com"
}

See additional JSON examples at www.json.org

' } }, diff --git a/ansibleworks/ui/static/js/forms/Hosts.js b/ansibleworks/ui/static/js/forms/Hosts.js index da04137d47..0afb0651ce 100644 --- a/ansibleworks/ui/static/js/forms/Hosts.js +++ b/ansibleworks/ui/static/js/forms/Hosts.js @@ -40,7 +40,14 @@ angular.module('HostFormDefinition', []) editRequird: false, rows: 10, class: 'span12', - default: "\{\}" + default: "\{\}", + awPopOver: "

Enter variables as JSON. Both the key and value must be wrapped in double quotes. " + + "Separate variables with commas, and wrap the entire string with { }. " + + " For example:

{"ntp_server": "ntp.example.com",
" + + '"proxy": "proxy.example.com"
}

See additional JSON examples at www.json.org

', + dataTitle: 'Host Variables', + dataPlacement: 'right' } }, diff --git a/ansibleworks/ui/static/js/forms/JobTemplates.js b/ansibleworks/ui/static/js/forms/JobTemplates.js index 2391159324..77fc178946 100644 --- a/ansibleworks/ui/static/js/forms/JobTemplates.js +++ b/ansibleworks/ui/static/js/forms/JobTemplates.js @@ -115,7 +115,15 @@ angular.module('JobTemplateFormDefinition', []) class: 'span12', addRequired: false, editRequired: false, - column: 2 + default: "\{\}", + column: 2, + awPopOver: "

Enter variables as JSON. Both the key and value must be wrapped in double quotes. " + + "Separate variables with commas, and wrap the entire string with { }. " + + " For example:

{"ntp_server": "ntp.example.com",
" + + '"proxy": "proxy.example.com"
}

See additional JSON examples at www.json.org

', + dataTitle: 'Extra Variables', + dataPlacement: 'left' } }, diff --git a/ansibleworks/ui/static/lib/ansible/directives.js b/ansibleworks/ui/static/lib/ansible/directives.js index da22518830..e2abde484b 100644 --- a/ansibleworks/ui/static/lib/ansible/directives.js +++ b/ansibleworks/ui/static/lib/ansible/directives.js @@ -130,5 +130,43 @@ angular.module('AWDirectives', ['RestServices']) }) } } - }]); + }]) + + /* + * Enable TB tooltips. To add a tooltip to an element, include the following directive in + * the element's attributes: + * + * aw-tool-tip="<< tooltip text here >>" + * + * Include the standard TB data-XXX attributes to controll a tooltip's appearance. We will + * default placement to the left and delay to 2 seconds. + */ + .directive('awToolTip', function() { + return function(scope, element, attrs) { + var delay = (attrs.delay != undefined && attrs.delay != null) ? attrs.delay : $AnsibleConfig.tooltip_delay; + var placement = (attrs.placement != undefined && attrs.placement != null) ? attrs.placement : 'left'; + $(element).tooltip({ placement: placement, delay: delay, title: attrs.awToolTip }); + } + }) + + /* + * Enable TB pop-overs. To add a pop-over to an element, include the following directive in + * the element's attributes: + * + * aw-pop-over="<< pop-over html here >>" + * + * Include the standard TB data-XXX attributes to controll the pop-over's appearance. We will + * default placement to the left, delay to 0 seconds, content type to HTML, and title to 'Help'. + */ + .directive('awPopOver', function() { + return function(scope, element, attrs) { + var placement = (attrs.placement != undefined && attrs.placement != null) ? attrs.placement : 'left'; + var title = (attrs.title != undefined && attrs.title != null) ? attrs.title : 'Help'; + $(element).popover({ placement: placement, delay: 0, title: title, + content: attrs.awPopOver, delay: 0, trigger: 'click', html: true }); + } + }); + + + diff --git a/ansibleworks/ui/static/lib/ansible/form-generator.js b/ansibleworks/ui/static/lib/ansible/form-generator.js index b5ec5c797d..5b587110b6 100644 --- a/ansibleworks/ui/static/lib/ansible/form-generator.js +++ b/ansibleworks/ui/static/lib/ansible/form-generator.js @@ -40,6 +40,15 @@ angular.module('FormGenerator', ['GeneratorHelpers']) case 'awToolTip': result = "aw-tool-tip=\"" + obj[key] + "\" "; break; + case 'awPopOver': + result = "aw-pop-over='" + obj[key] + "' "; + break; + case 'dataTitle': + result = "data-title=\"" + obj[key] + "\" "; + break; + case 'dataPlacement': + result = "data-placement=\"" + obj[key] + "\" "; + break; default: result = key + "=\"" + obj[key] + "\" "; } @@ -226,7 +235,14 @@ angular.module('FormGenerator', ['GeneratorHelpers']) html += "
' + field.label + '' + "\n"; + html += "' + "\n"; html += "
\n"; html += "