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:
pfh 2018-01-12 13:06:24 +08:00
parent a214084370
commit 78dcdc409f
27 changed files with 356 additions and 163 deletions

View File

@ -30,12 +30,17 @@ export const CONFIRMATION_DIALOG_TEMPLATE: string = `
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="2"> <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-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> <button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="3"> <ng-template [ngSwitchCase]="3">
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button> <button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template> </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> </div>
</clr-modal> </clr-modal>
`; `;

View File

@ -40,7 +40,6 @@ export class ConfirmationDialogComponent {
@Input() batchInfors: BatchInfo[] = []; @Input() batchInfors: BatchInfo[] = [];
isDelete: boolean = false; isDelete: boolean = false;
constructor( constructor(
private translate: TranslateService) {} private translate: TranslateService) {}
@ -98,7 +97,7 @@ export class ConfirmationDialogComponent {
this.close(); this.close();
} }
delete(): void { operate(): void {
if (!this.message){//Inproper condition if (!this.message){//Inproper condition
this.close(); this.close();
return; return;

View File

@ -1,12 +1,12 @@
export const LIST_REPLICATION_RULE_TEMPLATE: string = ` export const LIST_REPLICATION_RULE_TEMPLATE: string = `
<div style="padding-bottom: 15px;"> <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;"> <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" (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.length !== 1" (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.length == 0" (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 == 0" (click)="replicateRule(selectedRow)">{{'REPLICATION.REPLICATE' | translate}}</button>
</div> </div>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column> <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]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.SCHEDULE' | 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-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-row *ngFor="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
<clr-dg-cell>{{p.name}}</clr-dg-cell> <clr-dg-cell (click)="selectRule(p)">{{p.name}}</clr-dg-cell>
<clr-dg-cell *ngIf="!projectScope"> <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> <a href="javascript:void(0)" (click)="redirectTo(p)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell>{{p.description ? p.description : '-'}}</clr-dg-cell> <clr-dg-cell (click)="selectRule(p)">{{p.description ? p.description : '-'}}</clr-dg-cell>
<clr-dg-cell>{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell> <clr-dg-cell (click)="selectRule(p)">{{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.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}} <span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}

View File

@ -55,6 +55,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
nullTime: string = '0001-01-01T00:00:00Z'; nullTime: string = '0001-01-01T00:00:00Z';
@Input() projectId: number; @Input() projectId: number;
@Input() isSystemAdmin: boolean;
@Input() selectedId: number | string; @Input() selectedId: number | string;
@Input() withReplicationJob: boolean; @Input() withReplicationJob: boolean;
@Input() readonly: boolean; @Input() readonly: boolean;
@ -67,16 +68,17 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
@Output() toggleOne = new EventEmitter<ReplicationRule>(); @Output() toggleOne = new EventEmitter<ReplicationRule>();
@Output() redirect = new EventEmitter<ReplicationRule>(); @Output() redirect = new EventEmitter<ReplicationRule>();
@Output() openNewRule = new EventEmitter<any>(); @Output() openNewRule = new EventEmitter<any>();
@Output() replicateManual = new EventEmitter<ReplicationRule>(); @Output() replicateManual = new EventEmitter<ReplicationRule[]>();
@Output() hasJobs = new EventEmitter<boolean>();
projectScope: boolean = false; projectScope: boolean = false;
rules: ReplicationRule[]; rules: ReplicationRule[];
changedRules: ReplicationRule[]; changedRules: ReplicationRule[];
ruleName: string; ruleName: string;
canDeleteRule: boolean; canDeleteRuleList: boolean[] = [];
selectedRow: ReplicationRule; selectedRow: ReplicationRule[] = [];
batchDelectionInfos: BatchInfo[] = []; batchDelectionInfos: BatchInfo[] = [];
@ViewChild('toggleConfirmDialog') @ViewChild('toggleConfirmDialog')
@ -100,7 +102,6 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
return !this.readonly && !this.projectId ? true : false; return !this.readonly && !this.projectId ? true : false;
} }
ngOnInit(): void { ngOnInit(): void {
//Global scope //Global scope
if (!this.projectScope) { 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 { retrieveRules(ruleName: string = ''): void {
this.loading = true; this.loading = true;
this.selectedRow = [];
toPromise<ReplicationRule[]>(this.replicationService toPromise<ReplicationRule[]>(this.replicationService
.getReplicationRules(this.projectId, ruleName)) .getReplicationRules(this.projectId, ruleName))
.then(rules => { .then(rules => {
@ -176,38 +173,30 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
} }
} }
replicateRule(rules: ReplicationRule[]): void {
this.replicateManual.emit(rules);
}
deletionConfirm(message: ConfirmationAcknowledgement) { deletionConfirm(message: ConfirmationAcknowledgement) {
if (message && if (message &&
message.source === ConfirmationTargets.POLICY && message.source === ConfirmationTargets.POLICY &&
message.state === ConfirmationState.CONFIRMED) { message.state === ConfirmationState.CONFIRMED) {
let rule: ReplicationRule = message.data; this.deleteOpe(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);
});
}
});
} }
} }
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 { selectRule(rule: ReplicationRule): void {
this.selectedId = rule.id || ''; this.selectedRow = [];
this.selectOne.emit(rule); this.selectOne.emit(rule);
this.hasJobs.emit(true);
} }
redirectTo(rule: ReplicationRule): void { redirectTo(rule: ReplicationRule): void {
@ -218,12 +207,8 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
this.openNewRule.emit(); this.openNewRule.emit();
} }
editRule(rules: ReplicationRule) { editRule(rules: ReplicationRule[]) {
this.editOne.emit(rules); this.editOne.emit(rules[0]);
}
replicateRule(rule: ReplicationRule) {
this.replicateManual.emit(rule);
} }
toggleRule(rule: ReplicationRule) { toggleRule(rule: ReplicationRule) {
@ -237,12 +222,12 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
this.toggleConfirmDialog.open(toggleConfirmMessage); this.toggleConfirmDialog.open(toggleConfirmMessage);
} }
jobList(): Promise<void> { jobList(id: string | number): Promise<void> {
let ruleData: ReplicationJobItem[]; let ruleData: ReplicationJobItem[];
this.canDeleteRule = true; this.canDeleteRuleList = [];
let count: number = 0; let count: number = 0;
return toPromise<ReplicationJob>(this.replicationService return toPromise<ReplicationJob>(this.replicationService
.getJobs(this.selectedId)) .getJobs(id))
.then(response => { .then(response => {
ruleData = response.data; ruleData = response.data;
if (ruleData.length) { 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)); .catch(error => this.errorHandler.error(error));
} }
deleteRule(rule: ReplicationRule) { deleteRule(rules: ReplicationRule[]) {
this.jobList().then(() => { if (rules && rules.length) {
let deletionMessage: ConfirmationMessage; let nameArr: string[] = [];
if (!this.canDeleteRule) {
deletionMessage = new ConfirmationMessage(
'REPLICATION.DELETION_TITLE_FAILURE',
'REPLICATION.DELETION_SUMMARY_FAILURE',
rule.name || '',
rule,
ConfirmationTargets.POLICY,
ConfirmationButtons.CLOSE);
} else {
this.batchDelectionInfos = []; this.batchDelectionInfos = [];
let initBatchMessage = new BatchInfo (); rules.forEach(data => {
initBatchMessage.name = rule.name; nameArr.push(data.name);
let initBatchMessage = new BatchInfo();
initBatchMessage.name = data.name;
this.batchDelectionInfos.push(initBatchMessage); this.batchDelectionInfos.push(initBatchMessage);
deletionMessage = new ConfirmationMessage( });
let deletionMessage = new ConfirmationMessage(
'REPLICATION.DELETION_TITLE', 'REPLICATION.DELETION_TITLE',
'REPLICATION.DELETION_SUMMARY', 'REPLICATION.DELETION_SUMMARY',
rule.name || '', nameArr.join(',') || '',
rule, rules,
ConfirmationTargets.POLICY, ConfirmationTargets.POLICY,
ConfirmationButtons.DELETE_CANCEL); ConfirmationButtons.DELETE_CANCEL);
}
this.deletionConfirmDialog.open(deletionMessage); 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);
});
}
});
}
} }

View File

@ -11,8 +11,9 @@ export const REPLICATION_TEMPLATE: string = `
</div> </div>
</div> </div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <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>
<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 *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;"> <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> <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> </div>
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <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]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | 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> <clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
@ -66,5 +71,7 @@ export const REPLICATION_TEMPLATE: string = `
</clr-dg-footer> </clr-dg-footer>
</clr-datagrid> </clr-datagrid>
</div> </div>
</div>
<job-log-viewer #replicationLogViewer></job-log-viewer> <job-log-viewer #replicationLogViewer></job-log-viewer>
<confirmation-dialog #replicationConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmReplication($event)"></confirmation-dialog>
</div>`; </div>`;

View File

@ -43,6 +43,11 @@ import { JobLogViewerComponent } from '../job-log-viewer/index';
import { State } from "clarity-angular"; import { State } from "clarity-angular";
import {Observable} from "rxjs/Observable"; import {Observable} from "rxjs/Observable";
import {Subscription} from "rxjs/Subscription"; 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 } = [ const ruleStatus: { [key: string]: any } = [
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' }, { 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' },
@ -84,6 +89,7 @@ export class SearchOption {
export class ReplicationComponent implements OnInit, OnDestroy { export class ReplicationComponent implements OnInit, OnDestroy {
@Input() projectId: number | string; @Input() projectId: number | string;
@Input() isSystemAdmin: boolean;
@Input() withReplicationJob: boolean; @Input() withReplicationJob: boolean;
@Input() readonly: boolean; @Input() readonly: boolean;
@ -101,11 +107,13 @@ export class ReplicationComponent implements OnInit, OnDestroy {
changedRules: ReplicationRule[]; changedRules: ReplicationRule[];
initSelectedId: number | string; initSelectedId: number | string;
hasJobs: boolean;
rules: ReplicationRule[]; rules: ReplicationRule[];
loading: boolean; loading: boolean;
jobs: ReplicationJobItem[]; jobs: ReplicationJobItem[];
batchDelectionInfos: BatchInfo[] = [];
toggleJobSearchOption = optionalSearch; toggleJobSearchOption = optionalSearch;
currentJobSearchOption: number; currentJobSearchOption: number;
@ -119,6 +127,9 @@ export class ReplicationComponent implements OnInit, OnDestroy {
@ViewChild("replicationLogViewer") @ViewChild("replicationLogViewer")
replicationLogViewer: JobLogViewerComponent; replicationLogViewer: JobLogViewerComponent;
@ViewChild('replicationConfirmDialog')
replicationConfirmDialog: ConfirmationDialogComponent;
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date'); creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_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 { replicateManualRule(rules: ReplicationRule[]) {
toPromise<any>(this.replicationService.replicateRule(rule.id)) if (rules && rules.length) {
.then(response => { let nameArr: string[] = [];
this.refreshJobs(); this.batchDelectionInfos = [];
}) rules.forEach(rule => {
.catch(error => this.errorHandler.error(error)); 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) { customRedirect(rule: ReplicationRule) {
this.redirect.emit(rule); this.redirect.emit(rule);
@ -295,6 +354,14 @@ export class ReplicationComponent implements OnInit, OnDestroy {
this.loadFirstPage(); 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) { reloadRules(isReady: boolean) {
if (isReady) { if (isReady) {
this.search.ruleName = ''; this.search.ruleName = '';
@ -306,6 +373,13 @@ export class ReplicationComponent implements OnInit, OnDestroy {
this.listReplicationRule.retrieveRules(); this.listReplicationRule.retrieveRules();
} }
hasJobList(hasJob: boolean): void {
this.hasJobs = hasJob;
if (this.hasJobs) {
this.refreshJobs();
}
}
refreshJobs() { refreshJobs() {
this.search.repoName = ""; this.search.repoName = "";
this.search.startTimestamp = ""; this.search.startTimestamp = "";

View File

@ -156,8 +156,9 @@ export class RepositoryListviewComponent implements OnChanges, OnInit {
delOperate(repoName: string) { delOperate(repoName: string) {
let findedList = this.batchDelectionInfos.find(data => data.name === repoName); let findedList = this.batchDelectionInfos.find(data => data.name === repoName);
if (this.signedCon[repoName].length !== 0) { if (this.signedCon[repoName].length !== 0) {
this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED').subscribe(res => { Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
findedList.status = res; this.translateService.get('REPOSITORY.DELETION_TITLE_REPO_SIGNED')).subscribe(res => {
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
}); });
} else { } else {
return toPromise<number>(this.repositoryService return toPromise<number>(this.repositoryService

View File

@ -127,6 +127,8 @@ export abstract class ReplicationService {
* @memberof ReplicationService * @memberof ReplicationService
*/ */
abstract getJobLog(jobId: number | string): Observable<string> | Promise<string> | string; 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()) .then(response => response.text())
.catch(error => Promise.reject(error)); .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));
}
} }

View File

@ -65,5 +65,5 @@ export const enum ConfirmationState {
}; };
export const enum ConfirmationButtons { export const enum ConfirmationButtons {
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, REPLICATE_CANCEL
}; };

View File

@ -25,7 +25,7 @@ export const TAG_TEMPLATE = `
<clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbedded" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()"> <clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbedded" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
<clr-dg-action-bar> <clr-dg-action-bar>
<div class="btn-group"> <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" [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> <button type="button" class="btn btn-sm btn-secondary" *ngIf="hasProjectAdminRole" (click)="deleteTags(selectedRow)" [disabled]="!selectedRow.length">{{'REPOSITORY.DELETE' | translate}}</button>
</div> </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="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: 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-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-row *ngFor="let t of tags" [clrDgItem]='t'>
<clr-dg-cell class="truncated" style="width: 160px;" [ngSwitch]="withClair"> <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> <a *ngSwitchCase="true" href="javascript:void(0)" (click)="onTagClick(t)" title="{{t.name}}">{{t.name}}</a>

