mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Modify replication list to batch delection mode and add admin privilege to project replication module
Modity text about new replication rule
This commit is contained in:
parent
a214084370
commit
78dcdc409f
@ -30,12 +30,17 @@ export const CONFIRMATION_DIALOG_TEMPLATE: string = `
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="delete()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
||||
`;
|
@ -40,7 +40,6 @@ export class ConfirmationDialogComponent {
|
||||
@Input() batchInfors: BatchInfo[] = [];
|
||||
isDelete: boolean = false;
|
||||
|
||||
|
||||
constructor(
|
||||
private translate: TranslateService) {}
|
||||
|
||||
@ -98,7 +97,7 @@ export class ConfirmationDialogComponent {
|
||||
this.close();
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
operate(): void {
|
||||
if (!this.message){//Inproper condition
|
||||
this.close();
|
||||
return;
|
||||
|
@ -1,12 +1,12 @@
|
||||
export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<div style="padding-bottom: 15px;">
|
||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectedChange()">
|
||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||
<clr-dg-action-bar style="height:24px;">
|
||||
<div class="btn-group" *ngIf="opereateAvailable">
|
||||
<div class="btn-group" *ngIf="opereateAvailable || isSystemAdmin">
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="openModal()">{{'REPLICATION.NEW_REPLICATION_RULE' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow" (click)="editRule(selectedRow)">{{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow" (click)="deleteRule(selectedRow)">{{'REPLICATION.DELETE_POLICY' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)">{{'REPLICATION.REPLICATE' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="selectedRow.length !== 1" (click)="editRule(selectedRow)">{{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="selectedRow.length == 0" (click)="deleteRule(selectedRow)">{{'REPLICATION.DELETE_POLICY' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="selectedRow.length == 0" (click)="replicateRule(selectedRow)">{{'REPLICATION.REPLICATE' | translate}}</button>
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
@ -15,14 +15,14 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgField]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.SCHEDULE' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" (click)="selectRule(p)" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||
<clr-dg-cell>{{p.name}}</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="!projectScope">
|
||||
<clr-dg-row *ngFor="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||
<clr-dg-cell (click)="selectRule(p)">{{p.name}}</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="!projectScope" (click)="selectRule(p)">
|
||||
<a href="javascript:void(0)" (click)="redirectTo(p)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.description ? p.description : '-'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
|
||||
<clr-dg-cell (click)="selectRule(p)">{{p.description ? p.description : '-'}}</clr-dg-cell>
|
||||
<clr-dg-cell (click)="selectRule(p)">{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell>
|
||||
<clr-dg-cell (click)="selectRule(p)">{{p.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
||||
|
@ -55,6 +55,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
nullTime: string = '0001-01-01T00:00:00Z';
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() selectedId: number | string;
|
||||
@Input() withReplicationJob: boolean;
|
||||
@Input() readonly: boolean;
|
||||
@ -67,16 +68,17 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
@Output() toggleOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||
@Output() openNewRule = new EventEmitter<any>();
|
||||
@Output() replicateManual = new EventEmitter<ReplicationRule>();
|
||||
@Output() replicateManual = new EventEmitter<ReplicationRule[]>();
|
||||
@Output() hasJobs = new EventEmitter<boolean>();
|
||||
|
||||
projectScope: boolean = false;
|
||||
|
||||
rules: ReplicationRule[];
|
||||
changedRules: ReplicationRule[];
|
||||
ruleName: string;
|
||||
canDeleteRule: boolean;
|
||||
canDeleteRuleList: boolean[] = [];
|
||||
|
||||
selectedRow: ReplicationRule;
|
||||
selectedRow: ReplicationRule[] = [];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
|
||||
@ViewChild('toggleConfirmDialog')
|
||||
@ -100,7 +102,6 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
return !this.readonly && !this.projectId ? true : false;
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
//Global scope
|
||||
if (!this.projectScope) {
|
||||
@ -122,13 +123,9 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 200);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
}
|
||||
|
||||
retrieveRules(ruleName: string = ''): void {
|
||||
this.loading = true;
|
||||
this.selectedRow = [];
|
||||
toPromise<ReplicationRule[]>(this.replicationService
|
||||
.getReplicationRules(this.projectId, ruleName))
|
||||
.then(rules => {
|
||||
@ -176,38 +173,30 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
replicateRule(rules: ReplicationRule[]): void {
|
||||
this.replicateManual.emit(rules);
|
||||
}
|
||||
|
||||
deletionConfirm(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.POLICY &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let rule: ReplicationRule = message.data;
|
||||
toPromise<any>(this.replicationService
|
||||
.deleteReplicationRule(rule.id))
|
||||
.then(() => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => {
|
||||
this.batchDelectionInfos[0] = BathInfoChanges(this.batchDelectionInfos[0], res);
|
||||
});
|
||||
this.reload.emit(true);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
||||
this.batchDelectionInfos[0] = BathInfoChanges(this.batchDelectionInfos[0], res[0], false, true, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
this.batchDelectionInfos[0] = BathInfoChanges(this.batchDelectionInfos[0], res, false, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.deleteOpe(message.data);
|
||||
}
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
if (this.selectedRow.length !== 0) {
|
||||
this.hasJobs.emit(false);
|
||||
}
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 200);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
}
|
||||
|
||||
selectRule(rule: ReplicationRule): void {
|
||||
this.selectedId = rule.id || '';
|
||||
this.selectedRow = [];
|
||||
this.selectOne.emit(rule);
|
||||
this.hasJobs.emit(true);
|
||||
}
|
||||
|
||||
redirectTo(rule: ReplicationRule): void {
|
||||
@ -218,12 +207,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
this.openNewRule.emit();
|
||||
}
|
||||
|
||||
editRule(rules: ReplicationRule) {
|
||||
this.editOne.emit(rules);
|
||||
}
|
||||
|
||||
replicateRule(rule: ReplicationRule) {
|
||||
this.replicateManual.emit(rule);
|
||||
editRule(rules: ReplicationRule[]) {
|
||||
this.editOne.emit(rules[0]);
|
||||
}
|
||||
|
||||
toggleRule(rule: ReplicationRule) {
|
||||
@ -237,12 +222,12 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
this.toggleConfirmDialog.open(toggleConfirmMessage);
|
||||
}
|
||||
|
||||
jobList(): Promise<void> {
|
||||
jobList(id: string | number): Promise<void> {
|
||||
let ruleData: ReplicationJobItem[];
|
||||
this.canDeleteRule = true;
|
||||
this.canDeleteRuleList = [];
|
||||
let count: number = 0;
|
||||
return toPromise<ReplicationJob>(this.replicationService
|
||||
.getJobs(this.selectedId))
|
||||
.getJobs(id))
|
||||
.then(response => {
|
||||
ruleData = response.data;
|
||||
if (ruleData.length) {
|
||||
@ -252,37 +237,82 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
}
|
||||
});
|
||||
}
|
||||
this.canDeleteRule = count > 0 ? false : true;
|
||||
let canDeleteRule: boolean = count > 0 ? false : true;
|
||||
this.canDeleteRuleList.push(canDeleteRule);
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
deleteRule(rule: ReplicationRule) {
|
||||
this.jobList().then(() => {
|
||||
let deletionMessage: ConfirmationMessage;
|
||||
if (!this.canDeleteRule) {
|
||||
deletionMessage = new ConfirmationMessage(
|
||||
'REPLICATION.DELETION_TITLE_FAILURE',
|
||||
'REPLICATION.DELETION_SUMMARY_FAILURE',
|
||||
rule.name || '',
|
||||
rule,
|
||||
ConfirmationTargets.POLICY,
|
||||
ConfirmationButtons.CLOSE);
|
||||
} else {
|
||||
deleteRule(rules: ReplicationRule[]) {
|
||||
if (rules && rules.length) {
|
||||
let nameArr: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = rule.name;
|
||||
rules.forEach(data => {
|
||||
nameArr.push(data.name);
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = data.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
deletionMessage = new ConfirmationMessage(
|
||||
});
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
'REPLICATION.DELETION_TITLE',
|
||||
'REPLICATION.DELETION_SUMMARY',
|
||||
rule.name || '',
|
||||
rule,
|
||||
nameArr.join(',') || '',
|
||||
rules,
|
||||
ConfirmationTargets.POLICY,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
}
|
||||
this.deletionConfirmDialog.open(deletionMessage);
|
||||
}
|
||||
}
|
||||
deleteOpe(rules: ReplicationRule[]) {
|
||||
if (rules && rules.length) {
|
||||
let promiseLists: any[] = [];
|
||||
let promiseJobLists: any[] = [];
|
||||
rules.forEach(rule => {
|
||||
promiseJobLists.push(this.jobList(rule.id));
|
||||
})
|
||||
|
||||
Promise.all(promiseJobLists).then(items => {
|
||||
this.canDeleteRuleList.forEach((item, index) => {
|
||||
if (!item) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === rules[index].name);
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.DELETION_SUMMARY_FAILURE')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
} else {
|
||||
promiseLists.push(this.delOperate(+rules[index].id, rules[index].name));
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then(item => {
|
||||
this.selectedRow = [];
|
||||
this.reload.emit(true);
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 200);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(ruleId: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return toPromise<any>(this.replicationService
|
||||
.deleteReplicationRule(ruleId))
|
||||
.then(() => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => findedList = BathInfoChanges(findedList, res));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -11,8 +11,9 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<hbr-list-replication-rule #listReplicationRule [readonly]="readonly" [projectId]="projectId" (replicateManual)=replicateManualRule($event) (selectOne)="selectOneRule($event)" (openNewRule)="openModal()" (editOne)="openEditRule($event)" (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
|
||||
<hbr-list-replication-rule #listReplicationRule [readonly]="readonly" [projectId]="projectId" [isSystemAdmin]="isSystemAdmin" (replicateManual)=replicateManualRule($event) (hasJobs)="hasJobList($event)" (selectOne)="selectOneRule($event)" (openNewRule)="openModal()" (editOne)="openEditRule($event)" (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" [hidden]="!hasJobs">
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between" style="height:60px;">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
@ -37,7 +38,11 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)">
|
||||
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)"><clr-dg-action-bar>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(jobs && jobs.length>0)" (click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
|
||||
@ -66,5 +71,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<job-log-viewer #replicationLogViewer></job-log-viewer>
|
||||
<confirmation-dialog #replicationConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmReplication($event)"></confirmation-dialog>
|
||||
</div>`;
|
@ -43,6 +43,11 @@ import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
import { State } from "clarity-angular";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
import {Subscription} from "rxjs/Subscription";
|
||||
import {ConfirmationTargets, ConfirmationButtons, ConfirmationState} from "../shared/shared.const";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {BatchInfo, BathInfoChanges} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
|
||||
const ruleStatus: { [key: string]: any } = [
|
||||
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' },
|
||||
@ -84,6 +89,7 @@ export class SearchOption {
|
||||
export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() projectId: number | string;
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() withReplicationJob: boolean;
|
||||
@Input() readonly: boolean;
|
||||
|
||||
@ -101,11 +107,13 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
changedRules: ReplicationRule[];
|
||||
initSelectedId: number | string;
|
||||
hasJobs: boolean;
|
||||
|
||||
rules: ReplicationRule[];
|
||||
loading: boolean;
|
||||
|
||||
jobs: ReplicationJobItem[];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
|
||||
toggleJobSearchOption = optionalSearch;
|
||||
currentJobSearchOption: number;
|
||||
@ -119,6 +127,9 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("replicationLogViewer")
|
||||
replicationLogViewer: JobLogViewerComponent;
|
||||
|
||||
@ViewChild('replicationConfirmDialog')
|
||||
replicationConfirmDialog: ConfirmationDialogComponent;
|
||||
|
||||
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
||||
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
||||
|
||||
@ -259,13 +270,61 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
replicateManualRule(rule: ReplicationRule): void {
|
||||
toPromise<any>(this.replicationService.replicateRule(rule.id))
|
||||
.then(response => {
|
||||
this.refreshJobs();
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
replicateManualRule(rules: ReplicationRule[]) {
|
||||
if (rules && rules.length) {
|
||||
let nameArr: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
rules.forEach(rule => {
|
||||
nameArr.push(rule.name);
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = rule.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
});
|
||||
let replicationMessage = new ConfirmationMessage(
|
||||
'REPLICATION.REPLICATION_TITLE',
|
||||
'REPLICATION.REPLICATION_SUMMARY',
|
||||
nameArr.join(', ') || '',
|
||||
rules,
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.REPLICATE_CANCEL);
|
||||
this.replicationConfirmDialog.open(replicationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
confirmReplication(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let rules: Endpoint[] = message.data;
|
||||
if (rules && rules.length) {
|
||||
let promiseLists: any[] = [];
|
||||
rules.forEach(rule => {
|
||||
this.replicationOperate(+rule.id, rule.name);
|
||||
})
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
this.listReplicationRule.retrieveRules();
|
||||
this.refreshJobs();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replicationOperate(ruleId: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
|
||||
return toPromise<any>(this.replicationService.replicateRule(ruleId))
|
||||
.then(response => {
|
||||
this.translateService.get('BATCH.REPLICATE_SUCCESS')
|
||||
.subscribe(res => findedList = BathInfoChanges(findedList, res));
|
||||
})
|
||||
.catch(error => {
|
||||
this.translateService.get('BATCH.REPLICATE_FAILURE').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
customRedirect(rule: ReplicationRule) {
|
||||
this.redirect.emit(rule);
|
||||
@ -295,6 +354,14 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
this.loadFirstPage();
|
||||
}
|
||||
|
||||
stopJobs() {
|
||||
if (this.jobs && this.jobs.length) {
|
||||
toPromise(this.replicationService.stopJobs(this.jobs[0].policy_id))
|
||||
.then(res => {this.refreshJobs(); })
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
reloadRules(isReady: boolean) {
|
||||
if (isReady) {
|
||||
this.search.ruleName = '';
|
||||
@ -306,6 +373,13 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
this.listReplicationRule.retrieveRules();
|
||||
}
|
||||
|
||||
hasJobList(hasJob: boolean): void {
|
||||
this.hasJobs = hasJob;
|
||||
if (this.hasJobs) {
|
||||
this.refreshJobs();
|
||||
}
|
||||
}
|
||||
|
||||
refreshJobs() {
|
||||
this.search.repoName = "";
|
||||
this.search.startTimestamp = "";
|
||||
|
@ -156,8 +156,9 @@ export class RepositoryListviewComponent implements OnChanges, OnInit {
|
||||
delOperate(repoName: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === repoName);
|
||||
if (this.signedCon[repoName].length !== 0) {
|
||||
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED').subscribe(res => {
|
||||
findedList.status = res;
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
});
|
||||
} else {
|
||||
return toPromise<number>(this.repositoryService
|
||||
|
@ -127,6 +127,8 @@ export abstract class ReplicationService {
|
||||
* @memberof ReplicationService
|
||||
*/
|
||||
abstract getJobLog(jobId: number | string): Observable<string> | Promise<string> | string;
|
||||
|
||||
abstract stopJobs(jobId: number | string): Observable<string> | Promise<string> | string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,4 +303,10 @@ export class ReplicationDefaultService extends ReplicationService {
|
||||
.then(response => response.text())
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
public stopJobs(jobId: number | string): Observable<any> | Promise<any> | any {
|
||||
return this.http.put(this._jobBaseUrl, JSON.stringify({'policy_id': jobId, 'status': 'stop' }), HTTP_JSON_OPTIONS).toPromise()
|
||||
.then(response => response)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
}
|
@ -65,5 +65,5 @@ export const enum ConfirmationState {
|
||||
};
|
||||
|
||||
export const enum ConfirmationButtons {
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, REPLICATE_CANCEL
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ export const TAG_TEMPLATE = `
|
||||
<clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbedded" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||
<clr-dg-action-bar>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!canScanNow(selectedRow)" [disabled]="!(selectedRow.length==1)" (click)="scanNow(selectedRow)">{{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1)" (click)="scanNow(selectedRow)">{{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" >{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasProjectAdminRole" (click)="deleteTags(selectedRow)" [disabled]="!selectedRow.length">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</div>
|
||||
@ -38,7 +38,7 @@ export const TAG_TEMPLATE = `
|
||||
<clr-dg-column style="min-width: 130px;">{{'REPOSITORY.AUTHOR' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 160px;"[clrDgSortBy]="createdComparator">{{'REPOSITORY.CREATED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 80px;" [clrDgField]="'docker_version'" *ngIf="!withClair">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'TGA.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-placeholder>{{'TAG.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let t of tags" [clrDgItem]='t'>
|
||||
<clr-dg-cell class="truncated" style="width: 160px;" [ngSwitch]="withClair">
|
||||
<a *ngSwitchCase="true" href="javascript:void(0)" (click)="onTagClick(t)" title="{{t.name}}">{{t.name}}</a>
|
||||
|
@ -286,8 +286,7 @@ export class TagComponent implements OnInit {
|
||||
if (tags && tags.length) {
|
||||
let promiseLists: any[] = [];
|
||||
tags.forEach(tag => {
|
||||
this.delOperate(tag.signature, tag.name);
|
||||
|
||||
promiseLists.push(this.delOperate(tag.signature, tag.name));
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
|
@ -31,7 +31,7 @@
|
||||
"clarity-icons": "^0.10.17",
|
||||
"clarity-ui": "^0.10.17",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.6.13",
|
||||
"harbor-ui": "0.6.19",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
@ -4,7 +4,7 @@
|
||||
<span class="alert-text">
|
||||
{{message}}
|
||||
</span>
|
||||
<div class="alert-actions" *ngIf="needAuth">
|
||||
<div class="alert-actions" *ngIf="needAuth" style="display: inline;">
|
||||
<button class="btn alert-action" (click)="signIn()">{{ 'BUTTON.LOG_IN' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -156,7 +156,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
nameArr.join(','),
|
||||
m,
|
||||
ConfirmationTargets.PROJECT_MEMBER,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
ConfirmationButtons.SWITCH_CANCEL
|
||||
);
|
||||
this.OperateDialogService.openComfirmDialog(switchMessage);
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div style="margin-top: 24px;">
|
||||
<hbr-replication [readonly]="true" #replicationView [projectId]="projectIdentify" [withReplicationJob]='true'></hbr-replication>
|
||||
<hbr-replication [readonly]="true" #replicationView [projectId]="projectIdentify" [isSystemAdmin]="isSystemAdmin" [withReplicationJob]='true' (openCreateRule)="openCreatePage()" (openEdit)="openEditPage($event)"></hbr-replication>
|
||||
</div>
|
@ -12,8 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import { ReplicationComponent } from 'harbor-ui';
|
||||
import {SessionService} from "../shared/session.service";
|
||||
|
||||
@Component({
|
||||
selector: 'replicaton',
|
||||
@ -23,12 +24,27 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
|
||||
projectIdentify: string | number;
|
||||
@ViewChild("replicationView") replicationView: ReplicationComponent;
|
||||
|
||||
constructor(private route: ActivatedRoute) { }
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private session: SessionService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectIdentify = +this.route.snapshot.parent.params['id'];
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
let account = this.session.getCurrentUser();
|
||||
return account != null && account.has_admin_role > 0;
|
||||
}
|
||||
|
||||
openEditPage(id: number): void {
|
||||
this.router.navigate(['harbor', 'replications', id, 'rule', { projectId: this.projectIdentify}]);
|
||||
}
|
||||
|
||||
openCreatePage(): void {
|
||||
this.router.navigate(['harbor', 'replications', 'new-rule', { projectId: this.projectIdentify}] );
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
let isCreated: boolean = this.route.snapshot.queryParams['is_create'];
|
||||
if (isCreated) {
|
||||
|
@ -14,7 +14,6 @@ import {Subject} from "rxjs/Subject";
|
||||
import {ListProjectModelComponent} from "./list-project-model/list-project-model.component";
|
||||
import {toPromise, isEmptyObject, compareValue} from "harbor-ui/src/utils";
|
||||
|
||||
|
||||
const ONE_HOUR_SECONDS: number = 3600;
|
||||
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||
|
||||
@ -27,15 +26,18 @@ const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||
export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
_localTime: Date = new Date();
|
||||
policyId: number;
|
||||
projectId: number;
|
||||
targetList: Target[] = [];
|
||||
isFilterHide: boolean = false;
|
||||
weeklySchedule: boolean;
|
||||
isScheduleOpt: boolean;
|
||||
isImmediate: boolean = true;
|
||||
noProjectInfo: string;
|
||||
noEndpointInfo: string;
|
||||
filterCount: number = 0;
|
||||
selectedprojectList: Project[] = [];
|
||||
triggerNames: string[] = ['immediate', 'schedule', 'manual'];
|
||||
scheduleNames: string[] = ['daily', 'weekly'];
|
||||
triggerNames: string[] = ['Immediate', 'Scheduled', 'Manual'];
|
||||
scheduleNames: string[] = ['Daily', 'Weekly'];
|
||||
weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||
filterSelect: string[] = ['repository', 'tag'];
|
||||
ruleNameTooltip: string = 'TOOLTIP.EMPTY';
|
||||
@ -75,25 +77,33 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
|
||||
|
||||
Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
|
||||
.then(res => {
|
||||
if (!res[0] || !res[1]) {
|
||||
this.msgHandler.error('REPLICATION.BACKINFO');
|
||||
setTimeout(() => {
|
||||
this.router.navigate(['/harbor/replications']);
|
||||
}, 2000);
|
||||
};
|
||||
if (res[0] && res[1]) {
|
||||
if (!res[0]) {
|
||||
this.noEndpointInfo = 'NO_ENDPOINT_INFO';
|
||||
}else {
|
||||
this.targetList = res[0];
|
||||
if (!this.policyId) {
|
||||
this.setTarget([res[0][0]]);
|
||||
this.setProject([res[1][0]]);
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
}
|
||||
}
|
||||
if (!res[1]) {
|
||||
this.noProjectInfo = 'NO_PROJECT_INFO';
|
||||
}else {
|
||||
if (!this.policyId && !this.projectId) {
|
||||
this.setProject([res[1][0]]);
|
||||
}
|
||||
if (!this.policyId && this.projectId) {
|
||||
this.setProject( res[1].filter(rule => rule.project_id === this.projectId));
|
||||
}
|
||||
}
|
||||
if (res[0] && res[1] && !this.policyId) {
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.policyId = +this.route.snapshot.params['id'];
|
||||
this.projectId = +this.route.snapshot.params['projectId'];
|
||||
if (this.policyId) {
|
||||
this.headerTitle = 'REPLICATION.EDIT_POLICY_TITLE';
|
||||
this.repService.getReplicationRule(this.policyId)
|
||||
@ -406,7 +416,12 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
|
||||
this.inProgress = false;
|
||||
setTimeout(() => {
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
if (this.projectId) {
|
||||
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
||||
}else {
|
||||
this.router.navigate(['/harbor/replications']);
|
||||
}
|
||||
|
||||
}, 2000);
|
||||
|
||||
}).catch((error: any) => {
|
||||
@ -420,7 +435,11 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
|
||||
this.inProgress = false;
|
||||
setTimeout(() => {
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
if (this.projectId) {
|
||||
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
||||
}else {
|
||||
this.router.navigate(['/harbor/replications']);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
}).catch((error: any) => {
|
||||
@ -502,6 +521,10 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
|
||||
backReplication(): void {
|
||||
this.router.navigate(['/harbor/replications']);
|
||||
}
|
||||
backProjectReplication(): void {
|
||||
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
||||
}
|
||||
|
||||
|
||||
getChanges(): { [key: string]: any | any[] } {
|
||||
let changes: { [key: string]: any | any[] } = {};
|
||||
|
@ -16,6 +16,9 @@
|
||||
h4{
|
||||
color: #666;
|
||||
}
|
||||
.colorRed{color: red;}
|
||||
.colorRed a{text-decoration: underline;color: #007CBB;}
|
||||
|
||||
label:first-child {
|
||||
font-size: 15px;
|
||||
left: -10px !important;
|
||||
@ -31,4 +34,4 @@ label:first-child {
|
||||
|
||||
.projectInput{float: left;}
|
||||
.projectInput input{width: 185px;background-color: white;}
|
||||
.switchIcon{width:20px;height:20px; margin-top: 5px;margin-left: 15px;}
|
||||
.switchIcon{width:20px;height:20px; margin-top: 2px;margin-left: 15px;}
|
@ -1,10 +1,11 @@
|
||||
<div>
|
||||
<a class="cursor" (click)="backReplication()">< {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
|
||||
<a class="cursor" *ngIf="!projectId" (click)="backReplication()">< {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
|
||||
<a class="cursor" *ngIf="projectId" (click)="backProjectReplication()"><{{'SIDE_NAV.PROJECTS' | translate}} {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
|
||||
<h1 class="sub-header-title">{{headerTitle | translate}}</h1>
|
||||
<form [formGroup]="ruleForm" (ngSubmit)="onSubmit()" novalidate>
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.NAME' | translate}}<span style="color: red">*</span></label>
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.NAME' | translate}}<span class="colorRed">*</span></label>
|
||||
<label class="col-md-8" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
|
||||
[class.invalid]='(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || isRuleNameExist'>
|
||||
<input type="text" id="ruleName" required formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
|
||||
@ -19,13 +20,14 @@
|
||||
<!--Projects-->
|
||||
<h4>{{'REPLICATION.SOURCE' | translate}}</h4>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'PROJECT.PROJECTS' | translate}}<span style="color: red">*</span></label>
|
||||
<label class="col-md-4 form-group-label-override">{{'PROJECT.PROJECTS' | translate}}<span class="colorRed">*</span></label>
|
||||
<div formArrayName="projects">
|
||||
<div class="projectInput" *ngFor="let project of projects.controls; let i= index" [formGroupName]="i">
|
||||
<input formControlName="name" class="label" readonly value="name">
|
||||
</div>
|
||||
</div>
|
||||
<clr-icon shape="switch" class="is-solid switchIcon" (click)="openProjectModel()"></clr-icon>
|
||||
<clr-icon *ngIf="!(noProjectInfo || projectId)" shape="search" class="is-solid switchIcon" (click)="openProjectModel()"></clr-icon>
|
||||
<label *ngIf="noProjectInfo" class="colorRed">{{noProjectInfo | translate}}</label>
|
||||
</div>
|
||||
|
||||
<!--images/Filter-->
|
||||
@ -53,20 +55,21 @@
|
||||
<!--Targets-->
|
||||
<h4>{{'REPLICATION.TARGETS' | translate}}</h4>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'DESTINATION.ENDPOINT' | translate}} <span style="color: red">*</span></label>
|
||||
<label class="col-md-4 form-group-label-override">{{'DESTINATION.ENDPOINT' | translate}} <span class="colorRed">*</span></label>
|
||||
<div formArrayName="targets">
|
||||
<div class="select endpointSelect" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
|
||||
<select id="ruleTarget" (change)="targetChange($event)" formControlName="id">
|
||||
<option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<label *ngIf="noEndpointInfo" class="colorRed">{{noEndpointInfo | translate}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--Trigger-->
|
||||
<h4>{{'REPLICATION.TRIGGER' | translate}}</h4>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SCHEDULE' | translate}}</label>
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.MODE' | translate}}</label>
|
||||
<div formGroupName="trigger">
|
||||
<!--on trigger-->
|
||||
<div class="select floatSet">
|
||||
@ -100,7 +103,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<!--Setting-->
|
||||
<h4>{{'REPLICATION.SETTING' | translate}}</h4>
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SETTING' | translate}}</label>
|
||||
<div class="col-lg-7 padLeft0">
|
||||
|
@ -29,11 +29,16 @@
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="delete()" [hidden]="isDelete">{{isSwitch? 'BUTTON.SWITCH':'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.SWITCH' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
@ -45,13 +45,6 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
return false;
|
||||
}
|
||||
|
||||
get isSwitch(): boolean {
|
||||
if (this.dialogTitle && (this.dialogTitle.includes('SWITCH') || this.dialogTitle.includes('switch'))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
colorChange(list: BatchInfo) {
|
||||
if (!list.loading && !list.errorState) {
|
||||
return 'green';
|
||||
@ -119,7 +112,7 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
this.close();
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
operate(): void {
|
||||
if(!this.message){//Inproper condition
|
||||
this.close();
|
||||
return;
|
||||
|
@ -51,6 +51,7 @@ export const ListMode = {
|
||||
FULL: "full"
|
||||
};
|
||||
|
||||
|
||||
export const CommonRoutes = {
|
||||
SIGN_IN: "/sign-in",
|
||||
EMBEDDED_SIGN_IN: "/harbor/sign-in",
|
||||
@ -68,7 +69,7 @@ export const enum ConfirmationState {
|
||||
NA, CONFIRMED, CANCEL
|
||||
}
|
||||
export const enum ConfirmationButtons {
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, SWITCH_CANCEL
|
||||
}
|
||||
|
||||
export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };
|
||||
|
@ -258,8 +258,6 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
this.selectedRow = [];
|
||||
this.currentTerm = '';
|
||||
|
||||
this.msgHandler.showSuccess('USER.DELETE_SUCCESS');
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
@ -267,7 +265,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
delOperate(id: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return this.userService.deleteUser(id).then(() => {
|
||||
this.translate.get('BATCH.DELETE_SUCCESS').subscribe(res => {
|
||||
this.translate.get('BATCH.DELETED_SUCCESS').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
});
|
||||
}).catch(error => {
|
||||
|
@ -34,13 +34,16 @@
|
||||
"NEGATIVE": "NEGATIVE",
|
||||
"COPY": "COPY",
|
||||
"EDIT": "EDIT",
|
||||
"SWITCH": "SWITCH"
|
||||
"SWITCH": "SWITCH",
|
||||
"REPLICATE": "REPLICATE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Deleted successfully",
|
||||
"DELETED_FAILURE": "Deleted failed",
|
||||
"SWITCH_SUCCESS": "Switch successfully",
|
||||
"SWITCH_FAILURE": "Switch failed"
|
||||
"SWITCH_FAILURE": "Switch failed",
|
||||
"REPLICATE_SUCCESS": "Replicate successfully",
|
||||
"REPLICATE_FAILURE": "Replicate failed"
|
||||
},
|
||||
"TOOLTIP": {
|
||||
"EMAIL": "Email should be a valid email address like name@example.com.",
|
||||
@ -242,8 +245,10 @@
|
||||
"ENDPOINTS": "Endpoints",
|
||||
"FILTER_POLICIES_PLACEHOLDER": "Filter Rules",
|
||||
"FILTER_JOBS_PLACEHOLDER": "Filter Jobs",
|
||||
"DELETION_TITLE": "Confirm Rule Deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete rule {{param}}?",
|
||||
"DELETION_TITLE": "Confirm Rules Deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete rules {{param}}?",
|
||||
"REPLICATION_TITLE": "Confirm Rules replication",
|
||||
"REPLICATION_SUMMARY": "Do you want to replicate the Rules {{param}}?",
|
||||
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",
|
||||
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status",
|
||||
"FILTER_TARGETS_PLACEHOLDER": "Filter Endpoints",
|
||||
@ -276,6 +281,7 @@
|
||||
"LAST_START_TIME": "Last Start Time",
|
||||
"ACTIVATION": "Activation",
|
||||
"REPLICATION_JOBS": "Replication Jobs",
|
||||
"STOPJOB": "Stop Jobs",
|
||||
"ALL": "All",
|
||||
"PENDING": "Pending",
|
||||
"RUNNING": "Running",
|
||||
@ -311,16 +317,20 @@
|
||||
"PLACEHOLDER": "We couldn't find any replication rules!",
|
||||
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
|
||||
"JOB_LOG_VIEWER": "View Replication Job Log",
|
||||
"BACKINFO": "Please add project and endpoint first",
|
||||
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
||||
"NO_PROJECT_INFO": "Please go to projects and add a project first",
|
||||
"FILTER": "Filter",
|
||||
"SCHEDULE": "Schedule",
|
||||
"SETTING":"Setting",
|
||||
"TRIGGER":"Trigger",
|
||||
"SCHEDULE": "Scheduled",
|
||||
"MANUAL": "Manual",
|
||||
"IMMEDIATE": "Immediate",
|
||||
"SETTING":"Options",
|
||||
"TRIGGER":"Triggering Condition",
|
||||
"TARGETS":"Target",
|
||||
"MODE": "Mode",
|
||||
"SOURCE": "Source",
|
||||
"REPLICATE": "Replicate",
|
||||
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
|
||||
"REPLICATE_IMMEDIATE":"Replicate exiting images immediately"
|
||||
"REPLICATE_IMMEDIATE":"Replicate existing images immediately"
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "New Endpoint",
|
||||
@ -500,7 +510,7 @@
|
||||
"ABOUT": {
|
||||
"VERSION": "Version",
|
||||
"BUILD": "Build",
|
||||
"COPYRIGHT": "Copyright 1998-2017 VMware, Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at",
|
||||
"COPYRIGHT": "Copyright 1998-2018 VMware, Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at",
|
||||
"COPYRIGHT_SUFIX": ".",
|
||||
"TRADEMARK": "VMware is a registered trademark or trademark of VMware, Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.",
|
||||
"END_USER_LICENSE": "End User License Agreement",
|
||||
|
@ -34,13 +34,16 @@
|
||||
"NEGATIVE": "NEGATIVO",
|
||||
"COPY": "COPY",
|
||||
"EDIT": "EDITAR",
|
||||
"SWITCH": "SWITCH"
|
||||
"SWITCH": "SWITCH",
|
||||
"REPLICATE": "REPLICATE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Deleted successfully",
|
||||
"DELETED_FAILURE": "Deleted failed",
|
||||
"SWITCH_SUCCESS": "Switch successfully",
|
||||
"SWITCH_FAILURE": "Switch failed"
|
||||
"SWITCH_FAILURE": "Switch failed",
|
||||
"REPLICATE_SUCCESS": "Replicate successfully",
|
||||
"REPLICATE_FAILURE": "Replicate failed"
|
||||
},
|
||||
"TOOLTIP": {
|
||||
"EMAIL": "El email debe ser una dirección válida como nombre@ejemplo.com.",
|
||||
@ -245,6 +248,8 @@
|
||||
"DELETION_SUMMARY": "¿Quiere eliminar la regla {{param}}?",
|
||||
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",
|
||||
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status",
|
||||
"REPLICATION_TITLE": "Confirm Rules replication",
|
||||
"REPLICATION_SUMMARY": "Do you want to replicate the Rules {{param}}?",
|
||||
"FILTER_TARGETS_PLACEHOLDER": "Filtrar Endpoints",
|
||||
"DELETION_TITLE_TARGET": "Confirmar Eliminación de Endpoint",
|
||||
"DELETION_SUMMARY_TARGET": "¿Quiere eliminar el endpoint {{param}}?",
|
||||
@ -275,6 +280,7 @@
|
||||
"LAST_START_TIME": "Última Fecha de Inicio",
|
||||
"ACTIVATION": "Activación",
|
||||
"REPLICATION_JOBS": "Trabajos de Replicación",
|
||||
"STOPJOB": "Stop Jobs",
|
||||
"ALL": "Todos",
|
||||
"PENDING": "Pendiente",
|
||||
"RUNNING": "Ejecutando",
|
||||
@ -310,15 +316,19 @@
|
||||
"PLACEHOLDER": "We couldn't find any replication rules!",
|
||||
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
|
||||
"JOB_LOG_VIEWER": "View Replication Job Log",
|
||||
"BACKINFO": "Please add project and endpoint first",
|
||||
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
||||
"NO_PROJECT_INFO": "Please go to projects and add a project first",
|
||||
"FILTER": "Filter",
|
||||
"SCHEDULE": "Schedule",
|
||||
"SETTING":"Setting",
|
||||
"TRIGGER":"Trigger",
|
||||
"SCHEDULE": "Scheduled",
|
||||
"MANUAL": "Manual",
|
||||
"IMMEDIATE": "Immediate",
|
||||
"SETTING":"Options",
|
||||
"TRIGGER":"Triggering Condition",
|
||||
"TARGETS":"Target",
|
||||
"MODE": "Mode",
|
||||
"SOURCE": "Source",
|
||||
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
|
||||
"REPLICATE_IMMEDIATE":"Replicate exiting images immediately"
|
||||
"REPLICATE_IMMEDIATE":"Replicate existing images immediately"
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "Nuevo Endpoint",
|
||||
@ -499,7 +509,7 @@
|
||||
"ABOUT": {
|
||||
"VERSION": "Versión",
|
||||
"BUILD": "Construir",
|
||||
"COPYRIGHT": "Copyright 1998-2017 VMware, Inc. Todos los derechos reservados. Este producto está protegido por E.U. y las leyes de propiedad internacionales. Los productos de VMware estan cubiertos por una o más patentes listadas en",
|
||||
"COPYRIGHT": "Copyright 1998-2018 VMware, Inc. Todos los derechos reservados. Este producto está protegido por E.U. y las leyes de propiedad internacionales. Los productos de VMware estan cubiertos por una o más patentes listadas en",
|
||||
"COPYRIGHT_SUFIX": ".",
|
||||
"TRADEMARK": "VMware es una marca registrada o marca de VMware, Inc. en los Estados Unidos y otras jurisdicciones. Todas las demás marcas y nombres mencionados son marcas de sus respectivas compañías.",
|
||||
"END_USER_LICENSE": "Contrato de Usuario Final (EULA)",
|
||||
|
@ -34,13 +34,16 @@
|
||||
"NEGATIVE": "否",
|
||||
"COPY": "拷贝",
|
||||
"EDIT": "编辑",
|
||||
"SWITCH": "切换"
|
||||
"SWITCH": "切换",
|
||||
"REPLICATE": "复制"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "删除成功",
|
||||
"DELETED_FAILURE": "删除失败",
|
||||
"SWITCH_SUCCESS": "切换成功",
|
||||
"SWITCH_FAILURE": "切换失败"
|
||||
"SWITCH_FAILURE": "切换失败",
|
||||
"REPLICATE_SUCCESS": "复成功",
|
||||
"REPLICATE_FAILURE": "复制失败"
|
||||
},
|
||||
"TOOLTIP": {
|
||||
"EMAIL": "请使用正确的邮箱地址,比如name@example.com。",
|
||||
@ -244,6 +247,8 @@
|
||||
"DELETION_TITLE": "删除规则确认",
|
||||
"DELETION_SUMMARY": "确认删除规则 {{param}}?",
|
||||
"DELETION_TITLE_FAILURE": "规则确认删除失败",
|
||||
"REPLICATION_TITLE": "复制规则确认",
|
||||
"REPLICATION_SUMMARY": "确认复制规则 {{param}}?",
|
||||
"DELETION_SUMMARY_FAILURE": "{{param}} 有 pending/running/retrying 状态,不能删除",
|
||||
"FILTER_TARGETS_PLACEHOLDER": "过滤目标",
|
||||
"DELETION_TITLE_TARGET": "删除目标确认",
|
||||
@ -275,6 +280,7 @@
|
||||
"LAST_START_TIME": "上次起始时间",
|
||||
"ACTIVATION": "活动状态",
|
||||
"REPLICATION_JOBS": "复制任务",
|
||||
"STOPJOB": "停止任务",
|
||||
"ALL": "全部",
|
||||
"PENDING": "挂起",
|
||||
"RUNNING": "运行中",
|
||||
@ -310,12 +316,16 @@
|
||||
"PLACEHOLDER": "未发现任何复制规则!",
|
||||
"JOB_PLACEHOLDER": "未发现任何复制任务!",
|
||||
"JOB_LOG_VIEWER": "查看复制任务日志",
|
||||
"BACKINFO": "请先添加项目名称和目标",
|
||||
"NO_ENDPOINT_INFO": "请先添加目标",
|
||||
"NO_PROJECT_INFO": "请先添加项目",
|
||||
"FILTER": "过滤",
|
||||
"SCHEDULE": "日程",
|
||||
"SCHEDULE": "定时",
|
||||
"MANUAL": "手动",
|
||||
"IMMEDIATE": "即刻",
|
||||
"SETTING":"设置",
|
||||
"TRIGGER":"触发器",
|
||||
"TRIGGER":"触发条件",
|
||||
"TARGETS":"目标",
|
||||
"MODE": "模式",
|
||||
"SOURCE": "资源",
|
||||
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
|
||||
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。"
|
||||
@ -498,7 +508,7 @@
|
||||
"ABOUT": {
|
||||
"VERSION": "版本",
|
||||
"BUILD": "构建",
|
||||
"COPYRIGHT": "版权所有 © 1998-2017 VMware, Inc. 保留所有权利。此产品受美国及其他国家/地区的版权和知识产权以及国际条约保护。VMware产品受",
|
||||
"COPYRIGHT": "版权所有 © 1998-2018 VMware, Inc. 保留所有权利。此产品受美国及其他国家/地区的版权和知识产权以及国际条约保护。VMware产品受",
|
||||
"COPYRIGHT_SUFIX": "上列出的一项或多项专利保护。",
|
||||
"TRADEMARK": "VMware徽标及设计都是VMware, Inc.在美国和/或其他法律辖区的注册商标或者商标。此处提到的其他所有商标和名称分别是其各自公司的商标。",
|
||||
"END_USER_LICENSE": "终端用户许可协议",
|
||||
|
@ -325,8 +325,7 @@ Test Case - Delete Multi User
|
||||
Filter Object delete
|
||||
Multi-delete Object deletea deleteb deletec
|
||||
#assert delete
|
||||
#Delete Success comment temp wait for fixing
|
||||
Click Element //clr-modal//button[contains(.,'CLOSE')]
|
||||
Delete Success
|
||||
Sleep 1
|
||||
#filter object delete
|
||||
Page Should Not Contain deletea
|
||||
|
Loading…
Reference in New Issue
Block a user