mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 16:51:11 +03:00
Completed work necessary to support editing workflow links and nodes separately. Added hover and tooltip to links
This commit is contained in:
parent
87d6253176
commit
29b4979736
@ -111,6 +111,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'),
|
||||
@ -122,7 +124,8 @@ function TemplatesStrings (BaseString) {
|
||||
INVENTORY_WILL_NOT_OVERRIDE: t.s('The inventory of this node will not be overridden by the parent workflow inventory.'),
|
||||
INVENTORY_PROMPT_WILL_OVERRIDE: t.s('The inventory of this node will be overridden if a parent workflow inventory is provided at launch.'),
|
||||
INVENTORY_PROMPT_WILL_NOT_OVERRIDE: t.s('The inventory of this node will not be overridden if a parent workflow inventory is provided at launch.'),
|
||||
EDIT_LINK: ({ parentName, childName }) => t.s('EDIT LINK | {{parentName}} to {{childName}}', { parentName, childName })
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 `<span class="fa fa-link" />`;
|
||||
// })
|
||||
// .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 `<div class='WorkflowChart-tooltipContents'><div>${TemplatesStrings.get('workflow_maker.RUN')}: ${edgeTypeLabel}</div><div>${linkInstructionText}</div></div><div class='${arrowClass}'></div>`;
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
.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 `<div class='WorkflowChart-tooltipContents'><div>${TemplatesStrings.get('workflow_maker.RUN')}: ${edgeTypeLabel}</div><div>${linkInstructionText}</div></div><div class='${arrowClass}'></div>`;
|
||||
});
|
||||
}
|
||||
})
|
||||
.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
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ export default ['templateUrl',
|
||||
return {
|
||||
scope: {
|
||||
linkConfig: '<',
|
||||
readOnly: '<',
|
||||
cancel: '&',
|
||||
select: '&'
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="WorkflowMaker-formTitle">{{:: strings.get('workflow_maker.EDIT_LINK', {parentName: linkConfig.parent.name, childName: linkConfig.child.name}) }}</div>
|
||||
<div class="WorkflowMaker-formTitle">{{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}) }}</div>
|
||||
<div class="WorkflowMaker-form">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn">
|
||||
<label for="edgeType" class="Form-inputLabelContainer">
|
||||
@ -13,13 +13,15 @@
|
||||
class="form-control Form-dropDown"
|
||||
name="edgeType"
|
||||
tabindex="-1"
|
||||
ng-disabled="readOnly"
|
||||
aria-hidden="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons Form-buttons" id="workflow_maker_controls">
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_link_btn" ng-click="select({parentId: linkConfig.parent.id, childId: linkConfig.child.id, edgeType: edgeType.value})" ng-disabled="!edgeType"> {{:: strings.get('workflow_maker.SELECT') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="!readOnly" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_link_btn" ng-show="readOnly" ng-click="cancel()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_link_btn" ng-show="!readOnly" ng-click="select({edgeType: edgeType.value})" ng-disabled="!edgeType"> {{:: strings.get('SAVE') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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();
|
||||
}
|
||||
];
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@ -1,17 +1,111 @@
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'jobs'}" ng-click="toggleFormTab('jobs')">{{strings.get('workflow_maker.JOBS')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'project_sync'}" ng-click="toggleFormTab('project_sync')">{{strings.get('workflow_maker.PROJECT_SYNC')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'inventory_sync'}" ng-click="toggleFormTab('inventory_sync')">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</div>
|
||||
</div>
|
||||
<div class="WorkflowMaker-formLists">
|
||||
<div id="workflow-jobs-list" ui-view="jobTemplateList" ng-show="workflowMakerFormConfig.activeTab === 'jobs'"></div>
|
||||
<div id="workflow-project-sync-list" ui-view="projectSyncList" ng-show="workflowMakerFormConfig.activeTab === 'project_sync'"></div>
|
||||
<div id="workflow-inventory-sync-list" ui-view="inventorySyncList" ng-show="workflowMakerFormConfig.activeTab === 'inventory_sync'"></div>
|
||||
</div>
|
||||
<span ng-show="selectedTemplate &&
|
||||
((selectedTemplate.type === 'job_template' && workflowMakerFormConfig.activeTab === 'jobs') ||
|
||||
(selectedTemplate.type === 'project' && workflowMakerFormConfig.activeTab === 'project_sync') ||
|
||||
(selectedTemplate.type === 'inventory_source' && workflowMakerFormConfig.activeTab === 'inventory_sync'))">
|
||||
<div ng-show="nodeFormDataLoaded">
|
||||
<div class="WorkflowMaker-formTitle ng-binding">{{mode === 'edit' ? node.unifiedJobTemplate.name : strings.get('workflow_maker.ADD_A_TEMPLATE')}}</div>
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'jobs'}" ng-click="activeTab = 'jobs'">{{strings.get('workflow_maker.JOBS')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'project_syncs'}" ng-click="activeTab = 'project_syncs'">{{strings.get('workflow_maker.PROJECT_SYNC')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': activeTab === 'inventory_syncs'}" ng-click="activeTab = 'inventory_syncs'">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</div>
|
||||
</div>
|
||||
<div class="WorkflowMaker-formLists">
|
||||
<div id="workflow-jobs-list" ng-show="activeTab === 'jobs'">
|
||||
<div ng-hide="templates.length === 0 && (searchTags | isEmpty)">
|
||||
<smart-search django-model="templates" base-path="unified_job_templates" iterator="template" dataset="template_dataset" list="templateList" collection="templates" default-params="template_default_params" query-set="template_queryset" search-bar-full-width="true" search-tags="searchTags">
|
||||
</smart-search>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="templates.length === 0 && !(searchTags | isEmpty)">
|
||||
<div class="col-lg-12 List-searchNoResults" translate>No records matched your search.</div>
|
||||
</div>
|
||||
<div class="List-noItems" ng-show="templates.length === 0 && (searchTags | isEmpty)">PLEASE ADD ITEMS TO THIS LIST</div>
|
||||
<div class="list-table-container" ng-show="templates.length > 0">
|
||||
<table id="templates_table" class="List-table" is-extended="false">
|
||||
<thead>
|
||||
<tr class="List-tableHeaderRow">
|
||||
<th class="List-tableHeader select-column List-staticColumn--smallStatus" translate=""></th>
|
||||
<th base-path="unified_job_templates" collection="templates" dataset="template_dataset" column-sort="" column-field="name" column-iterator="template" column-no-sort="undefined" column-label="Name" column-custom-class="" query-set="template_queryset">
|
||||
</th>
|
||||
<th class="List-tableHeader--info" base-path="unified_job_templates" collection="templates" dataset="template_dataset" column-sort="" column-field="info" column-iterator="template" column-no-sort="true" column-label="" column-custom-class="" query-set="template_queryset">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-class="[template.success_class, {'List-tableRow--selected' : $stateParams['template_id'] == template.id}, {'List-tableRow--disabled': !template.summary_fields.user_capabilities.edit}]" id="{{ template.id }}" class="List-tableRow template_class" disable-row="{{ !template.summary_fields.user_capabilities.edit }}" ng-repeat="template in templates">
|
||||
<td class="List-tableCell">
|
||||
<input type="radio" ng-model="template.checked" ng-value="1" ng-false-value="0" name="check_template_{{template.id}}" ng-click="toggle_row(template)" ng-disabled="!template.summary_fields.user_capabilities.edit">
|
||||
</td>
|
||||
<td class="List-tableCell name-column col-md-8" ng-click="toggle_row(template)">
|
||||
{{ template.name }}</td>
|
||||
<td class="col-md-3" ng-include="'/static/partials/job-template-details.html'"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<paginate base-path="unified_job_templates" collection="templates" dataset="template_dataset" iterator="template" query-set="template_queryset" hide-view-per-page="true" max-visible-pages="5"></paginate>
|
||||
</div>
|
||||
<div id="workflow-project-sync-list" ng-show="activeTab === 'project_syncs'">
|
||||
<div ng-hide="projects.length === 0 && (searchTags | isEmpty)">
|
||||
<smart-search django-model="projects" base-path="projects" iterator="project" dataset="project_dataset" list="projectList" collection="projects" default-params="project_default_params" query-set="project_queryset" search-bar-full-width="true" search-tags="searchTags">
|
||||
</smart-search>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="projects.length === 0 && !(searchTags | isEmpty)">
|
||||
<div class="col-lg-12 List-searchNoResults" translate>No records matched your search.</div>
|
||||
</div>
|
||||
<div class="List-noItems" ng-show="projects.length === 0 && (searchTags | isEmpty)">No Projects Have Been Created</div>
|
||||
<div class="list-table-container" ng-show="projects.length > 0">
|
||||
<table id="projects_table" class="List-table" is-extended="false">
|
||||
<thead>
|
||||
<tr class="List-tableHeaderRow">
|
||||
<th class="List-tableHeader select-column List-staticColumn--smallStatus" translate=""></th>
|
||||
<th base-path="projects" collection="projects" dataset="project_dataset" column-sort="" column-field="name" column-iterator="project" column-no-sort="undefined" column-label="Name" column-custom-class="col-md-8" query-set="project_queryset">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-class="[project.success_class, {'List-tableRow--selected' : $stateParams['project_id'] == project.id}]" id="{{ project.id }}" class="List-tableRow project_class" ng-repeat="project in projects">
|
||||
<td class="List-tableCell">
|
||||
<input type="radio" ng-model="project.checked" ng-value="1" ng-false-value="0" name="check_project_{{project.id}}" ng-click="toggle_row(project)" ng-disabled="undefined">
|
||||
</td>
|
||||
<td class="List-tableCell name-column col-md-8" ng-click="toggle_row(project)">
|
||||
{{ project.name }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<paginate base-path="projects" collection="projects" dataset="project_dataset" iterator="project" query-set="project_queryset" hide-view-per-page="true" max-visible-pages="5"></paginate>
|
||||
</div>
|
||||
<div id="workflow-inventory-sync-list" ng-show="activeTab === 'inventory_syncs'">
|
||||
<div ng-hide="inventory_sources.length === 0 && (searchTags | isEmpty)">
|
||||
<smart-search django-model="inventory_sources" base-path="inventory_sources" iterator="inventory_source" dataset="inventory_source_dataset" list="inventorySourceList" collection="inventory_sources" default-params="inventory_source_default_params" query-set="inventory_source_queryset" search-bar-full-width="true" search-tags="searchTags">
|
||||
</smart-search>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="inventory_sources.length === 0 && !(searchTags | isEmpty)">
|
||||
<div class="col-lg-12 List-searchNoResults" translate>No records matched your search.</div>
|
||||
</div>
|
||||
<div class="List-noItems" ng-show="inventory_sources.length === 0 && (searchTags | isEmpty)">PLEASE ADD ITEMS TO THIS LIST</div>
|
||||
<div class="list-table-container" ng-show="inventory_sources.length > 0">
|
||||
<table id="workflow_inventory_sources_table" class="List-table" is-extended="false">
|
||||
<thead>
|
||||
<tr class="List-tableHeaderRow">
|
||||
<th class="List-tableHeader select-column List-staticColumn--smallStatus" translate=""></th>
|
||||
<th base-path="inventory_sources" collection="inventory_sources" dataset="inventory_source_dataset" column-sort="" column-field="name" column-iterator="inventory_source" column-no-sort="undefined" column-label="Name" column-custom-class="" query-set="inventory_source_queryset">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-class="[inventory_source.success_class, {'List-tableRow--selected' : $stateParams['inventory_source_id'] == inventory_source.id}]" id="{{ inventory_source.id }}" class="List-tableRow inventory_source_class" ng-repeat="inventory_source in inventory_sources">
|
||||
<td class="List-tableCell">
|
||||
<input type="radio" ng-model="inventory_source.checked" ng-value="1" ng-false-value="0" name="check_inventory_source_{{inventory_source.id}}" ng-click="toggle_row(inventory_source)" ng-disabled="undefined">
|
||||
</td>
|
||||
<td class="List-tableCell name-column col-md-11" ng-click="toggle_row(inventory_source)">
|
||||
<span aw-tool-tip="Inventory: {{inventory_source.summary_fields.inventory.name}}" data-placement="top">{{ inventory_source.name }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<paginate base-path="inventory_sources" collection="inventory_sources" dataset="inventory_source_dataset" iterator="inventory_source" query-set="inventory_source_queryset" hide-view-per-page="true" max-visible-pages="5"></paginate>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="selectedTemplate && selectedTemplateInvalid">
|
||||
<div class="WorkflowMaker-invalidJobTemplateWarning">
|
||||
<span class="fa fa-warning"></span>
|
||||
@ -24,28 +118,29 @@
|
||||
<span>{{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="selectedTemplate && !selectedTemplateInvalid && !(credentialRequiresPassword && !promptData.launchConf.ask_credential_on_launch)">
|
||||
<label for="verbosity" class="Form-inputLabelContainer">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="mode === 'add'">
|
||||
<label for="edgeType" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">{{:: strings.get('workflow_maker.RUN') }}</span>
|
||||
</label>
|
||||
<div>
|
||||
<select
|
||||
id="workflow_node_edge"
|
||||
id="workflow_node_edge_3"
|
||||
ng-options="v as v.label for v in edgeTypeOptions track by v.value"
|
||||
ng-model="edgeType"
|
||||
class="form-control Form-dropDown"
|
||||
name="edgeType"
|
||||
tabindex="-1"
|
||||
ng-disabled="!workflowJobTemplateObj.summary_fields.user_capabilities.edit"
|
||||
ng-disabled="readOnly"
|
||||
aria-hidden="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons Form-buttons" id="workflow_maker_controls">
|
||||
<button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> {{:: strings.get('prompt.PROMPT') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate) && !selectedTemplateInvalid && !(credentialRequiresPassword && !promptData.launchConf.ask_credential_on_launch)" ng-click="confirmNodeForm()" ng-disabled="!selectedTemplate || promptModalMissingReqFields || credentialRequiresPassword"> {{:: strings.get('workflow_maker.SELECT') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="!readOnly" ng-click="cancel()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="readOnly" ng-click="cancel()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="!readOnly" ng-click="select({selectedTemplate, promptData, edgeType})" ng-disabled="!selectedTemplate || promptModalMissingReqFields || credentialRequiresPassword || selectedTemplateInvalid"> {{:: strings.get('workflow_maker.SELECT') }}</button>
|
||||
</div>
|
||||
</span>
|
||||
<prompt prompt-data="promptData" action-text="{{:: strings.get('prompt.CONFIRM')}}" prevent-creds-with-passwords="preventCredsWithPasswords" read-only-prompts="readOnly"></prompt>
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
@ -11,27 +11,11 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
|
||||
ProcessErrors, CreateSelect2, $q, JobTemplate, WorkflowJobTemplate,
|
||||
Empty, PromptService, Rest, TemplatesStrings, $timeout, $state) {
|
||||
|
||||
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();
|
||||
@ -442,19 +349,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,
|
||||
@ -463,443 +366,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) {
|
||||
$scope.editNodeHelpMessage = null;
|
||||
|
||||
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,
|
||||
templateType: $scope.nodeBeingEdited.unifiedJobTemplate.type,
|
||||
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,
|
||||
templateType: $scope.nodeBeingEdited.unifiedJobTemplate.type,
|
||||
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 */
|
||||
|
||||
@ -907,18 +463,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
|
||||
}
|
||||
@ -934,19 +489,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 {
|
||||
@ -955,34 +510,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");
|
||||
};
|
||||
|
||||
@ -1005,7 +546,7 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
|
||||
$scope.confirmDeleteNode = function () {
|
||||
if ($scope.nodeToBeDeleted) {
|
||||
|
||||
if ($scope.linkBeingEdited) {
|
||||
if ($scope.linkBeingWorkedOn) {
|
||||
$scope.cancelLinkForm();
|
||||
}
|
||||
|
||||
@ -1020,225 +561,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;
|
||||
}
|
||||
};
|
||||
|
||||
function getEditNodeHelpMessage(workflowTemplate, selectedTemplate) {
|
||||
if (selectedTemplate.type === "workflow_job_template") {
|
||||
if (workflowTemplate.inventory) {
|
||||
if (selectedTemplate.ask_inventory_on_launch) {
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_WILL_OVERRIDE');
|
||||
}
|
||||
}
|
||||
|
||||
if (workflowTemplate.ask_inventory_on_launch) {
|
||||
if (selectedTemplate.ask_inventory_on_launch) {
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_PROMPT_WILL_OVERRIDE');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTemplate.type === "job_template") {
|
||||
if (workflowTemplate.inventory) {
|
||||
if (selectedTemplate.ask_inventory_on_launch) {
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_WILL_OVERRIDE');
|
||||
}
|
||||
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_WILL_NOT_OVERRIDE');
|
||||
}
|
||||
|
||||
if (workflowTemplate.ask_inventory_on_launch) {
|
||||
if (selectedTemplate.ask_inventory_on_launch) {
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_PROMPT_WILL_OVERRIDE');
|
||||
}
|
||||
|
||||
return $scope.strings.get('workflow_maker.INVENTORY_PROMPT_WILL_NOT_OVERRIDE');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$scope.templateManuallySelected = function (selectedTemplate) {
|
||||
|
||||
if (promptWatcher) {
|
||||
promptWatcher();
|
||||
}
|
||||
|
||||
if (surveyQuestionWatcher) {
|
||||
surveyQuestionWatcher();
|
||||
}
|
||||
|
||||
if (credentialsWatcher) {
|
||||
credentialsWatcher();
|
||||
}
|
||||
|
||||
$scope.promptData = null;
|
||||
$scope.editNodeHelpMessage = getEditNodeHelpMessage($scope.treeData.workflow_job_template_obj, selectedTemplate);
|
||||
|
||||
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) => {
|
||||
const launchConf = jobTemplate.getLaunchConf();
|
||||
|
||||
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 (jobTemplate.canLaunchWithoutPrompt()) {
|
||||
$scope.showPromptButton = false;
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if (['job_template', 'workflow_job_template'].includes(selectedTemplate.type)) {
|
||||
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,
|
||||
launchOptions: responses[0].data,
|
||||
surveyQuestions: processed.surveyQuestions,
|
||||
template: selectedTemplate.id,
|
||||
templateType: selectedTemplate.type,
|
||||
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,
|
||||
launchOptions: responses[0].data,
|
||||
template: selectedTemplate.id,
|
||||
templateType: selectedTemplate.type,
|
||||
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;
|
||||
};
|
||||
|
||||
@ -1268,10 +600,6 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
|
||||
$scope.$broadcast('zoomToFitChart');
|
||||
};
|
||||
|
||||
$scope.openPromptModal = function () {
|
||||
$scope.promptData.triggerModalOpen = true;
|
||||
};
|
||||
|
||||
let allNodes = [];
|
||||
let page = 1;
|
||||
|
||||
@ -1310,11 +638,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', {
|
||||
@ -1327,7 +651,5 @@ export default ['$scope', 'WorkflowService', 'TemplatesService',
|
||||
};
|
||||
|
||||
getNodes();
|
||||
|
||||
updateEdgeDropdownOptions();
|
||||
}
|
||||
];
|
||||
|
@ -84,71 +84,16 @@
|
||||
<workflow-chart ng-if="modalOpen" tree-data="treeData.data" add-node="startAddNode(parent, betweenTwoNodes)" edit-node="startEditNode(nodeToEdit)" edit-link="startEditLink(parentId, childId)" delete-node="startDeleteNode(nodeToDelete)" workflow-zoomed="workflowZoomed(zoom)" can-add-workflow-job-template="canAddWorkflowJobTemplate" workflow-job-template-obj="workflowJobTemplateObj" mode="edit" class="WorkflowMaker-chart"></workflow-chart>
|
||||
</div>
|
||||
<div class="WorkflowMaker-contentRight">
|
||||
<span ng-show="!editLink">
|
||||
<div class="WorkflowMaker-formTitle">{{(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')}}</div>
|
||||
<div class="WorkflowMaker-formHelp" ng-show="workflowMakerFormConfig.nodeMode === 'idle'" ng-bind="treeData.data.totalNodes === 0 ? strings.get('workflow_maker.PLEASE_CLICK_THE_START_BUTTON') : strings.get('workflow_maker.PLEASE_HOVER_OVER_A_TEMPLATE')"></div>
|
||||
<div class="WorkflowMaker-form" ng-show="workflowMakerFormConfig.nodeMode === 'add' || workflowMakerFormConfig.nodeMode === 'edit'">
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'jobs'}" ng-click="toggleFormTab('jobs')">{{strings.get('workflow_maker.JOBS')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'project_sync'}" ng-click="toggleFormTab('project_sync')">{{strings.get('workflow_maker.PROJECT_SYNC')}}</div>
|
||||
<div class="Form-tab WorkflowMaker-formTab" ng-class="{'is-selected': workflowMakerFormConfig.activeTab === 'inventory_sync'}" ng-click="toggleFormTab('inventory_sync')">{{strings.get('workflow_maker.INVENTORY_SYNC')}}</div>
|
||||
</div>
|
||||
<div class="WorkflowMaker-formLists">
|
||||
<div id="workflow-jobs-list" ui-view="jobTemplateList" ng-show="workflowMakerFormConfig.activeTab === 'jobs'"></div>
|
||||
<div id="workflow-project-sync-list" ui-view="projectSyncList" ng-show="workflowMakerFormConfig.activeTab === 'project_sync'"></div>
|
||||
<div id="workflow-inventory-sync-list" ui-view="inventorySyncList" ng-show="workflowMakerFormConfig.activeTab === 'inventory_sync'"></div>
|
||||
</div>
|
||||
<span ng-show="selectedTemplate &&
|
||||
((selectedTemplate.type === 'job_template' && workflowMakerFormConfig.activeTab === 'jobs') ||
|
||||
(selectedTemplate.type === 'project' && workflowMakerFormConfig.activeTab === 'project_sync') ||
|
||||
(selectedTemplate.type === 'inventory_source' && workflowMakerFormConfig.activeTab === 'inventory_sync'))">
|
||||
<div ng-if="selectedTemplate && selectedTemplateInvalid">
|
||||
<div class="WorkflowMaker-invalidJobTemplateWarning">
|
||||
<span class="fa fa-warning"></span>
|
||||
<span>{{:: strings.get('workflows.INVALID_JOB_TEMPLATE') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="selectedTemplate && credentialRequiresPassword">
|
||||
<div class="WorkflowMaker-invalidJobTemplateWarning">
|
||||
<span class="fa fa-warning"></span>
|
||||
<span>{{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="selectedTemplate && !selectedTemplateInvalid && !(credentialRequiresPassword && !promptData.launchConf.ask_credential_on_launch) && workflowMakerFormConfig.nodeMode === 'add'">
|
||||
<label for="verbosity" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">{{:: strings.get('workflow_maker.RUN') }}</span>
|
||||
</label>
|
||||
<div>
|
||||
<select
|
||||
id="workflow_node_edge"
|
||||
ng-options="v as v.label for v in edgeTypeOptions track by v.value"
|
||||
ng-model="edgeType"
|
||||
class="form-control Form-dropDown"
|
||||
name="edgeType"
|
||||
tabindex="-1"
|
||||
ng-disabled="!workflowJobTemplateObj.summary_fields.user_capabilities.edit"
|
||||
aria-hidden="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons Form-buttons" id="workflow_maker_controls">
|
||||
<button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> {{:: strings.get('prompt.PROMPT') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> {{:: strings.get('CANCEL') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate) && !selectedTemplateInvalid && !(credentialRequiresPassword && !promptData.launchConf.ask_credential_on_launch)" ng-click="confirmNodeForm()" ng-disabled="!selectedTemplate || promptModalMissingReqFields || credentialRequiresPassword"> {{:: strings.get('workflow_maker.SELECT') }}</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<span ng-if="formState.showNodeForm">
|
||||
<workflow-node-form mode="nodeFormMode" node="nodeBeingWorkedOn" select="confirmNodeForm(selectedTemplate, promptData, edgeType)" cancel="cancelNodeForm()" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit"/>
|
||||
</span>
|
||||
<span ng-if="editLink">
|
||||
<workflow-link-form link-config="linkConfig" select="confirmLinkForm(parentId, childId, edgeType)" cancel="cancelLinkForm()"/>
|
||||
<span ng-if="formState.showLinkForm">
|
||||
<workflow-link-form link-config="linkConfig" read-only="!workflowJobTemplateObj.summary_fields.user_capabilities.edit" select="confirmLinkForm(edgeType)" cancel="cancelLinkForm()"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="WorkflowMaker-buttonHolder">
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-cancelButton" ng-click="closeWorkflowMaker()"> {{:: strings.get('CLOSE') }}</button>
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-saveButton" ng-click="saveWorkflowMaker()" ng-show="workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate" ng-disabled="workflowMakerFormConfig.nodeMode === 'add'"> {{:: strings.get('SAVE') }}</button>
|
||||
<button type="button" class="btn btn-sm WorkflowMaker-saveButton" ng-click="saveWorkflowMaker()" ng-show="workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate" ng-disabled="showNodeForm || showLinkForm"> {{:: strings.get('SAVE') }}</button>
|
||||
</div>
|
||||
<prompt prompt-data="promptData" action-text="{{:: strings.get('prompt.CONFIRM')}}" prevent-creds-with-passwords="preventCredsWithPasswords" read-only-prompts="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)"></prompt>
|
||||
</div>
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user