From f63e42ebff009e82e4a5949409f1a2514bb23569 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Tue, 16 Jul 2013 12:08:49 -0400 Subject: [PATCH] AC-216 First pass at turning job event JSON into a modal form. --- awx/ui/static/css/ansible-ui.css | 19 ++++- awx/ui/static/js/awx-min.js | 45 +++++------ awx/ui/static/js/controllers/Teams.js | 6 +- awx/ui/static/js/forms/JobEvents.js | 43 +++++++---- awx/ui/static/js/helpers/Events.js | 84 +++++++++++++-------- awx/ui/static/lib/ansible/form-generator.js | 9 ++- 6 files changed, 131 insertions(+), 75 deletions(-) diff --git a/awx/ui/static/css/ansible-ui.css b/awx/ui/static/css/ansible-ui.css index 1f78373bf4..8f6692973a 100644 --- a/awx/ui/static/css/ansible-ui.css +++ b/awx/ui/static/css/ansible-ui.css @@ -368,7 +368,7 @@ } -/* Jobs page */ +/* Jobs pages */ .job-error, .job-failed, .active-failures-true, @@ -448,6 +448,11 @@ padding-left: 72px; } + #job_events .control-group { + margin-top: 0; + margin-bottom: 10px; + } + /* End Jobs Page */ @@ -577,7 +582,7 @@ .horizontal-narrow .control-label { float: left; - width: 120px; + width: 100px; padding-top: 5px; text-align: right; } @@ -585,18 +590,24 @@ .horizontal-narrow .controls { *display: inline-block; *padding-left: 20px; - margin-left: 140px; + margin-left: 120px; *margin-left: 0; } .horizontal-narrow .controls:first-child { - *padding-left: 140px; + *padding-left: 120px; } .modal-input-xlarge { width: 350px; } + .form-section-title { + font-weight: bold; + width: 100%; + border-bottom: 1px solid #d8d8d8; + margin-bottom: 5px; + } /* overrides to TB modal */ .modal-header { diff --git a/awx/ui/static/js/awx-min.js b/awx/ui/static/js/awx-min.js index 0734ee4e56..3c1ff585f0 100644 --- a/awx/ui/static/js/awx-min.js +++ b/awx/ui/static/js/awx-min.js @@ -4,7 +4,7 @@ * * awx-min.js * - * master-8f00625, Mon Jul 15 14:32:08 2013 -0400 + * master-6fcbfb1, Mon Jul 15 14:35:54 2013 -0400 * */ var urlPrefix="/static/"; @@ -64,8 +64,8 @@ b,c,e){d(u,a,b,f,{hdr:"Error!",msg:"Failed to retrieve event detail: "+p.event_i y({scope:t,list:e,url:k});t.search(e.iterator);a();t.showEvents=function(b,d){p.setUrl(d);p.get().success(function(d,c,f,e){a({path:"/jobs/"+d.id,title:d.name});h.url("/jobs/"+d.id+"/job_events/?host="+escape(b))}).error(function(a,b,c,f){u(t,a,b,form,{hdr:"Error!",msg:"Failed to lookup last job: "+d+". GET status: "+b})})};t.refresh=function(){t.search(e.iterator)};t.jobDetails=function(){h.path("/jobs/"+c.id)};t.jobEvents=function(){h.path("/jobs/"+c.id+"/job_events")}} JobHostSummaryList.$inject="$scope $rootScope $location $log $routeParams Rest Alert JobHostList GenerateList LoadBreadCrumbs Prompt SearchInit PaginateInit ReturnToCaller ClearScope ProcessErrors GetBasePath".split(" ");function JobsListCtrl(k,m,h,g,c,p,f,e,l,a,d,b,y,v,A,u,C,t,s,n){A("htmlTemplate");var B=C("jobs");h.path().replace(/^\//,"").split("/");var q=l.inject(e,{mode:"edit"});m.flashMessage=null;q.selected=[];q.PostRefreshRemove&&q.PostRefreshRemove();q.PostRefreshRemove=q.$on("PostRefresh",function(){$("tr.success").each(function(a){a=$(this).attr("ng-class");q[a]=""});for(var a,b=0;bEnter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.

