+
{{'REPLICATION.SOURCE' | translate}} {{'PROJECT.PROJECTS' | translate | lowercase}}
+
+
-
- {{'REPLICATION.NAME' | translate}}*
-
-
-
- {{'REPLICATION.NAME_IS_REQUIRED' | translate}}
-
+
+
{{noProjectInfo | translate}}
+
+
+
+
`;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/replication/replication.component.spec.ts b/src/ui_ng/lib/src/replication/replication.component.spec.ts
index 7b720ec15..d349a809b 100644
--- a/src/ui_ng/lib/src/replication/replication.component.spec.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.spec.ts
@@ -12,7 +12,7 @@ import { DatePickerComponent } from '../datetime-picker/datetime-picker.componen
import { DateValidatorDirective } from '../datetime-picker/date-validator.directive';
import { FilterComponent } from '../filter/filter.component';
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
-import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface';
+import {ReplicationRule, ReplicationJob, Endpoint} from '../service/interface';
import { ErrorHandler } from '../error-handler/error-handler';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
@@ -20,49 +20,92 @@ import { ReplicationService, ReplicationDefaultService } from '../service/replic
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
import { JobLogService, JobLogDefaultService, ReplicationJobItem } from '../service/index';
+import {Project} from "../project-policy-config/project";
+import {ProjectDefaultService, ProjectService} from "service/project.service";
-describe('Replication Component (inline template)', ()=>{
+describe('Replication Component (inline template)', () => {
let mockRules: ReplicationRule[] = [
- {
- "id": 1,
- "project_id": 1,
- "project_name": "library",
- "target_id": 1,
- "target_name": "target_01",
- "name": "sync_01",
- "enabled": 0,
- "description": "",
- "cron_str": "",
- "error_job_count": 2,
- "deleted": 0
- },
- {
- "id": 2,
- "project_id": 1,
- "project_name": "library",
- "target_id": 3,
- "target_name": "target_02",
- "name": "sync_02",
- "enabled": 1,
- "description": "",
- "cron_str": "",
- "error_job_count": 1,
- "deleted": 0
- },
- {
- "id": 3,
- "project_id": 1,
- "project_name": "library",
- "target_id": 2,
- "target_name": "target_03",
- "name": "sync_03",
- "enabled": 0,
- "description": "",
- "cron_str": "",
- "error_job_count": 0,
- "deleted": 0
- }
+ {
+ "id": 1,
+ "projects": [{
+ "project_id": 33,
+ "owner_id": 1,
+ "name": "aeas",
+ "deleted": 0,
+ "togglable": false,
+ "current_user_role_id": 0,
+ "repo_count": 0,
+ "metadata": {
+ "public": false,
+ "enable_content_trust": "",
+ "prevent_vul": "",
+ "severity": "",
+ "auto_scan": ""},
+ "owner_name": "",
+ "creation_time": null,
+ "update_time": null,
+ "has_project_admin_role": true,
+ "is_member": true,
+ "role_name": ""
+ }],
+ "targets": [{
+ "id": 1,
+ "endpoint": "https://10.117.4.151",
+ "name": "target_01",
+ "username": "admin",
+ "password": "",
+ "insecure": false,
+ "type": 0
+ }],
+ "name": "sync_01",
+ "description": "",
+ "filters": null,
+ "trigger": {"kind": "Manual", "schedule_param": null},
+ "error_job_count": 2,
+ "replicate_deletion": false,
+ "replicate_existing_image_now": false,
+ },
+ {
+ "id": 2,
+ "projects": [{
+ "project_id": 33,
+ "owner_id": 1,
+ "name": "aeas",
+ "deleted": 0,
+ "togglable": false,
+ "current_user_role_id": 0,
+ "repo_count": 0,
+ "metadata": {
+ "public": false,
+ "enable_content_trust": "",
+ "prevent_vul": "",
+ "severity": "",
+ "auto_scan": ""},
+ "owner_name": "",
+ "creation_time": null,
+ "update_time": null,
+ "has_project_admin_role": true,
+ "is_member": true,
+ "role_name": ""
+ }],
+ "targets": [{
+ "id": 1,
+ "endpoint": "https://10.117.4.151",
+ "name": "target_01",
+ "username": "admin",
+ "password": "",
+ "insecure": false,
+ "type": 0
+ }],
+ "name": "sync_02",
+ "description": "",
+ "filters": null,
+ "trigger": {"kind": "Manual", "schedule_param": null},
+ "error_job_count": 2,
+ "replicate_deletion": false,
+ "replicate_existing_image_now": false,
+ }
];
let mockJobs: ReplicationJobItem[] = [
@@ -81,7 +124,7 @@ describe('Replication Component (inline template)', ()=>{
"repository": "library/mysql",
"policy_id": 1,
"operation": "transfer",
- "update_time": new Date("2017-05-27 12:20:33"),
+ "update_time": new Date("2017-05-27 12:20:33"),
"tags": null
},
{
@@ -95,71 +138,66 @@ describe('Replication Component (inline template)', ()=>{
}
];
+ let mockEndpoints: Endpoint[] = [
+ {
+ "id": 1,
+ "endpoint": "https://10.117.4.151",
+ "name": "target_01",
+ "username": "admin",
+ "password": "",
+ "insecure": false,
+ "type": 0
+ },
+ {
+ "id": 2,
+ "endpoint": "https://10.117.5.142",
+ "name": "target_02",
+ "username": "AAA",
+ "password": "",
+ "insecure": false,
+ "type": 0
+ },
+ ];
+
+ let mockProjects: Project[] = [
+ { "project_id": 1,
+ "owner_id": 0,
+ "name": 'project_01',
+ "creation_time": '',
+ "deleted": 0,
+ "owner_name": '',
+ "togglable": false,
+ "update_time": '',
+ "current_user_role_id": 0,
+ "repo_count": 0,
+ "has_project_admin_role": false,
+ "is_member": false,
+ "role_name": '',
+ "metadata": {
+ "public": '',
+ "enable_content_trust": '',
+ "prevent_vul": '',
+ "severity": '',
+ "auto_scan": '',
+ }
+ }];
+
let mockJob: ReplicationJob = {
metadata: {xTotalCount: 3},
data: mockJobs
};
- let mockEndpoints: Endpoint[] = [
- {
- "id": 1,
- "endpoint": "https://10.117.4.151",
- "name": "target_01",
- "username": "admin",
- "password": "",
- "insecure": false,
- "type": 0
- },
- {
- "id": 2,
- "endpoint": "https://10.117.5.142",
- "name": "target_02",
- "username": "AAA",
- "password": "",
- "insecure": false,
- "type": 0
- },
- {
- "id": 3,
- "endpoint": "https://101.1.11.111",
- "name": "target_03",
- "username": "admin",
- "password": "",
- "insecure": false,
- "type": 0
- },
- {
- "id": 4,
- "endpoint": "http://4.4.4.4",
- "name": "target_04",
- "username": "",
- "password": "",
- "insecure": false,
- "type": 0
- }
- ];
-
- let mockRule: ReplicationRule = {
- "id": 1,
- "project_id": 1,
- "project_name": "library",
- "target_id": 1,
- "target_name": "target_01",
- "name": "sync_01",
- "enabled": 0,
- "description": "",
- "cron_str": "",
- "error_job_count": 2,
- "deleted": 0
- };
-
let fixture: ComponentFixture
;
+ let fixtureCreate: ComponentFixture;
let comp: ReplicationComponent;
+ let compCreate: CreateEditRuleComponent;
let replicationService: ReplicationService;
+ let endpointService: EndpointService;
let spyRules: jasmine.Spy;
let spyJobs: jasmine.Spy;
+ let spyEndpoint: jasmine.Spy;
let deGrids: DebugElement[];
let deRules: DebugElement;
@@ -194,23 +232,29 @@ describe('Replication Component (inline template)', ()=>{
{ provide: SERVICE_CONFIG, useValue: config },
{ provide: ReplicationService, useClass: ReplicationDefaultService },
{ provide: EndpointService, useClass: EndpointDefaultService },
+ { provide: ProjectService, useClass: ProjectDefaultService },
{ provide: JobLogService, useClass: JobLogDefaultService }
]
});
}));
- beforeEach(()=>{
+ beforeEach(() => {
fixture = TestBed.createComponent(ReplicationComponent);
-
+ fixtureCreate = TestBed.createComponent(CreateEditRuleComponent);
comp = fixture.componentInstance;
+ compCreate = fixtureCreate.componentInstance;
comp.projectId = 1;
comp.search.ruleId = 1;
replicationService = fixture.debugElement.injector.get(ReplicationService);
-
+
+ endpointService = fixtureCreate.debugElement.injector.get(EndpointService);
+
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob));
-
+
+ spyEndpoint = spyOn(endpointService, 'getEndpoints').and.returnValues(Promise.resolve(mockEndpoints));
+
fixture.detectChanges();
fixture.whenStable().then(()=>{
fixture.detectChanges();
@@ -221,6 +265,7 @@ describe('Replication Component (inline template)', ()=>{
});
});
+
it('Should load replication rules', async(()=>{
fixture.detectChanges();
fixture.whenStable().then(()=>{
diff --git a/src/ui_ng/lib/src/replication/replication.component.ts b/src/ui_ng/lib/src/replication/replication.component.ts
index 7a8de6fba..5479f9ad5 100644
--- a/src/ui_ng/lib/src/replication/replication.component.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.ts
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit, ViewChild, Input, Output, OnDestroy, EventEmitter } from '@angular/core';
-import { ResponseOptions, RequestOptions } from '@angular/http';
-import { NgModel } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
@@ -23,7 +21,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
import { ReplicationService } from '../service/replication.service';
import { RequestQueryParams } from '../service/RequestQueryParams';
-import { ReplicationRule, ReplicationJob, Endpoint, ReplicationJobItem } from '../service/interface';
+import { ReplicationRule, ReplicationJob, ReplicationJobItem } from '../service/interface';
import {
toPromise,
@@ -89,13 +87,14 @@ export class SearchOption {
export class ReplicationComponent implements OnInit, OnDestroy {
@Input() projectId: number | string;
+ @Input() projectName: string;
@Input() isSystemAdmin: boolean;
@Input() withReplicationJob: boolean;
- @Input() readonly: boolean;
@Output() redirect = new EventEmitter();
@Output() openCreateRule = new EventEmitter();
@Output() openEdit = new EventEmitter();
+ @Output() goToRegistry = new EventEmitter();
search: SearchOption = new SearchOption();
@@ -106,7 +105,6 @@ export class ReplicationComponent implements OnInit, OnDestroy {
currentJobStatus: { key: string, description: string };
changedRules: ReplicationRule[];
- initSelectedId: number | string;
rules: ReplicationRule[];
loading: boolean;
@@ -121,8 +119,8 @@ export class ReplicationComponent implements OnInit, OnDestroy {
@ViewChild(ListReplicationRuleComponent)
listReplicationRule: ListReplicationRuleComponent;
-/* @ViewChild(CreateEditRuleComponent)
- createEditPolicyComponent: CreateEditRuleComponent;*/
+ @ViewChild(CreateEditRuleComponent)
+ createEditPolicyComponent: CreateEditRuleComponent;
@ViewChild("replicationLogViewer")
replicationLogViewer: JobLogViewerComponent;
@@ -164,20 +162,22 @@ export class ReplicationComponent implements OnInit, OnDestroy {
}
}
+ // open replication rule
openModal(): void {
- this.openCreateRule.emit();
+ this.createEditPolicyComponent.openCreateEditRule();
}
+ // edit replication rule
openEditRule(rule: ReplicationRule) {
if (rule) {
- let editable = true;
- if (rule.enabled === 1) {
- editable = false;
- }
- this.openEdit.emit(rule.id);
+ this.createEditPolicyComponent.openCreateEditRule(rule.id);
}
}
+ goRegistry(): void {
+ this.goToRegistry.emit();
+ }
+
//Server driven data loading
clrLoadJobs(state: State): void {
if (!state || !state.page || !this.search.ruleId) {
@@ -209,6 +209,12 @@ export class ReplicationComponent implements OnInit, OnDestroy {
}
this.jobsLoading = true;
+
+ //Do filtering and sorting
+ this.jobs = doFiltering(this.jobs, state);
+ this.jobs = doSorting(this.jobs, state);
+
+ this.jobsLoading = false;
toPromise(this.replicationService
.getJobs(this.search.ruleId, params))
.then(
diff --git a/src/ui_ng/lib/src/service/interface.ts b/src/ui_ng/lib/src/service/interface.ts
index 6b876154d..5aa592b98 100644
--- a/src/ui_ng/lib/src/service/interface.ts
+++ b/src/ui_ng/lib/src/service/interface.ts
@@ -1,3 +1,4 @@
+import {Project} from "../project-policy-config/project";
/**
* The base interface contains the general properties
*
@@ -83,18 +84,40 @@ export interface Endpoint extends Base {
*
* @export
* @interface ReplicationRule
+ * @interface Filter
+ * @interface Trigger
*/
export interface ReplicationRule extends Base {
- project_id: number | string;
- project_name: string;
- target_id: number | string;
- target_name: string;
- enabled: number;
- description?: string;
- cron_str?: string;
- start_time?: Date;
- error_job_count?: number;
- deleted: number;
+ [key: string]: any;
+ id?: number;
+ name: string;
+ description: string;
+ projects: Project[];
+ targets: Endpoint[] ;
+ trigger: Trigger ;
+ filters: Filter[] ;
+ replicate_existing_image_now?: boolean;
+ replicate_deletion?: boolean;
+}
+
+export class Filter {
+ kind: string;
+ pattern: string;
+ constructor(kind: string, pattern: string) {
+ this.kind = kind;
+ this.pattern = pattern;
+ }
+}
+
+export class Trigger {
+ kind: string;
+ schedule_param: any | {
+ [key: string]: any | any[];
+ };
+ constructor(kind: string, param: any | { [key: string]: any | any[]; }) {
+ this.kind = kind;
+ this.schedule_param = param;
+ }
}
/**
@@ -115,7 +138,7 @@ export interface ReplicationJob {
* @interface ReplicationJob
*/
export interface ReplicationJobItem extends Base {
- [key: string]: any | any[]
+ [key: string]: any | any[];
status: string;
repository: string;
policy_id: number;
@@ -151,7 +174,7 @@ export interface AccessLog {
* @interface AccessLogItem
*/
export interface AccessLogItem {
- [key: string]: any | any[]
+ [key: string]: any | any[];
log_id: number;
project_id: number;
repo_name: string;
diff --git a/src/ui_ng/lib/src/service/project.service.ts b/src/ui_ng/lib/src/service/project.service.ts
index 341123dd7..6d90a20f4 100644
--- a/src/ui_ng/lib/src/service/project.service.ts
+++ b/src/ui_ng/lib/src/service/project.service.ts
@@ -6,7 +6,8 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { Project } from '../project-policy-config/project';
import { ProjectPolicy } from '../project-policy-config/project-policy-config.component';
-import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../utils";
+import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS, buildHttpRequestOptions} from "../utils";
+import {RequestQueryParams} from "./RequestQueryParams";
/**
* Define the service methods to handle the Prject related things.
@@ -38,6 +39,8 @@ export abstract class ProjectService {
* @memberOf EndpointService
*/
abstract updateProjectPolicy(projectId: number | string, projectPolicy: ProjectPolicy): Observable | Promise | any;
+
+ abstract listProjects(name: string, isPublic: number, page?: number, pageSize?: number): Observable | Promise | Project[];
}
/**
@@ -68,6 +71,27 @@ export class ProjectDefaultService extends ProjectService {
.catch(error => Observable.throw(error));
}
+ listProjects(name: string, isPublic: number, page?: number, pageSize?: number): Observable | Promise | Project[] {
+ let params = new RequestQueryParams();
+ if (page && pageSize) {
+ params.set('page', page + '');
+ params.set('page_size', pageSize + '');
+ }
+ if (name && name.trim() !== "") {
+ params.set('name', name);
+
+ }
+ if (isPublic !== undefined) {
+ params.set('public', '' + isPublic);
+ }
+
+ // let options = new RequestOptions({ headers: this.getHeaders, search: params });
+ return this.http
+ .get(`/api/projects`, buildHttpRequestOptions(params))
+ .map(response => response.json())
+ .catch(error => Observable.throw(error));
+ }
+
public updateProjectPolicy(projectId: number | string, projectPolicy: ProjectPolicy): any {
return this.http
.put(`/api/projects/${projectId}`, { 'metadata': {
diff --git a/src/ui_ng/lib/src/service/replication.service.ts b/src/ui_ng/lib/src/service/replication.service.ts
index ee111c279..ecb5b1797 100644
--- a/src/ui_ng/lib/src/service/replication.service.ts
+++ b/src/ui_ng/lib/src/service/replication.service.ts
@@ -1,6 +1,6 @@
import { Observable } from 'rxjs/Observable';
import { RequestQueryParams } from './RequestQueryParams';
-import { ReplicationJob, ReplicationRule, ReplicationJobItem } from './interface';
+import {ReplicationJob, ReplicationRule, ReplicationJobItem} from './interface';
import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { Http, RequestOptions } from '@angular/http';
@@ -62,7 +62,7 @@ export abstract class ReplicationService {
*
* @memberOf ReplicationService
*/
- abstract updateReplicationRule(replicationRule: ReplicationRule): Observable | Promise | any;
+ abstract updateReplicationRule(id: number, rep: ReplicationRule): Observable | Promise | any;
/**
* Delete the specified replication rule.
@@ -159,7 +159,7 @@ export class ReplicationDefaultService extends ReplicationService {
//Private methods
//Check if the rule object is valid
_isValidRule(rule: ReplicationRule): boolean {
- return rule !== undefined && rule != null && rule.name !== undefined && rule.name.trim() !== '' && rule.target_id !== 0;
+ return rule !== undefined && rule != null && rule.name !== undefined && rule.name.trim() !== '' && rule.targets.length !== 0;
}
public getReplicationRules(projectId?: number | string, ruleName?: string, queryParams?: RequestQueryParams): Observable | Promise | ReplicationRule[] {
@@ -177,7 +177,7 @@ export class ReplicationDefaultService extends ReplicationService {
return this.http.get(this._ruleBaseUrl, buildHttpRequestOptions(queryParams)).toPromise()
.then(response => response.json() as ReplicationRule[])
- .catch(error => Promise.reject(error))
+ .catch(error => Promise.reject(error));
}
public getReplicationRule(ruleId: number | string): Observable | Promise | ReplicationRule {
@@ -201,13 +201,13 @@ export class ReplicationDefaultService extends ReplicationService {
.catch(error => Promise.reject(error));
}
- public updateReplicationRule(replicationRule: ReplicationRule): Observable | Promise | any {
- if (!this._isValidRule(replicationRule) || !replicationRule.id) {
+ public updateReplicationRule(id: number, rep: ReplicationRule): Observable | Promise | any {
+ if (!this._isValidRule(rep)) {
return Promise.reject('Bad argument');
}
- let url: string = `${this._ruleBaseUrl}/${replicationRule.id}`;
- return this.http.put(url, JSON.stringify(replicationRule), HTTP_JSON_OPTIONS).toPromise()
+ let url = `${this._ruleBaseUrl}/${id}`;
+ return this.http.put(url, JSON.stringify(rep), HTTP_JSON_OPTIONS).toPromise()
.then(response => response)
.catch(error => Promise.reject(error));
}
@@ -298,7 +298,7 @@ export class ReplicationDefaultService extends ReplicationService {
return Promise.reject('Bad argument');
}
- let logUrl: string = `${this._jobBaseUrl}/${jobId}/log`;
+ let logUrl = `${this._jobBaseUrl}/${jobId}/log`;
return this.http.get(logUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response.text())
.catch(error => Promise.reject(error));
diff --git a/src/ui_ng/lib/src/shared/shared.module.ts b/src/ui_ng/lib/src/shared/shared.module.ts
index 365b14ee9..63d85fa16 100644
--- a/src/ui_ng/lib/src/shared/shared.module.ts
+++ b/src/ui_ng/lib/src/shared/shared.module.ts
@@ -2,8 +2,8 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule, Http } from '@angular/http';
import { ClarityModule } from 'clarity-angular';
-import { FormsModule } from '@angular/forms';
-import { TranslateModule, TranslateLoader, TranslateService, MissingTranslationHandler } from '@ngx-translate/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { TranslateModule, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core';
import { MyMissingTranslationHandler } from '../i18n/missing-trans.handler';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslatorJsonLoader } from '../i18n/local-json.loader';
@@ -41,6 +41,7 @@ export function GeneralTranslatorLoader(http: Http, config: IServiceConfig) {
CommonModule,
HttpModule,
FormsModule,
+ ReactiveFormsModule,
ClipboardModule,
CookieModule.forRoot(),
ClarityModule.forRoot(),
@@ -60,6 +61,7 @@ export function GeneralTranslatorLoader(http: Http, config: IServiceConfig) {
CommonModule,
HttpModule,
FormsModule,
+ ReactiveFormsModule,
CookieModule,
ClipboardModule,
ClarityModule,
diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json
index 2afe1600e..88b5993f2 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.45",
+ "harbor-ui": "0.6.46",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",
diff --git a/src/ui_ng/src/app/harbor-routing.module.ts b/src/ui_ng/src/app/harbor-routing.module.ts
index 42e083df2..9b84013cd 100644
--- a/src/ui_ng/src/app/harbor-routing.module.ts
+++ b/src/ui_ng/src/app/harbor-routing.module.ts
@@ -50,8 +50,6 @@ import { LeavingConfigRouteDeactivate } from './shared/route/leaving-config-deac
import { MemberGuard } from './shared/route/member-guard-activate.service';
import { TagDetailPageComponent } from './repository/tag-detail/tag-detail-page.component';
-import { ReplicationRuleComponent} from "./replication/replication-rule/replication-rule.component";
-import {LeavingNewRuleRouteDeactivate} from "./shared/route/leaving-new-rule-deactivate.service";
import { LeavingRepositoryRouteDeactivate } from './shared/route/leaving-repository-deactivate.service';
const harborRoutes: Routes = [
@@ -92,20 +90,6 @@ const harborRoutes: Routes = [
canActivate: [SystemAdminGuard],
canActivateChild: [SystemAdminGuard],
},
- {
- path: 'replications/:id/rule',
- component: ReplicationRuleComponent,
- canActivate: [SystemAdminGuard],
- canActivateChild: [SystemAdminGuard],
- canDeactivate: [LeavingNewRuleRouteDeactivate]
- },
- {
- path: 'replications/new-rule',
- component: ReplicationRuleComponent,
- canActivate: [SystemAdminGuard],
- canActivateChild: [SystemAdminGuard],
- canDeactivate: [LeavingNewRuleRouteDeactivate]
- },
{
path: 'tags/:id/:repo',
component: TagRepositoryComponent,
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 83bea8c9b..e61451484 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 69c25e83d..863c36e0e 100644
--- a/src/ui_ng/src/app/replication/replication-page.component.ts
+++ b/src/ui_ng/src/app/replication/replication-page.component.ts
@@ -15,6 +15,8 @@ import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import { ReplicationComponent } from 'harbor-ui';
import {SessionService} from "../shared/session.service";
+import {Project} from "../project/project";
+import {ProjectService} from "../project/project.service";
@Component({
selector: 'replicaton',
@@ -23,13 +25,25 @@ import {SessionService} from "../shared/session.service";
export class ReplicationPageComponent implements OnInit, AfterViewInit {
projectIdentify: string | number;
@ViewChild("replicationView") replicationView: ReplicationComponent;
+ projectName: string;
constructor(private route: ActivatedRoute,
- private router: Router,
+ private proService: ProjectService,
private session: SessionService) { }
ngOnInit(): void {
this.projectIdentify = +this.route.snapshot.parent.params['id'];
+
+ this.proService.listProjects("", undefined).toPromise()
+ .then(response => {
+ let projects = response.json() as Project[];
+ if (projects.length) {
+ let project = projects.find(data => data.project_id === this.projectIdentify);
+ if (project) {
+ this.projectName = project.name;
+ }
+ }
+ });
}
public get isSystemAdmin(): boolean {
@@ -37,14 +51,6 @@ export class ReplicationPageComponent implements OnInit, AfterViewInit {
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/list-project-model/list-project-model.component.css b/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.css
deleted file mode 100644
index ab5ec9250..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.datagrid .datagrid-head{border: 0;}
-.option-right{ position: absolute; right: 30px; top: 55px;}
-:host >>> .datagrid-head{height: 0;border-width: 0;}
-:host >>> .datagrid-scroll-wrapper .datagrid{margin-top: 0;}
-.modal-body{height: 30em; overflow-y: auto; margin-top: 20px;}
diff --git a/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.html b/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.html
deleted file mode 100644
index 0af247b58..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
- {{'PROJECT.ALL_PROJECTS' | translate}}
-
-
-
-
-
- {{projectTypes[0] | translate}}
- {{projectTypes[1] | translate}}
- {{projectTypes[2] | translate}}
-
-
-
-
-
-
-
-
-
-
- {{project.name}}
-
-
- {{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'PROJECT.OF' | translate}} {{pagination.totalItems }} {{'PROJECT.ITEMS' | translate}}
-
-
-
-
-
-
diff --git a/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.ts b/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.ts
deleted file mode 100644
index bf9738c99..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-import {
- Component,
- Output,
- Input,
- ChangeDetectionStrategy,
- ChangeDetectorRef,
- OnDestroy, EventEmitter
-} from '@angular/core';
-import { Router, NavigationExtras } from '@angular/router';
-
-import { SessionService } from '../../../shared/session.service';
-import { SearchTriggerService } from '../../../base/global-search/search-trigger.service';
-import { ProjectTypes, RoleInfo} from '../../../shared/shared.const';
-import { CustomComparator, doFiltering, doSorting, calculatePage } from '../../../shared/shared.utils';
-
-import { Comparator, State } from 'clarity-angular';
-import { MessageHandlerService } from '../../../shared/message-handler/message-handler.service';
-import { StatisticHandler } from '../../../shared/statictics/statistic-handler.service';
-import { Subscription } from 'rxjs/Subscription';
-import { ConfirmationDialogService } from '../../../shared/confirmation-dialog/confirmation-dialog.service';
-import { ConfirmationMessage } from '../../../shared/confirmation-dialog/confirmation-message';
-import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../../../shared/shared.const';
-import {ProjectService} from "../../../project/project.service";
-import {Project} from "../../../project/project";
-
-@Component({
- selector: 'list-project-model',
- templateUrl: 'list-project-model.component.html',
- styleUrls: ['list-project-model.component.css'],
- changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class ListProjectModelComponent {
- projectTypes = ProjectTypes;
- loading: boolean = true;
- projects: Project[] = [];
- filteredType: number = 0;//All projects
- searchKeyword: string = "";
- ismodelOpen: boolean ;
- currentFilteredType: number = 0;//all projects
- projectName: string = "";
- selectedProject: Project;
-
- roleInfo = RoleInfo;
- repoCountComparator: Comparator = new CustomComparator("repo_count", "number");
- timeComparator: Comparator = new CustomComparator("creation_time", "date");
- accessLevelComparator: Comparator = new CustomComparator("public", "number");
- roleComparator: Comparator = new CustomComparator("current_user_role_id", "number");
- currentPage: number = 1;
- totalCount: number = 0;
- pageSize: number = 10;
- currentState: State;
- @Output() selectedPro = new EventEmitter();
-
- constructor(
- private session: SessionService,
- private router: Router,
- private searchTrigger: SearchTriggerService,
- private proService: ProjectService,
- private msgHandler: MessageHandlerService,
- private statisticHandler: StatisticHandler,
- private deletionDialogService: ConfirmationDialogService,
- private ref: ChangeDetectorRef) {
- }
-
- get selecteType(): number {
- return this.currentFilteredType;
- }
- set selecteType(_project: number) {
- this.currentFilteredType = _project;
- if (window.sessionStorage) {
- window.sessionStorage['projectTypeValue'] = _project;
- }
- }
-
-
- clrLoad(state: State) {
- this.selectedProject = null;
- //Keep state for future filtering and sorting
- this.currentState = state;
-
- let pageNumber: number = calculatePage(state);
- if (pageNumber <= 0) { pageNumber = 1; }
-
- this.loading = true;
-
- let passInFilteredType: number = undefined;
- if (this.filteredType > 0) {
- passInFilteredType = this.filteredType - 1;
- }
- this.proService.listProjects(this.searchKeyword, passInFilteredType, pageNumber, this.pageSize).toPromise()
- .then(response => {
- //Get total count
- if (response.headers) {
- let xHeader: string = response.headers.get("X-Total-Count");
- if (xHeader) {
- this.totalCount = parseInt(xHeader, 0);
- }
- }
-
- this.projects = response.json() as Project[];
- //Do customising filtering and sorting
- this.projects = doFiltering(this.projects, state);
- this.projects = doSorting(this.projects, state);
-
- this.loading = false;
- })
- .catch(error => {
- this.loading = false;
- this.msgHandler.handleError(error);
- });
-
- //Force refresh view
- let hnd = setInterval(() => this.ref.markForCheck(), 100);
- setTimeout(() => clearInterval(hnd), 3000);
- }
-
- openModel(): void {
- this.selectedProject = null;
- this.ismodelOpen = true;
- //Force refresh view
- let hnd = setInterval(() => this.ref.markForCheck(), 100);
- setTimeout(() => clearInterval(hnd), 2000);
- }
-
- refresh(): void {
- this.currentPage = 1;
- this.filteredType = 0;
- this.searchKeyword = '';
-
- this.reload();
- }
-
- doFilterProject(): void {
- this.currentPage = 1;
- this.filteredType = this.selecteType;
- this.reload();
- }
-
- doSearchProject(proName: string): void {
- this.projectName = proName;
- this.currentPage = 1;
- this.searchKeyword = proName;
- this.reload();
- }
-
- reload(): void {
- let st: State = this.currentState;
- if (!st) {
- st = {
- page: {}
- };
- }
- st.page.from = 0;
- st.page.to = this.pageSize - 1;
- st.page.size = this.pageSize;
-
- this.clrLoad(st);
- }
-
- oKModel() {
- this.ismodelOpen = false;
- this.selectedPro.emit(this.selectedProject);
- }
-
- closeModel(): void {
- this.ismodelOpen = false;
- }
-}
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
deleted file mode 100644
index f45431806..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.component.ts
+++ /dev/null
@@ -1,590 +0,0 @@
-import {Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, AfterViewInit} from '@angular/core';
-import {ProjectService} from '../../project/project.service';
-import {Project} from '../../project/project';
-import {ActivatedRoute, Router} from '@angular/router';
-import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
-import {ReplicationRuleServie} from "./replication-rule.service";
-import {MessageHandlerService} from "../../shared/message-handler/message-handler.service";
-import {Target, Filter, ReplicationRule} from "./replication-rule";
-import {ConfirmationDialogService} from "../../shared/confirmation-dialog/confirmation-dialog.service";
-import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const';
-import {Subscription} from "rxjs/Subscription";
-import {ConfirmationMessage} from "../../shared/confirmation-dialog/confirmation-message";
-import {Subject} from "rxjs/Subject";
-import {ListProjectModelComponent} from "./list-project-model/list-project-model.component";
-import {toPromise, isEmptyObject, compareValue} from "harbor-ui/src/utils";
-import {CreateEditEndpointComponent} from "harbor-ui/src/create-edit-endpoint/create-edit-endpoint.component";
-
-const ONE_HOUR_SECONDS: number = 3600;
-const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
-
-@Component ({
- selector: 'repliction-rule',
- templateUrl: 'replication-rule.html',
- styleUrls: ['replication-rule.css']
-
-})
-
-export class ReplicationRuleComponent implements OnInit, OnDestroy {
- _localTime: Date = new Date();
- policyId: number;
- projectId: number;
- targetList: Target[] = [];
- isFilterHide: boolean = false;
- weeklySchedule: boolean;
- isScheduleOpt: boolean;
- isImmediate: boolean = false;
- noProjectInfo: string = "";
- noSelectedProject: boolean = true;
- noSelectedEndpoint: boolean = true;
- filterCount: number = 0;
- selectedprojectList: Project[] = [];
- triggerNames: string[] = ['Manual', 'Immediate', 'Scheduled'];
- scheduleNames: string[] = ['Daily', 'Weekly'];
- weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
- filterSelect: string[] = ['repository', 'tag'];
- ruleNameTooltip: string = 'TOOLTIP.EMPTY';
- headerTitle: string = 'REPLICATION.ADD_POLICY';
-
- filterListData: {[key: string]: any}[] = [];
- inProgress: boolean = false;
- inNameChecking: boolean = false;
- isRuleNameExist: boolean = false;
- isSubmitOver: boolean = false;
- nameChecker: Subject = new Subject();
-
- confirmSub: Subscription;
- ruleForm: FormGroup;
- copyUpdateForm: ReplicationRule;
- emptyEndpoint = new Target();
-
- @ViewChild(ListProjectModelComponent)
- projectListModel: ListProjectModelComponent;
-
- @ViewChild(CreateEditEndpointComponent)
- createEditEndpointComponent: CreateEditEndpointComponent;
-
- baseFilterData(name: string, option: string[], state: boolean) {
- return {
- name: name,
- options: option,
- state: state,
- isValid: true
- };
- }
-
- constructor(public projectService: ProjectService,
- private router: Router,
- private fb: FormBuilder,
- private repService: ReplicationRuleServie,
- private route: ActivatedRoute,
- private msgHandler: MessageHandlerService,
- private confirmService: ConfirmationDialogService,
- public ref: ChangeDetectorRef) {
- this.createForm();
- Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
- .then(res => {
- if (!res[0]) {
- this.noSelectedEndpoint = true;
- }else {
- this.targetList = res[0];
- if (!this.policyId) {
- res[0].unshift(this.emptyEndpoint);
- this.setTarget([res[0][0]]);
- }
- }
- if (!res[1]) {
- this.noProjectInfo = 'REPLICATION.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));
- this.noSelectedProject = false;
- }
- }
- if (!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)
- .then((response) => {
- this.copyUpdateForm = Object.assign({}, response);
- // set filter value is [] if callback fiter value is null.
- this.copyUpdateForm.filters = response.filters ? response.filters : [];
- this.updateForm(response);
- }).catch(error => {
- this.msgHandler.handleError(error);
- });
- }
-
- this.nameChecker.debounceTime(500).distinctUntilChanged().subscribe((ruleName: string) => {
- this.isRuleNameExist = false;
- this.inNameChecking = true;
- toPromise(this.repService.getReplicationRules(0, ruleName))
- .then(response => {
- if (response.some(rule => rule.name === ruleName)) {
- this.ruleNameTooltip = 'TOOLTIP.RULE_USER_EXISTING';
- this.isRuleNameExist = true;
- }
- this.inNameChecking = false;
- }).catch(() => {
- this.inNameChecking = false;
- });
- });
- }
-
- ngOnDestroy(): void {
- if (this.confirmSub) {
- this.confirmSub.unsubscribe();
- }
- if (this.nameChecker) {
- this.nameChecker.unsubscribe();
- }
- }
-
- get isVaild() {
- return !(this.isRuleNameExist || this.noSelectedProject || this.noSelectedEndpoint || this.inProgress || this.isSubmitOver);
- }
-
- createForm() {
- this.ruleForm = this.fb.group({
- name: ['', Validators.required],
- description: '',
- projects: this.fb.array([]),
- targets: this.fb.array([]),
- trigger: this.fb.group({
- kind: this.triggerNames[0],
- schedule_param: this.fb.group({
- type: this.scheduleNames[0],
- weekday: 1,
- offtime: '08:00'
- }),
- }),
- filters: this.fb.array([]),
- replicate_existing_image_now: true,
- replicate_deletion: false
- });
- }
-
- updateForm(rule: ReplicationRule): void {
- rule.trigger = this.updateTrigger(rule.trigger);
- this.ruleForm.reset({
- name: rule.name,
- description: rule.description,
- trigger: rule.trigger,
- replicate_existing_image_now: rule.replicate_existing_image_now,
- replicate_deletion: rule.replicate_deletion
- });
- this.setProject(rule.projects);
- this.noSelectedProject = false;
- this.setTarget(rule.targets);
- this.noSelectedEndpoint = false;
-
- if (rule.filters) {
- this.setFilter(rule.filters);
- this.updateFilter(rule.filters);
- }
-
- // Force refresh view
- let hnd = setInterval(() => this.ref.markForCheck(), 100);
- setTimeout(() => clearInterval(hnd), 2000);
- }
-
- get projects(): FormArray {
- return this.ruleForm.get('projects') as FormArray;
- }
- setProject(projects: Project[]) {
- const projectFGs = projects.map(project => this.fb.group(project));
- const projectFormArray = this.fb.array(projectFGs);
- this.ruleForm.setControl('projects', projectFormArray);
- }
-
- get filters(): FormArray {
- return this.ruleForm.get('filters') as FormArray;
- }
- setFilter(filters: Filter[]) {
- const filterFGs = filters.map(filter => this.fb.group(filter));
- const filterFormArray = this.fb.array(filterFGs);
- this.ruleForm.setControl('filters', filterFormArray);
- }
-
- get targets(): FormArray {
- return this.ruleForm.get('targets') as FormArray;
- }
- setTarget(targets: Target[]) {
- const targetFGs = targets.map(target => this.fb.group(target));
- const targetFormArray = this.fb.array(targetFGs);
- this.ruleForm.setControl('targets', targetFormArray);
- }
-
- initFilter(name: string) {
- return this.fb.group({
- kind: name,
- pattern: ['', Validators.required]
- });
- }
-
- filterChange($event: any) {
- if ($event && $event.target['value']) {
- let id: number = $event.target.id;
- let name: string = $event.target.name;
- let value: string = $event.target['value'];
-
- this.filterListData.forEach((data, index) => {
- if (index === +id) {
- data.name = $event.target.name = value;
- }else {
- data.options.splice(data.options.indexOf(value), 1);
- }
- if (data.options.indexOf(name) === -1) {
- data.options.push(name);
- }
- });
- }
- }
-
- targetChange($event: any) {
- if ($event && $event.target && event.target['value']) {
- if ($event.target['value'] === '-1') {
- this.noSelectedEndpoint = true;
- return;
- }
- let selecedTarget: Target = this.targetList.find(target => target.id === +$event.target['value']);
- this.setTarget([selecedTarget]);
- this.noSelectedEndpoint = false;
- }
- }
-
- openProjectModel(): void {
- this.projectListModel.openModel();
- }
-
- selectedProject(project: Project): void {
- if (!project) {
- this.noSelectedProject = true;
- }else {
- this.noSelectedProject = false;
- this.setProject([project]);
- }
- }
-
- addNewFilter(): void {
- if (this.filterCount === 0) {
- this.filterListData.push(this.baseFilterData(this.filterSelect[0], this.filterSelect.slice(), true));
- this.filters.push(this.initFilter(this.filterSelect[0]));
-
- }else {
- let nameArr: string[] = this.filterSelect.slice();
- this.filterListData.forEach(data => {
- nameArr.splice(nameArr.indexOf(data.name), 1);
- });
- // when add a new filter,the filterListData should change the options
- this.filterListData.filter((data) => {
- data.options.splice(data.options.indexOf(nameArr[0]), 1);
- });
- this.filterListData.push(this.baseFilterData(nameArr[0], nameArr, true));
- this.filters.push(this.initFilter(nameArr[0]));
- }
- this.filterCount += 1;
- if (this.filterCount >= this.filterSelect.length) {
- this.isFilterHide = true;
- }
- }
-
- // delete a filter
- deleteFilter(i: number): void {
- if (i || i === 0) {
- let delfilter = this.filterListData.splice(i, 1)[0];
- if (this.filterCount === this.filterSelect.length) {
- this.isFilterHide = false;
- }
- this.filterCount -= 1;
- if (this.filterListData.length) {
- let optionVal = delfilter.name;
- this.filterListData.filter(data => {
- if (data.options.indexOf(optionVal) === -1) {
- data.options.push(optionVal);
- }
- });
- }
- const control = this.ruleForm.controls['filters'];
- control.removeAt(i);
- }
- }
-
- selectTrigger($event: any): void {
- if ($event && $event.target && $event.target['value']) {
- let val: string = $event.target['value'];
- if (val === this.triggerNames[2]) {
- this.isScheduleOpt = true;
- this.isImmediate = false;
- }
- if (val === this.triggerNames[1]) {
- this.isScheduleOpt = false;
- this.isImmediate = true;
- }
- if (val === this.triggerNames[0]) {
- this.isScheduleOpt = false;
- this.isImmediate = false;
- }
- }
- }
-
- // Replication Schedule select value exchange
- selectSchedule($event: any): void {
- if ($event && $event.target && $event.target['value']) {
- switch ($event.target['value']) {
- case this.scheduleNames[1]:
- this.weeklySchedule = true;
- this.ruleForm.patchValue({
- trigger: {
- schedule_param: {
- weekday: 1,
- }
- }
- })
- break;
- case this.scheduleNames[0]:
- this.weeklySchedule = false;
- break;
- }
- }
- }
-
- checkRuleName(): void {
- let ruleName: string = this.ruleForm.controls['name'].value;
- if (ruleName) {
- this.nameChecker.next(ruleName);
- } else {
- this.ruleNameTooltip = 'TOOLTIP.EMPTY';
- }
- }
-
- updateFilter(filters: any) {
- let opt: string[] = this.filterSelect.slice();
- filters.forEach((filter: any) => {
- opt.splice(opt.indexOf(filter.kind), 1);
- })
- filters.forEach((filter: any) => {
- let option: string [] = opt.slice();
- option.unshift(filter.kind);
- this.filterListData.push(this.baseFilterData(filter.kind, option, true));
- });
- this.filterCount = filters.length;
- if (filters.length === this.filterSelect.length) {
- this.isFilterHide = true;
- }
- }
-
- updateTrigger(trigger: any) {
- if (trigger['schedule_param']) {
- this.isScheduleOpt = true;
- this.isImmediate = false;
- trigger['schedule_param']['offtime'] = this.getOfftime(trigger['schedule_param']['offtime']);
- if (trigger['schedule_param']['weekday']) {
- this.weeklySchedule = true;
- }else {
- // set default
- trigger['schedule_param']['weekday'] = 1;
- }
- }else {
- if (trigger['kind'] === this.triggerNames[0]) {
- this.isImmediate = false;
- }
- trigger['schedule_param'] = { type: this.scheduleNames[0],
- weekday: this.weekly[0],
- offtime: '08:00'};
- }
- return trigger;
- }
-
- setTriggerVaule(trigger: any) {
- if (!this.isScheduleOpt) {
- delete trigger['schedule_param'];
- return trigger;
- }else {
- if (!this.weeklySchedule) {
- delete trigger['schedule_param']['weekday'];
- }else {
- trigger['schedule_param']['weekday'] = +trigger['schedule_param']['weekday'];
- }
- trigger['schedule_param']['offtime'] = this.setOfftime(trigger['schedule_param']['offtime']);
- return trigger;
- }
- }
-
- public hasFormChange(): boolean {
- return !isEmptyObject(this.getChanges());
- }
-
- onSubmit() {
- // add new Replication rule
- this.inProgress = true;
- let copyRuleForm: ReplicationRule = this.ruleForm.value;
- copyRuleForm.trigger = this.setTriggerVaule(copyRuleForm.trigger);
- if (!this.policyId) {
- this.repService.createReplicationRule(copyRuleForm)
- .then(() => {
- this.msgHandler.showSuccess('REPLICATION.CREATED_SUCCESS');
- this.inProgress = false;
- this.isSubmitOver = true;
- 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) => {
- this.inProgress = false;
- this.msgHandler.handleError(error);
- });
- } else {
- this.repService.updateReplicationRule(this.policyId, this.ruleForm.value)
- .then(() => {
- this.msgHandler.showSuccess('REPLICATION.UPDATED_SUCCESS');
- this.inProgress = false;
- this.isSubmitOver = true;
- 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) => {
- this.inProgress = false;
- this.msgHandler.handleError(error);
- });
- }
- }
-
- openModal() {
- this.createEditEndpointComponent.openCreateEditTarget(true);
- }
-
- reload($event: boolean) {
- if ($event) {
- Promise.all([this.repService.getEndpoints()]).then(res => {
- this.targetList = res[0];
- this.setTarget([this.targetList[this.targetList.length - 1]]);
- this.noSelectedEndpoint = false;
- });
- }
- }
-
- onCancel(): void {
- this.router.navigate(['/harbor/replications']);
- }
-
- // UTC time
- public getOfftime(daily_time: any): string {
-
- let timeOffset: number = 0; // seconds
- if (daily_time && typeof daily_time === 'number') {
- timeOffset = +daily_time;
- }
-
- // Convert to current time
- let timezoneOffset: number = this._localTime.getTimezoneOffset();
- // Local time
- timeOffset = timeOffset - timezoneOffset * 60;
- if (timeOffset < 0) {
- timeOffset = timeOffset + ONE_DAY_SECONDS;
- }
-
- if (timeOffset >= ONE_DAY_SECONDS) {
- timeOffset -= ONE_DAY_SECONDS;
- }
-
- // To time string
- let hours: number = Math.floor(timeOffset / ONE_HOUR_SECONDS);
- let minutes: number = Math.floor((timeOffset - hours * ONE_HOUR_SECONDS) / 60);
-
- let timeStr: string = '' + hours;
- if (hours < 10) {
- timeStr = '0' + timeStr;
- }
- if (minutes < 10) {
- timeStr += ':0';
- } else {
- timeStr += ':';
- }
- timeStr += minutes;
-
- return timeStr;
- }
- public setOfftime(v: string) {
- if (!v || v === '') {
- return;
- }
-
- let values: string[] = v.split(':');
- if (!values || values.length !== 2) {
- return;
- }
-
- let hours: number = +values[0];
- let minutes: number = +values[1];
- // Convert to UTC time
- let timezoneOffset: number = this._localTime.getTimezoneOffset();
- let utcTimes: number = hours * ONE_HOUR_SECONDS + minutes * 60;
- utcTimes += timezoneOffset * 60;
- if (utcTimes < 0) {
- utcTimes += ONE_DAY_SECONDS;
- }
-
- if (utcTimes >= ONE_DAY_SECONDS) {
- utcTimes -= ONE_DAY_SECONDS;
- }
-
- return utcTimes;
- }
-
- 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[] } = {};
- let ruleValue: { [key: string]: any | any[] } = this.ruleForm.value;
- if (!ruleValue || !this.copyUpdateForm) {
- return changes;
- }
- for (let prop in ruleValue) {
- let field = this.copyUpdateForm[prop];
- if (!compareValue(field, ruleValue[prop])) {
- changes[prop] = ruleValue[prop];
- //Number
- if (typeof field === "number") {
- changes[prop] = +changes[prop];
- }
-
- //Trim string value
- if (typeof field === "string") {
- changes[prop] = ('' + changes[prop]).trim();
- }
- }
- }
-
- return changes;
- }
-
-}
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
deleted file mode 100644
index 4942247e5..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.css
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Created by pengf on 9/28/2017.
- */
-
-.select{
- width: 186px;
-}
-.select .optionMore{
- background-color: #bfbaba;
- height: 1.6em;
- font-size: 1.2em;
- cursor: pointer;
- text-align: center;
-}
-.hideFilter{ display: none;}
-h4{
- color: #666;
-}
-.colorRed{color: red;}
-.colorRed a{text-decoration: underline;color: #007CBB;}
-
-label:first-child {
- font-size: 15px;
- left: -10px !important;
-}
-.inputWidth{width: 310px;}
-.endpointSelect{ width: 290px; margin-right: 34px;}
-.filterSelect{width: 350px;}
-.filterSelect clr-icon{margin-left: 10px;}
-.filterSelect label{width: 175px;}
-.filterSelect label input{width: 100%;}
-.cursor{cursor: pointer;}
-.pull-left{float: left;}
-.padLeft0{padding-left: 0;}
-.floatSet {display: inline-block; width: 120px;margin-right: 10px;}
-.form-group{ min-height: 36px;}
-
-.projectInput{float: left;}
-.projectInput input{background-color: white;}
-.switchIcon{width:20px;height:20px; margin-top: 10px;margin-left: 10px; cursor: pointer;}
-.addEndpoint{ margin-top: .25em !important;}
-.shadow{position: absolute;top: 8px;}
-.is-solid{cursor: pointer;}
\ 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
deleted file mode 100644
index b8a1b2bd6..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.html
+++ /dev/null
@@ -1,128 +0,0 @@
-
\ No newline at end of file
diff --git a/src/ui_ng/src/app/replication/replication-rule/replication-rule.service.ts b/src/ui_ng/src/app/replication/replication-rule/replication-rule.service.ts
deleted file mode 100644
index fbe5a1c4c..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.service.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Created by pengf on 12/5/2017.
- */
-
-import {Injectable} from "@angular/core";
-import {Http, RequestOptions, Headers, URLSearchParams} from "@angular/http";
-import {Observable} from "rxjs/Observable";
-import {ReplicationRule, Target} from "./replication-rule";
-import {HTTP_GET_OPTIONS, HTTP_JSON_OPTIONS} from "../../shared/shared.utils";
-import {Project} from "../../project/project";
-
-@Injectable()
-export class ReplicationRuleServie {
- headers = new Headers({'Content-type': 'application/json'});
- options = new RequestOptions({'headers': this.headers});
- baseurl = '/api/policies/replication';
- targetUrl= '/api/targets';
-
- constructor(private http: Http) {}
-
- public createReplicationRule(replicationRule: ReplicationRule): Observable | Promise | any {
- /*if (!this._isValidRule(replicationRule)) {
- return Promise.reject('Bad argument');
- }*/
-
- return this.http.post(this.baseurl, JSON.stringify(replicationRule), this.options).toPromise()
- .then(response => response)
- .catch(error => Promise.reject(error));
- }
-
- public getReplicationRules(projectId?: number | string, ruleName?: string): Promise | ReplicationRule[] {
- let queryParams = new URLSearchParams();
- if (projectId) {
- queryParams.set('project_id', '' + projectId);
- }
-
- if (ruleName) {
- queryParams.set('name', ruleName);
- }
-
- return this.http.get(this.baseurl, {search: queryParams}).toPromise()
- .then(response => response.json() as ReplicationRule[])
- .catch(error => Promise.reject(error));
- }
-
- public getReplicationRule(policyId: number): Promise {
- let url: string = `${this.baseurl}/${policyId}`;
- return this.http.get(url, HTTP_GET_OPTIONS).toPromise()
- .then(response => response.json() as ReplicationRule)
- .catch(error => Promise.reject(error));
- }
-
-
- public getEndpoints(): Promise | Target[] {
- return this.http
- .get(this.targetUrl)
- .toPromise()
- .then(response => response.json())
- .catch(error => Promise.reject(error));
- }
-
- public listProjects(): Promise | Project[] {
- return this.http.get(`/api/projects`, HTTP_GET_OPTIONS).toPromise()
- .then(response => response.json())
- .catch(error => Promise.reject(error));
- }
-
- public updateReplicationRule(id: number, rep: {[key: string]: any | any[] }): Observable | Promise | any {
- let url: string = `${this.baseurl}/${id}`;
- return this.http.put(url, JSON.stringify(rep), HTTP_JSON_OPTIONS).toPromise()
- .then(response => response)
- .catch(error => Promise.reject(error));
- }
-
-}
diff --git a/src/ui_ng/src/app/replication/replication-rule/replication-rule.ts b/src/ui_ng/src/app/replication/replication-rule/replication-rule.ts
deleted file mode 100644
index bd320c9fa..000000000
--- a/src/ui_ng/src/app/replication/replication-rule/replication-rule.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import {Project} from "../../project/project";
-/**
- * Created by pengf on 12/7/2017.
- */
-
-export class Target {
- id: number;
- endpoint: string;
- name: string;
- username: string;
- password: string;
- type: number;
- insecure: true;
- creation_time: string;
- update_time: string;
- constructor() {
- this.id = -1;
- this.endpoint = "";
- this.name = "";
- this.username = "";
- this.password = "";
- this.type = 0;
- this.insecure = true;
- this.creation_time = "";
- this.update_time = "";
- }
-}
-
-export class Filter {
- kind: string;
- pattern: string;
- constructor(kind: string, pattern: string) {
- this.kind = kind;
- this.pattern = pattern;
- }
-}
-
-export class Trigger {
- kind: string;
- schedule_param: any | {
- [key: string]: any | any[];
- };
- constructor(kind: string, param: any | { [key: string]: any | any[]; }) {
- this.kind = kind;
- this.schedule_param = param;
- }
-}
-
-export interface ReplicationRule {
- id?: number;
- name: string;
- description: string;
- projects: Project[];
- targets: Target[] ;
- trigger: Trigger ;
- filters: Filter[] ;
- replicate_existing_image_now?: boolean;
- replicate_deletion?: boolean;
-}
-
diff --git a/src/ui_ng/src/app/replication/replication.module.ts b/src/ui_ng/src/app/replication/replication.module.ts
index 06cfca749..b8ca09742 100644
--- a/src/ui_ng/src/app/replication/replication.module.ts
+++ b/src/ui_ng/src/app/replication/replication.module.ts
@@ -20,10 +20,7 @@ import { TotalReplicationPageComponent } from './total-replication/total-replica
import { DestinationPageComponent } from './destination/destination-page.component';
import { SharedModule } from '../shared/shared.module';
-import {ReplicationRuleComponent} from "./replication-rule/replication-rule.component";
import {ReactiveFormsModule} from "@angular/forms";
-import {ReplicationRuleServie} from "./replication-rule/replication-rule.service";
-import {ListProjectModelComponent} from "./replication-rule/list-project-model/list-project-model.component";
@NgModule({
imports: [
@@ -36,15 +33,11 @@ import {ListProjectModelComponent} from "./replication-rule/list-project-model/l
ReplicationManagementComponent,
TotalReplicationPageComponent,
DestinationPageComponent,
- ReplicationRuleComponent,
- ListProjectModelComponent,
],
exports: [
ReplicationPageComponent,
DestinationPageComponent,
TotalReplicationPageComponent,
- ReplicationRuleComponent,
- ],
- providers: [ReplicationRuleServie]
+ ]
})
export class ReplicationModule { }
\ No newline at end of file
diff --git a/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.html b/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.html
index c4ef6f6e5..799257e08 100644
--- a/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.html
+++ b/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.html
@@ -1,4 +1,4 @@
{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}
-
+
\ No newline at end of file
diff --git a/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.ts b/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.ts
index c9e951046..2d38707a7 100644
--- a/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.ts
+++ b/src/ui_ng/src/app/replication/total-replication/total-replication-page.component.ts
@@ -13,9 +13,10 @@
// limitations under the License.
import { Component } from '@angular/core';
-import {Router,ActivatedRoute} from "@angular/router";
-import {ReplicationRule} from "../replication-rule/replication-rule";
+import {Router, ActivatedRoute} from "@angular/router";
+
import {SessionService} from "../../shared/session.service";
+import {ReplicationRule} from "harbor-ui";
@Component({
selector: 'total-replication',
@@ -31,17 +32,12 @@ export class TotalReplicationPageComponent {
this.router.navigate(['../projects', rule.projects[0].project_id, 'replications'], { relativeTo: this.activeRoute });
}
}
+ goRegistry(): void {
+ this.router.navigate(['../registries'], { relativeTo: this.activeRoute });
+ }
public get isSystemAdmin(): boolean {
let account = this.session.getCurrentUser();
return account != null && account.has_admin_role > 0;
}
-
- openEditPage(id: number): void {
- this.router.navigate([id, 'rule'], { relativeTo: this.activeRoute });
- }
-
- openCreatePage(): void {
- this.router.navigate(['new-rule'], { relativeTo: this.activeRoute });
- }
}
diff --git a/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html
index b6a469e64..03f462614 100644
--- a/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html
+++ b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html
@@ -32,7 +32,7 @@
{{'PROFILE.FULL_NAME' | translate}}
-
diff --git a/src/ui_ng/src/app/shared/route/leaving-new-rule-deactivate.service.ts b/src/ui_ng/src/app/shared/route/leaving-new-rule-deactivate.service.ts
deleted file mode 100644
index 60ea8de74..000000000
--- a/src/ui_ng/src/app/shared/route/leaving-new-rule-deactivate.service.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-import { Injectable } from '@angular/core';
-import {
- CanDeactivate, Router,
- ActivatedRouteSnapshot,
- RouterStateSnapshot
-} from '@angular/router';
-
-import { ConfirmationDialogService } from '../confirmation-dialog/confirmation-dialog.service';
-
-import { ConfigurationComponent } from '../../config/config.component';
-import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
-import { ConfirmationState, ConfirmationTargets } from '../shared.const';
-import {ReplicationRuleComponent} from "../../replication/replication-rule/replication-rule.component";
-
-@Injectable()
-export class LeavingNewRuleRouteDeactivate implements CanDeactivate {
- constructor(
- private router: Router,
- private confirmation: ConfirmationDialogService) { }
-
- canDeactivate(
- replicateRule: ReplicationRuleComponent,
- route: ActivatedRouteSnapshot,
- state: RouterStateSnapshot): Promise | boolean {
- //Confirmation before leaving config route
- return new Promise((resolve, reject) => {
- if (replicateRule && replicateRule.hasFormChange()) {
- let msg: ConfirmationMessage = new ConfirmationMessage(
- "CONFIG.LEAVING_CONFIRMATION_TITLE",
- "CONFIG.LEAVING_CONFIRMATION_SUMMARY",
- '',
- {},
- ConfirmationTargets.CONFIG_ROUTE
- );
- this.confirmation.openComfirmDialog(msg);
- return this.confirmation.confirmationConfirm$.subscribe(msg => {
- if (msg && msg.source === ConfirmationTargets.CONFIG_ROUTE) {
- if (msg.state === ConfirmationState.CONFIRMED) {
- return resolve(true);
- } else {
- return resolve(false);//Prevent leading route
- }
- } else {
- return resolve(true);//Should go on
- }
- });
- } else {
- return resolve(true);
- }
- });
- }
-}
diff --git a/src/ui_ng/src/app/shared/shared.module.ts b/src/ui_ng/src/app/shared/shared.module.ts
index 8d0e34a13..7de6142ae 100644
--- a/src/ui_ng/src/app/shared/shared.module.ts
+++ b/src/ui_ng/src/app/shared/shared.module.ts
@@ -58,7 +58,6 @@ import {
ErrorHandler,
HarborLibraryModule
} from 'harbor-ui';
-import {LeavingNewRuleRouteDeactivate} from "./route/leaving-new-rule-deactivate.service";
import { LeavingRepositoryRouteDeactivate } from './route/leaving-repository-deactivate.service';
const uiLibConfig: IServiceConfig = {
@@ -125,7 +124,6 @@ const uiLibConfig: IServiceConfig = {
AuthCheckGuard,
SignInGuard,
LeavingConfigRouteDeactivate,
- LeavingNewRuleRouteDeactivate,
LeavingRepositoryRouteDeactivate,
MemberGuard,
MessageHandlerService,
diff --git a/src/ui_ng/src/app/user/user.component.ts b/src/ui_ng/src/app/user/user.component.ts
index 48ca425d8..f44b888ae 100644
--- a/src/ui_ng/src/app/user/user.component.ts
+++ b/src/ui_ng/src/app/user/user.component.ts
@@ -167,6 +167,7 @@ export class UserComponent implements OnInit, OnDestroy {
//Filter items by keywords
doFilter(terms: string): void {
+ this.selectedRow = [];
this.currentTerm = terms;
this.originalUsers.then(users => {
if (terms.trim() === "") {
diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json
index 0d109d8f5..a8392613d 100644
--- a/src/ui_ng/src/i18n/lang/en-us-lang.json
+++ b/src/ui_ng/src/i18n/lang/en-us-lang.json
@@ -322,8 +322,8 @@
"PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log",
- "NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
- "NO_PROJECT_INFO": "Please go to projects and add a project name first",
+ "NO_ENDPOINT_INFO": "Please add an endpoint first",
+ "NO_PROJECT_INFO": "Please add a project name first",
"SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled",
"MANUAL": "Manual",
diff --git a/src/ui_ng/src/i18n/lang/es-es-lang.json b/src/ui_ng/src/i18n/lang/es-es-lang.json
index 010f956c6..a7d8ea389 100644
--- a/src/ui_ng/src/i18n/lang/es-es-lang.json
+++ b/src/ui_ng/src/i18n/lang/es-es-lang.json
@@ -322,8 +322,8 @@
"PLACEHOLDER": "We couldn't find any replication rules!",
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
"JOB_LOG_VIEWER": "View Replication Job Log",
- "NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
- "NO_PROJECT_INFO": "Please go to projects and add a project name first",
+ "NO_ENDPOINT_INFO": "Please add an endpoint first",
+ "NO_PROJECT_INFO": "Please add a project name first",
"SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled",
"MANUAL": "Manual",
diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json
index 68e8a7b42..a9464381b 100644
--- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json
+++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json
@@ -322,8 +322,8 @@
"PLACEHOLDER": "未发现任何复制规则!",
"JOB_PLACEHOLDER": "未发现任何复制任务!",
"JOB_LOG_VIEWER": "查看复制任务日志",
- "NO_ENDPOINT_INFO": "请先添加目标",
- "NO_PROJECT_INFO": "请先去项目添加一个新的项目名称",
+ "NO_ENDPOINT_INFO": "请先添加一个目标",
+ "NO_PROJECT_INFO": "请先添加一个项目名称",
"SOURCE_IMAGES_FILTER": "源镜像过滤器",
"SCHEDULE": "定时",
"MANUAL": "手动",