UI enhancement for replication namespace (#14818)

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Will Sun 2021-05-12 10:11:04 +08:00 committed by GitHub
parent a6d92ca807
commit eec3619df1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 270 additions and 102 deletions

View File

@ -0,0 +1,15 @@
export enum Flatten_Level {
NO_FLATTING = 0,
FLATTEN_LEVEl_1 = 1,
FLATTEN_LEVEl_2 = 2,
FLATTEN_LEVEl_3 = 3,
FLATTEN_ALL = -1
}
export const Flatten_I18n_MAP = {
[Flatten_Level.NO_FLATTING]: 'REPLICATION.NO_FLATTING',
[Flatten_Level.FLATTEN_LEVEl_1]: 'REPLICATION.FLATTEN_LEVEL_1',
[Flatten_Level.FLATTEN_LEVEl_2]: 'REPLICATION.FLATTEN_LEVEL_2',
[Flatten_Level.FLATTEN_LEVEl_3]: 'REPLICATION.FLATTEN_LEVEL_3',
[Flatten_Level.FLATTEN_ALL]: 'REPLICATION.FLATTEN_ALL',
};

View File

@ -151,19 +151,48 @@
</div>
</div>
<!--destination namespaces -->
<clr-input-container>
<label>{{'REPLICATION.DEST_NAMESPACE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.DESTINATION_NAMESPACE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<input clrInput formControlName="dest_namespace" type="text" id="dest_namespace" pattern="^[a-z0-9]+(?:[/._-][a-z0-9]+)*$"
class="inputWidth" maxlength="255">
<clr-control-error *ngIf='ruleForm.controls.dest_namespace.touched && ruleForm.controls.dest_namespace.invalid'>{{'REPLICATION.DESTINATION_NAME_TOOLTIP' | translate}}</clr-control-error>
</clr-input-container>
<div class="clr-form-control">
<label for="dest_namespace" class="clr-control-label">{{'REPLICATION.DESTINATION' | translate}}</label>
<div class="clr-control-container" [class.clr-error]="ruleForm.controls.dest_namespace.touched && ruleForm.controls.dest_namespace.invalid">
<div class="clr-input-wrapper">
<label class="sub-label">{{'REPLICATION.NAMESPACE' | translate}}:</label>
<input autocomplete="off" class="clr-input" formControlName="dest_namespace" type="text" id="dest_namespace" pattern="^[a-z0-9]+(?:[/._-][a-z0-9]+)*$"
maxlength="255">
<clr-tooltip class="des-tooltip">
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.DESTINATION_NAMESPACE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
<clr-control-error class="margin-left-90px" *ngIf="ruleForm.controls.dest_namespace.touched && ruleForm.controls.dest_namespace.invalid">
{{'REPLICATION.DESTINATION_NAME_TOOLTIP' | translate}}
</clr-control-error>
</div>
</div>
<!--destination namespaces -->
<div class="clr-form-control">
<label for="dest_namespace_replace_count" class="clr-control-label"></label>
<div class="clr-control-container" [class.clr-error]="ruleForm.controls.dest_namespace_replace_count.touched && ruleForm.controls.dest_namespace_replace_count.invalid">
<div class="input-width flex">
<div class="clr-select-wrapper">
<label class="sub-label">{{'REPLICATION.REPO_FLATTENING' | translate}}:</label>
<select id="dest_namespace_replace_count" formControlName="dest_namespace_replace_count" class="clr-select count-type">
<option *ngFor="let item of flattenLevelMap | keyvalue"
[value]="item.key">{{item.value | translate}}</option>
</select>
</div>
<div class="clr-input-wrapper">
<clr-tooltip class="des-tooltip">
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
{{'REPLICATION.FLATTEN_LEVEL_TIP' | translate}}
</clr-tooltip-content>
</clr-tooltip>
</div>
</div>
</div>
</div>
<!--Trigger-->
<div class="clr-form-control">
<label class="clr-control-label required">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
@ -216,13 +245,10 @@
</div>
</div>
</div>
<div class="loading-center">
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="this.inProgress" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" id="ruleBtnOk" class="btn btn-primary" (click)="onSubmit()" [disabled]="!isValid || !hasFormChange()">{{ 'BUTTON.SAVE' | translate }}</button>
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="inProgress" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" id="ruleBtnOk" class="btn btn-primary" [clrLoading]="inProgress" (click)="onSubmit()" [disabled]="!isValid || !hasFormChange()">{{ 'BUTTON.SAVE' | translate }}</button>
</div>
</clr-modal>

View File

@ -286,3 +286,21 @@ clr-modal {
.flex {
display: flex;
}
.sub-label {
display: inline-block;
width: 100px;
margin-right: 10px;
}
.clr-control-label {
width: 8.6rem;
}
.count {
width: 34px;
margin-left: 10px;
}
.des-tooltip {
margin-left: 10px;
}
.count-type {
width: 158px;
}

View File

@ -267,7 +267,7 @@ describe("CreateEditRuleComponent (inline template)", () => {
it("Should open modal to edit replication rule", fakeAsync( () => {
fixture.detectChanges();
comp.openCreateEditRule(mockRule.id);
comp.openCreateEditRule(mockRule);
fixture.detectChanges();
tick(5000);
const ruleNameInput: HTMLInputElement = fixture.nativeElement.querySelector("#ruleName");

View File

@ -33,9 +33,13 @@ import { cronRegex } from "../../../../../shared/units/utils";
import { FilterType } from "../../../../../shared/entities/shared.const";
import { RegistryService } from "../../../../../../../ng-swagger-gen/services/registry.service";
import { Registry } from "../../../../../../../ng-swagger-gen/models/registry";
import { Label } from "../../../../../../../ng-swagger-gen/models/label";
import { LabelService } from "../../../../../../../ng-swagger-gen/services/label.service";
import { Flatten_I18n_MAP, Flatten_Level } from "../../replication";
const PREFIX: string = '0 ';
const PAGE_SIZE: number = 100;
@Component({
selector: "hbr-create-edit-rule",
templateUrl: "./create-edit-rule.component.html",
@ -77,12 +81,14 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
@Output() reload = new EventEmitter<boolean>();
@ViewChild(InlineAlertComponent, {static: true}) inlineAlert: InlineAlertComponent;
flattenLevelMap = Flatten_I18n_MAP;
constructor(
private fb: FormBuilder,
private repService: ReplicationService,
private endpointService: RegistryService,
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private labelService: LabelService,
) {
this.createForm();
}
@ -95,8 +101,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.supportedFilters = adapter.supported_resource_filters;
this.supportedFilters.forEach(element => {
this.filters.push(this.initFilter(element.type));
// get supportedFilterLabels labels from supportedFilters
this.getLabelListFromAdapter(element);
});
this.supportedTriggers = adapter.supported_triggers;
this.ruleForm.get("trigger").get("type").setValue(this.supportedTriggers[0]);
@ -142,6 +146,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
});
}
ngOnInit(): void {
this.getAllLabels();
this.getAllRegistries();
this.nameChecker
.pipe(debounceTime(300))
@ -227,6 +232,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
src_registry: new FormControl(),
dest_registry: new FormControl(),
dest_namespace: "",
dest_namespace_replace_count: -1,
trigger: this.fb.group({
type: '',
trigger_settings: this.fb.group({
@ -261,7 +267,8 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
},
deletion: false,
enabled: true,
override: true
override: true,
dest_namespace_replace_count: Flatten_Level.FLATTEN_LEVEl_1
});
this.isPushMode = true;
}
@ -276,6 +283,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
name: rule.name,
description: rule.description,
dest_namespace: rule.dest_namespace,
dest_namespace_replace_count: rule.dest_namespace_replace_count,
src_registry: rule.src_registry,
dest_registry: rule.dest_registry,
trigger: rule.trigger,
@ -284,7 +292,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
override: rule.override
});
let filtersArray = this.getFilterArray(rule);
this.noSelectedEndpoint = false;
this.setFilter(filtersArray);
this.copyUpdateForm = clone(this.ruleForm.value);
@ -359,6 +366,8 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
// add new Replication rule
this.inProgress = true;
let copyRuleForm: ReplicationRule = this.ruleForm.value;
copyRuleForm.dest_namespace_replace_count = copyRuleForm.dest_namespace_replace_count
? parseInt(copyRuleForm.dest_namespace_replace_count.toString(), 10) : 0;
if (this.isPushMode) {
copyRuleForm.src_registry = null;
} else {
@ -404,26 +413,38 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
});
}
}
openCreateEditRule(ruleId?: number | string): void {
openCreateEditRule(rule?: ReplicationRule): void {
this.formReset();
this.copyUpdateForm = clone(this.ruleForm.value);
this.inlineAlert.close();
this.noSelectedEndpoint = true;
this.isRuleNameValid = true;
this.supportedFilterLabels = [];
this.policyId = -1;
this.createEditRuleOpened = true;
this.noEndpointInfo = "";
if (this.targetList.length === 0) {
this.noEndpointInfo = "REPLICATION.NO_ENDPOINT_INFO";
}
if (ruleId) {
if (rule) {
if (this.supportedFilterLabels && this.supportedFilterLabels.length) {
this.supportedFilterLabels.forEach((label, index) => {
if (rule.filters && rule.filters.length) {
rule.filters.forEach(f => {
if (f.type === FilterType.LABEL && f.value && f.value.length) {
f.value.forEach(name => {
if (label.name === name) {
this.supportedFilterLabels[index].select = true;
}
});
}
});
}
});
}
this.onGoing = true;
this.policyId = +ruleId;
this.policyId = +rule.id;
this.headerTitle = "REPLICATION.EDIT_POLICY_TITLE";
this.repService.getReplicationRule(ruleId)
this.repService.getReplicationRule(rule.id)
.subscribe((ruleInfo) => {
let srcRegistryId = ruleInfo.src_registry.id;
this.repService.getRegistryInfo(srcRegistryId)
@ -458,58 +479,11 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.setFilter([]);
this.supportedFilters.forEach(element => {
this.filters.push(this.initFilter(element.type));
// get supportedFilterLabels labels from supportedFilters
this.getLabelListFromAdapter(element);
// only when edit replication rule
if (ruleInfo && ruleInfo.filters && this.supportedFilterLabels.length ) {
this.getLabelListFromRuleInfo(ruleInfo);
}
});
this.supportedTriggers = adapter.supported_triggers;
this.ruleForm.get("trigger").get("type").setValue(this.supportedTriggers[0]);
}
getLabelListFromAdapter(supportedFilter) {
if (supportedFilter.type === FilterType.LABEL && supportedFilter.values) {
this.supportedFilterLabels = [];
supportedFilter.values.forEach( value => {
this.supportedFilterLabels.push({
name: value,
color: '#FFFFFF',
select: false,
scope: 'g'
});
});
}
}
getLabelListFromRuleInfo(ruleInfo) {
let labelValueObj = ruleInfo.filters.find((currentValue) => {
return currentValue.type === FilterType.LABEL;
});
if (labelValueObj) {
for (const labelValue of labelValueObj.value) {
let flagLabel = this.supportedFilterLabels.every((currentValue) => {
return currentValue.name !== labelValue;
});
if (flagLabel) {
this.supportedFilterLabels = [
{
name: labelValue,
color: '#FFFFFF',
select: true,
scope: 'g'
}, ...this.supportedFilterLabels];
}
//
for (const labelObj of this.supportedFilterLabels) {
if (labelObj.name === labelValue) {
labelObj.select = true;
}
}
}
}
}
close(): void {
this.createEditRuleOpened = false;
}
@ -612,4 +586,56 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}
}
}
getAllLabels(): void {
// get all global labels
this.labelService.ListLabelsResponse({
pageSize: PAGE_SIZE,
page: 1,
scope: 'g',
}).subscribe(res => {
if (res.headers) {
const xHeader: string = res.headers.get("X-Total-Count");
const totalCount = parseInt(xHeader, 0);
let arr = res.body || [];
if (totalCount <= PAGE_SIZE) { // already gotten all global labels
if (arr && arr.length) {
arr.forEach(data => {
this.supportedFilterLabels.push({
name: data.name,
color: data.color ? data.color : '#FFFFFF',
select: false,
scope: 'g'
});
});
}
} else { // get all the global labels in specified times
const times: number = Math.ceil(totalCount / PAGE_SIZE);
const observableList: Observable<Label[]>[] = [];
for (let i = 2; i <= times; i++) {
observableList.push(this.labelService.ListLabels({
page: i,
pageSize: PAGE_SIZE,
scope: 'g',
}));
}
forkJoin(observableList)
.subscribe(response => {
if (response && response.length) {
response.forEach(item => {
arr = arr.concat(item);
});
arr.forEach(data => {
this.supportedFilterLabels.push({
name: data.name,
color: data.color ? data.color : '#FFFFFF',
select: false,
scope: 'g'
});
});
}
});
}
}
});
}
}

View File

@ -46,6 +46,7 @@
<clr-dg-column class="col-width">{{'REPLICATION.SRC_REGISTRY' | translate}}</clr-dg-column>
<clr-dg-column class="col-width">{{'REPLICATION.REPLICATION_MODE' | translate}}</clr-dg-column>
<clr-dg-column class="min-width">{{'REPLICATION.DESTINATION_NAMESPACE' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.DES_REPO_FLATTENING' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.REPLICATION_TRIGGER' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
@ -79,6 +80,7 @@
<clr-dg-cell class="min-width">
{{p.dest_registry ? p.dest_registry.name : ''}} : {{p.dest_namespace? p.dest_namespace: '-'}}
</clr-dg-cell>
<clr-dg-cell>{{(getFlattenLevelString(p.dest_namespace_replace_count)) | translate}}</clr-dg-cell>
<clr-dg-cell>{{p.trigger ? p.trigger.type : ''}}</clr-dg-cell>
<clr-dg-cell>
{{p.description ? trancatedDescription(p.description) : '-'}}

View File

@ -6,7 +6,7 @@
vertical-align: text-bottom;
}
.min-width {
width: 224px;
width: 236px;
}
.col-width {
width: 140px;
@ -20,4 +20,4 @@
}
.margin-right-2px {
margin-right: 2px;
}
}

View File

@ -40,6 +40,7 @@ import { errorHandler } from "../../../../../shared/units/shared.utils";
import { ConfirmationAcknowledgement } from "../../../../global-confirmation-dialog/confirmation-state-message";
import { ConfirmationMessage } from "../../../../global-confirmation-dialog/confirmation-message";
import { HELM_HUB } from "../../../../../shared/services/endpoint.service";
import { Flatten_I18n_MAP } from "../../replication";
@Component({
selector: "hbr-list-replication-rule",
templateUrl: "./list-replication-rule.component.html",
@ -261,4 +262,10 @@ export class ListReplicationRuleComponent {
isHelmHub(srcRegistry: any): boolean {
return srcRegistry && srcRegistry.type === HELM_HUB;
}
getFlattenLevelString(level: number) {
if (level !== null && Flatten_I18n_MAP[level]) {
return Flatten_I18n_MAP[level];
}
return level;
}
}

View File

@ -209,7 +209,7 @@ export class ReplicationComponent implements OnInit, OnDestroy {
// edit replication rule
openEditRule(rule: ReplicationRule) {
if (rule) {
this.createEditPolicyComponent.openCreateEditRule(rule.id);
this.createEditPolicyComponent.openCreateEditRule(rule);
}
}

View File

@ -72,6 +72,7 @@ export interface ReplicationRule extends Base {
dest_registry?: any;
src_namespaces: string[];
dest_namespace?: string;
dest_namespace_replace_count?: number;
enabled: boolean;
override: boolean;
}

View File

@ -621,14 +621,23 @@
"TAG": "Tag",
"LABEL": "Label",
"RESOURCE": "Ressource",
"ENABLE_TITLE": "Aktiviere Regel",
"ENABLE_SUMMARY": "Soll die Regel {{param}} aktiviert werden?",
"DISABLE_TITLE": "Deaktiviere Regel",
"DISABLE_SUMMARY": "Soll die Regel {{param}} deaktiviert werden?",
"ENABLE_SUCCESS": "Aktivieren der Regel erfolgreich",
"ENABLE_TITLE": "Aktiviere Regel",
"ENABLE_SUMMARY": "Soll die Regel {{param}} aktiviert werden?",
"DISABLE_TITLE": "Deaktiviere Regel",
"DISABLE_SUMMARY": "Soll die Regel {{param}} deaktiviert werden?",
"ENABLE_SUCCESS": "Aktivieren der Regel erfolgreich",
"ENABLE_FAILED": "Aktivieren der Regel fehlgeschlagen",
"DISABLE_SUCCESS": "Deaktivieren der Regel erfolgreich",
"DISABLE_FAILED": "Deaktivieren der Regel fehlgeschlagen"
"DISABLE_FAILED": "Deaktivieren der Regel fehlgeschlagen",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "Neuer Endpunkt",

View File

@ -545,7 +545,7 @@
"DISABLE": "Disable",
"REPLICATION_MODE": "Replication Mode",
"SRC_REGISTRY": "Source registry",
"DESTINATION_NAMESPACE": "Destination registry:Namespace",
"DESTINATION_NAMESPACE": "Destination Registry:Namespace",
"DESTINATION_NAME_IS_REQUIRED": "Endpoint name is required.",
"NEW_DESTINATION": "New Endpoint",
"DESTINATION_URL": "Endpoint URL",
@ -628,7 +628,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "New Endpoint",

View File

@ -629,7 +629,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint",

View File

@ -618,7 +618,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "Nouveau Point Final",

View File

@ -628,7 +628,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "Novo Endpoint",

View File

@ -628,7 +628,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION": {
"NEW_ENDPOINT": "Yeni Uç Nokta",

View File

@ -71,7 +71,7 @@
"RESOURCE_FILTER": "过滤资源的类型。",
"PUSH_BASED": "把资源由本地Harbor推送到远端仓库。",
"PULL_BASED": "把资源由远端仓库拉取到本地Harbor。",
"DESTINATION_NAMESPACE": "指定目的端名称空间。如果不填,资源会被放到和源相同的名称空间下。",
"DESTINATION_NAMESPACE": "指定目名称空间。如果不填,资源会被放到和源相同的名称空间下。",
"OVERRIDE": "如果存在具有相同名称的资源,请指定是否覆盖目标上的资源。",
"EMAIL": "请使用正确的邮箱地址比如name@example.com。",
"USER_NAME": "不能包含特殊字符且长度不能超过255。",
@ -546,7 +546,7 @@
"DISABLE": "停用",
"REPLICATION_MODE": "复制模式",
"SRC_REGISTRY": "源仓库",
"DESTINATION_NAMESPACE": "目标仓库:名空间",
"DESTINATION_NAMESPACE": "目标仓库:空间",
"DESTINATION_NAME_IS_REQUIRED": "目标名称为必填项。",
"NEW_DESTINATION": "创建目标",
"DESTINATION_URL": "目标URL",
@ -573,6 +573,7 @@
"ADVANCED": "高级检索",
"STATUS": "状态",
"OPERATION": "操作",
"REPLICATION_TRIGGER": "触发器",
"CREATION_TIME": "创建时间",
"UPDATE_TIME": "更新时间",
"END_TIME": "结束时间",
@ -610,15 +611,15 @@
"DELETE_ENABLED": "默认启用该规则",
"NEW": "新增",
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"DESTINATION_NAME_TOOLTIP": "目标名称由小写字符、数字和._-/组成且至少2个字符并以字符或者数字开头。",
"DESTINATION_NAME_TOOLTIP": "名称空间由小写字符、数字和._-/组成且至少2个字符并以字符或者数字开头。",
"ACKNOWLEDGE": "确认",
"RULE_DISABLED": "这个规则因为过滤选项中的标签被删除已经不能用了,更新过滤项以便重新启用规则。",
"REPLI_MODE": "复制模式",
"SOURCE_REGISTRY": "源Registry",
"SOURCE_NAMESPACES": "源Namespace",
"DEST_REGISTRY": "目的Registry",
"DEST_NAMESPACE": "目的Namespace",
"NAMESPACE_TOOLTIP": "Namespace名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"SOURCE_REGISTRY": "源仓库",
"SOURCE_NAMESPACES": "源名称空间",
"DEST_REGISTRY": "目标仓库",
"DEST_NAMESPACE": "目标名称空间",
"NAMESPACE_TOOLTIP": "名称空间名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"TAG": "Tag",
"LABEL": "标签",
"RESOURCE": "资源",
@ -629,7 +630,16 @@
"ENABLE_SUCCESS": "启用规则成功",
"ENABLE_FAILED": "启用规则失败",
"DISABLE_SUCCESS": "禁用规则成功",
"DISABLE_FAILED": "禁用规则失败"
"DISABLE_FAILED": "禁用规则失败",
"DES_REPO_FLATTENING": "目标仓库扁平化",
"NAMESPACE": "名称空间",
"REPO_FLATTENING": "仓库扁平化",
"NO_FLATTING": "无替换",
"FLATTEN_LEVEL_1": "替换1级",
"FLATTEN_LEVEL_2": "替换2级",
"FLATTEN_LEVEL_3": "替换3级",
"FLATTEN_ALL": "替换所有级",
"FLATTEN_LEVEL_TIP": "此项用以在复制镜像时减少仓库的层级结构。默认值'替换1级'表示将用目标名称空间替换仓库的最左侧一级例如a/b/c/img -> namespace/b/c/img其他替换层级以此类推。'替换所有级'是v2.3之前版本所使用的行为,它表示 a/b/c/img -> namespace/img。"
},
"DESTINATION": {
"NEW_ENDPOINT": "新建目标",

View File

@ -625,7 +625,16 @@
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
"DISABLE_FAILED": "Disabling rule failed",
"DES_REPO_FLATTENING": "Destination Repository Flattening",
"NAMESPACE": "Namespace",
"REPO_FLATTENING": "Flattening",
"NO_FLATTING": "No Flatting",
"FLATTEN_LEVEL_1": "Flatten 1 Level",
"FLATTEN_LEVEL_2": "Flatten 2 Levels",
"FLATTEN_LEVEL_3": "Flatten 3 Levels",
"FLATTEN_ALL": "Flatten All Levels",
"FLATTEN_LEVEL_TIP": "Reduce the nested repository structure when copying images. Default 'Flatten 1 Level' will replace the left most repository component with the destination namespace a/b/c/img -> ns/b/c/img. 'Flatten all Levels' is the legacy option used prior v2.3 resulting in a/b/c/img -> ns/img."
},
"DESTINATION":{
"NEW_ENDPOINT": "新建目標",