diff --git a/awx/ui/client/src/projects/main.js b/awx/ui/client/src/projects/main.js index 37461d3d0a..e5534b4a79 100644 --- a/awx/ui/client/src/projects/main.js +++ b/awx/ui/client/src/projects/main.js @@ -13,6 +13,7 @@ import { N_ } from '../i18n'; import GetProjectPath from './factories/get-project-path.factory'; import GetProjectIcon from './factories/get-project-icon.factory'; import GetProjectToolTip from './factories/get-project-tool-tip.factory'; +import ProjectsTemplatesRoute from './projects-templates.route'; export default angular.module('Projects', []) @@ -24,9 +25,10 @@ angular.module('Projects', []) .factory('GetProjectToolTip', GetProjectToolTip) .factory('ProjectList', ProjectList) .factory('ProjectsForm', ProjectsForm) - .config(['$stateProvider', 'stateDefinitionsProvider', - function($stateProvider, stateDefinitionsProvider) { + .config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider', + function($stateProvider, stateDefinitionsProvider,$stateExtenderProvider) { let stateDefinitions = stateDefinitionsProvider.$get(); + let stateExtender = $stateExtenderProvider.$get(); var projectResolve = { CredentialTypes: ['Rest', '$stateParams', 'GetBasePath', 'ProcessErrors', (Rest, $stateParams, GetBasePath, ProcessErrors) => { @@ -45,12 +47,9 @@ angular.module('Projects', []) } ] }; - // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry - // see: stateDefinition.factory for usage documentation - $stateProvider.state({ - name: 'projects.**', - url: '/projects', - lazyLoad: () => stateDefinitions.generateTree({ + + function generateStateTree() { + let projectTree = stateDefinitions.generateTree({ parent: 'projects', // top-most node in the generated tree (will replace this state definition) modes: ['add', 'edit'], list: 'ProjectList', @@ -76,7 +75,25 @@ angular.module('Projects', []) add: projectResolve, edit: projectResolve } - }) - }); + }); + + return Promise.all([ + projectTree + ]).then((generated) => { + return { + states: _.reduce(generated, (result, definition) => { + return result.concat(definition.states); + }, [ + stateExtender.buildDefinition(ProjectsTemplatesRoute), + ]) + }; + }); + } + let stateTree = { + name: 'projects.**', + url: '/projects', + lazyLoad: () => generateStateTree() + }; + $stateProvider.state(stateTree); } ]); diff --git a/awx/ui/client/src/projects/projects-templates.route.js b/awx/ui/client/src/projects/projects-templates.route.js new file mode 100644 index 0000000000..0015c9661e --- /dev/null +++ b/awx/ui/client/src/projects/projects-templates.route.js @@ -0,0 +1,56 @@ +import { N_ } from '../i18n'; + +export default { + url: "/templates", + name: 'projects.edit.templates', + params: { + template_search: { + value: { + page_size: '20', + project: '', + order_by: "-id" + } + } + }, + ncyBreadcrumb: { + label: N_("JOB TEMPLATES") + }, + views: { + 'related': { + templateProvider: function(FormDefinition, GenerateForm) { + let html = GenerateForm.buildCollection({ + mode: 'edit', + related: 'templates', + form: typeof(FormDefinition) === 'function' ? + FormDefinition() : FormDefinition + }); + return html; + }, + controller: 'TemplatesListController' + } + }, + resolve: { + ListDefinition: ['TemplateList', '$transition$', (TemplateList, $transition$) => { + let id = $transition$.params().project_id; + TemplateList.actions.add.ngClick = `$state.go('templates.addJobTemplate', {project_id: ${id}})`; + TemplateList.basePath = 'job_templates'; + return TemplateList; + }], + Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope', + (list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => { + // allow related list definitions to use interpolated $rootScope / $stateParams in basePath field + let path, interpolator; + if (GetBasePath(list.basePath)) { + path = GetBasePath(list.basePath); + } else { + interpolator = $interpolate(list.basePath); + path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams }); + } + let project_id = $stateParams.project_id; + $stateParams[`${list.iterator}_search`].project = project_id; + path = GetBasePath('job_templates'); + return qs.search(path, $stateParams[`${list.iterator}_search`]); + } + ] + } +}; diff --git a/awx/ui/client/src/projects/projects.form.js b/awx/ui/client/src/projects/projects.form.js index 3223dbf104..a9d6762860 100644 --- a/awx/ui/client/src/projects/projects.form.js +++ b/awx/ui/client/src/projects/projects.form.js @@ -10,7 +10,8 @@ * @description This form is for adding/editing projects */ -export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) { +export default ['i18n', 'NotificationsList', 'TemplateList', + function(i18n, NotificationsList, TemplateList) { return function() { var projectsFormObj = { addTitle: i18n._('NEW PROJECT'), @@ -267,7 +268,10 @@ export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) { }, notifications: { include: "NotificationsList", - } + }, + templates: { + include: "TemplateList", + }, } }; @@ -275,6 +279,11 @@ export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) { var itm; for (itm in projectsFormObj.related) { + if (projectsFormObj.related[itm].include === "TemplateList") { + projectsFormObj.related[itm] = _.clone(TemplateList); + projectsFormObj.related[itm].title = i18n._('Job Templates'); + projectsFormObj.related[itm].skipGenerator = true; + } if (projectsFormObj.related[itm].include === "NotificationsList") { projectsFormObj.related[itm] = NotificationsList; projectsFormObj.related[itm].generateList = true; // tell form generator to call list generator and inject a list diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index d90cac087a..6444370b3b 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -1950,29 +1950,31 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat if (collection.fieldActions) { html += "