mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-25 11:46:43 +01:00
Add costomized banner message UI (#18827)
1.Fixes #18719 2.Add Banner Message item to configuration 3.Add banner_message property to systeminfo API Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
8fe561865d
commit
ef96c729c0
@ -116,4 +116,4 @@ This project uses open source components which have additional licensing terms.
|
|||||||
|
|
||||||
## Fossa Status
|
## 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)
|
[![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)
|
||||||
|
@ -7682,6 +7682,12 @@ definitions:
|
|||||||
GeneralInfo:
|
GeneralInfo:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
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.
|
||||||
|
example: "{\"closable\":true,\"message\":\"your banner message content\",\"type\":\"warning\",\"fromDate\":\"06/19/2023\",\"toDate\":\"06/21/2023\"}"
|
||||||
current_time:
|
current_time:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@ -8820,6 +8826,9 @@ definitions:
|
|||||||
session_timeout:
|
session_timeout:
|
||||||
$ref: '#/definitions/IntegerConfigItem'
|
$ref: '#/definitions/IntegerConfigItem'
|
||||||
description: The session timeout in minutes
|
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:
|
Configurations:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -9088,6 +9097,11 @@ definitions:
|
|||||||
description: Whether or not to skip update pull time for scanner
|
description: Whether or not to skip update pull time for scanner
|
||||||
x-omitempty: true
|
x-omitempty: true
|
||||||
x-isnullable: 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:
|
StringConfigItem:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -219,6 +219,9 @@ const (
|
|||||||
// SessionTimeout defines the web session timeout
|
// SessionTimeout defines the web session timeout
|
||||||
SessionTimeout = "session_timeout"
|
SessionTimeout = "session_timeout"
|
||||||
|
|
||||||
|
// Customized banner message
|
||||||
|
BannerMessage = "banner_message"
|
||||||
|
|
||||||
// UIMaxLengthLimitedOfNumber is the max length that UI limited for type number
|
// UIMaxLengthLimitedOfNumber is the max length that UI limited for type number
|
||||||
UIMaxLengthLimitedOfNumber = 10
|
UIMaxLengthLimitedOfNumber = 10
|
||||||
// ExecutionStatusRefreshIntervalSeconds is the interval seconds for refreshing execution status
|
// ExecutionStatusRefreshIntervalSeconds is the interval seconds for refreshing execution status
|
||||||
|
@ -47,6 +47,7 @@ type Data struct {
|
|||||||
PrimaryAuthMode bool
|
PrimaryAuthMode bool
|
||||||
SelfRegistration bool
|
SelfRegistration bool
|
||||||
HarborVersion string
|
HarborVersion string
|
||||||
|
BannerMessage string
|
||||||
AuthProxySettings *models.HTTPAuthProxy
|
AuthProxySettings *models.HTTPAuthProxy
|
||||||
Protected *protectedData
|
Protected *protectedData
|
||||||
}
|
}
|
||||||
@ -90,11 +91,18 @@ func (c *controller) GetInfo(ctx context.Context, opt Options) (*Data, error) {
|
|||||||
logger.Errorf("Error occurred getting config: %v", err)
|
logger.Errorf("Error occurred getting config: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
mgr := config.GetCfgManager(ctx)
|
||||||
|
err = mgr.Load(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Error occurred loading config: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
res := &Data{
|
res := &Data{
|
||||||
AuthMode: utils.SafeCastString(cfg[common.AUTHMode]),
|
AuthMode: utils.SafeCastString(cfg[common.AUTHMode]),
|
||||||
PrimaryAuthMode: utils.SafeCastBool(cfg[common.PrimaryAuthMode]),
|
PrimaryAuthMode: utils.SafeCastBool(cfg[common.PrimaryAuthMode]),
|
||||||
SelfRegistration: utils.SafeCastBool(cfg[common.SelfRegistration]),
|
SelfRegistration: utils.SafeCastBool(cfg[common.SelfRegistration]),
|
||||||
HarborVersion: fmt.Sprintf("%s-%s", version.ReleaseVersion, version.GitCommit),
|
HarborVersion: fmt.Sprintf("%s-%s", version.ReleaseVersion, version.GitCommit),
|
||||||
|
BannerMessage: utils.SafeCastString(mgr.Get(ctx, common.BannerMessage).GetString()),
|
||||||
}
|
}
|
||||||
if res.AuthMode == common.HTTPAuth {
|
if res.AuthMode == common.HTTPAuth {
|
||||||
if s, err := config.HTTPAuthProxySetting(ctx); err == nil {
|
if s, err := config.HTTPAuthProxySetting(ctx); err == nil {
|
||||||
|
@ -31,6 +31,7 @@ func (s *sysInfoCtlTestSuite) SetupTest() {
|
|||||||
common.RegistryStorageProviderName: "filesystem",
|
common.RegistryStorageProviderName: "filesystem",
|
||||||
common.ReadOnly: false,
|
common.ReadOnly: false,
|
||||||
common.NotificationEnable: false,
|
common.NotificationEnable: false,
|
||||||
|
common.BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}",
|
||||||
}
|
}
|
||||||
|
|
||||||
config.InitWithSettings(conf)
|
config.InitWithSettings(conf)
|
||||||
@ -58,6 +59,7 @@ func (s *sysInfoCtlTestSuite) TestGetInfo() {
|
|||||||
AuthMode: "db_auth",
|
AuthMode: "db_auth",
|
||||||
HarborVersion: "test-fakeid",
|
HarborVersion: "test-fakeid",
|
||||||
SelfRegistration: true,
|
SelfRegistration: true,
|
||||||
|
BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -66,6 +68,7 @@ func (s *sysInfoCtlTestSuite) TestGetInfo() {
|
|||||||
AuthMode: "db_auth",
|
AuthMode: "db_auth",
|
||||||
HarborVersion: "test-fakeid",
|
HarborVersion: "test-fakeid",
|
||||||
SelfRegistration: true,
|
SelfRegistration: true,
|
||||||
|
BannerMessage: "{\"closable\":false,\"message\":\"Just for test\",\"type\":\" error\"}",
|
||||||
Protected: &protectedData{
|
Protected: &protectedData{
|
||||||
RegistryURL: "test.goharbor.io",
|
RegistryURL: "test.goharbor.io",
|
||||||
ExtURL: "https://test.goharbor.io",
|
ExtURL: "https://test.goharbor.io",
|
||||||
|
@ -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.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.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`},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -255,3 +255,8 @@ func ScannerSkipUpdatePullTime(ctx context.Context) bool {
|
|||||||
log.Infof("skip_update_pull_time:%v", DefaultMgr().Get(ctx, common.ScannerSkipUpdatePullTime).GetBool())
|
log.Infof("skip_update_pull_time:%v", DefaultMgr().Get(ctx, common.ScannerSkipUpdatePullTime).GetBool())
|
||||||
return 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()
|
||||||
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<clr-alerts>
|
<clr-alerts>
|
||||||
|
<clr-alert
|
||||||
|
*ngIf="hasValidBannerMessage() && isLogin()"
|
||||||
|
[clrAlertType]="getBannerMessageType()"
|
||||||
|
[clrAlertAppLevel]="true"
|
||||||
|
[clrAlertClosable]="getBannerMessageClosable()">
|
||||||
|
<clr-alert-item>
|
||||||
|
<span class="alert-text">{{ getBannerMessage() }}</span>
|
||||||
|
</clr-alert-item>
|
||||||
|
</clr-alert>
|
||||||
<clr-alert
|
<clr-alert
|
||||||
*ngIf="showReadOnly && isLogin()"
|
*ngIf="showReadOnly && isLogin()"
|
||||||
[clrAlertType]="'warning'"
|
[clrAlertType]="'warning'"
|
||||||
|
@ -15,6 +15,10 @@ import { MessageService } from '../../../shared/components/global-message/messag
|
|||||||
import { Message } from '../../../shared/components/global-message/message';
|
import { Message } from '../../../shared/components/global-message/message';
|
||||||
import { JobServiceDashboardHealthCheckService } from '../../left-side-nav/job-service-dashboard/job-service-dashboard-health-check.service';
|
import { JobServiceDashboardHealthCheckService } from '../../left-side-nav/job-service-dashboard/job-service-dashboard-health-check.service';
|
||||||
import { AppConfigService } from '../../../services/app-config.service';
|
import { AppConfigService } from '../../../services/app-config.service';
|
||||||
|
import {
|
||||||
|
BannerMessage,
|
||||||
|
BannerMessageType,
|
||||||
|
} from '../../left-side-nav/config/config';
|
||||||
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
||||||
const YES: string = 'yes';
|
const YES: string = 'yes';
|
||||||
@Component({
|
@Component({
|
||||||
@ -185,4 +189,79 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
|
|||||||
isLogin(): boolean {
|
isLogin(): boolean {
|
||||||
return !!this.session.getCurrentUser();
|
return !!this.session.getCurrentUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasValidBannerMessage(): boolean {
|
||||||
|
if (
|
||||||
|
this.appConfigService.getConfig()?.banner_message &&
|
||||||
|
this.appConfigService.getConfig()?.current_time
|
||||||
|
) {
|
||||||
|
const current = new Date(
|
||||||
|
this.appConfigService.getConfig()?.current_time
|
||||||
|
);
|
||||||
|
const bm = JSON.parse(
|
||||||
|
this.appConfigService.getConfig()?.banner_message
|
||||||
|
) as BannerMessage;
|
||||||
|
if (bm?.fromDate && bm?.toDate) {
|
||||||
|
return (
|
||||||
|
new Date(current) <= new Date(bm.toDate) &&
|
||||||
|
new Date(current) >= 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ import { clone } from '../../../shared/units/utils';
|
|||||||
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
||||||
import { finalize } from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import {
|
||||||
|
EventService,
|
||||||
|
HarborEvent,
|
||||||
|
} from '../../../services/event-service/event.service';
|
||||||
|
|
||||||
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
||||||
|
|
||||||
@ -24,7 +28,8 @@ export class ConfigService {
|
|||||||
constructor(
|
constructor(
|
||||||
private confirmService: ConfirmationDialogService,
|
private confirmService: ConfirmationDialogService,
|
||||||
private configureService: ConfigureService,
|
private configureService: ConfigureService,
|
||||||
private msgHandler: MessageHandlerService
|
private msgHandler: MessageHandlerService,
|
||||||
|
private event: EventService
|
||||||
) {
|
) {
|
||||||
this._confirmSub = this.confirmService.confirmationConfirm$.subscribe(
|
this._confirmSub = this.confirmService.confirmationConfirm$.subscribe(
|
||||||
confirmation => {
|
confirmation => {
|
||||||
@ -66,6 +71,7 @@ export class ConfigService {
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this._currentConfig = res as Configuration;
|
this._currentConfig = res as Configuration;
|
||||||
|
this.event.publish(HarborEvent.REFRESH_BANNER_MESSAGE);
|
||||||
// Add password fields
|
// Add password fields
|
||||||
this._currentConfig.email_password = new StringValueItem(
|
this._currentConfig.email_password = new StringValueItem(
|
||||||
fakePass,
|
fakePass,
|
||||||
|
@ -114,6 +114,7 @@ export class Configuration {
|
|||||||
skip_audit_log_database: BoolValueItem;
|
skip_audit_log_database: BoolValueItem;
|
||||||
session_timeout: NumberValueItem;
|
session_timeout: NumberValueItem;
|
||||||
scanner_skip_update_pulltime: BoolValueItem;
|
scanner_skip_update_pulltime: BoolValueItem;
|
||||||
|
banner_message: StringValueItem;
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.auth_mode = new StringValueItem('db_auth', true);
|
this.auth_mode = new StringValueItem('db_auth', true);
|
||||||
this.primary_auth_mode = new BoolValueItem(false, 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.skip_audit_log_database = new BoolValueItem(false, true);
|
||||||
this.session_timeout = new NumberValueItem(60, true);
|
this.session_timeout = new NumberValueItem(60, true);
|
||||||
this.scanner_skip_update_pulltime = new BoolValueItem(false, 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',
|
SCHEDULE = 'Schedule',
|
||||||
EVENT = 'Event',
|
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',
|
||||||
|
};
|
||||||
|
@ -346,6 +346,127 @@
|
|||||||
" />
|
" />
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
</clr-checkbox-container>
|
</clr-checkbox-container>
|
||||||
|
<div class="clr-form-control">
|
||||||
|
<label class="clr-control-label">{{
|
||||||
|
'BANNER_MESSAGE.BANNER_MESSAGE' | translate
|
||||||
|
}}</label>
|
||||||
|
<div class="clr-control-container flex-baseline">
|
||||||
|
<div class="clr-textarea-wrapper">
|
||||||
|
<textarea
|
||||||
|
id="banner-message"
|
||||||
|
placeholder="{{
|
||||||
|
'BANNER_MESSAGE.ENTER_MESSAGE' | translate
|
||||||
|
}}"
|
||||||
|
autocomplete="off"
|
||||||
|
class="clr-textarea"
|
||||||
|
[(ngModel)]="messageText"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[disabled]="
|
||||||
|
!currentConfig.banner_message.editable
|
||||||
|
"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="message-type">
|
||||||
|
<div
|
||||||
|
class="clr-select-wrapper"
|
||||||
|
[ngClass]="{
|
||||||
|
'clr-form-control-disable':
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
}">
|
||||||
|
<label class="message-label">{{
|
||||||
|
'BANNER_MESSAGE.MESSAGE_TYPE' | translate
|
||||||
|
}}</label>
|
||||||
|
<select
|
||||||
|
id="banner-message-type"
|
||||||
|
class="clr-select message-select"
|
||||||
|
[(ngModel)]="messageType"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[disabled]="
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
">
|
||||||
|
<option
|
||||||
|
*ngFor="let t of bannerMessageTypes"
|
||||||
|
value="{{ t }}">
|
||||||
|
{{ translateMessageType(t) | translate }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="clr-checkbox-wrapper ml-1"
|
||||||
|
[ngClass]="{
|
||||||
|
'clr-form-control-disable':
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
}">
|
||||||
|
<input
|
||||||
|
class="clr-checkbox-inline"
|
||||||
|
type="checkbox"
|
||||||
|
[(ngModel)]="messageClosable"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[disabled]="
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
"
|
||||||
|
id="banner-message-closable" />
|
||||||
|
<label
|
||||||
|
class="clr-control-label"
|
||||||
|
for="banner-message-closable"
|
||||||
|
>{{ 'BANNER_MESSAGE.CLOSABLE' | translate }}</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-form-control">
|
||||||
|
<label class="clr-control-label"></label>
|
||||||
|
<div class="clr-control-container flex-baseline">
|
||||||
|
<div class="clr-textarea-wrapper duration">
|
||||||
|
<label>{{ 'REPLICATION.DURATION' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="clr-input-wrapper flex message-type"
|
||||||
|
[ngClass]="{
|
||||||
|
'clr-form-control-disable':
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
}">
|
||||||
|
<label>{{ 'BANNER_MESSAGE.FROM' | translate }}</label>
|
||||||
|
<input
|
||||||
|
class="date"
|
||||||
|
type="date"
|
||||||
|
id="from"
|
||||||
|
clrDate
|
||||||
|
[(ngModel)]="messageFromDate"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[disabled]="
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="clr-checkbox-wrapper flex ml-1"
|
||||||
|
[ngClass]="{
|
||||||
|
'clr-form-control-disable':
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
}">
|
||||||
|
<label>{{ 'BANNER_MESSAGE.TO' | translate }}</label>
|
||||||
|
<input
|
||||||
|
class="date"
|
||||||
|
clrDate
|
||||||
|
type="date"
|
||||||
|
id="to"
|
||||||
|
[(ngModel)]="messageToDate"
|
||||||
|
[disabled]="
|
||||||
|
!currentConfig.banner_message.editable ||
|
||||||
|
!messageText
|
||||||
|
"
|
||||||
|
[ngModelOptions]="{ standalone: true }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
$input-width: 12rem;
|
||||||
|
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -148,11 +151,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.clr-input {
|
.clr-input {
|
||||||
width: 12rem;
|
width: $input-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pro-creation {
|
.pro-creation {
|
||||||
width: 12rem;
|
width: $input-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::-webkit-outer-spin-button,
|
input::-webkit-outer-spin-button,
|
||||||
@ -165,3 +168,47 @@ input::-webkit-inner-spin-button {
|
|||||||
input[type=number] {
|
input[type=number] {
|
||||||
appearance: textfield;
|
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;
|
||||||
|
}
|
||||||
|
@ -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 { NgForm } from '@angular/forms';
|
||||||
import { Configuration } from '../config';
|
import {
|
||||||
|
BannerMessage,
|
||||||
|
BannerMessageI18nMap,
|
||||||
|
BannerMessageType,
|
||||||
|
Configuration,
|
||||||
|
} from '../config';
|
||||||
import {
|
import {
|
||||||
CURRENT_BASE_HREF,
|
CURRENT_BASE_HREF,
|
||||||
getChanges,
|
getChanges,
|
||||||
@ -10,13 +15,19 @@ import { ConfigService } from '../config.service';
|
|||||||
import { AppConfigService } from '../../../../services/app-config.service';
|
import { AppConfigService } from '../../../../services/app-config.service';
|
||||||
import { finalize } from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
|
import {
|
||||||
|
EventService,
|
||||||
|
HarborEvent,
|
||||||
|
} from '../../../../services/event-service/event.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'system-settings',
|
selector: 'system-settings',
|
||||||
templateUrl: './system-settings.component.html',
|
templateUrl: './system-settings.component.html',
|
||||||
styleUrls: ['./system-settings.component.scss'],
|
styleUrls: ['./system-settings.component.scss'],
|
||||||
})
|
})
|
||||||
export class SystemSettingsComponent implements OnInit {
|
export class SystemSettingsComponent implements OnInit, OnDestroy {
|
||||||
|
bannerMessageTypes: string[] = Object.values(BannerMessageType);
|
||||||
onGoing = false;
|
onGoing = false;
|
||||||
downloadLink: string;
|
downloadLink: string;
|
||||||
get currentConfig(): Configuration {
|
get currentConfig(): Configuration {
|
||||||
@ -26,18 +37,90 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
set currentConfig(cfg: Configuration) {
|
set currentConfig(cfg: Configuration) {
|
||||||
this.conf.setConfig(cfg);
|
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;
|
@ViewChild('systemConfigFrom') systemSettingsForm: NgForm;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private errorHandler: MessageHandlerService,
|
private errorHandler: MessageHandlerService,
|
||||||
private conf: ConfigService
|
private conf: ConfigService,
|
||||||
|
private event: EventService
|
||||||
) {
|
) {
|
||||||
this.downloadLink = CURRENT_BASE_HREF + '/systeminfo/getcert';
|
this.downloadLink = CURRENT_BASE_HREF + '/systeminfo/getcert';
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.conf.resetConfig();
|
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 {
|
get editable(): boolean {
|
||||||
@ -69,7 +152,17 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public hasChanges(): boolean {
|
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() {
|
public getChanges() {
|
||||||
@ -96,7 +189,8 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
prop === 'audit_log_forward_endpoint' ||
|
prop === 'audit_log_forward_endpoint' ||
|
||||||
prop === 'skip_audit_log_database' ||
|
prop === 'skip_audit_log_database' ||
|
||||||
prop === 'session_timeout' ||
|
prop === 'session_timeout' ||
|
||||||
prop === 'scanner_skip_update_pulltime'
|
prop === 'scanner_skip_update_pulltime' ||
|
||||||
|
prop === 'banner_message'
|
||||||
) {
|
) {
|
||||||
changes[prop] = allChanges[prop];
|
changes[prop] = allChanges[prop];
|
||||||
}
|
}
|
||||||
@ -128,6 +222,19 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public save(): void {
|
public save(): void {
|
||||||
let changes = this.getChanges();
|
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)) {
|
if (!isEmpty(changes)) {
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.conf
|
this.conf
|
||||||
@ -184,4 +291,8 @@ export class SystemSettingsComponent implements OnInit {
|
|||||||
this.currentConfig.skip_audit_log_database.value = false;
|
this.currentConfig.skip_audit_log_database.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translateMessageType(type: string): string {
|
||||||
|
return BannerMessageI18nMap[type] || type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ export class AppConfig {
|
|||||||
registry_storage_provider_name: string;
|
registry_storage_provider_name: string;
|
||||||
read_only: boolean;
|
read_only: boolean;
|
||||||
show_popular_repo: boolean;
|
show_popular_repo: boolean;
|
||||||
|
banner_message: string;
|
||||||
|
current_time: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Set default value
|
// Set default value
|
||||||
@ -49,5 +51,6 @@ export class AppConfig {
|
|||||||
this.registry_storage_provider_name = '';
|
this.registry_storage_provider_name = '';
|
||||||
this.read_only = false;
|
this.read_only = false;
|
||||||
this.show_popular_repo = false;
|
this.show_popular_repo = false;
|
||||||
|
this.banner_message = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,4 +81,5 @@ export enum HarborEvent {
|
|||||||
REFRESH_EXPORT_JOBS = 'refreshExportJobs',
|
REFRESH_EXPORT_JOBS = 'refreshExportJobs',
|
||||||
DELETE_ACCESSORY = 'deleteAccessory',
|
DELETE_ACCESSORY = 'deleteAccessory',
|
||||||
COPY_DIGEST = 'copyDigest',
|
COPY_DIGEST = 'copyDigest',
|
||||||
|
REFRESH_BANNER_MESSAGE = 'refreshBannerMessage',
|
||||||
}
|
}
|
||||||
|
@ -1860,5 +1860,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -804,7 +804,7 @@
|
|||||||
"LABEL": "Labels",
|
"LABEL": "Labels",
|
||||||
"REPOSITORY": "Repository",
|
"REPOSITORY": "Repository",
|
||||||
"REPO_READ_ONLY": "Repository Read Only",
|
"REPO_READ_ONLY": "Repository Read Only",
|
||||||
"WEBHOOK_NOTIFICATION_ENABLED": "Webhooks enabled",
|
"WEBHOOK_NOTIFICATION_ENABLED": "Webhooks Enabled",
|
||||||
"SYSTEM": "System Settings",
|
"SYSTEM": "System Settings",
|
||||||
"PROJECT_QUOTAS": "Project Quotas",
|
"PROJECT_QUOTAS": "Project Quotas",
|
||||||
"VULNERABILITY": "Vulnerability",
|
"VULNERABILITY": "Vulnerability",
|
||||||
@ -834,7 +834,7 @@
|
|||||||
"ROOT_CERT_LINK": "Download",
|
"ROOT_CERT_LINK": "Download",
|
||||||
"REGISTRY_CERTIFICATE": "Registry certificate",
|
"REGISTRY_CERTIFICATE": "Registry certificate",
|
||||||
"NO_CHANGE": "Save abort because nothing changed",
|
"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": {
|
"TOOLTIP": {
|
||||||
"SELF_REGISTRATION_ENABLE": "Enable sign up.",
|
"SELF_REGISTRATION_ENABLE": "Enable sign up.",
|
||||||
"SELF_REGISTRATION_DISABLE": "Deactivate sign up.",
|
"SELF_REGISTRATION_DISABLE": "Deactivate sign up.",
|
||||||
@ -1861,5 +1861,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1857,5 +1857,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1827,5 +1827,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Sélectionner le mois, le mois courant est {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Sélectionner l'année, l'année courante est {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Sélectionné"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1857,5 +1857,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1860,5 +1860,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "Select month, the current month is {CALENDAR_MONTH}",
|
"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_SELECT_YEAR_TEXT": "Select year, the current year is {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - Selected"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1857,5 +1857,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "选择月, 当前月是 {CALENDAR_MONTH}",
|
"DATE_PICKER_SELECT_MONTH_TEXT": "选择月, 当前月是 {CALENDAR_MONTH}",
|
||||||
"DATE_PICKER_SELECT_YEAR_TEXT": "选择年, 当前年是 {CALENDAR_YEAR}",
|
"DATE_PICKER_SELECT_YEAR_TEXT": "选择年, 当前年是 {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "已选择 - {FULL_DATE}"
|
"DATE_PICKER_SELECTED_LABEL": "已选择 - {FULL_DATE}"
|
||||||
|
},
|
||||||
|
"BANNER_MESSAGE": {
|
||||||
|
"BANNER_MESSAGE": "横幅消息",
|
||||||
|
"MESSAGE_TYPE": "消息类型",
|
||||||
|
"CLOSABLE": "可关闭",
|
||||||
|
"FROM": "从",
|
||||||
|
"TO": "至",
|
||||||
|
"SUCCESS": "成功",
|
||||||
|
"INFO": "信息",
|
||||||
|
"WARNING": "警告",
|
||||||
|
"DANGER": "危险",
|
||||||
|
"ENTER_MESSAGE": "请输入消息内容"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1849,5 +1849,17 @@
|
|||||||
"DATE_PICKER_SELECT_MONTH_TEXT": "選擇月份,目前月份為 {CALENDAR_MONTH}",
|
"DATE_PICKER_SELECT_MONTH_TEXT": "選擇月份,目前月份為 {CALENDAR_MONTH}",
|
||||||
"DATE_PICKER_SELECT_YEAR_TEXT": "選擇年份,目前年份為 {CALENDAR_YEAR}",
|
"DATE_PICKER_SELECT_YEAR_TEXT": "選擇年份,目前年份為 {CALENDAR_YEAR}",
|
||||||
"DATE_PICKER_SELECTED_LABEL": "{FULL_DATE} - 已選擇"
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ func (s *sysInfoAPI) convertInfo(d *si.Data) *models.GeneralInfo {
|
|||||||
PrimaryAuthMode: &d.PrimaryAuthMode,
|
PrimaryAuthMode: &d.PrimaryAuthMode,
|
||||||
SelfRegistration: &d.SelfRegistration,
|
SelfRegistration: &d.SelfRegistration,
|
||||||
HarborVersion: &d.HarborVersion,
|
HarborVersion: &d.HarborVersion,
|
||||||
|
BannerMessage: &d.BannerMessage,
|
||||||
}
|
}
|
||||||
if d.AuthProxySettings != nil {
|
if d.AuthProxySettings != nil {
|
||||||
res.AuthproxySettings = &models.AuthproxySetting{
|
res.AuthproxySettings = &models.AuthproxySetting{
|
||||||
|
Loading…
Reference in New Issue
Block a user