mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Merge pull request #6609 from jlmitch5/newCredOnJT
multi-credential ui selector on job template page
This commit is contained in:
@@ -264,7 +264,7 @@ export default
|
|||||||
$scope.credentialTypeOptions = [];
|
$scope.credentialTypeOptions = [];
|
||||||
credentialTypeData.results.forEach((credentialType => {
|
credentialTypeData.results.forEach((credentialType => {
|
||||||
credential_types[credentialType.id] = credentialType;
|
credential_types[credentialType.id] = credentialType;
|
||||||
if(credentialType.kind.match(/^(machine|cloud|network|ssh)$/)) {
|
if(credentialType.kind.match(/^(machine|cloud|net|ssh)$/)) {
|
||||||
$scope.credentialTypeOptions.push({
|
$scope.credentialTypeOptions.push({
|
||||||
name: credentialType.name,
|
name: credentialType.name,
|
||||||
value: credentialType.id
|
value: credentialType.id
|
||||||
|
|||||||
@@ -1423,9 +1423,21 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
html += "</div>\n";
|
html += "</div>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
//custom fields
|
|
||||||
if (field.type === 'custom') {
|
if (field.type === 'custom') {
|
||||||
html += label();
|
let labelOptions = {};
|
||||||
|
|
||||||
|
if (field.subCheckbox) {
|
||||||
|
labelOptions.checkbox = {
|
||||||
|
id: `${this.form.name}_${fld}_ask_chbox`,
|
||||||
|
ngShow: field.subCheckbox.ngShow,
|
||||||
|
ngChange: field.subCheckbox.ngChange,
|
||||||
|
ngModel: field.subCheckbox.variable,
|
||||||
|
ngDisabled: field.ngDisabled,
|
||||||
|
text: field.subCheckbox.text || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
html += label(labelOptions);
|
||||||
html += "<div ";
|
html += "<div ";
|
||||||
html += (horizontal) ? "class=\"" + getFieldWidth() + "\"" : "";
|
html += (horizontal) ? "class=\"" + getFieldWidth() + "\"" : "";
|
||||||
html += ">\n";
|
html += ">\n";
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
$scope.playbook_options = [];
|
$scope.playbook_options = [];
|
||||||
$scope.mode = "add";
|
$scope.mode = "add";
|
||||||
$scope.parseType = 'yaml';
|
$scope.parseType = 'yaml';
|
||||||
|
$scope.credentialNotPresent = false;
|
||||||
|
|
||||||
md5Setup({
|
md5Setup({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
@@ -261,6 +262,29 @@
|
|||||||
null, true);
|
null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.selectedCredentials.extra.map(cred => cred.id)
|
||||||
|
.forEach(function(cred_id) {
|
||||||
|
|
||||||
|
Rest.setUrl(data.related.extra_credentials);
|
||||||
|
Rest.post({'id': cred_id})
|
||||||
|
.success(function () {
|
||||||
|
})
|
||||||
|
.error(function (data,
|
||||||
|
status) {
|
||||||
|
ProcessErrors(
|
||||||
|
$scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
form,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to add extra credential. Post returned ' +
|
||||||
|
'status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var orgDefer = $q.defer();
|
var orgDefer = $q.defer();
|
||||||
var associationDefer = $q.defer();
|
var associationDefer = $q.defer();
|
||||||
@@ -399,6 +423,14 @@
|
|||||||
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
||||||
data.ask_variables_on_launch = $scope.ask_variables_on_launch ? $scope.ask_variables_on_launch : false;
|
data.ask_variables_on_launch = $scope.ask_variables_on_launch ? $scope.ask_variables_on_launch : false;
|
||||||
data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false;
|
data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false;
|
||||||
|
if ($scope.selectedCredentials && $scope.selectedCredentials
|
||||||
|
.machine && $scope.selectedCredentials
|
||||||
|
.machine) {
|
||||||
|
data.credential = $scope.selectedCredentials
|
||||||
|
.machine.id;
|
||||||
|
} else {
|
||||||
|
data.credential = null;
|
||||||
|
}
|
||||||
|
|
||||||
data.extra_vars = ToJSON($scope.parseType,
|
data.extra_vars = ToJSON($scope.parseType,
|
||||||
$scope.variables, true);
|
$scope.variables, true);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default
|
|||||||
$scope.parseType = 'yaml';
|
$scope.parseType = 'yaml';
|
||||||
$scope.showJobType = false;
|
$scope.showJobType = false;
|
||||||
$scope.instance_groups = InstanceGroupsData;
|
$scope.instance_groups = InstanceGroupsData;
|
||||||
|
$scope.credentialNotPresent = false;
|
||||||
|
|
||||||
SurveyControllerInit({
|
SurveyControllerInit({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
@@ -200,8 +201,6 @@ export default
|
|||||||
// watch for changes to 'verbosity', ensure we keep our select2 in sync when it changes.
|
// watch for changes to 'verbosity', ensure we keep our select2 in sync when it changes.
|
||||||
$scope.$watch('verbosity', sync_verbosity_select2);
|
$scope.$watch('verbosity', sync_verbosity_select2);
|
||||||
|
|
||||||
|
|
||||||
// Turn off 'Wait' after both cloud credential and playbook list come back
|
|
||||||
if ($scope.removeJobTemplateLoadFinished) {
|
if ($scope.removeJobTemplateLoadFinished) {
|
||||||
$scope.removeJobTemplateLoadFinished();
|
$scope.removeJobTemplateLoadFinished();
|
||||||
}
|
}
|
||||||
@@ -218,19 +217,11 @@ export default
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($scope.cloudCredentialReadyRemove) {
|
|
||||||
$scope.cloudCredentialReadyRemove();
|
|
||||||
}
|
|
||||||
$scope.cloudCredentialReadyRemove = $scope.$on('cloudCredentialReady', function () {
|
|
||||||
$scope.$emit('jobTemplateLoadFinished');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Retrieve each related set and populate the playbook list
|
// Retrieve each related set and populate the playbook list
|
||||||
if ($scope.jobTemplateLoadedRemove) {
|
if ($scope.jobTemplateLoadedRemove) {
|
||||||
$scope.jobTemplateLoadedRemove();
|
$scope.jobTemplateLoadedRemove();
|
||||||
}
|
}
|
||||||
$scope.jobTemplateLoadedRemove = $scope.$on('jobTemplateLoaded', function (e, related_cloud_credential, masterObject) {
|
$scope.jobTemplateLoadedRemove = $scope.$on('jobTemplateLoaded', function (e, masterObject) {
|
||||||
var dft;
|
var dft;
|
||||||
|
|
||||||
master = masterObject;
|
master = masterObject;
|
||||||
@@ -247,13 +238,7 @@ export default
|
|||||||
|
|
||||||
ParseTypeChange({ scope: $scope, field_id: 'job_template_variables', onChange: callback });
|
ParseTypeChange({ scope: $scope, field_id: 'job_template_variables', onChange: callback });
|
||||||
|
|
||||||
if($scope.job_template_obj.summary_fields.cloud_credential && related_cloud_credential) {
|
$scope.$emit('jobTemplateLoadFinished');
|
||||||
$scope.$emit('cloudCredentialReady', $scope.job_template_obj.summary_fields.cloud_credential.name);
|
|
||||||
} else {
|
|
||||||
// No existing cloud credential
|
|
||||||
$scope.$emit('cloudCredentialReady', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Wait('start');
|
Wait('start');
|
||||||
@@ -411,6 +396,73 @@ export default
|
|||||||
null, true);
|
null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let extraCredUrl = data.related.extra_credentials;
|
||||||
|
|
||||||
|
Rest.setUrl(extraCredUrl);
|
||||||
|
Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
let existingCreds = data.results
|
||||||
|
.map(cred => cred.id);
|
||||||
|
|
||||||
|
let newCreds = $scope.selectedCredentials.extra
|
||||||
|
.map(cred => cred.id);
|
||||||
|
|
||||||
|
let toAdd, toRemove;
|
||||||
|
|
||||||
|
[toAdd, toRemove] = _.partition(_.xor(existingCreds, newCreds), cred => (newCreds.indexOf(cred) > -1));
|
||||||
|
|
||||||
|
let destroyResolve = [];
|
||||||
|
|
||||||
|
toRemove.forEach((cred_id) => {
|
||||||
|
Rest.setUrl(extraCredUrl);
|
||||||
|
destroyResolve.push(
|
||||||
|
Rest.post({'id': cred_id, 'disassociate': true})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors(
|
||||||
|
$scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
form,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to remove extra credential. Post returned ' +
|
||||||
|
'status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
$q.all(destroyResolve)
|
||||||
|
.then(() => {
|
||||||
|
toAdd.forEach((cred_id) => {
|
||||||
|
Rest.setUrl(extraCredUrl);
|
||||||
|
Rest.post({'id': cred_id})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors(
|
||||||
|
$scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
form,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to add extra credential. Post returned ' +
|
||||||
|
'status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors($scope, data, status, form, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get existing extra credentials. GET returned ' +
|
||||||
|
'status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups)
|
InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||||
.catch(({data, status}) => {
|
.catch(({data, status}) => {
|
||||||
ProcessErrors($scope, data, status, form, {
|
ProcessErrors($scope, data, status, form, {
|
||||||
@@ -546,6 +598,14 @@ export default
|
|||||||
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
||||||
data.ask_variables_on_launch = $scope.ask_variables_on_launch ? $scope.ask_variables_on_launch : false;
|
data.ask_variables_on_launch = $scope.ask_variables_on_launch ? $scope.ask_variables_on_launch : false;
|
||||||
data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false;
|
data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false;
|
||||||
|
if ($scope.selectedCredentials && $scope.selectedCredentials
|
||||||
|
.machine && $scope.selectedCredentials
|
||||||
|
.machine) {
|
||||||
|
data.credential = $scope.selectedCredentials
|
||||||
|
.machine.id;
|
||||||
|
} else {
|
||||||
|
data.credential = null;
|
||||||
|
}
|
||||||
|
|
||||||
data.extra_vars = ToJSON($scope.parseType,
|
data.extra_vars = ToJSON($scope.parseType,
|
||||||
$scope.variables, true);
|
$scope.variables, true);
|
||||||
@@ -585,8 +645,8 @@ export default
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
Alert("Error", "Error parsing extra variables. " +
|
Alert("Error", "Error saving job template. " +
|
||||||
"Parser returned: " + err);
|
"Error: " + err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default
|
export default
|
||||||
function CallbackHelpInit($location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors,
|
function CallbackHelpInit($q, $location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors,
|
||||||
ParseVariableString, Empty, CredentialList, Wait) {
|
ParseVariableString, Empty, Wait) {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
defaultUrl = GetBasePath('job_templates'),
|
defaultUrl = GetBasePath('job_templates'),
|
||||||
@@ -13,8 +13,6 @@ export default
|
|||||||
// checkSCMStatus, getPlaybooks, callback,
|
// checkSCMStatus, getPlaybooks, callback,
|
||||||
// choicesCount = 0;
|
// choicesCount = 0;
|
||||||
|
|
||||||
CredentialList = _.cloneDeep(CredentialList);
|
|
||||||
|
|
||||||
// The form uses awPopOverWatch directive to 'watch' scope.callback_help for changes. Each time the
|
// The form uses awPopOverWatch directive to 'watch' scope.callback_help for changes. Each time the
|
||||||
// popover is activated, a function checks the value of scope.callback_help before constructing the content.
|
// popover is activated, a function checks the value of scope.callback_help before constructing the content.
|
||||||
scope.setCallbackHelp = function() {
|
scope.setCallbackHelp = function() {
|
||||||
@@ -136,7 +134,114 @@ export default
|
|||||||
|
|
||||||
scope.can_edit = data.summary_fields.user_capabilities.edit;
|
scope.can_edit = data.summary_fields.user_capabilities.edit;
|
||||||
|
|
||||||
scope.$emit('jobTemplateLoaded', data.related.cloud_credential, master);
|
scope.selectedCredentials = {
|
||||||
|
machine: null,
|
||||||
|
extra: []
|
||||||
|
};
|
||||||
|
|
||||||
|
var credDefers = [];
|
||||||
|
|
||||||
|
if (data.related.credential) {
|
||||||
|
Rest.setUrl(data.related.credential);
|
||||||
|
credDefers.push(Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
scope.selectedCredentials.machine = data;
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors(
|
||||||
|
scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get machine credential. ' +
|
||||||
|
'Get returned status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.related.extra_credentials) {
|
||||||
|
Rest.setUrl(data.related.extra_credentials);
|
||||||
|
credDefers.push(Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
scope.selectedCredentials.extra = data.results;
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors(
|
||||||
|
scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get extra credentials. ' +
|
||||||
|
'Get returned status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Rest.setUrl(GetBasePath('credential_types'));
|
||||||
|
credDefers.push(Rest.get()
|
||||||
|
.then(({data}) => {
|
||||||
|
scope.credentialTypeOptions = [];
|
||||||
|
data.results.forEach((credentialType => {
|
||||||
|
if(credentialType.kind.match(/^(machine|cloud|network|ssh)$/)) {
|
||||||
|
scope.credentialTypeOptions.push({
|
||||||
|
name: credentialType.name,
|
||||||
|
value: credentialType.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch(({data, status}) => {
|
||||||
|
ProcessErrors(
|
||||||
|
scope,
|
||||||
|
data,
|
||||||
|
status,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get credential types. Get returned ' +
|
||||||
|
'status: ' +
|
||||||
|
status
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
$q.all(credDefers)
|
||||||
|
.then(() => {
|
||||||
|
let machineCred = [];
|
||||||
|
let extraCreds = [];
|
||||||
|
|
||||||
|
if (scope.selectedCredentials && scope.selectedCredentials.machine) {
|
||||||
|
let mach = scope.selectedCredentials.machine;
|
||||||
|
mach.postType = "machine";
|
||||||
|
machineCred = [scope.selectedCredentials.machine];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.selectedCredentials && scope.selectedCredentials.extra) {
|
||||||
|
extraCreds = scope.selectedCredentials.extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
extraCreds = extraCreds.map(function(cred) {
|
||||||
|
cred.postType = "extra";
|
||||||
|
|
||||||
|
return cred;
|
||||||
|
});
|
||||||
|
|
||||||
|
let credTags = machineCred.concat(extraCreds);
|
||||||
|
|
||||||
|
scope.credentialsToPost = credTags.map(cred => ({
|
||||||
|
name: cred.name,
|
||||||
|
id: cred.id,
|
||||||
|
postType: cred.postType,
|
||||||
|
kind: scope.credentialTypeOptions
|
||||||
|
.filter(type => parseInt(cred.credential_type) === type.value)[0].name + ":"
|
||||||
|
}));
|
||||||
|
scope.$emit('jobTemplateLoaded', master);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
ProcessErrors(scope, data, status, form, {
|
ProcessErrors(scope, data, status, form, {
|
||||||
@@ -149,7 +254,7 @@ export default
|
|||||||
}
|
}
|
||||||
|
|
||||||
CallbackHelpInit.$inject =
|
CallbackHelpInit.$inject =
|
||||||
[ '$location', 'GetBasePath', 'Rest', 'JobTemplateForm', 'GenerateForm',
|
[ '$q', '$location', 'GetBasePath', 'Rest', 'JobTemplateForm', 'GenerateForm',
|
||||||
'$stateParams', 'ProcessErrors', 'ParseVariableString',
|
'$stateParams', 'ProcessErrors', 'ParseVariableString',
|
||||||
'Empty', 'CredentialList', 'Wait'
|
'Empty', 'Wait'
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -127,68 +127,26 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
|||||||
includePlaybookNotFoundError: true
|
includePlaybookNotFoundError: true
|
||||||
},
|
},
|
||||||
credential: {
|
credential: {
|
||||||
label: i18n._('Machine Credential'),
|
label: i18n._('Credentials'),
|
||||||
type: 'lookup',
|
type: 'custom',
|
||||||
list: 'CredentialList',
|
control: `
|
||||||
basePath: 'credentials',
|
<multi-credential
|
||||||
autopopulateLookup: false,
|
credentials="credentials"
|
||||||
search: {
|
prompt="ask_credential_on_launch"
|
||||||
kind: 'ssh'
|
selected-credentials="selectedCredentials"
|
||||||
},
|
credential-not-present="credentialNotPresent"
|
||||||
sourceModel: 'credential',
|
credentials-to-post="credentialsToPost">
|
||||||
sourceField: 'name',
|
</multi-credential>`,
|
||||||
awRequiredWhen: {
|
required: true,
|
||||||
reqExpression: '!ask_credential_on_launch',
|
awPopOver: "<p>" + i18n._("Select credentials that allow Tower to access the nodes this job will be ran against. You can only select one credential of each type.<br /><br />You must select either a machine (SSH) credential or \"Prompt on launch\". \"Prompt on launch\" requires you to select a machine credential at run time.<br /><br />If you select credentials AND check the \"Prompt on launch\" box, you make the selected credentials the defaults that can be updated at run time.") + "</p>",
|
||||||
alwaysShowAsterisk: true
|
dataTitle: i18n._('Credentials'),
|
||||||
},
|
|
||||||
requiredErrorMsg: i18n._("Please select a Machine Credential or check the Prompt on launch option."),
|
|
||||||
column: 1,
|
|
||||||
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',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
subCheckbox: {
|
subCheckbox: {
|
||||||
variable: 'ask_credential_on_launch',
|
variable: 'ask_credential_on_launch',
|
||||||
text: i18n._('Prompt on launch'),
|
text: i18n._('Prompt on launch'),
|
||||||
ngChange: 'job_template_form.credential_name.$validate()',
|
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
||||||
},
|
}
|
||||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
|
||||||
},
|
|
||||||
cloud_credential: {
|
|
||||||
label: i18n._('Cloud Credential'),
|
|
||||||
type: 'lookup',
|
|
||||||
list: 'CredentialList',
|
|
||||||
basePath: 'credentials',
|
|
||||||
search: {
|
|
||||||
cloud: 'true'
|
|
||||||
},
|
|
||||||
sourceModel: 'cloud_credential',
|
|
||||||
sourceField: 'name',
|
|
||||||
column: 1,
|
|
||||||
awPopOver:"<p>" + i18n._("Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
|
|
||||||
"running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.") + "</p>",
|
|
||||||
dataTitle: i18n._('Cloud Credential'),
|
|
||||||
dataPlacement: 'right',
|
|
||||||
dataContainer: "body",
|
|
||||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
|
||||||
},
|
|
||||||
network_credential: {
|
|
||||||
label: i18n._('Network Credential'),
|
|
||||||
type: 'lookup',
|
|
||||||
list: 'CredentialList',
|
|
||||||
basePath: 'credentials',
|
|
||||||
search: {
|
|
||||||
kind: 'net'
|
|
||||||
},
|
|
||||||
sourceModel: 'network_credential',
|
|
||||||
sourceField: 'name',
|
|
||||||
column: 1,
|
|
||||||
awPopOver: "<p>" + i18n._("Network credentials are used by Ansible networking modules to connect to and manage networking devices.") + "</p>",
|
|
||||||
dataTitle: i18n._('Network Credential'),
|
|
||||||
dataPlacement: 'right',
|
|
||||||
dataContainer: "body",
|
|
||||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
|
||||||
},
|
},
|
||||||
forks: {
|
forks: {
|
||||||
label: i18n._('Forks'),
|
label: i18n._('Forks'),
|
||||||
@@ -417,7 +375,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
|||||||
},
|
},
|
||||||
save: {
|
save: {
|
||||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||||
ngDisabled: "job_template_form.$invalid",//true //Disable when $pristine or $invalid, optional and when can_edit = false, for permission reasons
|
ngDisabled: "job_template_form.$invalid || credentialNotPresent",//true //Disable when $pristine or $invalid, optional and when can_edit = false, for permission reasons
|
||||||
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
ngShow: '(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
13
awx/ui/client/src/templates/job_templates/main.js
Normal file
13
awx/ui/client/src/templates/job_templates/main.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import jobTemplateAdd from './add-job-template/main';
|
||||||
|
import jobTemplateEdit from './edit-job-template/main';
|
||||||
|
import multiCredential from './multi-credential/main';
|
||||||
|
import md5Setup from './factories/md-5-setup.factory';
|
||||||
|
import CallbackHelpInit from './factories/callback-help-init.factory';
|
||||||
|
import JobTemplateForm from './job-template.form';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('jobTemplates', [jobTemplateAdd.name, jobTemplateEdit.name,
|
||||||
|
multiCredential.name])
|
||||||
|
.factory('md5Setup', md5Setup)
|
||||||
|
.factory('CallbackHelpInit', CallbackHelpInit)
|
||||||
|
.factory('JobTemplateForm', JobTemplateForm);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import multiCredential from './multi-credential.directive';
|
||||||
|
import multiCredentialModal from './multi-credential-modal.directive';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('multiCredential', [])
|
||||||
|
.directive('multiCredential', multiCredential)
|
||||||
|
.directive('multiCredentialModal', multiCredentialModal);
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile',
|
||||||
|
function(templateUrl, Rest, GetBasePath, GenerateList, $compile) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
credentialsToPost: '=',
|
||||||
|
credentials: '=',
|
||||||
|
selectedCredentials: '='
|
||||||
|
},
|
||||||
|
templateUrl: templateUrl('templates/job_templates/multi-credential/multi-credential-modal'),
|
||||||
|
|
||||||
|
link: function(scope, element) {
|
||||||
|
scope.credentialKind = "1";
|
||||||
|
|
||||||
|
$('#multi-credential-modal').on('hidden.bs.modal', function () {
|
||||||
|
$('#multi-credential-modal').off('hidden.bs.modal');
|
||||||
|
$(element).remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.showModal = function() {
|
||||||
|
$('#multi-credential-modal').modal('show');
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.destroyModal = function() {
|
||||||
|
scope.credentialKind = 1;
|
||||||
|
$('#multi-credential-modal').modal('hide');
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.generateCredentialList = function() {
|
||||||
|
let html = GenerateList.build({
|
||||||
|
list: scope.list,
|
||||||
|
input_type: 'radio',
|
||||||
|
mode: 'lookup'
|
||||||
|
});
|
||||||
|
$('#multi-credential-modal-body')
|
||||||
|
.append($compile(html)(scope));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Go out and get the credential types
|
||||||
|
Rest.setUrl(GetBasePath('credential_types'));
|
||||||
|
Rest.get()
|
||||||
|
.success(function (credentialTypeData) {
|
||||||
|
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;
|
||||||
|
scope.$emit('multiCredentialModalLinked');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: ['$scope', 'CredentialList', 'i18n', 'QuerySet',
|
||||||
|
'GetBasePath', function($scope, CredentialList, i18n, qs,
|
||||||
|
GetBasePath) {
|
||||||
|
|
||||||
|
let updateCredentialTags = function() {
|
||||||
|
let machineCred = [];
|
||||||
|
let extraCreds = [];
|
||||||
|
|
||||||
|
if ($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.machine) {
|
||||||
|
let mach = $scope.selectedCredentials.machine;
|
||||||
|
mach.postType = "machine";
|
||||||
|
machineCred = [$scope.selectedCredentials.machine];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.extra) {
|
||||||
|
extraCreds = $scope.selectedCredentials.extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
extraCreds = extraCreds.map(function(cred) {
|
||||||
|
cred.postType = "extra";
|
||||||
|
|
||||||
|
return cred;
|
||||||
|
});
|
||||||
|
|
||||||
|
let credTags = machineCred.concat(extraCreds);
|
||||||
|
|
||||||
|
$scope.credTags = credTags.map(cred => ({
|
||||||
|
name: cred.name,
|
||||||
|
id: cred.id,
|
||||||
|
postType: cred.postType,
|
||||||
|
kind: $scope.credentialTypeOptions
|
||||||
|
.filter(type => {
|
||||||
|
return parseInt(cred.credential_type) === type.value;
|
||||||
|
})[0].name + ":"
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateExtraCredentialsList = function() {
|
||||||
|
let extraCredIds = $scope.selectedCredentials.extra
|
||||||
|
.map(cred => cred.id);
|
||||||
|
$scope.credentials.forEach(cred => {
|
||||||
|
if (cred.credential_type !== 1) {
|
||||||
|
cred.checked = (extraCredIds
|
||||||
|
.indexOf(cred.id) > - 1) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateCredentialTags();
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateMachineCredentialList = function() {
|
||||||
|
$scope.credentials.forEach(cred => {
|
||||||
|
if (cred.credential_type === 1) {
|
||||||
|
cred.checked = ($scope.selectedCredentials
|
||||||
|
.machine !== null &&
|
||||||
|
cred.id === $scope.selectedCredentials
|
||||||
|
.machine.id) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateCredentialTags();
|
||||||
|
};
|
||||||
|
|
||||||
|
let uncheckAllCredentials = function() {
|
||||||
|
$scope.credentials.forEach(cred => {
|
||||||
|
cred.checked = 0;
|
||||||
|
});
|
||||||
|
updateCredentialTags();
|
||||||
|
};
|
||||||
|
|
||||||
|
let init = function() {
|
||||||
|
$scope.originalSelectedCredentials = _.cloneDeep($scope
|
||||||
|
.selectedCredentials);
|
||||||
|
$scope.credential_dataset = [];
|
||||||
|
$scope.credentials = $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_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.showModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$watchCollection('selectedCredentials.extra', () => {
|
||||||
|
if($scope.credentials && $scope.credentials.length > 0) {
|
||||||
|
if($scope.selectedCredentials.extra &&
|
||||||
|
$scope.selectedCredentials.extra.length > 0 &&
|
||||||
|
parseInt($scope.credentialKind) !== 1) {
|
||||||
|
updateExtraCredentialsList();
|
||||||
|
} else {
|
||||||
|
uncheckAllCredentials();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$watch('selectedCredentials.machine', () => {
|
||||||
|
if($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.machine &&
|
||||||
|
parseInt($scope.credentialKind) === 1) {
|
||||||
|
updateMachineCredentialList();
|
||||||
|
} else {
|
||||||
|
uncheckAllCredentials();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$watchGroup(['credentials',
|
||||||
|
'selectedCredentials.machine'], () => {
|
||||||
|
if($scope.credentials &&
|
||||||
|
$scope.credentials.length > 0) {
|
||||||
|
if($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.machine &&
|
||||||
|
parseInt($scope.credentialKind) === 1) {
|
||||||
|
updateMachineCredentialList();
|
||||||
|
} else if($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.extra &&
|
||||||
|
$scope.selectedCredentials.extra.length > 0 &&
|
||||||
|
parseInt($scope.credentialKind) !== 1) {
|
||||||
|
updateExtraCredentialsList();
|
||||||
|
} else {
|
||||||
|
uncheckAllCredentials();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$on('multiCredentialModalLinked', function() {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.toggle_row = function(selectedRow) {
|
||||||
|
if(parseInt($scope.credentialKind) === 1) {
|
||||||
|
if($scope.selectedCredentials &&
|
||||||
|
$scope.selectedCredentials.machine &&
|
||||||
|
$scope.selectedCredentials.machine.id === selectedRow.id) {
|
||||||
|
$scope.selectedCredentials.machine = null;
|
||||||
|
} else {
|
||||||
|
$scope.selectedCredentials.machine = _.cloneDeep(selectedRow);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let rowDeselected = false;
|
||||||
|
for (let i = $scope.selectedCredentials.extra.length - 1; i >= 0; i--) {
|
||||||
|
if($scope.selectedCredentials.extra[i].id === selectedRow
|
||||||
|
.id) {
|
||||||
|
$scope.selectedCredentials.extra.splice(i, 1);
|
||||||
|
rowDeselected = true;
|
||||||
|
} else if(selectedRow.credential_type === $scope
|
||||||
|
.selectedCredentials.extra[i].credential_type) {
|
||||||
|
$scope.selectedCredentials.extra.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!rowDeselected) {
|
||||||
|
$scope.selectedCredentials.extra
|
||||||
|
.push(_.cloneDeep(selectedRow));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeCredential = function(credToRemove) {
|
||||||
|
$scope.credTags
|
||||||
|
.forEach(function(cred) {
|
||||||
|
if (credToRemove === cred.id) {
|
||||||
|
if (cred.postType === 'machine') {
|
||||||
|
$scope.selectedCredentials[cred.postType] = null;
|
||||||
|
} else {
|
||||||
|
$scope.selectedCredentials[cred.postType] = $scope
|
||||||
|
.selectedCredentials[cred.postType]
|
||||||
|
.filter(cred => cred
|
||||||
|
.id !== credToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.credTags = $scope.credTags
|
||||||
|
.filter(cred => cred.id !== credToRemove);
|
||||||
|
|
||||||
|
if ($scope.credentials
|
||||||
|
.filter(cred => cred.id === credToRemove).length) {
|
||||||
|
uncheckAllCredentials();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancelForm = function() {
|
||||||
|
$scope.selectedCredentials = $scope.originalSelectedCredentials;
|
||||||
|
$scope.credTags = $scope.credentialsToPost;
|
||||||
|
$scope.destroyModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveForm = function() {
|
||||||
|
$scope.credentialsToPost = $scope.credTags;
|
||||||
|
$scope.destroyModal();
|
||||||
|
};
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}];
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<div id="multi-credential-modal" class="Lookup modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header Form-header">
|
||||||
|
<div class="Form-title Form-title--uppercase"
|
||||||
|
translate>
|
||||||
|
CREDENTIALS
|
||||||
|
</div>
|
||||||
|
<div class="Form-header--fields"></div>
|
||||||
|
<div class="Form-exitHolder">
|
||||||
|
<button type="button" class="Form-exit"
|
||||||
|
ng-click="cancelForm()">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="MultiCredential-selectedBar"
|
||||||
|
ng-show="credTags && credTags.length">
|
||||||
|
<span class="MultiCredential-selectedBarLabel" translate>
|
||||||
|
SELECTED:
|
||||||
|
</span>
|
||||||
|
<div class="MultiCredential-tags">
|
||||||
|
<div class="MultiCredential-tagSection">
|
||||||
|
<div class="MultiCredential-flexContainer">
|
||||||
|
<div
|
||||||
|
class="MultiCredential-tagContainer ng-scope"
|
||||||
|
ng-repeat="tag in credTags track by $index">
|
||||||
|
<div class="MultiCredential-deleteContainer"
|
||||||
|
ng-click="removeCredential(tag.id)">
|
||||||
|
<i class="fa fa-times
|
||||||
|
MultiCredential-tagDelete">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div class="MultiCredential-tag
|
||||||
|
MultiCredential-tag--deletable">
|
||||||
|
<span
|
||||||
|
class="MultiCredential-name--label
|
||||||
|
ng-binding">
|
||||||
|
{{ tag.kind }}
|
||||||
|
</span>
|
||||||
|
<span class="MultiCredential-name
|
||||||
|
ng-binding">
|
||||||
|
{{ tag.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="MultiCredential-credentialSubSection">
|
||||||
|
<select ng-model="credentialKind">
|
||||||
|
<option ng-repeat="option in credentialTypeOptions"
|
||||||
|
value="{{option.value}}">{{option.name}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="multi-credential-modal-body"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" ng-click="cancelForm()"
|
||||||
|
class="Lookup-cancel btn btn-sm Form-cancelButton"
|
||||||
|
translate>
|
||||||
|
CANCEL
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
ng-click="saveForm()"
|
||||||
|
ng-disabled="!credentials || credentials.length === 0"
|
||||||
|
class="Lookup-cancel btn btn-sm Form-saveButton" translate>
|
||||||
|
SELECT
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
@import "../../../shared/branding/colors.default.less";
|
||||||
|
|
||||||
|
.MultiCredential-selectedBar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: @default-no-items-bord;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid @default-icon-hov;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-selectedBarLabel {
|
||||||
|
margin-right: 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: @default-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tags {
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-bar i {
|
||||||
|
font-size: 16px;
|
||||||
|
color: @default-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-flexContainer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tagContainer {
|
||||||
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tag {
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
margin: 4px 0px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
background-color: @default-bg;
|
||||||
|
margin-right: 10px;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tag--deletable {
|
||||||
|
margin-right: 0px;
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-right: 0;
|
||||||
|
max-width: ~"calc(100% - 23px)";
|
||||||
|
background-color: @default-link;
|
||||||
|
color: @default-bg;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-deleteContainer {
|
||||||
|
background-color: @default-link!important;
|
||||||
|
color: white;
|
||||||
|
background-color: @default-bg;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 4px 0px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tagDelete {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-name {
|
||||||
|
flex: initial;
|
||||||
|
font-size: 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-name--label {
|
||||||
|
color: @default-list-header-bg;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-left: -7px;
|
||||||
|
margin-right: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-tag--deletable > .MultiCredential-name {
|
||||||
|
max-width: ~"calc(100% - 23px)";
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-deleteContainer:hover {
|
||||||
|
border-color: @default-err;
|
||||||
|
background-color: @default-err!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-deleteContainer:hover > .MultiCredential-tagDelete {
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MultiCredential-credentialSubSection {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2017 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default ['templateUrl', '$compile',
|
||||||
|
function(templateUrl, $compile) {
|
||||||
|
return {
|
||||||
|
scope: {
|
||||||
|
credentials: '=',
|
||||||
|
selectedCredentials: '=',
|
||||||
|
prompt: '=',
|
||||||
|
credentialNotPresent: '=',
|
||||||
|
credentialsToPost: '='
|
||||||
|
},
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: templateUrl('templates/job_templates/multi-credential/multi-credential'),
|
||||||
|
controller: ['$scope',
|
||||||
|
function($scope) {
|
||||||
|
if (!$scope.selectedCredentials) {
|
||||||
|
$scope.selectedCredentials = {
|
||||||
|
machine: null,
|
||||||
|
extra: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$scope.credentialsToPost) {
|
||||||
|
$scope.credentialsToPost = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.fieldDirty = false;
|
||||||
|
|
||||||
|
$scope.$watchGroup(['prompt', 'credentialsToPost'],
|
||||||
|
function() {
|
||||||
|
if ($scope.prompt ||
|
||||||
|
$scope.credentialsToPost.length) {
|
||||||
|
$scope.fieldDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.credentialNotPresent = !$scope.prompt &&
|
||||||
|
$scope.selectedCredentials.machine === null;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.removeCredential = function(credToRemove) {
|
||||||
|
$scope.credentialsToPost = $scope.credentialsToPost
|
||||||
|
.filter(function(cred) {
|
||||||
|
if (cred.id === credToRemove) {
|
||||||
|
if (cred.postType === 'machine') {
|
||||||
|
$scope.selectedCredentials.machine = null;
|
||||||
|
} else {
|
||||||
|
$scope.selectedCredentials
|
||||||
|
.extra = $scope.selectedCredentials
|
||||||
|
.extra
|
||||||
|
.filter(selectedCred => {
|
||||||
|
return selectedCred
|
||||||
|
.id !== credToRemove;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cred.id !== credToRemove;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
],
|
||||||
|
link: function(scope) {
|
||||||
|
scope.openMultiCredentialModal = function() {
|
||||||
|
$('#content-container')
|
||||||
|
.append($compile(`
|
||||||
|
<multi-credential-modal
|
||||||
|
credentials="credentials"
|
||||||
|
credentials-to-post="credentialsToPost"
|
||||||
|
selected-credentials="selectedCredentials">
|
||||||
|
</multi-credential-modal>`)(scope));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<div class="input-group Form-mixedInputGroup">
|
||||||
|
<span class="input-group-btn Form-variableHeightButtonGroup">
|
||||||
|
<button type="button"
|
||||||
|
class="Form-lookupButton
|
||||||
|
Form-lookupButton--variableHeight btn btn-default"
|
||||||
|
ng-click="openMultiCredentialModal()">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<span class="form-control Form-textInput Form-textInput--variableHeight
|
||||||
|
input-medium lookup"
|
||||||
|
ng-class="{
|
||||||
|
'ng-invalid': credentialNotPresent,
|
||||||
|
'ng-dirty': fieldDirty
|
||||||
|
}"
|
||||||
|
style="padding: 4px 6px;">
|
||||||
|
<div class="MultiCredential-tags">
|
||||||
|
<div class="MultiCredential-tagSection">
|
||||||
|
<div class="MultiCredential-flexContainer">
|
||||||
|
<div class="MultiCredential-tagContainer ng-scope"
|
||||||
|
ng-repeat="tag in credentialsToPost track by $index">
|
||||||
|
<div class="MultiCredential-deleteContainer"
|
||||||
|
ng-click="removeCredential(tag.id)">
|
||||||
|
<i class="fa fa-times MultiCredential-tagDelete">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div class="MultiCredential-tag
|
||||||
|
MultiCredential-tag--deletable">
|
||||||
|
<span class="MultiCredential-name--label
|
||||||
|
ng-binding">
|
||||||
|
{{ tag.kind }}
|
||||||
|
</span>
|
||||||
|
<span class="MultiCredential-name ng-binding">
|
||||||
|
{{ tag.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="error" ng-cloak ng-show="credentialNotPresent && fieldDirty"
|
||||||
|
translate>
|
||||||
|
Please select a machine (SSH) credential or check the "Prompt on launch" option.
|
||||||
|
</div>
|
||||||
@@ -7,8 +7,7 @@
|
|||||||
import templatesService from './templates.service';
|
import templatesService from './templates.service';
|
||||||
import surveyMaker from './survey-maker/main';
|
import surveyMaker from './survey-maker/main';
|
||||||
import templatesList from './list/main';
|
import templatesList from './list/main';
|
||||||
import jobTemplatesAdd from './job_templates/add-job-template/main';
|
import jobTemplates from './job_templates/main';
|
||||||
import jobTemplatesEdit from './job_templates/edit-job-template/main';
|
|
||||||
import workflowAdd from './workflows/add-workflow/main';
|
import workflowAdd from './workflows/add-workflow/main';
|
||||||
import workflowEdit from './workflows/edit-workflow/main';
|
import workflowEdit from './workflows/edit-workflow/main';
|
||||||
import labels from './labels/main';
|
import labels from './labels/main';
|
||||||
@@ -18,28 +17,21 @@ import workflowControls from './workflows/workflow-controls/main';
|
|||||||
import templatesListRoute from './list/templates-list.route';
|
import templatesListRoute from './list/templates-list.route';
|
||||||
import workflowService from './workflows/workflow.service';
|
import workflowService from './workflows/workflow.service';
|
||||||
import templateCopyService from './copy-template/template-copy.service';
|
import templateCopyService from './copy-template/template-copy.service';
|
||||||
import CallbackHelpInit from './job_templates/factories/callback-help-init.factory';
|
|
||||||
import md5Setup from './job_templates/factories/md-5-setup.factory';
|
|
||||||
import WorkflowForm from './workflows.form';
|
import WorkflowForm from './workflows.form';
|
||||||
import CompletedJobsList from './completed-jobs.list';
|
import CompletedJobsList from './completed-jobs.list';
|
||||||
import InventorySourcesList from './inventory-sources.list';
|
import InventorySourcesList from './inventory-sources.list';
|
||||||
import TemplateList from './templates.list';
|
import TemplateList from './templates.list';
|
||||||
import JobTemplateForm from './job-template.form';
|
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesAdd.name,
|
angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplates.name, labels.name, workflowAdd.name, workflowEdit.name,
|
||||||
jobTemplatesEdit.name, labels.name, workflowAdd.name, workflowEdit.name,
|
|
||||||
workflowChart.name, workflowMaker.name, workflowControls.name
|
workflowChart.name, workflowMaker.name, workflowControls.name
|
||||||
])
|
])
|
||||||
.service('TemplatesService', templatesService)
|
.service('TemplatesService', templatesService)
|
||||||
.service('WorkflowService', workflowService)
|
.service('WorkflowService', workflowService)
|
||||||
.service('TemplateCopyService', templateCopyService)
|
.service('TemplateCopyService', templateCopyService)
|
||||||
.factory('CallbackHelpInit', CallbackHelpInit)
|
|
||||||
.factory('md5Setup', md5Setup)
|
|
||||||
.factory('WorkflowForm', WorkflowForm)
|
.factory('WorkflowForm', WorkflowForm)
|
||||||
.factory('CompletedJobsList', CompletedJobsList)
|
.factory('CompletedJobsList', CompletedJobsList)
|
||||||
.factory('TemplateList', TemplateList)
|
.factory('TemplateList', TemplateList)
|
||||||
.factory('JobTemplateForm', JobTemplateForm)
|
|
||||||
.value('InventorySourcesList', InventorySourcesList)
|
.value('InventorySourcesList', InventorySourcesList)
|
||||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||||
|
|||||||
Reference in New Issue
Block a user