mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
* Change vul result bar texts * Support server-driven pagination for replication jobs
This commit is contained in:
parent
b4a81db15c
commit
baa863d026
@ -14,7 +14,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, ReplicationJobItem } from '../service/interface';
|
||||
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
@ -71,7 +71,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||
}
|
||||
];
|
||||
|
||||
let mockJobs: ReplicationJob[] = [
|
||||
let mockJobs: ReplicationJobItem[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"status": "stopped",
|
||||
@ -98,6 +98,11 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||
}
|
||||
];
|
||||
|
||||
let mockJob: ReplicationJob = {
|
||||
metadata: {xTotalCount: 3},
|
||||
data: mockJobs
|
||||
};
|
||||
|
||||
let mockEndpoints: Endpoint[] = [
|
||||
{
|
||||
"id": 1,
|
||||
@ -205,7 +210,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||
|
||||
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
|
||||
spyOneRule = spyOn(replicationService, 'getReplicationRule').and.returnValue(Promise.resolve(mockRule));
|
||||
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs));
|
||||
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
@ -46,7 +46,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)">
|
||||
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
|
||||
@ -54,7 +54,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.UPDATE_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let j of jobs" [clrDgItem]='j'>
|
||||
<clr-dg-row *ngFor="let j of jobs">
|
||||
<clr-dg-cell>{{j.repository}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.operation}}</clr-dg-cell>
|
||||
@ -66,13 +66,12 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<a href="javascript:void(0);" (click)="viewLog(j.id)">
|
||||
<clr-icon shape="clipboard"></clr-icon>
|
||||
</a></ng-template>
|
||||
|
||||
</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}</span>
|
||||
<span *ngIf="showPaginationIndex">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}</span>
|
||||
{{pagination.totalItems}} {{'REPLICATION.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||
import { JobLogService, JobLogDefaultService } from '../service/index';
|
||||
import { JobLogService, JobLogDefaultService, ReplicationJobItem } from '../service/index';
|
||||
|
||||
describe('Replication Component (inline template)', ()=>{
|
||||
|
||||
@ -65,7 +65,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||
}
|
||||
];
|
||||
|
||||
let mockJobs: ReplicationJob[] = [
|
||||
let mockJobs: ReplicationJobItem[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"status": "error",
|
||||
@ -95,6 +95,11 @@ describe('Replication Component (inline template)', ()=>{
|
||||
}
|
||||
];
|
||||
|
||||
let mockJob: ReplicationJob = {
|
||||
metadata: {xTotalCount: 3},
|
||||
data: mockJobs
|
||||
};
|
||||
|
||||
let mockEndpoints: Endpoint[] = [
|
||||
{
|
||||
"id": 1,
|
||||
@ -200,7 +205,7 @@ describe('Replication Component (inline template)', ()=>{
|
||||
replicationService = fixture.debugElement.injector.get(ReplicationService);
|
||||
|
||||
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
|
||||
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs));
|
||||
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(()=>{
|
||||
|
@ -23,9 +23,16 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||
|
||||
import { ReplicationService } from '../service/replication.service';
|
||||
import { RequestQueryParams } from '../service/RequestQueryParams';
|
||||
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface';
|
||||
import { ReplicationRule, ReplicationJob, Endpoint, ReplicationJobItem } from '../service/interface';
|
||||
|
||||
import { toPromise, CustomComparator } from '../utils';
|
||||
import {
|
||||
toPromise,
|
||||
CustomComparator,
|
||||
DEFAULT_PAGE_SIZE,
|
||||
doFiltering,
|
||||
doSorting,
|
||||
calculatePage
|
||||
} from '../utils';
|
||||
|
||||
import { Comparator } from 'clarity-angular';
|
||||
|
||||
@ -33,6 +40,7 @@ import { REPLICATION_TEMPLATE } from './replication.component.html';
|
||||
import { REPLICATION_STYLE } from './replication.component.css';
|
||||
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
import { State } from "clarity-angular";
|
||||
|
||||
const ruleStatus: { [key: string]: any } = [
|
||||
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' },
|
||||
@ -63,7 +71,7 @@ export class SearchOption {
|
||||
endTime: string = '';
|
||||
endTimestamp: string = '';
|
||||
page: number = 1;
|
||||
pageSize: number = 5;
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -93,10 +101,7 @@ export class ReplicationComponent implements OnInit {
|
||||
rules: ReplicationRule[];
|
||||
loading: boolean;
|
||||
|
||||
jobs: ReplicationJob[];
|
||||
|
||||
jobsTotalRecordCount: number;
|
||||
jobsTotalPage: number;
|
||||
jobs: ReplicationJobItem[];
|
||||
|
||||
toggleJobSearchOption = optionalSearch;
|
||||
currentJobSearchOption: number;
|
||||
@ -113,6 +118,13 @@ export class ReplicationComponent implements OnInit {
|
||||
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
||||
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
||||
|
||||
//Server driven pagination
|
||||
currentPage: number = 1;
|
||||
totalCount: number = 0;
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
currentState: State;
|
||||
jobsLoading: boolean = false;
|
||||
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private replicationService: ReplicationService,
|
||||
@ -123,6 +135,10 @@ export class ReplicationComponent implements OnInit {
|
||||
return !this.readonly && this.projectId ? true : false;
|
||||
}
|
||||
|
||||
public get showPaginationIndex(): boolean {
|
||||
return this.totalCount > 0;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.currentRuleStatus = this.ruleStatus[0];
|
||||
this.currentJobStatus = this.jobStatus[0];
|
||||
@ -143,32 +159,78 @@ export class ReplicationComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
fetchReplicationJobs() {
|
||||
//Server driven data loading
|
||||
clrLoadJobs(state: State): void {
|
||||
if (!state || !state.page || !this.search.ruleId) {
|
||||
return;
|
||||
}
|
||||
this.currentState = state;
|
||||
|
||||
let pageNumber: number = calculatePage(state);
|
||||
if (pageNumber <= 0) { pageNumber = 1; }
|
||||
|
||||
let params: RequestQueryParams = new RequestQueryParams();
|
||||
//Pagination
|
||||
params.set("page", '' + pageNumber);
|
||||
params.set("page_size", '' + this.pageSize);
|
||||
//Search by status
|
||||
if (this.search.status.trim()) {
|
||||
params.set('status', this.search.status);
|
||||
}
|
||||
//Search by repository
|
||||
if (this.search.repoName.trim()) {
|
||||
params.set('repository', this.search.repoName);
|
||||
}
|
||||
//Search by timestamps
|
||||
if (this.search.startTimestamp.trim()) {
|
||||
params.set('start_time', this.search.startTimestamp);
|
||||
}
|
||||
if (this.search.endTimestamp.trim()) {
|
||||
params.set('end_time', this.search.endTimestamp);
|
||||
}
|
||||
|
||||
toPromise<ReplicationJob[]>(this.replicationService
|
||||
this.jobsLoading = true;
|
||||
toPromise<ReplicationJob>(this.replicationService
|
||||
.getJobs(this.search.ruleId, params))
|
||||
.then(
|
||||
response => {
|
||||
this.jobs = response;
|
||||
this.totalCount = response.metadata.xTotalCount;
|
||||
this.jobs = response.data;
|
||||
|
||||
//Do filtering and sorting
|
||||
this.jobs = doFiltering<ReplicationJobItem>(this.jobs, state);
|
||||
this.jobs = doSorting<ReplicationJobItem>(this.jobs, state);
|
||||
|
||||
this.jobsLoading = false;
|
||||
|
||||
}).catch(error => {
|
||||
this.jobsLoading = false;
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
loadFirstPage(): void {
|
||||
let st: State = this.currentState;
|
||||
if (!st) {
|
||||
st = {
|
||||
page: {}
|
||||
};
|
||||
}
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
|
||||
this.clrLoadJobs(st);
|
||||
}
|
||||
|
||||
selectOneRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
if (rule && rule.id) {
|
||||
this.search.ruleId = rule.id || '';
|
||||
this.search.repoName = '';
|
||||
this.search.status = '';
|
||||
this.currentJobSearchOption = 0;
|
||||
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
||||
this.fetchReplicationJobs();
|
||||
this.loadFirstPage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +267,7 @@ export class ReplicationComponent implements OnInit {
|
||||
|
||||
doSearchJobs(repoName: string) {
|
||||
this.search.repoName = repoName;
|
||||
this.fetchReplicationJobs();
|
||||
this.loadFirstPage();
|
||||
}
|
||||
|
||||
reloadRules(isReady: boolean) {
|
||||
@ -220,7 +282,21 @@ export class ReplicationComponent implements OnInit {
|
||||
}
|
||||
|
||||
refreshJobs() {
|
||||
this.fetchReplicationJobs();
|
||||
this.search.repoName = "";
|
||||
this.search.startTimestamp = "";
|
||||
this.search.endTimestamp = "";
|
||||
this.search.status = "";
|
||||
|
||||
this.currentPage = 1;
|
||||
|
||||
let st: State = {
|
||||
page: {
|
||||
from: 0,
|
||||
to: this.pageSize - 1,
|
||||
size: this.pageSize
|
||||
}
|
||||
};
|
||||
this.clrLoadJobs(st);
|
||||
}
|
||||
|
||||
toggleSearchJobOptionalName(option: number) {
|
||||
@ -229,12 +305,12 @@ export class ReplicationComponent implements OnInit {
|
||||
|
||||
doJobSearchByStartTime(fromTimestamp: string) {
|
||||
this.search.startTimestamp = fromTimestamp;
|
||||
this.fetchReplicationJobs();
|
||||
this.loadFirstPage();
|
||||
}
|
||||
|
||||
doJobSearchByEndTime(toTimestamp: string) {
|
||||
this.search.endTimestamp = toTimestamp;
|
||||
this.fetchReplicationJobs();
|
||||
this.loadFirstPage();
|
||||
}
|
||||
|
||||
viewLog(jobId: number | string): void {
|
||||
|
@ -100,7 +100,19 @@ export interface ReplicationRule extends Base {
|
||||
* @export
|
||||
* @interface ReplicationJob
|
||||
*/
|
||||
export interface ReplicationJob extends Base {
|
||||
export interface ReplicationJob {
|
||||
metadata?: Metadata;
|
||||
data: ReplicationJobItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for replication job item.
|
||||
*
|
||||
* @export
|
||||
* @interface ReplicationJob
|
||||
*/
|
||||
export interface ReplicationJobItem extends Base {
|
||||
[key: string]: any | any[]
|
||||
status: string;
|
||||
repository: string;
|
||||
policy_id: number;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { RequestQueryParams } from './RequestQueryParams';
|
||||
import { ReplicationJob, ReplicationRule } 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';
|
||||
@ -109,11 +109,11 @@ export abstract class ReplicationService {
|
||||
* @abstract
|
||||
* @param {(number | string)} ruleId
|
||||
* @param {RequestQueryParams} [queryParams]
|
||||
* @returns {(Observable<ReplicationJob> | Promise<ReplicationJob[]> | ReplicationJob)}
|
||||
* @returns {(Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob)}
|
||||
*
|
||||
* @memberOf ReplicationService
|
||||
*/
|
||||
abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[];
|
||||
abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob;
|
||||
|
||||
/**
|
||||
* Get the log of the specified job.
|
||||
@ -238,7 +238,7 @@ export class ReplicationDefaultService extends ReplicationService {
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
public getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[] {
|
||||
public getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob {
|
||||
if (!ruleId || ruleId <= 0) {
|
||||
return Promise.reject('Bad argument');
|
||||
}
|
||||
@ -249,7 +249,29 @@ export class ReplicationDefaultService extends ReplicationService {
|
||||
|
||||
queryParams.set('policy_id', '' + ruleId);
|
||||
return this.http.get(this._jobBaseUrl, buildHttpRequestOptions(queryParams)).toPromise()
|
||||
.then(response => response.json() as ReplicationJob[])
|
||||
.then(response => {
|
||||
let result: ReplicationJob = {
|
||||
metadata: {
|
||||
xTotalCount: 0
|
||||
},
|
||||
data: []
|
||||
};
|
||||
|
||||
if (response && response.headers) {
|
||||
let xHeader: string = response.headers.get("X-Total-Count");
|
||||
if (xHeader) {
|
||||
result.metadata.xTotalCount = parseInt(xHeader, 0);
|
||||
}
|
||||
}
|
||||
result.data = response.json() as ReplicationJobItem[];
|
||||
if (result.metadata.xTotalCount === 0) {
|
||||
if (result.data && result.data.length > 0) {
|
||||
result.metadata.xTotalCount = result.data.length;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,12 @@ export class RepositoryDefaultService extends RepositoryService {
|
||||
|
||||
result.data = response.json() as RepositoryItem[];
|
||||
|
||||
if (result.metadata.xTotalCount === 0) {
|
||||
if (result.data && result.data.length > 0) {
|
||||
result.metadata.xTotalCount = result.data.length;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(error => Promise.reject(error));
|
||||
|
@ -67,20 +67,21 @@ export class ResultTipComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
this.translate.get(this.packageText(this.totalPackages)).subscribe((p1: string) => {
|
||||
this.translate.get(this.packageText(this.packagesWithVul)).subscribe((p2: string) => {
|
||||
this.translate.get(this.unitText(this.packagesWithVul)).subscribe((vul: string) => {
|
||||
this.translate.get('VULNERABILITY.CHART.TOOLTIPS_TITLE',
|
||||
let messageKey: string = "VULNERABILITY.CHART.TOOLTIPS_TITLE";
|
||||
if (this.packagesWithVul > 1) {
|
||||
messageKey = "VULNERABILITY.CHART.TOOLTIPS_TITLE_SINGULAR";
|
||||
}
|
||||
this.translate.get(messageKey,
|
||||
{
|
||||
totalVulnerability: this.packagesWithVul,
|
||||
totalPackages: this.totalPackages,
|
||||
package: p1,
|
||||
packageExt: p2,
|
||||
vulnerability: vul
|
||||
})
|
||||
.subscribe((res: string) => this._tipTitle = res);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tipWidth(severity: VulnerabilitySeverity): string {
|
||||
|
@ -31,7 +31,7 @@
|
||||
"clarity-icons": "^0.9.8",
|
||||
"clarity-ui": "^0.9.8",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.3.66",
|
||||
"harbor-ui": "0.3.91",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
@ -56,8 +56,7 @@
|
||||
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'AUDIT_LOG.OF' | translate}} </span>
|
||||
{{pagination.totalItems }} {{'AUDIT_LOG.ITEMS' | translate}}
|
||||
<span *ngIf="showPaginationIndex">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'AUDIT_LOG.OF' | translate}} </span> {{pagination.totalItems }} {{'AUDIT_LOG.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="15" [(clrDgPage)]="currentPage" [clrDgTotalItems]="totalRecordCount"></clr-dg-pagination>
|
||||
<!--{{totalRecordCount}} {{'AUDIT_LOG.ITEMS' | translate}}
|
||||
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>-->
|
||||
|
@ -68,7 +68,7 @@ export class AuditLogComponent implements OnInit {
|
||||
|
||||
pageOffset: number = 1;
|
||||
pageSize: number = 15;
|
||||
totalRecordCount: number;
|
||||
totalRecordCount: number = 0;
|
||||
currentPage: number;
|
||||
totalPage: number;
|
||||
|
||||
@ -83,6 +83,10 @@ export class AuditLogComponent implements OnInit {
|
||||
return this.toTimeInput.errors && this.toTimeInput.errors.dateValidator && (this.toTimeInput.dirty || this.toTimeInput.touched);
|
||||
}
|
||||
|
||||
get showPaginationIndex(): boolean {
|
||||
return this.totalRecordCount > 0;
|
||||
}
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageHandlerService: MessageHandlerService) {
|
||||
//Get current user from registered resolver.
|
||||
this.route.data.subscribe(data => this.currentUser = <SessionUser>data['auditLogResolver']);
|
||||
|
@ -487,8 +487,9 @@
|
||||
"FOOT_OF": "of"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Scan completed datetime",
|
||||
"TOOLTIPS_TITLE": "This image includes {{totalPackages}} {{package}} with {{vulnerability}} in {{totalVulnerability}} of the {{packageExt}}."
|
||||
"SCANNING_TIME": "Scan completed time:",
|
||||
"TOOLTIPS_TITLE": "{{totalVulnerability}} of {{totalPackages}} {{package}} have known {{vulnerability}}.",
|
||||
"TOOLTIPS_TITLE_SINGULAR": "{{totalVulnerability}} of {{totalPackages}} {{package}} has known {{vulnerability}}."
|
||||
},
|
||||
"SEVERITY": {
|
||||
"HIGH": "high",
|
||||
|
@ -486,8 +486,9 @@
|
||||
"FOOT_OF": "of"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Scan completed datetime",
|
||||
"TOOLTIPS_TITLE": "This image includes {{totalPackages}} {{package}} with {{vulnerability}} in {{totalVulnerability}} of the {{packageExt}}."
|
||||
"SCANNING_TIME": "Scan completed time:",
|
||||
"TOOLTIPS_TITLE": "{{totalVulnerability}} of {{totalPackages}} {{package}} have known {{vulnerability}}.",
|
||||
"TOOLTIPS_TITLE_SINGULAR": "{{totalVulnerability}} of {{totalPackages}} {{package}} has known {{vulnerability}}."
|
||||
},
|
||||
"SEVERITY": {
|
||||
"HIGH": "high",
|
||||
|
@ -487,8 +487,9 @@
|
||||
"FOOT_OF": "总共"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "扫描完成",
|
||||
"TOOLTIPS_TITLE": "在此镜像总共包含{{totalPackages}}{{package}},其中{{totalVulnerability}}个{{packageExt}}含有{{vulnerability}}。"
|
||||
"SCANNING_TIME": "扫描完成时间:",
|
||||
"TOOLTIPS_TITLE": "{{totalPackages}}个{{package}}中的{{totalVulnerability}}个含有{{vulnerability}}.",
|
||||
"TOOLTIPS_TITLE_SINGULAR": "{{totalPackages}}个{{package}}中的{{totalVulnerability}}个含有{{vulnerability}}."
|
||||
},
|
||||
"SEVERITY": {
|
||||
"HIGH": "严重",
|
||||
|
Loading…
Reference in New Issue
Block a user