mirror of https://github.com/goharbor/harbor.git
229 lines
14 KiB
HTML
229 lines
14 KiB
HTML
<clr-modal [(clrModalOpen)]="createEditRuleOpened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
|
|
<h3 class="modal-title">{{headerTitle | translate}}</h3>
|
|
<div class="modal-body modal-body-height">
|
|
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
|
|
<form [formGroup]="ruleForm" novalidate class="clr-form clr-form-horizontal">
|
|
<div class="clr-form-control" [class.clr-error]="(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || !isRuleNameValid">
|
|
<label class="clr-control-label required">{{'REPLICATION.NAME' | translate}}</label>
|
|
<div class="clr-control-container">
|
|
<div class="clr-input-wrapper">
|
|
<input class="clr-input" type="text" id="ruleName" size="35" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" required maxlength="255"
|
|
formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
|
|
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
|
<span class="spinner spinner-inline spinner-pos" [hidden]="!inNameChecking"></span>
|
|
</div>
|
|
<clr-control-error *ngIf="(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || !isRuleNameValid">{{ruleNameTooltip | translate}}</clr-control-error>
|
|
</div>
|
|
</div>
|
|
<!--Description-->
|
|
<clr-textarea-container>
|
|
<label>{{'REPLICATION.DESCRIPTION' | translate}}</label>
|
|
<textarea clrTextarea type="text" id="ruleDescription" class="inputWidth" row=3 formControlName="description"></textarea>
|
|
</clr-textarea-container>
|
|
<!-- replication mode -->
|
|
<clr-radio-container clrInline>
|
|
<label>{{'REPLICATION.REPLI_MODE' | translate}}</label>
|
|
<clr-radio-wrapper>
|
|
<input clrRadio type="radio" id="push_base" name="replicationMode" [value]=true [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode"
|
|
(change)="pushModeChange()" [ngModelOptions]="{standalone: true}">
|
|
<label for="push_base">Push-based
|
|
<clr-tooltip class="mode-tooltip">
|
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
|
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
|
|
<span>{{'TOOLTIP.PUSH_BASED' | translate}}</span>
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
</label>
|
|
</clr-radio-wrapper>
|
|
<clr-radio-wrapper>
|
|
<input clrRadio type="radio" id="pull_base" name="replicationMode" [value]=false [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode"
|
|
(change)="pullModeChange()" [ngModelOptions]="{standalone: true}">
|
|
<label for="pull_base">Pull-based
|
|
<clr-tooltip class="mode-tooltip">
|
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
|
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
|
|
<span>{{'TOOLTIP.PULL_BASED' | translate}}</span>
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
</label>
|
|
</clr-radio-wrapper>
|
|
</clr-radio-container>
|
|
<!--source registry-->
|
|
<div class="clr-form-control" *ngIf="!isPushMode">
|
|
<label class="required clr-control-label">{{'REPLICATION.SOURCE_REGISTRY' | translate}}</label>
|
|
<div class="clr-control-container">
|
|
<div class="clr-select-wrapper">
|
|
<select class="clr-select select-width" id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry"
|
|
[compareWith]="equals">
|
|
<option class="display-none"></option>
|
|
<option *ngFor="let source of sourceList" [ngValue]="source">{{source.name}}-{{source.url}}</option>
|
|
</select>
|
|
</div>
|
|
<div class="space-between">
|
|
<span *ngIf="noEndpointInfo.length != 0" class="alert-label">{{noEndpointInfo | translate}}</span>
|
|
<span class="alert-label go-link" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--images/Filter-->
|
|
<div class="clr-form-control">
|
|
<label class="clr-control-label">{{'REPLICATION.SOURCE_RESOURCE_FILTER' | translate}}</label>
|
|
<span class="spinner spinner-inline spinner-position" [hidden]="onGoing === false"></span>
|
|
<div formArrayName="filters" class="clr-control-container">
|
|
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index">
|
|
<div [formGroupName]="i" >
|
|
<div class="width-70">
|
|
<label>{{"REPLICATION." + supportedFilters[i]?.type.toUpperCase() | translate}}:</label>
|
|
</div>
|
|
<label *ngIf="supportedFilters[i]?.style==='input'" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
|
|
[class.invalid]='(filter.value.dirty || filter.value.touched) && filter.value.invalid'>
|
|
<input class="clr-input" (input)="trimText($event)" type="text" #filterValue size="14" formControlName="value" id="{{'filter_'+ supportedFilters[i]?.type}}">
|
|
</label>
|
|
<div class="select resource-box clr-select-wrapper" *ngIf="supportedFilters[i]?.style==='radio' && supportedFilters[i]?.values.length > 1">
|
|
<select class="clr-select width-100" formControlName="value" #selectedValue id="{{'select_'+ supportedFilters[i]?.type}}"
|
|
name="{{supportedFilters[i]?.type}}">
|
|
<option value="">{{'REPLICATION.ALL' | translate}}</option>
|
|
<option *ngFor="let value of supportedFilters[i]?.values;" value="{{value}}">{{value}}{{value==='chart'? ' (chartmuseum)': ''}}</option>
|
|
</select>
|
|
</div>
|
|
<div class="select resource-box" *ngIf="supportedFilters[i]?.type==='label'&& supportedFilters[i]?.style==='list'">
|
|
<div class="dropdown width-100 clr-select-wrapper" formArrayName="value">
|
|
<clr-dropdown class="width-100">
|
|
<button type="button" class="width-100 dropdown-toggle btn btn-link statistic-data label-text" clrDropdownTrigger>
|
|
<ng-template ngFor let-label [ngForOf]="filter.value.value" let-m="index">
|
|
<span class="label" *ngIf="m<1"> {{label}} </span>
|
|
</ng-template>
|
|
<span class="ellipsis color-white-dark" *ngIf="filter.value.value.length>1">···</span>
|
|
<div *ngFor="let label1 of filter.value.value;let k = index" hidden="true">
|
|
<input type="text" [formControlName]="k" #labelValue id="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}" name="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}"
|
|
placeholder="select labels">
|
|
</div>
|
|
</button>
|
|
<clr-dropdown-menu class="width-100" clrPosition="bottom-left" *clrIfOpen>
|
|
<button type="button" class="dropdown-item" *ngFor="let value of supportedFilterLabels" (click)="stickLabel(value,i)">
|
|
<clr-icon shape="check" [hidden]="!value.select" class='pull-left'></clr-icon>
|
|
<div class='labelDiv'>
|
|
<hbr-label-piece [label]="value" [labelWidth]="130"></hbr-label-piece>
|
|
</div>
|
|
</button>
|
|
</clr-dropdown-menu>
|
|
</clr-dropdown>
|
|
</div>
|
|
</div>
|
|
<div class="resource-box" *ngIf="supportedFilters[i]?.style==='radio' && supportedFilters[i]?.values.length <= 1">
|
|
<span>{{supportedFilters[i]?.values}}</span>
|
|
</div>
|
|
<clr-tooltip>
|
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
|
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
|
|
<span class="tooltip-content" *ngIf="supportedFilters[i]?.type==='name'">{{'TOOLTIP.NAME_FILTER' | translate}}</span>
|
|
<span class="tooltip-content" *ngIf="supportedFilters[i]?.type==='tag'">{{'TOOLTIP.TAG_FILTER' | translate}}</span>
|
|
<span class="tooltip-content" *ngIf="supportedFilters[i]?.type==='label'">{{'TOOLTIP.LABEL_FILTER' | translate}}</span>
|
|
<span class="tooltip-content" *ngIf="supportedFilters[i]?.type==='resource'">{{'TOOLTIP.RESOURCE_FILTER' | translate}}</span>
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--destination registry-->
|
|
<div *ngIf="isPushMode" class="clr-form-control">
|
|
<label class="clr-control-label required">{{'REPLICATION.DEST_REGISTRY' | 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>
|
|
<div class="form-select clr-control-container">
|
|
<div class="clr-select-wrapper">
|
|
<select class="clr-select select-width" id="dest_registry" (change)="targetChange($event)" formControlName="dest_registry"
|
|
[compareWith]="equals">
|
|
<option class="display-none"></option>
|
|
<option *ngFor="let target of targetList" [ngValue]="target">{{target.name}}-{{target.url}}</option>
|
|
</select>
|
|
</div>
|
|
<div class="space-between">
|
|
<label *ngIf="noEndpointInfo.length != 0" class="alert-label">{{noEndpointInfo | translate}}</label>
|
|
<span class="alert-label go-link" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
|
|
</div>
|
|
</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>
|
|
<!--Trigger-->
|
|
<div class="clr-form-control">
|
|
<label class="clr-control-label required">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
|
|
<div class="clr-control-container">
|
|
<div formGroupName="trigger">
|
|
<!--on trigger-->
|
|
<div class="select width-115 clr-select-wrapper">
|
|
<select (change)="changeTrigger($event)" id="ruleTrigger" formControlName="type" class="clr-select">
|
|
<option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{'REPLICATION.' + trigger.toUpperCase() | translate }}</option>
|
|
</select>
|
|
</div>
|
|
<div formGroupName="trigger_settings" class="clr-form-control">
|
|
<div class="flex" [hidden]="isNotSchedule()">
|
|
<label for="targetCron" class="required">Cron String</label>
|
|
<div class="clr-control-container" [class.clr-error]="cronInputShouldShowError()">
|
|
<div class="clr-input-wrapper">
|
|
<input autocomplete="off" (input)="inputInvalid($event)" type="text" name=targetCron id="targetCron" required class="form-control cron-input clr-input" formControlName="cron">
|
|
</div>
|
|
<clr-control-error *ngIf="cronInputShouldShowError()">
|
|
{{'TOOLTIP.CRON_REQUIRED' | translate }}
|
|
</clr-control-error>
|
|
</div>
|
|
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-left top-7 cron-tooltip">
|
|
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
|
<div class="tooltip-content table-box">
|
|
<cron-tooltip></cron-tooltip>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="clr-checkbox-wrapper clr-form-control" [hidden]="isNotEventBased()">
|
|
<input type="checkbox" class="clr-checkbox" [checked]="false" id="ruleDeletion" formControlName="deletion">
|
|
<label for="ruleDeletion">{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}</label>
|
|
</div>
|
|
<div class="clr-checkbox-wrapper clr-form-control">
|
|
<input type="checkbox" class="clr-checkbox" [checked]="true" id="overridePolicy" formControlName="override">
|
|
<label for="overridePolicy">{{'REPLICATION.OVERRIDE_INFO' | translate}}
|
|
<clr-tooltip class="override-tooltip">
|
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
|
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
|
|
<span>{{'TOOLTIP.OVERRIDE' | translate}}</span>
|
|
</clr-tooltip-content>
|
|
</clr-tooltip>
|
|
</label>
|
|
</div>
|
|
<div class="clr-checkbox-wrapper clr-form-control" [hidden]="policyId < 0">
|
|
<input type="checkbox" [checked]="true" id="enablePolicy" formControlName="enabled" class="clr-checkbox">
|
|
<label for="enablePolicy" class="clr-control-label">{{'REPLICATION.ENABLED_RULE' | translate}}</label>
|
|
</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>
|
|
</div>
|
|
</clr-modal>
|