Merge pull request #4425 from mabashian/toggles

Swap text-based on and off toggles to non-text based

Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
softwarefactory-project-zuul[bot] 2019-08-07 19:55:16 +00:00 committed by GitHub
commit d7c15a782f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 163 additions and 1941 deletions

View File

@ -15,3 +15,4 @@
@import 'utility/_index';
@import 'code-mirror/_index';
@import 'cards/_index';
@import 'switch/_index';

View File

@ -44,6 +44,7 @@ import truncate from '~components/truncate/truncate.directive';
import atCodeMirror from '~components/code-mirror';
import card from '~components/cards/card.directive';
import cardGroup from '~components/cards/group.directive';
import atSwitch from '~components/switch/switch.directive';
import BaseInputController from '~components/input/base.controller';
import ComponentsStrings from '~components/components.strings';
@ -98,6 +99,7 @@ angular
.directive('atTruncate', truncate)
.directive('atCard', card)
.directive('atCardGroup', cardGroup)
.directive('atSwitch', atSwitch)
.service('BaseInputController', BaseInputController)
.service('ComponentsStrings', ComponentsStrings);

View File

@ -0,0 +1,85 @@
.atSwitch-listTableCell {
display: flex;
align-items: center;
height: 100%;
justify-content: flex-end;
padding-right: 0px;
}
.atSwitch-outer {
position: relative;
display: inline-block;
width: 40px;
height: 26px;
cursor: pointer;
.fa {
display: none;
}
}
.atSwitch-on {
.atSwitch-slider {
background-color: @default-link;
}
.atSwitch-slider:before {
-webkit-transform: translateX(16px);
-ms-transform: translateX(16px);
transform: translateX(16px);
}
.fa {
display: block;
position: absolute;
top: 7px;
left: 4px;
color: @default-bg;
font-size: 12px;
}
}
.atSwitch-disabled {
cursor: not-allowed;
.atSwitch-inner {
pointer-events: none;
}
.atSwitch-slider {
background-color: @default-icon;
}
.atSwitch-slider:before {
background-color: @d2grey;
}
.fa {
color: @d2grey;
}
}
.atSwitch-slider {
position: absolute;
border-radius: 34px;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: @d2grey;
-webkit-transition: .4s;
transition: .4s;
}
.atSwitch-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 5px;
background-color: @default-bg;
-webkit-transition: .4s;
transition: .4s;
border-radius: 50%;
}

View File

@ -0,0 +1,22 @@
const templateUrl = require('~components/switch/switch.partial.html');
function atSwitch () {
return {
restrict: 'E',
replace: true,
templateUrl,
scope: {
hide: '=',
onToggle: '&',
switchOn: '=',
switchDisabled: '=',
tooltip: '=',
tooltipString: '@',
tooltipPlacement: '@',
tooltipContainer: '@',
tooltipWatch: '='
},
};
}
export default atSwitch;

View File

@ -0,0 +1,6 @@
<span class="atSwitch-outer" ng-hide="hide" ng-class="{'atSwitch-on': switchOn, 'atSwitch-disabled': switchDisabled}" aw-tool-tip="{{tooltipString ? tooltipString : tooltip}}" data-placement="{{tooltipPlacement}}" data-tip-watch="tooltipWatch">
<span class="atSwitch-inner" ng-click="onToggle()">
<span class="atSwitch-slider"></span>
<i class="fa fa-check"></i>
</span>
</span>

View File

@ -79,7 +79,6 @@
@import '../../src/inventories-hosts/inventories/inventories.block.less';
@import '../../src/inventories-hosts/shared/associate-groups/associate-groups.block.less';
@import '../../src/inventories-hosts/shared/associate-hosts/associate-hosts.block.less';
@import '../../src/job-submission/job-submission.block.less';
@import '../../src/license/license.block.less';
@import '../../src/login/loginModal/thirdPartySignOn/thirdPartySignOn.block.less';
@import '../../src/login/loginModal/loginModal.block.less';
@ -93,7 +92,6 @@
@import '../../src/scheduler/schedulerDatePicker.block.less';
@import '../../src/scheduler/schedulerFormDetail.block.less';
@import '../../src/scheduler/schedulertime.block.less';
@import '../../src/scheduler/scheduleToggle.block.less';
@import '../../src/scheduler/spinnerInput.block.less';
@import '../../src/shared/container/container.block.less';
@import '../../src/shared/detail-nav/detail-nav.block.less';

View File

@ -28,17 +28,14 @@
<div ng-show="systemVm.activeSystemForm === 'logging'">
<div class="form-group Form-formGroup">
<label class="Form-inputLabelContainer" for="LOG_AGGREGATOR_ENABLED">
<span class="Form-inputLabel">
<span class="Form-inputLabel" translate>
Enable External Logging
</span>
<a id="awp-LOG_AGGREGATOR_ENABLED" href="" aw-pop-over="Enable sending logs to external log aggregator." data-placement="top" over-title="Enable External Logging" class="help-link" tabindex="-1">
<i class="fa fa-question-circle"></i>
</a>
</label>
<div class="ScheduleToggle" ng-class="{'is-on': LOG_AGGREGATOR_ENABLED, 'ScheduleToggle--disabled': (!LOG_AGGREGATOR_ENABLED && (!requiredLogValues.LOG_AGGREGATOR_HOST || requiredLogValues.LOG_AGGREGATOR_HOST === '' || !requiredLogValues.LOG_AGGREGATOR_TYPE || requiredLogValues.LOG_AGGREGATOR_TYPE === '')) || $rootScope.user_is_system_auditor}">
<button ng-show="LOG_AGGREGATOR_ENABLED" class="ScheduleToggle-switch is-on" ng-click="toggleForm('LOG_AGGREGATOR_ENABLED')" ng-disabled="$rootScope.user_is_system_auditor">ON</button>
<button ng-show="!LOG_AGGREGATOR_ENABLED" class="ScheduleToggle-switch" ng-click="toggleForm('LOG_AGGREGATOR_ENABLED')" ng-disabled="!requiredLogValues.LOG_AGGREGATOR_HOST || requiredLogValues.LOG_AGGREGATOR_HOST === '' || !requiredLogValues.LOG_AGGREGATOR_TYPE || requiredLogValues.LOG_AGGREGATOR_TYPE === '' || $rootScope.user_is_system_auditor">OFF</button>
</div>
<at-switch on-toggle="toggleForm('LOG_AGGREGATOR_ENABLED')" switch-on="LOG_AGGREGATOR_ENABLED" switch-disabled="(!LOG_AGGREGATOR_ENABLED && (!requiredLogValues.LOG_AGGREGATOR_HOST || requiredLogValues.LOG_AGGREGATOR_HOST === '' || !requiredLogValues.LOG_AGGREGATOR_TYPE || requiredLogValues.LOG_AGGREGATOR_TYPE === '')) || $rootScope.user_is_system_auditor"></at-switch>
</div>
<div id="system-logging-form">

View File

@ -131,9 +131,8 @@ input#filePickerText {
position: relative;
display: inline-block;
// Filepicker and toggle disabling
.Form-browseButton, .Form-filePicker--textBox,
.ScheduleToggle {
// Filepicker disabling
.Form-browseButton, .Form-filePicker--textBox {
pointer-events: none;
cursor: not-allowed;
}

View File

@ -45,24 +45,7 @@
<at-row ng-repeat="instance in vm.instances"
ng-class="{'at-Row--active': (instance.id === vm.activeId)}">
<div class="at-Row-toggle">
<div class="ScheduleToggle"
ng-class="{'is-on': instance.enabled,
'ScheduleToggle--disabled': vm.rowAction.toggle._disabled}">
<button ng-show="instance.enabled"
type="button"
class="ScheduleToggle-switch is-on ng-hide"
ng-click="vm.toggle(instance)"
ng-disabled="vm.rowAction.toggle._disabled">
{{:: vm.strings.get('ON') }}
</button>
<button ng-show="!instance.enabled"
type="button"
class="ScheduleToggle-switch"
ng-click="vm.toggle(instance)"
ng-disabled="vm.rowAction.toggle._disabled">
{{:: vm.strings.get('OFF') }}
</button>
</div>
<at-switch on-toggle="vm.toggle(instance)" switch-on="instance.enabled" switch-disabled="vm.rowAction.toggle._disabled"></at-switch>
</div>
<div class="at-Row-items at-Row-items--instances">

View File

@ -52,10 +52,7 @@
<div class="List-staticColumnLayout--hosts List-tableRow" ng-repeat="host in hosts track by host.id">
<div></div>
<div class="List-tableCell toggleHost-column List-staticColumn--toggle">
<div class="ScheduleToggle" ng-class="{'is-on': host.enabled}" aw-tool-tip="{{strings.get('hostList.DISABLED_TOGGLE_TOOLTIP')}}" data-placement="right" data-tip-watch="undefined">
<button type="button" ng-show="host.enabled" class="ScheduleToggle-switch is-on" ng-click="toggleHost($event, host)" translate>ON</button>
<button type="button" ng-show="!host.enabled" class="ScheduleToggle-switch" ng-click="toggleHost($event, host)" translate>OFF</button>
</div>
<at-switch on-toggle="toggleHost($event, host)" switch-on="host.enabled" switch-disabled="!host.summary_fields.user_capabilities.edit" tooltip="strings.get('hostList.DISABLED_TOGGLE_TOOLTIP')" tooltip-placement="right"></at-switch>
</div>
<div class="List-tableCell active_failures-column status-column List-staticColumn--smallStatus">
<div class="host-name">

View File

