From 421288046ed36c7fb0b45c2de6858b0fa3c462cd Mon Sep 17 00:00:00 2001 From: kunw Date: Mon, 13 Feb 2017 13:13:49 +0800 Subject: [PATCH] Add configuration features to UI. --- src/ui/static/resources/css/admin-options.css | 6 +- .../configuration.directive.html | 267 +++++++++++--- .../configuration.directive.js | 348 +++++++++++++++++- .../system-management.directive.js | 1 + .../navigation-admin-options.directive.html | 8 +- .../navigation-admin-options.directive.js | 13 +- .../navigation-details.directive.js | 11 +- .../js/services/i18n/locale_messages_en-US.js | 54 ++- .../js/services/i18n/locale_messages_zh-CN.js | 54 ++- .../services.system-configuration.js | 43 +++ src/ui/views/navigation-detail.htm | 2 +- src/ui/views/sections/script-include.htm | 1 + 12 files changed, 725 insertions(+), 83 deletions(-) create mode 100644 src/ui/static/resources/js/services/system-info/services.system-configuration.js diff --git a/src/ui/static/resources/css/admin-options.css b/src/ui/static/resources/css/admin-options.css index 2cd2966f7..a7830e15c 100644 --- a/src/ui/static/resources/css/admin-options.css +++ b/src/ui/static/resources/css/admin-options.css @@ -14,7 +14,7 @@ */ .switch-pane-admin-options { display: inline; - width: 245px; + width: 340px; float: right; list-style-type: none; } @@ -28,4 +28,8 @@ .switch-pane-admin-options li .active { border-bottom: 2px solid rgb(0, 84, 190); font-weight: bold; +} + +.inline-help-config { + padding: 6px; } \ No newline at end of file diff --git a/src/ui/static/resources/js/components/system-management/configuration.directive.html b/src/ui/static/resources/js/components/system-management/configuration.directive.html index 3bde51eb8..4e330755c 100644 --- a/src/ui/static/resources/js/components/system-management/configuration.directive.html +++ b/src/ui/static/resources/js/components/system-management/configuration.directive.html @@ -12,65 +12,224 @@ See the License for the specific language governing permissions and limitations under the License. --> -
-
-
System Settings
-
-
-
-
- -
- -
- Host name is required. +
+ + +
+
+ +
+ +
+ +
+
+ +
-
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+ // 'timeout_is_required' | tr // + // 'invalid_timeout' | tr // + // 'invalid_timeout' | tr // +
+
+
+
+ +
+ +
+
+ +
+
+
+
+ // vm.pingMessage // + // vm.pingMessage // +
+
+
+
+
+ + + +
+
+
+
-
- -
- -
- Url protocol is required. +
+
+
+ +
+ +
+
+ +
-
-
-
- -
- -
- Email server is required. +
+ +
+ +
+ // 'invalid_port_number' | tr // + // 'invalid_port_number' | tr // +
+
+
+ +
-
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ + +
+
+
+
-
- -
- -
- LDAP URL is required. +
+
+
+ +
+ +
+
+ +
-
+
+ +
+ +
+
+ +
+
+
+
+
+ + +
+
+
+
-
-
Registration
-
-
-
-
- -
- -
-
-
-
- - -
-
-
- \ No newline at end of file +
\ No newline at end of file diff --git a/src/ui/static/resources/js/components/system-management/configuration.directive.js b/src/ui/static/resources/js/components/system-management/configuration.directive.js index bc21345ad..45db3711d 100644 --- a/src/ui/static/resources/js/components/system-management/configuration.directive.js +++ b/src/ui/static/resources/js/components/system-management/configuration.directive.js @@ -18,51 +18,365 @@ angular .module('harbor.system.management') + .constant('defaultPassword', '12345678') .directive('configuration', configuration); - ConfigurationController.$inject = []; + ConfigurationController.$inject = ['$scope', 'ConfigurationService', 'defaultPassword', '$filter', 'trFilter']; - function ConfigurationController() { + function ConfigurationController($scope, ConfigurationService, defaultPassword, $filter, trFilter) { var vm = this; - - vm.registrationOptions = [ - { - 'name': 'on', - 'value': true + + vm.toggleBooleans = [ + { + 'name': 'True', + 'value': true }, { - 'name': 'off', + 'name': 'False', 'value': false } ]; - vm.currentRegistration = { - 'name': 'on', - 'value': true + + vm.toggleCustoms = [ + { + 'name': 'Admin Only', + 'value': 'adminonly', + }, + { + 'name': 'Everyone', + 'value': 'everyone' + } + ]; + + vm.supportedAuths = [ + { + 'name': 'DB auth', + 'value': 'db_auth' + }, + { + 'name': 'LDAP auth', + 'value': 'ldap_auth' + } + ]; + + var confKeyDefinitions = { + 'auth_mode': { type: 'auth', attr: 'authMode' }, + 'self_registration': { type: 'auth', attr: 'selfRegistration' }, + 'ldap_url': { type: 'auth', attr: 'ldapURL' }, + 'ldap_search_dn': { type: 'auth', attr: 'ldapSearchDN' }, + 'ldap_search_password': { type: 'auth', attr: 'ldapSearchPassword' }, + 'ldap_base_dn': { type: 'auth', attr: 'ldapBaseDN' }, + 'ldap_uid': { type: 'auth', attr: 'ldapUID' }, + 'ldap_filter': { type: 'auth', attr: 'ldapFilter' }, + 'ldap_connection_timeout': { type: 'auth', attr: 'ldapConnectionTimeout' }, + 'ldap_scope': { type: 'auth', attr: 'ldapScope' }, + 'email_host': { type: 'email', attr: 'server' }, + 'email_port': { type: 'email', attr: 'serverPort' }, + 'email_username': { type: 'email', attr: 'username' }, + 'email_password': { type: 'email', attr: 'password' }, + 'email_from': { type: 'email', attr: 'from' }, + 'email_ssl': { type: 'email', attr: 'SSL' }, + 'project_creation_restriction': { type: 'system', attr: 'projectCreationRestriction' }, + 'verify_remote_cert': { type: 'system', attr: 'verifyRemoteCert' } }; - vm.changeSettings = changeSettings; + $scope.auth = {}; + $scope.email = {}; + $scope.system = {}; - vm.selectRegistration = selectRegistration; + vm.retrieve = retrieve; - function selectRegistration() { + vm.saveAuthConf = saveAuthConf; + vm.saveEmailConf = saveEmailConf; + vm.saveSystemConf = saveSystemConf; + + vm.gatherUpdateItems = gatherUpdateItems; + vm.clearUp = clearUp; + vm.hasChanged = hasChanged; + vm.setMaskPassword = setMaskPassword; + vm.undo = undo; + + vm.pingLDAP = pingLDAP; + vm.pingTIP = false; + vm.isError = false; + vm.pingMessage = ''; + + vm.retrieve(); + + function retrieve() { + + vm.ldapSearchPasswordChanged = false; + vm.emailPasswordChanged = false; + vm.changedItems = {}; + vm.updatedItems = {}; + vm.warning = {}; + vm.editable = {}; + ConfigurationService + .get() + .then(getConfigurationSuccess, getConfigurationFailed); + } + + function getConfigurationSuccess(response) { + var data = response.data || []; + for(var key in data) { + var mappedDef = keyMapping(key); + if(mappedDef) { + $scope[mappedDef['type']][mappedDef['attr']] = { 'target': mappedDef['type'] + '.' + mappedDef['attr'], 'data': valueMapping(data[key]['value']) }; + $scope.$watch(mappedDef['type'] + '.' + mappedDef['attr'], onChangedCallback, true); + $scope[mappedDef['type']][mappedDef['attr']]['origin'] = { 'target': mappedDef['type'] + '.' + mappedDef['attr'], 'data': valueMapping(data[key]['value']) }; + vm.editable[mappedDef['type'] + '.' + mappedDef['attr']] = data[key]['editable']; + } + } + + $scope.auth.ldapSearchPassword = { 'target': 'auth.ldapSearchPassword', 'data': defaultPassword}; + $scope.email.password = { 'target': 'email.password', 'data': defaultPassword}; + + $scope.$watch('auth.ldapSearchPassword', onChangedCallback, true); + $scope.$watch('email.password', onChangedCallback, true); + + $scope.auth.ldapSearchPassword.actual = { 'target': 'auth.ldapSearchPassword', 'data': ''}; + $scope.email.password.actual = { 'target': 'email.password', 'data': ''}; + } + + function keyMapping(confKey) { + for (var key in confKeyDefinitions) { + if (confKey === key) { + return confKeyDefinitions[key]; + } + } + return null; } - function changeSettings(system) { - console.log(system); + function valueMapping(value) { + switch(value) { + case true: + return vm.toggleBooleans[0]; + case false: + return vm.toggleBooleans[1]; + case 'db_auth': + return vm.supportedAuths[0]; + case 'ldap_auth': + return vm.supportedAuths[1]; + case 'adminonly': + return vm.toggleCustoms[0]; + case 'everyone': + return vm.toggleCustoms[1]; + default: + return value; + } } + + function onChangedCallback(current, previous) { + if(!angular.equals(current, previous)) { + var compositeKey = current.target.split("."); + vm.changed = false; + var changedData = {}; + switch(current.target) { + case 'auth.ldapSearchPassword': + if(vm.ldapSearchPasswordChanged) { + vm.changed = true; + changedData = $scope.auth.ldapSearchPassword.actual.data; + } + break; + case 'email.password': + if(vm.emailPasswordChanged) { + vm.changed = true; + changedData = $scope.email.password.actual.data; + } + break; + default: + if(!angular.equals(current.data, $scope[compositeKey[0]][compositeKey[1]]['origin']['data'])) { + vm.changed = true; + changedData = current.data; + } + } + if(vm.changed) { + vm.changedItems[current.target] = changedData; + vm.warning[current.target] = true; + } else { + delete vm.changedItems[current.target]; + vm.warning[current.target] = false; + } + } + } + + function getConfigurationFailed(response) { + console.log('Failed to get configurations.'); + } + + function updateConfigurationSuccess(response) { + $scope.$emit('modalTitle', $filter('tr')('update_configuration_title', [])); + $scope.$emit('modalMessage', $filter('tr')('successful_update_configuration', [])); + var emitInfo = { + 'confirmOnly': true, + 'contentType': 'text/plain', + 'action' : function() { + vm.retrieve(); + } + }; + $scope.$emit('raiseInfo', emitInfo); + console.log('Updated system configuration successfully.'); + } + + function updateConfigurationFailed() { + $scope.$emit('modalTitle', $filter('tr')('update_configuration_title', [])); + $scope.$emit('modalMessage', $filter('tr')('failed_to_update_configuration', [])); + $scope.$emit('raiseError', true); + console.log('Failed to update system configurations.'); + } + + function gatherUpdateItems() { + vm.updatedItems = {}; + for(var key in confKeyDefinitions) { + var value = confKeyDefinitions[key]; + var compositeKey = value.type + '.' + value.attr; + for(var itemKey in vm.changedItems) { + var item = vm.changedItems[itemKey]; + if (compositeKey === itemKey) { + (typeof item === 'object' && item) ? vm.updatedItems[key] = ((typeof item.value === 'boolean') ? Number(item.value) + '' : item.value) : vm.updatedItems[key] = String(item) || ''; + } + } + } + } + + function saveAuthConf(auth) { + vm.gatherUpdateItems(); + console.log('auth changed:' + angular.toJson(vm.updatedItems)); + ConfigurationService + .update(vm.updatedItems) + .then(updateConfigurationSuccess, updateConfigurationFailed); + } + + function saveEmailConf(email) { + vm.gatherUpdateItems(); + console.log('email changed:' + angular.toJson(vm.updatedItems)); + ConfigurationService + .update(vm.updatedItems) + .then(updateConfigurationSuccess, updateConfigurationFailed); + } + + function saveSystemConf(system) { + vm.gatherUpdateItems(); + console.log('system changed:' + angular.toJson(vm.updatedItems)); + ConfigurationService + .update(vm.updatedItems) + .then(updateConfigurationSuccess, updateConfigurationFailed); + } + + function clearUp(input) { + switch(input.target) { + case 'auth.ldapSearchPassword': + $scope.auth.ldapSearchPassword.data = ''; + break; + case 'email.password': + $scope.email.password.data = ''; + break; + } + } + + function hasChanged(input) { + switch(input.target) { + case 'auth.ldapSearchPassword': + vm.ldapSearchPasswordChanged = true; + $scope.auth.ldapSearchPassword.actual.data = input.data; + break; + case 'email.password': + vm.emailPasswordChanged = true; + $scope.email.password.actual.data = input.data; + break; + } + } + + function setMaskPassword(input) { + input.data = defaultPassword; + } + + function undo() { + vm.retrieve(); + } + + function pingLDAP(auth) { + var keyset = [ + {'name': 'ldapURL' , 'attr': 'ldap_url'}, + {'name': 'ldapSearchDN', 'attr': 'ldap_search_dn'}, + {'name': 'ldapSearchPassword' , 'attr': 'ldap_search_password'}, + {'name': 'ldapConnectionTimeout', 'attr': 'ldap_connection_timeout'} + ]; + var ldapConf = {}; + + for(var i = 0; i < keyset.length; i++) { + var key = keyset[i]; + var value; + if(key.name === 'ldapSearchPassword') { + value = auth[key.name]['actual']['data']; + }else { + value = auth[key.name]['data']; + } + ldapConf[key.attr] = value; + } + + vm.pingMessage = $filter('tr')('pinging_target'); + vm.pingTIP = true; + vm.isError = false; + + ConfigurationService + .pingLDAP(ldapConf) + .then(pingLDAPSuccess, pingLDAPFailed); + } + + function pingLDAPSuccess(response) { + vm.pingTIP = false; + vm.pingMessage = $filter('tr')('successful_ping_target'); + } + + function pingLDAPFailed(response) { + vm.isError = true; + vm.pingTIP = false; + vm.pingMessage = $filter('tr')('failed_to_ping_target'); + console.log('Failed to ping LDAP target:' + response.data); + } + } - function configuration() { + configuration.$inject = ['$filter', 'trFilter']; + + function configuration($filter, trFilter) { var directive = { 'restrict': 'E', 'templateUrl': '/static/resources/js/components/system-management/configuration.directive.html', 'scope': true, + 'link': link, 'controller': ConfigurationController, 'controllerAs': 'vm', 'bindToController': true }; return directive; + + function link(scope, element, attrs, ctrl) { + element.find('#ulTabHeader a').on('click', function(e) { + e.preventDefault(); + ctrl.gatherUpdateItems(); + if(!angular.equals(ctrl.updatedItems, {})) { + var emitInfo = { + 'confirmOnly': true, + 'contentType': 'text/html', + 'action' : function() { + return; + } + }; + scope.$emit('modalTitle', $filter('tr')('caution')); + scope.$emit('modalMessage', $filter('tr')('please_save_changes')); + scope.$emit('raiseInfo', emitInfo); + scope.$apply(); + e.stopPropagation(); + }else{ + $(this).tab('show'); + } + + }); + element.find('#ulTabHeader a:first').trigger('click'); + } } })(); \ No newline at end of file diff --git a/src/ui/static/resources/js/components/system-management/system-management.directive.js b/src/ui/static/resources/js/components/system-management/system-management.directive.js index 68d17938d..f04f54549 100644 --- a/src/ui/static/resources/js/components/system-management/system-management.directive.js +++ b/src/ui/static/resources/js/components/system-management/system-management.directive.js @@ -29,6 +29,7 @@ switch(currentTarget) { case 'destinations': case 'replication': + case 'configuration': $location.path('/' + currentTarget); vm.target = currentTarget; break; diff --git a/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.html b/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.html index ecbd0fafe..ab1641250 100644 --- a/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.html +++ b/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.html @@ -12,10 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -
    + \ No newline at end of file diff --git a/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.js b/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.js index 89de7e4a4..ffc293f46 100644 --- a/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.js +++ b/src/ui/static/resources/js/layout/navigation/navigation-admin-options.directive.js @@ -27,7 +27,9 @@ vm.path = $location.path(); } - function navigationAdminOptions() { + navigationAdminOptions.$inject = ['I18nService']; + + function navigationAdminOptions(I18nService) { var directive = { 'restrict': 'E', 'templateUrl': '/static/resources/js/layout/navigation/navigation-admin-options.directive.html', @@ -44,7 +46,14 @@ function link(scope, element, attrs, ctrl) { var visited = ctrl.path.substring(1); console.log('visited:' + visited); - + + var lang = I18nService().getCurrentLanguage(); + ctrl.customPos = {}; + + if(lang === 'zh-CN') { + ctrl.customPos = {'position': 'relative', 'left': '14%'}; + } + if(visited) { element.find('a[tag="' + visited + '"]').addClass('active'); }else{ diff --git a/src/ui/static/resources/js/layout/navigation/navigation-details.directive.js b/src/ui/static/resources/js/layout/navigation/navigation-details.directive.js index 5d6b06d45..b11cc2623 100644 --- a/src/ui/static/resources/js/layout/navigation/navigation-details.directive.js +++ b/src/ui/static/resources/js/layout/navigation/navigation-details.directive.js @@ -35,7 +35,9 @@ vm.path = $location.path(); } - function navigationDetails() { + navigationDetails.$inject = ['I18nService']; + + function navigationDetails(I18nService) { var directive = { restrict: 'E', templateUrl: '/navigation_detail?timestamp=' + new Date().getTime(), @@ -53,6 +55,13 @@ function link(scope, element, attrs, ctrl) { + var lang = I18nService().getCurrentLanguage(); + ctrl.customPos = {}; + + if(lang === 'zh-CN') { + ctrl.customPos = {'position': 'relative', 'left': '8%'}; + } + var visited = ctrl.path.substring(1); if(visited) { diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js index de5a02338..650f75aa5 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js @@ -287,5 +287,57 @@ var locale_messages = { 'confirm_to_toggle_enabled_policy': 'After enabling the replication policy, all repositories under the project will be replicated to the destination registry. Please confirm to continue.', 'confirm_to_toggle_disabled_policy_title': 'Disable Policy', 'confirm_to_toggle_disabled_policy': 'After disabling the policy, all unfinished replication jobs of this policy will be stopped and canceled. Please confirm to continue.', - 'begin_date_is_later_than_end_date': 'Begin date should not be later than end date.' + 'begin_date_is_later_than_end_date': 'Begin date should not be later than end date.', + 'configuration': 'Configuration', + 'authentication': 'Authentication', + 'email_settings': 'Email Settings', + 'system_settings': 'System Settings', + 'authentication_mode': 'Authentication Mode', + 'authentication_mode_desc': 'The default authentication mode is db_auth. Set it to ldap_auth when users\' credentials are stored in an LDAP or AD server. Note: this option can only be set once.', + 'self_registration': 'Self Registration', + 'self_registration_desc': 'Determine whether the self-registration is allowed or not. Set this to off to disable a user\'s self-registration in Harbor. This flag has no effect when users are stored in LDAP or AD.', + 'ldap_url': 'LDAP URL', + 'ldap_url_desc': 'The URL of an LDAP/AD server.', + 'ldap_search_dn': 'LDAP Search DN', + 'ldap_search_dn_desc': 'A user\'s DN who has the permission to search the LDAP/AD server. Leave blank if your LDAP/AD server supports anonymous search, otherwise you should configure this DN and LDAP Search Password.', + 'ldap_search_password': 'LDAP Search Password', + 'ldap_search_password_desc': 'The password of the user for LDAP search. Leave blank if your LDAP/AD server supports anonymous search.', + 'ldap_base_dn': 'LDAP Base DN', + 'ldap_base_dn_desc': 'The base DN of a node from which to look up a user for authentication. The search scope includes subtree of the node.', + 'ldap_uid': 'LDAP UID', + 'ldap_uid_desc': 'The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD server.', + 'ldap_filter': 'LDAP Filter', + 'ldap_filter_desc': 'Search filter for LDAP/AD, make sure the syntax of the filter is correct.', + 'ldap_connection_timeout': 'LDAP Connection Timeout(sec.)', + 'ldap_scope': 'LDAP Scope', + 'ldap_scope_desc': 'The scope to search for users.(1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE)', + 'email_server': 'Email Server', + 'email_server_desc': 'The mail server to send out emails to reset password.', + 'email_server_port': 'Email Server Port', + 'email_server_port_desc': 'The port of mail server.', + 'email_username': 'Email Username', + 'email_username_desc': 'The user from whom the password reset email is sent. Usually this is a system email address.', + 'email_password': 'Email Password', + 'email_password_desc': 'The password of the user from whom the password reset email is sent.', + 'email_from': 'Email From', + 'email_from_desc': 'The name of the email sender.', + 'email_ssl': 'Email SSL', + 'email_ssl_desc': 'Whether to enable secure mail transmission.', + 'project_creation_restriction': 'Project Creation Restriction', + 'project_creation_restriction_desc': 'The flag to control what users have permission to create projects. Be default everyone can create a project, set to "adminonly" such that only admin can create project.', + 'verify_remote_cert': 'Verify Remote Cert.', + 'verify_remote_cert_desc': 'Determine whether the image replication should verify the certificate of a remote Harbor registry. Set this flag to off when the remote registry uses a self-signed or untrusted certificate.', + 'max_job_workers': 'Max Job Workers', + 'max_job_workers_desc': 'Maximum number of job workers in job service.', + 'please_save_changes': 'Please save changes before leaving this page.', + 'undo': 'Undo', + 'invalid_port_number': 'Invalid port number.', + 'max_job_workers_is_required': 'Maxystem job workers number is required.', + 'timeout_is_required': 'Timeout value is required.', + 'invalid_timeout': 'Invalid timeout value.', + 'ldap_scope_is_required': 'Scope is required.', + 'invalid_ldap_scope': 'Invalid Scope value.', + 'update_configuration_title': 'Update Configuration', + 'successful_update_configuration': 'Update configurations successfully.', + 'failed_to_update_configuration': 'Failed to update configuration.' }; \ No newline at end of file diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js index 451351edf..fbd1bcfc5 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -287,5 +287,57 @@ var locale_messages = { 'confirm_to_toggle_enabled_policy': '启用策略后,该项目下的所有镜像仓库将复制到目标实例。请确认继续。', 'confirm_to_toggle_disabled_policy_title': '停用策略', 'confirm_to_toggle_disabled_policy': '停用策略后,所有未完成的复制任务将被终止和取消。请确认继续。', - 'begin_date_is_later_than_end_date': '起始日期不能晚于结束日期。' + 'begin_date_is_later_than_end_date': '起始日期不能晚于结束日期。', + 'configuration': '设置', + 'authentication': '认证设置', + 'email_settings': '邮箱设置', + 'system_settings': '系统设置', + 'authentication_mode': '认证模式', + 'authentication_mode_desc': '默认的认证模式是: 数据库认证。 当用户信息存储在LDAP或AD服务器时,应设置为LDAP认证模式。 注意:该选项只能被设置一次。', + 'self_registration': '自注册', + 'self_registration_desc': '确定是否允许或禁止用户自注册。 关闭该项会禁用Harbor用户注册。 当用户数据存储在LDAP或AD时,该选项无效。 ', + 'ldap_url': 'LDAP URL', + 'ldap_url_desc': 'LDAP/AD服务URL。', + 'ldap_search_dn': 'LDAP Search DN', + 'ldap_search_dn_desc': '提供一个拥有检索LDAP/AD权限的用户DN。 如果LDAP/AD允许匿名访问该项可以置空, 否则需提供用户DN和密码。', + 'ldap_search_password': 'LDAP Search 密码', + 'ldap_search_password_desc': '检索LDAP的用户密码。 如果LDAP/AD允许匿名访问该项可以置空。', + 'ldap_base_dn': 'LDAP Base DN', + 'ldap_base_dn_desc': '用于查询认证用户的基本DN节点。 检索范围包含子树节点。', + 'ldap_uid': 'LDAP UID', + 'ldap_uid_desc': '该属性用于检索匹配用户, 可以是uid, cn, Email, sAMAccountName或是其他属性, 取决于LDAP/AD服务。', + 'ldap_filter': 'LDAP 过滤器', + 'ldap_filter_desc': '用于过滤LDAP/AP检索, 请确保正确语法形式。', + 'ldap_connection_timeout': 'LDAP连接超时(秒)', + 'ldap_scope': 'LDAP检索范围', + 'ldap_scope_desc': '指定用户检索范围。(1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE)', + 'email_server': '邮箱服务地址', + 'email_server_desc': '用以发送重置密码的邮箱服务地址。', + 'email_server_port': '邮箱服务端口号', + 'email_server_port_desc': '邮箱服务端口号', + 'email_username': '邮箱用户名', + 'email_username_desc': '发送重置密码邮箱的用户。 通常是系统邮箱地址。', + 'email_password': '邮箱密码', + 'email_password_desc': '发送重置密码邮箱的密码。', + 'email_from': '寄信人', + 'email_from_desc': '邮箱寄信人名。', + 'email_ssl': '邮箱SSL', + 'email_ssl_desc': '是否启用安全邮箱传输。', + 'project_creation_restriction': '项目创建约束', + 'project_creation_restriction_desc': '此标志用于控制用户是否有权限创建项目。 默认允许任何人创建项目, 当设置为"adminonly"只允许管理员创建项目。', + 'verify_remote_cert': '验证远程服务证书', + 'verify_remote_cert_desc': '确定镜像复制是否检查远程Harbor服务证书。 当使用非受信或自签名证书时应该关闭该检查。', + 'max_job_workers': '最大任务调度数', + 'max_job_workers_desc': '任务调度服务最大调度数。', + 'please_save_changes': '请在离开此页之前保存。', + 'undo': '撤销', + 'invalid_port_number': '无效的端口号。', + 'max_job_workers_is_required': '最大任务数为必填项。', + 'timeout_is_required': '超时时间为必填项。', + 'invalid_timeout': '无效的超时时间。', + 'ldap_scope_is_required': '范围值为必填项。', + 'invalid_ldap_scope': '无效的范围值。', + 'update_configuration_title': '修改设置', + 'successful_update_configuration': '修改设置成功。', + 'failed_to_update_configuration': '修改设置失败。' }; \ No newline at end of file diff --git a/src/ui/static/resources/js/services/system-info/services.system-configuration.js b/src/ui/static/resources/js/services/system-info/services.system-configuration.js new file mode 100644 index 000000000..8c168cb90 --- /dev/null +++ b/src/ui/static/resources/js/services/system-info/services.system-configuration.js @@ -0,0 +1,43 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +(function() { + + 'use strict'; + angular.module('harbor.services.system.info') + .service('ConfigurationService', ConfigurationService); + + ConfigurationService.$inject = ['$http', '$q', '$timeout']; + + function ConfigurationService($http, $q, $timeout) { + this.get = get; + this.update = update; + this.pingLDAP = pingLDAP; + + function get() { + return $http.get('/api/configurations'); + } + + function update(updates) { + return $http.put('/api/configurations', updates); + } + + function pingLDAP(ldapConf) { + return $http + .post('/api/ldap/ping', ldapConf); + } + + } + +})(); \ No newline at end of file diff --git a/src/ui/views/navigation-detail.htm b/src/ui/views/navigation-detail.htm index 9fc68cfad..ad6bdd107 100644 --- a/src/ui/views/navigation-detail.htm +++ b/src/ui/views/navigation-detail.htm @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -
      +
      • // 'repositories' | tr //|
      • {{ if eq .IsAdmin 1 }}
      • // 'replication' | tr //|
      • diff --git a/src/ui/views/sections/script-include.htm b/src/ui/views/sections/script-include.htm index 187387a07..e8d3d9d1a 100644 --- a/src/ui/views/sections/script-include.htm +++ b/src/ui/views/sections/script-include.htm @@ -112,6 +112,7 @@ +