Form generator can now call the list generator when creating related lists. Job Template page is now using this method. We can now inject CompletedJobs and Schedules list objects on any page with full jobs page or schedule page functionality.

This commit is contained in:
Chris Houseknecht
2014-03-29 17:46:39 -04:00
parent fe5d154d76
commit 8217544376
6 changed files with 213 additions and 277 deletions

View File

@@ -114,7 +114,7 @@ function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routePa
// Inject dynamic view // Inject dynamic view
var defaultUrl = GetBasePath('job_templates'), var defaultUrl = GetBasePath('job_templates'),
form = JobTemplateForm, form = JobTemplateForm(),
generator = GenerateForm, generator = GenerateForm,
master = {}, master = {},
CloudCredentialList = {}, CloudCredentialList = {},
@@ -336,14 +336,14 @@ JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$lo
function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, GenerateForm, Rest, function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, GenerateForm, Rest,
Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, 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) { Wait, Stream, Empty, Prompt, ParseVariableString, ToJSON) {
ClearScope(); ClearScope();
var defaultUrl = GetBasePath('job_templates'), var defaultUrl = GetBasePath('job_templates'),
generator = GenerateForm, generator = GenerateForm,
form = JobTemplateForm, form = JobTemplateForm(),
loadingFinishedCount = 0, loadingFinishedCount = 0,
base = $location.path().replace(/^\//, '').split('/')[0], base = $location.path().replace(/^\//, '').split('/')[0],
master = {}, master = {},
@@ -553,7 +553,7 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
Rest.setUrl(defaultUrl + ':id/'); Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: { id: id } }) Rest.get({ params: { id: id } })
.success(function (data) { .success(function (data) {
var fld, i, related, set; var fld, i;
LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name }); LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name });
for (fld in form.fields) { for (fld in form.fields) {
if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) { if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) {
@@ -586,15 +586,8 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
} }
$scope.url = data.url; $scope.url = data.url;
related = data.related;
for (set in form.related) { relatedSets = form.relatedSets(data.related);
if (related[set]) {
relatedSets[set] = {
url: related[set],
iterator: form.related[set].iterator
};
}
}
$scope.callback_url = data.related.callback; $scope.callback_url = data.related.callback;
master.callback_url = $scope.callback_url; master.callback_url = $scope.callback_url;
@@ -625,16 +618,17 @@ function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $routeP
field: 'project' field: 'project'
}); });
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
RelatedSearchInit({ RelatedSearchInit({
scope: $scope, scope: $scope,
form: form, form: form,
relatedSets: relatedSets relatedSets: relatedSets
}); });
RelatedPaginateInit({ RelatedPaginateInit({
scope: $scope, scope: $scope,
relatedSets: relatedSets relatedSets: relatedSets
}); });
$scope.$emit('jobTemplateLoaded', data.related.cloud_credential); $scope.$emit('jobTemplateLoaded', data.related.cloud_credential);
}) })
.error(function (data, status) { .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', JobTemplatesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', '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', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream', 'Empty', 'Prompt',
'ParseVariableString', 'ToJSON' 'ParseVariableString', 'ToJSON'
]; ];

View File

