diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index ae200f1c4a..e8ff6012e4 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -114,7 +114,7 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa // Inject dynamic view var defaultUrl = GetBasePath('job_templates'), - form = JobTemplateForm, + form = JobTemplateForm(), generator = GenerateForm, master = {}, CloudCredentialList = {}, @@ -336,14 +336,14 @@ JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$lo function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, - CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange, JobStatusToolTip, FormatDate, + CredentialList, ProjectList, LookUpInit, GetBasePath, md5Setup, ParseTypeChange, JobStatusToolTip, FormatDate, Wait, Stream, Empty, Prompt, ParseVariableString, ToJSON) { ClearScope(); var defaultUrl = GetBasePath('job_templates'), generator = GenerateForm, - form = JobTemplateForm, + form = JobTemplateForm(), loadingFinishedCount = 0, base = $location.path().replace(/^\//, '').split('/')[0], master = {}, @@ -553,7 +553,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP Rest.setUrl(defaultUrl + ':id/'); Rest.get({ params: { id: id } }) .success(function (data) { - var fld, i, related, set; + var fld, i; LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name }); for (fld in form.fields) { if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) { @@ -586,16 +586,9 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP } $scope.url = data.url; - related = data.related; - for (set in form.related) { - if (related[set]) { - relatedSets[set] = { - url: related[set], - iterator: form.related[set].iterator - }; - } - } - + + relatedSets = form.relatedSets(data.related); + $scope.callback_url = data.related.callback; master.callback_url = $scope.callback_url; @@ -625,16 +618,17 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP field: 'project' }); - // Initialize related search functions. Doing it here to make sure relatedSets object is populated. RelatedSearchInit({ scope: $scope, form: form, relatedSets: relatedSets }); + RelatedPaginateInit({ scope: $scope, relatedSets: relatedSets }); + $scope.$emit('jobTemplateLoaded', data.related.cloud_credential); }) .error(function (data, status) { @@ -757,7 +751,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP JobTemplatesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', - 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords', + 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream', 'Empty', 'Prompt', 'ParseVariableString', 'ToJSON' ]; \ No newline at end of file diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 051a4d476b..77902099c6 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -95,7 +95,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea scope: queued_scope, list: QueuedJobsList, id: 'queued-jobs', - url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting$or__status=new' + url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting&or__status=new' }); scheduled_scope = $scope.$new(); LoadScope({ @@ -179,7 +179,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea GetChoices({ scope: $scope, - url: GetBasePath('jobs'), + url: GetBasePath('unified_jobs'), field: 'status', variable: 'status_choices', callback: 'choicesReady' @@ -187,7 +187,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea GetChoices({ scope: $scope, - url: '/static/sample/data/types/data.json', //GetBasePath('jobs') + url: GetBasePath('unified_jobs'), field: 'type', variable: 'type_choices', callback: 'choicesReady' diff --git a/awx/ui/static/js/forms/JobTemplates.js b/awx/ui/static/js/forms/JobTemplates.js index eac75d070e..97782b1cf2 100644 --- a/awx/ui/static/js/forms/JobTemplates.js +++ b/awx/ui/static/js/forms/JobTemplates.js @@ -4,11 +4,16 @@ * JobTemplates.js * Form definition for Job Template model * + * To get the JobTemplateForm object: JobTemplateForm(); * */ -angular.module('JobTemplateFormDefinition', []) - .value('JobTemplateForm', { - + +'use strict'; + +angular.module('JobTemplateFormDefinition', ['SchedulesListDefinition', 'CompletedJobsDefinition']) + + .value ('JobTemplateFormObject', { + addTitle: 'Create Job Templates', editTitle: '{{ name }}', name: 'job_templates', @@ -283,119 +288,44 @@ angular.module('JobTemplateFormDefinition', []) related: { - jobs: { - type: 'collection', - title: 'Jobs', - iterator: 'job', - index: false, - open: false, - - actions: { - reset: { - dataPlacement: 'top', - icon: "icon-undo", - mode: 'all', - 'class': 'btn-xs btn-primary', - awToolTip: "Reset the search filter", - ngClick: "resetSearch('job')", - iconSize: 'large' - } - }, - - fields: { - id: { - label: 'Job ID', - key: true, - desc: true, - searchType: 'int' - }, - created: { - label: 'Date', - link: false, - searchable: false - }, - status: { - label: 'Status', - "class": 'job-{{ job.status }}', - searchType: 'select', - linkTo: "{{}} job.statusLinkTo }}", - searchOptions: [ - { name: "new", value: "new" }, - { name: "waiting", value: "waiting" }, - { name: "pending", value: "pending" }, - { name: "running", value: "running" }, - { name: "successful", value: "successful" }, - { name: "error", value: "error" }, - { name: "failed", value: "failed" }, - { name: "canceled", value: "canceled" } - ], - badgeIcon: 'fa icon-job-{{ job.status }}', - badgePlacement: 'left', - badgeToolTip: "{{ job.statusBadgeToolTip }}", - badgeTipPlacement: 'top', - badgeNgHref: "{{ job.statusLinkTo }}", - awToolTip: "{{ job.statusBadgeToolTip }}", - dataPlacement: 'top' - } - }, - - fieldActions: { - edit: { - label: 'View', - ngClick: "edit('jobs', job.id, job.name)", - icon: 'icon-zoom-in' - } - } + schedules: { + include: "SchedulesList" }, - - schedules: { - type: 'collection', - title: 'Schedules', - iterator: 'schedule', - index: true, - open: false, - - fields: { - name: { - key: true, - label: 'Name' - }, - dtstart: { - label: 'Start' - }, - dtend: { - label: 'End' - } - }, - - actions: { - add: { - mode: 'all', - ngClick: 'addSchedule()', - awToolTip: 'Add a new schedule' - } - }, - - fieldActions: { - edit: { - label: 'Edit', - ngClick: "editSchedule(schedule.id)", - icon: 'icon-edit', - awToolTip: 'Edit schedule', - dataPlacement: 'top' - }, - - "delete": { - label: 'Delete', - ngClick: "deleteSchedule(schedule.id)", - icon: 'icon-trash', - awToolTip: 'Delete schedule', - dataPlacement: 'top' - } - } - + completed_jobs: { + include: "CompletedJobsList" } - } - - }); //InventoryForm + }, + relatedSets: function(urls) { + return { + completed_jobs: { + iterator: 'completed_job', + url: urls.jobs + }, + schedules: { + iterator: 'schedule', + url: urls.schedules + } + }; + } + }) + + .factory('JobTemplateForm', ['JobTemplateFormObject', 'SchedulesList', 'CompletedJobsList', + function(JobTemplateFormObject, SchedulesList, CompletedJobsList) { + return function() { + var itm; + + for (itm in JobTemplateFormObject.related) { + if (JobTemplateFormObject.related[itm].include === "SchedulesList") { + JobTemplateFormObject.related[itm] = SchedulesList; + JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list + } + if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") { + JobTemplateFormObject.related[itm] = CompletedJobsList; + JobTemplateFormObject.related[itm].generateList = true; + } + } + + return JobTemplateFormObject; + }; + }]); diff --git a/awx/ui/static/js/helpers/Jobs.js b/awx/ui/static/js/helpers/Jobs.js index 0bc829f527..9daafff92e 100644 --- a/awx/ui/static/js/helpers/Jobs.js +++ b/awx/ui/static/js/helpers/Jobs.js @@ -204,9 +204,6 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job scope.iterator = list.iterator; - - // The following bits probably don't belong here once the API is available. - if (scope.removePostRefresh) { scope.removePostRefresh(); } diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index 53547b6a78..01b8a521ec 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -10,11 +10,11 @@ 'use strict'; -angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) +angular.module('FormGenerator', ['GeneratorHelpers', 'Utilities', 'ListGenerator']) -.factory('GenerateForm', ['$rootScope', '$location', '$cookieStore', '$compile', 'SearchWidget', 'PaginateWidget', 'Attr', +.factory('GenerateForm', ['$rootScope', '$location', '$compile', 'GenerateList', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column', 'NavigationLink', 'HelpCollapse', 'Button', 'DropDown', 'Empty', 'SelectIcon', 'Store', - function ($rootScope, $location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, + function ($rootScope, $location, $compile, GenerateList, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse, Button, DropDown, Empty, SelectIcon, Store) { return { @@ -1338,148 +1338,158 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) // Create TB accordians with imbedded lists for related collections // Should not be called directly. Called internally by build(). // - var idx = 1, - form = this.form, - html, act, fAction, fld, itm, action, cnt, base; + var form = this.form, + html = '', + itm, collection; - if (options.collapseAlreadyStarted) { - // A collapse is already started for 'Properties' - html = ''; - } - else { - html = "
\n"; + if (!options.collapseAlreadyStarted) { + html = "
\n"; } for (itm in form.related) { - if (form.related[itm].type === 'collection') { - html += "