@ -80,9 +80,8 @@ function HostsList($scope, HostsList, $rootScope, GetBasePath,
}
host.enabled = !host.enabled;
HostsService.put(host).then(function(){
$state.go($state.current, null, {reload: true});
HostsService.patch(host.id, {
enabled: host.enabled
});
};

View File

@ -34,6 +34,15 @@
.catch(this.error.bind(this))
.finally(Wait('stop'));
},
patch: function(id, data){
Wait('start');
this.url = GetBasePath('hosts') + id;
Rest.setUrl(this.url);
return Rest.patch(data)
.then(this.success.bind(this))
.catch(this.error.bind(this))
.finally(Wait('stop'));
},
post: function(host){
Wait('start');
this.url = GetBasePath('hosts');

View File

@ -1,92 +0,0 @@
export default
function GetSurveyQuestions($filter, GetBasePath, Rest, Empty, ProcessErrors, $stateParams) {
// This factory goes out and gets a job template's survey questions and attaches
// them to scope so that they can be ng-repeated in the job submission template
return function(params) {
var id= params.id,
scope = params.scope,
submitJobType = params.submitJobType,
i,
survey_url;
if(submitJobType === 'job_template') {
survey_url = GetBasePath('job_templates') + id + '/survey_spec/';
}
else if(submitJobType === 'workflow_job_template') {
survey_url = GetBasePath('workflow_job_templates') + id + '/survey_spec/';
}
Rest.setUrl(survey_url);
Rest.get()
.then(({data}) => {
if(!Empty(data)){
scope.survey_name = data.name;
scope.survey_description = data.description;
scope.survey_questions = data.spec;
for(i=0; i<scope.survey_questions.length; i++){
var question = scope.survey_questions[i];
question.index = i;
question.question_name = $filter('sanitize')(question.question_name);
question.question_description = (question.question_description) ? $filter('sanitize')(question.question_description) : undefined;
if(question.type === "textarea" && !Empty(question.default_textarea)) {
question.model = angular.copy(question.default_textarea);
}
else if(question.type === "multiselect") {
question.model = question.default.split(/\n/);
question.choices = question.choices.split(/\n/);
}
else if(question.type === "multiplechoice") {
question.model = question.default ? angular.copy(question.default) : "";
question.choices = question.choices.split(/\n/);
// Add a default empty string option to the choices array. If this choice is
// selected then the extra var will not be sent when we POST to the launch
// endpoint
if(!question.required) {
question.choices.unshift('');
}
}
else if(question.type === "float"){
question.model = (!Empty(question.default)) ? angular.copy(question.default) : (!Empty(question.default_float)) ? angular.copy(question.default_float) : "";
}
else {
question.model = question.default ? angular.copy(question.default) : "";
}
if(question.type === "text" || question.type === "textarea" || question.type === "password") {
question.minlength = (!Empty(question.min)) ? Number(question.min) : "";
question.maxlength = (!Empty(question.max)) ? Number(question.max) : "" ;
}
else if(question.type === "integer") {
question.minValue = (!Empty(question.min)) ? Number(question.min) : "";
question.maxValue = (!Empty(question.max)) ? Number(question.max) : "" ;
}
else if(question.type === "float") {
question.minValue = (!Empty(question.min)) ? question.min : "";
question.maxValue = (!Empty(question.max)) ? question.max : "" ;
}
}
return;
}
})
.catch(({data, status}) => {
ProcessErrors(scope, data, status, { hdr: 'Error!',
msg: 'Failed to retrieve organization: ' + $stateParams.id + '. GET status: ' + status });
});
};
}
GetSurveyQuestions.$inject =
[ '$filter',
'GetBasePath',
'Rest' ,
'Empty',
'ProcessErrors',
'$stateParams'
];

View File

@ -1,261 +0,0 @@
.JobSubmission {
padding: 20px!important;
display: none;
height: auto!important;
min-height: 400px!important;
}
.JobSubmission-container {
flex-direction: column;
display: flex;
height: auto;
min-height: 360px;
}
.JobSubmission-dialog {
padding: 0px;
margin-bottom: 20px;
.ui-dialog-buttonpane, .ui-dialog-titlebar {
display:none;
}
}
.JobSubmission-header {
display: flex;
flex: 0 0 auto;
}
.JobSubmission-title {
align-items: center;
flex: 1 0 auto;
display: flex;
word-wrap: break-word;
word-break: break-all;
max-width: 98%;
}
.JobSubmission-titleText {
color: @list-title-txt;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
}
.JobSubmission-titleLockup {
margin-left: 4px;
margin-right: 6px;
display: inline-block;
margin-top: 0px;
padding-bottom: 2px;
vertical-align: bottom;
}
.JobSubmission-titleLockup:before {
content: "\007C";
color: @default-icon-hov;
display: block;
font-size: 13px;
}
.JobSubmission-close {
justify-content: flex-end;
display: flex;
}
.JobSubmission-exit{
cursor:pointer;
padding:0px;
border: none;
height:20px;
font-size: 20px;
background-color:@default-bg;
color:@d7grey;
transition: color 0.2s;
line-height:1;
}
.JobSubmission-exit:hover{
color:@default-icon;
}
.JobSubmission-stepsContainer {
display: flex;
flex: 0 0 auto;
margin-top: 25px;
}
.JobSubmission-steps {
display: flex;
margin-bottom: 20px;
min-height: 30px;
}
.JobSubmission-step {
color: @default-interface-txt;
background-color: @default-bg;
font-size: 12px;
border: 1px solid @default-border;
height: 30px;
border-radius: 5px;
margin-right: 20px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 5px;
padding-top: 5px;
transition: background-color 0.2s;
text-transform: uppercase;
line-height: 20px;
white-space: nowrap;
}
.JobSubmission-step:hover {
color: @btn-txt;
background-color: @btn-bg-hov;
cursor: pointer;
}
.JobSubmission-step--active {
color: @default-bg!important;
background-color: @default-icon!important;
border-color: @default-icon!important;
cursor: default!important;
}
.JobSubmission-step--disabled {
opacity: 0.65;
cursor: not-allowed!important;
}
.JobSubmission-formContainer {
display: flex;
flex: 1 0 auto;
}
.JobSubmission-form {
display: flex;
flex: 1 0 auto;
max-width: 100%;
flex-direction: column;
}
.JobSubmission-footerContainer {
display: flex;
flex: 0 0 auto;
margin-top: 15px;
justify-content: space-between;
}
.JobSubmission-footerPreview {
display: flex;
}
.JobSubmission-footerButtons {
justify-content: flex-end;
display: flex;
align-items: flex-end;
}
.JobSubmission-previewItem {
min-width: 150px;
font-weight: normal;
font-size: small;
}
.JobSubmission-previewItemTitle, .JobSubmission-previewItemSubTitle, .JobSubmission-selectedItemInfoSubTitle {
color: @default-interface-txt;
}
.JobSubmission-previewItemNone {
color: @default-icon;
}
.JobSubmission-actionButton {
background-color: @submit-button-bg;
color: @submit-button-text;
height: 30px;
padding-left:15px;
padding-right: 15px;
width: 85px;
}
.JobSubmission-actionButton:hover,
.JobSubmission-actionButton:focus {
color: @submit-button-text;
background-color: @submit-button-bg-hov;
}
.JobSubmission-defaultButton{
background-color: @default-bg;
color: @btn-txt;
text-transform: uppercase;
border-radius: 5px;
border: 1px solid @btn-bord;
padding-left:15px;
padding-right: 15px;
height: 30px;
min-width: 85px;
margin-right: 20px;
}
.JobSubmission-defaultButton:hover{
background-color: @btn-bg-hov;
color: @btn-txt;
}
.JobSubmission-revertLink {
font-size: 12px;
}
.JobSubmission-selectedItem {
display: flex;
flex: 1 0 auto;
margin-bottom: 15px;
align-items: baseline;
}
.JobSubmission-selectedItemInfo {
display: flex;
flex: 0 0 100%;
background-color: @default-no-items-bord;
border: 1px solid @default-border;
padding: 10px;
border-radius: 5px;
max-height: 120px;
overflow-y: scroll;
}
.JobSubmission-selectedItemRevert {
display: flex;
flex: 0 0 auto;
}
.JobSubmission-credentialSubSection {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 15px;
}
.JobSubmission-selectedItemLabel, .JobSubmission-label {
color: @default-interface-txt;
margin-right: 10px;
}
.JobSubmission-label {
line-height: 24px;
}
.JobSubmission-selectedItemNone {
color: @default-icon;
}
.JobSubmission-selectedItemContainer {
display: block;
width: 100%;
}
.JobSubmission-instructions {
color: @default-interface-txt;
margin-top: 25px;
margin-bottom: 15px;
}
.JobSubmission-passwordButton {
padding: 5px 13px!important;
}
.JobSubmission .List-noItems {
margin-top: auto;
}
.JobSubmission-selectedItemLabel {
flex: 0 0 80px;
line-height: 29px;
}
.JobSubmission-previewTags--outer {
flex: 1 0 auto;
max-width: ~"calc(100% - 140px)";
}
.JobSubmission-previewTags--inner {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.JobSubmission-previewTagLabel {
color: @default-interface-txt;
}
.JobSubmission-previewTagLabel--deletable{
color: @default-list-header-bg;
}
.JobSubmission-previewTagRevert {
flex: 0 0 60px;
line-height: 29px;
}
.JobSubmission-previewTagContainer {
display: flex;
}
.JobSubmission-credentialSubSection .select2 {
width: 50% !important;
}

View File

@ -1,584 +0,0 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name controllers.function:JobSubmission
* @description This controller's for the Job Submission Modal
* The job-submission directive is intended to handle job launch/relaunch from a playbook. There are 4 potential steps involved in launching a job:
*
* Select an Inventory
* Select a Credential
* Extra prompts (extra vars, limit, job type, job tags)
* Fill in survey
*
* #Workflow when user hits launch button
*
* A 'get' call is made to the API's 'job_templates/:job_template_id/launch' endpoint for that job template. The response from the API will specify
*
*```
* "credential_needed_to_start": true,
* "can_start_without_user_input": false,
* "ask_variables_on_launch": false,
* "passwords_needed_to_start": [],
* "variables_needed_to_start": [],
* "survey_enabled": false
*```
* #Step 1 - Hit the launch/relaunch endpoint
*
* The launch/relaunch endpoint(s) let us know what the default values are for a particular job template. It also lets us know what the creator of
* the job template selected as "promptable" fields.
*
* #Step 2 - Gather inv/credential lists and job template survey questions
*
* If the job template allows for inventory or credential prompting then we need to go out and get the available inventories and credentials for the
* launching user. We also go out and get the survey from its endpoint if a survey has been created and is enabled for this job template (getsurveyquestions.factory).
*
* #Step 3 - Fill out the job launch form
*
* No server calls needed as a user fills out the form. Note that if no user input is required (no prompts, no passwords) then we skip ahead to the next
* step.
*
* #Step 4 - Launch the job: LaunchJob
*
* This is maybe the most crucial step. We have setup everything we need in order to gather information from the user and now we want to be sure
* we handle it correctly. And there are many scenarios to take into account. The first scenario we check for is is ``survey_enabled=true`` and
* ``prompt_for_vars=false``, in which case we want to make sure to include the extra_vars from the job template in the data being
* sent to the API (it is important to note that anything specified in the extra vars on job submission will override vars specified in the job template.
* Likewise, any variables specified in the extra vars that are duplicated by the survey vars, will get overridden by the survey vars).
* If the previous scenario is NOT the case, then we continue to gather the modal's answers regularly: gather the passwords, then the extra_vars, then
* any survey results. Also note that we must gather any required survey answers, as well as any optional survey answers that happened to be provided
* by the user. We also include the credential that was chosen if the user was prompted to select a credential.
* At this point we have all the info we need and we are almost ready to perform a POST to the '/launch' endpoint. We must lastly check
* if the user was not prompted for anything and therefore we don't want to pass any extra_vars to the POST. Once this is done we
* make the REST POST call and provide all the data to hte API. The response from the API will be the job ID, which is used to redirect the user
* to the job detail page for that job run.
*
* @Usage
* This is usage information.
*/
export default
[ '$scope', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors',
'LaunchJob', '$state', 'InventoryList', 'CredentialList', 'ParseTypeChange',
function($scope, GetBasePath, Wait, Rest, ProcessErrors,
LaunchJob, $state, InventoryList, CredentialList, ParseTypeChange) {
var launch_url;
var clearRequiredPasswords = function() {
$scope.ssh_password_required = false;
$scope.ssh_key_unlock_required = false;
$scope.become_password_required = false;
$scope.ssh_password = "";
$scope.ssh_key_unlock = "";
$scope.become_password = "";
};
var launchJob = function() {
LaunchJob({
scope: $scope,
url: launch_url,
submitJobType: $scope.submitJobType,
relaunchHostType: $scope.relaunchHostType
});
};
// This gets things started - goes out and hits the launch endpoint (based on launch/relaunch) and
// prepares the form fields, defauts, etc.
$scope.init = function() {
$scope.forms = {};
$scope.passwords = {};
$scope.selected_credentials = {
machine: null,
extra: []
};
// As of 3.0, the only place the user can relaunch a
// playbook is on jobTemplates.edit (completed_jobs tab),
// jobs, and jobResults $states.
if (!$scope.submitJobRelaunch) {
if($scope.submitJobType && $scope.submitJobType === 'job_template') {
launch_url = GetBasePath('job_templates') + $scope.submitJobId + '/launch/';
}
else if($scope.submitJobType && $scope.submitJobType === 'workflow_job_template') {
launch_url = GetBasePath('workflow_job_templates') + $scope.submitJobId + '/launch/';
}
}
else {
if($scope.submitJobType && $scope.submitJobType === 'workflow_job') {
launch_url = GetBasePath('workflow_jobs') + $scope.submitJobId + '/relaunch/';
}
else {
launch_url = GetBasePath('jobs') + $scope.submitJobId + '/relaunch/';
}
}
$scope.$watch('selected_credentials.machine', function(){
if($scope.selected_credentials.machine) {
if($scope.selected_credentials.machine.id === $scope.defaults.credential.id) {
clearRequiredPasswords();
for(var i=0; i<$scope.passwords_needed_to_start.length; i++) {
var password = $scope.passwords_needed_to_start[i];
switch(password) {
case "ssh_password":
$scope.ssh_password_required = true;
break;
case "ssh_key_unlock":
$scope.ssh_key_unlock_required = true;
break;
case "become_password":
$scope.become_password_required = true;
break;
}
}
}
else {
$scope.ssh_password_required = ($scope.selected_credentials.machine.inputs && $scope.selected_credentials.machine.inputs.password === "ASK") ? true : false;
$scope.ssh_key_unlock_required = ($scope.selected_credentials.machine.inputs && $scope.selected_credentials.machine.inputs.ssh_key_unlock === "ASK") ? true : false;
$scope.become_password_required = $scope.selected_credentials.machine.inputs && ($scope.selected_credentials.machine.inputs.become_password === "ASK") ? true : false;
}
}
else {
clearRequiredPasswords();
}
});
// Get the job or job_template record
Wait('start');
Rest.setUrl(launch_url);
Rest.get()
.then(({data}) => {
// Put all the data that we get back about the launch onto scope
angular.extend($scope, data);
// General catch-all for "other prompts" - used in this link function and to hide the Other Prompts tab when
// it should be hidden
$scope.has_other_prompts = (data.ask_verbosity_on_launch || data.ask_job_type_on_launch || data.ask_limit_on_launch || data.ask_tags_on_launch || data.ask_skip_tags_on_launch || data.ask_variables_on_launch || data.ask_diff_mode_on_launch) ? true : false;
$scope.password_needed = data.passwords_needed_to_start && data.passwords_needed_to_start.length > 0;
$scope.has_default_inventory = data.defaults && data.defaults.inventory && data.defaults.inventory.id;
$scope.has_default_credential = data.defaults && data.defaults.credential && data.defaults.credential.id;
$scope.has_default_vault_credential = data.defaults && data.defaults.vault_credential && data.defaults.vault_credential.id;
$scope.vault_password_required = ($scope.password_needed && data.passwords_needed_to_start.includes('vault_password'));
$scope.has_default_extra_credentials = data.defaults && data.defaults.extra_credentials && data.defaults.extra_credentials.length > 0;
$scope.other_prompt_data = {};
let getChoices = (options, lookup) => {
return _.get(options, lookup, []).map(c => ({label: c[1], value: c[0]}));
};
let getChoiceFromValue = (choices, value) => {
return _.find(choices, item => item.value === value);
};
if ($scope.has_other_prompts) {
Rest.options()
.then(options => {
if ($scope.ask_job_type_on_launch) {
let choices = getChoices(options.data, 'actions.POST.job_type.choices');
let initialValue = _.get(data, 'defaults.job_type');
let initialChoice = getChoiceFromValue(choices, initialValue);
$scope.other_prompt_data.job_type_options = choices;
$scope.other_prompt_data.job_type = initialChoice;
}
if ($scope.ask_verbosity_on_launch) {
let choices = getChoices(options.data, 'actions.POST.verbosity.choices');
let initialValue = _.get(data, 'defaults.verbosity');
let initialChoice = getChoiceFromValue(choices, initialValue);
$scope.other_prompt_data.verbosity_options = choices;
$scope.other_prompt_data.verbosity = initialChoice;
}
})
.catch((error) => {
ProcessErrors($scope, error.data, error.status, null, {
hdr: 'Error!',
msg: `Failed to get ${launch_url}. OPTIONS status: ${error.status}`
});
});
}
if($scope.ask_limit_on_launch) {
$scope.other_prompt_data.limit = (data.defaults && data.defaults.limit) ? data.defaults.limit : "";
}
if($scope.ask_tags_on_launch) {
$scope.other_prompt_data.job_tags_options = (data.defaults && data.defaults.job_tags) ? data.defaults.job_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
$scope.other_prompt_data.job_tags = $scope.other_prompt_data.job_tags_options;
}
if($scope.ask_skip_tags_on_launch) {
$scope.other_prompt_data.skip_tags_options = (data.defaults && data.defaults.skip_tags) ? data.defaults.skip_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
$scope.other_prompt_data.skip_tags = $scope.other_prompt_data.skip_tags_options;
}
if($scope.ask_diff_mode_on_launch) {
$scope.other_prompt_data.diff_mode = (data.defaults && data.defaults.diff_mode) ? data.defaults.diff_mode : false;
}
if($scope.ask_variables_on_launch) {
$scope.jobLaunchVariables = (data.defaults && data.defaults.extra_vars) ? data.defaults.extra_vars : "---";
$scope.other_prompt_data.parseType = 'yaml';
$scope.parseType = 'yaml';
}
if($scope.has_default_inventory) {
$scope.selected_inventory = angular.copy($scope.defaults.inventory);
}
if($scope.has_default_credential) {
$scope.selected_credentials.machine = angular.copy($scope.defaults.credential);
}
if($scope.has_default_vault_credential) {
$scope.selected_credentials.vault = angular.copy($scope.defaults.vault_credential);
}
if($scope.has_default_extra_credentials) {
$scope.selected_credentials.extra = angular.copy($scope.defaults.extra_credentials);
}
if( ($scope.submitJobType === 'workflow_job_template' && !$scope.survey_enabled) || ($scope.submitJobRelaunch && !$scope.password_needed) || (!$scope.submitJobRelaunch && $scope.can_start_without_user_input && !$scope.ask_inventory_on_launch && !$scope.ask_credential_on_launch && !$scope.has_other_prompts && !$scope.survey_enabled)) {
// The job can be launched if
// a) It's a relaunch and no passwords are needed
// or
// b) It's not a relaunch and there's not any prompting/surveys
launchJob();
Wait('stop');
}
else {
var initiateModal = function() {
// Go out and get the credential types
Rest.setUrl(GetBasePath('credential_types'));
Rest.get()
.then( (response) => {
let credentialTypeData = response.data;
let credential_types = {};
$scope.credentialTypeOptions = [];
credentialTypeData.results.forEach((credentialType => {
credential_types[credentialType.id] = credentialType;
if(credentialType.kind.match(/^(machine|cloud|net|ssh)$/)) {
$scope.credentialTypeOptions.push({
name: credentialType.name,
value: credentialType.id
});
}
}));
$scope.credential_types = credential_types;
})
.catch(({data, status}) => {
ProcessErrors($scope, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get credential types. GET status: ' + status
});
});
// Figure out which step the user needs to start on
if($scope.ask_inventory_on_launch) {
$scope.setStep("inventory", true);
}
else if($scope.ask_credential_on_launch || $scope.password_needed) {
$scope.setStep("credential", true);
}
else if($scope.has_other_prompts) {
$scope.setStep("otherprompts", true);
}
else if($scope.survey_enabled) {
$scope.setStep("survey", true);
}
$scope.openLaunchModal();
};
if($scope.submitJobRelaunch) {
// Go out and get some of the job details like inv, cred, name
Rest.setUrl(GetBasePath('jobs') + $scope.submitJobId);
Rest.get()
.then( (response) => {
let jobResultData = response.data;
$scope.job_template_data = {
name: jobResultData.name
};
$scope.defaults = {};
if(jobResultData.summary_fields.inventory) {
$scope.defaults.inventory = angular.copy(jobResultData.summary_fields.inventory);
$scope.selected_inventory = angular.copy(jobResultData.summary_fields.inventory);
}
if(jobResultData.summary_fields.credential) {
$scope.defaults.credential = angular.copy(jobResultData.summary_fields.credential);
$scope.selected_credentials.machine = angular.copy(jobResultData.summary_fields.credential);
}
initiateModal();
})
.catch(({data, status}) => {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get job details. GET returned status: ' + status });
});
}
else {
// Move forward with the modal
initiateModal();
}
}
})
.catch(({data, status}) => {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get job template details. GET returned status: ' + status });
});
};
$scope.setStep = function(step, initialStep) {
$scope.step = step;
if(step === "credential") {
$scope.credentialTabEnabled = true;
}
else if(step === "otherprompts") {
$scope.otherPromptsTabEnabled = true;
if(!initialStep && $scope.step === 'otherprompts' && $scope.ask_variables_on_launch && !$scope.extra_vars_code_mirror_loaded) {
ParseTypeChange({
scope: $scope,
variable: 'jobLaunchVariables',
field_id: 'job_launch_variables'
});
$scope.extra_vars_code_mirror_loaded = true;
}
}
else if(step === "survey") {
$scope.surveyTabEnabled = true;
}
};
$scope.revertToDefaultInventory = function() {
if($scope.has_default_inventory) {
$scope.selected_inventory = angular.copy($scope.defaults.inventory);
}
else {
$scope.selected_inventory = null;
}
};
$scope.revertToDefaultCredentials = function() {
if($scope.has_default_credential) {
$scope.selected_credentials.machine = angular.copy($scope.defaults.credential);
}
else {
$scope.selected_credentials.machine = null;
}
if($scope.has_default_vault_credential) {
$scope.selected_credentials.vault = angular.copy($scope.defaults.vault_credential);
}
else {
$scope.selected_credentials.vault = null;
}
if($scope.has_default_extra_credentials) {
$scope.selected_credentials.extra = angular.copy($scope.defaults.extra_credentials);
}
else {
$scope.selected_credentials.extra = [];
}
};
$scope.toggle_credential = function(cred) {
$scope.credentials.forEach(function(row, i) {
if (row.id === cred.id) {
$scope.selected_credentials.machine = angular.copy(row);
$scope.credentials[i].checked = 1;
} else {
$scope.credentials[i].checked = 0;
}
});
};
$scope.getActionButtonText = function() {
if($scope.step === "inventory") {
return ($scope.ask_credential_on_launch || $scope.password_needed || $scope.has_other_prompts || $scope.survey_enabled) ? "NEXT" : "LAUNCH";
}
else if($scope.step === "credential") {
return ($scope.has_other_prompts || $scope.survey_enabled) ? "NEXT" : "LAUNCH";
}
else if($scope.step === "otherprompts") {
return ($scope.survey_enabled) ? "NEXT" : "LAUNCH";
}
else if($scope.step === "survey") {
return "LAUNCH";
}
};
$scope.actionButtonDisabled = function() {
if($scope.step === "inventory") {
if($scope.selected_inventory) {
return false;
}
else {
$scope.credentialTabEnabled = false;
$scope.otherPromptsTabEnabled = false;
$scope.surveyTabEnabled = false;
return true;
}
}
else if($scope.step === "credential") {
if(($scope.selected_credentials.machine || $scope.selected_credentials.vault) && $scope.forms.credentialpasswords && $scope.forms.credentialpasswords.$valid) {
return false;
}
else {
$scope.otherPromptsTabEnabled = false;
$scope.surveyTabEnabled = false;
return true;
}
}
else if($scope.step === "otherprompts") {
if($scope.forms.otherprompts.$valid) {
return false;
}
else {
$scope.surveyTabEnabled = false;
return true;
}
}
else if($scope.step === "survey") {
if($scope.forms.survey.$valid) {
return false;
}
else {
return true;
}
}
};
$scope.takeAction = function() {
if($scope.step === "inventory") {
// Check to see if there's another step after this one
if($scope.ask_credential_on_launch || $scope.password_needed) {
$scope.setStep("credential");
}
else if($scope.has_other_prompts) {
$scope.setStep("otherprompts");
}
else if($scope.survey_enabled) {
$scope.setStep("survey");
}
else {
launchJob();
}
}
else if($scope.step === "credential") {
if($scope.has_other_prompts) {
$scope.setStep("otherprompts");
}
else if($scope.survey_enabled) {
$scope.setStep("survey");
}
else {
launchJob();
}
}
else if($scope.step === "otherprompts") {
if($scope.survey_enabled) {
$scope.setStep("survey");
}
else {
launchJob();
}
}
else {
launchJob();
}
};
$scope.toggleForm = function(key) {
$scope.other_prompt_data[key] = !$scope.other_prompt_data[key];
};
$scope.updateParseType = function() {
// This is what the ParseTypeChange factory is expecting
// It shares the same scope with this directive and will
// pull the new value of parseType out to determine which
// direction to convert the extra vars
$scope.parseType = $scope.other_prompt_data.parseType;
$scope.parseTypeChange('parseType', 'jobLaunchVariables');
};
$scope.showRevertCredentials = function(){
let machineCredentialMatches = true;
let extraCredentialsMatch = true;
if($scope.defaults.credential && $scope.defaults.credential.id) {
if(!$scope.selected_credentials.machine || ($scope.selected_credentials.machine && $scope.selected_credentials.machine.id !== $scope.defaults.credential.id)) {
machineCredentialMatches = false;
}
}
else {
if($scope.selected_credentials.machine && $scope.selected_credentials.machine.id) {
machineCredentialMatches = false;
}
}
if($scope.defaults.extra_credentials && $scope.defaults.extra_credentials.length > 0) {
if($scope.selected_credentials.extra && $scope.selected_credentials.extra.length > 0) {
if($scope.defaults.extra_credentials.length !== $scope.selected_credentials.extra.length) {
extraCredentialsMatch = false;
}
else {
$scope.defaults.extra_credentials.forEach((defaultExtraCredential) =>{
let matchesSelected = false;
$scope.selected_credentials.extra.forEach((selectedExtraCredential) =>{
if(defaultExtraCredential.id === selectedExtraCredential.id) {
matchesSelected = true;
}
});
if(!matchesSelected) {
extraCredentialsMatch = false;
}
});
}
}
else {
extraCredentialsMatch = false;
}
}
else {
if($scope.selected_credentials.extra && $scope.selected_credentials.extra.length > 0) {
extraCredentialsMatch = false;
}
}
return machineCredentialMatches && extraCredentialsMatch ? false : true;
};
$scope.$on('inventorySelected', function(evt, selectedRow){
$scope.selected_inventory = _.cloneDeep(selectedRow);
});
$scope.deleteMachineCred = function() {
$scope.selected_credentials.machine = null;
};
$scope.deleteExtraCred = function(index) {
$scope.selected_credentials.extra.splice(index, 1);
};
$scope.deleteSelectedInventory = function() {
$scope.selected_inventory = null;
};
}
];

View File

@ -1,144 +0,0 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import jobSubmissionController from './job-submission.controller';
export default [ 'templateUrl', 'CreateDialog', 'Wait', 'CreateSelect2', 'ParseTypeChange', 'GetSurveyQuestions', 'i18n', 'credentialTypesLookup', '$transitions',
function(templateUrl, CreateDialog, Wait, CreateSelect2, ParseTypeChange, GetSurveyQuestions, i18n, credentialTypesLookup, $transitions) {
return {
scope: {
submitJobId: '=',
submitJobType: '@',
submitJobRelaunch: '=',
relaunchHostType: '@'
},
templateUrl: templateUrl('job-submission/job-submission'),
controller: jobSubmissionController,
restrict: 'E',
link: function(scope) {
scope.openLaunchModal = function() {
if (scope.removeLaunchJobModalReady) {
scope.removeLaunchJobModalReady();
}
scope.removeLaunchJobModalReady = scope.$on('LaunchJobModalReady', function() {
credentialTypesLookup()
.then(kinds => {
if(scope.ask_credential_on_launch) {
scope.credentialKind = "" + kinds.ssh;
scope.includeCredentialList = true;
}
});
// Go get the list/survey data that we need from the server
if(scope.ask_inventory_on_launch) {
scope.includeInventoryList = true;
}
if(scope.survey_enabled) {
GetSurveyQuestions({
scope: scope,
id: scope.submitJobId,
submitJobType: scope.submitJobType
});
}
$('#job-launch-modal').dialog('open');
// select2-ify the job type dropdown
CreateSelect2({
element: '#job_launch_job_type',
multiple: false
});
CreateSelect2({
element: '#job_launch_verbosity',
multiple: false
});
CreateSelect2({
element: `#job-launch-credential-kind-select`,
multiple: false,
placeholder: i18n._('Select a credential')
});
CreateSelect2({
element: '#job_launch_job_tags',
multiple: true,
addNew: true
});
CreateSelect2({
element: '#job_launch_skip_tags',
multiple: true,
addNew: true
});
if(scope.step === 'otherprompts' && scope.ask_variables_on_launch) {
ParseTypeChange({
scope: scope,
variable: 'jobLaunchVariables',
field_id: 'job_launch_variables'
});
scope.extra_vars_code_mirror_loaded = true;
}
});
CreateDialog({
id: 'job-launch-modal',
scope: scope,
width: 800,
minWidth: 400,
draggable: false,
dialogClass: 'JobSubmission-dialog',
onOpen: function() {
Wait('stop');
},
callback: 'LaunchJobModalReady'
});
};
scope.clearDialog = function() {
// Destroy the dialog
if($("#job-launch-modal").hasClass('ui-dialog-content')) {
$('#job-launch-modal').dialog('destroy');
}
// Remove the directive from the page
$('#content-container').find('submit-job').remove();
// Clear out the scope (we'll create a new scope the next time
// job launch is called)
scope.$destroy();
};
// This function is used to hide/show the contents of a password
// within a form
scope.togglePassword = function(id) {
var buttonId = id + "_show_input_button",
inputId = id,
buttonInnerHTML = $(buttonId).html();
if (buttonInnerHTML.indexOf("Show") > -1) {
$(buttonId).html("Hide");
$(inputId).attr("type", "text");
} else {
$(buttonId).html("Show");
$(inputId).attr("type", "password");
}
};
$transitions.onStart({}, function() {
scope.$evalAsync(function( scope ) {
scope.clearDialog();
});
});
scope.init();
}
};
}];

View File

@ -1,375 +0,0 @@
<div id="job-launch-modal" class="JobSubmission modal">
<div class="JobSubmission-container">
<div class="JobSubmission-header">
<div class="JobSubmission-title">
<div class="JobSubmission-titleText">
<span translate>LAUNCH JOB</span>
<div class="JobSubmission-titleLockup"></div>
<span>{{job_template_data.name || workflow_job_template_data.name}}</span>
</div>
</div>
<div class="JobSubmission-close">
<button class="JobSubmission-exit" ng-click="clearDialog()"><i class="fa fa-times-circle"></i></button>
</div>
</div>
<div class="JobSubmission-stepsContainer">
<div class="JobSubmission-steps">
<div class="JobSubmission-step" ng-class="{'JobSubmission-step--active': step==='inventory'}" ng-if="ask_inventory_on_launch" ng-click="setStep('inventory')" translate>Inventory</div>
<div class="JobSubmission-step" ng-class="{'JobSubmission-step--active': step==='credential', 'JobSubmission-step--disabled': !credentialTabEnabled}" ng-if="ask_credential_on_launch || password_needed" ng-click="!credentialTabEnabled || setStep('credential')" translate>Credential</div>
<div class="JobSubmission-step" ng-class="{'JobSubmission-step--active': step==='otherprompts', 'JobSubmission-step--disabled': !otherPromptsTabEnabled}" ng-if="has_other_prompts" ng-click="!otherPromptsTabEnabled || setStep('otherprompts')" translate>Other Prompts</div>
<div class="JobSubmission-step" ng-class="{'JobSubmission-step--active': step==='survey', 'JobSubmission-step--disabled': !surveyTabEnabled}" ng-if="survey_enabled" ng-click="!surveyTabEnabled || setStep('survey')" translate>Survey</div>
</div>
</div>
<div class="JobSubmission-formContainer">
<div ng-if="ask_inventory_on_launch" ng-show="step === 'inventory'" class="JobSubmission-form">
<div class="JobSubmission-selectedItemContainer">
<div class="JobSubmission-selectedItem">
<div class="JobSubmission-selectedItemInfo" ng-hide="!selected_inventory">
<div class="JobSubmission-selectedItemLabel">
<span translate>SELECTED:</span>
</div>
<div class="JobSubmission-previewTags--outer">
<div class="JobSubmission-previewTags--inner">
<div class="JobSubmission-previewTagContainer">
<div class="JobSubmission-previewTag JobSubmission-previewTag--deletable">
<span>{{selected_inventory.name}}</span>
</div>
<div class="JobSubmission-previewTagContainerDelete" ng-click="deleteSelectedInventory()">
<i class="fa fa-times JobSubmission-previewTagContainerTagDelete"></i>
</div>
</div>
</div>
</div>
<div class="JobSubmission-previewTagRevert">
<a class="JobSubmission-revertLink" href="" ng-hide="selected_inventory.id === defaults.inventory.id" ng-click="revertToDefaultInventory()" translate>REVERT</a>
</div>
</div>
</div>
<job-sub-inv-list ng-if="includeInventoryList" selected-inventory="$parent.selected_inventory"></job-sub-inv-list>
</div>
</div>
<div ng-if="ask_credential_on_launch || password_needed" ng-show="step === 'credential'" class="JobSubmission-form">
<div class="JobSubmission-selectedItemContainer">
<div class="JobSubmission-selectedItem">
<div class="JobSubmission-selectedItemInfo" ng-hide="!selected_credentials.machine && !selected_credentials.vault && selected_credentials.extra.length === 0">
<div class="JobSubmission-selectedItemLabel">
<span translate>SELECTED:</span>
</div>
<div class="JobSubmission-previewTags--outer">
<div class="JobSubmission-previewTags--inner">
<div class="JobSubmission-previewTagContainer u-wordwrap" ng-show="selected_credentials.machine">
<div class="JobSubmission-previewTag" ng-class="{'JobSubmission-previewTag--deletable': ask_credential_on_launch}">
<span><span class="JobSubmission-previewTagLabel" ng-class="{'JobSubmission-previewTagLabel--deletable': ask_credential_on_launch}">
MACHINE</span>:&nbsp;{{selected_credentials.machine.name}}</span>
</div>
<div class="JobSubmission-previewTagContainerDelete" ng-click="deleteMachineCred()" ng-show="ask_credential_on_launch">
<i class="fa fa-times JobSubmission-previewTagContainerTagDelete"></i>
</div>
</div>
<div class="JobSubmission-previewTagContainer" ng-repeat="extraCredential in selected_credentials.extra">
<div class="JobSubmission-previewTag" ng-class="{'JobSubmission-previewTag--deletable': ask_credential_on_launch}">
<span><span class="JobSubmission-previewTagLabel" ng-class="{'JobSubmission-previewTagLabel--deletable': ask_credential_on_launch}">
{{credential_types[extraCredential.credential_type].name | uppercase}}</span>:&nbsp;{{extraCredential.name}}</span>
</div>
<div class="JobSubmission-previewTagContainerDelete" ng-click="deleteExtraCred($index)" ng-show="ask_credential_on_launch">
<i class="fa fa-times JobSubmission-previewTagContainerTagDelete"></i>
</div>
</div>
<div class="JobSubmission-previewTagContainer JobSubmission-previewTagContainer--vault" ng-show="selected_credentials.vault">
<div class="JobSubmission-previewTag JobSubmission-previewTag--vault" ng-class="{'JobSubmission-previewTag--deletable': ask_credential_on_launch}">
<span><span class="JobSubmission-previewTagLabel" ng-class="{'JobSubmission-previewTagLabel--deletable': ask_credential_on_launch}">
VAULT</span>:&nbsp;{{selected_credentials.vault.name}}</span>
</div>
</div>
</div>
</div>
<div class="JobSubmission-previewTagRevert" ng-if="ask_credential_on_launch">
<a class="JobSubmission-revertLink" href="" ng-show="showRevertCredentials()" ng-click="revertToDefaultCredentials()" translate>REVERT</a>
</div>
</div>
</div>
<div class="JobSubmission-credentialSubSection" ng-show="ask_credential_on_launch">
<span class="JobSubmission-label" translate>CREDENTIAL TYPE:</span>
<select id="job-launch-credential-kind-select" ng-model="credentialKind">
<option ng-repeat="option in credentialTypeOptions" value="{{option.value}}">{{option.name}}</option>
</select>
</div>
<job-sub-cred-list ng-if="includeCredentialList" selected-credentials="selected_credentials" credential-types="::credential_types" credential-kind="credentialKind"></job-sub-cred-list>
<div ng-show="ssh_password_required || ssh_key_unlock_required || become_password_required || vault_password_required">
<div class="JobSubmission-instructions" translate>Launching this job requires the passwords listed below. Enter and confirm each password before continuing.</div>
<form name="forms.credentialpasswords" autocomplete="off" novalidate>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ssh_password_required">
<label for="ssh_password" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Password</span>
</label>
<div class="input-group">
<span class="input-group-btn input-group-prepend">
<button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-password')" data-container="job-launch-modal" data-original-title="" title="" translate>Show</button>
</span>
<input id="job-submission-ssh-password" type="password" ng-model="passwords.ssh_password" name="ssh_password" class="password-field form-control input-sm Form-textInput" required>
</div>
<div class="error" ng-show="forms.credentialpasswords.ssh_password.$dirty && forms.credentialpasswords.ssh_password.$error.required" translate>Please enter a password.</div>
<div class="error api-error" ng-bind="ssh_password_api_error"></div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ssh_key_unlock_required">
<label for="ssh_key_unlock" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Private Key Passphrase</span>
</label>
<div class="input-group">
<span class="input-group-btn input-group-prepend">
<button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-key-unlock_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-key-unlock')" data-container="job-launch-modal" data-original-title="" title="" translate>Show</button>
</span>
<input id="job-submission-ssh-key-unlock" type="password" ng-model="passwords.ssh_key_unlock" name="ssh_key_unlock" class="password-field form-control input-sm Form-textInput" required>
</div>
<div class="error" ng-show="forms.credentialpasswords.ssh_key_unlock.$dirty && forms.credentialpasswords.ssh_key_unlock.$error.required" translate>Please enter a password.</div>
<div class="error api-error" ng-bind="ssh_key_unlock_api_error"></div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="become_password_required">
<label for="become_password" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Privilege Escalation Password</span>
</label>
<div class="input-group">
<span class="input-group-btn input-group-prepend">
<button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-become-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-become-password')" data-container="job-launch-modal" data-original-title="" title="" translate>Show</button>
</span>
<input id="job-submission-become-password" type="password" ng-model="passwords.become_password" name="become_password" class="password-field form-control input-sm Form-textInput" required>
</div>
<div class="error" ng-show="forms.credentialpasswords.become_password.$dirty && forms.credentialpasswords.become_password.$error.required" translate>Please enter a password.</div>
<div class="error api-error" ng-bind="become_password_api_error"></div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="vault_password_required">
<label for="vault_password" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Vault Password</span>
</label>
<div class="input-group">
<span class="input-group-btn input-group-prepend">
<button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-vault-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-vault-password')" data-container="job-launch-modal" data-original-title="" title="" translate>Show</button>
</span>
<input id="job-submission-vault-password" type="password" ng-model="passwords.vault_password" name="vault_password" class="password-field form-control input-sm Form-textInput" required>
</div>
<div class="error" ng-show="forms.credentialpasswords.vault_password.$dirty && forms.credentialpasswords.vault_password.$error.required" translate>Please enter a password.</div>
<div class="error api-error" ng-bind="vault_password_api_error"></div>
</div>
</form>
</div>
</div>
</div>
<div ng-if="has_other_prompts" ng-show="step === 'otherprompts'" class="JobSubmission-form">
<form name="forms.otherprompts" autocomplete="off" novalidate>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_variables_on_launch">
<label for="variables">
<span class="Form-inputLabel" translate>Extra Variables</span>
<a id="awp-variables" href="" aw-pop-over="<p>Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON.</p>JSON:<br /><blockquote>{<br />&quot;somevar&quot;: &quot;somevalue&quot;,<br />&quot;password&quot;: &quot;magic&quot;<br /> }</blockquote>YAML:<br /><blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>" data-placement="right" data-container="body" over-title="Extra Variables" class="help-link" data-original-title="" title="" tabindex="-1">
<i class="fa fa-question-circle"></i>
</a>
<div class="parse-selection" id="job_launch_variables_parse_type">
<input type="radio" ng-model="other_prompt_data.parseType" value="yaml" ng-change="updateParseType()">
<span class="parse-label" translate>YAML</span>
<input type="radio" ng-model="other_prompt_data.parseType" value="json" ng-change="updateParseType()">
<span class="parse-label" translate>JSON</span>
</div>
</label>
<div>
<textarea rows="6" ng-model="other_prompt_data.extra_vars" name="variables" class="form-control Form-textArea Form-textAreaLabel" id="job_launch_variables" aw-watch=""></textarea>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_verbosity_on_launch">
<label for="verbosity" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Verbosity</span>
</label>
<div>
<select
id="job_launch_verbosity"
ng-options="v.label for v in other_prompt_data.verbosity_options track by v.value"
ng-model="other_prompt_data.verbosity"
class="form-control Form-dropDown"
name="verbosity"
tabindex="-1"
aria-hidden="true"
required>
<option value="" class="" selected="selected">Choose a verbosity</option>
</select>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_job_type_on_launch">
<label for="job_type" class="Form-inputLabelContainer">
<span class="Form-requiredAsterisk">*</span>
<span class="Form-inputLabel" translate> Job Type</span>
</label>
<div>
<select
id="job_launch_job_type"
ng-options="v.label for v in other_prompt_data.job_type_options track by v.value"
ng-model="other_prompt_data.job_type"
class="form-control Form-dropDown"
name="job_type"
tabindex="-1"
aria-hidden="true"
required>
<option value="" class="" selected="selected">Choose a job type</option>
</select>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_limit_on_launch">
<label for="limit">
<span class="Form-inputLabel" translate>Limit</span>
</label>
<div>
<input type="text" ng-model="other_prompt_data.limit" name="limit" class="form-control Form-textInput">
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_tags_on_launch">
<label for="tags">
<span class="Form-inputLabel" translate>Job Tags</span>
</label>
<div>
<select
id="job_launch_job_tags"
ng-options="v.label for v in other_prompt_data.job_tags_options track by v.value"
ng-model="other_prompt_data.job_tags"
class="form-control Form-dropDown"
name="verbosity"
tabindex="-1"
aria-hidden="true"
multiple>
</select>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_skip_tags_on_launch">
<label for="skip_tags">
<span class="Form-inputLabel" translate>Skip Tags</span>
</label>
<div>
<!-- <textarea rows="5" ng-model="other_prompt_data.skip_tags" name="skip_tags" class="form-control Form-textArea Form-textInput"></textarea> -->
<select
id="job_launch_skip_tags"
ng-options="v.label for v in other_prompt_data.skip_tags_options track by v.value"
ng-model="other_prompt_data.skip_tags"
class="form-control Form-dropDown"
name="verbosity"
tabindex="-1"
aria-hidden="true"
multiple>
</select>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_diff_mode_on_launch">
<label for="diff_mode">
<span class="Form-inputLabel" translate>Diff Mode</span>
</label>
<div>
<div class="ScheduleToggle" ng-class="{'is-on': other_prompt_data.diff_mode}" aw-tool-tip="" data-placement="top" data-original-title="" title="">
<button type="button" ng-show="other_prompt_data.diff_mode" class="ScheduleToggle-switch is-on" ng-click="toggleForm('diff_mode')"
translate>ON</button>
<button type="button" ng-show="!other_prompt_data.diff_mode" class="ScheduleToggle-switch ng-hide" ng-click="toggleForm('diff_mode')" translate>OFF</button>
</div>
</div>
</div>
</form>
</div>
<div ng-if="survey_enabled" ng-show="step === 'survey'" class="JobSubmission-form">
<form name="forms.survey" autocomplete="off" novalidate>
<div ng-repeat="question in survey_questions" id="taker_'+question.index+'" class="form-group Form-formGroup Form-formGroup--singleColumn">
<label ng-attr-for="{{question.variable}}" class="Form-inputLabelContainer">
<span ng-show="question.required===true" class="Form-requiredAsterisk">*</span>
<span class="label-text Form-inputLabel"> {{question.question_name}}</span>
</label>
<div class="survey_taker_description" ng-if="question.question_description">
<i ng-bind-html="question.question_description"></i>
</div>
<div ng-if="question.type === 'text'">
<input type="text" id="survey_question_{{$index}}" ng-model="question.model" name="survey_question_{{$index}}" ng-minlength="question.minlength" ng-maxlength="question.maxlength" class="form-control Form-textInput" ng-required="question.required">
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$dirty && forms.survey.survey_question_{{$index}}.$error.required" translate>Please enter an answer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.minlength || forms.survey.survey_question_{{$index}}.$error.maxlength"><span translate>Please enter an answer between</span> {{question.minlength}} <span translate>to</span> {{question.maxlength}} <span translate>characters long.</span></div>
</div>
<div ng-if="question.type === 'textarea'">
<textarea id="survey_question_{{$index}}" name="survey_question_{{$index}}" ng-model="question.model" ng-minlength="question.minlength" ng-maxlength="question.maxlength" class="form-control final Form-textArea" ng-required="question.required" rows="3"></textarea>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$dirty && forms.survey.survey_question_{{$index}}.$error.required" translate>Please enter an answer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.minlength || forms.survey.survey_question_{{$index}}.$error.maxlength"><span translate>Please enter an answer between</span> {{question.minlength}} <span translate>to</span> {{question.maxlength}} <span translate>characters long.</span></div>
</div>
<div ng-if="question.type === 'password'">
<div class="input-group">
<span class="input-group-btn input-group-prepend">
<button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="survey_question_{{$index}}_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#survey_question_' + $index)" data-original-title="" data-container="job-launch-modal" title="" translate>Show</button>
</span>
<input id="survey_question_{{$index}}" ng-if="!question.default" type="password" ng-model="question.model" name="survey_question_{{$index}}" ng-required="question.required" ng-minlength="question.minlength" ng-maxlength="question.maxlength" class="form-control Form-textInput" autocomplete="false">
<input id="survey_question_{{$index}}" ng-if="question.default" type="password" ng-model="question.model" name="survey_question_{{$index}}" ng-required="question.required" aw-password-min="question.minlength" aw-password-max="question.maxlength" class="form-control Form-textInput" autocomplete="false">
</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$dirty && forms.survey.survey_question_{{$index}}.$error.required" translate>Please enter an answer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.awPasswordMin || forms.survey.survey_question_{{$index}}.$error.awPasswordMax || forms.survey.survey_question_{{$index}}.$error.minlength || forms.survey.survey_question_{{$index}}.$error.maxlength"><span translate>Please enter an answer between</span> {{question.minlength}} <span translate>to</span> {{question.maxlength}} <span translate>characters long.</span></div>
</div>
<div ng-if="question.type === 'integer'">
<input type="number" id="survey_question_{{$index}}" ng-model="question.model" class="form-control Form-textInput" name="survey_question_{{$index}}" ng-required="question.required" integer aw-min="question.minValue" aw-max="question.maxValue"/>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$dirty && forms.survey.survey_question_{{$index}}.$error.required" translate>Please enter an answer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.number || forms.survey.survey_question_{{$index}}.$error.integer" translate>Please enter an answer that is a valid integer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.awMin || forms.survey.survey_question_{{$index}}.$error.awMax"><span translate>Please enter an answer between</span> {{question.minValue}} <span>and</span> {{question.maxValue}}.</div>
</div>
<div ng-if="question.type === 'float'">
<input type="number" id="survey_question_{{$index}}" ng-model="question.model" class="form-control Form-textInput" name="survey_question_{{$index}}" ng-required="question.required" smart-float aw-min="question.minValue" aw-max="question.maxValue"/>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$dirty && forms.survey.survey_question_{{$index}}.$error.required" translate>Please enter an answer.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.number || forms.survey.survey_question_{{$index}}.$error.float" translate>Please enter an answer that is a decimal number.</div>
<div class="error survey_error" ng-show="forms.survey.survey_question_{{$index}}.$error.awMin || forms.survey.survey_question_{{$index}}.$error.awMax"><span translate>Please enter an answer between</span> {{question.minValue}} <span translate>and</span> {{question.maxValue}}.</div>
</div>
<div ng-if="question.type === 'multiplechoice'">
<div class="survey_taker_input">
<multiple-choice
multi-select="false"
question="question"
choices="question.choices"
ng-required="question.required"
ng-model="question.model">
</multiple-choice>
</div>
</div>
<div ng-if="question.type === 'multiselect'">
<multiple-choice
multi-select="true"
question="question"
choices="question.choices"
ng-required="question.required"
ng-model="question.model">
</multiple-choice>
</div>
</div>
</form>
</div>
</div>
<div class="JobSubmission-footerContainer">
<div class="JobSubmission-footerPreview">
<div class="JobSubmission-previewItem" ng-show="submitJobType === 'job_template'">
<div class="JobSubmission-previewItemTitle" translate>INVENTORY</div>
<div ng-show="selected_inventory" ng-bind="selected_inventory.name"></div>
<div class="JobSubmission-previewItemNone" ng-show="!selected_inventory" translate>None selected</div>
</div>
<div class="JobSubmission-previewItem" ng-show="submitJobType === 'job_template'">
<div class="JobSubmission-previewItemTitle" translate>CREDENTIAL</div>
<div>
<span ng-show="!selected_credentials.machine && !selected_credentials.vault && selected_credentials.extra.length === 0" class="JobSubmission-selectedItemNone" translate>None selected</span>
</div>
<div>
<span ng-show="selected_credentials.machine" class="JobSubmission-previewItemSubTitle"><span translate>Machine</span>:&nbsp;</span>
<span class="u-wordwrap" ng-show="selected_credentials.machine">{{selected_credentials.machine.name}}</span>
</div>
<div ng-repeat="extraCredential in selected_credentials.extra">
<span class="JobSubmission-previewItemSubTitle">{{credential_types[extraCredential.credential_type].name}}:&nbsp;</span>
<span class="u-wordwrap">{{extraCredential.name}}</span>
</div>
<div>
<span ng-show="selected_credentials.vault" class="JobSubmission-previewItemSubTitle"><span translate>Vault</span>:&nbsp;</span>
<span ng-show="selected_credentials.vault" class="u-wordwrap">{{selected_credentials.vault.name}}</span>
</div>
</div>
</div>
<div class="JobSubmission-footerButtons">
<button id="job-submission-close-button" class="btn btn-sm JobSubmission-defaultButton" ng-click="clearDialog()" translate>CANCEL</button>
<button id="job-submission-action-button" class="btn btn-sm JobSubmission-actionButton" ng-click="takeAction()" ng-disabled="actionButtonDisabled()">{{getActionButtonText()}}</button>
</div>
</div>
</div>
</div>

View File

@ -1,128 +0,0 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
[ '$scope', 'CredentialList', 'i18n', 'QuerySet', 'GetBasePath', 'credentialTypesLookup',
function($scope, CredentialList, i18n, qs, GetBasePath, credentialTypesLookup) {
let credentialKinds = {};
let updateExtraCredentialsList = function() {
$scope.credentials.forEach((row, i) => {
$scope.credentials[i].checked = 0;
$scope.selectedCredentials.extra.forEach((extraCredential, j) => {
if (row.id === $scope.selectedCredentials.extra[j].id) {
$scope.credentials[i].checked = 1;
}
});
});
};
let updateMachineCredentialList = function() {
$scope.credentials.forEach((row, i) => {
$scope.credentials[i].checked = 0;
if (row.id === $scope.selectedCredentials.machine.id) {
$scope.credentials[i].checked = 1;
}
});
};
let uncheckAllCredentials = function() {
$scope.credentials.forEach((row, i) => {
$scope.credentials[i].checked = 0;
});
};
let init = function() {
$scope.credential_dataset = [];
$scope.credentials = [];
$scope.listRendered = false;
let credList = _.cloneDeep(CredentialList);
credList.emptyListText = i18n._('No Credentials Matching This Type Have Been Created');
$scope.list = credList;
$scope.credential_default_params = {
order_by: 'name',
page_size: 5
};
$scope.credential_queryset = {
order_by: 'name',
page_size: 5
};
$scope.$watch('credentialKind', function(){
$scope.credential_queryset.page = 1;
$scope.credential_default_params.credential_type = $scope.credential_queryset.credential_type = parseInt($scope.credentialKind);
qs.search(GetBasePath('credentials'), $scope.credential_default_params)
.then(res => {
$scope.credential_dataset = res.data;
$scope.credentials = $scope.credential_dataset.results;
if(!$scope.listRendered) {
$scope.generateCredentialList();
$scope.listRendered = true;
}
});
});
$scope.$watchCollection('selectedCredentials.extra', () => {
if($scope.credentials && $scope.credentials.length > 0) {
if($scope.selectedCredentials.extra && $scope.selectedCredentials.extra.length > 0 && parseInt($scope.credentialKind) !== credentialKinds.ssh) {
updateExtraCredentialsList();
} else if (parseInt($scope.credentialKind) !== credentialKinds.ssh) {
uncheckAllCredentials();
}
}
});
$scope.$watch('selectedCredentials.machine', () => {
if($scope.credentials && $scope.credentials.length > 0) {
if($scope.selectedCredentials.machine && parseInt($scope.credentialKind) === credentialKinds.ssh) {
updateMachineCredentialList();
}
else {
uncheckAllCredentials();
}
}
});
$scope.$watchGroup(['credentials', 'selectedCredentials.machine'], () => {
if($scope.credentials && $scope.credentials.length > 0) {
if($scope.selectedCredentials.machine && parseInt($scope.credentialKind) === credentialKinds.ssh) {
updateMachineCredentialList();
}
else if($scope.selectedCredentials.extra && $scope.selectedCredentials.extra.length > 0 && parseInt($scope.credentialKind) !== credentialKinds.ssh) {
updateExtraCredentialsList();
}
else {
uncheckAllCredentials();
}
}
});
};
$scope.toggle_row = function(selectedRow) {
if(parseInt($scope.credentialKind) === credentialKinds.ssh) {
$scope.selectedCredentials.machine = _.cloneDeep(selectedRow);
}
else {
for (let i = $scope.selectedCredentials.extra.length - 1; i >= 0; i--) {
if(selectedRow.credential_type === $scope.selectedCredentials.extra[i].credential_type) {
$scope.selectedCredentials.extra.splice(i, 1);
}
}
$scope.selectedCredentials.extra.push(_.cloneDeep(selectedRow));
}
};
credentialTypesLookup()
.then(kinds => {
credentialKinds = kinds;
init();
});
}
];

View File

@ -1,31 +0,0 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import jobSubCredListController from './job-sub-cred-list.controller';
export default [ 'templateUrl', '$compile', 'generateList',
(templateUrl, $compile, GenerateList) => {
return {
scope: {
selectedCredentials: '=',
credentialKind: '=',
credentialTypes: '='
},
templateUrl: templateUrl('job-submission/lists/credential/job-sub-cred-list'),
controller: jobSubCredListController,
restrict: 'E',
link: scope => {
scope.generateCredentialList = function() {
let html = GenerateList.build({
list: scope.list,
input_type: 'radio',
mode: 'lookup'
});
$('#job-submission-credential-lookup').append($compile(html)(scope));
};
}
};
}];

View File

@ -1,3 +0,0 @@
<div>
<div id="job-submission-credential-lookup"></div>
</div>

View File

@ -1,63 +0,0 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
[ '$scope',
function($scope) {
let updateInventoryList = function() {
$scope.inventories.forEach((row, i) => {
$scope.inventories[i].checked = 0;
if (row.id === $scope.selectedInventory.id) {
$scope.inventories[i].checked = 1;
}
});
};
let uncheckAllInventories = function() {
$scope.inventories.forEach((row, i) => {
$scope.inventories[i].checked = 0;
});
};
let init = function() {
$scope.$watch('selectedInventory', () => {
if($scope.inventories && $scope.inventories.length > 0) {
if($scope.selectedInventory) {
updateInventoryList();
}
else {
uncheckAllInventories();
}
}
});
};
init();
$scope.toggle_row = function(selectedRow) {
let list = $scope.list;
let count = 0;
$scope[list.name].forEach(function(row) {
if (row.id === selectedRow.id) {
if (row.checked) {
row.success_class = 'success';
} else {
row.checked = 1;
row.success_class = '';
}
$scope.$emit('inventorySelected', row);
} else {
row.checked = 0;
row.success_class = '';
}
if (row.checked) {
count++;
}
});
};
}
];

View File

@ -1,66 +0,0 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import jobSubInvListController from './job-sub-inv-list.controller';
export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$compile', 'InventoryList',
(templateUrl, qs, GetBasePath, GenerateList, $compile, InventoryList) => {
return {
scope: {
selectedInventory: '='
},
templateUrl: templateUrl('job-submission/lists/inventory/job-sub-inv-list'),
controller: jobSubInvListController,
restrict: 'E',
link: scope => {
let toDestroy = [];
scope.inventory_default_params = {
order_by: 'name',
page_size: 5
};
scope.inventory_queryset = {
order_by: 'name',
page_size: 5
};
// Fire off the initial search
qs.search(GetBasePath('inventory'), scope.inventory_default_params)
.then(res => {
scope.inventory_dataset = res.data;
scope.inventories = scope.inventory_dataset.results;
let invList = _.cloneDeep(InventoryList);
let html = GenerateList.build({
list: invList,
input_type: 'radio',
mode: 'lookup'
});
scope.list = invList;
$('#job-submission-inventory-lookup').append($compile(html)(scope));
toDestroy.push(scope.$watchCollection('selectedInventory', () => {
if(scope.selectedInventory) {
// Loop across the inventories and see if one of them should be "checked"
scope.inventories.forEach((row, i) => {
if (row.id === scope.selectedInventory.id) {
scope.inventories[i].checked = 1;
}
else {
scope.inventories[i].checked = 0;
}
});
}
}));
});
scope.$on('$destroy', () => toDestroy.forEach(watcher => watcher()));
}
};
}];

