38a6b3621c
When the user commits the configuration, if they have chosen a non-DB auth system, we now auto-link the superuser account to that auth system, to ensure they can login again after restart.
914 lines
No EOL
30 KiB
JavaScript
914 lines
No EOL
30 KiB
JavaScript
angular.module("core-config-setup", ['angularFileUpload'])
|
|
.directive('configSetupTool', function() {
|
|
var directiveDefinitionObject = {
|
|
priority: 1,
|
|
templateUrl: '/static/directives/config/config-setup-tool.html',
|
|
replace: true,
|
|
transclude: true,
|
|
restrict: 'C',
|
|
scope: {
|
|
'isActive': '=isActive',
|
|
'configurationSaved': '&configurationSaved'
|
|
},
|
|
controller: function($rootScope, $scope, $element, $timeout, ApiService) {
|
|
$scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9\.]+(:[0-9]+)?$';
|
|
$scope.GITHOST_REGEX = '^https?://([a-zA-Z0-9]+\.?\/?)+$';
|
|
|
|
$scope.SERVICES = [
|
|
{'id': 'redis', 'title': 'Redis'},
|
|
|
|
{'id': 'registry-storage', 'title': 'Registry Storage'},
|
|
|
|
{'id': 'ssl', 'title': 'SSL certificate and key', 'condition': function(config) {
|
|
return config.PREFERRED_URL_SCHEME == 'https';
|
|
}},
|
|
|
|
{'id': 'ldap', 'title': 'LDAP Authentication', 'condition': function(config) {
|
|
return config.AUTHENTICATION_TYPE == 'LDAP';
|
|
}, 'password': true},
|
|
|
|
{'id': 'jwt', 'title': 'JWT Authentication', 'condition': function(config) {
|
|
return config.AUTHENTICATION_TYPE == 'JWT';
|
|
}, 'password': true},
|
|
|
|
{'id': 'keystone', 'title': 'Keystone Authentication', 'condition': function(config) {
|
|
return config.AUTHENTICATION_TYPE == 'Keystone';
|
|
}, 'password': true},
|
|
|
|
{'id': 'mail', 'title': 'E-mail Support', 'condition': function(config) {
|
|
return config.FEATURE_MAILING;
|
|
}},
|
|
|
|
{'id': 'github-login', 'title': 'Github (Enterprise) Authentication', 'condition': function(config) {
|
|
return config.FEATURE_GITHUB_LOGIN;
|
|
}},
|
|
|
|
{'id': 'google-login', 'title': 'Google Authentication', 'condition': function(config) {
|
|
return config.FEATURE_GOOGLE_LOGIN;
|
|
}},
|
|
|
|
{'id': 'github-trigger', 'title': 'GitHub (Enterprise) Build Triggers', 'condition': function(config) {
|
|
return config.FEATURE_GITHUB_BUILD;
|
|
}},
|
|
|
|
{'id': 'bitbucket-trigger', 'title': 'BitBucket Build Triggers', 'condition': function(config) {
|
|
return config.FEATURE_BITBUCKET_BUILD;
|
|
}},
|
|
|
|
{'id': 'gitlab-trigger', 'title': 'GitLab Build Triggers', 'condition': function(config) {
|
|
return config.FEATURE_GITLAB_BUILD;
|
|
}}
|
|
];
|
|
|
|
$scope.STORAGE_CONFIG_FIELDS = {
|
|
'LocalStorage': [
|
|
{'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/some/directory', 'kind': 'text'}
|
|
],
|
|
|
|
'S3Storage': [
|
|
{'name': 's3_access_key', 'title': 'AWS Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text'},
|
|
{'name': 's3_secret_key', 'title': 'AWS Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'},
|
|
{'name': 's3_bucket', 'title': 'S3 Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'},
|
|
{'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}
|
|
],
|
|
|
|
'GoogleCloudStorage': [
|
|
{'name': 'access_key', 'title': 'Cloud Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text'},
|
|
{'name': 'secret_key', 'title': 'Cloud Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'},
|
|
{'name': 'bucket_name', 'title': 'GCS Bucket', 'placeholder': 'my-cool-bucket', 'kind': 'text'},
|
|
{'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}
|
|
],
|
|
|
|
'RadosGWStorage': [
|
|
{'name': 'hostname', 'title': 'Rados Server Hostname', 'placeholder': 'my.rados.hostname', 'kind': 'text'},
|
|
{'name': 'is_secure', 'title': 'Is Secure', 'placeholder': 'Require SSL', 'kind': 'bool'},
|
|
{'name': 'access_key', 'title': 'Access Key', 'placeholder': 'accesskeyhere', 'kind': 'text', 'help_url': 'http://ceph.com/docs/master/radosgw/admin/'},
|
|
{'name': 'secret_key', 'title': 'Secret Key', 'placeholder': 'secretkeyhere', 'kind': 'text'},
|
|
{'name': 'bucket_name', 'title': 'Bucket Name', 'placeholder': 'my-cool-bucket', 'kind': 'text'},
|
|
{'name': 'storage_path', 'title': 'Storage Directory', 'placeholder': '/path/inside/bucket', 'kind': 'text'}
|
|
],
|
|
|
|
'SwiftStorage': [
|
|
{'name': 'auth_url', 'title': 'Swift Auth URL', 'placeholder': '', 'kind': 'text'},
|
|
{'name': 'swift_container', 'title': 'Swift Container Name', 'placeholder': 'mycontainer', 'kind': 'text'},
|
|
{'name': 'storage_path', 'title': 'Storage Path', 'placeholder': '/path/inside/container', 'kind': 'text'},
|
|
|
|
{'name': 'swift_user', 'title': 'Username', 'placeholder': 'accesskeyhere', 'kind': 'text'},
|
|
{'name': 'swift_password', 'title': 'Password/Key', 'placeholder': 'secretkeyhere', 'kind': 'text'},
|
|
|
|
{'name': 'ca_cert_path', 'title': 'CA Cert Filename', 'placeholder': 'conf/stack/swift.cert', 'kind': 'text', 'optional': true},
|
|
{'name': 'os_options', 'title': 'OS Options', 'kind': 'map',
|
|
'keys': ['tenant_id', 'auth_token', 'service_type', 'endpoint_type', 'tenant_name', 'object_storage_url', 'region_name']}
|
|
]
|
|
};
|
|
|
|
$scope.validateHostname = function(hostname) {
|
|
if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) {
|
|
return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.'
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
$scope.config = null;
|
|
$scope.mapped = {
|
|
'$hasChanges': false
|
|
};
|
|
|
|
$scope.validating = null;
|
|
$scope.savingConfiguration = false;
|
|
|
|
$scope.getServices = function(config) {
|
|
var services = [];
|
|
if (!config) { return services; }
|
|
|
|
for (var i = 0; i < $scope.SERVICES.length; ++i) {
|
|
var service = $scope.SERVICES[i];
|
|
if (!service.condition || service.condition(config)) {
|
|
services.push({
|
|
'service': service,
|
|
'status': 'validating'
|
|
});
|
|
}
|
|
}
|
|
|
|
return services;
|
|
};
|
|
|
|
$scope.validationStatus = function(serviceInfos) {
|
|
if (!serviceInfos) { return 'validating'; }
|
|
|
|
var hasError = false;
|
|
for (var i = 0; i < serviceInfos.length; ++i) {
|
|
if (serviceInfos[i].status == 'validating') {
|
|
return 'validating';
|
|
}
|
|
if (serviceInfos[i].status == 'error') {
|
|
hasError = true;
|
|
}
|
|
}
|
|
|
|
return hasError ? 'failed' : 'success';
|
|
};
|
|
|
|
$scope.cancelValidation = function() {
|
|
$('#validateAndSaveModal').modal('hide');
|
|
$scope.validating = null;
|
|
$scope.savingConfiguration = false;
|
|
};
|
|
|
|
$scope.validateService = function(serviceInfo, opt_password) {
|
|
var params = {
|
|
'service': serviceInfo.service.id
|
|
};
|
|
|
|
var data = {
|
|
'config': $scope.config,
|
|
'password': opt_password || ''
|
|
};
|
|
|
|
ApiService.scValidateConfig(data, params).then(function(resp) {
|
|
serviceInfo.status = resp.status ? 'success' : 'error';
|
|
serviceInfo.errorMessage = $.trim(resp.reason || '');
|
|
}, ApiService.errorDisplay('Could not validate configuration. Please report this error.'));
|
|
};
|
|
|
|
$scope.checkValidateAndSave = function() {
|
|
if ($scope.configform.$valid) {
|
|
$scope.validateAndSave();
|
|
return;
|
|
}
|
|
|
|
$element.find("input.ng-invalid:first")[0].scrollIntoView();
|
|
$element.find("input.ng-invalid:first").focus();
|
|
};
|
|
|
|
$scope.validateAndSave = function() {
|
|
$scope.validating = $scope.getServices($scope.config);
|
|
|
|
var requirePassword = false;
|
|
for (var i = 0; i < $scope.validating.length; ++i) {
|
|
var serviceInfo = $scope.validating[i];
|
|
if (serviceInfo.service.password) {
|
|
requirePassword = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!requirePassword) {
|
|
$scope.performValidateAndSave();
|
|
return;
|
|
}
|
|
|
|
var box = bootbox.dialog({
|
|
"message": 'Please enter your superuser password to validate your auth configuration:' +
|
|
'<form style="margin-top: 10px" action="javascript:void(0)">' +
|
|
'<input id="validatePassword" class="form-control" type="password" placeholder="Password">' +
|
|
'</form>',
|
|
"title": 'Enter Password',
|
|
"buttons": {
|
|
"success": {
|
|
"label": "Validate Config",
|
|
"className": "btn-success",
|
|
"callback": function() {
|
|
$scope.performValidateAndSave($('#validatePassword').val());
|
|
}
|
|
},
|
|
"close": {
|
|
"label": "Cancel",
|
|
"className": "btn-default",
|
|
"callback": function() {
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
box.bind('shown.bs.modal', function(){
|
|
box.find("input").focus();
|
|
box.find("form").submit(function() {
|
|
if (!$('#validatePassword').val()) { return; }
|
|
|
|
box.modal('hide');
|
|
verifyNow();
|
|
});
|
|
});
|
|
};
|
|
|
|
$scope.performValidateAndSave = function(opt_password) {
|
|
$scope.savingConfiguration = false;
|
|
$scope.validating = $scope.getServices($scope.config);
|
|
|
|
$('#validateAndSaveModal').modal({
|
|
keyboard: false,
|
|
backdrop: 'static'
|
|
});
|
|
|
|
for (var i = 0; i < $scope.validating.length; ++i) {
|
|
var serviceInfo = $scope.validating[i];
|
|
$scope.validateService(serviceInfo, opt_password);
|
|
}
|
|
};
|
|
|
|
$scope.saveConfiguration = function() {
|
|
$scope.savingConfiguration = true;
|
|
|
|
// Make sure to note that fully verified setup is completed. We use this as a signal
|
|
// in the setup tool.
|
|
$scope.config['SETUP_COMPLETE'] = true;
|
|
|
|
var data = {
|
|
'config': $scope.config,
|
|
'hostname': window.location.host
|
|
};
|
|
|
|
ApiService.scUpdateConfig(data).then(function(resp) {
|
|
$scope.savingConfiguration = false;
|
|
$scope.mapped.$hasChanges = false;
|
|
$('#validateAndSaveModal').modal('hide');
|
|
$scope.configurationSaved({'config': $scope.config});
|
|
}, ApiService.errorDisplay('Could not save configuration. Please report this error.'));
|
|
};
|
|
|
|
var gitlabSelector = function(key) {
|
|
return function(value) {
|
|
if (!value || !$scope.config) { return; }
|
|
|
|
if (!$scope.config[key]) {
|
|
$scope.config[key] = {};
|
|
}
|
|
|
|
if (value == 'enterprise') {
|
|
if ($scope.config[key]['GITLAB_ENDPOINT'] == 'https://gitlab.com/') {
|
|
$scope.config[key]['GITLAB_ENDPOINT'] = '';
|
|
}
|
|
} else if (value == 'hosted') {
|
|
$scope.config[key]['GITLAB_ENDPOINT'] = 'https://gitlab.com/';
|
|
}
|
|
};
|
|
};
|
|
|
|
var githubSelector = function(key) {
|
|
return function(value) {
|
|
if (!value || !$scope.config) { return; }
|
|
|
|
if (!$scope.config[key]) {
|
|
$scope.config[key] = {};
|
|
}
|
|
|
|
if (value == 'enterprise') {
|
|
if ($scope.config[key]['GITHUB_ENDPOINT'] == 'https://github.com/') {
|
|
$scope.config[key]['GITHUB_ENDPOINT'] = '';
|
|
}
|
|
delete $scope.config[key]['API_ENDPOINT'];
|
|
} else if (value == 'hosted') {
|
|
$scope.config[key]['GITHUB_ENDPOINT'] = 'https://github.com/';
|
|
$scope.config[key]['API_ENDPOINT'] = 'https://api.github.com/';
|
|
}
|
|
};
|
|
};
|
|
|
|
var getKey = function(config, path) {
|
|
if (!config) {
|
|
return null;
|
|
}
|
|
|
|
var parts = path.split('.');
|
|
var current = config;
|
|
for (var i = 0; i < parts.length; ++i) {
|
|
var part = parts[i];
|
|
if (!current[part]) { return null; }
|
|
current = current[part];
|
|
}
|
|
return current;
|
|
};
|
|
|
|
var initializeMappedLogic = function(config) {
|
|
var gle = getKey(config, 'GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT');
|
|
var gte = getKey(config, 'GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT');
|
|
|
|
$scope.mapped['GITHUB_LOGIN_KIND'] = gle == 'https://github.com/' ? 'hosted' : 'enterprise';
|
|
$scope.mapped['GITHUB_TRIGGER_KIND'] = gte == 'https://github.com/' ? 'hosted' : 'enterprise';
|
|
|
|
var glabe = getKey(config, 'GITLAB_TRIGGER_KIND.GITHUB_ENDPOINT');
|
|
$scope.mapped['GITLAB_TRIGGER_KIND'] = glabe == 'https://gitlab.com/' ? 'hosted' : 'enterprise';
|
|
|
|
$scope.mapped['redis'] = {};
|
|
$scope.mapped['redis']['host'] = getKey(config, 'BUILDLOGS_REDIS.host') || getKey(config, 'USER_EVENTS_REDIS.host');
|
|
$scope.mapped['redis']['port'] = getKey(config, 'BUILDLOGS_REDIS.port') || getKey(config, 'USER_EVENTS_REDIS.port');
|
|
$scope.mapped['redis']['password'] = getKey(config, 'BUILDLOGS_REDIS.password') || getKey(config, 'USER_EVENTS_REDIS.password');
|
|
};
|
|
|
|
var redisSetter = function(keyname) {
|
|
return function(value) {
|
|
if (value == null || !$scope.config) { return; }
|
|
|
|
if (!$scope.config['BUILDLOGS_REDIS']) {
|
|
$scope.config['BUILDLOGS_REDIS'] = {};
|
|
}
|
|
|
|
if (!$scope.config['USER_EVENTS_REDIS']) {
|
|
$scope.config['USER_EVENTS_REDIS'] = {};
|
|
}
|
|
|
|
if (!value) {
|
|
delete $scope.config['BUILDLOGS_REDIS'][keyname];
|
|
delete $scope.config['USER_EVENTS_REDIS'][keyname];
|
|
return;
|
|
}
|
|
|
|
$scope.config['BUILDLOGS_REDIS'][keyname] = value;
|
|
$scope.config['USER_EVENTS_REDIS'][keyname] = value;
|
|
};
|
|
};
|
|
|
|
// Add mapped logic.
|
|
$scope.$watch('mapped.GITHUB_LOGIN_KIND', githubSelector('GITHUB_LOGIN_CONFIG'));
|
|
$scope.$watch('mapped.GITHUB_TRIGGER_KIND', githubSelector('GITHUB_TRIGGER_CONFIG'));
|
|
$scope.$watch('mapped.GITLAB_TRIGGER_KIND', gitlabSelector('GITLAB_TRIGGER_KIND'));
|
|
|
|
$scope.$watch('mapped.redis.host', redisSetter('host'));
|
|
$scope.$watch('mapped.redis.port', redisSetter('port'));
|
|
$scope.$watch('mapped.redis.password', redisSetter('password'));
|
|
|
|
// Add a watch to remove any fields not allowed by the current storage configuration.
|
|
// We have to do this otherwise extra fields (which are not allowed) can end up in the
|
|
// configuration.
|
|
$scope.$watch('config.DISTRIBUTED_STORAGE_CONFIG.local[0]', function(value) {
|
|
// Remove any fields not associated with the current kind.
|
|
if (!value || !$scope.STORAGE_CONFIG_FIELDS[value]
|
|
|| !$scope.config.DISTRIBUTED_STORAGE_CONFIG
|
|
|| !$scope.config.DISTRIBUTED_STORAGE_CONFIG.local
|
|
|| !$scope.config.DISTRIBUTED_STORAGE_CONFIG.local[1]) { return; }
|
|
|
|
var allowedFields = $scope.STORAGE_CONFIG_FIELDS[value];
|
|
var configObject = $scope.config.DISTRIBUTED_STORAGE_CONFIG.local[1];
|
|
|
|
// Remove any fields not allowed.
|
|
for (var fieldName in configObject) {
|
|
if (!configObject.hasOwnProperty(fieldName)) {
|
|
continue;
|
|
}
|
|
|
|
var isValidField = $.grep(allowedFields, function(field) {
|
|
return field.name == fieldName;
|
|
}).length > 0;
|
|
|
|
if (!isValidField) {
|
|
delete configObject[fieldName];
|
|
}
|
|
}
|
|
|
|
// Set any boolean fields to false.
|
|
for (var i = 0; i < allowedFields.length; ++i) {
|
|
if (allowedFields[i].kind == 'bool') {
|
|
configObject[allowedFields[i].name] = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
$scope.$watch('config', function(value) {
|
|
$scope.mapped['$hasChanges'] = true;
|
|
}, true);
|
|
|
|
$scope.$watch('isActive', function(value) {
|
|
if (!value) { return; }
|
|
|
|
ApiService.scGetConfig().then(function(resp) {
|
|
$scope.config = resp['config'] || {};
|
|
initializeMappedLogic($scope.config);
|
|
$scope.mapped['$hasChanges'] = false;
|
|
}, ApiService.errorDisplay('Could not load config'));
|
|
});
|
|
}
|
|
};
|
|
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configParsedField', function ($timeout) {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-parsed-field.html',
|
|
replace: false,
|
|
transclude: true,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding',
|
|
'parser': '&parser',
|
|
'serializer': '&serializer'
|
|
},
|
|
controller: function($scope, $element, $transclude) {
|
|
$scope.childScope = null;
|
|
|
|
$transclude(function(clone, scope) {
|
|
$scope.childScope = scope;
|
|
$scope.childScope['fields'] = {};
|
|
$element.append(clone);
|
|
});
|
|
|
|
$scope.childScope.$watch('fields', function(value) {
|
|
// Note: We need the timeout here because Angular starts the digest of the
|
|
// parent scope AFTER the child scope, which means it can end up one action
|
|
// behind. The timeout ensures that the parent scope will be fully digest-ed
|
|
// and then we update the binding. Yes, this is a hack :-/.
|
|
$timeout(function() {
|
|
$scope.binding = $scope.serializer({'fields': value});
|
|
});
|
|
}, true);
|
|
|
|
$scope.$watch('binding', function(value) {
|
|
var parsed = $scope.parser({'value': value});
|
|
for (var key in parsed) {
|
|
if (parsed.hasOwnProperty(key)) {
|
|
$scope.childScope['fields'][key] = parsed[key];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configVariableField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-variable-field.html',
|
|
replace: false,
|
|
transclude: true,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.sections = {};
|
|
$scope.currentSection = null;
|
|
|
|
$scope.setSection = function(section) {
|
|
$scope.binding = section.value;
|
|
};
|
|
|
|
this.addSection = function(section, element) {
|
|
$scope.sections[section.value] = {
|
|
'title': section.valueTitle,
|
|
'value': section.value,
|
|
'element': element
|
|
};
|
|
|
|
element.hide();
|
|
|
|
if (!$scope.binding) {
|
|
$scope.binding = section.value;
|
|
}
|
|
};
|
|
|
|
$scope.$watch('binding', function(binding) {
|
|
if (!binding) { return; }
|
|
|
|
if ($scope.currentSection) {
|
|
$scope.currentSection.element.hide();
|
|
}
|
|
|
|
if ($scope.sections[binding]) {
|
|
$scope.sections[binding].element.show();
|
|
$scope.currentSection = $scope.sections[binding];
|
|
}
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('variableSection', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-variable-field.html',
|
|
priority: 1,
|
|
require: '^configVariableField',
|
|
replace: false,
|
|
transclude: true,
|
|
restrict: 'C',
|
|
scope: {
|
|
'value': '@value',
|
|
'valueTitle': '@valueTitle'
|
|
},
|
|
controller: function($scope, $element) {
|
|
var parentCtrl = $element.parent().controller('configVariableField');
|
|
parentCtrl.addSection($scope, $element);
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configListField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-list-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding',
|
|
'placeholder': '@placeholder',
|
|
'defaultValue': '@defaultValue',
|
|
'itemTitle': '@itemTitle'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.removeItem = function(item) {
|
|
var index = $scope.binding.indexOf(item);
|
|
if (index >= 0) {
|
|
$scope.binding.splice(index, 1);
|
|
}
|
|
};
|
|
|
|
$scope.addItem = function() {
|
|
if (!$scope.newItemName) {
|
|
return;
|
|
}
|
|
|
|
if (!$scope.binding) {
|
|
$scope.binding = [];
|
|
}
|
|
|
|
if ($scope.binding.indexOf($scope.newItemName) >= 0) {
|
|
return;
|
|
}
|
|
|
|
$scope.binding.push($scope.newItemName);
|
|
$scope.newItemName = null;
|
|
};
|
|
|
|
$scope.$watch('binding', function(binding) {
|
|
if (!binding && $scope.defaultValue) {
|
|
$scope.binding = eval($scope.defaultValue);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configFileField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-file-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'filename': '@filename',
|
|
'skipCheckFile': '@skipCheckFile',
|
|
'hasFile': '=hasFile'
|
|
},
|
|
controller: function($scope, $element, Restangular, $upload) {
|
|
$scope.hasFile = false;
|
|
|
|
$scope.onFileSelect = function(files) {
|
|
if (files.length < 1) {
|
|
$scope.hasFile = false;
|
|
return;
|
|
}
|
|
|
|
$scope.uploadProgress = 0;
|
|
$scope.upload = $upload.upload({
|
|
url: '/api/v1/superuser/config/file/' + $scope.filename,
|
|
method: 'POST',
|
|
data: {'_csrf_token': window.__token},
|
|
file: files[0],
|
|
}).progress(function(evt) {
|
|
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total);
|
|
if ($scope.uploadProgress == 100) {
|
|
$scope.uploadProgress = null;
|
|
$scope.hasFile = true;
|
|
}
|
|
}).success(function(data, status, headers, config) {
|
|
$scope.uploadProgress = null;
|
|
$scope.hasFile = true;
|
|
});
|
|
};
|
|
|
|
var loadStatus = function(filename) {
|
|
Restangular.one('superuser/config/file/' + filename).get().then(function(resp) {
|
|
$scope.hasFile = resp['exists'];
|
|
});
|
|
};
|
|
|
|
if ($scope.filename && $scope.skipCheckFile != "true") {
|
|
loadStatus($scope.filename);
|
|
}
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configBoolField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-bool-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding'
|
|
},
|
|
controller: function($scope, $element) {
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configNumericField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-numeric-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding',
|
|
'placeholder': '@placeholder',
|
|
'defaultValue': '@defaultValue'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.bindinginternal = 0;
|
|
|
|
$scope.$watch('binding', function(binding) {
|
|
if ($scope.binding == 0 && $scope.defaultValue) {
|
|
$scope.binding = $scope.defaultValue * 1;
|
|
}
|
|
|
|
$scope.bindinginternal = $scope.binding;
|
|
});
|
|
|
|
$scope.$watch('bindinginternal', function(binding) {
|
|
var newValue = $scope.bindinginternal * 1;
|
|
if (isNaN(newValue)) {
|
|
newValue = 0;
|
|
}
|
|
$scope.binding = newValue;
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configContactsField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-contacts-field.html',
|
|
priority: 1,
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding'
|
|
},
|
|
controller: function($scope, $element) {
|
|
var padItems = function(items) {
|
|
// Remove the last item if both it and the second to last items are empty.
|
|
if (items.length > 1 && !items[items.length - 2].value && !items[items.length - 1].value) {
|
|
items.splice(items.length - 1, 1);
|
|
return;
|
|
}
|
|
|
|
// If the last item is non-empty, add a new item.
|
|
if (items.length == 0 || items[items.length - 1].value) {
|
|
items.push({'value': ''});
|
|
return;
|
|
}
|
|
};
|
|
|
|
$scope.itemHash = null;
|
|
$scope.$watch('items', function(items) {
|
|
if (!items) { return; }
|
|
padItems(items);
|
|
|
|
var itemHash = '';
|
|
var binding = [];
|
|
for (var i = 0; i < items.length; ++i) {
|
|
var item = items[i];
|
|
if (item.value && (URI(item.value).host() || URI(item.value).path())) {
|
|
binding.push(item.value);
|
|
itemHash += item.value;
|
|
}
|
|
}
|
|
|
|
$scope.itemHash = itemHash;
|
|
$scope.binding = binding;
|
|
}, true);
|
|
|
|
$scope.$watch('binding', function(binding) {
|
|
var current = binding || [];
|
|
var items = [];
|
|
var itemHash = '';
|
|
for (var i = 0; i < current.length; ++i) {
|
|
items.push({'value': current[i]})
|
|
itemHash += current[i];
|
|
}
|
|
|
|
if ($scope.itemHash != itemHash) {
|
|
$scope.items = items;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configContactField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-contact-field.html',
|
|
priority: 1,
|
|
replace: false,
|
|
transclude: true,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.kind = null;
|
|
$scope.value = null;
|
|
|
|
var updateBinding = function() {
|
|
if ($scope.value == null) { return; }
|
|
var value = $scope.value || '';
|
|
|
|
switch ($scope.kind) {
|
|
case 'mailto':
|
|
$scope.binding = 'mailto:' + value;
|
|
return;
|
|
|
|
case 'tel':
|
|
$scope.binding = 'tel:' + value;
|
|
return;
|
|
|
|
case 'irc':
|
|
$scope.binding = 'irc://' + value;
|
|
return;
|
|
|
|
default:
|
|
$scope.binding = value;
|
|
return;
|
|
}
|
|
};
|
|
|
|
$scope.$watch('kind', updateBinding);
|
|
$scope.$watch('value', updateBinding);
|
|
|
|
$scope.$watch('binding', function(value) {
|
|
if (!value) {
|
|
$scope.kind = null;
|
|
$scope.value = null;
|
|
return;
|
|
}
|
|
|
|
var uri = URI(value);
|
|
$scope.kind = uri.scheme();
|
|
|
|
switch ($scope.kind) {
|
|
case 'mailto':
|
|
case 'tel':
|
|
$scope.value = uri.path();
|
|
break;
|
|
|
|
case 'irc':
|
|
$scope.value = value.substr('irc://'.length);
|
|
break;
|
|
|
|
default:
|
|
$scope.kind = 'http';
|
|
$scope.value = value;
|
|
break;
|
|
}
|
|
});
|
|
|
|
$scope.getPlaceholder = function(kind) {
|
|
switch (kind) {
|
|
case 'mailto':
|
|
return 'some@example.com';
|
|
|
|
case 'tel':
|
|
return '555-555-5555';
|
|
|
|
case 'irc':
|
|
return 'myserver:port/somechannel';
|
|
|
|
default:
|
|
return 'http://some/url';
|
|
}
|
|
};
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configMapField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-map-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding',
|
|
'keys': '=keys'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.newKey = null;
|
|
$scope.newValue = null;
|
|
|
|
$scope.hasValues = function(binding) {
|
|
return binding && Object.keys(binding).length;
|
|
};
|
|
|
|
$scope.removeKey = function(key) {
|
|
delete $scope.binding[key];
|
|
};
|
|
|
|
$scope.addEntry = function() {
|
|
if (!$scope.newKey || !$scope.newValue) { return; }
|
|
|
|
$scope.binding = $scope.binding || {};
|
|
$scope.binding[$scope.newKey] = $scope.newValue;
|
|
$scope.newKey = null;
|
|
$scope.newValue = null;
|
|
}
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
})
|
|
|
|
.directive('configStringField', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/config/config-string-field.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'binding': '=binding',
|
|
'placeholder': '@placeholder',
|
|
'pattern': '@pattern',
|
|
'defaultValue': '@defaultValue',
|
|
'validator': '&validator',
|
|
'isOptional': '=isOptional'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.getRegexp = function(pattern) {
|
|
if (!pattern) {
|
|
pattern = '.*';
|
|
}
|
|
return new RegExp(pattern);
|
|
};
|
|
|
|
$scope.$watch('binding', function(binding) {
|
|
if (!binding && $scope.defaultValue) {
|
|
$scope.binding = $scope.defaultValue;
|
|
}
|
|
|
|
$scope.errorMessage = $scope.validator({'value': binding || ''});
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
}); |