diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js
index e0007f4431..48c9173f7b 100644
--- a/awx/ui/client/features/templates/templates.strings.js
+++ b/awx/ui/client/features/templates/templates.strings.js
@@ -110,6 +110,8 @@ function TemplatesStrings (BaseString) {
JOBS: t.s('JOBS'),
PLEASE_CLICK_THE_START_BUTTON: t.s('Please click the start button to build your workflow.'),
PLEASE_HOVER_OVER_A_TEMPLATE: t.s('Please hover over a template for additional options.'),
+ EDIT_LINK_TOOLTIP: t.s('Click to edit link'),
+ VIEW_LINK_TOOLTIP: t.s('Click to view link'),
RUN: t.s('RUN'),
CHECK: t.s('CHECK'),
SELECT: t.s('SELECT'),
@@ -118,6 +120,7 @@ function TemplatesStrings (BaseString) {
DETAILS: t.s('DETAILS'),
TITLE: t.s('WORKFLOW VISUALIZER'),
EDIT_LINK: ({ parentName, childName }) => t.s('EDIT LINK | {{parentName}} to {{childName}}', { parentName, childName }),
+ VIEW_LINK: ({ parentName, childName }) => t.s('VIEW LINK | {{parentName}} to {{childName}}', { parentName, childName })
}
}
diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.block.less b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.block.less
index f5e9bd7378..b2deec2bf3 100644
--- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.block.less
+++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.block.less
@@ -1,43 +1,88 @@
-.link circle,
-.link polygon,
-.link .linkCross,
-.node circle,
-.node .linkIcon,
-.node .WorkflowChart-hoverPath {
+.WorkflowChart-node {
+ font-size: 12px;
+ font-family: 'Open Sans', sans-serif, 'FontAwesome';
+}
+
+.WorkflowChart-link {
+ fill: none;
+ stroke-width: 2px;
+}
+
+.WorkflowChart-linkOverlay {
+ fill: @default-interface-txt;
+}
+
+.WorkflowChart-link--active.WorkflowChart-linkOverlay,
+.WorkflowChart-linkHovering .WorkflowChart-linkOverlay {
+ cursor: pointer;
+ opacity: 1;
+ fill: @cgrey;
+}
+
+.WorkflowChart-linkHovering .WorkflowChart-linkPath {
+ cursor: pointer;
+}
+
+.WorkflowChart-link circle,
+.WorkflowChart-link polygon,
+.WorkflowChart-link .WorkflowChart-betweenNodesIcon,
+.WorkflowChart-node .WorkflowChart-nodeAddCircle,
+.WorkflowChart-node .WorkflowChart-nodeRemoveCircle,
+.WorkflowChart-node .WorkflowChart-nodeAddIcon,
+.WorkflowChart-node .WorkflowChart-nodeRemoveIcon {
opacity: 0;
}
-.node .addCircle, .link .addCircle {
+.WorkflowChart-node .WorkflowChart-addCircle, .WorkflowChart-link .WorkflowChart-addCircle {
fill: @default-succ;
}
-.addCircle.addHovering {
+.WorkflowChart-addCircle.WorkflowChart-addHovering {
fill: @default-succ-hov;
}
-.node .removeCircle {
+.WorkflowChart-node .WorkflowChart-nodeRemoveCircle {
fill: @default-err;
}
-.removeCircle.removeHovering {
+.WorkflowChart-nodeRemoveCircle.removeHovering {
fill: @default-err-hov;
}
-.node .linkCircle {
- fill: @default-link;
+.WorkflowChart-node .WorkflowChart-rect {
+ fill: @default-secondary-bg;
}
-.node .linkIcon {
- color: @default-bg;
+.WorkflowChart-rect.WorkflowChart-placeholder {
+ stroke-dasharray: 3;
}
-.linkCircle.removeHovering {
- fill: @default-link-hov;
+.WorkflowChart-node .WorkflowChart-transparentRect {
+ fill: @default-bg;
+ opacity: 0;
}
-.node {
- font-size: 12px;
- font-family: 'Open Sans', sans-serif, 'FontAwesome';
+.WorkflowChart-alwaysShowAdd circle,
+.WorkflowChart-alwaysShowAdd path,
+.WorkflowChart-alwaysShowAdd .WorkflowChart-betweenNodesIcon,
+.WorkflowChart-nodeHovering .WorkflowChart-nodeAddCircle,
+.WorkflowChart-nodeHovering .WorkflowChart-nodeAddIcon,
+.WorkflowChart-nodeHovering .WorkflowChart-nodeRemoveCircle,
+.WorkflowChart-nodeHovering .WorkflowChart-nodeRemoveIcon,
+.WorkflowChart-addHovering circle,
+.WorkflowChart-addHovering path,
+.WorkflowChart-addHovering .WorkflowChart-betweenNodesIcon {
+ cursor: pointer;
+ opacity: 1;
+}
+
+.WorkflowChart-link.WorkflowChart-placeholder {
+ stroke-dasharray: 3;
+}
+
+.WorkflowChart-svg {
+ border-bottom-left-radius: 5px;
+ width: 100%;
}
.WorkflowChart-defaultText {
@@ -49,76 +94,36 @@
cursor: default;
}
-.node .rect {
- fill: @default-secondary-bg;
-}
-
-.rect.placeholder {
- stroke-dasharray: 3;
-}
-
-.node .transparentRect {
- fill: @default-bg;
- opacity: 0;
-}
-
-.WorkflowChart-alwaysShowAdd circle,
-.WorkflowChart-alwaysShowAdd path,
-.WorkflowChart-alwaysShowAdd .linkCross,
-.hovering .addCircle,
-.hovering .removeCircle,
-.addHovering .betweenNodesCircle,
-.hovering .linkCircle,
-.hovering .linkIcon,
-.hovering .WorkflowChart-hoverPath,
-.addHovering .linkCross {
- cursor: pointer;
- opacity: 1;
-}
-
-.link {
- fill: none;
- stroke-width: 2px;
-}
-
-.link.placeholder {
- stroke-dasharray: 3;
-}
-
-.WorkflowChart-svg {
- border-bottom-left-radius: 5px;
- width: 100%;
-}
-
-.WorkflowResults-rightSide .WorkflowChart-svg {
- background-color: @f6grey;
- border: 1px solid @d7grey;
- border-top: 0px;
- border-bottom-right-radius: 5px;
-}
.WorkflowChart-nodeTypeCircle {
fill: @default-icon;
}
+
.WorkflowChart-nodeTypeLetter {
fill: @default-bg;
}
-.workflowChart-nodeStatus--running {
+
+.WorkflowChart-nodeStatus--running {
fill: @default-icon;
}
-.workflowChart-nodeStatus--success {
+
+.WorkflowChart-nodeStatus--success {
fill: @default-succ;
}
-.workflowChart-nodeStatus--failed, .workflowChart-nodeStatus--canceled {
+
+.WorkflowChart-nodeStatus--failed, .WorkflowChart-nodeStatus--canceled {
fill: @default-err;
}
+
.WorkflowChart-detailsLink {
fill: @default-link;
cursor: pointer;
font-size: 10px;
}
+
.WorkflowChart-incompleteIcon {
color: @default-warning;
}
+
.WorkflowChart-deletedText {
width: 90px;
color: @default-interface-txt;
@@ -126,6 +131,7 @@
.WorkflowChart-activeNode {
fill: @default-link;
}
+
.WorkflowChart-elapsedHolder {
background-color: @b7grey;
color: @default-bg;
@@ -134,40 +140,47 @@
padding: 1px 3px;
border-radius: 4px;
}
+
.WorkflowChart-nameText {
font-size: 10px;
}
+.WorkflowChart-tooltip {
+ pointer-events: none;
+ text-align: center;
+}
+
.WorkflowChart-tooltipContents {
padding: 10px;
- background-color: #707070;
- color: #FFFFFF;
+ background-color: @default-interface-txt;
+ color: @default-bg;
border-radius: 4px;
word-wrap: break-word;
max-width: 325px;
+ font-size: 10px;
}
-.WorkflowChart-tooltipArrow {
+
+.WorkflowChart-tooltipArrow--down {
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
- border-top: 10px solid #707070;
+ border-top: 10px solid @default-interface-txt;
margin: auto;
}
+
+.WorkflowChart-tooltipArrow--right {
+ width: 0;
+ height: 0;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ border-left: 10px solid @default-interface-txt;
+ margin: auto;
+ position: relative;
+ right: -55px;
+ top: -34px;
+}
+
.WorkflowChart-dashedNode {
stroke-dasharray: 5,5;
}
-
-.linkOverlay {
- fill: @default-interface-txt;
-}
-
-.linkActiveEdit.linkOverlay,
-.overlayHovering .linkOverlay {
- cursor: pointer;
- opacity: 0.4;
-}
-
-.overlayHovering .linkPath {
- cursor: pointer;
-}
diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js
index ea5c34a794..07ab752efe 100644
--- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js
+++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js
@@ -239,14 +239,14 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
let nodes = tree.nodes(scope.treeData),
links = tree.links(nodes);
- let node = svgGroup.selectAll("g.node")
+ let node = svgGroup.selectAll("g.WorkflowChart-node")
.data(nodes, function(d) {
d.y = d.depth * 240;
return d.id || (d.id = ++i);
});
let nodeEnter = node.enter().append("g")
- .attr("class", "node")
+ .attr("class", "WorkflowChart-node")
.attr("id", function(d){return "node-" + d.id;})
.attr("parent", function(d){return d.parent ? d.parent.id : null;})
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
@@ -308,7 +308,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
})
.attr('stroke-width', "2px")
.attr("class", function(d) {
- let classString = d.placeholder ? "rect placeholder" : "rect";
+ let classString = d.placeholder ? "WorkflowChart-rect WorkflowChart-placeholder" : "WorkflowChart-rect";
classString += !d.unifiedJobTemplate ? " WorkflowChart-dashedNode" : "";
return classString;
});
@@ -398,7 +398,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
thisNode.append("rect")
.attr("width", nodeW)
.attr("height", nodeH)
- .attr("class", "transparentRect")
+ .attr("class", "WorkflowChart-transparentRect")
.call(edit_node)
.on("mouseover", function(d) {
if(!d.isStartNode) {
@@ -409,13 +409,13 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
// As such, we need to move the nodes after the links so that when the tooltip renders it shows up on top
// of the links and not underneath them. I tried rendering the links before the nodes but that lead to
// some weird link animation that I didn't care to try to fix.
- svgGroup.selectAll("g.node").each(function() {
+ svgGroup.selectAll("g.WorkflowChart-node").each(function() {
this.parentNode.appendChild(this);
});
// After the nodes have been properly placed after the links, we need to make sure that the node that
// the user is hovering over is at the very end of the list. This way the tooltip will appear on top
// of all other nodes.
- svgGroup.selectAll("g.node").sort(function (a) {
+ svgGroup.selectAll("g.WorkflowChart-node").sort(function (a) {
return (a.id !== d.id) ? -1 : 1;
});
// Render the tooltip quickly in the dom and then remove. This lets us know how big the tooltip is so that we can place
@@ -437,14 +437,14 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
});
}
d3.select("#node-" + d.id)
- .classed("hovering", true);
+ .classed("WorkflowChart-nodeHovering", true);
}
})
.on("mouseout", function(d){
$('.WorkflowChart-tooltip').remove();
if(!d.isStartNode) {
d3.select("#node-" + d.id)
- .classed("hovering", false);
+ .classed("WorkflowChart-nodeHovering", false);
}
});
thisNode.append("text")
@@ -461,23 +461,23 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.attr("id", function(d){return "node-" + d.id + "-add";})
.attr("cx", nodeW)
.attr("r", 10)
- .attr("class", "addCircle nodeCircle")
+ .attr("class", "WorkflowChart-addCircle WorkflowChart-nodeAddCircle")
.style("display", function(d) { return d.placeholder || !(userCanAddEdit) ? "none" : null; })
.call(add_node)
.on("mouseover", function(d) {
d3.select("#node-" + d.id)
- .classed("hovering", true);
+ .classed("WorkflowChart-nodeHovering", true);
d3.select("#node-" + d.id + "-add")
- .classed("addHovering", true);
+ .classed("WorkflowChart-addHovering", true);
})
.on("mouseout", function(d){
d3.select("#node-" + d.id)
- .classed("hovering", false);
+ .classed("WorkflowChart-nodeHovering", false);
d3.select("#node-" + d.id + "-add")
- .classed("addHovering", false);
+ .classed("WorkflowChart-addHovering", false);
});
thisNode.append("path")
- .attr("class", "nodeAddCross WorkflowChart-hoverPath")
+ .attr("class", "WorkflowChart-nodeAddIcon")
.style("fill", "white")
.attr("transform", function() { return "translate(" + nodeW + "," + 0 + ")"; })
.attr("d", d3.svg.symbol()
@@ -488,38 +488,38 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.call(add_node)
.on("mouseover", function(d) {
d3.select("#node-" + d.id)
- .classed("hovering", true);
+ .classed("WorkflowChart-nodeHovering", true);
d3.select("#node-" + d.id + "-add")
- .classed("addHovering", true);
+ .classed("WorkflowChart-addHovering", true);
})
.on("mouseout", function(d){
d3.select("#node-" + d.id)
- .classed("hovering", false);
+ .classed("WorkflowChart-nodeHovering", false);
d3.select("#node-" + d.id + "-add")
- .classed("addHovering", false);
+ .classed("WorkflowChart-addHovering", false);
});
thisNode.append("circle")
.attr("id", function(d){return "node-" + d.id + "-remove";})
.attr("cx", nodeW)
.attr("cy", nodeH)
.attr("r", 10)
- .attr("class", "removeCircle")
+ .attr("class", "WorkflowChart-nodeRemoveCircle")
.style("display", function(d) { return (d.canDelete === false || d.placeholder || !(userCanAddEdit)) ? "none" : null; })
.call(remove_node)
.on("mouseover", function(d) {
d3.select("#node-" + d.id)
- .classed("hovering", true);
+ .classed("WorkflowChart-nodeHovering", true);
d3.select("#node-" + d.id + "-remove")
.classed("removeHovering", true);
})
.on("mouseout", function(d){
d3.select("#node-" + d.id)
- .classed("hovering", false);
+ .classed("WorkflowChart-nodeHovering", false);
d3.select("#node-" + d.id + "-remove")
.classed("removeHovering", false);
});
thisNode.append("path")
- .attr("class", "nodeRemoveCross WorkflowChart-hoverPath")
+ .attr("class", "WorkflowChart-nodeRemoveIcon")
.style("fill", "white")
.attr("transform", function() { return "translate(" + nodeW + "," + nodeH + ") rotate(-45)"; })
.attr("d", d3.svg.symbol()
@@ -530,60 +530,16 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.call(remove_node)
.on("mouseover", function(d) {
d3.select("#node-" + d.id)
- .classed("hovering", true);
+ .classed("WorkflowChart-nodeHovering", true);
d3.select("#node-" + d.id + "-remove")
.classed("removeHovering", true);
})
.on("mouseout", function(d){
d3.select("#node-" + d.id)
- .classed("hovering", false);
+ .classed("WorkflowChart-nodeHovering", false);
d3.select("#node-" + d.id + "-remove")
.classed("removeHovering", false);
});
- // thisNode.append("circle")
- // .attr("id", function(d){return "node-" + d.id + "-link";})
- // .attr("cx", nodeW)
- // .attr("cy", nodeH/2)
- // .attr("r", 10)
- // .attr("class", "linkCircle nodeCircle")
- // .style("display", function(d) { return d.placeholder || !(userCanAddEdit) ? "none" : null; })
- // .call(link_node)
- // .on("mouseover", function(d) {
- // d3.select("#node-" + d.id)
- // .classed("hovering", true);
- // d3.select("#node-" + d.id + "-link")
- // .classed("addHovering", true);
- // })
- // .on("mouseout", function(d){
- // d3.select("#node-" + d.id)
- // .classed("hovering", false);
- // d3.select("#node-" + d.id + "-link")
- // .classed("addHovering", false);
- // });
- // // TODO: clean up the placement of this icon... this works but it's not
- // // clean
- // thisNode.append("foreignObject")
- // .attr("x", nodeW - 6)
- // .attr("y", nodeH/2 - 9)
- // .style("font-size","14px")
- // .html(function () {
- // return ``;
- // })
- // .attr("class", "linkIcon")
- // .style("display", function(d) { return d.placeholder || !(userCanAddEdit) ? "none" : null; })
- // .call(link_node)
- // .on("mouseover", function(d) {
- // d3.select("#node-" + d.id)
- // .classed("hovering", true);
- // d3.select("#node-" + d.id + "-link")
- // .classed("addHovering", true);
- // })
- // .on("mouseout", function(d){
- // d3.select("#node-" + d.id)
- // .classed("hovering", false);
- // d3.select("#node-" + d.id + "-link")
- // .classed("addHovering", false);
- // });
thisNode.append("circle")
.attr("class", function(d) {
@@ -593,25 +549,25 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
if(d.job){
switch(d.job.status) {
case "pending":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "waiting":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "running":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "successful":
- statusClass += "workflowChart-nodeStatus--success";
+ statusClass += "WorkflowChart-nodeStatus--success";
break;
case "failed":
- statusClass += "workflowChart-nodeStatus--failed";
+ statusClass += "WorkflowChart-nodeStatus--failed";
break;
case "error":
- statusClass += "workflowChart-nodeStatus--failed";
+ statusClass += "WorkflowChart-nodeStatus--failed";
break;
case "canceled":
- statusClass += "workflowChart-nodeStatus--canceled";
+ statusClass += "WorkflowChart-nodeStatus--canceled";
break;
}
}
@@ -652,63 +608,149 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
graphLoaded = true;
- let link = svgGroup.selectAll("g.link")
+ let link = svgGroup.selectAll("g.WorkflowChart-link")
.data(links, function(d) {
return d.source.id + "-" + d.target.id;
});
let linkEnter = link.enter().append("g")
- .attr("class", "link")
+ .attr("class", "WorkflowChart-link")
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id;});
linkEnter.append("polygon", "g")
.attr("class", function(d) {
- let linkClasses = ["linkOverlay"];
+ let linkClasses = ["WorkflowChart-linkOverlay"];
if (d.source.isLinkEditParent && d.target.isLinkEditChild) {
- linkClasses.push("linkActiveEdit");
+ linkClasses.push("WorkflowChart-link--active");
}
return linkClasses.join(' ');
})
.attr("id", function(d){return "link-" + d.source.id + "-" + d.target.id + "-overlay";})
.attr("points",function(d) {
- const pt1 = [d.source.y + nodeW, d.source.x + 10 + nodeH/2].join(",");
- const pt2 = [d.target.y,d.target.x + 10 + nodeH/2].join(",");
- const pt3 = [d.target.y,d.target.x - 10 + nodeH/2].join(",");
- const pt4 = [d.source.y + nodeW,d.source.x - 10 + nodeH/2].join(",");
+ let x1 = d.source.y + nodeW;
+ let y1 = d.source.x + nodeH / 2;
+ let x2 = d.target.y;
+ let y2 = d.target.x + nodeH / 2;
+ let slope = (y2 - y1)/(x2-x1);
+ let yIntercept = y1 - slope*x1;
+ let orthogonalDistance = 8;
+
+ const pt1 = [x1, slope*x1 + yIntercept + orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt2 = [x2, slope*x2 + yIntercept + orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt3 = [x2, slope*x2 + yIntercept - orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt4 = [x1, slope*x1 + yIntercept - orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+
return [pt1, pt2, pt3, pt4].join(" ");
})
.call(edit_link)
.on("mouseover", function(d) {
- if(!d.source.isStartNode && !d.target.placeholder && scope.mode !== 'details') {
+ if(!d.source.isStartNode && !d.source.placeholder && !d.target.placeholder && scope.mode !== 'details') {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("overlayHovering", true);
+ .classed("WorkflowChart-linkHovering", true);
+
+ let xPos, yPos, arrowClass;
+ if (d.source.x === d.target.x) {
+ xPos = d.source.y + nodeW + ((d.target.y - (d.source.y + nodeW))/2) - (100/2);
+ yPos = (d.source.x + nodeH/2 - d.target.x + nodeH/2)/2 + (d.target.x + nodeH/2) - 100;
+ arrowClass = 'WorkflowChart-tooltipArrow--down';
+ } else {
+ xPos = d.source.y + nodeW + ((d.target.y - (d.source.y + nodeW))/2) - 115;
+ yPos = (d.source.x + nodeH/2 - d.target.x + nodeH/2)/2 + (d.target.x + nodeH/2) - 50;
+ arrowClass = 'WorkflowChart-tooltipArrow--right';
+ }
+
+ let edgeTypeLabel;
+
+ switch(d.target.edgeType) {
+ case "always":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ALWAYS');
+ break;
+ case "success":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ON_SUCCESS');
+ break;
+ case "failure":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ON_FAILURE');
+ break;
+ }
+
+ let linkInstructionText = _.get(scope, 'workflowJobTemplateObj.summary_fields.user_capabilities.edit') ? TemplatesStrings.get('workflow_maker.EDIT_LINK_TOOLTIP') : TemplatesStrings.get('workflow_maker.VIEW_LINK_TOOLTIP');
+
+ linkEnter.append("foreignObject")
+ .attr("x", xPos)
+ .attr("y", yPos)
+ .attr("width", 100)
+ .attr("height", 60)
+ .attr("class", "WorkflowChart-tooltip")
+ .html(function(){
+ return `
${TemplatesStrings.get('workflow_maker.RUN')}: ${edgeTypeLabel}
${linkInstructionText}
`;
+ });
}
+
})
.on("mouseout", function(d){
if(!d.source.isStartNode && !d.target.placeholder && scope.mode !== 'details') {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("overlayHovering", false);
+ .classed("WorkflowChart-linkHovering", false);
}
+ $('.WorkflowChart-tooltip').remove();
});
// Add entering links in the parent’s old position.
linkEnter.append("path", "g")
.attr("class", function(d) {
- return (d.source.placeholder || d.target.placeholder) ? "linkPath placeholder" : "linkPath";
+ return (d.source.placeholder || d.target.placeholder) ? "WorkflowChart-linkPath WorkflowChart-placeholder" : "WorkflowChart-linkPath";
})
.attr("d", lineData)
.call(edit_link)
- .on("mouseover", function(d) {
- if(!d.source.isStartNode && !d.target.placeholder && scope.mode !== 'details') {
+ .on("mouseenter", function(d) {
+ if(!d.source.isStartNode && !d.source.placeholder && !d.target.placeholder && scope.mode !== 'details') {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("overlayHovering", true);
+ .classed("WorkflowChart-linkHovering", true);
+
+ let xPos, yPos, arrowClass;
+ if (d.source.x === d.target.x) {
+ xPos = d.source.y + nodeW + ((d.target.y - (d.source.y + nodeW))/2) - (100/2);
+ yPos = (d.source.x + nodeH/2 - d.target.x + nodeH/2)/2 + (d.target.x + nodeH/2) - 100;
+ arrowClass = 'WorkflowChart-tooltipArrow--down';
+ } else {
+ xPos = d.source.y + nodeW + ((d.target.y - (d.source.y + nodeW))/2) - 115;
+ yPos = (d.source.x + nodeH/2 - d.target.x + nodeH/2)/2 + (d.target.x + nodeH/2) - 50;
+ arrowClass = 'WorkflowChart-tooltipArrow--right';
+ }
+
+ let edgeTypeLabel;
+
+ switch(d.target.edgeType) {
+ case "always":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ALWAYS');
+ break;
+ case "success":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ON_SUCCESS');
+ break;
+ case "failure":
+ edgeTypeLabel = TemplatesStrings.get('workflow_maker.ON_FAILURE');
+ break;
+ }
+
+ let linkInstructionText = _.get(scope, 'workflowJobTemplateObj.summary_fields.user_capabilities.edit') ? TemplatesStrings.get('workflow_maker.EDIT_LINK_TOOLTIP') : TemplatesStrings.get('workflow_maker.VIEW_LINK_TOOLTIP');
+
+ linkEnter.append("foreignObject")
+ .attr("x", xPos)
+ .attr("y", yPos)
+ .attr("width", 100)
+ .attr("height", 60)
+ .attr("class", "WorkflowChart-tooltip")
+ .html(function(){
+ return `${TemplatesStrings.get('workflow_maker.RUN')}: ${edgeTypeLabel}
${linkInstructionText}
`;
+ });
}
})
- .on("mouseout", function(d){
+ .on("mouseleave", function(d){
if(!d.source.isStartNode && !d.target.placeholder && scope.mode !== 'details') {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("overlayHovering", false);
+ .classed("WorkflowChart-linkHovering", false);
}
+ $('.WorkflowChart-tooltip').remove();
})
.attr('stroke', function(d) {
if(d.target.edgeType) {
@@ -736,20 +778,20 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
return (d.source.isStartNode) ? ((d.target.x + startNodeOffsetY + rootH/2) + (d.source.x + nodeH/2)) / 2 : (d.target.x + d.source.x + nodeH) / 2;
})
.attr("r", 10)
- .attr("class", "addCircle betweenNodesCircle")
+ .attr("class", "WorkflowChart-addCircle WorkflowChart-circleBetweenNodes")
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder || !(userCanAddEdit)) ? "none" : null; })
.call(add_node_between)
.on("mouseover", function(d) {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("addHovering", true);
+ .classed("WorkflowChart-addHovering", true);
})
.on("mouseout", function(d){
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("addHovering", false);
+ .classed("WorkflowChart-addHovering", false);
});
linkEnter.append("path")
- .attr("class", "linkCross")
+ .attr("class", "WorkflowChart-betweenNodesIcon")
.style("fill", "white")
.attr("transform", function(d) {
let translate;
@@ -769,11 +811,11 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.call(add_node_between)
.on("mouseover", function(d) {
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("addHovering", true);
+ .classed("WorkflowChart-addHovering", true);
})
.on("mouseout", function(d){
d3.select("#link-" + d.source.id + "-" + d.target.id)
- .classed("addHovering", false);
+ .classed("WorkflowChart-addHovering", false);
});
link.exit().remove();
@@ -781,21 +823,21 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
// Transition nodes and links to their new positions.
let t = baseSvg.transition();
- t.selectAll(".nodeCircle")
+ t.selectAll(".WorkflowChart-nodeAddCircle")
.style("display", function(d) { return d.placeholder || !(userCanAddEdit) ? "none" : null; });
- t.selectAll(".nodeAddCross")
+ t.selectAll(".WorkflowChart-nodeAddIcon")
.style("display", function(d) { return d.placeholder || !(userCanAddEdit) ? "none" : null; });
- t.selectAll(".removeCircle")
+ t.selectAll(".WorkflowChart-nodeRemoveCircle")
.style("display", function(d) { return (d.canDelete === false || d.placeholder || !(userCanAddEdit)) ? "none" : null; });
- t.selectAll(".nodeRemoveCross")
+ t.selectAll(".WorkflowChart-nodeRemoveIcon")
.style("display", function(d) { return (d.canDelete === false || d.placeholder || !(userCanAddEdit)) ? "none" : null; });
- t.selectAll(".linkPath")
+ t.selectAll(".WorkflowChart-linkPath")
.attr("class", function(d) {
- return (d.source.placeholder || d.target.placeholder) ? "linkPath placeholder" : "linkPath";
+ return (d.source.placeholder || d.target.placeholder) ? "WorkflowChart-linkPath WorkflowChart-placeholder" : "WorkflowChart-linkPath";
})
.attr("d", lineData)
.attr('stroke', function(d) {
@@ -815,7 +857,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}
});
- t.selectAll(".betweenNodesCircle")
+ t.selectAll(".WorkflowChart-circleBetweenNodes")
+ .style("display", function(d) { return (d.source.placeholder || d.target.placeholder || !(userCanAddEdit)) ? "none" : null; })
.attr("cx", function(d) {
return (d.source.isStartNode) ? (d.target.y + d.source.y + rootW) / 2 : (d.target.y + d.source.y + nodeW) / 2;
})
@@ -823,23 +866,32 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
return (d.source.isStartNode) ? ((d.target.x + startNodeOffsetY + rootH/2) + (d.source.x + nodeH/2)) / 2 : (d.target.x + d.source.x + nodeH) / 2;
});
- t.selectAll(".linkOverlay")
+ t.selectAll(".WorkflowChart-linkOverlay")
.attr("class", function(d) {
- let linkClasses = ["linkOverlay"];
+ let linkClasses = ["WorkflowChart-linkOverlay"];
if (d.source.isLinkEditParent && d.target.isLinkEditChild) {
- linkClasses.push("linkActiveEdit");
+ linkClasses.push("WorkflowChart-link--active");
}
return linkClasses.join(' ');
})
.attr("points",function(d) {
- const pt1 = [d.source.y + nodeW, d.source.x + 10 + nodeH/2].join(",");
- const pt2 = [d.target.y,d.target.x + 10 + nodeH/2].join(",");
- const pt3 = [d.target.y,d.target.x - 10 + nodeH/2].join(",");
- const pt4 = [d.source.y + nodeW,d.source.x - 10 + nodeH/2].join(",");
+ let x1 = d.source.y + nodeW;
+ let y1 = d.source.x + nodeH / 2;
+ let x2 = d.target.y;
+ let y2 = d.target.x + nodeH / 2;
+ let slope = (y2 - y1)/(x2-x1);
+ let yIntercept = y1 - slope*x1;
+ let orthogonalDistance = 8;
+
+ const pt1 = [x1, slope*x1 + yIntercept + orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt2 = [x2, slope*x2 + yIntercept + orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt3 = [x2, slope*x2 + yIntercept - orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+ const pt4 = [x1, slope*x1 + yIntercept - orthogonalDistance*Math.sqrt(1+slope*slope)].join(",");
+
return [pt1, pt2, pt3, pt4].join(" ");
});
- t.selectAll(".linkCross")
+ t.selectAll(".WorkflowChart-betweenNodesIcon")
.style("display", function(d) { return (d.source.placeholder || d.target.placeholder || !(userCanAddEdit)) ? "none" : null; })
.attr("transform", function(d) {
let translate;
@@ -852,7 +904,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
return translate;
});
- t.selectAll(".rect")
+ t.selectAll(".WorkflowChart-rect")
.attr('stroke', function(d) {
if(d.job && d.job.status) {
if(d.job.status === "successful"){
@@ -870,12 +922,12 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
}
})
.attr("class", function(d) {
- let classString = d.placeholder ? "rect placeholder" : "rect";
+ let classString = d.placeholder ? "WorkflowChart-rect WorkflowChart-placeholder" : "WorkflowChart-rect";
classString += !d.unifiedJobTemplate ? " WorkflowChart-dashedNode" : "";
return classString;
});
- t.selectAll(".node")
+ t.selectAll(".WorkflowChart-node")
.attr("parent", function(d){return d.parent ? d.parent.id : null;})
.attr("transform", function(d) {d.px = d.x; d.py = d.y; return "translate(" + d.y + "," + d.x + ")"; });
@@ -937,25 +989,25 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
if(d.job){
switch(d.job.status) {
case "pending":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "waiting":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "running":
- statusClass += "workflowChart-nodeStatus--running";
+ statusClass += "WorkflowChart-nodeStatus--running";
break;
case "successful":
- statusClass += "workflowChart-nodeStatus--success";
+ statusClass += "WorkflowChart-nodeStatus--success";
break;
case "failed":
- statusClass += "workflowChart-nodeStatus--failed";
+ statusClass += "WorkflowChart-nodeStatus--failed";
break;
case "error":
- statusClass += "workflowChart-nodeStatus--failed";
+ statusClass += "WorkflowChart-nodeStatus--failed";
break;
case "canceled":
- statusClass += "workflowChart-nodeStatus--canceled";
+ statusClass += "WorkflowChart-nodeStatus--canceled";
break;
}
}
@@ -1058,11 +1110,10 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
function edit_link() {
this.on("click", function(d) {
- if(!d.source.isStartNode && !d.target.placeholder && scope.mode !== 'details'){
- // What if the node is new? it won't have a nodeId right?
+ if(!d.source.isStartNode && !d.source.placeholder && !d.target.placeholder && scope.mode !== 'details'){
scope.editLink({
- parentId: d.source.nodeId,
- childId: d.target.nodeId
+ parentId: d.source.id,
+ childId: d.target.id
});
}
});
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.directive.js b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.directive.js
index 00b3afc765..8591b9a728 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.directive.js
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.directive.js
@@ -11,6 +11,7 @@ export default ['templateUrl',
return {
scope: {
linkConfig: '<',
+ readOnly: '<',
cancel: '&',
select: '&'
},
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.partial.html
index cf03f1624c..a09dcd0618 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.partial.html
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-link-form.partial.html
@@ -1,4 +1,4 @@
-{{:: strings.get('workflow_maker.EDIT_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) }}
+{{readOnly ? strings.get('workflow_maker.VIEW_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) : strings.get('workflow_maker.EDIT_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) }}
-
-
+
+
+
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js
index 41e8b11142..2310c067bf 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.controller.js
@@ -4,8 +4,635 @@
* All Rights Reserved
*************************************************/
-export default ['$scope',
- function($scope) {
- console.log('inside wnf controller');
+export default ['$scope', 'TemplatesService', 'JobTemplateModel', 'PromptService', 'Rest', '$q',
+ 'WorkflowService', 'TemplatesStrings', 'CreateSelect2', 'Empty', 'generateList', 'QuerySet',
+ 'GetBasePath', 'TemplateList', 'ProjectList', 'InventorySourcesList',
+ function($scope, TemplatesService, JobTemplate, PromptService, Rest, $q,
+ WorkflowService, TemplatesStrings, CreateSelect2, Empty, generateList, qs,
+ GetBasePath, TemplateList, ProjectList, InventorySourcesList
+ ) {
+
+ let promptWatcher, credentialsWatcher, surveyQuestionWatcher, listPromises = [];
+
+ $scope.strings = TemplatesStrings;
+
+ let templateList = _.cloneDeep(TemplateList);
+ delete templateList.actions;
+ delete templateList.fields.type;
+ delete templateList.fields.description;
+ delete templateList.fields.smart_status;
+ delete templateList.fields.labels;
+ delete templateList.fieldActions;
+ templateList.fields.name.columnClass = "col-md-8";
+ templateList.disableRow = "{{ readOnly }}";
+ templateList.disableRowValue = 'readOnly';
+ templateList.fields.info = {
+ ngInclude: "'/static/partials/job-template-details.html'",
+ type: 'template',
+ columnClass: 'col-md-3',
+ label: '',
+ nosort: true
+ };
+ templateList.maxVisiblePages = 5;
+ templateList.searchBarFullWidth = true;
+ $scope.templateList = templateList;
+
+ let inventorySourceList = _.cloneDeep(InventorySourcesList);
+ inventorySourceList.maxVisiblePages = 5;
+ inventorySourceList.searchBarFullWidth = true;
+ inventorySourceList.disableRow = "{{ readOnly }}";
+ inventorySourceList.disableRowValue = 'readOnly';
+ $scope.inventorySourceList = inventorySourceList;
+
+ let projectList = _.cloneDeep(ProjectList);
+ delete projectList.fields.status;
+ delete projectList.fields.scm_type;
+ delete projectList.fields.last_updated;
+ projectList.fields.name.columnClass = "col-md-11";
+ projectList.maxVisiblePages = 5;
+ projectList.searchBarFullWidth = true;
+ projectList.disableRow = "{{ readOnly }}";
+ projectList.disableRowValue = 'readOnly';
+ $scope.projectList = projectList;
+
+ $scope.$watch('node', (newNode, oldNode) => {
+ if (oldNode.id !== newNode.id) {
+ setupNodeForm();
+ }
+ });
+
+ $scope.$watchGroup(['templates', 'projects', 'inventory_sources', 'activeTab'], () => {
+ // TODO: make this more concise
+ switch($scope.activeTab) {
+ case 'jobs':
+ $scope.templates.forEach(function(row, i) {
+ if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
+ $scope.templates[i].checked = 1;
+ }
+ else {
+ $scope.templates[i].checked = 0;
+ }
+ });
+ break;
+ case 'project_syncs':
+ $scope.projects.forEach(function(row, i) {
+ if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
+ $scope.projects[i].checked = 1;
+ }
+ else {
+ $scope.projects[i].checked = 0;
+ }
+ });
+ break;
+ case 'inventory_syncs':
+ $scope.inventory_sources.forEach(function(row, i) {
+ if(_.hasIn($scope, 'node.unifiedJobTemplate.id') && row.id === $scope.node.unifiedJobTemplate.id) {
+ $scope.inventory_sources[i].checked = 1;
+ }
+ else {
+ $scope.inventory_sources[i].checked = 0;
+ }
+ });
+ break;
+ }
+ });
+
+ const checkCredentialsForRequiredPasswords = () => {
+ let credentialRequiresPassword = false;
+ $scope.promptData.prompts.credentials.value.forEach((credential) => {
+ if ((credential.passwords_needed &&
+ credential.passwords_needed.length > 0) ||
+ (_.has(credential, 'inputs.vault_password') &&
+ credential.inputs.vault_password === "ASK")
+ ) {
+ credentialRequiresPassword = true;
+ }
+ });
+ $scope.credentialRequiresPassword = credentialRequiresPassword;
+ };
+
+ const watchForPromptChanges = () => {
+ let promptDataToWatch = [
+ 'promptData.prompts.inventory.value',
+ 'promptData.prompts.verbosity.value',
+ 'missingSurveyValue'
+ ];
+
+ promptWatcher = $scope.$watchGroup(promptDataToWatch, function() {
+ let missingPromptValue = false;
+ if ($scope.missingSurveyValue) {
+ missingPromptValue = true;
+ } else if (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
+ missingPromptValue = true;
+ }
+ $scope.promptModalMissingReqFields = missingPromptValue;
+ });
+
+ if ($scope.promptData.launchConf.ask_credential_on_launch && $scope.credentialRequiresPassword) {
+ credentialsWatcher = $scope.$watch('promptData.prompts.credentials', () => {
+ checkCredentialsForRequiredPasswords();
+ });
+ }
+ };
+
+ const finishConfiguringEdit = () => {
+
+ let jobTemplate = new JobTemplate();
+
+ console.log($scope.node);
+
+ if (!_.isEmpty($scope.node.promptData)) {
+ $scope.promptData = _.cloneDeep($scope.node.promptData);
+ const launchConf = $scope.promptData.launchConf;
+
+ if (!launchConf.survey_enabled &&
+ !launchConf.ask_inventory_on_launch &&
+ !launchConf.ask_credential_on_launch &&
+ !launchConf.ask_verbosity_on_launch &&
+ !launchConf.ask_job_type_on_launch &&
+ !launchConf.ask_limit_on_launch &&
+ !launchConf.ask_tags_on_launch &&
+ !launchConf.ask_skip_tags_on_launch &&
+ !launchConf.ask_diff_mode_on_launch &&
+ !launchConf.credential_needed_to_start &&
+ !launchConf.ask_variables_on_launch &&
+ 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, 'node.originalNodeObj.summary_fields.inventory')) {
+ $scope.promptModalMissingReqFields = true;
+ } else {
+ $scope.promptModalMissingReqFields = false;
+ }
+ }
+ $scope.nodeFormDataLoaded = true;
+ } else if (
+ _.get($scope, 'node.unifiedJobTemplate.unified_job_type') === 'job_template' ||
+ _.get($scope, 'node.unifiedJobTemplate.type') === 'job_template'
+ ) {
+ let promises = [jobTemplate.optionsLaunch($scope.node.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.node.unifiedJobTemplate.id)];
+
+ if (_.has($scope, 'node.originalNodeObj.related.credentials')) {
+ Rest.setUrl($scope.node.originalNodeObj.related.credentials);
+ promises.push(Rest.get());
+ }
+
+ $q.all(promises)
+ .then((responses) => {
+ let launchOptions = responses[0].data,
+ launchConf = responses[1].data,
+ workflowNodeCredentials = responses[2] ? responses[2].data.results : [];
+
+ let prompts = PromptService.processPromptValues({
+ launchConf: responses[1].data,
+ launchOptions: responses[0].data,
+ currentValues: $scope.node.originalNodeObj
+ });
+
+ let defaultCredsWithoutOverrides = [];
+
+ prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials);
+
+ const credentialHasScheduleOverride = (templateDefaultCred) => {
+ let credentialHasOverride = false;
+ workflowNodeCredentials.forEach((scheduleCred) => {
+ if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
+ if (
+ (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
+ (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
+ ) {
+ credentialHasOverride = true;
+ }
+ }
+ });
+
+ return credentialHasOverride;
+ };
+
+ if (_.has(launchConf, 'defaults.credentials')) {
+ launchConf.defaults.credentials.forEach((defaultCred) => {
+ if (!credentialHasScheduleOverride(defaultCred)) {
+ defaultCredsWithoutOverrides.push(defaultCred);
+ }
+ });
+ }
+
+ prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
+
+ if ((!$scope.node.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.node.unifiedJobTemplate.project) {
+ $scope.selectedTemplateInvalid = true;
+ } else {
+ $scope.selectedTemplateInvalid = false;
+ }
+
+ let credentialRequiresPassword = false;
+
+ prompts.credentials.value.forEach((credential) => {
+ if(credential.inputs) {
+ if ((credential.inputs.password && credential.inputs.password === "ASK") ||
+ (credential.inputs.become_password && credential.inputs.become_password === "ASK") ||
+ (credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") ||
+ (credential.inputs.vault_password && credential.inputs.vault_password === "ASK")
+ ) {
+ credentialRequiresPassword = true;
+ }
+ } else if (credential.passwords_needed && credential.passwords_needed.length > 0) {
+ credentialRequiresPassword = true;
+ }
+ });
+
+ $scope.credentialRequiresPassword = credentialRequiresPassword;
+
+ if (!launchConf.survey_enabled &&
+ !launchConf.ask_inventory_on_launch &&
+ !launchConf.ask_credential_on_launch &&
+ !launchConf.ask_verbosity_on_launch &&
+ !launchConf.ask_job_type_on_launch &&
+ !launchConf.ask_limit_on_launch &&
+ !launchConf.ask_tags_on_launch &&
+ !launchConf.ask_skip_tags_on_launch &&
+ !launchConf.ask_diff_mode_on_launch &&
+ !launchConf.credential_needed_to_start &&
+ !launchConf.ask_variables_on_launch &&
+ launchConf.variables_needed_to_start.length === 0) {
+ $scope.showPromptButton = false;
+ $scope.promptModalMissingReqFields = false;
+ $scope.nodeFormDataLoaded = true;
+ } else {
+ $scope.showPromptButton = true;
+
+ if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'node.originalNodeObj.summary_fields.inventory')) {
+ $scope.promptModalMissingReqFields = true;
+ } else {
+ $scope.promptModalMissingReqFields = false;
+ }
+
+ if (responses[1].data.survey_enabled) {
+ // go out and get the survey questions
+ jobTemplate.getSurveyQuestions($scope.node.unifiedJobTemplate.id)
+ .then((surveyQuestionRes) => {
+
+ let processed = PromptService.processSurveyQuestions({
+ surveyQuestions: surveyQuestionRes.data.spec,
+ extra_data: _.cloneDeep($scope.node.originalNodeObj.extra_data)
+ });
+
+ $scope.missingSurveyValue = processed.missingSurveyValue;
+
+ $scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
+
+ $scope.node.promptData = $scope.promptData = {
+ launchConf: launchConf,
+ launchOptions: launchOptions,
+ prompts: prompts,
+ surveyQuestions: surveyQuestionRes.data.spec,
+ template: $scope.node.unifiedJobTemplate.id
+ };
+
+ surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
+ let missingSurveyValue = false;
+ _.each($scope.promptData.surveyQuestions, (question) => {
+ if (question.required && (Empty(question.model) || question.model === [])) {
+ missingSurveyValue = true;
+ }
+ });
+ $scope.missingSurveyValue = missingSurveyValue;
+ }, true);
+
+ checkCredentialsForRequiredPasswords();
+
+ watchForPromptChanges();
+
+ $scope.nodeFormDataLoaded = true;
+ });
+ } else {
+ $scope.node.promptData = $scope.promptData = {
+ launchConf: launchConf,
+ launchOptions: launchOptions,
+ prompts: prompts,
+ template: $scope.node.unifiedJobTemplate.id
+ };
+
+ checkCredentialsForRequiredPasswords();
+
+ watchForPromptChanges();
+
+ $scope.nodeFormDataLoaded = true;
+ }
+ }
+ });
+ } else {
+ $scope.nodeFormDataLoaded = true;
+ }
+
+ if (_.get($scope, 'node.unifiedJobTemplate')) {
+ if (_.get($scope, 'node.unifiedJobTemplate.type') === "job_template") {
+ $scope.activeTab = "jobs";
+ }
+
+ $scope.selectedTemplate = $scope.node.unifiedJobTemplate;
+
+ if ($scope.selectedTemplate.unified_job_type) {
+ switch ($scope.selectedTemplate.unified_job_type) {
+ case "job":
+ $scope.activeTab = "jobs";
+ break;
+ case "project_update":
+ $scope.activeTab = "project_syncs";
+ break;
+ case "inventory_update":
+ $scope.activeTab = "inventory_syncs";
+ break;
+ }
+ } else if ($scope.selectedTemplate.type) {
+ switch ($scope.selectedTemplate.type) {
+ case "job_template":
+ $scope.activeTab = "jobs";
+ break;
+ case "project":
+ $scope.activeTab = "project_syncs";
+ break;
+ case "inventory_source":
+ $scope.activeTab = "inventory_syncs";
+ break;
+ }
+ }
+ } else {
+ $scope.activeTab = "jobs";
+ }
+
+ if ($scope.mode === 'add') {
+ const alwaysOption = {
+ label: $scope.strings.get('workflow_maker.ALWAYS'),
+ value: 'always'
+ };
+ const successOption = {
+ label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
+ value: 'success'
+ };
+ const failureOption = {
+ label: $scope.strings.get('workflow_maker.ON_FAILURE'),
+ value: 'failure'
+ };
+ $scope.edgeTypeOptions = [alwaysOption];
+ switch($scope.node.isRoot) {
+ case true:
+ $scope.edgeType = alwaysOption;
+ break;
+ case false:
+ $scope.edgeType = successOption;
+ $scope.edgeTypeOptions.push(successOption, failureOption);
+ break;
+ }
+ CreateSelect2({
+ element: '#workflow_node_edge_3',
+ multiple: false
+ });
+
+ $scope.nodeFormDataLoaded = true;
+ }
+ };
+ // Determine whether or not we need to go out and GET this nodes unified job template
+ // in order to determine whether or not prompt fields are needed
+
+ $scope.openPromptModal = function() {
+ $scope.promptData.triggerModalOpen = true;
+ };
+
+ $scope.toggle_row = function(selectedRow) {
+ if (!$scope.readOnly) {
+ // TODO: make this more concise
+ switch($scope.activeTab) {
+ case 'jobs':
+ $scope.templates.forEach(function(row, i) {
+ if (row.id === selectedRow.id) {
+ $scope.templates[i].checked = 1;
+
+ } else {
+ $scope.templates[i].checked = 0;
+ }
+ });
+ break;
+ case 'project_syncs':
+ $scope.projects.forEach(function(row, i) {
+ if (row.id === selectedRow.id) {
+ $scope.projects[i].checked = 1;
+ } else {
+ $scope.projects[i].checked = 0;
+ }
+ });
+ break;
+ case 'inventory_syncs':
+ $scope.inventory_sources.forEach(function(row, i) {
+ if (row.id === selectedRow.id) {
+ $scope.inventory_sources[i].checked = 1;
+ } else {
+ $scope.inventory_sources[i].checked = 0;
+ }
+ });
+ break;
+ }
+ templateManuallySelected(selectedRow);
+ }
+ };
+
+ const templateManuallySelected = (selectedTemplate) => {
+
+ if (promptWatcher) {
+ promptWatcher();
+ }
+
+ if (surveyQuestionWatcher) {
+ surveyQuestionWatcher();
+ }
+
+ if (credentialsWatcher) {
+ credentialsWatcher();
+ }
+
+ $scope.promptData = null;
+
+ if (selectedTemplate.type === "job_template") {
+ let jobTemplate = new JobTemplate();
+
+ $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)])
+ .then((responses) => {
+ let launchConf = responses[1].data;
+
+ if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) {
+ $scope.selectedTemplateInvalid = true;
+ } else {
+ $scope.selectedTemplateInvalid = false;
+ }
+
+ if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) {
+ $scope.credentialRequiresPassword = true;
+ } else {
+ $scope.credentialRequiresPassword = false;
+ }
+
+ $scope.selectedTemplate = angular.copy(selectedTemplate);
+
+ if (!launchConf.survey_enabled &&
+ !launchConf.ask_inventory_on_launch &&
+ !launchConf.ask_credential_on_launch &&
+ !launchConf.ask_verbosity_on_launch &&
+ !launchConf.ask_job_type_on_launch &&
+ !launchConf.ask_limit_on_launch &&
+ !launchConf.ask_tags_on_launch &&
+ !launchConf.ask_skip_tags_on_launch &&
+ !launchConf.ask_diff_mode_on_launch &&
+ !launchConf.credential_needed_to_start &&
+ !launchConf.ask_variables_on_launch &&
+ 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) {
+ // go out and get the survey questions
+ jobTemplate.getSurveyQuestions(selectedTemplate.id)
+ .then((surveyQuestionRes) => {
+
+ let processed = PromptService.processSurveyQuestions({
+ surveyQuestions: surveyQuestionRes.data.spec
+ });
+
+ $scope.missingSurveyValue = processed.missingSurveyValue;
+
+ $scope.promptData = {
+ launchConf: responses[1].data,
+ launchOptions: responses[0].data,
+ surveyQuestions: processed.surveyQuestions,
+ template: selectedTemplate.id,
+ prompts: PromptService.processPromptValues({
+ launchConf: responses[1].data,
+ launchOptions: responses[0].data
+ }),
+ };
+
+ surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
+ let missingSurveyValue = false;
+ _.each($scope.promptData.surveyQuestions, (question) => {
+ if (question.required && (Empty(question.model) || question.model === [])) {
+ missingSurveyValue = true;
+ }
+ });
+ $scope.missingSurveyValue = missingSurveyValue;
+ }, true);
+
+ watchForPromptChanges();
+ });
+ } else {
+ $scope.promptData = {
+ launchConf: responses[1].data,
+ launchOptions: responses[0].data,
+ template: selectedTemplate.id,
+ prompts: PromptService.processPromptValues({
+ launchConf: responses[1].data,
+ launchOptions: responses[0].data
+ }),
+ };
+
+ watchForPromptChanges();
+ }
+ }
+ });
+ } else {
+ $scope.selectedTemplate = angular.copy(selectedTemplate);
+ $scope.selectedTemplateInvalid = false;
+ $scope.showPromptButton = false;
+ $scope.promptModalMissingReqFields = false;
+ }
+ };
+
+ const setupNodeForm = () => {
+ $scope.nodeFormDataLoaded = false;
+ $scope.template_queryset = {
+ page_size: '5',
+ order_by: 'name',
+ type: 'workflow_job_template,job_template'
+ };
+
+ $scope.templates = [];
+ $scope.template_dataset = {};
+
+ // Go out and GET the list contents for each of the tabs
+
+ listPromises.push(
+ qs.search(GetBasePath('unified_job_templates'), $scope.template_queryset)
+ .then(function(res) {
+ $scope.template_dataset = res.data;
+ $scope.templates = $scope.template_dataset.results;
+ })
+ );
+
+ $scope.project_queryset = {
+ page_size: '5',
+ order_by: 'name'
+ };
+
+ $scope.projects = [];
+ $scope.project_dataset = {};
+
+ listPromises.push(
+ qs.search(GetBasePath('projects'), $scope.project_queryset)
+ .then(function(res) {
+ $scope.project_dataset = res.data;
+ $scope.projects = $scope.project_dataset.results;
+ })
+ );
+
+ $scope.inventory_source_dataset = {
+ page_size: '5',
+ order_by: 'name',
+ not__source: ''
+ }
+
+ $scope.inventory_sources = [];
+ $scope.inventory_source_dataset = {};
+
+ listPromises.push(
+ qs.search(GetBasePath('inventory_sources'), $scope.inventory_source_dataset)
+ .then(function(res) {
+ $scope.inventory_source_dataset = res.data;
+ $scope.inventory_sources = $scope.inventory_source_dataset.results;
+ })
+ );
+
+ $q.all(listPromises)
+ .then(() => {
+ if (!$scope.node.isNew && !$scope.node.edited && $scope.node.unifiedJobTemplate && $scope.node.unifiedJobTemplate.unified_job_type && $scope.node.unifiedJobTemplate.unified_job_type === 'job') {
+ // This is a node that we got back from the api with an incomplete
+ // unified job template so we're going to pull down the whole object
+
+ TemplatesService.getUnifiedJobTemplate($scope.node.unifiedJobTemplate.id)
+ .then(function(data) {
+ $scope.node.unifiedJobTemplate = _.clone(data.data.results[0]);
+ finishConfiguringEdit();
+ }, function(error) {
+ ProcessErrors($scope, error.data, error.status, null, {
+ hdr: 'Error!',
+ msg: 'Failed to get unified job template. GET returned ' +
+ 'status: ' + error.status
+ });
+ });
+ } else {
+ finishConfiguringEdit();
+ }
+ });
+ }
+
+ setupNodeForm();
}
];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.directive.js b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.directive.js
index 197b6ae86b..2a273a4e0f 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.directive.js
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.directive.js
@@ -9,13 +9,16 @@ import workflowNodeFormController from './workflow-node-form.controller';
export default ['templateUrl',
function(templateUrl) {
return {
- scope: {},
+ scope: {
+ mode: '<',
+ node: '=',
+ cancel: '&',
+ select: '&',
+ readOnly: '<'
+ },
restrict: 'E',
templateUrl: templateUrl('templates/workflows/workflow-maker/forms/workflow-node-form'),
- controller: workflowNodeFormController,
- link: function(scope) {
- console.log('inside link function for workflow node form');
- }
+ controller: workflowNodeFormController
};
}
];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html
index c35a26575a..c6c239f405 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/forms/workflow-node-form.partial.html
@@ -1,17 +1,111 @@
-
-
-
+
+
{{mode === 'edit' ? node.unifiedJobTemplate.name : strings.get('workflow_maker.ADD_A_TEMPLATE')}}
+
+
@@ -24,28 +118,29 @@
{{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}
-
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less
index 580d1aa231..3dd307a426 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.block.less
@@ -13,22 +13,26 @@
display: flex;
height: 34px;
}
+
.WorkflowMaker-title {
align-items: center;
flex: 1 0 auto;
display: flex;
height: 34px;
}
+
.WorkflowMaker-titleText {
color: @list-title-txt;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
}
+
.WorkflowMaker-exitHolder {
justify-content: flex-end;
display: flex;
}
+
.WorkflowMaker-exit{
cursor:pointer;
padding:0px;
@@ -40,9 +44,11 @@
transition: color 0.2s;
line-height:1;
}
+
.WorkflowMaker-exit:hover{
color:@default-icon;
}
+
.WorkflowMaker-contentHolder {
display: flex;
border: 1px solid @b7grey;
@@ -50,11 +56,13 @@
height: ~"calc(100% - 85px)";
overflow: hidden;
}
+
.WorkflowMaker-contentLeft {
flex: 1;
flex-direction: column;
height: 100%;
}
+
.WorkflowMaker-contentRight {
flex: 0 0 400px;
border-left: 1px solid @b7grey;
@@ -63,12 +71,14 @@
height: 100%;
overflow-y: scroll;
}
+
.WorkflowMaker-buttonHolder {
height: 30px;
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
+
.WorkflowMaker-saveButton{
background-color: @submit-button-bg;
color: @submit-button-text;
@@ -117,45 +127,55 @@
justify-content: center;
border-radius: 4px;
}
+
.WorkflowMaker-deleteModal {
height: 200px;
width: 600px;
background-color: @default-bg;
border-radius: 5px;
}
+
.WorkflowMaker-formTitle {
color: @list-title-txt;
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
}
+
.WorkflowMaker-formHelp {
color: @default-interface-txt;
}
+
.WorkflowMaker-formLists {
margin-bottom: 20px;
.SmartSearch-searchTermContainer {
width: 100%;
}
}
+
.WorkflowMaker-formTitle {
display: flex;
color: @default-interface-txt;
margin-right: 10px;
}
+
.WorkflowMaker-formLabel {
font-weight: normal;
}
+
.WorkflowMaker-formElement {
margin-bottom: 10px;
}
+
.WorkflowMaker-chart {
display: flex;
width: 100%;
}
+
.WorkflowMaker-totalJobs {
margin-right: 5px;
}
+
.WorkflowLegend-maker {
display: flex;
height: 40px;
@@ -164,33 +184,39 @@
background: @default-bg;
border-bottom: 1px solid @b7grey;
}
+
.WorkflowLegend-maker--left {
flex: 1 0 auto;
}
+
.WorkflowLegend-maker--right {
flex: 0 0 217px;
text-align: right;
padding-right: 20px;
position: relative;
}
+
.WorkflowLegend-onSuccessLegend {
height: 4px;
width: 20px;
background-color: @submit-button-bg;
margin: 18px 5px 18px 0px;
}
+
.WorkflowLegend-onFailLegend {
height: 4px;
width: 20px;
background-color: @default-err;
margin: 18px 5px 18px 0px;
}
+
.WorkflowLegend-alwaysLegend {
height: 4px;
width: 20px;
background-color: @default-link;
margin: 18px 5px 18px 0px;
}
+
.WorkflowLegend-letterCircle{
border-radius: 50%;
width: 20px;
@@ -201,6 +227,7 @@
margin: 10px 5px 10px 0px;
line-height: 20px;
}
+
.WorkflowLegend-details {
align-items: center;
display: flex;
@@ -215,6 +242,7 @@
display: block;
flex: 1 0 auto;
}
+
.WorkflowLegend-details--right {
flex: 0 0 44px;
text-align: right;
@@ -229,15 +257,18 @@
font-size: 1.2em;
margin-left: 15px;
}
+
.Key-menuIcon:hover,
.WorkflowMaker-manualControlsIcon:hover {
color: @default-link-hov;
cursor: pointer;
}
+
.Key-menuIcon--active,
.WorkflowMaker-manualControlsIcon--active {
color: @default-link-hov;
}
+
.WorkflowMaker-manualControls {
position: absolute;
left: -106px;
@@ -251,6 +282,7 @@
margin-left: -1px;
border-right: 0;
}
+
.WorkflowLegend-manualControls {
position: absolute;
left: -272px;
@@ -262,13 +294,16 @@
border: 1px solid @d7grey;
border-bottom-left-radius: 5px;
}
+
.WorkflowMaker-formTab {
margin-right: 10px;
}
+
.WorkflowMaker-preventBodyScrolling {
height: 100%;
overflow: hidden;
}
+
.WorkflowMaker-invalidJobTemplateWarning {
margin-bottom: 5px;
color: @default-err;
@@ -281,15 +316,18 @@
background-color: @default-bg;
border: 1px solid @default-list-header-bg;
}
+
.Key-listItem {
display: flex;
padding: 0;
margin: 5px 0 0 0;
}
+
.Key-listItemContent {
margin: 0;
line-height: 20px;
}
+
.Key-listItemContent--circle {
line-height: 28px;
}
@@ -300,27 +338,34 @@
margin: 9px 5px 9px 0px;
outline: none;
}
+
.Key-heading {
font-weight: 700;
margin: 0 0 10px;
line-height: 0;
padding: 0;
}
+
.Key-icon--success {
background-color: @submit-button-bg;
}
+
.Key-icon--fail {
background-color: @default-err;
}
+
.Key-icon--always {
background-color: @default-link;
}
+
.Key-icon--warning {
background: @default-warning;
}
+
.Key-icon--default {
background: @default-icon;
}
+
.Key-icon--circle {
border-radius: 50%;
width: 20px;
@@ -330,6 +375,7 @@
line-height: 20px;
margin: 4px 5px 5px 0px;
}
+
.Key-details {
display: flex;
height: 40px;
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 c3b52b74a1..c1fa993396 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
@@ -11,27 +11,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
ProcessErrors, CreateSelect2, $q, JobTemplate, WorkflowJobTemplate,
Empty, PromptService, Rest, TemplatesStrings, $timeout) {
- let promptWatcher, surveyQuestionWatcher, credentialsWatcher;
-
$scope.strings = TemplatesStrings;
+ // TODO: I don't think this needs to be on scope but changing it will require changes to
+ // all the prompt places
$scope.preventCredsWithPasswords = true;
- $scope.workflowMakerFormConfig = {
- nodeMode: "idle",
- activeTab: "jobs",
- formIsValid: false
- };
-
- $scope.job_type_options = [{
- label: $scope.strings.get('workflow_maker.RUN'),
- value: "run"
- }, {
- label: $scope.strings.get('workflow_maker.CHECK'),
- value: "check"
- }];
-
- $scope.edgeTypeOptions = createEdgeTypeOptions();
-
let editRequests = [];
let associateRequests = [];
let disassociateRequests = [];
@@ -41,32 +25,10 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.toggleKey = () => $scope.showKey = !$scope.showKey;
$scope.keyClassList = `{ 'Key-menuIcon--active': showKey }`;
- function createEdgeTypeOptions() {
- return ([{
- label: $scope.strings.get('workflow_maker.ALWAYS'),
- value: 'always'
- },
- {
- label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
- value: 'success'
- },
- {
- label: $scope.strings.get('workflow_maker.ON_FAILURE'),
- value: 'failure'
- }
- ]);
- }
-
- function resetNodeForm() {
- $scope.workflowMakerFormConfig.nodeMode = "idle";
- delete $scope.selectedTemplate;
- delete $scope.placeholderNode;
- delete $scope.betweenTwoNodes;
- $scope.nodeBeingEdited = null;
- $scope.workflowMakerFormConfig.activeTab = "jobs";
-
- $scope.$broadcast('clearWorkflowLists');
- }
+ $scope.formState = {
+ 'showNodeForm': false,
+ 'showLinkForm': false
+ };
function recursiveNodeUpdates(params, completionCallback) {
// params.parentId
@@ -301,62 +263,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
}
}
- let updateEdgeDropdownOptions = (edgeTypeValue) => {
- // Not passing an edgeTypeValue will include all by default
-
- if (edgeTypeValue) {
- $scope.edgeTypeOptions = _.filter(createEdgeTypeOptions(), {
- 'value': edgeTypeValue
- });
- } else {
- $scope.edgeTypeOptions = createEdgeTypeOptions();
- }
-
- CreateSelect2({
- element: '#workflow_node_edge',
- multiple: false
- });
- };
-
- let checkCredentialsForRequiredPasswords = () => {
- let credentialRequiresPassword = false;
- $scope.promptData.prompts.credentials.value.forEach((credential) => {
- if ((credential.passwords_needed &&
- credential.passwords_needed.length > 0) ||
- (_.has(credential, 'inputs.vault_password') &&
- credential.inputs.vault_password === "ASK")
- ) {
- credentialRequiresPassword = true;
- }
- });
- $scope.credentialRequiresPassword = credentialRequiresPassword;
- };
-
- let watchForPromptChanges = () => {
- let promptDataToWatch = [
- 'promptData.prompts.inventory.value',
- 'promptData.prompts.verbosity.value',
- 'missingSurveyValue'
- ];
-
- promptWatcher = $scope.$watchGroup(promptDataToWatch, function () {
- let missingPromptValue = false;
- if ($scope.missingSurveyValue) {
- missingPromptValue = true;
- } else if ($scope.selectedTemplate.type === 'job_template' && (!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id)) {
- missingPromptValue = true;
- }
- $scope.promptModalMissingReqFields = missingPromptValue;
- });
-
- if ($scope.promptData.launchConf.ask_credential_on_launch && $scope.credentialRequiresPassword) {
- credentialsWatcher = $scope.$watch('promptData.prompts.credentials', () => {
- checkCredentialsForRequiredPasswords();
- });
- }
- };
-
- $scope.closeWorkflowMaker = function () {
+ $scope.closeWorkflowMaker = function() {
// Revert the data to the master which was created when the dialog was opened
$scope.treeData.data = angular.copy($scope.treeDataMaster);
$scope.closeDialog();
@@ -440,19 +347,15 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.startAddNode = function (parent, betweenTwoNodes) {
- if ($scope.placeholderNode || $scope.nodeBeingEdited) {
+ if ($scope.nodeBeingWorkedOn) {
$scope.cancelNodeForm();
}
- if ($scope.linkBeingEdited) {
+ if ($scope.linkBeingWorkedOn) {
$scope.cancelLinkForm();
}
- $scope.workflowMakerFormConfig.nodeMode = "add";
- $scope.addParent = parent;
- $scope.betweenTwoNodes = betweenTwoNodes;
-
- $scope.placeholderNode = WorkflowService.addPlaceholderNode({
+ $scope.nodeBeingWorkedOn = WorkflowService.addPlaceholderNode({
parent: parent,
betweenTwoNodes: betweenTwoNodes,
tree: $scope.treeData.data,
@@ -461,440 +364,96 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.treeData.nextIndex++;
- // Set the default to success
- let edgeType = {
- label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
- value: "success"
- };
-
- if (parent && ((betweenTwoNodes && parent.source.isStartNode) || (!betweenTwoNodes && parent.isStartNode))) {
- // This node will always be executed
- updateEdgeDropdownOptions('always');
- edgeType = {
- label: $scope.strings.get('workflow_maker.ALWAYS'),
- value: "always"
- };
- } else {
- updateEdgeDropdownOptions();
- }
-
- $scope.edgeType = edgeType;
$scope.$broadcast("refreshWorkflowChart");
+ $scope.nodeFormMode = "add";
+ $scope.formState.showNodeForm = true;
};
- $scope.confirmNodeForm = function () {
- if ($scope.workflowMakerFormConfig.nodeMode === "add") {
- if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) {
-
- $scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate;
- $scope.placeholderNode.edgeType = $scope.edgeType.value;
- if ($scope.placeholderNode.unifiedJobTemplate.type === 'job_template' ||
- $scope.placeholderNode.unifiedJobTemplate.type === 'workflow_job_template') {
- $scope.placeholderNode.promptData = _.cloneDeep($scope.promptData);
+ $scope.confirmNodeForm = function(selectedTemplate, promptData, edgeType) {
+ if ($scope.nodeFormMode === "add") {
+ if (selectedTemplate && edgeType && edgeType.value) {
+ $scope.nodeBeingWorkedOn.unifiedJobTemplate = selectedTemplate;
+ $scope.nodeBeingWorkedOn.edgeType = edgeType.value;
+ if ($scope.nodeBeingWorkedOn.unifiedJobTemplate.type === 'job_template') {
+ $scope.nodeBeingWorkedOn.promptData = _.cloneDeep(promptData);
}
- $scope.placeholderNode.canEdit = true;
+ $scope.nodeBeingWorkedOn.canEdit = true;
- delete $scope.placeholderNode.placeholder;
-
- resetNodeForm();
+ delete $scope.nodeBeingWorkedOn.placeholder;
// Increment the total node counter
$scope.treeData.data.totalNodes++;
}
- } else if ($scope.workflowMakerFormConfig.nodeMode === "edit") {
- if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) {
- $scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate;
- $scope.nodeBeingEdited.edgeType = $scope.edgeType.value;
- if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template' || $scope.nodeBeingEdited.unifiedJobTemplate.type === 'workflow_job_template') {
- $scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData);
+ } else if ($scope.nodeFormMode === "edit") {
+ if (selectedTemplate) {
+ $scope.nodeBeingWorkedOn.unifiedJobTemplate = selectedTemplate;
+
+ if ($scope.nodeBeingWorkedOn.unifiedJobTemplate.type === 'job_template') {
+ $scope.nodeBeingWorkedOn.promptData = _.cloneDeep(promptData);
}
- $scope.nodeBeingEdited.isActiveEdit = false;
+ $scope.nodeBeingWorkedOn.isActiveEdit = false;
- $scope.nodeBeingEdited.edited = true;
-
- resetNodeForm();
+ $scope.nodeBeingWorkedOn.edited = true;
}
}
- if (promptWatcher) {
- promptWatcher();
- }
-
- if (surveyQuestionWatcher) {
- surveyQuestionWatcher();
- }
-
- if (credentialsWatcher) {
- credentialsWatcher();
- }
-
- $scope.promptData = null;
+ $scope.formState.showNodeForm = false;
+ $scope.nodeFormMode = null;
+ $scope.nodeBeingWorkedOn = null;
$scope.$broadcast("refreshWorkflowChart");
};
- $scope.cancelNodeForm = function () {
- if ($scope.workflowMakerFormConfig.nodeMode === "add") {
+ $scope.cancelNodeForm = function() {
+ if ($scope.nodeFormMode === "add") {
// Remove the placeholder node from the tree
WorkflowService.removeNodeFromTree({
tree: $scope.treeData.data,
- nodeToBeDeleted: $scope.placeholderNode
+ nodeToBeDeleted: $scope.nodeBeingWorkedOn
});
- } else if ($scope.workflowMakerFormConfig.nodeMode === "edit") {
- $scope.nodeBeingEdited.isActiveEdit = false;
+ } else if ($scope.nodeFormMode === "edit") {
+ $scope.nodeBeingWorkedOn.isActiveEdit = false;
}
-
- if (promptWatcher) {
- promptWatcher();
- }
-
- if (surveyQuestionWatcher) {
- surveyQuestionWatcher();
- }
-
- if (credentialsWatcher) {
- credentialsWatcher();
- }
-
- $scope.promptData = null;
- $scope.selectedTemplateInvalid = false;
- $scope.showPromptButton = false;
-
- // Reset the form
- resetNodeForm();
-
+ $scope.formState.showNodeForm = false;
+ $scope.nodeBeingWorkedOn = null;
+ $scope.nodeFormMode = null;
$scope.$broadcast("refreshWorkflowChart");
};
/* EDIT NODE FUNCTIONS */
- $scope.startEditNode = function (nodeToEdit) {
-
- if ($scope.linkBeingEdited) {
+ $scope.startEditNode = function(nodeToEdit) {
+ if ($scope.linkBeingWorkedOn) {
$scope.cancelLinkForm();
}
- if (!$scope.nodeBeingEdited || ($scope.nodeBeingEdited && $scope.nodeBeingEdited.id !== nodeToEdit.id)) {
- if ($scope.placeholderNode || $scope.nodeBeingEdited) {
+ if (!$scope.nodeBeingWorkedOn || ($scope.nodeBeingWorkedOn && $scope.nodeBeingWorkedOn.id !== nodeToEdit.id)) {
+ if ($scope.nodeBeingWorkedOn) {
$scope.cancelNodeForm();
-
- // Refresh this object as the parent has changed
- nodeToEdit = WorkflowService.searchTree({
- element: $scope.treeData.data,
- matchingId: nodeToEdit.id
- });
}
- $scope.workflowMakerFormConfig.nodeMode = "edit";
+ $scope.nodeFormMode = "edit";
+
+ $scope.formState.showNodeForm = true;
let parent = WorkflowService.searchTree({
element: $scope.treeData.data,
matchingId: nodeToEdit.parent.id
});
- $scope.nodeBeingEdited = WorkflowService.searchTree({
+ $scope.nodeBeingWorkedOn = WorkflowService.searchTree({
element: parent,
matchingId: nodeToEdit.id
});
- $scope.nodeBeingEdited.isActiveEdit = true;
-
- let finishConfiguringEdit = function () {
- let templateType = $scope.nodeBeingEdited.unifiedJobTemplate.type;
- let jobTemplate = templateType === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate();
- if (!_.isEmpty($scope.nodeBeingEdited.promptData)) {
- $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData);
- const launchConf = $scope.promptData.launchConf;
-
- if (!launchConf.survey_enabled &&
- !launchConf.ask_inventory_on_launch &&
- !launchConf.ask_credential_on_launch &&
- !launchConf.ask_verbosity_on_launch &&
- !launchConf.ask_job_type_on_launch &&
- !launchConf.ask_limit_on_launch &&
- !launchConf.ask_tags_on_launch &&
- !launchConf.ask_skip_tags_on_launch &&
- !launchConf.ask_diff_mode_on_launch &&
- !launchConf.credential_needed_to_start &&
- !launchConf.ask_variables_on_launch &&
- launchConf.variables_needed_to_start.length === 0) {
- $scope.showPromptButton = false;
- $scope.promptModalMissingReqFields = false;
- } else {
- $scope.showPromptButton = true;
-
- if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template' && launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
- $scope.promptModalMissingReqFields = true;
- } else {
- $scope.promptModalMissingReqFields = false;
- }
- }
- } else if (
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job' ||
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'job_template' ||
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'workflow_job' ||
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === 'workflow_job_template'
- ) {
- let promises = [jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id)];
-
- if (_.has($scope, 'nodeBeingEdited.originalNodeObj.related.credentials')) {
- Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials);
- promises.push(Rest.get());
- }
-
- $q.all(promises)
- .then((responses) => {
- let launchOptions = responses[0].data,
- launchConf = responses[1].data,
- workflowNodeCredentials = responses[2] ? responses[2].data.results : [];
-
- let prompts = PromptService.processPromptValues({
- launchConf: responses[1].data,
- launchOptions: responses[0].data,
- currentValues: $scope.nodeBeingEdited.originalNodeObj
- });
-
- let defaultCredsWithoutOverrides = [];
-
- prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials);
-
- const credentialHasScheduleOverride = (templateDefaultCred) => {
- let credentialHasOverride = false;
- workflowNodeCredentials.forEach((scheduleCred) => {
- if (templateDefaultCred.credential_type === scheduleCred.credential_type) {
- if (
- (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
- (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
- ) {
- credentialHasOverride = true;
- }
- }
- });
-
- return credentialHasOverride;
- };
-
- if (_.has(launchConf, 'defaults.credentials')) {
- launchConf.defaults.credentials.forEach((defaultCred) => {
- if (!credentialHasScheduleOverride(defaultCred)) {
- defaultCredsWithoutOverrides.push(defaultCred);
- }
- });
- }
-
- prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
-
- if ($scope.nodeBeingEdited.unifiedJobTemplate.unified_job_template === 'job') {
- if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) {
- $scope.selectedTemplateInvalid = true;
- } else {
- $scope.selectedTemplateInvalid = false;
- }
- } else {
- $scope.selectedTemplateInvalid = false;
- }
-
- let credentialRequiresPassword = false;
-
- prompts.credentials.value.forEach((credential) => {
- if (credential.inputs) {
- if ((credential.inputs.password && credential.inputs.password === "ASK") ||
- (credential.inputs.become_password && credential.inputs.become_password === "ASK") ||
- (credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") ||
- (credential.inputs.vault_password && credential.inputs.vault_password === "ASK")
- ) {
- credentialRequiresPassword = true;
- }
- } else if (credential.passwords_needed && credential.passwords_needed.length > 0) {
- credentialRequiresPassword = true;
- }
- });
-
- $scope.credentialRequiresPassword = credentialRequiresPassword;
-
- if (!launchConf.survey_enabled &&
- !launchConf.ask_inventory_on_launch &&
- !launchConf.ask_credential_on_launch &&
- !launchConf.ask_verbosity_on_launch &&
- !launchConf.ask_job_type_on_launch &&
- !launchConf.ask_limit_on_launch &&
- !launchConf.ask_tags_on_launch &&
- !launchConf.ask_skip_tags_on_launch &&
- !launchConf.ask_diff_mode_on_launch &&
- !launchConf.credential_needed_to_start &&
- !launchConf.ask_variables_on_launch &&
- 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) {
- // go out and get the survey questions
- jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id)
- .then((surveyQuestionRes) => {
-
- let processed = PromptService.processSurveyQuestions({
- surveyQuestions: surveyQuestionRes.data.spec,
- extra_data: _.cloneDeep($scope.nodeBeingEdited.originalNodeObj.extra_data)
- });
-
- $scope.missingSurveyValue = processed.missingSurveyValue;
-
- $scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
-
- $scope.nodeBeingEdited.promptData = $scope.promptData = {
- launchConf: launchConf,
- launchOptions: launchOptions,
- prompts: prompts,
- surveyQuestions: surveyQuestionRes.data.spec,
- template: $scope.nodeBeingEdited.unifiedJobTemplate.id
- };
-
- surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
- let missingSurveyValue = false;
- _.each($scope.promptData.surveyQuestions, (question) => {
- if (question.required && (Empty(question.model) || question.model === [])) {
- missingSurveyValue = true;
- }
- });
- $scope.missingSurveyValue = missingSurveyValue;
- }, true);
-
- checkCredentialsForRequiredPasswords();
-
- watchForPromptChanges();
- });
- } else {
- $scope.nodeBeingEdited.promptData = $scope.promptData = {
- launchConf: launchConf,
- launchOptions: launchOptions,
- prompts: prompts,
- template: $scope.nodeBeingEdited.unifiedJobTemplate.id
- };
-
- checkCredentialsForRequiredPasswords();
-
- watchForPromptChanges();
- }
- }
- });
- }
-
- if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) {
-
- if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template" ||
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "workflow_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":
- case "workflow_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":
- case "workflow_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";
- }
-
- let edgeDropdownOptions = null;
-
- // Select RUN dropdown option
- switch ($scope.nodeBeingEdited.edgeType) {
- case "always":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ALWAYS'),
- value: "always"
- };
- if ($scope.nodeBeingEdited.isRoot) {
- edgeDropdownOptions = 'always';
- }
- break;
- case "success":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
- value: "success"
- };
- break;
- case "failure":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ON_FAILURE'),
- value: "failure"
- };
- break;
- }
-
- $timeout(updateEdgeDropdownOptions(edgeDropdownOptions));
-
- $scope.$broadcast("refreshWorkflowChart");
- };
-
- // Determine whether or not we need to go out and GET this nodes unified job template
- // in order to determine whether or not prompt fields are needed
- if (!$scope.nodeBeingEdited.isNew && !$scope.nodeBeingEdited.edited &&
- (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'job' ||
- _.get($scope, 'nodeBeingEdited.unifiedJobTemplate.unified_job_type') === 'workflow_job')) {
- // This is a node that we got back from the api with an incomplete
- // unified job template so we're going to pull down the whole object
-
- TemplatesService.getUnifiedJobTemplate($scope.nodeBeingEdited.unifiedJobTemplate.id)
- .then(function (data) {
- $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
- finishConfiguringEdit();
- }, function ({
- data,
- status,
- config
- }) {
- ProcessErrors($scope, data, status, null, {
- hdr: $scope.strings.get('error.HEADER'),
- msg: $scope.strings.get('error.CALL', {
- path: `${config.url}`,
- action: `${config.method}`,
- status
- })
- });
- });
- } else {
- finishConfiguringEdit();
- }
-
+ $scope.nodeBeingWorkedOn.isActiveEdit = true;
}
- };
+ $scope.$broadcast("refreshWorkflowChart");
+ }
/* EDIT LINK FUNCTIONS */
@@ -902,18 +461,17 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
const setupLinkEdit = () => {
const parentNode = WorkflowService.searchTree({
element: $scope.treeData.data,
- matchingId: parentId,
- byNodeId: true
+ matchingId: parentId
});
parentNode.isLinkEditParent = true;
// Loop across children looking for childId
- const childNode = _.find(parentNode.children, {'nodeId': childId});
+ const childNode = _.find(parentNode.children, {'id': childId});
childNode.isLinkEditChild = true;
- $scope.linkBeingEdited = {
+ $scope.linkBeingWorkedOn = {
parent: parentNode,
child: childNode
}
@@ -929,19 +487,19 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
},
edgeType: childNode.edgeType
}
- $scope.editLink = true;
+ $scope.formState.showLinkForm = true;
$scope.$broadcast("refreshWorkflowChart");
}
- if ($scope.nodeBeingEdited || $scope.placeholderNode) {
+ if ($scope.nodeBeingWorkedOn) {
$scope.cancelNodeForm();
}
- if ($scope.linkBeingEdited) {
- if ($scope.linkBeingEdited.parent.nodeId !== parentId || $scope.linkBeingEdited.child.nodeId !== childId) {
- $scope.linkBeingEdited.parent.isLinkEditParent = false;
- $scope.linkBeingEdited.child.isLinkEditChild = false;
+ if ($scope.linkBeingWorkedOn) {
+ if ($scope.linkBeingWorkedOn.parent.nodeId !== parentId || $scope.linkBeingWorkedOn.child.nodeId !== childId) {
+ $scope.linkBeingWorkedOn.parent.isLinkEditParent = false;
+ $scope.linkBeingWorkedOn.child.isLinkEditChild = false;
setupLinkEdit()
}
} else {
@@ -950,34 +508,20 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
};
- $scope.confirmLinkForm = (parentId, childId, edgeType) => {
- $scope.linkBeingEdited.parent.isLinkEditParent = false;
- $scope.linkBeingEdited.child.isLinkEditChild = false;
- const parentNode = WorkflowService.searchTree({
- element: $scope.treeData.data,
- matchingId: parentId,
- byNodeId: true
- });
-
- // Loop across children looking for childId
- const childNode = _.find(parentNode.children, {'nodeId': childId});
-
- childNode.edgeType = edgeType;
-
- $scope.linkBeingEdited = null;
-
- $scope.editLink = false;
-
+ $scope.confirmLinkForm = (newEdgeType) => {
+ $scope.linkBeingWorkedOn.parent.isLinkEditParent = false;
+ $scope.linkBeingWorkedOn.child.isLinkEditChild = false;
+ $scope.linkBeingWorkedOn.child.edgeType = newEdgeType;
+ $scope.linkBeingWorkedOn = null;
+ $scope.formState.showLinkForm = false;
$scope.$broadcast("refreshWorkflowChart");
}
$scope.cancelLinkForm = () => {
- $scope.linkBeingEdited.parent.isLinkEditParent = false;
- $scope.linkBeingEdited.child.isLinkEditChild = false;
- $scope.linkBeingEdited = null;
-
- $scope.editLink = false;
-
+ $scope.linkBeingWorkedOn.parent.isLinkEditParent = false;
+ $scope.linkBeingWorkedOn.child.isLinkEditChild = false;
+ $scope.linkBeingWorkedOn = null;
+ $scope.formState.showLinkForm = false;
$scope.$broadcast("refreshWorkflowChart");
};
@@ -1000,7 +544,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.confirmDeleteNode = function () {
if ($scope.nodeToBeDeleted) {
- if ($scope.linkBeingEdited) {
+ if ($scope.linkBeingWorkedOn) {
$scope.cancelLinkForm();
}
@@ -1015,195 +559,16 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.treeData.data.deletedNodes.push($scope.nodeToBeDeleted.nodeId);
}
- if ($scope.nodeToBeDeleted.isActiveEdit) {
- resetNodeForm();
- }
-
resetDeleteNode();
$scope.$broadcast("refreshWorkflowChart");
- if ($scope.placeholderNode) {
- let edgeType = {
- label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
- value: "success"
- };
-
- if ($scope.placeholderNode.isRoot) {
- updateEdgeDropdownOptions('always');
- edgeType = {
- label: $scope.strings.get('workflow_maker.ALWAYS'),
- value: "always"
- };
- } else {
- updateEdgeDropdownOptions();
- }
-
- $scope.edgeType = edgeType;
- } else if ($scope.nodeBeingEdited) {
-
- switch ($scope.nodeBeingEdited.edgeType) {
- case "always":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ALWAYS'),
- value: "always"
- };
- if ($scope.nodeBeingEdited.isRoot) {
- updateEdgeDropdownOptions('always');
- } else {
- updateEdgeDropdownOptions();
- }
- break;
- case "success":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ON_SUCCESS'),
- value: "success"
- };
- updateEdgeDropdownOptions();
- break;
- case "failure":
- $scope.edgeType = {
- label: $scope.strings.get('workflow_maker.ON_FAILURE'),
- value: "failure"
- };
- updateEdgeDropdownOptions();
- break;
- }
- }
-
$scope.treeData.data.totalNodes--;
}
};
- $scope.toggleFormTab = function (tab) {
- if ($scope.workflowMakerFormConfig.activeTab !== tab) {
- $scope.workflowMakerFormConfig.activeTab = tab;
- }
- };
-
- $scope.templateManuallySelected = function (selectedTemplate) {
-
- if (promptWatcher) {
- promptWatcher();
- }
-
- if (surveyQuestionWatcher) {
- surveyQuestionWatcher();
- }
-
- if (credentialsWatcher) {
- credentialsWatcher();
- }
-
- $scope.promptData = null;
- if (selectedTemplate.type === "job_template" || selectedTemplate.type === "workflow_job_template") {
- let jobTemplate = selectedTemplate.type === "workflow_job_template" ? new WorkflowJobTemplate() : new JobTemplate();
-
- $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)])
- .then((responses) => {
- let launchConf = responses[1].data;
- if (selectedTemplate.type === 'job_template') {
- if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) {
- $scope.selectedTemplateInvalid = true;
- } else {
- $scope.selectedTemplateInvalid = false;
- }
-
- if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) {
- $scope.credentialRequiresPassword = true;
- } else {
- $scope.credentialRequiresPassword = false;
- }
- }
-
- $scope.selectedTemplate = angular.copy(selectedTemplate);
-
- if (!launchConf.survey_enabled &&
- !launchConf.ask_inventory_on_launch &&
- !launchConf.ask_credential_on_launch &&
- !launchConf.ask_verbosity_on_launch &&
- !launchConf.ask_job_type_on_launch &&
- !launchConf.ask_limit_on_launch &&
- !launchConf.ask_tags_on_launch &&
- !launchConf.ask_skip_tags_on_launch &&
- !launchConf.ask_diff_mode_on_launch &&
- !launchConf.credential_needed_to_start &&
- !launchConf.ask_variables_on_launch &&
- launchConf.variables_needed_to_start.length === 0) {
- $scope.showPromptButton = false;
- $scope.promptModalMissingReqFields = false;
- } else {
- $scope.showPromptButton = true;
-
- if (selectedTemplate.type === 'job_template') {
- if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
- $scope.promptModalMissingReqFields = true;
- } else {
- $scope.promptModalMissingReqFields = false;
- }
- } else {
- $scope.promptModalMissingReqFields = false;
- }
-
- if (launchConf.survey_enabled) {
- // go out and get the survey questions
- jobTemplate.getSurveyQuestions(selectedTemplate.id)
- .then((surveyQuestionRes) => {
-
- let processed = PromptService.processSurveyQuestions({
- surveyQuestions: surveyQuestionRes.data.spec
- });
-
- $scope.missingSurveyValue = processed.missingSurveyValue;
-
- $scope.promptData = {
- launchConf: responses[1].data,
- launchOptions: responses[0].data,
- surveyQuestions: processed.surveyQuestions,
- template: selectedTemplate.id,
- prompts: PromptService.processPromptValues({
- launchConf: responses[1].data,
- launchOptions: responses[0].data
- }),
- };
-
- surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
- let missingSurveyValue = false;
- _.each($scope.promptData.surveyQuestions, (question) => {
- if (question.required && (Empty(question.model) || question.model === [])) {
- missingSurveyValue = true;
- }
- });
- $scope.missingSurveyValue = missingSurveyValue;
- }, true);
-
- watchForPromptChanges();
- });
- } else {
- $scope.promptData = {
- launchConf: responses[1].data,
- launchOptions: responses[0].data,
- template: selectedTemplate.id,
- prompts: PromptService.processPromptValues({
- launchConf: responses[1].data,
- launchOptions: responses[0].data
- }),
- };
-
- watchForPromptChanges();
- }
- }
- });
- } else {
- $scope.selectedTemplate = angular.copy(selectedTemplate);
- $scope.selectedTemplateInvalid = false;
- $scope.showPromptButton = false;
- $scope.promptModalMissingReqFields = false;
- }
- };
-
- $scope.toggleManualControls = function () {
+ $scope.toggleManualControls = function() {
$scope.showManualControls = !$scope.showManualControls;
};
@@ -1233,10 +598,6 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
$scope.$broadcast('zoomToFitChart');
};
- $scope.openPromptModal = function () {
- $scope.promptData.triggerModalOpen = true;
- };
-
let allNodes = [];
let page = 1;
@@ -1275,11 +636,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
// This is the last page
buildTreeFromNodes();
}
- }, function ({
- data,
- status,
- config
- }) {
+ }, function ({ data, status, config }) {
ProcessErrors($scope, data, status, null, {
hdr: $scope.strings.get('error.HEADER'),
msg: $scope.strings.get('error.CALL', {
@@ -1292,7 +649,5 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
};
getNodes();
-
- updateEdgeDropdownOptions();
}
];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
index b7c18669e5..aaa1c6aef0 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
@@ -84,71 +84,16 @@
-
- {{(workflowMakerFormConfig.nodeMode === 'edit' && nodeBeingEdited) ? ((nodeBeingEdited.unifiedJobTemplate && nodeBeingEdited.unifiedJobTemplate.name) ? nodeBeingEdited.unifiedJobTemplate.name : strings.get('workflow_maker.EDIT_TEMPLATE')) : strings.get('workflow_maker.ADD_A_TEMPLATE')}}
-
-
+
+
-
-
+
+
-
+
-
diff --git a/awx/ui/client/src/workflow-results/workflow-results.block.less b/awx/ui/client/src/workflow-results/workflow-results.block.less
index 3fbe218d88..60141e7a3a 100644
--- a/awx/ui/client/src/workflow-results/workflow-results.block.less
+++ b/awx/ui/client/src/workflow-results/workflow-results.block.less
@@ -157,3 +157,10 @@
border-radius: 5px;
font-size: 11px;
}
+
+.WorkflowResults-rightSide .WorkflowChart-svg {
+ background-color: @f6grey;
+ border: 1px solid @d7grey;
+ border-top: 0px;
+ border-bottom-right-radius: 5px;
+}