" + form.related[itm].title + "

\n"; - html += "
\n"; - - if (form.related[itm].instructions) { - html += "
\n"; - html += "\n"; - html += "Hint: " + form.related[itm].instructions + "\n"; - html += "
\n"; - } - - //html += "
\n"; - html += "
\n"; - - html += SearchWidget({ - iterator: form.related[itm].iterator, - template: form.related[itm], - mini: true - }); - - html += "
\n"; - html += "
\n"; - - for (act in form.related[itm].actions) { - action = form.related[itm].actions[act]; - html += this.button({ - btn: action, - action: act, - toolbar: true - }); - } - - html += "
\n"; - html += "
\n"; - html += "
\n"; - - // Start the list - html += "
\n"; - html += "\n"; - html += "\n"; - html += "\n"; - html += (form.related[itm].index === undefined || form.related[itm].index !== false) ? "\n" : ""; - for (fld in form.related[itm].fields) { - html += "\n"; - } - html += "\n"; - html += "\n"; - html += ""; - html += "\n"; - - html += "\n"; - if (form.related[itm].index === undefined || form.related[itm].index !== false) { - html += "\n"; - } - cnt = 1; - base = (form.related[itm].base) ? form.related[itm].base : itm; - base = base.replace(/^\//, ''); - for (fld in form.related[itm].fields) { - cnt++; - html += Column({ - list: form.related[itm], - fld: fld, - options: options, - base: base - }); - } - - // Row level actions - html += ""; - html += "\n"; - - // Message for when a related collection is empty - html += "\n"; - html += "\n"; - html += "\n"; - - // Message for loading - html += "\n"; - html += "\n"; - html += "\n"; - - // End List - html += "\n"; - html += "
#" + - form.related[itm].fields[fld].label; - html += " Actions
{{ $index + ((" + form.related[itm].iterator + "_page - 1) * " + - form.related[itm].iterator + "_page_size) + 1 }}."; - for (act in form.related[itm].fieldActions) { - fAction = form.related[itm].fieldActions[act]; - html += " " + fAction.label + "": ""; - html += ""; - } - html += "
No records matched your search.
Loading...
\n"; - //html += "
\n"; // close well - html += "
\n"; // close list-wrapper div - - html += PaginateWidget({ - set: itm, - iterator: form.related[itm].iterator, - mini: true - }); - - // End Accordion - html += "
\n"; // accordion inner - - idx++; + collection = form.related[itm]; + html += "

