Configure Tower in Tower

This commit is contained in:
Ken Hoes 2016-10-27 15:48:36 -04:00 committed by jaredevantabor
parent 06dd5bb5d2
commit ee035cbb99
29 changed files with 1842 additions and 2 deletions

View File

@ -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,

View File

@ -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
});
}
];

View File

@ -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>

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View File

@ -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
}
}
};
}

View 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%;
}

View 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
});
}
];

View 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>

View 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'
}
},
};

View 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;
}
};
}
];

View File

@ -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;
}
}
};
}
];

View File

@ -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, {
});
}
];

View File

@ -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
}
}
};
}

View File

@ -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>

View 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);
}]);

View File

@ -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, {
});
}
];

View File

@ -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
}
}
};
}

View File

@ -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>

View File

@ -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, {
});
}
];

View File

@ -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
}
}
};
}

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();