View File

@ -1,3 +0,0 @@
<div>
<div id="job-submission-inventory-lookup"></div>
</div>

View File

@ -5,31 +5,23 @@
*************************************************/
import LaunchJob from './job-submission-factories/launchjob.factory';
import GetSurveyQuestions from './job-submission-factories/getsurveyquestions.factory';
import AdhocRun from './job-submission-factories/adhoc-run.factory.js';
import CheckPasswords from './job-submission-factories/check-passwords.factory';
import CreateLaunchDialog from './job-submission-factories/create-launch-dialog.factory';
import InventoryUpdate from './job-submission-factories/inventory-update.factory';
import ProjectUpdate from './job-submission-factories/project-update.factory';
import PromptForPasswords from './job-submission-factories/prompt-for-passwords.factory';
import submitJob from './job-submission.directive';
import credentialList from './lists/credential/job-sub-cred-list.directive';
import inventoryList from './lists/inventory/job-sub-inv-list.directive';
import awPasswordMin from './job-submission-directives/aw-password-min.directive';
import awPasswordMax from './job-submission-directives/aw-password-max.directive';
export default
angular.module('jobSubmission', [])
.factory('LaunchJob', LaunchJob)
.factory('GetSurveyQuestions', GetSurveyQuestions)
.factory('AdhocRun', AdhocRun)
.factory('CheckPasswords', CheckPasswords)
.factory('CreateLaunchDialog', CreateLaunchDialog)
.factory('InventoryUpdate', InventoryUpdate)
.factory('ProjectUpdate', ProjectUpdate)
.factory('PromptForPasswords', PromptForPasswords)
.directive('submitJob', submitJob)
.directive('jobSubCredList', credentialList)
.directive('jobSubInvList', inventoryList)
.directive('awPasswordMin', awPasswordMin)
.directive('awPasswordMax', awPasswordMax);