View JSON examples at www.json.org

View YAML examples at ansibleworks.com

',dataTitle:"Inventory Variables",dataPlacement:"bottom",column:2}},buttons:{save:{label:"Save",icon:"icon-ok","class":"btn-success",ngClick:"formSave()",ngDisabled:!0}, reset:{ngClick:"formReset()",label:"Reset",icon:"icon-remove",ngDisabled:!0}},related:{groups:{type:"tree",open:!0,actions:{}},hosts:{type:"treeview",title:"{{ groupTitle }}",iterator:"host",actions:{select:{ngClick:"selectHost()",icon:"icon-check",label:"Add Existing Host",awToolTip:"Select existing host",ngHide:"createButtonShow == false","class":"btn btn-pad"},create:{ngClick:"createHost()",icon:"icon-plus",label:"Create New Host",awToolTip:"Create a new host",ngHide:"createButtonShow == false", "class":"btn-success"}},fields:{name:{key:!0,label:"Host Name",ngClick:"editHost({{ host.id }}, '{{ host.name }}')"},has_active_failures:{label:"Failed jobs?",showValue:!1,ngClick:"showEvents('{{ host.name }}', '{{ host.related.last_job }}')",ngShow:"{{ host.has_active_failures }}",icon:"icon-exclamation-sign","class":"active-failures-{{ host.has_active_failures }}",text:"View failures",searchField:"has_active_failures",searchType:"boolean",searchOptions:[{name:"No",value:0},{name:"Yes",value:1}]}}, -fieldActions:{edit:{ngClick:"editHost({{ host.id }}, '{{ host.name }}')",icon:"icon-edit",label:"Edit","class":"btn-success",awToolTip:"Edit host"},"delete":{ngClick:"deleteHost({{ host.id }}, '{{ host.name }}')",icon:"icon-remove",label:"Delete","class":"btn-danger",awToolTip:"Remove host"}}}}});angular.module("JobEventFormDefinition",[]).value("JobEventForm",{editTitle:"{{ id }} - {{ event }}",name:"job_events","class":"horizontal-narrow",well:!1,fields:{created:{label:"Created",type:"text",readonly:!0,"class":"span3",section:"Event Info"},status:{labelClass:"job-{{ status }}",icon:"icon-circle",type:"custom",control:'
{{ status }}
',section:"Event"},host:{label:"Host",type:"text",readonly:!0,section:"Event"},task:{label:"Task",type:"text", -readonly:!0,section:"Event"},conditional:{label:"Conditional?",type:"checkbox",readonly:!0},msg:{label:"Message",type:"textarea",readonly:!0,section:"Results",rows:5},stdout:{label:"Standard Out",type:"textarea",readonly:!0,section:"Results",rows:5},stderr:{label:"Standard Error",type:"textarea",readonly:!0,section:"Results",rows:5},start:{label:"Start",type:"text",readonly:!0,section:"Timing"},end:{label:"End",type:"text",readonly:!0,section:"Timing"},delta:{label:"Elapsed",type:"text",readonly:!0, -section:"Timing"},module_name:{label:"Name",type:"text",readonly:!0,section:"Module"},module_args:{label:"Arguments",type:"text",readonly:!0,section:"Module"}},buttons:{},related:{}});angular.module("JobFormDefinition",[]).value("JobForm",{addTitle:"Create Job",editTitle:"{{ name }}",name:"jobs",well:!0,twoColumns:!0,fields:{name:{label:"Name",type:"text",addRequired:!0,editRequired:!0,column:1},description:{label:"Description",type:"text",addRequired:!1,editRequired:!1,column:1},job_type:{label:"Job Type",type:"select",ngOptions:"type.label for type in job_type_options","default":"run",addRequired:!0,editRequired:!0,awPopOver:"