View File

@ -286,8 +286,7 @@ export class TagComponent implements OnInit {
if (tags && tags.length) { if (tags && tags.length) {
let promiseLists: any[] = []; let promiseLists: any[] = [];
tags.forEach(tag => { tags.forEach(tag => {
this.delOperate(tag.signature, tag.name); promiseLists.push(this.delOperate(tag.signature, tag.name));
}); });
Promise.all(promiseLists).then((item) => { Promise.all(promiseLists).then((item) => {

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.10.17", "clarity-icons": "^0.10.17",
"clarity-ui": "^0.10.17", "clarity-ui": "^0.10.17",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"harbor-ui": "0.6.13", "harbor-ui": "0.6.19",
"intl": "^1.2.5", "intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2", "mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0", "ngx-cookie": "^1.0.0",

View File

@ -4,7 +4,7 @@
<span class="alert-text"> <span class="alert-text">
{{message}} {{message}}
</span> </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> <button class="btn alert-action" (click)="signIn()">{{ 'BUTTON.LOG_IN' | translate }}</button>
</div> </div>
</div> </div>

View File

@ -156,7 +156,7 @@ export class MemberComponent implements OnInit, OnDestroy {
nameArr.join(','), nameArr.join(','),
m, m,
ConfirmationTargets.PROJECT_MEMBER, ConfirmationTargets.PROJECT_MEMBER,
ConfirmationButtons.DELETE_CANCEL ConfirmationButtons.SWITCH_CANCEL
); );
this.OperateDialogService.openComfirmDialog(switchMessage); this.OperateDialogService.openComfirmDialog(switchMessage);
} }

View File

@ -1,3 +1,3 @@
<div style="margin-top: 24px;"> <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> </div>

View File

@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; 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 { ReplicationComponent } from 'harbor-ui';
import {SessionService} from "../shared/session.service";
@Component({ @Component({
selector: 'replicaton', selector: 'replicaton',
@ -23,12 +24,27 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
projectIdentify: string | number; projectIdentify: string | number;
@ViewChild("replicationView") replicationView: ReplicationComponent; @ViewChild("replicationView") replicationView: ReplicationComponent;
constructor(private route: ActivatedRoute) { } constructor(private route: ActivatedRoute,
private router: Router,
private session: SessionService) { }
ngOnInit(): void { ngOnInit(): void {
this.projectIdentify = +this.route.snapshot.parent.params['id']; 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 { ngAfterViewInit(): void {
let isCreated: boolean = this.route.snapshot.queryParams['is_create']; let isCreated: boolean = this.route.snapshot.queryParams['is_create'];
if (isCreated) { if (isCreated) {

View File

@ -14,7 +14,6 @@ import {Subject} from "rxjs/Subject";
import {ListProjectModelComponent} from "./list-project-model/list-project-model.component"; import {ListProjectModelComponent} from "./list-project-model/list-project-model.component";
import {toPromise, isEmptyObject, compareValue} from "harbor-ui/src/utils"; import {toPromise, isEmptyObject, compareValue} from "harbor-ui/src/utils";
const ONE_HOUR_SECONDS: number = 3600; const ONE_HOUR_SECONDS: number = 3600;
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS; 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 { export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestroy {
_localTime: Date = new Date(); _localTime: Date = new Date();
policyId: number; policyId: number;
projectId: number;
targetList: Target[] = []; targetList: Target[] = [];
isFilterHide: boolean = false; isFilterHide: boolean = false;
weeklySchedule: boolean; weeklySchedule: boolean;
isScheduleOpt: boolean; isScheduleOpt: boolean;
isImmediate: boolean = true; isImmediate: boolean = true;
noProjectInfo: string;
noEndpointInfo: string;
filterCount: number = 0; filterCount: number = 0;
selectedprojectList: Project[] = []; selectedprojectList: Project[] = [];
triggerNames: string[] = ['immediate', 'schedule', 'manual']; triggerNames: string[] = ['Immediate', 'Scheduled', 'Manual'];
scheduleNames: string[] = ['daily', 'weekly']; scheduleNames: string[] = ['Daily', 'Weekly'];
weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
filterSelect: string[] = ['repository', 'tag']; filterSelect: string[] = ['repository', 'tag'];
ruleNameTooltip: string = 'TOOLTIP.EMPTY'; ruleNameTooltip: string = 'TOOLTIP.EMPTY';
@ -75,25 +77,33 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
Promise.all([this.repService.getEndpoints(), this.repService.listProjects()]) Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
.then(res => { .then(res => {
if (!res[0] || !res[1]) { if (!res[0]) {
this.msgHandler.error('REPLICATION.BACKINFO'); this.noEndpointInfo = 'NO_ENDPOINT_INFO';
setTimeout(() => { }else {
this.router.navigate(['/harbor/replications']);
}, 2000);
};
if (res[0] && res[1]) {
this.targetList = res[0]; this.targetList = res[0];
if (!this.policyId) { if (!this.policyId) {
this.setTarget([res[0][0]]); 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 { ngOnInit(): void {
this.policyId = +this.route.snapshot.params['id']; this.policyId = +this.route.snapshot.params['id'];
this.projectId = +this.route.snapshot.params['projectId'];
if (this.policyId) { if (this.policyId) {
this.headerTitle = 'REPLICATION.EDIT_POLICY_TITLE'; this.headerTitle = 'REPLICATION.EDIT_POLICY_TITLE';
this.repService.getReplicationRule(this.policyId) this.repService.getReplicationRule(this.policyId)
@ -406,7 +416,12 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
this.inProgress = false; this.inProgress = false;
setTimeout(() => { setTimeout(() => {
this.copyUpdateForm = Object.assign({}, this.ruleForm.value); this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
if (this.projectId) {
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
}else {
this.router.navigate(['/harbor/replications']); this.router.navigate(['/harbor/replications']);
}
}, 2000); }, 2000);
}).catch((error: any) => { }).catch((error: any) => {
@ -420,7 +435,11 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
this.inProgress = false; this.inProgress = false;
setTimeout(() => { setTimeout(() => {
this.copyUpdateForm = Object.assign({}, this.ruleForm.value); this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
if (this.projectId) {
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
}else {
this.router.navigate(['/harbor/replications']); this.router.navigate(['/harbor/replications']);
}
}, 2000); }, 2000);
}).catch((error: any) => { }).catch((error: any) => {
@ -502,6 +521,10 @@ export class ReplicationRuleComponent implements OnInit, AfterViewInit, OnDestro
backReplication(): void { backReplication(): void {
this.router.navigate(['/harbor/replications']); this.router.navigate(['/harbor/replications']);
} }
backProjectReplication(): void {
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
}
getChanges(): { [key: string]: any | any[] } { getChanges(): { [key: string]: any | any[] } {
let changes: { [key: string]: any | any[] } = {}; let changes: { [key: string]: any | any[] } = {};

View File

@ -16,6 +16,9 @@
h4{ h4{
color: #666; color: #666;
} }
.colorRed{color: red;}
.colorRed a{text-decoration: underline;color: #007CBB;}
label:first-child { label:first-child {
font-size: 15px; font-size: 15px;
left: -10px !important; left: -10px !important;
@ -31,4 +34,4 @@ label:first-child {
.projectInput{float: left;} .projectInput{float: left;}
.projectInput input{width: 185px;background-color: white;} .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;}

View File

@ -1,10 +1,11 @@
<div> <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}} &nbsp; {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
<h1 class="sub-header-title">{{headerTitle | translate}}</h1> <h1 class="sub-header-title">{{headerTitle | translate}}</h1>
<form [formGroup]="ruleForm" (ngSubmit)="onSubmit()" novalidate> <form [formGroup]="ruleForm" (ngSubmit)="onSubmit()" novalidate>
<section class="form-block"> <section class="form-block">
<div class="form-group"> <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" <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'> [class.invalid]='(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || isRuleNameExist'>
<input type="text" id="ruleName" required formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off"> <input type="text" id="ruleName" required formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
@ -19,13 +20,14 @@
<!--Projects--> <!--Projects-->
<h4>{{'REPLICATION.SOURCE' | translate}}</h4> <h4>{{'REPLICATION.SOURCE' | translate}}</h4>
<div class="form-group"> <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 formArrayName="projects">
<div class="projectInput" *ngFor="let project of projects.controls; let i= index" [formGroupName]="i"> <div class="projectInput" *ngFor="let project of projects.controls; let i= index" [formGroupName]="i">
<input formControlName="name" class="label" readonly value="name"> <input formControlName="name" class="label" readonly value="name">
</div> </div>
</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> </div>
<!--images/Filter--> <!--images/Filter-->
@ -53,20 +55,21 @@
<!--Targets--> <!--Targets-->
<h4>{{'REPLICATION.TARGETS' | translate}}</h4> <h4>{{'REPLICATION.TARGETS' | translate}}</h4>
<div class="form-group"> <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 formArrayName="targets">
<div class="select endpointSelect" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i"> <div class="select endpointSelect" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
<select id="ruleTarget" (change)="targetChange($event)" formControlName="id"> <select id="ruleTarget" (change)="targetChange($event)" formControlName="id">
<option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option> <option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option>
</select> </select>
</div> </div>
<label *ngIf="noEndpointInfo" class="colorRed">{{noEndpointInfo | translate}}</label>
</div> </div>
</div> </div>
<!--Trigger--> <!--Trigger-->
<h4>{{'REPLICATION.TRIGGER' | translate}}</h4> <h4>{{'REPLICATION.TRIGGER' | translate}}</h4>
<div class="form-group"> <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"> <div formGroupName="trigger">
<!--on trigger--> <!--on trigger-->
<div class="select floatSet"> <div class="select floatSet">
@ -100,7 +103,6 @@
</div> </div>
</div> </div>
<!--Setting--> <!--Setting-->
<h4>{{'REPLICATION.SETTING' | translate}}</h4>
<div class="form-group"> <div class="form-group">
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SETTING' | translate}}</label> <label class="col-md-4 form-group-label-override">{{'REPLICATION.SETTING' | translate}}</label>
<div class="col-lg-7 padLeft0"> <div class="col-lg-7 padLeft0">

View File

@ -29,11 +29,16 @@
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="2"> <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-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> <button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="3"> <ng-template [ngSwitchCase]="3">
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button> <button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
</ng-template> </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> </div>
</clr-modal> </clr-modal>

View File

@ -45,13 +45,6 @@ export class ConfirmationDialogComponent implements OnDestroy {
return false; return false;
} }
get isSwitch(): boolean {
if (this.dialogTitle && (this.dialogTitle.includes('SWITCH') || this.dialogTitle.includes('switch'))) {
return true;
}
return false;
}
colorChange(list: BatchInfo) { colorChange(list: BatchInfo) {
if (!list.loading && !list.errorState) { if (!list.loading && !list.errorState) {
return 'green'; return 'green';
@ -119,7 +112,7 @@ export class ConfirmationDialogComponent implements OnDestroy {
this.close(); this.close();
} }
delete(): void { operate(): void {
if(!this.message){//Inproper condition if(!this.message){//Inproper condition
this.close(); this.close();
return; return;

View File

@ -51,6 +51,7 @@ export const ListMode = {
FULL: "full" FULL: "full"
}; };
export const CommonRoutes = { export const CommonRoutes = {
SIGN_IN: "/sign-in", SIGN_IN: "/sign-in",
EMBEDDED_SIGN_IN: "/harbor/sign-in", EMBEDDED_SIGN_IN: "/harbor/sign-in",
@ -68,7 +69,7 @@ export const enum ConfirmationState {
NA, CONFIRMED, CANCEL NA, CONFIRMED, CANCEL
} }
export const enum ConfirmationButtons { 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' }; export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };

View File

@ -258,8 +258,6 @@ export class UserComponent implements OnInit, OnDestroy {
Promise.all(promiseLists).then((item) => { Promise.all(promiseLists).then((item) => {
this.selectedRow = []; this.selectedRow = [];
this.currentTerm = ''; this.currentTerm = '';
this.msgHandler.showSuccess('USER.DELETE_SUCCESS');
this.refresh(); this.refresh();
}); });
} }
@ -267,7 +265,7 @@ export class UserComponent implements OnInit, OnDestroy {
delOperate(id: number, name: string) { delOperate(id: number, name: string) {
let findedList = this.batchDelectionInfos.find(data => data.name === name); let findedList = this.batchDelectionInfos.find(data => data.name === name);
return this.userService.deleteUser(id).then(() => { 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); findedList = BathInfoChanges(findedList, res);
}); });
}).catch(error => { }).catch(error => {

View File

@ -34,13 +34,16 @@
"NEGATIVE": "NEGATIVE", "NEGATIVE": "NEGATIVE",
"COPY": "COPY", "COPY": "COPY",
"EDIT": "EDIT", "EDIT": "EDIT",
"SWITCH": "SWITCH" "SWITCH": "SWITCH",
"REPLICATE": "REPLICATE"
}, },
"BATCH": { "BATCH": {
"DELETED_SUCCESS": "Deleted successfully", "DELETED_SUCCESS": "Deleted successfully",
"DELETED_FAILURE": "Deleted failed", "DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully", "SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed" "SWITCH_FAILURE": "Switch failed",
"REPLICATE_SUCCESS": "Replicate successfully",
"REPLICATE_FAILURE": "Replicate failed"
}, },
"TOOLTIP": { "TOOLTIP": {
"EMAIL": "Email should be a valid email address like name@example.com.", "EMAIL": "Email should be a valid email address like name@example.com.",
@ -242,8 +245,10 @@
"ENDPOINTS": "Endpoints", "ENDPOINTS": "Endpoints",
"FILTER_POLICIES_PLACEHOLDER": "Filter Rules", "FILTER_POLICIES_PLACEHOLDER": "Filter Rules",
"FILTER_JOBS_PLACEHOLDER": "Filter Jobs", "FILTER_JOBS_PLACEHOLDER": "Filter Jobs",
"DELETION_TITLE": "Confirm Rule Deletion", "DELETION_TITLE": "Confirm Rules Deletion",
"DELETION_SUMMARY": "Do you want to delete rule {{param}}?", "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_TITLE_FAILURE": "failed to delete Rule Deletion",
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status", "DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status",
"FILTER_TARGETS_PLACEHOLDER": "Filter Endpoints", "FILTER_TARGETS_PLACEHOLDER": "Filter Endpoints",
@ -276,6 +281,7 @@
"LAST_START_TIME": "Last Start Time", "LAST_START_TIME": "Last Start Time",
"ACTIVATION": "Activation", "ACTIVATION": "Activation",
"REPLICATION_JOBS": "Replication Jobs", "REPLICATION_JOBS": "Replication Jobs",
"STOPJOB": "Stop Jobs",
"ALL": "All", "ALL": "All",
"PENDING": "Pending", "PENDING": "Pending",
"RUNNING": "Running", "RUNNING": "Running",
@ -311,16 +317,20 @@
"PLACEHOLDER": "We couldn't find any replication rules!", "PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!", "JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log", "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", "FILTER": "Filter",
"SCHEDULE": "Schedule", "SCHEDULE": "Scheduled",
"SETTING":"Setting", "MANUAL": "Manual",
"TRIGGER":"Trigger", "IMMEDIATE": "Immediate",
"SETTING":"Options",
"TRIGGER":"Triggering Condition",
"TARGETS":"Target", "TARGETS":"Target",
"MODE": "Mode",
"SOURCE": "Source", "SOURCE": "Source",
"REPLICATE": "Replicate", "REPLICATE": "Replicate",
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted", "DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
"REPLICATE_IMMEDIATE":"Replicate exiting images immediately" "REPLICATE_IMMEDIATE":"Replicate existing images immediately"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "New Endpoint", "NEW_ENDPOINT": "New Endpoint",
@ -500,7 +510,7 @@
"ABOUT": { "ABOUT": {
"VERSION": "Version", "VERSION": "Version",
"BUILD": "Build", "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": ".", "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.", "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", "END_USER_LICENSE": "End User License Agreement",

View File

@ -34,13 +34,16 @@
"NEGATIVE": "NEGATIVO", "NEGATIVE": "NEGATIVO",
"COPY": "COPY", "COPY": "COPY",
"EDIT": "EDITAR", "EDIT": "EDITAR",
"SWITCH": "SWITCH" "SWITCH": "SWITCH",
"REPLICATE": "REPLICATE"
}, },
"BATCH": { "BATCH": {
"DELETED_SUCCESS": "Deleted successfully", "DELETED_SUCCESS": "Deleted successfully",
"DELETED_FAILURE": "Deleted failed", "DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully", "SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed" "SWITCH_FAILURE": "Switch failed",
"REPLICATE_SUCCESS": "Replicate successfully",
"REPLICATE_FAILURE": "Replicate failed"
}, },
"TOOLTIP": { "TOOLTIP": {
"EMAIL": "El email debe ser una dirección válida como nombre@ejemplo.com.", "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_SUMMARY": "¿Quiere eliminar la regla {{param}}?",
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion", "DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",
"DELETION_SUMMARY_FAILURE": "{{param}} have pending/running/retrying status", "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", "FILTER_TARGETS_PLACEHOLDER": "Filtrar Endpoints",
"DELETION_TITLE_TARGET": "Confirmar Eliminación de Endpoint", "DELETION_TITLE_TARGET": "Confirmar Eliminación de Endpoint",
"DELETION_SUMMARY_TARGET": "¿Quiere eliminar el endpoint {{param}}?", "DELETION_SUMMARY_TARGET": "¿Quiere eliminar el endpoint {{param}}?",
@ -275,6 +280,7 @@
"LAST_START_TIME": "Última Fecha de Inicio", "LAST_START_TIME": "Última Fecha de Inicio",
"ACTIVATION": "Activación", "ACTIVATION": "Activación",
"REPLICATION_JOBS": "Trabajos de Replicación", "REPLICATION_JOBS": "Trabajos de Replicación",
"STOPJOB": "Stop Jobs",
"ALL": "Todos", "ALL": "Todos",
"PENDING": "Pendiente", "PENDING": "Pendiente",
"RUNNING": "Ejecutando", "RUNNING": "Ejecutando",
@ -310,15 +316,19 @@
"PLACEHOLDER": "We couldn't find any replication rules!", "PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!", "JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log", "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", "FILTER": "Filter",
"SCHEDULE": "Schedule", "SCHEDULE": "Scheduled",
"SETTING":"Setting", "MANUAL": "Manual",
"TRIGGER":"Trigger", "IMMEDIATE": "Immediate",
"SETTING":"Options",
"TRIGGER":"Triggering Condition",
"TARGETS":"Target", "TARGETS":"Target",
"MODE": "Mode",
"SOURCE": "Source", "SOURCE": "Source",
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted", "DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
"REPLICATE_IMMEDIATE":"Replicate exiting images immediately" "REPLICATE_IMMEDIATE":"Replicate existing images immediately"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint", "NEW_ENDPOINT": "Nuevo Endpoint",
@ -499,7 +509,7 @@
"ABOUT": { "ABOUT": {
"VERSION": "Versión", "VERSION": "Versión",
"BUILD": "Construir", "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": ".", "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.", "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)", "END_USER_LICENSE": "Contrato de Usuario Final (EULA)",

View File

@ -34,13 +34,16 @@
"NEGATIVE": "否", "NEGATIVE": "否",
"COPY": "拷贝", "COPY": "拷贝",
"EDIT": "编辑", "EDIT": "编辑",
"SWITCH": "切换" "SWITCH": "切换",
"REPLICATE": "复制"
}, },
"BATCH": { "BATCH": {
"DELETED_SUCCESS": "删除成功", "DELETED_SUCCESS": "删除成功",
"DELETED_FAILURE": "删除失败", "DELETED_FAILURE": "删除失败",
"SWITCH_SUCCESS": "切换成功", "SWITCH_SUCCESS": "切换成功",
"SWITCH_FAILURE": "切换失败" "SWITCH_FAILURE": "切换失败",
"REPLICATE_SUCCESS": "复成功",
"REPLICATE_FAILURE": "复制失败"
}, },
"TOOLTIP": { "TOOLTIP": {
"EMAIL": "请使用正确的邮箱地址比如name@example.com。", "EMAIL": "请使用正确的邮箱地址比如name@example.com。",
@ -244,6 +247,8 @@
"DELETION_TITLE": "删除规则确认", "DELETION_TITLE": "删除规则确认",
"DELETION_SUMMARY": "确认删除规则 {{param}}?", "DELETION_SUMMARY": "确认删除规则 {{param}}?",
"DELETION_TITLE_FAILURE": "规则确认删除失败", "DELETION_TITLE_FAILURE": "规则确认删除失败",
"REPLICATION_TITLE": "复制规则确认",
"REPLICATION_SUMMARY": "确认复制规则 {{param}}?",
"DELETION_SUMMARY_FAILURE": "{{param}} 有 pending/running/retrying 状态,不能删除", "DELETION_SUMMARY_FAILURE": "{{param}} 有 pending/running/retrying 状态,不能删除",
"FILTER_TARGETS_PLACEHOLDER": "过滤目标", "FILTER_TARGETS_PLACEHOLDER": "过滤目标",
"DELETION_TITLE_TARGET": "删除目标确认", "DELETION_TITLE_TARGET": "删除目标确认",
@ -275,6 +280,7 @@
"LAST_START_TIME": "上次起始时间", "LAST_START_TIME": "上次起始时间",
"ACTIVATION": "活动状态", "ACTIVATION": "活动状态",
"REPLICATION_JOBS": "复制任务", "REPLICATION_JOBS": "复制任务",
"STOPJOB": "停止任务",
"ALL": "全部", "ALL": "全部",
"PENDING": "挂起", "PENDING": "挂起",
"RUNNING": "运行中", "RUNNING": "运行中",
@ -310,12 +316,16 @@
"PLACEHOLDER": "未发现任何复制规则!", "PLACEHOLDER": "未发现任何复制规则!",
"JOB_PLACEHOLDER": "未发现任何复制任务!", "JOB_PLACEHOLDER": "未发现任何复制任务!",
"JOB_LOG_VIEWER": "查看复制任务日志", "JOB_LOG_VIEWER": "查看复制任务日志",
"BACKINFO": "请先添加项目名称和目标", "NO_ENDPOINT_INFO": "请先添加目标",
"NO_PROJECT_INFO": "请先添加项目",
"FILTER": "过滤", "FILTER": "过滤",
"SCHEDULE": "日程", "SCHEDULE": "定时",
"MANUAL": "手动",
"IMMEDIATE": "即刻",
"SETTING":"设置", "SETTING":"设置",
"TRIGGER":"触发器", "TRIGGER":"触发条件",
"TARGETS":"目标", "TARGETS":"目标",
"MODE": "模式",
"SOURCE": "资源", "SOURCE": "资源",
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。", "DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。" "REPLICATE_IMMEDIATE":"立即复制现有的镜像。"
@ -498,7 +508,7 @@
"ABOUT": { "ABOUT": {
"VERSION": "版本", "VERSION": "版本",
"BUILD": "构建", "BUILD": "构建",
"COPYRIGHT": "版权所有 © 1998-2017 VMware, Inc. 保留所有权利。此产品受美国及其他国家/地区的版权和知识产权以及国际条约保护。VMware产品受", "COPYRIGHT": "版权所有 © 1998-2018 VMware, Inc. 保留所有权利。此产品受美国及其他国家/地区的版权和知识产权以及国际条约保护。VMware产品受",
"COPYRIGHT_SUFIX": "上列出的一项或多项专利保护。", "COPYRIGHT_SUFIX": "上列出的一项或多项专利保护。",
"TRADEMARK": "VMware徽标及设计都是VMware, Inc.在美国和/或其他法律辖区的注册商标或者商标。此处提到的其他所有商标和名称分别是其各自公司的商标。", "TRADEMARK": "VMware徽标及设计都是VMware, Inc.在美国和/或其他法律辖区的注册商标或者商标。此处提到的其他所有商标和名称分别是其各自公司的商标。",
"END_USER_LICENSE": "终端用户许可协议", "END_USER_LICENSE": "终端用户许可协议",

View File

@ -325,8 +325,7 @@ Test Case - Delete Multi User
Filter Object delete Filter Object delete
Multi-delete Object deletea deleteb deletec Multi-delete Object deletea deleteb deletec
#assert delete #assert delete
#Delete Success comment temp wait for fixing Delete Success
Click Element //clr-modal//button[contains(.,'CLOSE')]
Sleep 1 Sleep 1
#filter object delete #filter object delete
Page Should Not Contain deletea Page Should Not Contain deletea