" + (collection.title || collection.editTitle) + "

\n"; + html += "
\n"; + if (collection.generateList) { + html += GenerateList.buildHTML(collection, { mode: 'edit', breadCrumbs: false }); } + else { + html += this.GenerateColleciton({ form: form, related: itm }, options); + } + html += "
\n"; // accordion inner + } + + if (!options.collapseAlreadyStarted) { + html += "
\n"; // accordion body } - html += "
\n"; // accordion body - html += "\n"; + //console.log(html); + + return html; + }, + + GenerateColleciton: function(params, options) { + var html = '', + form = params.form, + itm = params.related, + collection = form.related[itm], + act, action, fld, cnt, base, fAction; + + if (collection.instructions) { + html += "
\n"; + html += "\n"; + html += "Hint: " + collection.instructions + "\n"; + html += "
\n"; + } + + //html += "
\n"; + html += "
\n"; + + html += SearchWidget({ + iterator: collection.iterator, + template: collection, + mini: true + }); + + html += "
\n"; + html += "
\n"; + + for (act in collection.actions) { + action = collection.actions[act]; + html += this.button({ + btn: action, + action: act, + toolbar: true + }); + } + + html += "
\n"; + html += "
\n"; + html += "
\n"; + + // Start the list + html += "
\n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += (collection.index === undefined || collection.index !== false) ? "\n" : ""; + for (fld in collection.fields) { + html += "\n"; + } + html += "\n"; + html += "\n"; + html += ""; + html += "\n"; + + html += "\n"; + if (collection.index === undefined || collection.index !== false) { + html += "\n"; + } + cnt = 1; + base = (collection.base) ? collection.base : itm; + base = base.replace(/^\//, ''); + for (fld in collection.fields) { + cnt++; + html += Column({ + list: collection, + fld: fld, + options: options, + base: base + }); + } + + // Row level actions + html += ""; + html += "\n"; + + // Message for when a related collection is empty + html += "\n"; + html += "\n"; + html += "\n"; + + // Message for loading + html += "\n"; + html += "\n"; + html += "\n"; + + // End List + html += "\n"; + html += "
#" + + collection.fields[fld].label; + html += " Actions
{{ $index + ((" + collection.iterator + "_page - 1) * " + + collection.iterator + "_page_size) + 1 }}."; + for (act in collection.fieldActions) { + fAction = collection.fieldActions[act]; + html += " " + fAction.label + "": ""; + html += ""; + } + html += "
No records matched your search.
Loading...
\n"; + //html += "
\n"; // close well + html += "
\n"; // close list-wrapper div + + html += PaginateWidget({ + set: itm, + iterator: collection.iterator, + mini: true + }); return html; } }; diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 728fac30f5..bef9b98a66 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -34,6 +34,11 @@ angular.module('ListGenerator', ['GeneratorHelpers']) button: Button, + buildHTML: function(list, options) { + this.setList(list); + return this.build(options); + }, + inject: function (list, options) { // options.mode = one of edit, select or lookup // @@ -44,7 +49,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) // // hdr: // - // Inject into a custom element using options.id: <'.selector'> + // Inject into a custom element using options.id: // Control breadcrumb creation with options.breadCrumbs: // var element;