diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.html b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.html new file mode 100644 index 000000000..02c1ab00f --- /dev/null +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.html @@ -0,0 +1,235 @@ + + + + + diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.scss b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.scss new file mode 100644 index 000000000..f3ae9bc56 --- /dev/null +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.scss @@ -0,0 +1,80 @@ +@mixin cus-font { + font-size: .5417rem; + font-weight: 400; +} + +.sub-label { + display: inline-block; + width: 90px; + @include cus-font; +} + +.width-220 { + width: 220px; +} + +.label-text { + text-transform: none; + letter-spacing: normal; + font-size: 13px; + font-weight: 400; + color: #000; + height: 1.2rem; + margin: 0 !important; + line-height: 1rem; + text-align: left; + padding-left: 6px; + outline: none; + border-bottom: 1px solid rgb(154 154 154); +} + +.dropdown-toggle { + height: 24px; + width: 212px; +} + +.right-align { + min-width: 220px; + overflow-y: auto; +} + +.no-labels { + cursor: default; + padding: 0 0.5rem; +} + +.dropdown-item { + min-height: 26px; +} + +.spinner { + margin: auto; +} + +.absolute{ + position: absolute; +} + +.flex { + display: flex; +} + +.clr-control-label { + width: 8rem !important; +} + +.names { + text-overflow: ellipsis; + max-width: 270px; + display: inline-block; + overflow: hidden; +} + +.space-between { + display: flex; + justify-content: space-between; +} + +.input-width { + width: 310px; +} diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.spec.ts b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.spec.ts new file mode 100644 index 000000000..2b64be4db --- /dev/null +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ExportCveComponent } from './export-cve.component'; +import { SharedTestingModule } from '../../../../../shared/shared.module'; + +describe('ExportCveComponent', () => { + let component: ExportCveComponent; + let fixture: ComponentFixture; + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SharedTestingModule], + declarations: [ExportCveComponent], + providers: [], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExportCveComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.ts b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.ts new file mode 100644 index 000000000..7d930cdc4 --- /dev/null +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/export-cve/export-cve.component.ts @@ -0,0 +1,211 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { Label } from 'ng-swagger-gen/models/label'; +import { LabelService } from 'ng-swagger-gen/services/label.service'; +import { forkJoin, Observable } from 'rxjs'; +import { finalize } from 'rxjs/operators'; +import { Project } from 'src/app/base/project/project'; +import { NgForm } from '@angular/forms'; +import { ClrLoadingState } from '@clr/angular'; +import { InlineAlertComponent } from '../../../../../shared/components/inline-alert/inline-alert.component'; +import { ScanDataExportService } from '../../../../../../../ng-swagger-gen/services/scan-data-export.service'; +import { MessageHandlerService } from '../../../../../shared/services/message-handler.service'; +import { + EventService, + HarborEvent, +} from '../../../../../services/event-service/event.service'; + +const PAGE_SIZE: number = 100; +const SUPPORTED_MIME_TYPE: string = + 'application/vnd.security.vulnerability.report; version=1.1'; +@Component({ + selector: 'export-cve', + templateUrl: './export-cve.component.html', + styleUrls: ['./export-cve.component.scss'], +}) +export class ExportCveComponent { + selectedProjects: Project[] = []; + opened: boolean = false; + loading: boolean = false; + repos: string; + tags: string; + CVEIds: string; + selectedLabels: Label[] = []; + loadingAllLabels: boolean = false; + allLabels: Label[] = []; + @ViewChild('names', { static: true }) + namesSpan: ElementRef; + @ViewChild('exportCVEForm', { static: true }) currentForm: NgForm; + saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; + @ViewChild(InlineAlertComponent) + inlineAlertComponent: InlineAlertComponent; + constructor( + private labelService: LabelService, + private scanDataExportService: ScanDataExportService, + private msgHandler: MessageHandlerService, + private event: EventService + ) {} + reset() { + this.inlineAlertComponent?.close(); + this.selectedProjects = []; + this.repos = null; + this.tags = null; + this.selectedLabels = []; + this.CVEIds = null; + this.currentForm?.reset(); + this.allLabels = []; + } + open(projects: Project[]) { + this.reset(); + this.opened = true; + this.selectedProjects = projects; + this.getAllLabels(); + } + + close() { + this.opened = false; + } + + cancel() { + this.close(); + } + + save() { + this.loading = true; + this.saveBtnState = ClrLoadingState.LOADING; + const param: ScanDataExportService.ExportScanDataParams = { + criteria: { + projects: this.selectedProjects.map(item => item.project_id), + labels: this.selectedLabels.map(item => item.id), + repositories: this.handleBrace(this.repos), + tags: this.handleBrace(this.tags), + cveIds: this.handleBrace(this.CVEIds), + }, + XScanDataType: SUPPORTED_MIME_TYPE, + }; + this.scanDataExportService + .exportScanData(param) + .pipe( + finalize(() => { + this.loading = false; + this.saveBtnState = ClrLoadingState.DEFAULT; + }) + ) + .subscribe( + res => { + this.msgHandler.showSuccess( + 'CVE_EXPORT.TRIGGER_EXPORT_SUCCESS' + ); + this.event.publish(HarborEvent.REFRESH_EXPORT_JOBS); + this.close(); + }, + err => { + this.inlineAlertComponent.showInlineError(err); + } + ); + } + inputName() {} + isSelected(l: Label): boolean { + let flag: boolean = false; + this.selectedLabels.forEach(item => { + if (item.name === l.name) { + flag = true; + } + }); + return flag; + } + selectOrUnselect(l: Label) { + if (this.isSelected(l)) { + this.selectedLabels = this.selectedLabels.filter( + item => item.name !== l.name + ); + } else { + this.selectedLabels.push(l); + } + } + + getProjectNames(): string { + if (this.selectedProjects?.length) { + const names: string[] = []; + this.selectedProjects.forEach(item => { + names.push(item.name); + }); + return names.join(', '); + } + return 'CVE_EXPORT.ALL_PROJECTS'; + } + + isOverflow(): boolean { + return !( + this.namesSpan?.nativeElement?.clientWidth >= + this.namesSpan?.nativeElement?.scrollWidth + ); + } + getAllLabels(): void { + // get all global labels + this.loadingAllLabels = true; + this.labelService + .ListLabelsResponse({ + pageSize: PAGE_SIZE, + page: 1, + scope: 'g', + }) + .pipe(finalize(() => (this.loadingAllLabels = false))) + .subscribe(res => { + if (res.headers) { + const xHeader: string = res.headers.get('X-Total-Count'); + const totalCount = parseInt(xHeader, 0); + let arr = res.body || []; + if (totalCount <= 100) { + // already gotten all global labels + if (arr && arr.length) { + arr.forEach(data => { + this.allLabels.push(data); + }); + } + } else { + // get all the global labels in specified times + const times: number = Math.ceil(totalCount / PAGE_SIZE); + const observableList: Observable[] = []; + for (let i = 2; i <= times; i++) { + observableList.push( + this.labelService.ListLabels({ + page: i, + pageSize: PAGE_SIZE, + scope: 'g', + }) + ); + } + this.loadingAllLabels = true; + forkJoin(observableList) + .pipe( + finalize(() => (this.loadingAllLabels = false)) + ) + .subscribe(response => { + if (response && response.length) { + response.forEach(item => { + arr = arr.concat(item); + }); + arr.forEach(data => { + this.allLabels.push(data); + }); + } + }); + } + } + }); + } + handleBrace(originStr: string): string { + if (originStr) { + if ( + originStr.indexOf(',') !== -1 && + originStr.indexOf('{') === -1 && + originStr.indexOf('}') === -1 + ) { + return `{${originStr}}`; + } else { + return originStr; + } + } + return null; + } +} diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.html b/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.html index fb7b85195..bf99ec72b 100644 --- a/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.html +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.html @@ -12,16 +12,36 @@ 'PROJECT.NEW_PROJECT' | translate }} - + + {{ 'MEMBER.ACTION' | translate + }} + + + + + + {{ 'PROJECT.NAME' | translate @@ -82,3 +102,4 @@ + diff --git a/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.ts b/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.ts index a1c3de5dd..1151e9847 100644 --- a/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.ts +++ b/src/portal/src/app/base/left-side-nav/projects/list-project/list-project.component.ts @@ -12,7 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Subscription, forkJoin, of } from 'rxjs'; -import { Component, Output, OnDestroy, EventEmitter } from '@angular/core'; +import { + Component, + Output, + OnDestroy, + EventEmitter, + ViewChild, +} from '@angular/core'; import { Router } from '@angular/router'; import { ProjectService, State } from '../../../../shared/services'; import { TranslateService } from '@ngx-translate/core'; @@ -47,6 +53,8 @@ import { import { ConfirmationDialogService } from '../../../global-confirmation-dialog/confirmation-dialog.service'; import { errorHandler } from '../../../../shared/units/shared.utils'; import { ConfirmationMessage } from '../../../global-confirmation-dialog/confirmation-message'; +import { ExportCveComponent } from './export-cve/export-cve.component'; + @Component({ selector: 'list-project', templateUrl: 'list-project.component.html', @@ -73,6 +81,8 @@ export class ListProjectComponent implements OnDestroy { 1: 'PROJECT.PROXY_CACHE', }; state: ClrDatagridStateInterface; + @ViewChild(ExportCveComponent) + exportCveComponent: ExportCveComponent; constructor( private session: SessionService, private appConfigService: AppConfigService, @@ -201,8 +211,26 @@ export class ListProjectComponent implements OnDestroy { this.totalCount = parseInt(xHeader, 0); } } - this.projects = response.body as Project[]; + // When the reference of the projects in "this.projects" is modified, should also modify the + // reference of the projects in "this.selectedRow" + this.projects?.forEach(item => { + if (this.selectedRow?.length) { + for ( + let i = this.selectedRow?.length - 1; + i >= 0; + i-- + ) { + if ( + this.selectedRow[i].project_id === + item.project_id + ) { + this.selectedRow.splice(i, 1); + this.selectedRow.push(item); + } + } + } + }); }, error => { this.msgHandler.handleError(error); @@ -297,7 +325,7 @@ export class ListProjectComponent implements OnDestroy { this.currentPage = 1; this.filteredType = 0; this.searchKeyword = ''; - + this.selectedRow = []; this.reload(); this.statisticHandler.refresh(); } @@ -351,4 +379,14 @@ export class ListProjectComponent implements OnDestroy { return st; } + exportCVE() { + this.exportCveComponent.open(this.selectedRow); + } + + getExportButtonText(): string { + if (this.selectedRow?.length) { + return `CVE_EXPORT.EXPORT_SOME_PROJECTS`; + } + return 'CVE_EXPORT.EXPORT_ALL_PROJECTS'; + } } diff --git a/src/portal/src/app/base/left-side-nav/projects/projects.module.ts b/src/portal/src/app/base/left-side-nav/projects/projects.module.ts index 1483f4963..c6639b13e 100644 --- a/src/portal/src/app/base/left-side-nav/projects/projects.module.ts +++ b/src/portal/src/app/base/left-side-nav/projects/projects.module.ts @@ -18,6 +18,7 @@ import { ListProjectComponent } from './list-project/list-project.component'; import { CreateProjectComponent } from './create-project/create-project.component'; import { RouterModule, Routes } from '@angular/router'; import { StatisticsPanelComponent } from './statictics/statistics-panel.component'; +import { ExportCveComponent } from './list-project/export-cve/export-cve.component'; const routes: Routes = [ { @@ -33,6 +34,7 @@ const routes: Routes = [ ListProjectComponent, CreateProjectComponent, StatisticsPanelComponent, + ExportCveComponent, ], providers: [], }) diff --git a/src/portal/src/app/services/event-service/event.service.ts b/src/portal/src/app/services/event-service/event.service.ts index 8b5fb966a..dedca6de2 100644 --- a/src/portal/src/app/services/event-service/event.service.ts +++ b/src/portal/src/app/services/event-service/event.service.ts @@ -78,4 +78,5 @@ export enum HarborEvent { START_SCAN_ARTIFACT = 'startScanArtifact', STOP_SCAN_ARTIFACT = 'stopScanArtifact', UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo', + REFRESH_EXPORT_JOBS = 'refreshExportJobs', } diff --git a/src/portal/src/app/shared/components/operation/operate.ts b/src/portal/src/app/shared/components/operation/operate.ts index ff82454a8..1955be5f7 100644 --- a/src/portal/src/app/shared/components/operation/operate.ts +++ b/src/portal/src/app/shared/components/operation/operate.ts @@ -1,7 +1,7 @@ export class OperateInfo { name: string; state: string; - data: { [key: string]: string | number }; + data: { [key: string]: string | number | boolean }; timeStamp: number; timeDiff: string; constructor() { diff --git a/src/portal/src/app/shared/components/operation/operation.component.css b/src/portal/src/app/shared/components/operation/operation.component.css index 5d7848091..5c4ec3849 100644 --- a/src/portal/src/app/shared/components/operation/operation.component.css +++ b/src/portal/src/app/shared/components/operation/operation.component.css @@ -7,6 +7,7 @@ padding-top: 20px; border-left: 1px solid #e0e0e0; } +/* stylelint-disable */ .eventInfo {display: flex; justify-content: flex-start; align-content: flex-start; padding: 8px 5px 8px 10px; border-bottom: 1px solid #ccc;} .iconsArea{ flex-shrink: 1;} @@ -42,6 +43,7 @@ text-decoration: none; } .freshIcon{float: right; margin-right: 20px; margin-top: -10px;cursor: pointer;} + :host::ng-deep#contentAll{ position: absolute; top: 115px; @@ -49,6 +51,7 @@ width: 100%; overflow-y: auto; } + :host::ng-deep#contentFailed{ position: absolute; top: 115px; @@ -56,6 +59,7 @@ width: 100%; overflow-y: auto; } + :host::ng-deep#contentRun{ position: absolute; top: 115px; @@ -87,6 +91,12 @@ .hidden-info { display: none; } + .margin-left-5 { margin-left: 5px; } + +.flex { + display: flex; + align-items: center; +} diff --git a/src/portal/src/app/shared/components/operation/operation.component.html b/src/portal/src/app/shared/components/operation/operation.component.html index 8479cf430..ae518d923 100644 --- a/src/portal/src/app/shared/components/operation/operation.component.html +++ b/src/portal/src/app/shared/components/operation/operation.component.html @@ -27,6 +27,63 @@ {{ 'OPERATION.ALL' | translate }} +
+
+ + + + +
+
+ + {{ + item.data.name + }}{{ + item.timeDiff | translate + }} + {{ item.data.errorInf }} +
+
@@ -78,6 +135,60 @@ {{ 'OPERATION.RUNNING' | translate }} + +
+
+ + + + +
+
+ + {{ + item.data.name + }}{{ + item.timeDiff | translate + }} + {{ item.data.errorInf }} +
+
+
@@ -124,6 +235,56 @@ {{ 'OPERATION.FAILED' | translate }} +
+
+ + + + +
+
+ + {{ + item.data.name + }}{{ + item.timeDiff | translate + }} + {{ item.data.errorInf }} +
+
diff --git a/src/portal/src/app/shared/components/operation/operation.component.ts b/src/portal/src/app/shared/components/operation/operation.component.ts index d2a94cab3..2b19d6aaf 100644 --- a/src/portal/src/app/shared/components/operation/operation.component.ts +++ b/src/portal/src/app/shared/components/operation/operation.component.ts @@ -1,5 +1,10 @@ import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; -import { OperationService } from './operation.service'; +import { + downloadCVEs, + EventState, + ExportJobStatus, + OperationService, +} from './operation.service'; import { forkJoin, Subscription } from 'rxjs'; import { OperateInfo, @@ -9,11 +14,19 @@ import { import { SlideInOutAnimation } from '../../_animations/slide-in-out.animation'; import { TranslateService } from '@ngx-translate/core'; import { SessionService } from '../../services/session.service'; - +import { ScanDataExportService } from '../../../../../ng-swagger-gen/services/scan-data-export.service'; +import { + EventService, + HarborEvent, +} from '../../../services/event-service/event.service'; +import { MessageHandlerService } from '../../services/message-handler.service'; +import { HarborDatetimePipe } from '../../pipes/harbor-datetime.pipe'; +const STAY_TIME: number = 5000; const OPERATION_KEY: string = 'operation'; const MAX_NUMBER: number = 500; const MAX_SAVING_TIME: number = 1000 * 60 * 60 * 24 * 30; // 30 days - +const TIMEOUT = 7000; +const FILE_NAME_PREFIX: string = 'csv_file_'; @Component({ selector: 'hbr-operation-model', templateUrl: './operation.component.html', @@ -21,8 +34,10 @@ const MAX_SAVING_TIME: number = 1000 * 60 * 60 * 24 * 30; // 30 days animations: [SlideInOutAnimation], }) export class OperationComponent implements OnInit, OnDestroy { + fileNamePrefix: string = FILE_NAME_PREFIX; batchInfoSubscription: Subscription; resultLists: OperateInfo[] = []; + exportJobs: OperateInfo[] = []; animationState = 'out'; private _newMessageCount: number = 0; private _timeoutInterval; @@ -42,12 +57,22 @@ export class OperationComponent implements OnInit, OnDestroy { ); } } - + timeout; constructor( private session: SessionService, private operationService: OperationService, - private translate: TranslateService + private translate: TranslateService, + private scanDataExportService: ScanDataExportService, + private event: EventService, + private msgHandler: MessageHandlerService ) { + this.event.subscribe(HarborEvent.REFRESH_EXPORT_JOBS, () => { + if (this.animationState === 'out') { + this._newMessageCount += 1; + } + this.refreshExportJobs(); + }); + this.batchInfoSubscription = operationService.operationInfo$.subscribe( data => { if (this.animationState === 'out') { @@ -91,7 +116,7 @@ export class OperationComponent implements OnInit, OnDestroy { if (!this._timeoutInterval) { this._timeoutInterval = setTimeout(() => { this.animationState = 'out'; - }, 5000); + }, STAY_TIME); } } @@ -117,6 +142,7 @@ export class OperationComponent implements OnInit, OnDestroy { init() { if (this.session.getCurrentUser()) { + this.refreshExportJobs(); const operationInfosString: string = localStorage.getItem( `${OPERATION_KEY}-${this.session.getCurrentUser().user_id}` ); @@ -163,6 +189,10 @@ export class OperationComponent implements OnInit, OnDestroy { clearInterval(this._timeoutInterval); this._timeoutInterval = null; } + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } } toggleTitle(errorSpan: any) { @@ -207,6 +237,16 @@ export class OperationComponent implements OnInit, OnDestroy { daysAgo ); }); + this.exportJobs.forEach(data => { + const timeDiff: number = new Date().getTime() - +data.timeStamp; + data.timeDiff = this.calculateTime( + timeDiff, + secondsAgo, + minutesAgo, + hoursAgo, + daysAgo + ); + }); } calculateTime( @@ -227,4 +267,92 @@ export class OperationComponent implements OnInit, OnDestroy { return s; } } + refreshExportJobs() { + if (this.session.getCurrentUser()) { + this.scanDataExportService + .getScanDataExportExecutionList({ + userName: this.session?.getCurrentUser()?.username, + }) + .subscribe(res => { + if (res?.items) { + this.exportJobs = []; + let flag: boolean = false; + res.items.forEach(item => { + const info: OperateInfo = { + name: 'CVE_EXPORT.EXPORT_TITLE', + state: this.MapStatus(item.status), + data: { + hasFile: item.file_present, + name: `${FILE_NAME_PREFIX}${new HarborDatetimePipe().transform( + item.start_time, + 'yyyyMMddHHss' + )}`, + id: item.id, + errorInf: + item.status === ExportJobStatus.ERROR + ? item.status_text + : null, + }, + timeStamp: new Date(item.start_time).getTime(), + timeDiff: 'OPERATION.SECOND_AGO', + }; + this.exportJobs.push(info); + if (this.isRunningState(item.status)) { + flag = true; + } + }); + if (flag) { + this.timeout = setTimeout(() => { + this.refreshExportJobs(); + }, TIMEOUT); + } + } + }); + } + } + + isRunningState(state: string): boolean { + if (state) { + return ( + state === ExportJobStatus.RUNNING || + state === ExportJobStatus.PENDING || + state === ExportJobStatus.SCHEDULED + ); + } + return false; + } + MapStatus(originStatus: string): string { + if (originStatus) { + if (this.isRunningState(originStatus)) { + return EventState.PROGRESSING; + } + if (originStatus === ExportJobStatus.STOPPED) { + return EventState.INTERRUPT; + } + if (originStatus === ExportJobStatus.SUCCESS) { + return EventState.SUCCESS; + } + if (originStatus === ExportJobStatus.ERROR) { + return EventState.FAILURE; + } + } + return EventState.FAILURE; + } + download(info: OperateInfo) { + if (info?.data?.id && info?.data?.name) { + this.scanDataExportService + .downloadScanData({ + executionId: +info.data.id, + }) + .subscribe( + res => { + downloadCVEs(res, info.data.name); + this.refreshExportJobs(); + }, + error => { + this.msgHandler.error(error); + } + ); + } + } } diff --git a/src/portal/src/app/shared/components/operation/operation.service.ts b/src/portal/src/app/shared/components/operation/operation.service.ts index 41b997da3..809214990 100644 --- a/src/portal/src/app/shared/components/operation/operation.service.ts +++ b/src/portal/src/app/shared/components/operation/operation.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; +import { Subject } from 'rxjs'; import { OperateInfo } from './operate'; @Injectable({ @@ -15,3 +15,30 @@ export class OperationService { this.operationInfoSource.next(data); } } + +export function downloadCVEs(data, filename) { + let url = window.URL.createObjectURL(data); + let a = document.createElement('a'); + document.body.appendChild(a); + a.setAttribute('style', 'display: none'); + a.href = url; + a.download = filename; + a.click(); + window.URL.revokeObjectURL(url); + a.remove(); +} +export enum EventState { + SUCCESS = 'success', + FAILURE = 'failure', + INTERRUPT = 'interrupt', + PROGRESSING = 'progressing', +} + +export enum ExportJobStatus { + PENDING = 'Pending', + RUNNING = 'Running', + STOPPED = 'Stopped', + ERROR = 'Error', + SUCCESS = 'Success', + SCHEDULED = 'Scheduled', +} diff --git a/src/portal/src/i18n/lang/de-de-lang.json b/src/portal/src/i18n/lang/de-de-lang.json index 993c4a284..0493cf001 100644 --- a/src/portal/src/i18n/lang/de-de-lang.json +++ b/src/portal/src/i18n/lang/de-de-lang.json @@ -1753,5 +1753,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 55852c29a..40b97c3bd 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -1753,5 +1753,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 9bb425e74..94bc4d619 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -1752,5 +1752,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index d7c05355b..882c48f0a 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -1722,5 +1722,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 217e48756..5a7136e74 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -1749,5 +1749,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index e77ac2ad7..b265767d0 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -1753,5 +1753,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } } diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 6d9c0eb75..9f1629b02 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -1751,5 +1751,19 @@ "SKIP_DATABASE_TOOLTIP": "开启此项将不会在数据库中记录日志,需先配置日志转发端点", "STOP_GC_SUCCESS": "成功触发停止垃圾回收的操作", "STOP_PURGE_SUCCESS": "成功触发停止清理日志的操作" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "导出 CVEs - {{number}} 个项目", + "EXPORT_ALL_PROJECTS": "导出 CVEs - 全部项目", + "ALL_PROJECTS": "全部项目", + "EXPORT_TITLE": "导出 CVE", + "EXPORT_SUBTITLE": "设置导出条件", + "EXPORT_CVE_FILTER_HELP_TEXT": "使用逗号分割 cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "导出", + "JOB_NAME": "任务名称", + "JOB_NAME_REQUIRED": "任务名称为必填项", + "JOB_NAME_EXISTING": "任务名称已存在", + "TRIGGER_EXPORT_SUCCESS": "触发导出 CVEs 任务成功!" } } diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json index ac1428d83..4afe6733d 100644 --- a/src/portal/src/i18n/lang/zh-tw-lang.json +++ b/src/portal/src/i18n/lang/zh-tw-lang.json @@ -1744,5 +1744,19 @@ "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully", "STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully" + }, + "CVE_EXPORT": { + "EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)", + "EXPORT_ALL_PROJECTS": "Export CVEs - All projects", + "ALL_PROJECTS": "All projects", + "EXPORT_TITLE": "Export CVE", + "EXPORT_SUBTITLE": "Set exporting conditions", + "EXPORT_CVE_FILTER_HELP_TEXT": "Enter multiple comma separated cveIds", + "CVE_IDS": "CVE IDs", + "EXPORT_BUTTON": "EXPORT", + "JOB_NAME": "Job Name", + "JOB_NAME_REQUIRED": "Job name is required", + "JOB_NAME_EXISTING": "Job name already exists", + "TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!" } }