mirror of
https://github.com/ansible/awx.git
synced 2026-01-17 12:41:19 -03:30
Configure Tower in Tower
This commit is contained in:
parent
06dd5bb5d2
commit
ee035cbb99
@ -56,6 +56,7 @@ import setupMenu from './setup-menu/main';
|
||||
import mainMenu from './main-menu/main';
|
||||
import breadCrumb from './bread-crumb/main';
|
||||
import browserData from './browser-data/main';
|
||||
import configuration from './configuration/main';
|
||||
import dashboard from './dashboard/main';
|
||||
import moment from './shared/moment/main';
|
||||
import login from './login/main';
|
||||
@ -100,6 +101,7 @@ var tower = angular.module('Tower', [
|
||||
license.name,
|
||||
RestServices.name,
|
||||
browserData.name,
|
||||
configuration.name,
|
||||
systemTracking.name,
|
||||
inventories.name,
|
||||
inventoryScripts.name,
|
||||
|
||||
@ -0,0 +1,235 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$q',
|
||||
'configurationGithubForm',
|
||||
'configurationGithubOrgForm',
|
||||
'configurationGithubTeamForm',
|
||||
'configurationGoogleForm',
|
||||
'configurationLdapForm',
|
||||
'configurationRadiusForm',
|
||||
'configurationSamlForm',
|
||||
'ConfigurationService',
|
||||
'ConfigurationUtils',
|
||||
'CreateSelect2',
|
||||
'GenerateForm',
|
||||
'ParseTypeChange',
|
||||
'Wait',
|
||||
function(
|
||||
$scope,
|
||||
$state,
|
||||
$stateParams,
|
||||
$timeout,
|
||||
$q,
|
||||
configurationGithubForm,
|
||||
configurationGithubOrgForm,
|
||||
configurationGithubTeamForm,
|
||||
configurationGoogleForm,
|
||||
configurationLdapForm,
|
||||
configurationRadiusForm,
|
||||
configurationSamlForm,
|
||||
ConfigurationService,
|
||||
ConfigurationUtils,
|
||||
CreateSelect2,
|
||||
GenerateForm,
|
||||
ParseTypeChange,
|
||||
Wait
|
||||
) {
|
||||
var authVm = this;
|
||||
|
||||
var generator = GenerateForm;
|
||||
var formTracker = $scope.$parent.vm.formTracker;
|
||||
var dropdownValue = 'github';
|
||||
var activeAuthForm = 'github';
|
||||
|
||||
// Default active form
|
||||
if ($stateParams.currentTab === '' || $stateParams.currentTab === 'auth') {
|
||||
formTracker.setCurrentAuth(activeAuthForm);
|
||||
}
|
||||
|
||||
var activeForm = function() {
|
||||
if(!$scope.$parent[formTracker.currentFormName()].$dirty) {
|
||||
authVm.activeAuthForm = authVm.dropdownValue;
|
||||
formTracker.setCurrentAuth(authVm.activeAuthForm);
|
||||
} else {
|
||||
var msg = 'You have unsaved changes. Would you like to proceed <strong>without</strong> saving?';
|
||||
var title = 'Warning: Unsaved Changes';
|
||||
var buttons = [{
|
||||
label: "Discard changes",
|
||||
"class": "btn Form-cancelButton",
|
||||
"id": "formmodal-cancel-button",
|
||||
onClick: function() {
|
||||
$scope.$parent.vm.populateFromApi();
|
||||
$scope.$parent[formTracker.currentFormName()].$setPristine();
|
||||
authVm.activeAuthForm = authVm.dropdownValue;
|
||||
formTracker.setCurrentAuth(authVm.activeAuthForm);
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
}
|
||||
}, {
|
||||
label: "Save changes",
|
||||
onClick: function() {
|
||||
$scope.$parent.vm.formSave()
|
||||
.then(function() {
|
||||
$scope.$parent[formTracker.currentFormName()].$setPristine();
|
||||
$scope.$parent.vm.populateFromApi();
|
||||
authVm.activeAuthForm = authVm.dropdownValue;
|
||||
formTracker.setCurrentAuth(authVm.activeAuthForm);
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
});
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
"id": "formmodal-save-button"
|
||||
}];
|
||||
$scope.$parent.vm.triggerModal(msg, title, buttons);
|
||||
}
|
||||
formTracker.setCurrentAuth(authVm.activeAuthForm);
|
||||
};
|
||||
|
||||
var dropdownOptions = [
|
||||
{label: 'Github', value: 'github'},
|
||||
{label: 'Github Org', value: 'github_org'},
|
||||
{label: 'Github Team', value: 'github_team'},
|
||||
{label: 'Google OAuth2', value: 'google_oauth'},
|
||||
{label: 'LDAP', value: 'ldap'},
|
||||
{label: 'RADIUS', value: 'radius'},
|
||||
{label: 'SAML', value: 'saml'}
|
||||
];
|
||||
|
||||
CreateSelect2({
|
||||
element: '#configure-dropdown-nav',
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
var authForms = [{
|
||||
formDef: configurationGithubForm,
|
||||
id: 'auth-github-form'
|
||||
}, {
|
||||
formDef: configurationGithubOrgForm,
|
||||
id: 'auth-github-org-form'
|
||||
}, {
|
||||
formDef: configurationGithubTeamForm,
|
||||
id: 'auth-github-team-form'
|
||||
}, {
|
||||
formDef: configurationGoogleForm,
|
||||
id: 'auth-google-form'
|
||||
}, {
|
||||
formDef: configurationLdapForm,
|
||||
id: 'auth-ldap-form'
|
||||
}, {
|
||||
formDef: configurationRadiusForm,
|
||||
id: 'auth-radius-form'
|
||||
}, {
|
||||
formDef: configurationSamlForm,
|
||||
id: 'auth-saml-form'
|
||||
}, ];
|
||||
|
||||
var forms = _.pluck(authForms, 'formDef');
|
||||
|
||||
_.each(forms, function(form) {
|
||||
var keys = _.keys(form.fields);
|
||||
_.each(keys, function(key) {
|
||||
if($scope.$parent.configDataResolve[key].type === 'choice') {
|
||||
// Create options for dropdowns
|
||||
var optionsGroup = key + '_options';
|
||||
$scope.$parent[optionsGroup] = [];
|
||||
_.each($scope.$parent.configDataResolve[key].choices, function(choice){
|
||||
$scope.$parent[optionsGroup].push({
|
||||
name: choice[0],
|
||||
label: choice[1],
|
||||
value: choice[0]
|
||||
});
|
||||
});
|
||||
}
|
||||
addFieldInfo(form, key);
|
||||
});
|
||||
});
|
||||
|
||||
function addFieldInfo(form, key) {
|
||||
_.extend(form.fields[key], {
|
||||
awPopOver: $scope.$parent.configDataResolve[key].help_text,
|
||||
label: $scope.$parent.configDataResolve[key].label,
|
||||
name: key,
|
||||
toggleSource: key,
|
||||
dataPlacement: 'top',
|
||||
placeholder: ConfigurationUtils.formatPlaceholder($scope.$parent.configDataResolve[key].placeholder, key) || null,
|
||||
dataTitle: $scope.$parent.configDataResolve[key].label
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$parent.parseType = 'json';
|
||||
|
||||
_.each(authForms, function(form) {
|
||||
// Generate the forms
|
||||
generator.inject(form.formDef, {
|
||||
id: form.id,
|
||||
mode: 'edit',
|
||||
scope: $scope.$parent,
|
||||
related: true
|
||||
});
|
||||
});
|
||||
|
||||
// Flag to avoid re-rendering and breaking Select2 dropdowns on tab switching
|
||||
var dropdownRendered = false;
|
||||
|
||||
$scope.$on('populated', function() {
|
||||
// Attach codemirror to fields that need it
|
||||
_.each(authForms, function(form) {
|
||||
_.each(form.formDef.fields, function(field) {
|
||||
// Codemirror balks at empty values so give it one
|
||||
if($scope.$parent[field.name] === null && field.codeMirror) {
|
||||
$scope.$parent[field.name] = '{}';
|
||||
}
|
||||
if(field.codeMirror) {
|
||||
ParseTypeChange({
|
||||
scope: $scope.$parent,
|
||||
variable: field.name,
|
||||
parse_variable: 'parseType',
|
||||
field_id: form.formDef.name + '_' + field.name
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Create Select2 fields
|
||||
var opts = [];
|
||||
if($scope.$parent.AUTH_LDAP_GROUP_TYPE !== null) {
|
||||
opts.push({
|
||||
id: $scope.$parent.AUTH_LDAP_GROUP_TYPE,
|
||||
text: $scope.$parent.AUTH_LDAP_GROUP_TYPE
|
||||
});
|
||||
}
|
||||
|
||||
if(!dropdownRendered) {
|
||||
dropdownRendered = true;
|
||||
CreateSelect2({
|
||||
element: '#configuration_ldap_template_AUTH_LDAP_GROUP_TYPE',
|
||||
multiple: false,
|
||||
placeholder: 'Select group types',
|
||||
opts: opts
|
||||
});
|
||||
// Fix for bug where adding selected opts causes form to be $dirty and triggering modal
|
||||
// TODO Find better solution for this bug
|
||||
$timeout(function(){
|
||||
$scope.$parent.configuration_ldap_template_form.$setPristine();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
angular.extend(authVm, {
|
||||
activeForm: activeForm,
|
||||
activeAuthForm: activeAuthForm,
|
||||
authForms: authForms,
|
||||
dropdownOptions: dropdownOptions,
|
||||
dropdownValue: dropdownValue
|
||||
});
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,53 @@
|
||||
<div class="tab-pane Configuration-container" id="configuration_edit">
|
||||
<!-- <div ui-view="form"></div>
|
||||
<div ng-cloak id="htmlTemplate"> -->
|
||||
<div class="Form-nav--dropdown">
|
||||
<select
|
||||
id="configure-dropdown-nav"
|
||||
class="form-control"
|
||||
ng-model="authVm.dropdownValue"
|
||||
ng-options="opt.value as opt.label for opt in authVm.dropdownOptions"
|
||||
ng-change="authVm.activeForm()">
|
||||
</select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div ng-show="authVm.activeAuthForm === 'github'">
|
||||
<div id="auth-github-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'github_org'">
|
||||
<div id="auth-github-org-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'github_team'">
|
||||
<div id="auth-github-team-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'google_oauth'">
|
||||
<div id="auth-google-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'ldap'">
|
||||
<div id="auth-ldap-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'radius'">
|
||||
<div id="auth-radius-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authVm.activeAuthForm === 'saml'">
|
||||
<div id="auth-saml-form">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,43 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
name: 'configuration_github_org_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_ORG_KEY'
|
||||
},
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_ORG_SECRET'
|
||||
},
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_ORG_NAME'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
name: 'configuration_github_team_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
SOCIAL_AUTH_GITHUB_TEAM_KEY: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_TEAM_KEY'
|
||||
},
|
||||
SOCIAL_AUTH_GITHUB_TEAM_SECRET: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_TEAM_SECRET'
|
||||
},
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_TEAM_ID'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
name: 'configuration_github_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
SOCIAL_AUTH_GITHUB_KEY: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_KEY'
|
||||
},
|
||||
SOCIAL_AUTH_GITHUB_SECRET: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GITHUB_SECRET'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
name: 'configuration_google_oauth_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GOOGLE_OAUTH2_KEY'
|
||||
},
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET'
|
||||
},
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS',
|
||||
rows: 6
|
||||
},
|
||||
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS',
|
||||
codeMirror: true,
|
||||
rows: 6,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
// editTitle: 'Authorization Configuration',
|
||||
name: 'configuration_ldap_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
AUTH_LDAP_SERVER_URI: {
|
||||
type: 'text',
|
||||
reset: 'AUTH_LDAP_SERVER_URI'
|
||||
},
|
||||
AUTH_LDAP_BIND_DN: {
|
||||
type: 'text',
|
||||
reset: 'AUTH_LDAP_BIND_DN'
|
||||
},
|
||||
AUTH_LDAP_BIND_PASSWORD: {
|
||||
type: 'password'
|
||||
},
|
||||
AUTH_LDAP_USER_SEARCH: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_USER_SEARCH'
|
||||
},
|
||||
AUTH_LDAP_USER_DN_TEMPLATE: {
|
||||
type: 'text',
|
||||
reset: 'AUTH_LDAP_USER_DN_TEMPLATE'
|
||||
},
|
||||
AUTH_LDAP_USER_ATTR_MAP: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_USER_ATTR_MAP',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
},
|
||||
AUTH_LDAP_GROUP_SEARCH: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_GROUP_SEARCH'
|
||||
},
|
||||
AUTH_LDAP_GROUP_TYPE: {
|
||||
type: 'select',
|
||||
reset: 'AUTH_LDAP_GROUP_TYPE',
|
||||
ngOptions: 'group.label for group in AUTH_LDAP_GROUP_TYPE_options track by group.value',
|
||||
},
|
||||
AUTH_LDAP_REQUIRE_GROUP: {
|
||||
type: 'text',
|
||||
reset: 'AUTH_LDAP_REQUIRE_GROUP'
|
||||
},
|
||||
AUTH_LDAP_DENY_GROUP: {
|
||||
type: 'text',
|
||||
reset: 'AUTH_LDAP_DENY_GROUP'
|
||||
},
|
||||
AUTH_LDAP_START_TLS: {
|
||||
type: 'toggleSwitch'
|
||||
},
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_USER_FLAGS_BY_GROUP',
|
||||
codeMirror: true,
|
||||
rows: 6,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
},
|
||||
AUTH_LDAP_ORGANIZATION_MAP: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_ORGANIZATION_MAP',
|
||||
codeMirror: true,
|
||||
rows: 6,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
},
|
||||
AUTH_LDAP_TEAM_MAP: {
|
||||
type: 'textarea',
|
||||
reset: 'AUTH_LDAP_TEAM_MAP',
|
||||
codeMirror: true,
|
||||
rows: 6,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
// editTitle: 'Authorization Configuration',
|
||||
name: 'configuration_radius_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
RADIUS_SERVER: {
|
||||
type: 'text',
|
||||
reset: 'RADIUS_SERVER'
|
||||
},
|
||||
RADIUS_PORT: {
|
||||
type: 'text',
|
||||
reset: 'RADIUS_PORT'
|
||||
},
|
||||
RADIUS_SECRET: {
|
||||
type: 'text',
|
||||
reset: 'RADIUS_SECRET'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
name: 'configuration_saml_template',
|
||||
showActions: true,
|
||||
showHeader: false,
|
||||
|
||||
fields: {
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_SAML_SP_ENTITY_ID'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_SAML_SP_PUBLIC_CERT'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: {
|
||||
type: 'text',
|
||||
reset: 'SOCIAL_AUTH_SAML_SP_PRIVATE_KEY'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_ORG_INFO: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_SAML_ORG_INFO',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_TECHNICAL_CONTACT: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_SAML_TECHNICAL_CONTACT',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_SUPPORT_CONTACT: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_SAML_SUPPORT_CONTACT',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth'
|
||||
},
|
||||
SOCIAL_AUTH_SAML_ENABLED_IDPS: {
|
||||
type: 'textarea',
|
||||
reset: 'SOCIAL_AUTH_SAML_ENABLED_IDPS',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
27
awx/ui/client/src/configuration/configuration.block.less
Normal file
27
awx/ui/client/src/configuration/configuration.block.less
Normal file
@ -0,0 +1,27 @@
|
||||
.Form-resetValue {
|
||||
float: right;
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.Form-tab {
|
||||
min-width: 77px;
|
||||
}
|
||||
|
||||
.Form-button--left {
|
||||
margin-right: auto;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.Form-nav--dropdown {
|
||||
width: 175px;
|
||||
margin-top: -52px;
|
||||
margin-bottom: 22px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.Form-tabRow {
|
||||
width: 80%;
|
||||
}
|
||||
405
awx/ui/client/src/configuration/configuration.controller.js
Normal file
405
awx/ui/client/src/configuration/configuration.controller.js
Normal file
@ -0,0 +1,405 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
'$scope', '$state', '$stateParams', '$timeout', '$q', 'Alert', 'ClearScope',
|
||||
'ConfigurationService', 'ConfigurationUtils', 'CreateDialog', 'CreateSelect2', 'ParseTypeChange', 'ProcessErrors',
|
||||
'Wait', 'configDataResolve',
|
||||
//Form definitions
|
||||
'configurationGithubForm',
|
||||
'configurationGithubOrgForm',
|
||||
'configurationGithubTeamForm',
|
||||
'configurationGoogleForm',
|
||||
'configurationLdapForm',
|
||||
'configurationRadiusForm',
|
||||
'configurationSamlForm',
|
||||
'ConfigurationJobsForm',
|
||||
'ConfigurationSystemForm',
|
||||
'ConfigurationUiForm',
|
||||
function(
|
||||
$scope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
|
||||
ConfigurationService, ConfigurationUtils, CreateDialog, CreateSelect2, ParseTypeChange, ProcessErrors,
|
||||
Wait, configDataResolve,
|
||||
//Form definitions
|
||||
configurationGithubForm,
|
||||
configurationGithubOrgForm,
|
||||
configurationGithubTeamForm,
|
||||
configurationGoogleForm,
|
||||
configurationLdapForm,
|
||||
configurationRadiusForm,
|
||||
configurationSamlForm,
|
||||
ConfigurationJobsForm,
|
||||
ConfigurationSystemForm,
|
||||
ConfigurationUiForm
|
||||
) {
|
||||
var vm = this;
|
||||
|
||||
var formDefs = {
|
||||
'github': configurationGithubForm,
|
||||
'github_org': configurationGithubOrgForm,
|
||||
'github_team': configurationGithubTeamForm,
|
||||
'google_oauth': configurationGoogleForm,
|
||||
'ldap': configurationLdapForm,
|
||||
'radius': configurationRadiusForm,
|
||||
'saml': configurationSamlForm,
|
||||
'jobs': ConfigurationJobsForm,
|
||||
'system': ConfigurationSystemForm,
|
||||
'ui': ConfigurationUiForm
|
||||
};
|
||||
|
||||
var populateFromApi = function() {
|
||||
ConfigurationService.getCurrentValues()
|
||||
.then(function(data) {
|
||||
var currentKeys = _.keys(data);
|
||||
_.each(currentKeys, function(key) {
|
||||
if (data[key] !== null && typeof data[key] === 'object') {
|
||||
if (Array.isArray(data[key])) {
|
||||
//handle arrays
|
||||
$scope[key] = ConfigurationUtils.arrayToList(data[key], key);
|
||||
} else {
|
||||
//handle nested objects
|
||||
if(ConfigurationUtils.isEmpty(data[key])) {
|
||||
$scope[key] = '{}';
|
||||
} else {
|
||||
$scope[key] = JSON.stringify(data[key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$scope[key] = data[key];
|
||||
}
|
||||
});
|
||||
$scope.$broadcast('populated', data);
|
||||
});
|
||||
};
|
||||
|
||||
populateFromApi();
|
||||
|
||||
var formTracker = {
|
||||
lastForm: '',
|
||||
currentForm: '',
|
||||
currentAuth: '',
|
||||
setCurrent: function(form) {
|
||||
this.lastForm = this.currentForm;
|
||||
this.currentForm = form;
|
||||
},
|
||||
setCurrentAuth: function(form) {
|
||||
this.currentAuth = form;
|
||||
this.setCurrent(this.currentAuth);
|
||||
},
|
||||
getCurrent: function() {
|
||||
return this.currentForm;
|
||||
},
|
||||
currentFormName: function() {
|
||||
return 'configuration_' + this.currentForm + '_template_form';
|
||||
}
|
||||
};
|
||||
|
||||
// Default to auth form and tab
|
||||
if ($stateParams.currentTab === '') {
|
||||
$state.go('configuration', {
|
||||
currentTab: 'auth'
|
||||
}, {
|
||||
location: true,
|
||||
inherit: false,
|
||||
notify: false,
|
||||
reload: false
|
||||
});
|
||||
}
|
||||
|
||||
var currentForm = '';
|
||||
var currentTab = function() {
|
||||
if ($stateParams.currentTab === '' || $stateParams.currentTab === 'auth') {
|
||||
return 'auth';
|
||||
} else if ($stateParams.currentTab !== '' && $stateParams.currentTab !== 'auth') {
|
||||
formTracker.setCurrent($stateParams.currentTab);
|
||||
return $stateParams.currentTab;
|
||||
}
|
||||
};
|
||||
var activeTab = currentTab();
|
||||
|
||||
$scope.configDataResolve = configDataResolve;
|
||||
|
||||
function activeTabCheck(setForm) {
|
||||
if(!$scope[formTracker.currentFormName()].$dirty) {
|
||||
active(setForm);
|
||||
} else {
|
||||
var msg = 'You have unsaved changes. Would you like to proceed <strong>without</strong> saving?';
|
||||
var title = 'Warning: Unsaved Changes';
|
||||
var buttons = [{
|
||||
label: "Discard changes",
|
||||
"class": "btn Form-cancelButton",
|
||||
"id": "formmodal-cancel-button",
|
||||
onClick: function() {
|
||||
clearApiErrors();
|
||||
populateFromApi();
|
||||
$scope[formTracker.currentFormName()].$setPristine();
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
active(setForm);
|
||||
}
|
||||
}, {
|
||||
label: "Save changes",
|
||||
onClick: function() {
|
||||
vm.formSave();
|
||||
$scope[formTracker.currentFormName()].$setPristine();
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
active(setForm);
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
"id": "formmodal-save-button"
|
||||
}];
|
||||
triggerModal(msg, title, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
function active(setForm) {
|
||||
if (setForm === 'auth') {
|
||||
// Default to 'github' on first load
|
||||
if (formTracker.currentAuth === '') {
|
||||
formTracker.setCurrentAuth('github');
|
||||
} else {
|
||||
// If returning to auth tab reset current form to previously viewed
|
||||
formTracker.setCurrentAuth(formTracker.currentAuth);
|
||||
}
|
||||
} else {
|
||||
formTracker.setCurrent(setForm);
|
||||
}
|
||||
vm.activeTab = setForm;
|
||||
$state.go('configuration', {
|
||||
currentTab: setForm
|
||||
}, {
|
||||
location: true,
|
||||
inherit: false,
|
||||
notify: false,
|
||||
reload: false
|
||||
});
|
||||
}
|
||||
|
||||
var formCancel = function() {
|
||||
if ($scope[formTracker.currentFormName()].$dirty === true) {
|
||||
var msg = 'You have unsaved changes. Would you like to proceed <strong>without</strong> saving?';
|
||||
var title = 'Warning: Unsaved Changes';
|
||||
var buttons = [{
|
||||
label: "Discard changes",
|
||||
"class": "btn Form-cancelButton",
|
||||
"id": "formmodal-cancel-button",
|
||||
onClick: function() {
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
$state.go('setup');
|
||||
}
|
||||
}, {
|
||||
label: "Save changes",
|
||||
onClick: function() {
|
||||
$scope.formSave();
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
$state.go('setup');
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
"id": "formmodal-save-button"
|
||||
}];
|
||||
triggerModal(msg, title, buttons);
|
||||
} else {
|
||||
$state.go('setup');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.resetValue = function(key) {
|
||||
Wait('start');
|
||||
var payload = {};
|
||||
payload[key] = $scope.configDataResolve[key].default;
|
||||
|
||||
ConfigurationService.patchConfiguration(payload)
|
||||
.then(function(data) {
|
||||
$scope[key] = $scope.configDataResolve[key].default;
|
||||
})
|
||||
.catch(function(error) {
|
||||
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: 'There was an error resetting value. Returned status: ' + error.detail
|
||||
});
|
||||
|
||||
})
|
||||
.finally(function() {
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
|
||||
var triggerModal = function(msg, title, buttons) {
|
||||
if ($scope.removeModalReady) {
|
||||
$scope.removeModalReady();
|
||||
}
|
||||
$scope.removeModalReady = $scope.$on('ModalReady', function() {
|
||||
// $('#lookup-save-button').attr('disabled', 'disabled');
|
||||
$('#FormModal-dialog').dialog('open');
|
||||
});
|
||||
|
||||
$('#FormModal-dialog').html(msg);
|
||||
|
||||
CreateDialog({
|
||||
scope: $scope,
|
||||
buttons: buttons,
|
||||
width: 600,
|
||||
height: 200,
|
||||
minWidth: 500,
|
||||
title: title,
|
||||
id: 'FormModal-dialog',
|
||||
resizable: false,
|
||||
callback: 'ModalReady'
|
||||
});
|
||||
};
|
||||
|
||||
function clearApiErrors() {
|
||||
var currentForm = formDefs[formTracker.getCurrent()];
|
||||
for (var fld in currentForm.fields) {
|
||||
if (currentForm.fields[fld].sourceModel) {
|
||||
$scope[currentForm.fields[fld].sourceModel + '_' + currentForm.fields[fld].sourceField + '_api_error'] = '';
|
||||
$('[name="' + currentForm.fields[fld].sourceModel + '_' + currentForm.fields[fld].sourceField + '"]').removeClass('ng-invalid');
|
||||
} else if (currentForm.fields[fld].realName) {
|
||||
$scope[currentForm.fields[fld].realName + '_api_error'] = '';
|
||||
$('[name="' + currentForm.fields[fld].realName + '"]').removeClass('ng-invalid');
|
||||
} else {
|
||||
$scope[fld + '_api_error'] = '';
|
||||
$('[name="' + fld + '"]').removeClass('ng-invalid');
|
||||
}
|
||||
}
|
||||
if (!$scope.$$phase) {
|
||||
$scope.$digest();
|
||||
}
|
||||
}
|
||||
|
||||
// Some dropdowns are listed as "list" type in the API even though they're a dropdown:
|
||||
var multiselectDropdowns = ['AD_HOC_COMMANDS'];
|
||||
var formSave = function() {
|
||||
var saveDeferred = $q.defer();
|
||||
var keys = _.keys(formDefs[formTracker.getCurrent()].fields);
|
||||
var payload = {};
|
||||
clearApiErrors();
|
||||
_.each(keys, function(key) {
|
||||
if($scope.configDataResolve[key].type === 'choice' || multiselectDropdowns.indexOf(key) !== -1) {
|
||||
//Parse dropdowns and dropdowns labeled as lists
|
||||
if($scope[key] === null) {
|
||||
payload[key] = null;
|
||||
} else if($scope[key][0] && $scope[key][0].value !== undefined) {
|
||||
payload[key] = _.map($scope[key], 'value').join(',');
|
||||
} else {
|
||||
payload[key] = $scope[key].value;
|
||||
}
|
||||
} else if($scope.configDataResolve[key].type === 'list' && $scope[key] !== null) {
|
||||
// Parse lists
|
||||
payload[key] = ConfigurationUtils.listToArray($scope[key], key);
|
||||
}
|
||||
else if($scope.configDataResolve[key].type === 'nested object') {
|
||||
if($scope[key] === '') {
|
||||
payload[key] = {};
|
||||
} else {
|
||||
payload[key] = JSON.parse($scope[key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Everything else
|
||||
payload[key] = $scope[key];
|
||||
}
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
ConfigurationService.patchConfiguration(payload)
|
||||
.then(function(data) {
|
||||
saveDeferred.resolve(data);
|
||||
$scope[formTracker.currentFormName()].$setPristine();
|
||||
})
|
||||
.catch(function(error, status) {
|
||||
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to save settings. Returned status: ' + status
|
||||
});
|
||||
saveDeferred.reject(error);
|
||||
})
|
||||
.finally(function() {
|
||||
Wait('stop');
|
||||
});
|
||||
|
||||
return saveDeferred.promise;
|
||||
};
|
||||
|
||||
$scope.toggleForm = function(key) {
|
||||
$scope[key] = !$scope[key];
|
||||
Wait('start');
|
||||
var payload = {};
|
||||
payload[key] = $scope[key];
|
||||
ConfigurationService.patchConfiguration(payload)
|
||||
.then(function(results) {
|
||||
//TODO consider updating form values with returned data here
|
||||
})
|
||||
.catch(function(error, status) {
|
||||
//Change back on unsuccessful update
|
||||
$scope[key] = !$scope[key];
|
||||
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to save toggle settings. Returned status: ' + error.detail
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
|
||||
var resetAll = function() {
|
||||
Wait('start');
|
||||
ConfigurationService.resetAll()
|
||||
.then(function(results) {
|
||||
populateFromApi();
|
||||
$scope[formTracker.currentFormName].$setPristine();
|
||||
})
|
||||
.catch(function(error) {
|
||||
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: 'There was an error resetting values. Returned status: ' + error.detail
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
|
||||
var resetAllConfirm = function() {
|
||||
var buttons = [{
|
||||
label: "Cancel",
|
||||
"class": "btn btn-default",
|
||||
"id": "formmodal-cancel-button",
|
||||
onClick: function() {
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
}
|
||||
}, {
|
||||
label: "Confirm Reset",
|
||||
onClick: function() {
|
||||
resetAll();
|
||||
$('#FormModal-dialog').dialog('close');
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
"id": "formmodal-reset-button"
|
||||
}];
|
||||
var msg = 'This will reset all configuration values to their factory defaults. Are you sure you want to proceed?';
|
||||
var title = 'Confirm factory reset';
|
||||
triggerModal(msg, title, buttons);
|
||||
};
|
||||
|
||||
angular.extend(vm, {
|
||||
activeTab: activeTab,
|
||||
activeTabCheck: activeTabCheck,
|
||||
currentForm: currentForm,
|
||||
formCancel: formCancel,
|
||||
formTracker: formTracker,
|
||||
formSave: formSave,
|
||||
populateFromApi: populateFromApi,
|
||||
resetAllConfirm: resetAllConfirm,
|
||||
triggerModal: triggerModal
|
||||
});
|
||||
}
|
||||
];
|
||||
22
awx/ui/client/src/configuration/configuration.partial.html
Normal file
22
awx/ui/client/src/configuration/configuration.partial.html
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="tab-pane" id="configuration-panel">
|
||||
<div ng-cloak id="htmlTemplate" class="Panel">
|
||||
<div class="Form-header">
|
||||
<div class="Form-title">Configure Tower</div>
|
||||
</div>
|
||||
<div class="row Form-tabRow">
|
||||
<div class="col-lg-12">
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('auth')" ng-class="{'is-selected': vm.activeTab === 'auth' }">Authorization</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('jobs')" ng-class="{'is-selected': vm.activeTab === 'jobs' }">Jobs</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('system')" ng-class="{'is-selected': vm.activeTab === 'system' }">System</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('ui')" ng-class="{'is-selected': vm.activeTab === 'ui' }">UI</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-view="auth" ng-show="vm.activeTab === 'auth'"></div>
|
||||
<div ui-view="jobs" ng-show="vm.activeTab === 'jobs'"></div>
|
||||
<div ui-view="system" ng-show="vm.activeTab === 'system'"></div>
|
||||
<div ui-view="ui" ng-show="vm.activeTab === 'ui'"></div>
|
||||
<div id="FormModal-dialog"></div>
|
||||
</div>
|
||||
</div>
|
||||
56
awx/ui/client/src/configuration/configuration.route.js
Normal file
56
awx/ui/client/src/configuration/configuration.route.js
Normal file
@ -0,0 +1,56 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import ConfigurationController from './configuration.controller';
|
||||
|
||||
// Import form controllers
|
||||
import ConfigurationAuthController from './auth-form/configuration-auth.controller';
|
||||
import ConfigurationJobsController from './jobs-form/configuration-jobs.controller';
|
||||
import ConfigurationSystemController from './system-form/configuration-system.controller';
|
||||
import ConfigurationUiController from './ui-form/configuration-ui.controller';
|
||||
|
||||
export default {
|
||||
name: 'configuration',
|
||||
route: '/configuration/:currentTab',
|
||||
|
||||
ncyBreadcrumb: {
|
||||
label: "Edit Configuration"
|
||||
},
|
||||
controller: ConfigurationController,
|
||||
resolve: {
|
||||
configDataResolve: ['ConfigurationService', function(ConfigurationService){
|
||||
return ConfigurationService.getConfigurationOptions();
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'': {
|
||||
templateUrl: templateUrl('configuration/configuration'),
|
||||
controller: ConfigurationController,
|
||||
controllerAs: 'vm'
|
||||
},
|
||||
'auth@configuration': {
|
||||
templateUrl: templateUrl('configuration/auth-form/configuration-auth'),
|
||||
controller: ConfigurationAuthController,
|
||||
controllerAs: 'authVm'
|
||||
},
|
||||
'jobs@configuration': {
|
||||
templateUrl: templateUrl('configuration/jobs-form/configuration-jobs'),
|
||||
controller: ConfigurationJobsController,
|
||||
controllerAs: 'jobsVm'
|
||||
},
|
||||
'system@configuration': {
|
||||
templateUrl: templateUrl('configuration/system-form/configuration-system'),
|
||||
controller: ConfigurationSystemController,
|
||||
controllerAs: 'systemVm'
|
||||
},
|
||||
'ui@configuration': {
|
||||
templateUrl: templateUrl('configuration/ui-form/configuration-ui'),
|
||||
controller: ConfigurationUiController,
|
||||
controllerAs: 'uiVm'
|
||||
}
|
||||
},
|
||||
};
|
||||
74
awx/ui/client/src/configuration/configuration.service.js
Normal file
74
awx/ui/client/src/configuration/configuration.service.js
Normal file
@ -0,0 +1,74 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['GetBasePath', 'ProcessErrors', '$q', '$http', 'Rest', '$rootScope', '$timeout', 'Wait',
|
||||
function(GetBasePath, ProcessErrors, $q, $http, Rest, $rootScope, $timeout, Wait) {
|
||||
var url = GetBasePath('settings');
|
||||
|
||||
return {
|
||||
getConfigurationOptions: function() {
|
||||
var deferred = $q.defer();
|
||||
Rest.setUrl(url + '/all');
|
||||
Rest.options()
|
||||
.success(function(data) {
|
||||
var returnData = data.actions.PUT;
|
||||
//LICENSE is read only, returning here explicitly for display
|
||||
returnData.LICENSE = data.actions.GET.LICENSE;
|
||||
deferred.resolve(returnData);
|
||||
})
|
||||
.error(function(error) {
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
patchConfiguration: function(body) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Rest.setUrl(url + 'all');
|
||||
Rest.patch(body)
|
||||
.success(function(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.error(function(error) {
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
getCurrentValues: function() {
|
||||
var deferred = $q.defer();
|
||||
Rest.setUrl(url + '/all');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.error(function(error) {
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
resetAll: function() {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Rest.setUrl(url + '/all');
|
||||
Rest.delete()
|
||||
.success(function(data) {
|
||||
deferred.resolve(data);
|
||||
})
|
||||
.error(function(error) {
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,67 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
function() {
|
||||
|
||||
return {
|
||||
listToArray: function(input, key) {
|
||||
if (input.indexOf('\n') !== -1) {
|
||||
//Parse multiline input
|
||||
return input.replace(/^\s+|\s+$/g, "").split('\n');
|
||||
} else {
|
||||
return input.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/);
|
||||
}
|
||||
},
|
||||
|
||||
arrayToList: function(input, key) {
|
||||
var multiLineInput = false;
|
||||
_.each(input, function(statement) {
|
||||
if (statement.indexOf(',') !== -1) {
|
||||
multiLineInput = true;
|
||||
}
|
||||
});
|
||||
if (multiLineInput === false) {
|
||||
return input.join(', ');
|
||||
} else {
|
||||
return input.join('\n');
|
||||
}
|
||||
},
|
||||
|
||||
isEmpty: function(map) {
|
||||
for (var key in map) {
|
||||
if (map.hasOwnProperty(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
formatPlaceholder: function(input, key) {
|
||||
if(input !== null && typeof input === 'object') {
|
||||
if(Array.isArray(input)) {
|
||||
var multiLineInput = false;
|
||||
_.each(input, function(statement) {
|
||||
if(statement.indexOf(',') !== -1) {
|
||||
multiLineInput = true;
|
||||
}
|
||||
});
|
||||
if(multiLineInput === false) {
|
||||
return input.join(', ');
|
||||
} else {
|
||||
return input.join('\n');
|
||||
}
|
||||
} else {
|
||||
return JSON.stringify(input);
|
||||
}
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,94 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$timeout',
|
||||
'ConfigurationJobsForm',
|
||||
'ConfigurationService',
|
||||
'ConfigurationUtils',
|
||||
'CreateSelect2',
|
||||
'GenerateForm',
|
||||
function(
|
||||
$scope,
|
||||
$state,
|
||||
$timeout,
|
||||
ConfigurationJobsForm,
|
||||
ConfigurationService,
|
||||
ConfigurationUtils,
|
||||
CreateSelect2,
|
||||
GenerateForm
|
||||
) {
|
||||
var jobsVm = this;
|
||||
var generator = GenerateForm;
|
||||
var form = ConfigurationJobsForm;
|
||||
$scope.$parent.AD_HOC_COMMANDS_options = [];
|
||||
_.each($scope.$parent.configDataResolve.AD_HOC_COMMANDS.default, function(command) {
|
||||
$scope.$parent.AD_HOC_COMMANDS_options.push({
|
||||
name: command,
|
||||
label: command,
|
||||
value: command
|
||||
});
|
||||
});
|
||||
|
||||
var keys = _.keys(form.fields);
|
||||
_.each(keys, function(key) {
|
||||
addFieldInfo(form, key);
|
||||
});
|
||||
|
||||
function addFieldInfo(form, key) {
|
||||
_.extend(form.fields[key], {
|
||||
awPopOver: $scope.$parent.configDataResolve[key].help_text,
|
||||
label: $scope.$parent.configDataResolve[key].label,
|
||||
name: key,
|
||||
toggleSource: key,
|
||||
dataPlacement: 'top',
|
||||
dataTitle: $scope.$parent.configDataResolve[key].label
|
||||
});
|
||||
}
|
||||
|
||||
generator.inject(form, {
|
||||
id: 'configure-jobs-form',
|
||||
mode: 'edit',
|
||||
scope: $scope.$parent,
|
||||
related: false
|
||||
});
|
||||
|
||||
// Flag to avoid re-rendering and breaking Select2 dropdowns on tab switching
|
||||
var dropdownRendered = false;
|
||||
|
||||
$scope.$on('populated', function() {
|
||||
var opts = [];
|
||||
_.each(ConfigurationUtils.listToArray($scope.$parent.AD_HOC_COMMANDS), function(command) {
|
||||
opts.push({
|
||||
id: command,
|
||||
text: command
|
||||
});
|
||||
});
|
||||
|
||||
if(!dropdownRendered) {
|
||||
dropdownRendered = true;
|
||||
CreateSelect2({
|
||||
element: '#configuration_jobs_template_AD_HOC_COMMANDS',
|
||||
multiple: true,
|
||||
placeholder: 'Select commands',
|
||||
opts: opts
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
// Fix for bug where adding selected opts causes form to be $dirty and triggering modal
|
||||
// TODO Find better solution for this bug
|
||||
$timeout(function(){
|
||||
$scope.$parent.configuration_jobs_template_form.$setPristine();
|
||||
}, 1000);
|
||||
|
||||
angular.extend(jobsVm, {
|
||||
});
|
||||
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,67 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
showHeader: false,
|
||||
name: 'configuration_jobs_template',
|
||||
showActions: true,
|
||||
|
||||
fields: {
|
||||
AD_HOC_COMMANDS: {
|
||||
type: 'select',
|
||||
ngOptions: 'command.label for command in AD_HOC_COMMANDS_options track by command.value',
|
||||
reset: 'AD_HOC_COMMANDS',
|
||||
multiSelect: true
|
||||
},
|
||||
STDOUT_MAX_BYTES_DISPLAY: {
|
||||
type: 'number',
|
||||
reset: 'STDOUT_MAX_BYTES_DISPLAY'
|
||||
},
|
||||
AWX_PROOT_BASE_PATH: {
|
||||
type: 'text',
|
||||
reset: 'AWX_PROOT_BASE_PATH',
|
||||
},
|
||||
SCHEDULE_MAX_JOBS: {
|
||||
type: 'number',
|
||||
reset: 'SCHEDULE_MAX_JOBS'
|
||||
},
|
||||
AWX_PROOT_SHOW_PATHS: {
|
||||
type: 'textarea',
|
||||
reset: 'AWX_PROOT_SHOW_PATHS',
|
||||
rows: 6
|
||||
},
|
||||
AWX_ANSIBLE_CALLBACK_PLUGINS: {
|
||||
type: 'textarea',
|
||||
reset: 'AWX_ANSIBLE_CALLBACK_PLUGINS',
|
||||
rows: 6
|
||||
},
|
||||
AWX_PROOT_HIDE_PATHS: {
|
||||
type: 'textarea',
|
||||
reset: 'AWX_PROOT_HIDE_PATHS',
|
||||
rows: 6
|
||||
},
|
||||
AWX_PROOT_ENABLED: {
|
||||
type: 'toggleSwitch',
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
// ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<div class="tab-pane Configuration-container" id="configuration_edit">
|
||||
<!-- <div ui-view="form"></div>
|
||||
<div ng-cloak id="htmlTemplate"> -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div id="configure-jobs-form"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
44
awx/ui/client/src/configuration/main.js
Normal file
44
awx/ui/client/src/configuration/main.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import configurationService from './configuration.service';
|
||||
import ConfigurationUtils from './configurationUtils.service';
|
||||
import configurationRoute from './configuration.route';
|
||||
import configurationController from './configuration.controller.js';
|
||||
|
||||
// Import forms
|
||||
//authorization sub-forms
|
||||
import configurationGithubForm from './auth-form/sub-forms/auth-github.form.js';
|
||||
import configurationGithubOrgForm from './auth-form/sub-forms/auth-github-org.form';
|
||||
import configurationGithubTeamForm from './auth-form/sub-forms/auth-github-team.form';
|
||||
import configurationGoogleForm from './auth-form/sub-forms/auth-google-oauth2.form';
|
||||
import configurationLdapForm from './auth-form/sub-forms/auth-ldap.form.js';
|
||||
import configurationRadiusForm from './auth-form/sub-forms/auth-radius.form.js';
|
||||
import configurationSamlForm from './auth-form/sub-forms/auth-saml.form';
|
||||
|
||||
import configurationJobsForm from './jobs-form/configuration-jobs.form';
|
||||
import configurationSystemForm from './system-form/configuration-system.form';
|
||||
import configurationUiForm from './ui-form/configuration-ui.form';
|
||||
|
||||
export default
|
||||
angular.module('configuration', [])
|
||||
.controller('ConfigurationController', configurationController)
|
||||
//auth forms
|
||||
.factory('configurationGithubForm', configurationGithubForm)
|
||||
.factory('configurationGithubOrgForm', configurationGithubOrgForm)
|
||||
.factory('configurationGithubTeamForm', configurationGithubTeamForm)
|
||||
.factory('configurationGoogleForm', configurationGoogleForm)
|
||||
.factory('configurationLdapForm', configurationLdapForm)
|
||||
.factory('configurationRadiusForm', configurationRadiusForm)
|
||||
.factory('configurationSamlForm', configurationSamlForm)
|
||||
.factory('ConfigurationJobsForm', configurationJobsForm)
|
||||
.factory('ConfigurationSystemForm', configurationSystemForm)
|
||||
.factory('ConfigurationUiForm', configurationUiForm)
|
||||
.factory('ConfigurationUtils', ConfigurationUtils)
|
||||
.service('ConfigurationService', configurationService)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(configurationRoute);
|
||||
}]);
|
||||
@ -0,0 +1,68 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
'$scope', '$state', 'AngularCodeMirror', 'ConfigurationSystemForm', 'ConfigurationService', 'ConfigurationUtils', 'GenerateForm', 'ParseTypeChange',
|
||||
function(
|
||||
$scope, $state, AngularCodeMirror, ConfigurationSystemForm, ConfigurationService, ConfigurationUtils, GenerateForm, ParseTypeChange
|
||||
) {
|
||||
var systemVm = this;
|
||||
var generator = GenerateForm;
|
||||
var form = ConfigurationSystemForm;
|
||||
var keys = _.keys(form.fields);
|
||||
|
||||
_.each(keys, function(key) {
|
||||
addFieldInfo(form, key);
|
||||
});
|
||||
|
||||
function addFieldInfo(form, key) {
|
||||
_.extend(form.fields[key], {
|
||||
awPopOver: $scope.$parent.configDataResolve[key].help_text,
|
||||
label: $scope.$parent.configDataResolve[key].label,
|
||||
name: key,
|
||||
toggleSource: key,
|
||||
dataPlacement: 'top',
|
||||
dataTitle: $scope.$parent.configDataResolve[key].label
|
||||
});
|
||||
}
|
||||
|
||||
generator.inject(form, {
|
||||
id: 'configure-system-form',
|
||||
mode: 'edit',
|
||||
scope: $scope.$parent,
|
||||
related: true
|
||||
});
|
||||
|
||||
|
||||
$scope.$on('populated', function() {
|
||||
|
||||
// var fld = 'LICENSE';
|
||||
// var readOnly = true;
|
||||
// $scope.$parent[fld + 'codeMirror'] = AngularCodeMirror(readOnly);
|
||||
// $scope.$parent[fld + 'codeMirror'].addModes($AnsibleConfig.variable_edit_modes);
|
||||
// $scope.$parent[fld + 'codeMirror'].showTextArea({
|
||||
// scope: $scope.$parent,
|
||||
// model: fld,
|
||||
// element: "configuration_system_template_LICENSE",
|
||||
// lineNumbers: true,
|
||||
// mode: 'json',
|
||||
// });
|
||||
|
||||
$scope.$parent.parseType = 'json';
|
||||
ParseTypeChange({
|
||||
scope: $scope.$parent,
|
||||
variable: 'LICENSE',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'configuration_system_template_LICENSE',
|
||||
readOnly: true
|
||||
});
|
||||
});
|
||||
|
||||
angular.extend(systemVm, {
|
||||
|
||||
});
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,55 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
showHeader: false,
|
||||
name: 'configuration_system_template',
|
||||
showActions: true,
|
||||
|
||||
fields: {
|
||||
TOWER_URL_BASE: {
|
||||
type: 'text',
|
||||
reset: 'TOWER_URL_BASE',
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
},
|
||||
TOWER_ADMIN_ALERTS: {
|
||||
type: 'toggleSwitch',
|
||||
},
|
||||
ACTIVITY_STREAM_ENABLED: {
|
||||
type: 'toggleSwitch',
|
||||
},
|
||||
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: {
|
||||
type: 'toggleSwitch'
|
||||
},
|
||||
ORG_ADMINS_CAN_SEE_ALL_USERS: {
|
||||
type: 'toggleSwitch',
|
||||
},
|
||||
LICENSE: {
|
||||
type: 'textarea',
|
||||
rows: 6,
|
||||
codeMirror: true,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<div class="tab-pane Configuration-container">
|
||||
<!-- <div ui-view="form"></div>
|
||||
<div ng-cloak id="htmlTemplate"> -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div id="configure-system-form"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,92 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default [
|
||||
'$scope',
|
||||
'$state',
|
||||
'$timeout',
|
||||
'ConfigurationUiForm',
|
||||
'ConfigurationService',
|
||||
'CreateSelect2',
|
||||
'GenerateForm',
|
||||
function(
|
||||
$scope,
|
||||
$state,
|
||||
$timeout,
|
||||
ConfigurationUiForm,
|
||||
ConfigurationService,
|
||||
CreateSelect2,
|
||||
GenerateForm
|
||||
) {
|
||||
var uiVm = this;
|
||||
var generator = GenerateForm;
|
||||
var form = ConfigurationUiForm;
|
||||
|
||||
|
||||
var keys = _.keys(form.fields);
|
||||
_.each(keys, function(key) {
|
||||
if($scope.$parent.configDataResolve[key].type === 'choice') {
|
||||
// Create options for dropdowns
|
||||
var optionsGroup = key + '_options';
|
||||
$scope.$parent[optionsGroup] = [];
|
||||
_.each($scope.$parent.configDataResolve[key].choices, function(choice){
|
||||
$scope.$parent[optionsGroup].push({
|
||||
name: choice[0],
|
||||
label: choice[1],
|
||||
value: choice[0]
|
||||
});
|
||||
});
|
||||
}
|
||||
addFieldInfo(form, key);
|
||||
});
|
||||
|
||||
function addFieldInfo(form, key) {
|
||||
_.extend(form.fields[key], {
|
||||
awPopOver: $scope.$parent.configDataResolve[key].help_text,
|
||||
label: $scope.$parent.configDataResolve[key].label,
|
||||
name: key,
|
||||
toggleSource: key,
|
||||
dataPlacement: 'top',
|
||||
dataTitle: $scope.$parent.configDataResolve[key].label
|
||||
});
|
||||
}
|
||||
|
||||
generator.inject(form, {
|
||||
id: 'configure-ui-form',
|
||||
mode: 'edit',
|
||||
scope: $scope.$parent,
|
||||
related: true
|
||||
});
|
||||
|
||||
// Flag to avoid re-rendering and breaking Select2 dropdowns on tab switching
|
||||
var dropdownRendered = false;
|
||||
|
||||
$scope.$on('populated', function(){
|
||||
if(!dropdownRendered) {
|
||||
dropdownRendered = true;
|
||||
CreateSelect2({
|
||||
element: '#configuration_ui_template_PENDO_TRACKING_STATE',
|
||||
multiple: false,
|
||||
placeholder: 'Select commands',
|
||||
opts: [{
|
||||
id: $scope.$parent.PENDO_TRACKING_STATE,
|
||||
text: $scope.$parent.PENDO_TRACKING_STATE
|
||||
}]
|
||||
});
|
||||
// Fix for bug where adding selected opts causes form to be $dirty and triggering modal
|
||||
// TODO Find better solution for this bug
|
||||
$timeout(function(){
|
||||
$scope.$parent.configuration_ui_template_form.$setPristine();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
angular.extend(uiVm, {
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,37 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
showHeader: false,
|
||||
name: 'configuration_ui_template',
|
||||
showActions: true,
|
||||
|
||||
fields: {
|
||||
PENDO_TRACKING_STATE: {
|
||||
type: 'select',
|
||||
ngChange: 'changedPendo()',
|
||||
ngOptions: 'choice.label for choice in PENDO_TRACKING_STATE_options track by choice.value',
|
||||
reset: 'PENDO_TRACKING_STATE'
|
||||
},
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'vm.resetAllConfirm()',
|
||||
label: 'Reset All',
|
||||
class: 'Form-button--left Form-cancelButton'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'vm.formCancel()',
|
||||
},
|
||||
save: {
|
||||
ngClick: 'vm.formSave()',
|
||||
ngDisabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<div class="tab-pane Configuration-container">
|
||||
<!-- <div ui-view="form"></div>
|
||||
<div ng-cloak id="htmlTemplate"> -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div id="configure-ui-form"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,6 +49,12 @@
|
||||
View and edit your license information.
|
||||
</p>
|
||||
</a>
|
||||
<a ui-sref="configuration" class="SetupItem">
|
||||
<h4 class="SetupItem-title">Configure Tower</h4>
|
||||
<p class="SetupItem-description">
|
||||
Edit Tower's configuration.
|
||||
</p>
|
||||
</a>
|
||||
<a ui-sref="setup.about" class="SetupItem">
|
||||
<h4 class="SetupItem-title" translate>About Tower</h4>
|
||||
<p class="SetupItem-description" translate>
|
||||
|
||||
@ -228,7 +228,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
||||
scope[field + '_api_error'] = data[form.fields[field]][0];
|
||||
//scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false);
|
||||
$('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid');
|
||||
$('[name="' + form.fields[field].realName + '"]').ScrollTo({ "onlyIfOutside": true, "offsetTop": 100 });
|
||||
$('html, body').animate({scrollTop: $('[name="' + form.fields[field].realName + '"]').offset().top}, 0);
|
||||
fieldErrors = true;
|
||||
}
|
||||
}
|
||||
@ -246,7 +246,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
||||
scope[field + '_api_error'] = data[field][0];
|
||||
//scope[form.name + '_form'][field].$setValidity('apiError', false);
|
||||
$('[name="' + field + '"]').addClass('ng-invalid');
|
||||
$('[name="' + field + '"]').ScrollTo({ "onlyIfOutside": true, "offsetTop": 100 });
|
||||
$('html, body').animate({scrollTop: $('[name="' + field + '"]').offset().top}, 0);
|
||||
fieldErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,6 +478,12 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
let cls = action["class"] || "";
|
||||
html += `<a class="Form-labelAction ${cls}" href="${href}" ng-click="${ngClick}">${action.label}</a>`;
|
||||
}
|
||||
|
||||
if(field.reset) {
|
||||
var resetValue = "'" + field.reset+ "'";
|
||||
html+= `<a class="Form-resetValue" ng-click="resetValue(${resetValue})">Reset</a>`;
|
||||
}
|
||||
|
||||
html += "\n\t</label>\n";
|
||||
}
|
||||
return html;
|
||||
@ -535,6 +541,16 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
html += (field.awFeature) ? "aw-feature=\"" + field.awFeature + "\" " : "";
|
||||
html += ">\n";
|
||||
|
||||
// toggle switches
|
||||
if(field.type === 'toggleSwitch') {
|
||||
html += label();
|
||||
html += `<div class="ScheduleToggle" ng-class="{'is-on': ${field.toggleSource}}" aw-tool-tip=""
|
||||
data-placement="undefined" data-tip-watch="undefined" data-original-title="" title="">
|
||||
<div ng-show="${field.toggleSource}" class="ScheduleToggle-switch is-on" ng-click="toggleForm('${field.toggleSource}')">ON</div>
|
||||
<div ng-show="!${field.toggleSource}" class="ScheduleToggle-switch" ng-click="toggleForm('${field.toggleSource}')">OFF</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
//text fields
|
||||
if (field.type === 'text' || field.type === 'password' || field.type === 'email') {
|
||||
html += label();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user