View File

@ -39,7 +39,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){
label: i18n._("Start"),
flag: 'notification_templates_started',
type: "toggle",
ngClick: "toggleNotification($event, notification.id, \"notification_templates_started\")",
ngClick: "toggleNotification($event, notification.id, 'notification_templates_started')",
ngDisabled: "!sufficientRoleForNotifToggle",
awToolTip: "{{ schedule.play_tip }}",
dataTipWatch: "schedule.play_tip",
@ -51,7 +51,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){
label: i18n._('Success'),
flag: 'notification_templates_success',
type: "toggle",
ngClick: "toggleNotification($event, notification.id, \"notification_templates_success\")",
ngClick: "toggleNotification($event, notification.id, 'notification_templates_success')",
ngDisabled: "!sufficientRoleForNotifToggle",
awToolTip: "{{ schedule.play_tip }}",
dataTipWatch: "schedule.play_tip",
@ -64,7 +64,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){
columnClass: 'd-none d-md-flex justify-content-start col-md-1 NotifierList-lastColumn',
flag: 'notification_templates_error',
type: "toggle",
ngClick: "toggleNotification($event, notification.id, \"notification_templates_error\")",
ngClick: "toggleNotification($event, notification.id, 'notification_templates_error')",
ngDisabled: "!sufficientRoleForNotifToggle",
awToolTip: "{{ schedule.play_tip }}",
dataTipWatch: "schedule.play_tip",

View File

@ -22,10 +22,7 @@
<div class="SurveyMaker-header">
<div class="SurveyMaker-title">
<div class="SurveyMaker-titleText">{{name || "New Job Template"}}<div class="SurveyMaker-titleLockup"></div><span translate>SURVEY</span></div>
<div class="ScheduleToggle" ng-class="{'is-on': survey_enabled}" aw-tool-tip="surveyEnabledTooltip" data-container="#survey-modal-dialog" data-placement="right" data-tip-watch="surveyEnabledTooltip">
<button type="button" ng-show="survey_enabled" class="ScheduleToggle-switch is-on" ng-click="toggleSurveyEnabled()" translate>ON</button>
<button type="button" ng-show="!survey_enabled" class="ScheduleToggle-switch" ng-click="toggleSurveyEnabled()" translate>OFF</button>
</div>
<at-switch on-toggle="toggleSurveyEnabled()" switch-on="survey_enabled" switch-disabled="(!job_template_obj.summary_fields.user_capabilities.edit && !workflow_job_template_obj.summary_fields.user_capabilities.edit && !canAdd)" tooltip="surveyEnabledTooltip" tooltip-placement="right" tooltip-watch="surveyEnabledTooltip"></at-switch>
</div>
<div class="SurveyMaker-close">
<button class="SurveyMaker-exit" ng-click="closeSurvey('survey-modal-dialog')"><i class="fa fa-times-circle"></i></button>

View File

@ -1,84 +0,0 @@
/** @define ScheduleToggle */
.Form-formGroup--disabled .ScheduleToggle {
cursor: not-allowed;
border-color: @default-link !important;
.ScheduleToggle-switch {
background-color: @d7grey !important;
cursor: not-allowed;
}
}
.ScheduleToggle {
border-radius: 5px;
border: 1px solid @default-link;
background-color: @default-link;
cursor: pointer;
display: flex;
justify-content: flex-end;
width: 42px;
&.ScheduleToggle--disabled {
cursor: not-allowed;
background-color: none;
border-color: @d7grey !important;
.ScheduleToggle-switch {
background-color: @d7grey !important;
cursor: not-allowed;
}
}
}
.ScheduleToggle-switch {
color: @default-link;
background-color: @default-bg;
border-left: 1px solid @default-link;
text-align: center;
text-transform: uppercase;
font-size: 11px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
padding: 0px 5px;
border-top: 0px;
border-bottom: 0px;
border-right: 0px;
height: 16px;
line-height: 16px;
}
.ScheduleToggle.is-on {
border-color: @default-link;
background-color: @default-bg;
justify-content: flex-start;
}
.ScheduleToggle-switch.is-on {
background-color: @default-link;
color: @default-bg;
border-left: 0;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
padding: 0 7px;
}
.ScheduleToggle-switch:hover {
background-color: @default-tertiary-bg;
}
.ScheduleToggle.is-on:hover {
border-color: @default-link-hov;
}
.ScheduleToggle-switch.is-on:hover {
background-color: @default-link-hov;
}
.ScheduleToggle-listTableCell {
display: flex;
align-items: center;
height: 100%;
justify-content: flex-end;
padding-right: 0px;
}

View File

@ -25,6 +25,7 @@
@f7grey: #F7F7F7;
@insights-yellow: #dedc4f;
@f2grey: #f2f2f2;
@d2grey: #d2d2d2;
@fcgrey: #fcfcfc;
@f6grey: #f6f6f6;
@ebgrey: #ebebeb;

View File

@ -512,21 +512,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
var html = '';
// extend these blocks to include elements similarly buildField()
if (field.type === 'toggle'){
html += "<div class=\"Field-header--" + key;
html += (field['class']) ? " " + field['class'] : "";
html += " " + field.columnClass;
html += "\"><div class='ScheduleToggle' ng-class='{\"is-on\": " + form.iterator + ".";
html += (field.flag) ? field.flag : "enabled";
html += (field.ngDisabled) ? ', "ScheduleToggle--disabled": ' + field.ngDisabled : '';
html += "\}' aw-tool-tip='" + field.awToolTip + "' data-placement='" + field.dataPlacement + "' data-tip-watch='" + field.dataTipWatch + "'><button type='button' ng-show='" + form.iterator + "." ;
html += (field.flag) ? field.flag : 'enabled';
html += "' ";
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
html += " class='ScheduleToggle-switch is-on' ng-click='" + field.ngClick + "' translate>" + i18n._("ON") + "</button><button type='button' ng-show='!" + form.iterator + "." ;
html += (field.flag) ? field.flag : "enabled";
html += "' ";
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
html += " class='ScheduleToggle-switch' ng-click='" + field.ngClick + "' translate>" + i18n._("OFF") + "</button></div></div>";
html += `
<div class="Field-header--${key} ${field['class']} ${field.columnClass}">
<at-switch on-toggle="${field.ngClick}" switch-on="${"flag" in field} ? ${form.iterator}.${field.flag} : ${form.iterator}.enabled" switch-disabled="${"ngDisabled" in field} ? ${field.ngDisabled} : false" tooltip-string="${field.awToolTip}" tooltip-placement="${field.dataPlacement ? field.dataPlacement : 'right'}"></at-switch>
</div>
`;
} else if (field.type === 'html') {
html += field.html;
}
@ -676,17 +666,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
}
if (field.type === 'toggle'){
html += "<td class=\"List-tableCell " + fld + "-column";
html += (field['class']) ? " " + field['class'] : "";
html += " " + field.columnClass;
html += "\"><div class='ScheduleToggle' ng-class='{\"is-on\": " + form.iterator + ".";
html += (field.flag) ? field.flag : "enabled";
html += (field.ngDisabled) ? ', "ScheduleToggle--disabled": ' + field.ngDisabled : '';
html += "\}' aw-tool-tip='" + field.awToolTip + "' data-placement='" + field.dataPlacement + "' data-tip-watch='" + field.dataTipWatch + "'><div ng-show='" + form.iterator + "." ;
html += (field.flag) ? field.flag : 'enabled';
html += "' class='ScheduleToggle-switch is-on' ng-click='" + field.ngClick + "' translate>ON</div><div ng-show='!" + form.iterator + "." ;
html += (field.flag) ? field.flag : "enabled";
html += "' class='ScheduleToggle-switch' ng-click='" + field.ngClick + "' translate>OFF</div></div></td>";
html += `
<td class="List-tableCell-${fld}-column ${field['class']} ${field.columnClass}">
<at-switch on-toggle="${field.ngClick}" switch-on="${"flag" in field} ? ${form.iterator}.${field.flag} : ${form.iterator}.enabled" switch-disabled="${"ngDisabled" in field} ? ${field.ngDisabled} : false" tooltip-string="${field.awToolTip}" tooltip-placement="${field.dataPlacement ? field.dataPlacement : 'right'}"></at-switch>
</td>
`;
}
if (field.type === 'alertblock') {
@ -741,14 +725,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += label(labelOptions);
html += `<div class="ScheduleToggle" ng-class="{'is-on': ${field.toggleSource}, 'ScheduleToggle--disabled': ${field.ngDisabled}}" aw-tool-tip="" `;
html += (field.ngShow) ? "ng-show=\"" + field.ngShow + "\" " : "";
html += `data-placement="top">`;
html += `<button type="button" ng-show="${field.toggleSource}" class="ScheduleToggle-switch is-on" ng-click="toggleForm('${field.toggleSource}')"
ng-disabled="${field.ngDisabled}" translate>${i18n._("ON")}</button>
<button type="button" ng-show="!${field.toggleSource}" class="ScheduleToggle-switch" ng-click="toggleForm('${field.toggleSource}')"
ng-disabled="${field.ngDisabled}" translate>${i18n._("OFF")}</button>
</div>`;
html += `
<div>
<at-switch on-toggle="toggleForm('${field.toggleSource}')" switch-on="${field.toggleSource}" switch-disabled="${"ngDisabled" in field} ? ${field.ngDisabled} : false" hide="!(${"ngShow" in field ? field.ngShow : true})"></at-switch>
</div>
`;
}
//text fields

