diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts index 086d008e0..252030497 100644 --- a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.html.ts @@ -30,12 +30,17 @@ export const CONFIRMATION_DIALOG_TEMPLATE: string = ` - + + + + + + `; \ No newline at end of file diff --git a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts index 9216d6356..0d471f59f 100644 --- a/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts +++ b/src/ui_ng/lib/src/confirmation-dialog/confirmation-dialog.component.ts @@ -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; diff --git a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts index 86a112481..d0ddaa645 100644 --- a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts +++ b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts @@ -1,12 +1,12 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
- + -
+
- - - + + +
{{'REPLICATION.NAME' | translate}} @@ -15,14 +15,14 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = ` {{'REPLICATION.DESTINATION_NAME' | translate}} {{'REPLICATION.SCHEDULE' | translate}} {{'REPLICATION.PLACEHOLDER' | translate }} - - {{p.name}} - + + {{p.name}} + {{p.projects?.length>0 ? p.projects[0].name : ''}} - {{p.description ? p.description : '-'}} - {{p.targets?.length>0 ? p.targets[0].name : ''}} - {{p.trigger ? p.trigger.kind : ''}} + {{p.description ? p.description : '-'}} + {{p.targets?.length>0 ? p.targets[0].name : ''}} + {{p.trigger ? p.trigger.kind : ''}} {{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} {{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}} diff --git a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.ts b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.ts index 96bc4f98d..83d595299 100644 --- a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.ts +++ b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.ts @@ -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(); @Output() redirect = new EventEmitter(); @Output() openNewRule = new EventEmitter(); - @Output() replicateManual = new EventEmitter(); + @Output() replicateManual = new EventEmitter(); + @Output() hasJobs = new EventEmitter(); 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(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(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 { + jobList(id: string | number): Promise { let ruleData: ReplicationJobItem[]; - this.canDeleteRule = true; + this.canDeleteRuleList = []; let count: number = 0; return toPromise(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 { - this.batchDelectionInfos = []; - let initBatchMessage = new BatchInfo (); - initBatchMessage.name = rule.name; + deleteRule(rules: ReplicationRule[]) { + if (rules && rules.length) { + let nameArr: string[] = []; + this.batchDelectionInfos = []; + rules.forEach(data => { + nameArr.push(data.name); + let initBatchMessage = new BatchInfo(); + initBatchMessage.name = data.name; this.batchDelectionInfos.push(initBatchMessage); - deletionMessage = new ConfirmationMessage( - 'REPLICATION.DELETION_TITLE', - 'REPLICATION.DELETION_SUMMARY', - rule.name || '', - rule, - ConfirmationTargets.POLICY, - ConfirmationButtons.DELETE_CANCEL); - } + }); + let deletionMessage = new ConfirmationMessage( + 'REPLICATION.DELETION_TITLE', + 'REPLICATION.DELETION_SUMMARY', + 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)); + }) -} \ No newline at end of file + 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(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); + }); + } + }); + } +} diff --git a/src/ui_ng/lib/src/replication/replication.component.html.ts b/src/ui_ng/lib/src/replication/replication.component.html.ts index 4c5b34a92..3246ae2ce 100644 --- a/src/ui_ng/lib/src/replication/replication.component.html.ts +++ b/src/ui_ng/lib/src/replication/replication.component.html.ts @@ -11,8 +11,9 @@ export const REPLICATION_TEMPLATE: string = `
- +
+
{{'REPLICATION.REPLICATION_JOBS' | translate}}
@@ -37,7 +38,11 @@ export const REPLICATION_TEMPLATE: string = `
- + +
+ +
+
{{'REPLICATION.NAME' | translate}} {{'REPLICATION.STATUS' | translate}} {{'REPLICATION.OPERATION' | translate}} @@ -66,5 +71,7 @@ export const REPLICATION_TEMPLATE: string = `
+
+ `; \ No newline at end of file diff --git a/src/ui_ng/lib/src/replication/replication.component.ts b/src/ui_ng/lib/src/replication/replication.component.ts index 96a52d7e3..cdfc87544 100644 --- a/src/ui_ng/lib/src/replication/replication.component.ts +++ b/src/ui_ng/lib/src/replication/replication.component.ts @@ -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 = new CustomComparator('creation_time', 'date'); updateTimeComparator: Comparator = new CustomComparator('update_time', 'date'); @@ -259,14 +270,62 @@ export class ReplicationComponent implements OnInit, OnDestroy { } } - replicateManualRule(rule: ReplicationRule): void { - toPromise(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(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 = ""; diff --git a/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts b/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts index 6c73dc700..62aae2601 100644 --- a/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts +++ b/src/ui_ng/lib/src/repository-listview/repository-listview.component.ts @@ -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(this.repositoryService diff --git a/src/ui_ng/lib/src/service/replication.service.ts b/src/ui_ng/lib/src/service/replication.service.ts index c0f3bce2a..ee111c279 100644 --- a/src/ui_ng/lib/src/service/replication.service.ts +++ b/src/ui_ng/lib/src/service/replication.service.ts @@ -127,6 +127,8 @@ export abstract class ReplicationService { * @memberof ReplicationService */ abstract getJobLog(jobId: number | string): Observable | Promise | string; + + abstract stopJobs(jobId: number | string): Observable | Promise | 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 | Promise | 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)); + } } \ No newline at end of file diff --git a/src/ui_ng/lib/src/shared/shared.const.ts b/src/ui_ng/lib/src/shared/shared.const.ts index bd858757b..d93ac3188 100644 --- a/src/ui_ng/lib/src/shared/shared.const.ts +++ b/src/ui_ng/lib/src/shared/shared.const.ts @@ -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 }; diff --git a/src/ui_ng/lib/src/tag/tag.component.html.ts b/src/ui_ng/lib/src/tag/tag.component.html.ts index 878cf182d..b6a504a69 100644 --- a/src/ui_ng/lib/src/tag/tag.component.html.ts +++ b/src/ui_ng/lib/src/tag/tag.component.html.ts @@ -25,7 +25,7 @@ export const TAG_TEMPLATE = `
- +
@@ -38,7 +38,7 @@ export const TAG_TEMPLATE = ` {{'REPOSITORY.AUTHOR' | translate}} {{'REPOSITORY.CREATED' | translate}} {{'REPOSITORY.DOCKER_VERSION' | translate}} - {{'TGA.PLACEHOLDER' | translate }} + {{'TAG.PLACEHOLDER' | translate }} {{t.name}} diff --git a/src/ui_ng/lib/src/tag/tag.component.ts b/src/ui_ng/lib/src/tag/tag.component.ts index f99fd07e5..10f1c145c 100644 --- a/src/ui_ng/lib/src/tag/tag.component.ts +++ b/src/ui_ng/lib/src/tag/tag.component.ts @@ -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) => { diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json index 724ed8d2f..36a46656a 100644 --- a/src/ui_ng/package.json +++ b/src/ui_ng/package.json @@ -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", diff --git a/src/ui_ng/src/app/global-message/message.component.html b/src/ui_ng/src/app/global-message/message.component.html index d99d548b6..c3595dbbb 100644 --- a/src/ui_ng/src/app/global-message/message.component.html +++ b/src/ui_ng/src/app/global-message/message.component.html @@ -4,7 +4,7 @@ {{message}} -
+
diff --git a/src/ui_ng/src/app/project/member/member.component.ts b/src/ui_ng/src/app/project/member/member.component.ts index d24ab6fd9..01cba055a 100644 --- a/src/ui_ng/src/app/project/member/member.component.ts +++ b/src/ui_ng/src/app/project/member/member.component.ts @@ -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); } diff --git a/src/ui_ng/src/app/replication/replication-page.component.html b/src/ui_ng/src/app/replication/replication-page.component.html index b2ebb01ba..83bea8c9b 100644 --- a/src/ui_ng/src/app/replication/replication-page.component.html +++ b/src/ui_ng/src/app/replication/replication-page.component.html @@ -1,3 +1,3 @@
- +
\ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication-page.component.ts b/src/ui_ng/src/app/replication/replication-page.component.ts index ba76fa970..69c25e83d 100644 --- a/src/ui_ng/src/app/replication/replication-page.component.ts +++ b/src/ui_ng/src/app/replication/replication-page.component.ts @@ -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) { diff --git a/src/ui_ng/src/app/replication/replication-rule/replication-rule.component.ts b/src/ui_ng/src/app/replication/replication-rule/replication-rule.component.ts index 3a1f76a4d..f7047db37 100644 --- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.component.ts +++ b/src/ui_ng/src/app/replication/replication-rule/replication-rule.component.ts @@ -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); - this.router.navigate(['/harbor/replications']); + 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); - this.router.navigate(['/harbor/replications']); + 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[] } = {}; diff --git a/src/ui_ng/src/app/replication/replication-rule/replication-rule.css b/src/ui_ng/src/app/replication/replication-rule/replication-rule.css index 28e92dee5..63b87a8d3 100644 --- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.css +++ b/src/ui_ng/src/app/replication/replication-rule/replication-rule.css @@ -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;} \ No newline at end of file +.switchIcon{width:20px;height:20px; margin-top: 2px;margin-left: 15px;} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication-rule/replication-rule.html b/src/ui_ng/src/app/replication/replication-rule/replication-rule.html index c763a07cc..2765c080c 100644 --- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.html +++ b/src/ui_ng/src/app/replication/replication-rule/replication-rule.html @@ -1,10 +1,11 @@
- < {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}} + < {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}} + <{{'SIDE_NAV.PROJECTS' | translate}}   {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}

{{headerTitle | translate}}

- +