mirror of
https://github.com/ZwareBear/awx.git
synced 2026-05-04 16:01:51 -05:00
Merge branch 'release_3.3.0' into 1137-cancel-prompt
This commit is contained in:
@@ -24,16 +24,18 @@
|
||||
header-link="{{ vm.getLink(job) }}"
|
||||
header-tag="{{ vm.jobTypes[job.type] }}">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_STARTED') }}"
|
||||
value="{{ job.started | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_FINISHED') }}"
|
||||
value="{{ job.finished | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
<div class="at-Row--inline">
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_STARTED') }}"
|
||||
value="{{ job.started | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_FINISHED') }}"
|
||||
value="{{ job.finished | longDate }}"
|
||||
inline="true">
|
||||
</at-row-item>
|
||||
</div>
|
||||
<at-row-item
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_WORKFLOW_JOB') }}"
|
||||
value="{{ job.summary_fields.source_workflow_job.name }}"
|
||||
@@ -64,7 +66,7 @@
|
||||
tag-values="job.summary_fields.credentials"
|
||||
tags-are-creds="true">
|
||||
</at-row-item>
|
||||
<labels-list class="LabelList" show-delete="false" is-row-item="true">
|
||||
<labels-list class="LabelList" show-delete="false" is-row-item="true" state="job">
|
||||
</labels-list>
|
||||
</div>
|
||||
<div class="at-Row-actions">
|
||||
|
||||
@@ -89,6 +89,10 @@ function TemplatesStrings (BaseString) {
|
||||
ns.warnings = {
|
||||
WORKFLOW_RESTRICTED_COPY: t.s('You do not have access to all resources used by this workflow. Resources that you don\'t have access to will not be copied and will result in an incomplete workflow.')
|
||||
};
|
||||
|
||||
ns.workflows = {
|
||||
INVALID_JOB_TEMPLATE: t.s('This Job Template is missing a default inventory or project. This must be addressed in the Job Template form before this node can be saved.')
|
||||
};
|
||||
}
|
||||
|
||||
TemplatesStrings.$inject = ['BaseStringService'];
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_RAN') }}"
|
||||
value="{{ vm.getLastRan(template) }}">
|
||||
</at-row-item>
|
||||
<labels-list class="LabelList" show-delete="false" is-row-item="true">
|
||||
<labels-list class="LabelList" show-delete="false" is-row-item="true" state="template">
|
||||
</labels-list>
|
||||
</div>
|
||||
<div class="at-Row-actions">
|
||||
|
||||
@@ -879,7 +879,6 @@ input[type="checkbox"].checkbox-no-label {
|
||||
|
||||
.checkbox-options {
|
||||
font-weight: normal;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
/* Display list actions next to search widget */
|
||||
|
||||
@@ -788,3 +788,8 @@ input[type='radio']:checked:before {
|
||||
border-color: @b7grey;
|
||||
background-color: @ebgrey;
|
||||
}
|
||||
|
||||
.Form-checkboxRow {
|
||||
display: flex;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
.at-LaunchTemplate {
|
||||
margin-left: 15px;
|
||||
|
||||
&--button {
|
||||
font-size: 16px;
|
||||
height: 30px;
|
||||
@@ -8,8 +6,9 @@
|
||||
color: #848992;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&--button:hover {
|
||||
background-color: @at-blue;
|
||||
color: white;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
}
|
||||
|
||||
.at-List-toolbarActionButton {
|
||||
border: none;
|
||||
border-radius: @at-border-radius;
|
||||
min-width: 80px;
|
||||
}
|
||||
@@ -67,12 +68,8 @@
|
||||
}
|
||||
|
||||
.at-Row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: @at-padding-list-row;
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 10px 1fr;
|
||||
}
|
||||
|
||||
.at-Row--active {
|
||||
@@ -85,15 +82,21 @@
|
||||
border-left: @at-white solid 1px;
|
||||
}
|
||||
|
||||
.at-Row--active .at-Row-content {
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.at-Row ~ .at-Row {
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
border-top: @at-border-default-width solid @at-color-list-border;
|
||||
}
|
||||
|
||||
.at-Row--invalid {
|
||||
align-items: center;
|
||||
background: @at-color-error;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
width: @at-space-2x;
|
||||
|
||||
.at-Popover {
|
||||
padding: 0;
|
||||
@@ -108,31 +111,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.at-Row ~ .at-Row {
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
border-top: @at-border-default-width solid @at-color-list-border;
|
||||
}
|
||||
|
||||
.at-Row--rowLayout {
|
||||
.at-Row-content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.at-RowItem {
|
||||
margin-right: @at-space-4x;
|
||||
|
||||
&-label {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
flex-wrap: wrap;
|
||||
grid-column-start: 2;
|
||||
padding: @at-padding-list-row;
|
||||
}
|
||||
|
||||
.at-RowStatus {
|
||||
align-self: flex-start;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.at-Row-firstColumn {
|
||||
.at-Row-toggle {
|
||||
margin-right: @at-space-4x;
|
||||
}
|
||||
|
||||
@@ -141,12 +128,12 @@
|
||||
}
|
||||
|
||||
.at-Row-items {
|
||||
align-self: flex-start;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.at-RowItem {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 120px 1fr;
|
||||
align-items: center;
|
||||
line-height: @at-height-list-row-item;
|
||||
}
|
||||
@@ -156,6 +143,7 @@
|
||||
}
|
||||
|
||||
.at-RowItem--isHeader {
|
||||
display: flex;
|
||||
color: @at-color-body-text;
|
||||
margin-bottom: @at-margin-bottom-list-header;
|
||||
line-height: @at-line-height-list-row-item-header;
|
||||
@@ -171,6 +159,12 @@
|
||||
|
||||
.at-RowItem--labels {
|
||||
line-height: @at-line-height-list-row-item-labels;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
* {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.at-RowItem-header {
|
||||
@@ -178,6 +172,7 @@
|
||||
}
|
||||
|
||||
.at-RowItem-tagContainer {
|
||||
display: flex;
|
||||
margin-left: @at-margin-left-list-row-item-tag-container;
|
||||
}
|
||||
|
||||
@@ -210,8 +205,6 @@
|
||||
|
||||
.at-RowItem-label {
|
||||
text-transform: uppercase;
|
||||
width: auto;
|
||||
width: @at-width-list-row-item-label;
|
||||
color: @at-color-list-row-item-label;
|
||||
font-size: @at-font-size;
|
||||
}
|
||||
@@ -280,6 +273,7 @@
|
||||
@media screen and (max-width: @at-breakpoint-compact-list) {
|
||||
.at-Row-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.at-RowAction {
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<div class="at-Row--invalid" ng-show="invalid">
|
||||
<at-popover state="invalidTooltip"></at-popover>
|
||||
</div>
|
||||
<ng-transclude></ng-transclude>
|
||||
<div class="at-Row-content" ng-transclude></div>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@ function atRelaunchCtrl (
|
||||
|
||||
vm.$onInit = () => {
|
||||
vm.showRelaunch = vm.job.type !== 'system_job' && vm.job.summary_fields.user_capabilities.start;
|
||||
vm.showDropdown = vm.job.type === 'job' && vm.job.failed === true;
|
||||
vm.showDropdown = vm.job.type === 'job' && vm.job.status === 'failed';
|
||||
|
||||
vm.createDropdown();
|
||||
vm.createTooltips();
|
||||
|
||||
@@ -1,21 +1,36 @@
|
||||
let Base;
|
||||
let Credential;
|
||||
|
||||
function setDependentResources (id) {
|
||||
this.dependentResources = [
|
||||
{
|
||||
model: new Credential(),
|
||||
params: {
|
||||
organization: id
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function OrganizationModel (method, resource, config) {
|
||||
Base.call(this, 'organizations');
|
||||
|
||||
this.Constructor = OrganizationModel;
|
||||
this.setDependentResources = setDependentResources.bind(this);
|
||||
|
||||
return this.create(method, resource, config);
|
||||
}
|
||||
|
||||
function OrganizationModelLoader (BaseModel) {
|
||||
function OrganizationModelLoader (BaseModel, CredentialModel) {
|
||||
Base = BaseModel;
|
||||
Credential = CredentialModel;
|
||||
|
||||
return OrganizationModel;
|
||||
}
|
||||
|
||||
OrganizationModelLoader.$inject = [
|
||||
'BaseModel'
|
||||
'BaseModel',
|
||||
'CredentialModel'
|
||||
];
|
||||
|
||||
export default OrganizationModelLoader;
|
||||
|
||||
@@ -73,6 +73,7 @@ function BaseStringService (namespace) {
|
||||
this.deleteResource = {
|
||||
HEADER: t.s('Delete'),
|
||||
USED_BY: resourceType => t.s('The {{ resourceType }} is currently being used by other resources.', { resourceType }),
|
||||
UNAVAILABLE: resourceType => t.s('Deleting this {{ resourceType }} will make the following resources unavailable.', { resourceType }),
|
||||
CONFIRM: resourceType => t.s('Are you sure you want to delete this {{ resourceType }}?', { resourceType })
|
||||
};
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
@import '../../src/templates/survey-maker/survey-maker.block.less';
|
||||
@import '../../src/templates/survey-maker/shared/survey-controls.block.less';
|
||||
@import '../../src/templates/survey-maker/survey-maker.block.less';
|
||||
@import '../../src/templates/workflows/workflow.block.less';
|
||||
@import '../../src/templates/workflows/workflow-chart/workflow-chart.block.less';
|
||||
@import '../../src/templates/workflows/workflow-controls/workflow-controls.block.less';
|
||||
@import '../../src/templates/workflows/workflow-maker/workflow-maker.block.less';
|
||||
|
||||
@@ -46,4 +46,8 @@ capacity-bar {
|
||||
.Capacity-details--percentage {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:only-child {
|
||||
margin-right: 50px;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 3;
|
||||
overflow-y: scroll;
|
||||
|
||||
.modal-dialog {
|
||||
padding-top: 100px;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<at-list results='vm.instances'>
|
||||
<at-row ng-repeat="instance in vm.instances"
|
||||
ng-class="{'at-Row--active': (instance.id === vm.activeId)}">
|
||||
<div class="at-Row-firstColumn">
|
||||
<div class="at-Row-toggle">
|
||||
<div class="ScheduleToggle"
|
||||
ng-class="{'is-on': instance.enabled}">
|
||||
<button ng-show="instance.enabled"
|
||||
@@ -53,14 +53,13 @@
|
||||
|
||||
<div class="at-Row-items">
|
||||
<at-row-item header-value="{{ instance.hostname }}"></at-row-item>
|
||||
<div class="at-Row--rowLayout">
|
||||
<at-row-item
|
||||
label-value="Running Jobs"
|
||||
label-state="instanceGroups.instanceJobs({instance_group_id: {{vm.instance_group_id}}, instance_id: {{instance.id}}})"
|
||||
value="{{ instance.jobs_running }}"
|
||||
badge="true">
|
||||
</at-row-item>
|
||||
</div>
|
||||
<at-row-item
|
||||
label-value="Running Jobs"
|
||||
label-state="instanceGroups.instanceJobs({instance_group_id: {{vm.instance_group_id}}, instance_id: {{instance.id}}})"
|
||||
value="{{ instance.jobs_running }}"
|
||||
inline="true"
|
||||
badge="true">
|
||||
</at-row-item>
|
||||
</div>
|
||||
|
||||
<div class="at-Row-actions">
|
||||
|
||||
@@ -39,11 +39,12 @@
|
||||
header-link="/#/instance_groups/{{ instance_group.id }}">
|
||||
</at-row-item>
|
||||
|
||||
<div class="at-Row--rowLayout">
|
||||
<div class="at-Row--inline">
|
||||
<at-row-item
|
||||
label-value="Instances"
|
||||
label-link="/#/instance_groups/{{ instance_group.id }}/instances"
|
||||
value="{{ instance_group.instances }}"
|
||||
inline="true"
|
||||
badge="true">
|
||||
</at-row-item>
|
||||
|
||||
@@ -51,10 +52,10 @@
|
||||
label-value="Running Jobs"
|
||||
label-link="/#/instance_groups/{{ instance_group.id }}/jobs"
|
||||
value="{{ instance_group.jobs_running }}"
|
||||
inline="true"
|
||||
badge="true">
|
||||
</at-row-item>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="at-Row-actions">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
hover: true,
|
||||
multiSelect: true,
|
||||
trackBy: 'group.id',
|
||||
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/root_groups/',
|
||||
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/groups/',
|
||||
|
||||
fields: {
|
||||
failed_hosts: {
|
||||
|
||||
@@ -6,39 +6,40 @@
|
||||
|
||||
|
||||
export default ['$stateParams', '$scope', '$rootScope',
|
||||
'Rest', 'OrganizationList', 'Prompt',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'rbacUiControlService', '$filter', 'Dataset', 'i18n',
|
||||
'Rest', 'OrganizationList', 'Prompt', 'OrganizationModel',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state',
|
||||
'rbacUiControlService', '$filter', 'Dataset', 'i18n',
|
||||
'AppStrings',
|
||||
function($stateParams, $scope, $rootScope,
|
||||
Rest, OrganizationList, Prompt,
|
||||
ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset, i18n) {
|
||||
Rest, OrganizationList, Prompt, Organization,
|
||||
ProcessErrors, GetBasePath, Wait, $state,
|
||||
rbacUiControlService, $filter, Dataset, i18n,
|
||||
AppStrings
|
||||
) {
|
||||
|
||||
var defaultUrl = GetBasePath('organizations'),
|
||||
list = OrganizationList;
|
||||
|
||||
init();
|
||||
$scope.canAdd = false;
|
||||
|
||||
function init() {
|
||||
$scope.canAdd = false;
|
||||
rbacUiControlService.canAdd("organizations")
|
||||
.then(function(params) {
|
||||
$scope.canAdd = params.canAdd;
|
||||
});
|
||||
$scope.orgCount = Dataset.data.count;
|
||||
|
||||
rbacUiControlService.canAdd("organizations")
|
||||
.then(function(params) {
|
||||
$scope.canAdd = params.canAdd;
|
||||
});
|
||||
$scope.orgCount = Dataset.data.count;
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
// search init
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
$scope.orgCards = parseCardData($scope[list.name]);
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
$scope.orgCards = parseCardData($scope[list.name]);
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
// grab the pagination elements, move, destroy list generator elements
|
||||
$('#organization-pagination').appendTo('#OrgCards');
|
||||
$('#organizations tag-search').appendTo('.OrgCards-search');
|
||||
$('#organizations-list').remove();
|
||||
}
|
||||
// grab the pagination elements, move, destroy list generator elements
|
||||
$('#organization-pagination').appendTo('#OrgCards');
|
||||
$('#organizations tag-search').appendTo('.OrgCards-search');
|
||||
$('#organizations-list').remove();
|
||||
|
||||
function parseCardData(cards) {
|
||||
return cards.map(function(card) {
|
||||
@@ -167,13 +168,34 @@ export default ['$stateParams', '$scope', '$rootScope',
|
||||
});
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: i18n._('Delete'),
|
||||
resourceName: $filter('sanitize')(name),
|
||||
body: '<div class="Prompt-bodyQuery">' + i18n._('Are you sure you want to delete this organization? This makes everything in this organization unavailable.') + '</div>',
|
||||
action: action,
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
const organization = new Organization();
|
||||
|
||||
organization.getDependentResourceCounts(id)
|
||||
.then((counts) => {
|
||||
const invalidateRelatedLines = [];
|
||||
let deleteModalBody = `<div class="Prompt-bodyQuery">${AppStrings.get('deleteResource.CONFIRM', 'organization')}</div>`;
|
||||
|
||||
counts.forEach(countObj => {
|
||||
if(countObj.count && countObj.count > 0) {
|
||||
invalidateRelatedLines.push(`<div><span class="Prompt-warningResourceTitle">${countObj.label}</span><span class="badge List-titleBadge">${countObj.count}</span></div>`);
|
||||
}
|
||||
});
|
||||
|
||||
if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
|
||||
deleteModalBody = `<div class="Prompt-bodyQuery">${AppStrings.get('deleteResource.UNAVAILABLE', 'organization')} ${AppStrings.get('deleteResource.CONFIRM', 'organization')}</div>`;
|
||||
invalidateRelatedLines.forEach(invalidateRelatedLine => {
|
||||
deleteModalBody += invalidateRelatedLine;
|
||||
});
|
||||
}
|
||||
|
||||
Prompt({
|
||||
hdr: i18n._('Delete'),
|
||||
resourceName: $filter('sanitize')(name),
|
||||
body: deleteModalBody,
|
||||
action: action,
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
@@ -241,7 +241,7 @@ export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert',
|
||||
resourceName: $filter('sanitize')(name),
|
||||
body: deleteModalBody,
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -545,6 +545,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
html += "' ";
|
||||
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
|
||||
html += " class='ScheduleToggle-switch' ng-click='" + field.ngClick + "' translate>" + i18n._("OFF") + "</button></div></div>";
|
||||
} else if (field.type === 'html') {
|
||||
html += field.html;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
@@ -599,6 +601,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
label = (includeLabel !== undefined && includeLabel === false) ? false : true;
|
||||
|
||||
if (label) {
|
||||
html += "<span class=\"Form-checkboxRow\">";
|
||||
html += "<label class=\"";
|
||||
html += (field.inline === undefined || field.inline === true) ? "checkbox-inline" : "";
|
||||
html += (field.labelClass) ? " " + field.labelClass : "";
|
||||
@@ -628,6 +631,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
html += field.label + " ";
|
||||
html += (field.awPopOver) ? Attr(field, 'awPopOver', fld) : "";
|
||||
html += "</label>\n";
|
||||
html += "</span>";
|
||||
}
|
||||
|
||||
return html;
|
||||
|
||||
@@ -155,16 +155,16 @@ function SmartSearchController (
|
||||
const unmodifiedQueryset = _.clone(queryset);
|
||||
|
||||
const searchInputQueryset = qs.getSearchInputQueryset(terms, isRelatedField, isAnsibleFactField, singleSearchParam);
|
||||
const modifiedQueryset = qs.mergeQueryset(queryset, searchInputQueryset, singleSearchParam);
|
||||
queryset = qs.mergeQueryset(queryset, searchInputQueryset, singleSearchParam);
|
||||
|
||||
// Go back to the first page after a new search
|
||||
delete modifiedQueryset.page;
|
||||
delete queryset.page;
|
||||
|
||||
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
||||
// This transition will not reload controllers/resolves/views but will register new
|
||||
// $stateParams[searchKey] terms.
|
||||
if (!$scope.querySet) {
|
||||
$state.go('.', { [searchKey]: modifiedQueryset })
|
||||
$state.go('.', { [searchKey]: queryset })
|
||||
.then(() => {
|
||||
// same as above in $scope.remove. For some reason deleting the page
|
||||
// from the queryset works for all lists except lists in modals.
|
||||
@@ -172,10 +172,10 @@ function SmartSearchController (
|
||||
});
|
||||
}
|
||||
|
||||
qs.search(path, modifiedQueryset)
|
||||
qs.search(path, queryset)
|
||||
.then(({ data }) => {
|
||||
if ($scope.querySet) {
|
||||
$scope.querySet = modifiedQueryset;
|
||||
$scope.querySet = queryset;
|
||||
}
|
||||
$scope.dataset = data;
|
||||
$scope.collection = data.results;
|
||||
@@ -191,10 +191,10 @@ function SmartSearchController (
|
||||
const { singleSearchParam } = $scope;
|
||||
const [term] = $scope.searchTags.splice(index, 1);
|
||||
|
||||
const modifiedQueryset = qs.removeTermsFromQueryset(queryset, term, isRelatedField, singleSearchParam);
|
||||
queryset = qs.removeTermsFromQueryset(queryset, term, isRelatedField, singleSearchParam);
|
||||
|
||||
if (!$scope.querySet) {
|
||||
$state.go('.', { [searchKey]: modifiedQueryset })
|
||||
$state.go('.', { [searchKey]: queryset })
|
||||
.then(() => {
|
||||
// for some reason deleting a tag from a list in a modal does not
|
||||
// remove the param from $stateParams. Here we'll manually check to make sure
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.LabelList-tagContainer {
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.LabelList-tagContainer,
|
||||
.LabelList-seeMoreLess {
|
||||
@@ -70,6 +73,7 @@
|
||||
.LabelList-name {
|
||||
flex: initial;
|
||||
max-width: 100%;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.LabelList-tag--deletable > .LabelList-name {
|
||||
|
||||
@@ -12,7 +12,9 @@ export default
|
||||
function(templateUrl, Wait, Rest, GetBasePath, ProcessErrors, Prompt, $q, $filter, $state) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: false,
|
||||
scope: {
|
||||
state: '='
|
||||
},
|
||||
templateUrl: templateUrl('templates/labels/labelsList'),
|
||||
link: function(scope, element, attrs) {
|
||||
scope.showDelete = attrs.showDelete === 'true';
|
||||
@@ -33,7 +35,9 @@ export default
|
||||
|
||||
scope.seeMore = function () {
|
||||
var seeMoreResolve = $q.defer();
|
||||
Rest.setUrl(scope[scope.$parent.list.iterator].related.labels);
|
||||
if (scope.state) {
|
||||
Rest.setUrl(scope.state.related.labels);
|
||||
}
|
||||
Rest.get()
|
||||
.then(({data}) => {
|
||||
if (data.next) {
|
||||
@@ -92,23 +96,16 @@ export default
|
||||
});
|
||||
};
|
||||
|
||||
if (scope.$parent.$parent.template) {
|
||||
if (_.has(scope, '$parent.$parent.template.summary_fields.labels.results')) {
|
||||
scope.labels = scope.$parent.$parent.template.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.$parent.$parent.template.summary_fields.labels.count;
|
||||
}
|
||||
} else if (scope.$parent.$parent.job) {
|
||||
if (_.has(scope, '$parent.$parent.job.summary_fields.labels.results')) {
|
||||
scope.labels = scope.$parent.$parent.job.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.$parent.$parent.job.summary_fields.labels.count;
|
||||
}
|
||||
} else {
|
||||
scope.$watchCollection(scope.$parent.list.iterator, function() {
|
||||
if (_.has(scope.state, 'summary_fields.labels.results')) {
|
||||
scope.labels = scope.state.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.state.summary_fields.labels.count;
|
||||
} else {
|
||||
scope.$watchCollection(scope.state, function() {
|
||||
// To keep the array of labels fresh, we need to set up a watcher - otherwise, the
|
||||
// array will get set initially and then never be updated as labels are removed
|
||||
if (scope[scope.$parent.list.iterator].summary_fields.labels){
|
||||
scope.labels = scope[scope.$parent.list.iterator].summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope[scope.$parent.list.iterator].summary_fields.labels.count;
|
||||
if (scope.state.summary_fields.labels){
|
||||
scope.labels = scope.state.summary_fields.labels.results.slice(0, 5);
|
||||
scope.count = scope.state.summary_fields.labels.count;
|
||||
}
|
||||
else{
|
||||
scope.labels = null;
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
ng-click="seeMore()" ng-if="!isRowItem">View More</div>
|
||||
<div class="LabelList-seeMoreLess" ng-show="count > 5 && !seeMoreInactive"
|
||||
ng-click="seeLess()" ng-if="!isRowItem">View Less</div>
|
||||
<div class="at-RowItem at-RowItem--labels" ng-show="count > 0" ng-if="isRowItem">
|
||||
<div class="at-RowItem-label">
|
||||
Labels
|
||||
</div>
|
||||
<div class="at-RowItem" ng-show="count > 0" ng-if="isRowItem">
|
||||
<div class="at-RowItem-label">
|
||||
Labels
|
||||
</div>
|
||||
<div class="at-RowItem--labels">
|
||||
<div class="LabelList-tagContainer" ng-repeat="label in labels">
|
||||
<div class="LabelList-tag" ng-class="{'LabelList-tag--deletable' : (showDelete && template.summary_fields.user_capabilities.edit)}">
|
||||
<span class="LabelList-name">{{ label.name }}</span>
|
||||
@@ -30,4 +31,5 @@
|
||||
ng-click="seeMore()">View More</div>
|
||||
<div class="LabelList-seeMoreLess" ng-show="count > 5 && !seeMoreInactive"
|
||||
ng-click="seeLess()">View Less</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,6 @@ import workflowService from './workflows/workflow.service';
|
||||
import WorkflowForm from './workflows.form';
|
||||
import InventorySourcesList from './inventory-sources.list';
|
||||
import TemplateList from './templates.list';
|
||||
import TemplatesStrings from './templates.strings';
|
||||
import listRoute from '~features/templates/routes/templatesList.route.js';
|
||||
import templateCompletedJobsRoute from '~features/jobs/routes/templateCompletedJobs.route.js';
|
||||
|
||||
@@ -32,7 +31,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
|
||||
.factory('TemplateList', TemplateList)
|
||||
.value('InventorySourcesList', InventorySourcesList)
|
||||
.service('TemplatesStrings', TemplatesStrings)
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
|
||||
@@ -379,6 +377,15 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
|
||||
response.status
|
||||
});
|
||||
});
|
||||
}],
|
||||
workflowLaunch: ['$stateParams', 'WorkflowJobTemplateModel',
|
||||
function($stateParams, WorkflowJobTemplate) {
|
||||
let workflowJobTemplate = new WorkflowJobTemplate();
|
||||
|
||||
return workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id)
|
||||
.then(({data}) => {
|
||||
return data;
|
||||
});
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,31 +70,33 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel',
|
||||
|
||||
vm.promptDataClone.prompts.credentials.passwords = {};
|
||||
|
||||
vm.promptDataClone.launchConf.passwords_needed_to_start.forEach((passwordNeeded) => {
|
||||
if(passwordNeeded === "ssh_password") {
|
||||
vm.promptDataClone.prompts.credentials.passwords.ssh = {};
|
||||
}
|
||||
if(passwordNeeded === "become_password") {
|
||||
vm.promptDataClone.prompts.credentials.passwords.become = {};
|
||||
}
|
||||
if(passwordNeeded === "ssh_key_unlock") {
|
||||
vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock = {};
|
||||
}
|
||||
if(passwordNeeded.startsWith("vault_password")) {
|
||||
let vault_id;
|
||||
if(passwordNeeded.includes('.')) {
|
||||
vault_id = passwordNeeded.split(/\.(.+)/)[1];
|
||||
if(vm.promptData.launchConf.passwords_needed_to_start) {
|
||||
vm.promptData.launchConf.passwords_needed_to_start.forEach((passwordNeeded) => {
|
||||
if(passwordNeeded === "ssh_password") {
|
||||
vm.promptData.prompts.credentials.passwords.ssh = {};
|
||||
}
|
||||
|
||||
if(!vm.promptDataClone.prompts.credentials.passwords.vault) {
|
||||
vm.promptDataClone.prompts.credentials.passwords.vault = [];
|
||||
if(passwordNeeded === "become_password") {
|
||||
vm.promptData.prompts.credentials.passwords.become = {};
|
||||
}
|
||||
if(passwordNeeded === "ssh_key_unlock") {
|
||||
vm.promptData.prompts.credentials.passwords.ssh_key_unlock = {};
|
||||
}
|
||||
if(passwordNeeded.startsWith("vault_password")) {
|
||||
let vault_id;
|
||||
if(passwordNeeded.includes('.')) {
|
||||
vault_id = passwordNeeded.split(/\.(.+)/)[1];
|
||||
}
|
||||
|
||||
vm.promptDataClone.prompts.credentials.passwords.vault.push({
|
||||
vault_id: vault_id
|
||||
});
|
||||
}
|
||||
});
|
||||
if(!vm.promptData.prompts.credentials.passwords.vault) {
|
||||
vm.promptData.prompts.credentials.passwords.vault = [];
|
||||
}
|
||||
|
||||
vm.promptData.prompts.credentials.passwords.vault.push({
|
||||
vault_id: vault_id
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vm.promptDataClone.credentialTypeMissing = [];
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
function TemplatesStrings (BaseString) {
|
||||
BaseString.call(this, 'templates');
|
||||
}
|
||||
|
||||
TemplatesStrings.$inject = ['BaseStringService'];
|
||||
|
||||
export default TemplatesStrings;
|
||||
@@ -27,6 +27,16 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
|
||||
detailsClick: "$state.go('templates.editWorkflowJobTemplate')",
|
||||
include: ['/static/partials/survey-maker-modal.html'],
|
||||
|
||||
headerFields: {
|
||||
missingTemplates: {
|
||||
type: 'html',
|
||||
html: `<div ng-show="missingTemplates" class="Workflow-warning">
|
||||
<span class="Workflow-warningIcon fa fa-warning"></span>` +
|
||||
i18n._("Missing Job Templates found in the <span class='Workflow-warningLink' ng-click='openWorkflowMaker()'>Workflow Editor</span>") +
|
||||
`</div>`
|
||||
}
|
||||
},
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
@@ -82,6 +92,22 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)' // TODO: get working
|
||||
},
|
||||
checkbox_group: {
|
||||
label: i18n._('Options'),
|
||||
type: 'checkbox_group',
|
||||
fields: [{
|
||||
name: 'allow_simultaneous',
|
||||
label: i18n._('Enable Concurrent Jobs'),
|
||||
type: 'checkbox',
|
||||
column: 2,
|
||||
awPopOver: "<p>" + i18n._("If enabled, simultaneous runs of this workflow job template will be allowed.") + "</p>",
|
||||
dataPlacement: 'right',
|
||||
dataTitle: i18n._('Enable Concurrent Jobs'),
|
||||
dataContainer: "body",
|
||||
labelClass: 'stack-inline',
|
||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -59,7 +59,14 @@ export default [
|
||||
|
||||
try {
|
||||
for (fld in form.fields) {
|
||||
data[fld] = $scope[fld];
|
||||
if(form.fields[fld].type === 'checkbox_group') {
|
||||
// Loop across the checkboxes
|
||||
for(var i=0; i<form.fields[fld].fields.length; i++) {
|
||||
data[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name];
|
||||
}
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
data.extra_vars = ToJSON($scope.parseType,
|
||||
|
||||
@@ -10,10 +10,15 @@ export default [
|
||||
'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2',
|
||||
'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification',
|
||||
'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'i18n',
|
||||
'workflowLaunch', '$transitions', 'WorkflowJobTemplateModel',
|
||||
function($scope, $stateParams, WorkflowForm, GenerateForm, Alert,
|
||||
ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
|
||||
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
|
||||
TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n) {
|
||||
ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
|
||||
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
|
||||
TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n,
|
||||
workflowLaunch, $transitions, WorkflowJobTemplate
|
||||
) {
|
||||
|
||||
$scope.missingTemplates = _.has(workflowLaunch, 'node_templates_missing') && workflowLaunch.node_templates_missing.length > 0 ? true : false;
|
||||
|
||||
$scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) {
|
||||
if (val === false) {
|
||||
@@ -21,6 +26,25 @@ export default [
|
||||
}
|
||||
});
|
||||
|
||||
const criteriaObj = {
|
||||
from: (state) => state.name === 'templates.editWorkflowJobTemplate.workflowMaker',
|
||||
to: (state) => state.name === 'templates.editWorkflowJobTemplate'
|
||||
};
|
||||
|
||||
$transitions.onSuccess(criteriaObj, function() {
|
||||
if ($scope.missingTemplates) {
|
||||
// Go out and check the new launch response to see if the user has fixed the
|
||||
// missing node templates
|
||||
|
||||
let workflowJobTemplate = new WorkflowJobTemplate();
|
||||
|
||||
workflowJobTemplate.getLaunch($stateParams.workflow_job_template_id)
|
||||
.then(({data}) => {
|
||||
$scope.missingTemplates = _.has(data, 'node_templates_missing') && data.node_templates_missing.length > 0 ? true : false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Inject dynamic view
|
||||
let form = WorkflowForm(),
|
||||
generator = GenerateForm,
|
||||
@@ -30,117 +54,34 @@ export default [
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.includeWorkflowMaker = false;
|
||||
|
||||
function init() {
|
||||
$scope.openWorkflowMaker = function() {
|
||||
$state.go('.workflowMaker');
|
||||
};
|
||||
|
||||
// Select2-ify the lables input
|
||||
CreateSelect2({
|
||||
element:'#workflow_job_template_labels',
|
||||
multiple: true,
|
||||
addNew: true
|
||||
});
|
||||
$scope.formSave = function () {
|
||||
let fld, data = {};
|
||||
$scope.invalid_survey = false;
|
||||
|
||||
SurveyControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
id: id,
|
||||
templateType: 'workflow_job_template'
|
||||
});
|
||||
// Can't have a survey enabled without a survey
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.survey_enabled = false;
|
||||
}
|
||||
|
||||
$scope.labelOptions = availableLabels
|
||||
.map((i) => ({label: i.name, value: i.id}));
|
||||
generator.clearApiErrors($scope);
|
||||
|
||||
var opts = selectedLabels
|
||||
.map(i => ({id: i.id + "",
|
||||
test: i.name}));
|
||||
Wait('start');
|
||||
|
||||
CreateSelect2({
|
||||
element:'#workflow_job_template_labels',
|
||||
multiple: true,
|
||||
addNew: true,
|
||||
opts: opts
|
||||
});
|
||||
|
||||
$scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
|
||||
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
|
||||
|
||||
$scope.workflow_job_template_obj = workflowJobTemplateData;
|
||||
$scope.name = workflowJobTemplateData.name;
|
||||
$scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
|
||||
let fld, i;
|
||||
for (fld in form.fields) {
|
||||
if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) {
|
||||
if (form.fields[fld].type === 'select') {
|
||||
if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
|
||||
for (i = 0; i < $scope[fld + '_options'].length; i++) {
|
||||
if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) {
|
||||
$scope[fld] = $scope[fld + '_options'][i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$scope[fld] = workflowJobTemplateData[fld];
|
||||
try {
|
||||
for (fld in form.fields) {
|
||||
if(form.fields[fld].type === 'checkbox_group') {
|
||||
// Loop across the checkboxes
|
||||
for(var i=0; i<form.fields[fld].fields.length; i++) {
|
||||
data[form.fields[fld].fields[i].name] = $scope[form.fields[fld].fields[i].name];
|
||||
}
|
||||
} else {
|
||||
$scope[fld] = workflowJobTemplateData[fld];
|
||||
if(!Empty(workflowJobTemplateData.summary_fields.survey)) {
|
||||
$scope.survey_exists = true;
|
||||
}
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
if (fld === 'variables') {
|
||||
// Parse extra_vars, converting to YAML.
|
||||
$scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars);
|
||||
|
||||
ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' });
|
||||
}
|
||||
if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) {
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
|
||||
if(workflowJobTemplateData.organization) {
|
||||
OrgAdminLookup.checkForRoleLevelAdminAccess(workflowJobTemplateData.organization, 'workflow_admin_role')
|
||||
.then(function(canEditOrg){
|
||||
$scope.canEditOrg = canEditOrg;
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.canEditOrg = true;
|
||||
}
|
||||
|
||||
$scope.url = workflowJobTemplateData.url;
|
||||
$scope.survey_enabled = workflowJobTemplateData.survey_enabled;
|
||||
|
||||
$scope.includeWorkflowMaker = true;
|
||||
|
||||
$scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.openWorkflowMaker = function() {
|
||||
$state.go('.workflowMaker');
|
||||
};
|
||||
|
||||
$scope.formSave = function () {
|
||||
let fld, data = {};
|
||||
$scope.invalid_survey = false;
|
||||
|
||||
// Can't have a survey enabled without a survey
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.survey_enabled = false;
|
||||
}
|
||||
|
||||
generator.clearApiErrors($scope);
|
||||
|
||||
Wait('start');
|
||||
|
||||
try {
|
||||
for (fld in form.fields) {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
|
||||
data.extra_vars = ToJSON($scope.parseType,
|
||||
$scope.variables, true);
|
||||
@@ -289,6 +230,91 @@ export default [
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
// Select2-ify the lables input
|
||||
CreateSelect2({
|
||||
element:'#workflow_job_template_labels',
|
||||
multiple: true,
|
||||
addNew: true
|
||||
});
|
||||
|
||||
SurveyControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
id: id,
|
||||
templateType: 'workflow_job_template'
|
||||
});
|
||||
|
||||
$scope.labelOptions = availableLabels
|
||||
.map((i) => ({label: i.name, value: i.id}));
|
||||
|
||||
var opts = selectedLabels
|
||||
.map(i => ({id: i.id + "",
|
||||
test: i.name}));
|
||||
|
||||
CreateSelect2({
|
||||
element:'#workflow_job_template_labels',
|
||||
multiple: true,
|
||||
addNew: true,
|
||||
opts: opts
|
||||
});
|
||||
|
||||
$scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
|
||||
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
|
||||
|
||||
$scope.workflow_job_template_obj = workflowJobTemplateData;
|
||||
$scope.name = workflowJobTemplateData.name;
|
||||
$scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;
|
||||
let fld, i;
|
||||
for (fld in form.fields) {
|
||||
if (fld !== 'variables' && fld !== 'survey' && workflowJobTemplateData[fld] !== null && workflowJobTemplateData[fld] !== undefined) {
|
||||
if (form.fields[fld].type === 'select') {
|
||||
if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
|
||||
for (i = 0; i < $scope[fld + '_options'].length; i++) {
|
||||
if (workflowJobTemplateData[fld] === $scope[fld + '_options'][i].value) {
|
||||
$scope[fld] = $scope[fld + '_options'][i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$scope[fld] = workflowJobTemplateData[fld];
|
||||
}
|
||||
} else {
|
||||
$scope[fld] = workflowJobTemplateData[fld];
|
||||
if(!Empty(workflowJobTemplateData.summary_fields.survey)) {
|
||||
$scope.survey_exists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fld === 'variables') {
|
||||
// Parse extra_vars, converting to YAML.
|
||||
$scope.variables = ParseVariableString(workflowJobTemplateData.extra_vars);
|
||||
|
||||
ParseTypeChange({ scope: $scope, field_id: 'workflow_job_template_variables' });
|
||||
}
|
||||
if (form.fields[fld].type === 'lookup' && workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel]) {
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
|
||||
if(workflowJobTemplateData.organization) {
|
||||
OrgAdminLookup.checkForRoleLevelAdminAccess(workflowJobTemplateData.organization, 'workflow_admin_role')
|
||||
.then(function(canEditOrg){
|
||||
$scope.canEditOrg = canEditOrg;
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.canEditOrg = true;
|
||||
}
|
||||
|
||||
$scope.url = workflowJobTemplateData.url;
|
||||
$scope.survey_enabled = workflowJobTemplateData.survey_enabled;
|
||||
|
||||
$scope.includeWorkflowMaker = true;
|
||||
|
||||
$scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import workflowMaker from './workflow-maker.directive';
|
||||
import WorkflowMakerController from './workflow-maker.controller';
|
||||
import WorkflowMakerForm from './workflow-maker.form';
|
||||
|
||||
export default
|
||||
angular.module('templates.workflowMaker', [])
|
||||
// In order to test this controller I had to expose it at the module level
|
||||
// like so. Is this correct? Is there a better pattern for doing this?
|
||||
.controller('WorkflowMakerController', WorkflowMakerController)
|
||||
.factory('WorkflowMakerForm', WorkflowMakerForm)
|
||||
.directive('workflowMaker', workflowMaker);
|
||||
|
||||
@@ -275,6 +275,10 @@
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.WorkflowMaker-invalidJobTemplateWarning {
|
||||
margin-bottom: 5px;
|
||||
color: @default-err;
|
||||
}
|
||||
|
||||
.Key-list {
|
||||
margin: 0;
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
'$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q', 'JobTemplateModel',
|
||||
'Empty', 'PromptService', 'Rest',
|
||||
function($scope, WorkflowService, GetBasePath, TemplatesService, $state,
|
||||
ProcessErrors, CreateSelect2, WorkflowMakerForm, $q, JobTemplate,
|
||||
Empty, PromptService, Rest) {
|
||||
'$state', 'ProcessErrors', 'CreateSelect2', '$q', 'JobTemplateModel',
|
||||
'Empty', 'PromptService', 'Rest', 'TemplatesStrings',
|
||||
function($scope, WorkflowService, GetBasePath, TemplatesService,
|
||||
$state, ProcessErrors, CreateSelect2, $q, JobTemplate,
|
||||
Empty, PromptService, Rest, TemplatesStrings) {
|
||||
|
||||
let form = WorkflowMakerForm();
|
||||
let promptWatcher, surveyQuestionWatcher;
|
||||
|
||||
$scope.strings = TemplatesStrings;
|
||||
|
||||
$scope.workflowMakerFormConfig = {
|
||||
nodeMode: "idle",
|
||||
activeTab: "jobs",
|
||||
@@ -75,15 +76,19 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
// Create the node
|
||||
let sendableNodeData = {
|
||||
unified_job_template: params.node.unifiedJobTemplate.id,
|
||||
credential: _.get(params, 'node.originalNodeObj.credential') || null
|
||||
extra_data: {},
|
||||
inventory: null,
|
||||
job_type: null,
|
||||
job_tags: null,
|
||||
skip_tags: null,
|
||||
limit: null,
|
||||
diff_mode: null,
|
||||
verbosity: null,
|
||||
credential: null
|
||||
};
|
||||
|
||||
if (_.has(params, 'node.promptData.extraVars')) {
|
||||
if (_.get(params, 'node.promptData.launchConf.defaults.extra_vars')) {
|
||||
if (!sendableNodeData.extra_data) {
|
||||
sendableNodeData.extra_data = {};
|
||||
}
|
||||
|
||||
const defaultVars = jsyaml.safeLoad(params.node.promptData.launchConf.defaults.extra_vars);
|
||||
|
||||
// Only include extra vars that differ from the template default vars
|
||||
@@ -184,7 +189,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
params.node.isNew = false;
|
||||
continueRecursing(data.data.id);
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
ProcessErrors($scope, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add workflow node. ' +
|
||||
'POST returned status: ' +
|
||||
@@ -403,7 +408,11 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
$q.all(associatePromises.concat(credentialPromises))
|
||||
.then(function() {
|
||||
$scope.closeDialog();
|
||||
}).catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, null);
|
||||
});
|
||||
}).catch(({data, status}) => {
|
||||
ProcessErrors($scope, data, status, null);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -552,6 +561,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
}
|
||||
|
||||
$scope.promptData = null;
|
||||
$scope.selectedTemplateInvalid = false;
|
||||
$scope.showPromptButton = false;
|
||||
|
||||
// Reset the edgeConflict flag
|
||||
resetEdgeConflict();
|
||||
@@ -647,6 +658,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
|
||||
|
||||
if ((!$scope.nodeBeingEdited.unifiedJobTemplate.inventory && !launchConf.ask_inventory_on_launch) || !$scope.nodeBeingEdited.unifiedJobTemplate.project) {
|
||||
$scope.selectedTemplateInvalid = true;
|
||||
} else {
|
||||
$scope.selectedTemplateInvalid = false;
|
||||
}
|
||||
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
@@ -658,15 +675,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
!launchConf.ask_diff_mode_on_launch &&
|
||||
!launchConf.survey_enabled &&
|
||||
!launchConf.credential_needed_to_start &&
|
||||
!launchConf.inventory_needed_to_start &&
|
||||
launchConf.passwords_needed_to_start.length === 0 &&
|
||||
launchConf.variables_needed_to_start.length === 0) {
|
||||
$scope.showPromptButton = false;
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
} else {
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
}
|
||||
|
||||
if (responses[1].data.survey_enabled) {
|
||||
@@ -716,38 +735,42 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
}
|
||||
|
||||
if ($scope.nodeBeingEdited.unifiedJobTemplate.type === "job_template") {
|
||||
if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate')) {
|
||||
if (_.get($scope, 'nodeBeingEdited.unifiedJobTemplate.type') === "job_template") {
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
}
|
||||
|
||||
$scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate;
|
||||
|
||||
if ($scope.selectedTemplate.unified_job_type) {
|
||||
switch ($scope.selectedTemplate.unified_job_type) {
|
||||
case "job":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
break;
|
||||
case "project_update":
|
||||
$scope.workflowMakerFormConfig.activeTab = "project_sync";
|
||||
break;
|
||||
case "inventory_update":
|
||||
$scope.workflowMakerFormConfig.activeTab = "inventory_sync";
|
||||
break;
|
||||
}
|
||||
} else if ($scope.selectedTemplate.type) {
|
||||
switch ($scope.selectedTemplate.type) {
|
||||
case "job_template":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
break;
|
||||
case "project":
|
||||
$scope.workflowMakerFormConfig.activeTab = "project_sync";
|
||||
break;
|
||||
case "inventory_source":
|
||||
$scope.workflowMakerFormConfig.activeTab = "inventory_sync";
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
}
|
||||
|
||||
$scope.selectedTemplate = $scope.nodeBeingEdited.unifiedJobTemplate;
|
||||
|
||||
if ($scope.selectedTemplate.unified_job_type) {
|
||||
switch ($scope.selectedTemplate.unified_job_type) {
|
||||
case "job":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
break;
|
||||
case "project_update":
|
||||
$scope.workflowMakerFormConfig.activeTab = "project_sync";
|
||||
break;
|
||||
case "inventory_update":
|
||||
$scope.workflowMakerFormConfig.activeTab = "inventory_sync";
|
||||
break;
|
||||
}
|
||||
} else if ($scope.selectedTemplate.type) {
|
||||
switch ($scope.selectedTemplate.type) {
|
||||
case "job_template":
|
||||
$scope.workflowMakerFormConfig.activeTab = "jobs";
|
||||
break;
|
||||
case "project":
|
||||
$scope.workflowMakerFormConfig.activeTab = "project_sync";
|
||||
break;
|
||||
case "inventory_source":
|
||||
$scope.workflowMakerFormConfig.activeTab = "inventory_sync";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
|
||||
tree: $scope.treeData.data,
|
||||
parentId: parent.id,
|
||||
@@ -759,7 +782,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
switch($scope.nodeBeingEdited.edgeType) {
|
||||
case "always":
|
||||
$scope.edgeType = {label: "Always", value: "always"};
|
||||
if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) {
|
||||
if (siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always") || $scope.nodeBeingEdited.isRoot) {
|
||||
edgeDropdownOptions = ["always"];
|
||||
}
|
||||
break;
|
||||
@@ -794,7 +817,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
$scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
|
||||
finishConfiguringEdit();
|
||||
}, function(error) {
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
ProcessErrors($scope, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get unified job template. GET returned ' +
|
||||
'status: ' + error.status
|
||||
@@ -946,8 +969,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
|
||||
$scope.templateManuallySelected = function(selectedTemplate) {
|
||||
|
||||
$scope.selectedTemplate = angular.copy(selectedTemplate);
|
||||
|
||||
if (selectedTemplate.type === "job_template") {
|
||||
let jobTemplate = new JobTemplate();
|
||||
|
||||
@@ -955,6 +976,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
.then((responses) => {
|
||||
let launchConf = responses[1].data;
|
||||
|
||||
if ((!selectedTemplate.inventory && !launchConf.ask_inventory_on_launch) || !selectedTemplate.project) {
|
||||
$scope.selectedTemplateInvalid = true;
|
||||
} else {
|
||||
$scope.selectedTemplateInvalid = false;
|
||||
}
|
||||
|
||||
$scope.selectedTemplate = angular.copy(selectedTemplate);
|
||||
|
||||
if (!launchConf.survey_enabled &&
|
||||
!launchConf.ask_inventory_on_launch &&
|
||||
!launchConf.ask_credential_on_launch &&
|
||||
@@ -966,15 +995,17 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
!launchConf.ask_diff_mode_on_launch &&
|
||||
!launchConf.survey_enabled &&
|
||||
!launchConf.credential_needed_to_start &&
|
||||
!launchConf.inventory_needed_to_start &&
|
||||
launchConf.passwords_needed_to_start.length === 0 &&
|
||||
launchConf.variables_needed_to_start.length === 0) {
|
||||
$scope.showPromptButton = false;
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
} else {
|
||||
$scope.showPromptButton = true;
|
||||
|
||||
if (launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
|
||||
$scope.promptModalMissingReqFields = true;
|
||||
} else {
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
}
|
||||
|
||||
if (launchConf.survey_enabled) {
|
||||
@@ -1028,7 +1059,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
});
|
||||
} else {
|
||||
// TODO - clear out prompt data?
|
||||
$scope.selectedTemplate = angular.copy(selectedTemplate);
|
||||
$scope.selectedTemplateInvalid = false;
|
||||
$scope.showPromptButton = false;
|
||||
$scope.promptModalMissingReqFields = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1114,7 +1148,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
|
||||
buildTreeFromNodes();
|
||||
}
|
||||
}, function(error){
|
||||
ProcessErrors($scope, error.data, error.status, form, {
|
||||
ProcessErrors($scope, error.data, error.status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get workflow job template nodes. GET returned ' +
|
||||
'status: ' + error.status
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name forms.function:JobTemplate
|
||||
* @description This form is for adding/editing a Job Template
|
||||
*/
|
||||
|
||||
export default ['NotificationsList', 'i18n', '$rootScope', function(NotificationsList, i18n, $rootScope) {
|
||||
return function() {
|
||||
var WorkflowMakerFormObject = {
|
||||
|
||||
addTitle: '',
|
||||
editTitle: '',
|
||||
name: 'workflow_maker',
|
||||
basePath: 'job_templates',
|
||||
tabs: false,
|
||||
cancelButton: false,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
edgeType: {
|
||||
label: i18n._('Type'),
|
||||
type: 'radio_group',
|
||||
ngShow: 'selectedTemplate && edgeFlags.showTypeOptions',
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
||||
options: [
|
||||
{
|
||||
label: i18n._('On Success'),
|
||||
value: 'success',
|
||||
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "successFailure"'
|
||||
},
|
||||
{
|
||||
label: i18n._('On Failure'),
|
||||
value: 'failure',
|
||||
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "successFailure"'
|
||||
},
|
||||
{
|
||||
label: i18n._('Always'),
|
||||
value: 'always',
|
||||
ngShow: '!edgeFlags.typeRestriction || edgeFlags.typeRestriction === "always"'
|
||||
}
|
||||
],
|
||||
awRequiredWhen: {
|
||||
reqExpression: 'edgeFlags.showTypeOptions'
|
||||
}
|
||||
},
|
||||
credential: {
|
||||
label: i18n._('Credential'),
|
||||
type: 'lookup',
|
||||
sourceModel: 'credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCredential()',
|
||||
requiredErrorMsg: i18n._("Please select a Credential."),
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n._("Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
|
||||
" the username and SSH key or password that Ansible will need to log into the remote hosts.") + "</p>",
|
||||
dataTitle: i18n._('Credential'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_credential_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
||||
awRequiredWhen: {
|
||||
reqExpression: 'selectedTemplate && selectedTemplate.ask_credential_on_launch'
|
||||
}
|
||||
},
|
||||
inventory: {
|
||||
label: i18n._('Inventory'),
|
||||
type: 'lookup',
|
||||
sourceModel: 'inventory',
|
||||
sourceField: 'name',
|
||||
list: 'OrganizationList',
|
||||
basePath: 'organization',
|
||||
ngClick: 'lookUpInventory()',
|
||||
requiredErrorMsg: i18n._("Please select an Inventory."),
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n._("Select the inventory containing the hosts you want this job to manage.") + "</p>",
|
||||
dataTitle: i18n._('Inventory'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_inventory_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
||||
awRequiredWhen: {
|
||||
reqExpression: 'selectedTemplate && selectedTemplate.ask_inventory_on_launch'
|
||||
}
|
||||
},
|
||||
job_type: {
|
||||
label: i18n._('Job Type'),
|
||||
type: 'select',
|
||||
ngOptions: 'type.label for type in job_type_options track by type.value',
|
||||
"default": 0,
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n.sprintf(i18n._("When this template is submitted as a job, setting the type to %s will execute the playbook, running tasks " +
|
||||
" on the selected hosts."), "<em>run</em>") + "</p> <p>" +
|
||||
i18n.sprintf(i18n._("Setting the type to %s will not execute the playbook. Instead, %s will check playbook " +
|
||||
" syntax, test environment setup and report problems."), "<em>check</em>", "<code>ansible</code>") + "</p> <p>" +
|
||||
i18n.sprintf(i18n._("Setting the type to %s will execute the playbook and store any " +
|
||||
" scanned facts for use with " + $rootScope.BRAND_NAME + "'s System Tracking feature."), "<em>scan</em>") + "</p>",
|
||||
dataTitle: i18n._('Job Type'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_job_type_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
||||
awRequiredWhen: {
|
||||
reqExpression: 'selectedTemplate && selectedTemplate.ask_job_type_on_launch'
|
||||
}
|
||||
},
|
||||
limit: {
|
||||
label: i18n._('Limit'),
|
||||
type: 'text',
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n.sprintf(i18n._("Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
|
||||
"Multiple patterns can be separated by %s %s or %s"), ";", ":", ",") + "</p><p>" +
|
||||
i18n.sprintf(i18n._("For more information and examples see " +
|
||||
"%sthe Patterns topic at docs.ansible.com%s."), "<a href=\"http://docs.ansible.com/intro_patterns.html\" target=\"_blank\">", "</a>") + "</p>",
|
||||
dataTitle: i18n._('Limit'),
|
||||
dataPlacement: 'right',
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_limit_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
},
|
||||
job_tags: {
|
||||
label: i18n._('Job Tags'),
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
'elementClass': 'Form-textInput',
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n._("Provide a comma separated list of tags.") + "</p>\n" +
|
||||
"<p>" + i18n._("Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.") + "</p>" +
|
||||
"<p>" + i18n._("Consult the Ansible documentation for further details on the usage of tags.") + "</p>",
|
||||
dataTitle: i18n._("Job Tags"),
|
||||
dataPlacement: "right",
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_tags_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
},
|
||||
skip_tags: {
|
||||
label: i18n._('Skip Tags'),
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
'elementClass': 'Form-textInput',
|
||||
class: 'Form-formGroup--fullWidth',
|
||||
awPopOver: "<p>" + i18n._("Provide a comma separated list of tags.") + "</p>\n" +
|
||||
"<p>" + i18n._("Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.") + "</p>" +
|
||||
"<p>" + i18n._("Consult the Ansible documentation for further details on the usage of tags.") + "</p>",
|
||||
dataTitle: i18n._("Skip Tags"),
|
||||
dataPlacement: "right",
|
||||
dataContainer: "body",
|
||||
ngShow: "selectedTemplate.ask_skip_tags_on_launch",
|
||||
ngDisabled: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
}
|
||||
},
|
||||
buttons: {
|
||||
cancel: {
|
||||
ngClick: 'cancelNodeForm()',
|
||||
ngShow: '(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
},
|
||||
close: {
|
||||
ngClick: 'cancelNodeForm()',
|
||||
ngShow: '!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
},
|
||||
select: {
|
||||
ngClick: 'saveNodeForm()',
|
||||
ngDisabled: "workflow_maker_form.$invalid || !selectedTemplate",
|
||||
ngShow: '(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var itm;
|
||||
for (itm in WorkflowMakerFormObject.related) {
|
||||
if (WorkflowMakerFormObject.related[itm].include === "NotificationsList") {
|
||||
WorkflowMakerFormObject.related[itm] = NotificationsList;
|
||||
WorkflowMakerFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||
}
|
||||
}
|
||||
return WorkflowMakerFormObject;
|
||||
};
|
||||
}];
|
||||
@@ -93,31 +93,35 @@
|
||||
<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>
|
||||
<div ng-show="selectedTemplate">
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn">
|
||||
<label for="verbosity" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">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"
|
||||
aria-hidden="true">
|
||||
</select>
|
||||
</div>
|
||||
<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 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()"> 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()"> 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()"> 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)" ng-click="confirmNodeForm()" ng-disabled="workflow_maker_form.$invalid || !selectedTemplate || promptModalMissingReqFields" disabled="disabled"> Select</button>
|
||||
</div>
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-show="selectedTemplate && !selectedTemplateInvalid">
|
||||
<label for="verbosity" class="Form-inputLabelContainer">
|
||||
<span class="Form-requiredAsterisk">*</span>
|
||||
<span class="Form-inputLabel">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"
|
||||
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()"> 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()"> 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()"> 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" ng-click="confirmNodeForm()" ng-disabled="!selectedTemplate || promptModalMissingReqFields"> Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
13
awx/ui/client/src/templates/workflows/workflow.block.less
Normal file
13
awx/ui/client/src/templates/workflows/workflow.block.less
Normal file
@@ -0,0 +1,13 @@
|
||||
.Workflow-warning {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
color: @default-interface-txt;
|
||||
}
|
||||
.Workflow-warningIcon {
|
||||
color: @default-warning;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.Workflow-warningLink {
|
||||
color: @default-link;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -145,7 +145,8 @@ describe('Controller: WorkflowAdd', () => {
|
||||
labels: undefined,
|
||||
organization: undefined,
|
||||
variables: undefined,
|
||||
extra_vars: undefined
|
||||
extra_vars: undefined,
|
||||
allow_simultaneous: undefined
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user