@@ -95,7 +95,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
scope: queued_scope, scope: queued_scope,
list: QueuedJobsList, list: QueuedJobsList,
id: 'queued-jobs', 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(); scheduled_scope = $scope.$new();
LoadScope({ LoadScope({
@@ -179,7 +179,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
GetChoices({ GetChoices({
scope: $scope, scope: $scope,
url: GetBasePath('jobs'), url: GetBasePath('unified_jobs'),
field: 'status', field: 'status',
variable: 'status_choices', variable: 'status_choices',
callback: 'choicesReady' callback: 'choicesReady'
@@ -187,7 +187,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
GetChoices({ GetChoices({
scope: $scope, scope: $scope,
url: '/static/sample/data/types/data.json', //GetBasePath('jobs') url: GetBasePath('unified_jobs'),
field: 'type', field: 'type',
variable: 'type_choices', variable: 'type_choices',
callback: 'choicesReady' callback: 'choicesReady'

View File

@@ -4,10 +4,15 @@
* JobTemplates.js * JobTemplates.js
* Form definition for Job Template model * 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', addTitle: 'Create Job Templates',
editTitle: '{{ name }}', editTitle: '{{ name }}',
@@ -283,119 +288,44 @@ angular.module('JobTemplateFormDefinition', [])
related: { 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: { schedules: {
type: 'collection', include: "SchedulesList"
title: 'Schedules', },
completed_jobs: {
include: "CompletedJobsList"
}
},
relatedSets: function(urls) {
return {
completed_jobs: {
iterator: 'completed_job',
url: urls.jobs
},
schedules: {
iterator: 'schedule', iterator: 'schedule',
index: true, url: urls.schedules
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: { .factory('JobTemplateForm', ['JobTemplateFormObject', 'SchedulesList', 'CompletedJobsList',
edit: { function(JobTemplateFormObject, SchedulesList, CompletedJobsList) {
label: 'Edit', return function() {
ngClick: "editSchedule(schedule.id)", var itm;
icon: 'icon-edit',
awToolTip: 'Edit schedule',
dataPlacement: 'top'
},
"delete": { for (itm in JobTemplateFormObject.related) {
label: 'Delete', if (JobTemplateFormObject.related[itm].include === "SchedulesList") {
ngClick: "deleteSchedule(schedule.id)", JobTemplateFormObject.related[itm] = SchedulesList;
icon: 'icon-trash', JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
awToolTip: 'Delete schedule', }
dataPlacement: 'top' if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") {
JobTemplateFormObject.related[itm] = CompletedJobsList;
JobTemplateFormObject.related[itm].generateList = true;
} }
} }
} return JobTemplateFormObject;
} };
}]);
}); //InventoryForm

View File

@@ -204,9 +204,6 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
scope.iterator = list.iterator; scope.iterator = list.iterator;
// The following bits probably don't belong here once the API is available.
if (scope.removePostRefresh) { if (scope.removePostRefresh) {
scope.removePostRefresh(); scope.removePostRefresh();
} }

View File

@@ -10,11 +10,11 @@
'use strict'; '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', '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) { HelpCollapse, Button, DropDown, Empty, SelectIcon, Store) {
return { return {
@@ -1338,27 +1338,47 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
// Create TB accordians with imbedded lists for related collections // Create TB accordians with imbedded lists for related collections
// Should not be called directly. Called internally by build(). // Should not be called directly. Called internally by build().
// //
var idx = 1, var form = this.form,
form = this.form, html = '',
html, act, fAction, fld, itm, action, cnt, base; itm, collection;
if (options.collapseAlreadyStarted) { if (!options.collapseAlreadyStarted) {
// A collapse is already started for 'Properties' html = "<div id=\"" + this.form.name + "-collapse-1\" class=\"jqui-accordion\">\n";
html = '';
}
else {
html = "<div id=\"" + this.form.name + "-collapse-" + idx + "\" class=\"jqui-accordion\">\n";
} }
for (itm in form.related) { for (itm in form.related) {
if (form.related[itm].type === 'collection') { collection = form.related[itm];
html += "<h3 class=\"" + itm + "_collapse\">" + form.related[itm].title + "</h3>\n"; html += "<h3 class=\"" + itm + "_collapse\">" + (collection.title || collection.editTitle) + "</h3>\n";
html += "<div>\n"; html += "<div>\n";
if (collection.generateList) {
html += GenerateList.buildHTML(collection, { mode: 'edit', breadCrumbs: false });
}
else {
html += this.GenerateColleciton({ form: form, related: itm }, options);
}
html += "</div>\n"; // accordion inner
}
if (form.related[itm].instructions) { if (!options.collapseAlreadyStarted) {
html += "</div>\n"; // accordion body
}
//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 += "<div class=\"alert alert-info alert-block\">\n"; html += "<div class=\"alert alert-info alert-block\">\n";
html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">&times;</button>\n"; html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">&times;</button>\n";
html += "<strong>Hint: </strong>" + form.related[itm].instructions + "\n"; html += "<strong>Hint: </strong>" + collection.instructions + "\n";
html += "</div>\n"; html += "</div>\n";
} }
@@ -1366,16 +1386,16 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
html += "<div class=\"row\">\n"; html += "<div class=\"row\">\n";
html += SearchWidget({ html += SearchWidget({
iterator: form.related[itm].iterator, iterator: collection.iterator,
template: form.related[itm], template: collection,
mini: true mini: true
}); });
html += "<div class=\"col-lg-8\">\n"; html += "<div class=\"col-lg-8\">\n";
html += "<div class=\"list-actions\">\n"; html += "<div class=\"list-actions\">\n";
for (act in form.related[itm].actions) { for (act in collection.actions) {
action = form.related[itm].actions[act]; action = collection.actions[act];
html += this.button({ html += this.button({
btn: action, btn: action,
action: act, action: act,
@@ -1389,17 +1409,17 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
// Start the list // Start the list
html += "<div class=\"list-wrapper\">\n"; html += "<div class=\"list-wrapper\">\n";
html += "<table id=\"" + itm + "_table" + "\" class=\"" + form.related[itm].iterator + " table table-condensed table-hover\">\n"; html += "<table id=\"" + itm + "_table" + "\" class=\"" + collection.iterator + " table table-condensed table-hover\">\n";
html += "<thead>\n"; html += "<thead>\n";
html += "<tr>\n"; html += "<tr>\n";
html += (form.related[itm].index === undefined || form.related[itm].index !== false) ? "<th class=\"col-xs-1\">#</th>\n" : ""; html += (collection.index === undefined || collection.index !== false) ? "<th class=\"col-xs-1\">#</th>\n" : "";
for (fld in form.related[itm].fields) { for (fld in collection.fields) {
html += "<th class=\"list-header\" id=\"" + form.related[itm].iterator + '-' + fld + "-header\" " + html += "<th class=\"list-header\" id=\"" + collection.iterator + '-' + fld + "-header\" " +
"ng-click=\"sort('" + form.related[itm].iterator + "', '" + fld + "')\">" + "ng-click=\"sort('" + collection.iterator + "', '" + fld + "')\">" +
form.related[itm].fields[fld].label; collection.fields[fld].label;
html += " <i class=\""; html += " <i class=\"";
if (form.related[itm].fields[fld].key) { if (collection.fields[fld].key) {
if (form.related[itm].fields[fld].desc) { if (collection.fields[fld].desc) {
html += "fa fa-sort-down"; html += "fa fa-sort-down";
} else { } else {
html += "fa fa-sort-up"; html += "fa fa-sort-up";
@@ -1414,18 +1434,18 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
html += "</thead>"; html += "</thead>";
html += "<tbody>\n"; html += "<tbody>\n";
html += "<tr ng-repeat=\"" + form.related[itm].iterator + " in " + itm + "\" >\n"; html += "<tr ng-repeat=\"" + collection.iterator + " in " + itm + "\" >\n";
if (form.related[itm].index === undefined || form.related[itm].index !== false) { if (collection.index === undefined || collection.index !== false) {
html += "<td>{{ $index + ((" + form.related[itm].iterator + "_page - 1) * " + html += "<td>{{ $index + ((" + collection.iterator + "_page - 1) * " +
form.related[itm].iterator + "_page_size) + 1 }}.</td>\n"; collection.iterator + "_page_size) + 1 }}.</td>\n";
} }
cnt = 1; cnt = 1;
base = (form.related[itm].base) ? form.related[itm].base : itm; base = (collection.base) ? collection.base : itm;
base = base.replace(/^\//, ''); base = base.replace(/^\//, '');
for (fld in form.related[itm].fields) { for (fld in collection.fields) {
cnt++; cnt++;
html += Column({ html += Column({
list: form.related[itm], list: collection,
fld: fld, fld: fld,
options: options, options: options,
base: base base: base
@@ -1434,8 +1454,8 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
// Row level actions // Row level actions
html += "<td class=\"actions\">"; html += "<td class=\"actions\">";
for (act in form.related[itm].fieldActions) { for (act in collection.fieldActions) {
fAction = form.related[itm].fieldActions[act]; fAction = collection.fieldActions[act];
html += "<a "; html += "<a ";
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : ""; html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
html += (fAction.ngClick) ? this.attr(fAction, 'ngClick') : ""; html += (fAction.ngClick) ? this.attr(fAction, 'ngClick') : "";
@@ -1450,12 +1470,12 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
html += "</tr>\n"; html += "</tr>\n";
// Message for when a related collection is empty // Message for when a related collection is empty
html += "<tr class=\"loading-info\" ng-show=\"" + form.related[itm].iterator + "Loading == false && (" + itm + " == null || " + itm + ".length == 0)\">\n"; html += "<tr class=\"loading-info\" ng-show=\"" + collection.iterator + "Loading == false && (" + itm + " == null || " + itm + ".length == 0)\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">No records matched your search.</div></td>\n"; html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">No records matched your search.</div></td>\n";
html += "</tr>\n"; html += "</tr>\n";
// Message for loading // Message for loading
html += "<tr ng-show=\"" + form.related[itm].iterator + "Loading == true\">\n"; html += "<tr ng-show=\"" + collection.iterator + "Loading == true\">\n";
html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">Loading...</div></td>\n"; html += "<td colspan=\"" + cnt + "\"><div class=\"loading-info\">Loading...</div></td>\n";
html += "</tr>\n"; html += "</tr>\n";
@@ -1467,19 +1487,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
html += PaginateWidget({ html += PaginateWidget({
set: itm, set: itm,
iterator: form.related[itm].iterator, iterator: collection.iterator,
mini: true mini: true
}); });
// End Accordion
html += "</div>\n"; // accordion inner
idx++;
}
}
html += "</div>\n"; // accordion body
html += "</div>\n";
return html; return html;
} }
}; };

View File

@@ -34,6 +34,11 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
button: Button, button: Button,
buildHTML: function(list, options) {
this.setList(list);
return this.build(options);
},
inject: function (list, options) { inject: function (list, options) {
// options.mode = one of edit, select or lookup // options.mode = one of edit, select or lookup
// //
@@ -44,7 +49,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
// //
// hdr: <lookup dialog header> // hdr: <lookup dialog header>
// //
// Inject into a custom element using options.id: <'.selector'> // Inject into a custom element using options.id: <element id attribute value>
// Control breadcrumb creation with options.breadCrumbs: <true | false> // Control breadcrumb creation with options.breadCrumbs: <true | false>
// //
var element; var element;