When this template is submitted as a job, setting the type to run will execute the playbook, running tasks on the selected hosts.

Setting the type to check will not execute the playbook. Instead, ansible will check playbook syntax, test environment setup and report problems.

", +fieldActions:{edit:{ngClick:"editHost({{ host.id }}, '{{ host.name }}')",icon:"icon-edit",label:"Edit","class":"btn-success",awToolTip:"Edit host"},"delete":{ngClick:"deleteHost({{ host.id }}, '{{ host.name }}')",icon:"icon-remove",label:"Delete","class":"btn-danger",awToolTip:"Remove host"}}}}});angular.module("JobEventFormDefinition",[]).value("JobEventForm",{editTitle:"{{ id }} - {{ event }}",name:"job_events","class":"horizontal-narrow",well:!1,fields:{status:{labelClass:"job-{{ status }}",icon:"icon-circle",type:"custom",control:'
{{ status }}
',section:"Event"},id:{label:"ID",type:"text",readonly:!0,section:"Event","class":"span1"},created:{label:"Created",type:"text",readonly:!0,section:"Event"},host:{label:"Host",type:"text",readonly:!0, +section:"Event"},task:{label:"Task",type:"text",readonly:!0,section:"Event"},conditional:{label:"Conditional?",type:"checkbox",readonly:!0,section:"Event"},rc:{label:"Return Code",type:"text",readonly:!0,section:"Results","class":"span1"},msg:{label:"Message",type:"textarea",readonly:!0,section:"Results","class":"modal-input-xlarge",rows:1},stdout:{label:"Std Out",type:"textarea",readonly:!0,section:"Results","class":"modal-input-xlarge",rows:1},stderr:{label:"Std Error",type:"textarea",readonly:!0, +section:"Results","class":"modal-input-xlarge",rows:1},start:{label:"Start",type:"text",readonly:!0,section:"Timing"},end:{label:"End",type:"text",readonly:!0,section:"Timing"},delta:{label:"Elapsed",type:"text",readonly:!0,section:"Timing"},module_name:{label:"Name",type:"text",readonly:!0,section:"Module"},module_args:{label:"Arguments",type:"text",readonly:!0,section:"Module"}},buttons:{},related:{}});angular.module("JobFormDefinition",[]).value("JobForm",{addTitle:"Create Job",editTitle:"{{ name }}",name:"jobs",well:!0,twoColumns:!0,fields:{name:{label:"Name",type:"text",addRequired:!0,editRequired:!0,column:1},description:{label:"Description",type:"text",addRequired:!1,editRequired:!1,column:1},job_type:{label:"Job Type",type:"select",ngOptions:"type.label for type in job_type_options","default":"run",addRequired:!0,editRequired:!0,awPopOver:"

When this template is submitted as a job, setting the type to run will execute the playbook, running tasks on the selected hosts.

Setting the type to check will not execute the playbook. Instead, ansible will check playbook syntax, test environment setup and report problems.

", dataTitle:"Job Type",dataPlacement:"right",column:1},inventory:{label:"Inventory",type:"lookup",sourceModel:"inventory",sourceField:"name",addRequired:!0,editRequired:!0,ngClick:"lookUpInventory()",column:1},project:{label:"Project",type:"lookup",sourceModel:"project",sourceField:"name",addRequired:!0,editRequired:!0,ngClick:"lookUpProject()",column:1},playbook:{label:"Playbook",type:"select",ngOptions:"book for book in playbook_options",id:"playbook-select",addRequired:!0,editRequired:!0,column:1}, credential:{label:"Credential",type:"lookup",sourceModel:"credential",sourceField:"name",ngClick:"lookUpCredential()",addRequired:!1,editRequired:!1,column:2},forks:{label:"Forks",id:"forks-number",type:"number",integer:!0,min:0,max:100,slider:!0,"class":"input-mini","default":"0",addRequired:!1,editRequired:!1,column:2,awPopOver:"

