From 7ba93046899dd54da2e5ad472a93437d16b518ce Mon Sep 17 00:00:00 2001 From: AllForNothing Date: Mon, 5 Jun 2023 10:48:20 +0800 Subject: [PATCH] Add costomized banner message UI 1.Fixes #18719 2.Add Banner Message item to configuration 3.Add banner_message property to systeminfo API Signed-off-by: AllForNothing --- README.md | 2 +- api/v2.0/swagger.yaml | 13 ++ src/common/const.go | 3 + src/controller/systeminfo/controller.go | 9 ++ src/controller/systeminfo/controller_test.go | 3 + src/lib/config/metadata/metadatalist.go | 2 + src/lib/config/userconfig.go | 5 + .../app-level-alerts.component.html | 9 ++ .../app-level-alerts.component.ts | 79 +++++++++++ .../left-side-nav/config/config.service.ts | 8 +- .../app/base/left-side-nav/config/config.ts | 30 +++++ .../system/system-settings.component.html | 121 +++++++++++++++++ .../system/system-settings.component.scss | 51 +++++++- .../system/system-settings.component.ts | 123 +++++++++++++++++- src/portal/src/app/services/app-config.ts | 3 + .../services/event-service/event.service.ts | 1 + src/portal/src/i18n/lang/de-de-lang.json | 12 ++ src/portal/src/i18n/lang/en-us-lang.json | 16 ++- src/portal/src/i18n/lang/es-es-lang.json | 12 ++ src/portal/src/i18n/lang/fr-fr-lang.json | 12 ++ src/portal/src/i18n/lang/pt-br-lang.json | 12 ++ src/portal/src/i18n/lang/tr-tr-lang.json | 12 ++ src/portal/src/i18n/lang/zh-cn-lang.json | 12 ++ src/portal/src/i18n/lang/zh-tw-lang.json | 25 +++- src/server/v2.0/handler/systeminfo.go | 2 +- 25 files changed, 563 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 923eaf1fa3bb..88d00b5a623c 100644 --- a/README.md +++ b/README.md @@ -116,4 +116,4 @@ This project uses open source components which have additional licensing terms. ## Fossa Status -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgoharbor%2Fharbor.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fgoharbor%2Fharbor?ref=badge_large) \ No newline at end of file +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgoharbor%2Fharbor.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fgoharbor%2Fharbor?ref=badge_large) diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index de635c98128b..ce33daf6291c 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -7682,6 +7682,11 @@ definitions: GeneralInfo: type: object properties: + banner_message: + type: string + x-nullable: true + x-omitempty: true + description: The banner message for the UI. It is the stringified result of the banner message object. current_time: type: string format: date-time @@ -8820,6 +8825,9 @@ definitions: session_timeout: $ref: '#/definitions/IntegerConfigItem' description: The session timeout in minutes + banner_message: + $ref: '#/definitions/StringConfigItem' + description: The banner message for the UI.It is the stringified result of the banner message object Configurations: type: object properties: @@ -9088,6 +9096,11 @@ definitions: description: Whether or not to skip update pull time for scanner x-omitempty: true x-isnullable: true + banner_message: + type: string + description: The banner message for the UI.It is the stringified result of the banner message object + x-omitempty: true + x-isnullable: true StringConfigItem: type: object properties: diff --git a/src/common/const.go b/src/common/const.go index f030dacf1d95..5e30b78fcc02 100644 --- a/src/common/const.go +++ b/src/common/const.go @@ -219,6 +219,9 @@ const ( // SessionTimeout defines the web session timeout SessionTimeout = "session_timeout" + // Customized banner message + BannerMessage = "banner_message" + // UIMaxLengthLimitedOfNumber is the max length that UI limited for type number UIMaxLengthLimitedOfNumber = 10 // ExecutionStatusRefreshIntervalSeconds is the interval seconds for refreshing execution status diff --git a/src/controller/systeminfo/controller.go b/src/controller/systeminfo/controller.go index cfca363f51d8..e153cc40fb2e 100644 --- a/src/controller/systeminfo/controller.go +++ b/src/controller/systeminfo/controller.go @@ -47,6 +47,7 @@ type Data struct { PrimaryAuthMode bool SelfRegistration bool HarborVersion string + BannerMessage string AuthProxySettings *models.HTTPAuthProxy Protected *protectedData } @@ -90,11 +91,19 @@ func (c *controller) GetInfo(ctx context.Context, opt Options) (*Data, error) { logger.Errorf("Error occurred getting config: %v", err) return nil, err } + mgr := config.GetCfgManager(ctx) + err = mgr.Load(ctx) + if err != nil { + logger.Errorf("Error occurred loading context: %v", err) + return nil, err + } + logger.Info(mgr.Get(ctx, common.BannerMessage).GetString()) res := &Data{ AuthMode: utils.SafeCastString(cfg[common.AUTHMode]), PrimaryAuthMode: utils.SafeCastBool(cfg[common.PrimaryAuthMode]), SelfRegistration: utils.SafeCastBool(cfg[common.SelfRegistration]), HarborVersion: fmt.Sprintf("%s-%s", version.ReleaseVersion, version.GitCommit), + BannerMessage: utils.SafeCastString(mgr.Get(ctx, common.BannerMessage).GetString()), } if res.AuthMode == common.HTTPAuth { if s, err := config.HTTPAuthProxySetting(ctx); err == nil { diff --git a/src/controller/systeminfo/controller_test.go b/src/controller/systeminfo/controller_test.go index a8156f1ed918..f3590a9fcab3 100644 --- a/src/controller/systeminfo/controller_test.go +++ b/src/controller/systeminfo/controller_test.go @@ -31,6 +31,7 @@ func (s *sysInfoCtlTestSuite) SetupTest() { common.RegistryStorageProviderName: "filesystem", common.ReadOnly: false, common.NotificationEnable: false, + common.BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}", } config.InitWithSettings(conf) @@ -58,6 +59,7 @@ func (s *sysInfoCtlTestSuite) TestGetInfo() { AuthMode: "db_auth", HarborVersion: "test-fakeid", SelfRegistration: true, + BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}" }, }, { @@ -66,6 +68,7 @@ func (s *sysInfoCtlTestSuite) TestGetInfo() { AuthMode: "db_auth", HarborVersion: "test-fakeid", SelfRegistration: true, + BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}" Protected: &protectedData{ RegistryURL: "test.goharbor.io", ExtURL: "https://test.goharbor.io", diff --git a/src/lib/config/metadata/metadatalist.go b/src/lib/config/metadata/metadatalist.go index 3f7c308128d2..bb3285e5ff50 100644 --- a/src/lib/config/metadata/metadatalist.go +++ b/src/lib/config/metadata/metadatalist.go @@ -189,5 +189,7 @@ var ( {Name: common.SessionTimeout, Scope: UserScope, Group: BasicGroup, EnvKey: "SESSION_TIMEOUT", DefaultValue: "60", ItemType: &Int64Type{}, Editable: true, Description: `The session timeout in minutes`}, {Name: common.ExecutionStatusRefreshIntervalSeconds, Scope: SystemScope, Group: BasicGroup, EnvKey: "EXECUTION_STATUS_REFRESH_INTERVAL_SECONDS", DefaultValue: "30", ItemType: &Int64Type{}, Editable: false, Description: `The interval seconds to refresh the execution status`}, + + {Name: common.BannerMessage, Scope: UserScope, Group: BasicGroup, EnvKey: "BANNER_MESSAGE", DefaultValue: "", ItemType: &StringType{}, Editable: true, Description: `The customized banner message for the UI`}, } ) diff --git a/src/lib/config/userconfig.go b/src/lib/config/userconfig.go index 863c8fb9e71f..e22b438f7ebd 100644 --- a/src/lib/config/userconfig.go +++ b/src/lib/config/userconfig.go @@ -255,3 +255,8 @@ func ScannerSkipUpdatePullTime(ctx context.Context) bool { log.Infof("skip_update_pull_time:%v", DefaultMgr().Get(ctx, common.ScannerSkipUpdatePullTime).GetBool()) return DefaultMgr().Get(ctx, common.ScannerSkipUpdatePullTime).GetBool() } + +// BannerMessage returns the customized banner message +func BannerMessage(ctx context.Context) string { + return DefaultMgr().Get(ctx, common.BannerMessage).GetString() +} diff --git a/src/portal/src/app/base/harbor-shell/app-level-alerts/app-level-alerts.component.html b/src/portal/src/app/base/harbor-shell/app-level-alerts/app-level-alerts.component.html index 797d1aad3b2e..769891065a27 100644 --- a/src/portal/src/app/base/harbor-shell/app-level-alerts/app-level-alerts.component.html +++ b/src/portal/src/app/base/harbor-shell/app-level-alerts/app-level-alerts.component.html @@ -1,4 +1,13 @@ + + + {{ getBannerMessage() }} + + = new Date(bm.fromDate) + ); + } + if (bm?.fromDate && !bm?.toDate) { + return new Date(current) >= new Date(bm.fromDate); + } + + if (!bm?.fromDate && bm?.toDate) { + return new Date(current) <= new Date(bm.toDate); + } + } + return false; + } + + getBannerMessage() { + if ( + this.appConfigService.getConfig()?.banner_message && + ( + JSON.parse( + this.appConfigService.getConfig()?.banner_message + ) as BannerMessage + )?.message + ) { + return ( + JSON.parse( + this.appConfigService.getConfig()?.banner_message + ) as BannerMessage + )?.message; + } + return null; + } + + getBannerMessageType() { + if ( + this.appConfigService.getConfig()?.banner_message && + ( + JSON.parse( + this.appConfigService.getConfig()?.banner_message + ) as BannerMessage + )?.type + ) { + return ( + JSON.parse( + this.appConfigService.getConfig()?.banner_message + ) as BannerMessage + )?.type; + } + return BannerMessageType.WARNING; + } + + getBannerMessageClosable(): boolean { + if (this.appConfigService.getConfig()?.banner_message) { + return ( + JSON.parse( + this.appConfigService.getConfig()?.banner_message + ) as BannerMessage + )?.closable; + } + return true; + } } diff --git a/src/portal/src/app/base/left-side-nav/config/config.service.ts b/src/portal/src/app/base/left-side-nav/config/config.service.ts index 26ac30f2f3f7..516eac239e97 100644 --- a/src/portal/src/app/base/left-side-nav/config/config.service.ts +++ b/src/portal/src/app/base/left-side-nav/config/config.service.ts @@ -11,6 +11,10 @@ import { clone } from '../../../shared/units/utils'; import { MessageHandlerService } from '../../../shared/services/message-handler.service'; import { finalize } from 'rxjs/operators'; import { Observable, Subscription } from 'rxjs'; +import { + EventService, + HarborEvent, +} from '../../../services/event-service/event.service'; const fakePass = 'aWpLOSYkIzJTTU4wMDkx'; @@ -24,7 +28,8 @@ export class ConfigService { constructor( private confirmService: ConfirmationDialogService, private configureService: ConfigureService, - private msgHandler: MessageHandlerService + private msgHandler: MessageHandlerService, + private event: EventService ) { this._confirmSub = this.confirmService.confirmationConfirm$.subscribe( confirmation => { @@ -66,6 +71,7 @@ export class ConfigService { .subscribe( res => { this._currentConfig = res as Configuration; + this.event.publish(HarborEvent.REFRESH_BANNER_MESSAGE); // Add password fields this._currentConfig.email_password = new StringValueItem( fakePass, diff --git a/src/portal/src/app/base/left-side-nav/config/config.ts b/src/portal/src/app/base/left-side-nav/config/config.ts index 30babedd00d3..0fedfca3007f 100644 --- a/src/portal/src/app/base/left-side-nav/config/config.ts +++ b/src/portal/src/app/base/left-side-nav/config/config.ts @@ -114,6 +114,7 @@ export class Configuration { skip_audit_log_database: BoolValueItem; session_timeout: NumberValueItem; scanner_skip_update_pulltime: BoolValueItem; + banner_message: StringValueItem; public constructor() { this.auth_mode = new StringValueItem('db_auth', true); this.primary_auth_mode = new BoolValueItem(false, true); @@ -190,6 +191,10 @@ export class Configuration { this.skip_audit_log_database = new BoolValueItem(false, true); this.session_timeout = new NumberValueItem(60, true); this.scanner_skip_update_pulltime = new BoolValueItem(false, true); + this.banner_message = new StringValueItem( + JSON.stringify(new BannerMessage()), + true + ); } } @@ -208,3 +213,28 @@ export enum Triggers { SCHEDULE = 'Schedule', EVENT = 'Event', } + +export class BannerMessage { + message: string; + closable: boolean; + type: string; + fromDate: Date; + toDate: Date; + constructor() { + this.closable = false; + } +} + +export enum BannerMessageType { + SUCCESS = 'success', + INFO = 'info', + WARNING = 'warning', + ERROR = 'danger', +} + +export const BannerMessageI18nMap = { + [BannerMessageType.SUCCESS]: 'BANNER_MESSAGE.SUCCESS', + [BannerMessageType.INFO]: 'BANNER_MESSAGE.INFO', + [BannerMessageType.WARNING]: 'BANNER_MESSAGE.WARNING', + [BannerMessageType.ERROR]: 'BANNER_MESSAGE.DANGER', +}; diff --git a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.html b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.html index 2968cbde4761..08a1e469d104 100644 --- a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.html +++ b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.html @@ -346,6 +346,127 @@ " /> +
+ +
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+ + +
+
+
diff --git a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.scss b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.scss index e107684c70da..08ffcc9730cd 100644 --- a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.scss +++ b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.scss @@ -1,3 +1,6 @@ +$input-width: 12rem; + + .subtitle { font-size: 14px; font-weight: 600; @@ -148,11 +151,11 @@ } .clr-input { - width: 12rem; + width: $input-width; } .pro-creation { - width: 12rem; + width: $input-width; } input::-webkit-outer-spin-button, @@ -165,3 +168,47 @@ input::-webkit-inner-spin-button { input[type=number] { appearance: textfield; } + +.clr-textarea { + max-width: none; + width: $input-width; +} + +.flex-baseline { + display: flex; + align-items: baseline; +} + +$message-type-width: 12rem; + +.message-type { + margin-left: 2rem; + width: $message-type-width; +} + +.message-label { + margin-right: 0.25rem; +} + +.duration { + width: $input-width; + display: flex; + justify-content: right; +} + +.flex { + display: flex; +} + +:host::ng-deep clr-date-container{ + margin-top: 0; + margin-left: 0.5rem; +} + +.message-select { + width: 6.75rem; +} + +.date { + width: 6rem; +} diff --git a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.ts b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.ts index 8888664e90b2..dc84b9ca6616 100644 --- a/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.ts +++ b/src/portal/src/app/base/left-side-nav/config/system/system-settings.component.ts @@ -1,6 +1,11 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { NgForm } from '@angular/forms'; -import { Configuration } from '../config'; +import { + BannerMessage, + BannerMessageI18nMap, + BannerMessageType, + Configuration, +} from '../config'; import { CURRENT_BASE_HREF, getChanges, @@ -10,13 +15,19 @@ import { ConfigService } from '../config.service'; import { AppConfigService } from '../../../../services/app-config.service'; import { finalize } from 'rxjs/operators'; import { MessageHandlerService } from '../../../../shared/services/message-handler.service'; +import { + EventService, + HarborEvent, +} from '../../../../services/event-service/event.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'system-settings', templateUrl: './system-settings.component.html', styleUrls: ['./system-settings.component.scss'], }) -export class SystemSettingsComponent implements OnInit { +export class SystemSettingsComponent implements OnInit, OnDestroy { + bannerMessageTypes: string[] = Object.values(BannerMessageType); onGoing = false; downloadLink: string; get currentConfig(): Configuration { @@ -26,18 +37,90 @@ export class SystemSettingsComponent implements OnInit { set currentConfig(cfg: Configuration) { this.conf.setConfig(cfg); } + + messageText: string; + messageType: string; + messageClosable: boolean; + messageFromDate: Date; + messageToDate: Date; + // the copy of bannerMessage + messageTextCopy: string; + messageTypeCopy: string; + messageClosableCopy: boolean; + messageFromDateCopy: Date; + messageToDateCopy: Date; + bannerRefreshSub: Subscription; + @ViewChild('systemConfigFrom') systemSettingsForm: NgForm; constructor( private appConfigService: AppConfigService, private errorHandler: MessageHandlerService, - private conf: ConfigService + private conf: ConfigService, + private event: EventService ) { this.downloadLink = CURRENT_BASE_HREF + '/systeminfo/getcert'; } ngOnInit() { this.conf.resetConfig(); + if (!this.bannerRefreshSub) { + this.bannerRefreshSub = this.event.subscribe( + HarborEvent.REFRESH_BANNER_MESSAGE, + () => { + this.setValueForBannerMessage(); + } + ); + } + if (this.currentConfig.banner_message) { + this.setValueForBannerMessage(); + } + } + + ngOnDestroy() { + if (this.bannerRefreshSub) { + this.bannerRefreshSub.unsubscribe(); + this.bannerRefreshSub = null; + } + } + + setValueForBannerMessage() { + if (this.currentConfig.banner_message.value) { + this.messageText = ( + JSON.parse( + this.currentConfig.banner_message.value + ) as BannerMessage + ).message; + this.messageType = ( + JSON.parse( + this.currentConfig.banner_message.value + ) as BannerMessage + ).type; + this.messageClosable = ( + JSON.parse( + this.currentConfig.banner_message.value + ) as BannerMessage + ).closable; + this.messageFromDate = ( + JSON.parse( + this.currentConfig.banner_message.value + ) as BannerMessage + ).fromDate; + this.messageToDate = ( + JSON.parse( + this.currentConfig.banner_message.value + ) as BannerMessage + ).toDate; + } else { + this.messageText = null; + this.messageType = BannerMessageType.WARNING; + this.messageClosable = false; + } + this.messageTextCopy = this.messageText; + this.messageTypeCopy = this.messageType; + this.messageClosableCopy = this.messageClosable; + this.messageFromDateCopy = this.messageFromDate; + this.messageToDateCopy = this.messageToDate; } get editable(): boolean { @@ -69,7 +152,17 @@ export class SystemSettingsComponent implements OnInit { } public hasChanges(): boolean { - return !isEmpty(this.getChanges()); + return !isEmpty(this.getChanges()) || this.hasBannerMessageChanged(); + } + + hasBannerMessageChanged() { + return ( + this.messageTextCopy != this.messageText || + this.messageTypeCopy != this.messageType || + this.messageClosableCopy != this.messageClosable || + this.messageFromDateCopy != this.messageFromDate || + this.messageToDateCopy != this.messageToDate + ); } public getChanges() { @@ -96,7 +189,8 @@ export class SystemSettingsComponent implements OnInit { prop === 'audit_log_forward_endpoint' || prop === 'skip_audit_log_database' || prop === 'session_timeout' || - prop === 'scanner_skip_update_pulltime' + prop === 'scanner_skip_update_pulltime' || + prop === 'banner_message' ) { changes[prop] = allChanges[prop]; } @@ -128,6 +222,19 @@ export class SystemSettingsComponent implements OnInit { */ public save(): void { let changes = this.getChanges(); + if (this.hasBannerMessageChanged()) { + const bm = new BannerMessage(); + bm.message = this.messageText; + bm.type = this.messageType; + bm.closable = this.messageClosable; + bm.fromDate = this.messageFromDate; + bm.toDate = this.messageToDate; + if (bm.message) { + changes['banner_message'] = JSON.stringify(bm); + } else { + changes['banner_message'] = ''; + } + } if (!isEmpty(changes)) { this.onGoing = true; this.conf @@ -184,4 +291,8 @@ export class SystemSettingsComponent implements OnInit { this.currentConfig.skip_audit_log_database.value = false; } } + + translateMessageType(type: string): string { + return BannerMessageI18nMap[type] || type; + } } diff --git a/src/portal/src/app/services/app-config.ts b/src/portal/src/app/services/app-config.ts index 2e5c1cad2e69..0235ffa1a28b 100644 --- a/src/portal/src/app/services/app-config.ts +++ b/src/portal/src/app/services/app-config.ts @@ -29,6 +29,8 @@ export class AppConfig { registry_storage_provider_name: string; read_only: boolean; show_popular_repo: boolean; + banner_message: string; + current_time: string; constructor() { // Set default value @@ -49,5 +51,6 @@ export class AppConfig { this.registry_storage_provider_name = ''; this.read_only = false; this.show_popular_repo = false; + this.banner_message = ''; } } diff --git a/src/portal/src/app/services/event-service/event.service.ts b/src/portal/src/app/services/event-service/event.service.ts index e9506598fd51..c920edc5937a 100644 --- a/src/portal/src/app/services/event-service/event.service.ts +++ b/src/portal/src/app/services/event-service/event.service.ts @@ -81,4 +81,5 @@ export enum HarborEvent { REFRESH_EXPORT_JOBS = 'refreshExportJobs', DELETE_ACCESSORY = 'deleteAccessory', COPY_DIGEST = 'copyDigest', + REFRESH_BANNER_MESSAGE = 'refreshBannerMessage', } diff --git a/src/portal/src/i18n/lang/de-de-lang.json b/src/portal/src/i18n/lang/de-de-lang.json index 364e9920cb31..39ddd2ec6085 100644 --- a/src/portal/src/i18n/lang/de-de-lang.json +++ b/src/portal/src/i18n/lang/de-de-lang.json @@ -1859,5 +1859,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 1ac867f86a3d..29926dc16255 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -804,7 +804,7 @@ "LABEL": "Labels", "REPOSITORY": "Repository", "REPO_READ_ONLY": "Repository Read Only", - "WEBHOOK_NOTIFICATION_ENABLED": "Webhooks enabled", + "WEBHOOK_NOTIFICATION_ENABLED": "Webhooks Enabled", "SYSTEM": "System Settings", "PROJECT_QUOTAS": "Project Quotas", "VULNERABILITY": "Vulnerability", @@ -834,7 +834,7 @@ "ROOT_CERT_LINK": "Download", "REGISTRY_CERTIFICATE": "Registry certificate", "NO_CHANGE": "Save abort because nothing changed", - "SKIP_SCANNER_PULL_TIME": "Retain image \"last pull time\" on scanning", + "SKIP_SCANNER_PULL_TIME": "Retain Image \"last pull time\" On Scanning", "TOOLTIP": { "SELF_REGISTRATION_ENABLE": "Enable sign up.", "SELF_REGISTRATION_DISABLE": "Deactivate sign up.", @@ -1860,5 +1860,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 9cd6c62ca963..b58fb8be3ee6 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -1856,5 +1856,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index e8faad26e173..2c8139746142 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -1826,5 +1826,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Sélectionner le mois, le mois courant est {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Sélectionner l'année, l'année courante est {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Sélectionné" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index b9e46d39707d..db931bf4d9bf 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -1856,5 +1856,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index c53ff98161a9..f9d16674fa51 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -1859,5 +1859,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 31bfa22cffb3..a70a119c612c 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -1856,5 +1856,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "选择月, 当前月是 {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "选择年, 当前年是 {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "已选择 - {FULL_DATE}" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "横幅消息", + "MESSAGE_TYPE": "消息类型", + "CLOSABLE": "可关闭", + "FROM": "从", + "TO": "至", + "SUCCESS": "成功", + "INFO": "信息", + "WARNING": "警告", + "DANGER": "危险", + "ENTER_MESSAGE": "请输入消息内容" } } diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json index 6c3070159b2b..3d4df6f90720 100644 --- a/src/portal/src/i18n/lang/zh-tw-lang.json +++ b/src/portal/src/i18n/lang/zh-tw-lang.json @@ -67,7 +67,18 @@ "TIME_OUT": "逾時" }, "TOOLTIP": { - "NAME_FILTER": "過濾資源的名稱。不填或填寫「**」可匹配所有資源;「library/**」僅匹配「library」下的資源。更多的匹配模式請參考使用者手冊。", + "NAME_FILTER": "過濾資源的名稱。不填或填寫「**」可匹配所有資源;「library + * + 」 + 僅匹配 + 「 + library + 」 + 下的資源 + 。 + 更多的匹配模式請參考使用者手冊 + 。 + ", "TAG_FILTER": "過濾資源的tag/version。不填或填寫「」可匹配所有;「1.0*」僅匹配以「1.0」開頭的tag/version。", "LABEL_FILTER": "根據標籤篩選資源。", "RESOURCE_FILTER": "過濾資源的類型。", @@ -1848,5 +1859,17 @@ "DATE_PICKER_SELECT_MONTH_TEXT": "選擇月份,目前月份為 {CALENDAR_MONTH}", "DATE_PICKER_SELECT_YEAR_TEXT": "選擇年份,目前年份為 {CALENDAR_YEAR}", "DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - 已選擇" + }, + "BANNER_MESSAGE": { + "BANNER_MESSAGE": "Banner Message", + "MESSAGE_TYPE": "Message type", + "CLOSABLE": "Closable", + "FROM": "From", + "TO": "To", + "SUCCESS": "Success", + "INFO": "Info", + "WARNING": "Warning", + "DANGER": "Danger", + "ENTER_MESSAGE": "Enter your message here" } } diff --git a/src/server/v2.0/handler/systeminfo.go b/src/server/v2.0/handler/systeminfo.go index 9fa19dfb9692..e145851b5e77 100644 --- a/src/server/v2.0/handler/systeminfo.go +++ b/src/server/v2.0/handler/systeminfo.go @@ -16,7 +16,6 @@ package handler import ( "context" - "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" @@ -86,6 +85,7 @@ func (s *sysInfoAPI) convertInfo(d *si.Data) *models.GeneralInfo { PrimaryAuthMode: &d.PrimaryAuthMode, SelfRegistration: &d.SelfRegistration, HarborVersion: &d.HarborVersion, + BannerMessage: &d.BannerMessage, } if d.AuthProxySettings != nil { res.AuthproxySettings = &models.AuthproxySetting{