diff --git a/lib/ui/static/css/ansible-ui.css b/lib/ui/static/css/ansible-ui.css index d0aafec9b5..e053b0c85e 100644 --- a/lib/ui/static/css/ansible-ui.css +++ b/lib/ui/static/css/ansible-ui.css @@ -16,6 +16,10 @@ padding-top: 40px; } + hr { + border-color: #e3e3e3; + } + .container-fluid { min-height: 700px ; } @@ -175,6 +179,13 @@ display: inline-block; font-size: 18px; margin-left: 15px; + min-width: 30px; + } + + .search-widget label { + display:inline-block; + vertical-align: middle; + padding-right: 15px; } .nav-path { @@ -241,7 +252,7 @@ .list-actions { display: inline-block; vertical-align: top; - margin-left: 15px; + margin-left: 10px; margin-top: 3px; } /* End Display list actions */ @@ -256,15 +267,20 @@ background-color: #fff; } - .job-error, .job-failed { + .job-error, .job-failed, + input[type="text"].job-failed, + input[type="text"].job-error + { color: #da4f49; } - .job-new { + .job-new, input[type="text"].job-new { color: #778899; } - .job-pending, .job-running { + .job-pending, .job-running, .job-success, + input[type="text"].job-success + { color: #5bb75b; } @@ -273,5 +289,28 @@ font-weight: bold; padding-left: 15px; } + + #job_events label { + margin-right: 10px; + } + + #job_events { + text-align: center; + } + + #job_events_items_form { + margin-top: 15px; + } + + .form-items .search-widget { + margin-top: 15px; + } + + .form-items .item-count { + display: inline-block; + font-size: small; + margin-top: 25px; + } + /* End Jobs Page */ diff --git a/lib/ui/static/js/app.js b/lib/ui/static/js/app.js index aab51d05fb..a781080b77 100644 --- a/lib/ui/static/js/app.js +++ b/lib/ui/static/js/app.js @@ -52,7 +52,7 @@ angular.module('ansible', [ .config(['$routeProvider', function($routeProvider) { $routeProvider. when('/jobs', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsList }). + { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsListCtrl }). when('/jobs/:id', { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsEdit }). diff --git a/lib/ui/static/js/controllers/JobTemplates.js b/lib/ui/static/js/controllers/JobTemplates.js index b8fd18281a..345dd149d7 100644 --- a/lib/ui/static/js/controllers/JobTemplates.js +++ b/lib/ui/static/js/controllers/JobTemplates.js @@ -167,7 +167,7 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re Rest.setUrl(data.related.start); Rest.post() .success( function(data, status, headers, config) { - $location.path(GetBasePath('jobs')); + $location.path('/jobs'); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, diff --git a/lib/ui/static/js/controllers/Jobs.js b/lib/ui/static/js/controllers/Jobs.js index 7834e718a8..4582f01f36 100644 --- a/lib/ui/static/js/controllers/Jobs.js +++ b/lib/ui/static/js/controllers/Jobs.js @@ -10,9 +10,9 @@ 'use strict'; -function JobsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, - GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath, LookUpInit) +function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, + GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, + ClearScope, ProcessErrors, GetBasePath, LookUpInit) { ClearScope('htmlTemplate'); var list = JobList; @@ -33,16 +33,73 @@ function JobsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Aler } scope.viewEvents = function(id) { - console.log('headed to: ' + $location.path() + '/' + id + '/job_events'); $location.path($location.path() + '/' + id + '/job_events'); } + + scope.deleteJob = function(id, name) { + Rest.setUrl(defaultUrl + id + '/'); + Rest.get() + .success( function(data, status, headers, config) { + + var url, action_label, restcall, hdr; + + if (data.status == 'pending') { + url = data.related.cancel; + action_label = 'cancel'; + hdr = 'Cancel Job'; + } + else { + url = defaultUrl + id + '/'; + action_label = 'delete'; + hdr = 'Delete Job'; + } + + var action = function() { + Rest.setUrl(url); + if (action_label == 'cancel') { + Rest.post() + .success( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + scope.search(list.iterator); + }) + .error( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); + }); + } + else { + Rest.delete() + .success( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + scope.search(list.iterator); + }) + .error( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); + }); + } + }; + + Prompt({ hdr: hdr, + body: 'Are you sure you want to ' + action_label + ' job ' + id + '?', + action: action + }); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get job details. GET returned status: ' + status }); + }); + + } } -JobsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList', - 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', - 'ProcessErrors','GetBasePath', 'LookUpInit' - ]; +JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList', + 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', + 'ProcessErrors','GetBasePath', 'LookUpInit' + ]; function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, @@ -274,7 +331,8 @@ JobsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$ function JobEvents ($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, - GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath) + GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, SearchInit, + PaginateInit, GetBasePath) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -285,31 +343,57 @@ function JobEvents ($scope, $rootScope, $compile, $location, $log, $routeParams, var scope = GenerateForm.inject(form, {mode: 'edit', related: true}); generator.reset(); - var defaultUrl = GetBasePath('jobs') + $routeParams.id + '/job_events'; + var defaultUrl = GetBasePath('jobs') + $routeParams.id + '/job_events/'; var base = $location.path().replace(/^\//,'').split('/')[0]; var master = {}; var id = $routeParams.id; var relatedSets = {}; - + if (scope.PostRefreshRemove){ + scope.PostRefreshRemove(); + } + scope.PostRefreshRemove = scope.$on('PostRefresh', function() { + var results = scope[form.items.event.set][0]; + // Disable Next/Prev buttons when we rich the end/beginning of array + scope[form.items.event.iterator + 'NextUrlDisable'] = (scope[form.items.event.iterator + 'NextUrl']) ? "" : "disabled"; + scope[form.items.event.iterator + 'PrevUrlDisable'] = (scope[form.items.event.iterator + 'PrevUrl']) ? "" : "disabled"; + + // Set the scope input field values + for (var fld in form.items.event.fields) { + if (fld == 'event_data') { + scope.event_data = JSON.stringify(results[fld]); + } + else { + if (results[fld]) { + scope[fld] = results[fld]; + } + } + } + scope['event_status'] = (results.failed) ? 'failed' : 'success'; + }); + // Retrieve detail record and prepopulate the form Rest.setUrl(defaultUrl); Rest.get({ params: {page_size: 1} }) .success( function(data, status, headers, config) { - LoadBreadCrumbs({ path: '/organizations/' + id, title: data.name }); + var results = data.results[0]; + scope[form.items.event.iterator + 'NextUrl'] = data.next; + scope[form.items.event.iterator + 'PrevUrl'] = data.previous; + scope[form.items.event.iterator + 'Count'] = data.count; + LoadBreadCrumbs({ path: '/jobs/' + id, title: results["summary_fields"].job.name }); for (var fld in form.fields) { - if (data[fld]) { - scope[fld] = data[fld]; + if (results[fld]) { + scope[fld] = results[fld]; + } + if (form.fields[fld].sourceModel && results.summary_fields[form.fields[fld].sourceModel]) { + scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + results.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; } } - for (var fld in form.items) { - if (data[fld]) { - scope[fld] = data[fld]; - } - } - scope['event_satus'] = (data.failed) ? 'failed' : 'success'; - scope.next = data.next; - scope.previous = data.previous; + scope[form.items.event.set] = data.results; + SearchInit({ scope: scope, set: form.items.event.set, list: form.items.event, iterator: form.items.event.iterator, url: defaultUrl }); + PaginateInit({ scope: scope, list: form.items.event, iterator: form.items.event.iterator, url: defaultUrl , pageSize: 1 }); + scope.$emit('PostRefresh'); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, @@ -319,5 +403,6 @@ function JobEvents ($scope, $rootScope, $compile, $location, $log, $routeParams, } JobEvents.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventForm', - 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath' ]; + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'SearchInit', + 'PaginateInit', 'GetBasePath' ]; diff --git a/lib/ui/static/js/forms/JobEvents.js b/lib/ui/static/js/forms/JobEvents.js index 566bdfbcc5..05083146c6 100644 --- a/lib/ui/static/js/forms/JobEvents.js +++ b/lib/ui/static/js/forms/JobEvents.js @@ -13,26 +13,27 @@ angular.module('JobEventFormDefinition', []) editTitle: '{{ name }} Events', //Legend in edit mode name: 'job_events', well: true, - formInline: true, + fieldsAsHeader: true, fields: { job: { - label: 'Job ID', + label: 'Job', type: 'text', + class: 'span1', readonly: true }, - name: { - label: 'Name', + job_name: { type: 'text', sourceModel: 'job', sourceField: 'name', + class: 'span5', readonly: true }, - description: { - label: 'Description', + job_description: { type: 'text', sourceModel: 'job', sourceField: 'description', + class: 'span5', readonly: true } }, @@ -43,29 +44,54 @@ angular.module('JobEventFormDefinition', []) items: { event: { + set: 'job_events', + iterator: 'job_event', label: 'Event', - type: 'text', - readonly: true - }, - created: { - label: 'Event Timestamp', - type: 'text', - readonly: true - }, - event_status: { - label: 'Event Status \{\{ status \}\}', - type: 'text', - readonly: true, - control: false - }, - event_data: { - label: 'Event Data', - type: 'textarea', - class: 'span12', - rows: 10 - } + fields: { + id: { + label: 'Event ID', + type: 'text', + readonly: true, + class: 'span2', + key: true, + searchType: 'int' + }, + created: { + label: 'Event Timestamp', + type: 'text', + readonly: true, + class: 'span4' + }, + event: { + label: 'Event', + type: 'text', + readonly: true + }, + host: { + label: 'Host', + type: 'text', + readonly: true + }, + event_status: { + label: 'Event Status', + type: 'text', + class: 'job-\{\{ event_status \}\}', + readonly: true, + searchField: 'failed', + searchType: 'boolean', + searchOptions: [{ name: "success", value: 0 }, { name: "failed", value: 1 }], + }, + event_data: { + label: 'Event Data', + type: 'textarea', + class: 'span12', + rows: 10, + readonly: true + } + } + } }, - + related: { //related colletions (and maybe items?) } diff --git a/lib/ui/static/js/helpers/JobTemplate.js b/lib/ui/static/js/helpers/JobTemplate.js index 58903dd454..6e54500219 100644 --- a/lib/ui/static/js/helpers/JobTemplate.js +++ b/lib/ui/static/js/helpers/JobTemplate.js @@ -24,7 +24,7 @@ angular.module('JobTemplateHelper', [ 'RestServices', 'Utilities', 'CredentialFo Rest.setUrl(start_url); Rest.post(pswd) .success( function(data, status, headers, config) { - $location.path(GetBasPath('jobs')); + $location.path('/jobs'); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, @@ -37,6 +37,7 @@ angular.module('JobTemplateHelper', [ 'RestServices', 'Utilities', 'CredentialFo // Add the password field field = form.fields[passwords[i]]; fld = passwords[i]; + scope[fld] = ''; html += "
\n"; html += "' + "\n"; html += "
\n"; @@ -57,6 +58,7 @@ angular.module('JobTemplateHelper', [ 'RestServices', 'Utilities', 'CredentialFo // Add the related confirm field fld = field.associated; field = form.fields[field.associated]; + scope[fld] = ''; html += "
\n"; html += "' + "\n"; html += "
\n"; diff --git a/lib/ui/static/js/helpers/paginate.js b/lib/ui/static/js/helpers/paginate.js index c850a436ad..7534101b52 100644 --- a/lib/ui/static/js/helpers/paginate.js +++ b/lib/ui/static/js/helpers/paginate.js @@ -20,32 +20,44 @@ angular.module('PaginateHelper', ['RefreshHelper']) var scope = params.scope; var list = params.list; + var iterator = (params.iterator) ? params.iterator : list.iterator; var url = params.url; var mode = (params.mode) ? params.mode : null; - scope[list.iterator + 'Page'] = 0; + scope[iterator + 'Page'] = 0; - if (mode == 'lookup') { - scope[list.iterator + 'PageSize'] = 5; + if (params.pageSize) { + scope[iterator + 'PageSize'] = params.pageSize; + } + else if (mode == 'lookup') { + scope[iterator + 'PageSize'] = 5; } else { - scope[list.iterator + 'PageSize'] = 20; + scope[iterator + 'PageSize'] = 20; } scope.nextSet = function(set, iterator) { - scope[iterator + 'Page']++; - Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] }); + if (scope[iterator + 'NextUrl']) { + scope[iterator + 'Page']++; + Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] }); + } }; scope.prevSet = function(set, iterator) { - scope[iterator + 'Page']--; - Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] }); + if (scope[iterator + 'PrevUrl']) { + scope[iterator + 'Page']--; + Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] }); + } }; scope.changePageSize = function(set, iterator) { // Called when a new page size is selected scope[iterator + 'Page'] = 0; - url += (scope[iterator + 'SearchParams']) ? scope[iterator + 'SearchParams'] : ''; + console.log(url); + url = url.replace(/\/\?.*$/,'/'); + console.log(url); + url += (scope[iterator + 'SearchParams']) ? scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] : + '?page_size=' + scope[iterator + 'PageSize' ]; Refresh({ scope: scope, set: set, iterator: iterator, url: url }); } } diff --git a/lib/ui/static/js/helpers/refresh.js b/lib/ui/static/js/helpers/refresh.js index 8f43c24b98..f7aec7994f 100644 --- a/lib/ui/static/js/helpers/refresh.js +++ b/lib/ui/static/js/helpers/refresh.js @@ -22,7 +22,6 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities']) var set = params.set; var iterator = params.iterator; var url = params.url; - Rest.setUrl(url); Rest.get() .success( function(data, status, headers, config) { @@ -32,6 +31,7 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities']) scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize'])); scope[iterator + 'SearchSpin'] = false; scope[set] = data['results']; + scope.$emit('PostRefresh'); }) .error ( function(data, status, headers, config) { scope[iterator + 'SearchSpin'] = false; diff --git a/lib/ui/static/js/helpers/search.js b/lib/ui/static/js/helpers/search.js index 50e4935b0c..736616f9c2 100644 --- a/lib/ui/static/js/helpers/search.js +++ b/lib/ui/static/js/helpers/search.js @@ -21,12 +21,12 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) return function(params) { var scope = params.scope; - var set = params.set; + var set = params.set; var defaultUrl = params.url; var list = params.list; - var iterator = list.iterator; + var iterator = (params.iterator) ? params.iterator : list.iterator; var default_order; - + // Set default values for (fld in list.fields) { if (list.fields[fld].key) { @@ -40,11 +40,34 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) scope[iterator + 'SearchTypeLabel'] = 'Contains'; scope[iterator + 'SearchParams'] = ''; scope[iterator + 'SearchValue'] = ''; + scope[iterator + 'SelectShow'] = false; // show/hide the Select + scope[iterator + 'HideSearchType'] = false; + + var f = scope[iterator + 'SearchField'] + if (list.fields[f].searchType && list.fields[f].searchType == 'boolean') { + scope[iterator + 'SelectShow'] = true; + scope[iterator + 'SearchSelectOpts'] = list.fields[fld].searchOptions; + } + if (list.fields[f].searchType && list.fields[f].searchType == 'int') { + scope[iterator + 'HideSearchType'] = true; + } // Functions to handle search widget changes scope.setSearchField = function(iterator, fld, label) { scope[iterator + 'SearchFieldLabel'] = label; scope[iterator + 'SearchField'] = fld; + scope[iterator + 'SearchValue'] = ''; + scope[iterator + 'SelectShow'] = false; + scope[iterator + 'HideSearchType'] = false; + + if (list.fields[fld].searchType && list.fields[fld].searchType == 'boolean') { + scope[iterator + 'SelectShow'] = true; + scope[iterator + 'SearchSelectOpts'] = list.fields[f].searchOptions; + } + if (list.fields[fld].searchType && list.fields[fld].searchType == 'int') { + scope[iterator + 'HideSearchType'] = true; + } + scope.search(iterator); } @@ -60,19 +83,37 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) // scope[iterator + 'SearchSpin'] = true; var url = defaultUrl; - if (scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) { + if ( (scope[iterator + 'SelectShow'] == false && scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) || + (scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) ) { if (list.fields[scope[iterator + 'SearchField']].sourceModel) { // handle fields whose source is a related model e.g. inventories.organization scope[iterator + 'SearchParams'] = '?' + list.fields[scope[iterator + 'SearchField']].sourceModel + '__' + - list.fields[scope[iterator + 'SearchField']].sourceField + '__' + - scope[iterator + 'SearchType'] + '=' + escape(scope[iterator + 'SearchValue']); - scope[iterator + 'SearchParams'] += (default_order) ? '&order_by=' + escape(default_order) : ''; + list.fields[scope[iterator + 'SearchField']].sourceField + '__'; + } + else if (list.fields[scope[iterator + 'SearchField']].searchField) { + scope[iterator + 'SearchParams'] = '?' + list.fields[scope[iterator + 'SearchField']].searchField + '__'; } else { - scope[iterator + 'SearchParams'] = '?' + scope[iterator + 'SearchField'] + '__' + - scope[iterator + 'SearchType'] + '=' + escape(scope[iterator + 'SearchValue']); - scope[iterator + 'SearchParams'] += (default_order) ? '&order_by=' + escape(default_order) : ''; + scope[iterator + 'SearchParams'] = '?' + scope[iterator + 'SearchField'] + '__'; } + + if ( list.fields[scope[iterator + 'SearchField']].searchType && + (list.fields[scope[iterator + 'SearchField']].searchType == 'int' || + list.fields[scope[iterator + 'SearchField']].searchType == 'boolean') ) { + scope[iterator + 'SearchParams'] += 'int='; + } + else { + scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '='; + } + + if ( list.fields[scope[iterator + 'SearchField']].searchType && + list.fields[scope[iterator + 'SearchField']].searchType == 'boolean' ) { + scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue'].value; + } + else { + scope[iterator + 'SearchParams'] += escape(scope[iterator + 'SearchValue']); + } + scope[iterator + 'SearchParams'] += (default_order) ? '&order_by=' + escape(default_order) : ''; } else { scope[iterator + 'SearchParams'] = ''; diff --git a/lib/ui/static/js/lists/Jobs.js b/lib/ui/static/js/lists/Jobs.js index 87213869e9..9c877b1177 100644 --- a/lib/ui/static/js/lists/Jobs.js +++ b/lib/ui/static/js/lists/Jobs.js @@ -21,7 +21,8 @@ angular.module('JobsListDefinition', []) id: { label: 'Job ID', key: true, - desc: true + desc: true, + searchType: 'int' }, created: { label: 'Creation Date', @@ -69,7 +70,7 @@ angular.module('JobsListDefinition', []) title: 'Cancel', icon: 'icon-minus-sign', mode: 'all', - ngClick: 'cancel(\{\{ job.id \}\})', + ngClick: 'deleteJob(\{\{ job.id \}\})', class: 'btn-danger btn-mini', awToolTip: 'Cancel job', ngDisabled: "job.status == 'error' || job.status == 'failed'" diff --git a/lib/ui/static/lib/ansible/form-generator.js b/lib/ui/static/lib/ansible/form-generator.js index c7c88160a9..f8526e96bd 100644 --- a/lib/ui/static/lib/ansible/form-generator.js +++ b/lib/ui/static/lib/ansible/form-generator.js @@ -68,9 +68,9 @@ angular.module('FormGenerator', ['GeneratorHelpers']) } this.setForm(form); - element.html(this.build(options)); // Inject the html - this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference - // From here use 'scope' to manipulate the form, as the form is not in '$scope' + element.html(this.build(options)); // Inject the html + this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference + // From here use 'scope' to manipulate the form, as the form is not in '$scope' $compile(element)(this.scope); if ((!options.modal) && options.related) { @@ -142,245 +142,258 @@ angular.module('FormGenerator', ['GeneratorHelpers']) }); }, + headerField: function(fld, field, options) { + var html = ''; + if (field.label) { + html += "\n"; + } + html += "'; - html += (field.icon) ? this.icon(field.icon) : ""; - html += field.label + '' + "\n"; - html += "
\n"; - html += (field.clear) ? "
\n" : ""; - if (field.control === null || field.control === undefined || field.control) { - html += "\n"; - html += "
\n"; - } - if (field.ask) { - html += " \n"; - } - html += "
\n"; - // Add error messages - if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { - html += "A value is required!\n"; - } - if (field.type == "email") { - html += "A valid email address is required!\n"; - } - if (field.awPassMatch) { - html += "Must match Password value\n"; - } - html += "\n"; - } - html += "
\n"; - html += "
\n"; - } + //text fields + if (field.type == 'text' || field.type == 'password' || field.type == 'email') { + if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { + html += "
'; + html += (field.icon) ? this.icon(field.icon) : ""; + html += field.label + '' + "\n"; + html += "
\n"; + html += (field.clear) ? "
\n" : ""; + if (field.control === null || field.control === undefined || field.control) { + html += "\n"; + html += "
\n"; + } + if (field.ask) { + html += " \n"; + } + html += "
\n"; + // Add error messages + if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { + html += "A value is required!\n"; + } + if (field.type == "email") { + html += "A valid email address is required!\n"; + } + if (field.awPassMatch) { + html += "Must match Password value\n"; + } + html += "\n"; + } + html += "
\n"; + html += "
\n"; } + } - //textarea fields - if (field.type == 'textarea') { - if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { - html += "
' + field.label + '' + "\n"; - html += "
\n"; - html += "
\n"; + // Add error messages + if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { + html += "A value is required!\n"; + } + html += "\n"; + html += "
\n"; + html += "
\n"; + } + } - //select field - if (field.type == 'select') { - if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { - html += "
' + field.label + '' + "\n"; - html += "
\n"; - html += "
\n"; - // Add error messages - if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { - html += "A value is required!\n"; - } - html += "\n"; - html += "
\n"; - html += "
\n"; - } - } + //select field + if (field.type == 'select') { + if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { + html += "
' + field.label + '' + "\n"; + html += "
\n"; + html += "
\n"; + // Add error messages + if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { + html += "A value is required!\n"; + } + html += "\n"; + html += "
\n"; + html += "
\n"; + } + } - //number field - if (field.type == 'number') { - if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { - html += "
' + field.label + '' + "\n"; - html += "
\n"; - // Use 'text' rather than 'number' so that our integer directive works correctly - html += "A value is required!\n"; - } - if (field.integer) { - html += "Must be an integer value\n"; - } - if (field.min || field.max) { - html += "Must be in range " + field.min + " to " + - field.max + "\n"; - } - html += "\n"; - html += "
\n"; - html += "
\n"; - } - } + //number field + if (field.type == 'number') { + if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { + html += "
' + field.label + '' + "\n"; + html += "
\n"; + // Use 'text' rather than 'number' so that our integer directive works correctly + html += "A value is required!\n"; + } + if (field.integer) { + html += "Must be an integer value\n"; + } + if (field.min || field.max) { + html += "Must be in range " + field.min + " to " + + field.max + "\n"; + } + html += "\n"; + html += "
\n"; + html += "
\n"; + } + } - //checkbox - if (field.type == 'checkbox') { - if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { - html += "
\n"; - html += "
\n"; - } - } + html += "
\n"; + html += "
\n"; + } + } - if (field.type == 'hidden') { - if ( (options.mode == 'edit' && field.includeOnEdit) || - (options.mode == 'add' && field.includeOnAdd) ) { - html += ""; - } - } + if (field.type == 'hidden') { + if ( (options.mode == 'edit' && field.includeOnEdit) || + (options.mode == 'add' && field.includeOnAdd) ) { + html += ""; + } + } - //lookup type fields - if (field.type == 'lookup') { - html += "
' + field.label + '' + "\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - html += "A value is required!\n"; - } - html += "Value not found\n"; - html += "\n"; - html += "
\n"; - html += "
\n"; - } + //lookup type fields + if (field.type == 'lookup') { + html += "
' + field.label + '' + "\n"; + html += "
\n"; + html += "
\n"; + html += "\n"; + html += "A value is required!\n"; + } + html += "Value not found\n"; + html += "\n"; + html += "
\n"; + html += "
\n"; + } - return html; + return html; }, build: function(options) { @@ -406,91 +419,102 @@ angular.module('FormGenerator', ['GeneratorHelpers']) html += "\n\n
\n"; } - // Start the well - if ( this.has('well') ) { + if (this.form.fieldsAsHeader) { html += "
\n"; - } - - html += "
' + "\n"; - - html += "
{{ flashMessage }}
\n"; - - var field; - if (this.form.twoColumns) { - html += "
\n"; - html += "
\n"; + html += "\n"; for (var fld in this.form.fields) { field = this.form.fields[fld]; - if (field.column == 1) { - html += this.buildField(fld, field, options); - } + html += this.headerField(fld, field, options); } - html += "
\n"; - html += "
\n"; - for (var fld in this.form.fields) { - field = this.form.fields[fld]; - if (field.column == 2) { - html += this.buildField(fld, field, options); - } - } - html += "
\n"; - html += "
\n"; - } - else { - // original, single-column form - for (var fld in this.form.fields) { - var field = this.form.fields[fld]; - html += this.buildField(fld, field, options); - } - } - - //buttons - if (!this.modal) { - if (this.has('buttons')) { - html += "
\n"; - html += "
\n"; - } - for (var btn in this.form.buttons) { - var button = this.form.buttons[btn]; - //button - html += "
\n"; - html += "
\n"; - } - } - html += "
\n"; - - if ( this.has('well') ) { + html += "\n"; html += "
\n"; } - + else { + // Start the well + if ( this.has('well') ) { + html += "
\n"; + } + + html += "
' + "\n"; + html += "
{{ flashMessage }}
\n"; + + var field; + if (this.form.twoColumns) { + html += "
\n"; + html += "
\n"; + for (var fld in this.form.fields) { + field = this.form.fields[fld]; + if (field.column == 1) { + html += this.buildField(fld, field, options); + } + } + html += "
\n"; + html += "
\n"; + for (var fld in this.form.fields) { + field = this.form.fields[fld]; + if (field.column == 2) { + html += this.buildField(fld, field, options); + } + } + html += "
\n"; + html += "
\n"; + } + else { + // original, single-column form + for (var fld in this.form.fields) { + var field = this.form.fields[fld]; + html += this.buildField(fld, field, options); + } + } + + //buttons + if (!this.modal) { + if (this.has('buttons')) { + html += "
\n"; + html += "
\n"; + } + for (var btn in this.form.buttons) { + var button = this.form.buttons[btn]; + //button + html += "
\n"; + html += "
\n"; + } + html += "
\n"; + } + + if ( this.has('well') ) { + html += "
\n"; + } + } + if ((!this.modal && this.form.statusFields)) { // Add status fields section (used in Jobs form) html += "
\n"; @@ -500,6 +524,35 @@ angular.module('FormGenerator', ['GeneratorHelpers']) } html += "
\n"; } + + if ((!this.modal && this.form.items)) { + for (itm in this.form.items) { + html += "
\n"; + html += SearchWidget({ iterator: this.form.items[itm].iterator, template: this.form.items[itm], mini: false, label: 'Filter Events'}); + html += "
Viewing" + " \{\{ " + this.form.items[itm].iterator + "Page + 1 \}\} of " + + "\{\{ " + this.form.items[itm].iterator + "Count \}\}
\n"; + html += "
\n"; + html += "\n"; + html += "
' + "\n"; + for (var fld in this.form.items[itm].fields) { + var field = this.form.items[itm].fields[fld]; + html += this.buildField(fld, field, options); + } + html += "
\n"; + html += "\n"; + html += "
\n"; + } + } if ((!this.modal) && options.related && this.form.related) { html += this.buildCollections(); diff --git a/lib/ui/static/lib/ansible/generator-helpers.js b/lib/ui/static/lib/ansible/generator-helpers.js index 335a25deed..81d6292e49 100644 --- a/lib/ui/static/lib/ansible/generator-helpers.js +++ b/lib/ui/static/lib/ansible/generator-helpers.js @@ -16,9 +16,11 @@ angular.module('GeneratorHelpers', []) var iterator = params.iterator; var form = params.template; var useMini = params.mini; + var label = (params.label) ? params.label : null; var html= ''; html += "
\n"; + html += (label) ? "" : ""; html += "
\n"; html += "
\n"; html += "
\n"; - html += "\n"; + + html += "\n"; + html += "
\n"; - html += "
\n"; html += "
\n"; - html += "
\n"; + html += "
\n"; html += "
\n"; return html; diff --git a/lib/ui/static/lib/ansible/list-generator.js b/lib/ui/static/lib/ansible/list-generator.js index 6bc9670a44..5007c42570 100644 --- a/lib/ui/static/lib/ansible/list-generator.js +++ b/lib/ui/static/lib/ansible/list-generator.js @@ -140,7 +140,7 @@ angular.module('ListGenerator', ['GeneratorHelpers',]) if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) { if ( (list.basePaths == undefined) || (list.basePaths && list.basePaths.indexOf(base) > -1) ) { html += "