The number of parallel or simultaneous processes to use while executing the playbook. Provide a value between 0 and 100. A value of zero will use the ansible default setting of 5 parallel processes.

", dataTitle:"Forks",dataPlacement:"left"},limit:{label:"Limit",type:"text",addRequired:!1,editRequired:!1,column:2,awPopOver:'

Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. Multiple patterns can be separated by ; : or ,

For more information and examples see the Selecting Targets section under Inventory and Patterns in the Ansible documentation.

', @@ -198,8 +198,9 @@ a})}));e||m("Access Denied","You do not have access to this function. Please con 0>=parseInt(g.free_instances)&&h("License Warning",'Your AnsibleWorks AWX License has reached capacity for the number of managed hosts allowed. You will not be able to add any additional hosts. To extend your license, please visit ansibleworks.com/ansibleworks-awx., or contact info@ansibleworks.com for more information.',"alert-info",null,!0))}}]);angular.module("APIDefaults",["RestServices","Utilities"]).factory("GetAPIDefaults",["Alert","Rest","$rootScope",function(k,m,h){return function(g){function c(c){var a={};for(id in h.apiDefaults)if(id==c||id.iterator==c){a[id]=defaults[id];break}return a}function k(){if(f=={}&&5>e)e++,setTimeout(1E3,k());else if("success"==f.status)return c(g)}var f={},e=0;return null==h.apiDefaults||void 0==h.apiDefaults?(f={},m.setUrl("/api/v1"),m.get().success(function(c,a,d,b){defaults=c;for(var e in defaults)switch(e){case "organizations":dafaults[e].iterator= "organization";break;case "jobs":defaults[e].iterator="job";break;case "users":defaults[e].iterator="user";break;case "teams":defaults[e].iterator="team";break;case "hosts":defaults[e].iterator="host";break;case "groups":defaults[e].iterator="group";break;case "projects":defaults[e].iterator="project"}h.apiDefaults=defaults;f={status:"success"}}).error(function(c,a,d,b){f={status:"error",msg:"Call to /api/v1 failed. GET returned status: "+a}}),k()):c(g)}}]);angular.module("ChildrenHelper",["RestServices","Utilities"]).factory("ToggleChildren",["Alert","Rest","GetBasePath","ProcessErrors","FormatDate",function(k,m,h,g,c){return function(c){function f(b){a[b].ngicon="icon-collapse-alt";for(var d=b+1;d\n";b+='\n";b+='
\n';b+=a.slider?'
\n':"";b+='A value is required!\n';a.integer&&(b+='Must be an integer value\n');if(a.min||a.max)b+='Must be in range '+a.min+" to "+a.max+"\n";b+='\n';b+="
\n";b+="\n"}if("checkbox"==a.type&&(!a.readonly||a.readonly&&"edit"==d.mode))b+='
\n",b+='
\n',b+='
\n",b+="
\n"; +">\n",b+='
\n',b+='
\n",b+="\n"; if("radio"==a.type&&(!a.readonly||a.readonly&&"edit"==d.mode)){for(var b=b+'
\n",b=b+('\n"),b=b+'
\n',e=0;e",b+='A value is required!

\n';b+='

A value is required!

\n';b+='

