diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js
index 322aaec3d6..b024b8ad3e 100644
--- a/awx/ui/client/lib/services/base-string.service.js
+++ b/awx/ui/client/lib/services/base-string.service.js
@@ -73,7 +73,7 @@ function BaseStringService (namespace) {
this.deleteResource = {
HEADER: t.s('Delete'),
USED_BY: resourceType => t.s('The {{ resourceType }} is currently being used by other resources.', { resourceType }),
- UNAVAILABLE: resourceType => t.s('Deleting this {{ resourceType }} will make the following resources unavailable.', { resourceType }),
+ UNAVAILABLE: resourceType => t.s('Deleting this {{ resourceType }} will make the following resources unavailable.', { resourceType }),
CONFIRM: resourceType => t.s('Are you sure you want to delete this {{ resourceType }}?', { resourceType })
};
diff --git a/awx/ui/client/lib/theme/index.less b/awx/ui/client/lib/theme/index.less
index a0f7738272..caf02e2882 100644
--- a/awx/ui/client/lib/theme/index.less
+++ b/awx/ui/client/lib/theme/index.less
@@ -126,6 +126,7 @@
@import '../../src/templates/survey-maker/survey-maker.block.less';
@import '../../src/templates/survey-maker/shared/survey-controls.block.less';
@import '../../src/templates/survey-maker/survey-maker.block.less';
+@import '../../src/templates/workflows/workflow.block.less';
@import '../../src/templates/workflows/workflow-chart/workflow-chart.block.less';
@import '../../src/templates/workflows/workflow-controls/workflow-controls.block.less';
@import '../../src/templates/workflows/workflow-maker/workflow-maker.block.less';
diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js
index 106bc3642d..574e8e8342 100644
--- a/awx/ui/client/src/shared/form-generator.js
+++ b/awx/ui/client/src/shared/form-generator.js
@@ -545,6 +545,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "' ";
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
html += " class='ScheduleToggle-switch' ng-click='" + field.ngClick + "' translate>" + i18n._("OFF") + "";
+ } else if (field.type === 'html') {
+ html += field.html;
}
return html;
},
diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js
index dc83056fe7..5a77520a5c 100644
--- a/awx/ui/client/src/templates/main.js
+++ b/awx/ui/client/src/templates/main.js
@@ -377,6 +377,15 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
response.status
});
});
+ }],
+ workflowLaunch: ['$stateParams', 'WorkflowJobTemplateModel',
+ function($stateParams, WorkflowJobTemplate) {
+ let workflowJobTemplate = new WorkflowJobTemplate();
+
+ return workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id)
+ .then(({data}) => {
+ return data;
+ });
}]
}
}
diff --git a/awx/ui/client/src/templates/workflows.form.js b/awx/ui/client/src/templates/workflows.form.js
index ac2a6a7d5a..7be0a70472 100644
--- a/awx/ui/client/src/templates/workflows.form.js
+++ b/awx/ui/client/src/templates/workflows.form.js
@@ -27,6 +27,16 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
detailsClick: "$state.go('templates.editWorkflowJobTemplate')",
include: ['/static/partials/survey-maker-modal.html'],
+ headerFields: {
+ missingTemplates: {
+ type: 'html',
+ html: `
+ ` +
+ i18n._("Missing Job Templates found in the Workflow Editor") +
+ `
`
+ }
+ },
+
fields: {
name: {
label: i18n._('Name'),
diff --git a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js
index a67cad1156..4d1bc567a7 100644
--- a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js
+++ b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js
@@ -10,10 +10,15 @@ export default [
'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2',
'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification',
'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'i18n',
+ 'workflowLaunch', '$transitions', 'WorkflowJobTemplateModel',
function($scope, $stateParams, WorkflowForm, GenerateForm, Alert,
- ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
- ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
- TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n) {
+ ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
+ ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
+ TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n,
+ workflowLaunch, $transitions, WorkflowJobTemplate
+ ) {
+
+ $scope.missingTemplates = _.has(workflowLaunch, 'node_templates_missing') && workflowLaunch.node_templates_missing.length > 0 ? true : false;
$scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) {
if (val === false) {
@@ -21,6 +26,25 @@ export default [
}
});
+ const criteriaObj = {
+ from: (state) => state.name === 'templates.editWorkflowJobTemplate.workflowMaker',
+ to: (state) => state.name === 'templates.editWorkflowJobTemplate'
+ };
+
+ $transitions.onSuccess(criteriaObj, function() {
+ if ($scope.missingTemplates) {
+ // Go out and check the new launch response to see if the user has fixed the
+ // missing node templates
+
+ let workflowJobTemplate = new WorkflowJobTemplate();
+
+ workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id)
+ .then(({data}) => {
+ $scope.missingTemplates = _.has(data, 'node_templates_missing') && data.node_templates_missing.length > 0 ? true : false;
+ });
+ }
+ });
+
// Inject dynamic view
let form = WorkflowForm(),
generator = GenerateForm,
@@ -30,119 +54,24 @@ export default [
$scope.parseType = 'yaml';
$scope.includeWorkflowMaker = false;
- function init() {
+ $scope.openWorkflowMaker = function() {
+ $state.go('.workflowMaker');
+ };
- // Select2-ify the lables input
- CreateSelect2({
- element:'#workflow_job_template_labels',
- multiple: true,
- addNew: true
- });
+ $scope.formSave = function () {
+ let fld, data = {};
+ $scope.invalid_survey = false;
- SurveyControllerInit({
- scope: $scope,
- parent_scope: $scope,
- id: id,
- templateType: 'workflow_job_template'
- });
-
- $scope.labelOptions = availableLabels
- .map((i) => ({label: i.name, value: i.id}));
-
- var opts = selectedLabels
- .map(i => ({id: i.id + "",
- test: i.name}));
-
- CreateSelect2({
- element:'#workflow_job_template_labels',
- multiple: true,
- addNew: true,
- opts: opts
- });
-
- $scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
- $scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
-
- $scope.workflow_job_template_obj = workflowJobTemplateData;
- $scope.name = workflowJobTemplateData.name;
- $scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
- let fld, i;
- for (fld in form.fields) {
- if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) {
- if (form.fields[fld].type === 'select') {
- if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
- for (i = 0; i < $scope[fld + '_options'].length; i++) {
- if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) {
- $scope[fld] = $scope[fld + '_options'][i];
- }
- }
- } else {
- $scope[fld] = workflowJobTemplateData[fld];
- }
- } else {
- $scope[fld] = workflowJobTemplateData[fld];
- if(!Empty(workflowJobTemplateData.summary_fields.survey)) {
- $scope.survey_exists = true;
- }
- }
- }
- if (fld === 'variables') {
- // Parse extra_vars, converting to YAML.
- $scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars);
-
- ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' });
- }
- if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) {
- $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
- workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
- }
- if (form.fields[fld].type === 'checkbox_group') {
- for(var j=0; j ({label: i.name, value: i.id}));
+
+ var opts = selectedLabels
+ .map(i => ({id: i.id + "",
+ test: i.name}));
+
+ CreateSelect2({
+ element:'#workflow_job_template_labels',
+ multiple: true,
+ addNew: true,
+ opts: opts
+ });
+
+ $scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
+ $scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
+
+ $scope.workflow_job_template_obj = workflowJobTemplateData;
+ $scope.name = workflowJobTemplateData.name;
+ $scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
+ let fld, i;
+ for (fld in form.fields) {
+ if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) {
+ if (form.fields[fld].type === 'select') {
+ if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
+ for (i = 0; i < $scope[fld + '_options'].length; i++) {
+ if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) {
+ $scope[fld] = $scope[fld + '_options'][i];
+ }
+ }
+ } else {
+ $scope[fld] = workflowJobTemplateData[fld];
+ }
+ } else {
+ $scope[fld] = workflowJobTemplateData[fld];
+ if(!Empty(workflowJobTemplateData.summary_fields.survey)) {
+ $scope.survey_exists = true;
+ }
+ }
+ }
+ if (fld === 'variables') {
+ // Parse extra_vars, converting to YAML.
+ $scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars);
+
+ ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' });
+ }
+ if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) {
+ $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
+ workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
+ }
+ }
+
+ if(workflowJobTemplateData.organization) {
+ OrgAdminLookup.checkForRoleLevelAdminAccess(workflowJobTemplateData.organization, 'workflow_admin_role')
+ .then(function(canEditOrg){
+ $scope.canEditOrg = canEditOrg;
+ });
+ }
+ else {
+ $scope.canEditOrg = true;
+ }
+
+ $scope.url = workflowJobTemplateData.url;
+ $scope.survey_enabled = workflowJobTemplateData.survey_enabled;
+
+ $scope.includeWorkflowMaker = true;
+
+ $scope.$on('SurveySaved', function() {
+ Wait('stop');
+ $scope.survey_exists = true;
+ $scope.invalid_survey = false;
+ });
}
];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
index 51c5f31e22..c27ecde1d7 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
@@ -76,15 +76,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
// Create the node
let sendableNodeData = {
unified_job_template: params.node.unifiedJobTemplate.id,
- credential: _.get(params, 'node.originalNodeObj.credential') || null
+ extra_data: {},
+ inventory: null,
+ job_type: null,
+ job_tags: null,
+ skip_tags: null,
+ limit: null,
+ diff_mode: null,
+ verbosity: null,
+ credential: null
};
if (_.has(params, 'node.promptData.extraVars')) {
if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) {
- if (!sendableNodeData.extra_data) {
- sendableNodeData.extra_data = {};
- }
-
const defaultVars = jsyaml.safeLoad(params.node.promptData.launchConf.defaults.extra_vars);
// Only include extra vars that differ from the template default vars
@@ -674,11 +678,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
launchConf.passwords_needed_to_start.length === 0 &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
+ $scope.promptModalMissingReqFields = false;
} else {
$scope.showPromptButton = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
$scope.promptModalMissingReqFields = true;
+ } else {
+ $scope.promptModalMissingReqFields = false;
}
if (responses[1].data.survey_enabled) {
@@ -728,38 +735,42 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
});
}
- if ($scope.nodeBeingEdited.unifiedJobTemplate.type === "job_template") {
+ if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) {
+ if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template") {
+ $scope.workflowMakerFormConfig.activeTab = "jobs";
+ }
+
+ $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate;
+
+ if ($scope.selectedTemplate.unified_job_type) {
+ switch ($scope.selectedTemplate.unified_job_type) {
+ case "job":
+ $scope.workflowMakerFormConfig.activeTab = "jobs";
+ break;
+ case "project_update":
+ $scope.workflowMakerFormConfig.activeTab = "project_sync";
+ break;
+ case "inventory_update":
+ $scope.workflowMakerFormConfig.activeTab = "inventory_sync";
+ break;
+ }
+ } else if ($scope.selectedTemplate.type) {
+ switch ($scope.selectedTemplate.type) {
+ case "job_template":
+ $scope.workflowMakerFormConfig.activeTab = "jobs";
+ break;
+ case "project":
+ $scope.workflowMakerFormConfig.activeTab = "project_sync";
+ break;
+ case "inventory_source":
+ $scope.workflowMakerFormConfig.activeTab = "inventory_sync";
+ break;
+ }
+ }
+ } else {
$scope.workflowMakerFormConfig.activeTab = "jobs";
}
- $scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate;
-
- if ($scope.selectedTemplate.unified_job_type) {
- switch ($scope.selectedTemplate.unified_job_type) {
- case "job":
- $scope.workflowMakerFormConfig.activeTab = "jobs";
- break;
- case "project_update":
- $scope.workflowMakerFormConfig.activeTab = "project_sync";
- break;
- case "inventory_update":
- $scope.workflowMakerFormConfig.activeTab = "inventory_sync";
- break;
- }
- } else if ($scope.selectedTemplate.type) {
- switch ($scope.selectedTemplate.type) {
- case "job_template":
- $scope.workflowMakerFormConfig.activeTab = "jobs";
- break;
- case "project":
- $scope.workflowMakerFormConfig.activeTab = "project_sync";
- break;
- case "inventory_source":
- $scope.workflowMakerFormConfig.activeTab = "inventory_sync";
- break;
- }
- }
-
let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
tree: $scope.treeData.data,
parentId: parent.id,
@@ -771,7 +782,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
switch($scope.nodeBeingEdited.edgeType) {
case "always":
$scope.edgeType = {label: "Always", value: "always"};
- if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) {
+ if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always") || $scope.nodeBeingEdited.isRoot) {
edgeDropdownOptions = ["always"];
}
break;
@@ -987,11 +998,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
launchConf.passwords_needed_to_start.length === 0 &&
launchConf.variables_needed_to_start.length === 0) {
$scope.showPromptButton = false;
+ $scope.promptModalMissingReqFields = false;
} else {
$scope.showPromptButton = true;
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
$scope.promptModalMissingReqFields = true;
+ } else {
+ $scope.promptModalMissingReqFields = false;
}
if (launchConf.survey_enabled) {
@@ -1048,6 +1062,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
$scope.selectedTemplate = angular.copy(selectedTemplate);
$scope.selectedTemplateInvalid = false;
$scope.showPromptButton = false;
+ $scope.promptModalMissingReqFields = false;
}
};
diff --git a/awx/ui/client/src/templates/workflows/workflow.block.less b/awx/ui/client/src/templates/workflows/workflow.block.less
new file mode 100644
index 0000000000..66f3f76ec4
--- /dev/null
+++ b/awx/ui/client/src/templates/workflows/workflow.block.less
@@ -0,0 +1,13 @@
+.Workflow-warning {
+ float: right;
+ margin-right: 20px;
+ color: @default-interface-txt;
+}
+.Workflow-warningIcon {
+ color: @default-warning;
+ margin-right: 5px;
+}
+.Workflow-warningLink {
+ color: @default-link;
+ cursor: pointer;
+}
diff --git a/awx/ui/test/spec/workflows/workflow-add.controller-test.js b/awx/ui/test/spec/workflows/workflow-add.controller-test.js
index 82421e0e6c..9eceb528ce 100644
--- a/awx/ui/test/spec/workflows/workflow-add.controller-test.js
+++ b/awx/ui/test/spec/workflows/workflow-add.controller-test.js
@@ -145,7 +145,8 @@ describe('Controller: WorkflowAdd', () => {
labels: undefined,
organization: undefined,
variables: undefined,
- extra_vars: undefined
+ extra_vars: undefined,
+ allow_simultaneous: undefined
});
});