View File

@ -527,21 +527,11 @@ angular.module('GeneratorHelpers', [systemStatus.name])
} else if (field.type === 'template') {
html = Template(field);
} else if (field.type === 'toggle') {
html += "<div class=\"ScheduleToggle-listTableCell " + fld + "-column";
html += (field['class']) ? " " + field['class'] : "";
html += field.columnClass ? " " + field.columnClass : "";
html += "\"><div class='ScheduleToggle' ng-class='{\"is-on\": " + list.iterator + ".";
html += (field.flag) ? field.flag : "enabled";
html += (field.ngDisabled) ? ', "ScheduleToggle--disabled": ' + field.ngDisabled : '';
html += "\}' aw-tool-tip='" + field.awToolTip + "' data-placement='" + field.dataPlacement + "' data-tip-watch='" + field.dataTipWatch + "'><button type='button'";
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
html += "ng-show='" + list.iterator + "." ;
html += (field.flag) ? field.flag : 'enabled';
html += "' class='ScheduleToggle-switch is-on' ng-click='" + field.ngClick + "'>" + i18n._("ON") + "</button><button type='button'";
html += (field.ngDisabled) ? `ng-disabled="${field.ngDisabled}" ` : "";
html += "ng-show='!" + list.iterator + "." ;
html += (field.flag) ? field.flag : "enabled";
html += "' class='ScheduleToggle-switch' ng-click='" + field.ngClick + "'>" + i18n._("OFF") + "</button></div></div>";
html += `
<div class="atSwitch-listTableCell ${field}-column ${field['class']} ${field.columnClass}">
<at-switch on-toggle="${field.ngClick}" switch-on="${"flag" in field} ? ${list.iterator}.${field.flag} : ${list.iterator}.enabled" switch-disabled="${"ngDisabled" in field} ? ${field.ngDisabled} : false" tooltip-string="${field.awToolTip}" tooltip-placement="${field.dataPlacement ? field.dataPlacement : 'right'}" tooltip-watch="${field.dataTipWatch}"></at-switch>
</div>
`;
} else if (field.type === 'invalid') {
html += `<div class='List-tableRow--invalid'><div class='List-tableRow--invalidBar' ng-show="${field.ngShow}"`;
html += `aw-tool-tip="${field.awToolTip}" data-placement=${field.dataPlacement}>`;

View File

@ -111,10 +111,7 @@
</a>
</label>
<div>
<div class="ScheduleToggle" ng-class="{'is-on': promptData.prompts.diffMode.value}" aw-tool-tip="" data-placement="top" data-original-title="" title="" ng-click="vm.toggleDiff()">
<button type="button" ng-show="promptData.prompts.diffMode.value" class="ScheduleToggle-switch is-on" ng-disabled="readOnlyPrompts">{{:: vm.strings.get('ON') }}</button>
<button type="button" ng-show="!promptData.prompts.diffMode.value" class="ScheduleToggle-switch ng-hide" ng-disabled="readOnlyPrompts">{{:: vm.strings.get('OFF') }}</button>
</div>
<at-switch on-toggle="vm.toggleDiff()" switch-on="promptData.prompts.diffMode.value" switch-disabled="readOnlyPrompts"></at-switch>
</div>
</div>
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="promptData.launchConf.ask_variables_on_launch && !promptData.prompts.variables.ignore">

View File

@ -18,7 +18,7 @@ const standardInvDetails = createFormSection({
'#inventory_form .Form-textArea',
'#inventory_form input[type="checkbox"]',
'#inventory_form .ui-spinner-input',
'#inventory_form .ScheduleToggle-switch'
'#inventory_form .atSwitch-inner'
]
},
labels: {

View File

@ -19,7 +19,7 @@ const details = createFormSection({
'#notification_template_form input[type="radio"]',
'#notification_template_form .ui-spinner-input',
'#notification_template_form .Form-textArea',
'#notification_template_form .ScheduleToggle-switch',
'#notification_template_form .atSwitch-inner',
'#notification_template_form .Form-lookupButton'
]
}

View File

@ -18,7 +18,7 @@ const jtDetails = createFormSection({
'#job_template_form .Form-textArea',
'#job_template_form input[type="checkbox"]',
'#job_template_form .ui-spinner-input',
'#job_template_form .ScheduleToggle-switch'
'#job_template_form .atSwitch-inner'
]
},
labels: {
@ -38,7 +38,7 @@ const wfjtDetails = createFormSection({
'#workflow_job_template_form .Form-textArea',
'#workflow_job_template_form input[type="checkbox"]',
'#workflow_job_template_form .ui-spinner-input',
'#workflow_job_template_form .ScheduleToggle-switch'
'#workflow_job_template_form .atSwitch-inner'
]
},
labels: {