\n';b+="
\n";b+="
\n"}if("hidden"==a.type&&("edit"==d.mode&&a.includeOnEdit||"add"==d.mode&&a.includeOnAdd))b+='';if("lookup"==a.type&&(void 0==a.excludeMode||a.excludeMode!=d.mode)){b+='
\n";b+='\n";b+='
\n';b+='
\n'; b+='\n';b+='A value is required!\n';b+=' '
\n';if(this.form.statusActions){var a=a+'
\n',d;for(action in this.form.statusActions)d=this.form.statusActions[action],a+="
\n";a+='
\n'}var a= a+'
\n',b;for(b in this.form.statusFields)d=this.form.statusFields[b],a+=this.buildField(b,d,c);a+="
\x3c!-- status fields --\x3e\n";a+="
\x3c!-- well --\x3e\n"}if(this.form.fieldsAsHeader){a+='
\n';a+='
\n';for(b in this.form.fields)d=this.form.fields[b],a+=this.headerField(b,d,c);a+="
\n";a+="
\n"}else{this.form.collapse&&this.form.collapseMode== c.mode&&(a+='
\n",a+="
\n");this.has("well")&&(a+='
\n');a+='
\n';a+='
{{ flashMessage }}
\n'; -if(this.form.twoColumns){a+='
\n';a+='
\n';for(b in this.form.fields)d=this.form.fields[b],1==d.column&&(a+=this.buildField(b,d,c));a+="
\x3c!-- column 1 --\x3e\n";a+='
\n';for(b in this.form.fields)d=this.form.fields[b],2==d.column&&(a+=this.buildField(b,d,c));a+="
\x3c!-- column 2 --\x3e\n";a+="
\x3c!-- inner row --\x3e\n"}else for(b in this.form.fields)d=this.form.fields[b],a+=this.buildField(b,d,c);if(!this.modal){this.has("buttons")&& -(a+=this.form.twoColumns?"
":"",a+='
\n',a+='
\n');for(var e in this.form.buttons)d=this.form.buttons[e],a+="
\n",a+="
\n");a+="
\n"}this.has("well")&&(a+="
\n");this.form.collapse&&this.form.collapseMode==c.mode&&(a+="
\n",a+="
\n")}if(!this.modal&&this.form.items)for(itm in this.form.items){a+='
\n';a+=g({iterator:this.form.items[itm].iterator,template:this.form.items[itm],mini:!1,label:"Filter Events"});a+='
Viewing {{ '+ -this.form.items[itm].iterator+"Page + 1 }} of {{ "+this.form.items[itm].iterator+"Count }}
\n";a+="
\n";a+='\n";a+='
\n';for(b in this.form.items[itm].fields)d=this.form.items[itm].fields[b],a+=this.buildField(b,d,c);a+="
\n";a+='\n";a+="
\x3c!-- well --\x3e\n"}"inventory"==this.form.name&&"edit"==c.mode?a+=this.buildTree(c):!this.modal&&(c.related&&this.form.related)&&(a+=this.buildCollections(c));return a},buildTree:function(f){var a=1,d=this.form;html='
\n';html+="

Inventory Content

\n"; -html+="
\n";for(var b in d.related){if("tree"==d.related[b].type)html+='
',html+='
',html+='',html+='', +if(this.form.twoColumns){a+='
\n';a+='
\n';for(b in this.form.fields)d=this.form.fields[b],1==d.column&&(a+=this.buildField(b,d,c));a+="
\x3c!-- column 1 --\x3e\n";a+='
\n';for(b in this.form.fields)d=this.form.fields[b],2==d.column&&(a+=this.buildField(b,d,c));a+="
\x3c!-- column 2 --\x3e\n";a+="
\x3c!-- inner row --\x3e\n"}else{var e="";for(b in this.form.fields)d=this.form.fields[b],d.section&&d.section!=e&&(a+='
'+ +d.section+"
\n",e=d.section),a+=this.buildField(b,d,c)}if(!this.modal){this.has("buttons")&&(a+=this.form.twoColumns?"
":"",a+='
\n',a+='
\n');for(var f in this.form.buttons)d=this.form.buttons[f],a+="
\n",a+="
\n");a+="\n"}this.has("well")&&(a+="
\n");this.form.collapse&&this.form.collapseMode==c.mode&&(a+="
\n",a+="
\n")}if(!this.modal&&this.form.items)for(itm in this.form.items){a+='
\n';a+=g({iterator:this.form.items[itm].iterator, +template:this.form.items[itm],mini:!1,label:"Filter Events"});a+='
Viewing {{ '+this.form.items[itm].iterator+"Page + 1 }} of {{ "+this.form.items[itm].iterator+"Count }}
\n";a+="
\n";a+='\n";a+='
\n';for(b in this.form.items[itm].fields)d=this.form.items[itm].fields[b],a+=this.buildField(b,d,c);a+="
\n";a+='\n";a+="
\x3c!-- well --\x3e\n"}"inventory"==this.form.name&&"edit"==c.mode?a+=this.buildTree(c):!this.modal&&(c.related&&this.form.related)&&(a+=this.buildCollections(c));return a},buildTree:function(f){var a=1,d=this.form;html='
\n'; +html+="

Inventory Content

\n";html+="
\n";for(var b in d.related){if("tree"==d.related[b].type)html+='
',html+='
',html+='',html+='', html+='',html+="
\n",html+='
\n',html+='
',html+='
\n', html+="
\n";else{html+='
\n';html+='
\n';html+='

'+d.related[b].title+"

\n";html+=g({iterator:d.related[b].iterator,template:d.related[b],mini:!0});html+='
\n';for(var h in d.related[b].actions)html+='\n";html+="
\n";html+='
\n';html+= diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index f8bb692c07..a9f0113a32 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -283,13 +283,13 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to retrieve team: ' + $routeParams.id + '. GET status: ' + status }); + { hdr: 'Error!', msg: 'Failed to retrieve team: ' + $routeParams.team_id + '. GET status: ' + status }); }); // Save changes to the parent scope.formSave = function() { $rootScope.flashMessage = null; - Rest.setUrl(defaultUrl + $routeParams.id +'/'); + Rest.setUrl(defaultUrl + $routeParams.team_id +'/'); var data = {} for (var fld in form.fields) { data[fld] = scope[fld]; @@ -301,7 +301,7 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.id + '. PUT status: ' + status }); + { hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.team_id + '. PUT status: ' + status }); }); }; diff --git a/awx/ui/static/js/forms/JobEvents.js b/awx/ui/static/js/forms/JobEvents.js index f6c8159187..a7fc5e77ee 100644 --- a/awx/ui/static/js/forms/JobEvents.js +++ b/awx/ui/static/js/forms/JobEvents.js @@ -16,13 +16,6 @@ angular.module('JobEventFormDefinition', []) well: false, fields: { - created: { - label: 'Created', - type: 'text', - readonly: true, - "class": 'span3', - section: 'Event Info' - }, status: { labelClass: 'job-\{\{ status \}\}', icon: 'icon-circle', @@ -30,6 +23,19 @@ angular.module('JobEventFormDefinition', []) control: '
\{\{ status \}\}
', section: 'Event' }, + id: { + label: 'ID', + type: 'text', + readonly: true, + section: 'Event', + 'class': 'span1' + }, + created: { + label: 'Created', + type: 'text', + readonly: true, + section: 'Event' + }, host: { label: 'Host', type: 'text', @@ -45,28 +51,39 @@ angular.module('JobEventFormDefinition', []) conditional: { label: 'Conditional?', type: 'checkbox', - readonly: true + readonly: true, + section: 'Event' }, + rc: { + label: 'Return Code', + type: 'text', + readonly: true, + section: 'Results', + 'class': 'span1' + }, msg: { label: 'Message', type: 'textarea', readonly: true, section: 'Results', - rows: 5 + 'class': 'modal-input-xlarge', + rows: 1 }, stdout: { - label: 'Standard Out', + label: 'Std Out', type: 'textarea', readonly: true, section: 'Results', - rows: 5 + 'class': 'modal-input-xlarge', + rows: 1 }, stderr: { - label: 'Standard Error', + label: 'Std Error', type: 'textarea', readonly: true, section: 'Results', - rows: 5 + 'class': 'modal-input-xlarge', + rows: 1 }, start: { label: 'Start', diff --git a/awx/ui/static/js/helpers/Events.js b/awx/ui/static/js/helpers/Events.js index 389de5a968..f3120758a1 100644 --- a/awx/ui/static/js/helpers/Events.js +++ b/awx/ui/static/js/helpers/Events.js @@ -35,40 +35,62 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventFormDefini Rest.get() .success( function(data, status, headers, config) { for (var fld in form.fields) { - if (fld == 'status') { - if (data['failed']) { - scope['status'] = 'error'; - } - else if (data['changed']) { - scope['status'] = 'changed'; - } - else { - scope['status'] = 'success'; - } - } - else if (fld == 'event_data') { - scope['event_data'] = JSON.stringify(data['event_data'], undefined, '\t'); - } - else if (fld == 'host') { - if (data['summary_fields'] && data['summary_fields']['host']) { - scope['host'] = data['summary_fields']['host']['name']; - } - } - else { - if (fld == 'created') { - var cDate = new Date(data['created']); - scope['created'] = FormatDate(cDate); - } - else { - if (data[fld]) { - scope[fld] = data[fld]; - } - } + switch(fld) { + case 'status': + if (data['failed']) { + scope['status'] = 'error'; + } + else if (data['changed']) { + scope['status'] = 'changed'; + } + else { + scope['status'] = 'success'; + } + break; + case 'created': + var cDate = new Date(data['created']); + scope['created'] = FormatDate(cDate); + break; + case 'host': + if (data['summary_fields'] && data['summary_fields']['host']) { + scope['host'] = data['summary_fields']['host']['name']; + } + break; + case 'id': + case 'task': + scope[fld] = data[fld]; + break; + case 'msg': + case 'stdout': + case 'stderr': + case 'start': + case 'end': + case 'delta': + case 'rc': + if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) { + scope[fld] = data['event_data']['res'][fld]; + if (form.fields[fld].type == 'textarea') { + var n = data['event_data']['res'][fld].match(/\n/g); + rows = (n) ? n.length : 1; + rows = (rows > 5) ? 5 : rows; + $('textarea[name="' + fld + '"]').attr('rows',rows); + } + } + break; + case 'conditional': + if (data['event_data']['res']) { + scope[fld] = data['event_data']['res']['is_conditional']; + } + break; + case 'module_name': + case 'module_args': + if (data['event_data']['res'] && data['event_data']['res']['invocation']) { + scope[fld] = data['event_data']['res']['invocation'][fld]; + } + break; } } - scope['formModalHeader'] = data.event_display.replace(/^\u00a0*/g,''); - }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index 9831337567..c15b3cc8b6 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -402,7 +402,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += this.attr(field,'trueValue'); html += this.attr(field,'falseValue'); html += (field.checked) ? "checked " : ""; - html += (field.readonly) ? "readonly " : ""; + html += (field.readonly) ? "disabled " : ""; html += " /> " + field.label + "\n"; html += (field.awPopOver) ? this.attr(field, 'awPopOver') : ""; html += "\n"; @@ -431,7 +431,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += "value=\"" + field.options[i].value + "\" "; html += "ng-model=\"" + fld + "\" "; html += (field.ngChange) ? this.attr(field,'ngChange') : ""; - html += (field.readonly) ? "readonly " : ""; + html += (field.readonly) ? "disabled " : ""; html += (options.mode == 'edit' && field.editRequired) ? "required " : ""; html += (options.mode == 'add' && field.addRequired) ? "required " : ""; html += " /> " + field.options[i].label + "\n"; @@ -632,8 +632,13 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) } else { // original, single-column form + var section = ''; for (var fld in this.form.fields) { var field = this.form.fields[fld]; + if (field.section && field.section != section) { + html += "
" + field.section + "
\n"; + section = field.section; + } html += this.buildField(fld, field, options); } }