mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-29 13:57:33 +02:00
Fix router issues for UI (#17235)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
bff4e13087
commit
04fa3853c9
@ -1,14 +1,12 @@
|
|||||||
<h5 class="history-header font-style mt-3" id="history-header">
|
<h5 class="history-header font-style mt-3" id="history-header">
|
||||||
{{ 'CLEARANCES.PURGE_HISTORY' | translate }}
|
{{ 'CLEARANCES.PURGE_HISTORY' | translate }}
|
||||||
</h5>
|
</h5>
|
||||||
<span class="refresh-btn" (click)="refresh()">
|
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
|
||||||
</span>
|
|
||||||
<clr-datagrid
|
<clr-datagrid
|
||||||
[(clrDgSelected)]="selectedRow"
|
[(clrDgSelected)]="selectedRow"
|
||||||
[clrDgLoading]="loading"
|
[clrDgLoading]="loading"
|
||||||
(clrDgRefresh)="getJobs(true, $event)">
|
(clrDgRefresh)="getJobs(true, $event)">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar class="action-bar">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button
|
<button
|
||||||
id="stop-purge"
|
id="stop-purge"
|
||||||
@ -19,6 +17,9 @@
|
|||||||
{{ 'REPLICATION.STOPJOB' | translate }}
|
{{ 'REPLICATION.STOPJOB' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'id'">{{
|
<clr-dg-column [clrDgField]="'id'">{{
|
||||||
'GC.JOB_ID' | translate
|
'GC.JOB_ID' | translate
|
||||||
@ -31,6 +32,9 @@
|
|||||||
'UPDATE_TIME' | translate
|
'UPDATE_TIME' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
<clr-dg-column>{{ 'LOGS' | translate }}</clr-dg-column>
|
<clr-dg-column>{{ 'LOGS' | translate }}</clr-dg-column>
|
||||||
|
<clr-dg-placeholder>{{
|
||||||
|
'CLEARANCES.NO_PURGE_RECORDS' | translate
|
||||||
|
}}</clr-dg-placeholder>
|
||||||
<clr-dg-row *ngFor="let job of jobs" [clrDgItem]="job">
|
<clr-dg-row *ngFor="let job of jobs" [clrDgItem]="job">
|
||||||
<clr-dg-cell>{{ job.id }}</clr-dg-cell>
|
<clr-dg-cell>{{ job.id }}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{
|
<clr-dg-cell>{{
|
||||||
|
@ -4,11 +4,17 @@
|
|||||||
width: 97%;
|
width: 97%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.refresh-btn {
|
.refresh-btn {
|
||||||
|
margin-right: 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #007CBB;
|
color: #007CBB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,11 @@
|
|||||||
<h5 class="history-header mt-3 font-style" id="history-header">
|
<h5 class="history-header mt-3 font-style" id="history-header">
|
||||||
{{ 'GC.JOB_HISTORY' | translate }}
|
{{ 'GC.JOB_HISTORY' | translate }}
|
||||||
</h5>
|
</h5>
|
||||||
<span class="refresh-btn" (click)="refresh()">
|
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
|
||||||
</span>
|
|
||||||
<clr-datagrid
|
<clr-datagrid
|
||||||
[(clrDgSelected)]="selectedRow"
|
[(clrDgSelected)]="selectedRow"
|
||||||
[clrDgLoading]="loading"
|
[clrDgLoading]="loading"
|
||||||
(clrDgRefresh)="getJobs(true, $event)">
|
(clrDgRefresh)="getJobs(true, $event)">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar class="action-bar">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button
|
<button
|
||||||
id="stop-gc"
|
id="stop-gc"
|
||||||
@ -19,6 +16,9 @@
|
|||||||
{{ 'REPLICATION.STOPJOB' | translate }}
|
{{ 'REPLICATION.STOPJOB' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'id'">{{
|
<clr-dg-column [clrDgField]="'id'">{{
|
||||||
'GC.JOB_ID' | translate
|
'GC.JOB_ID' | translate
|
||||||
@ -31,6 +31,9 @@
|
|||||||
'UPDATE_TIME' | translate
|
'UPDATE_TIME' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
<clr-dg-column>{{ 'LOGS' | translate }}</clr-dg-column>
|
<clr-dg-column>{{ 'LOGS' | translate }}</clr-dg-column>
|
||||||
|
<clr-dg-placeholder>{{
|
||||||
|
'CLEARANCES.NO_GC_RECORDS' | translate
|
||||||
|
}}</clr-dg-placeholder>
|
||||||
<clr-dg-row *ngFor="let job of jobs" [clrDgItem]="job">
|
<clr-dg-row *ngFor="let job of jobs" [clrDgItem]="job">
|
||||||
<clr-dg-cell>{{ job.id }}</clr-dg-cell>
|
<clr-dg-cell>{{ job.id }}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{
|
<clr-dg-cell>{{
|
||||||
|
@ -4,11 +4,17 @@
|
|||||||
width: 97%;
|
width: 97%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.refresh-btn {
|
.refresh-btn {
|
||||||
|
margin-right: 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #007CBB;
|
color: #007CBB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,8 +59,10 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
id="changePwd"
|
id="changePwd"
|
||||||
[hidden]="!canCreateUser"
|
[disabled]="
|
||||||
[disabled]="!(selectedRow.length === 1)"
|
!(selectedRow.length === 1) ||
|
||||||
|
!canCreateUser
|
||||||
|
"
|
||||||
(click)="openChangePwdModal()">
|
(click)="openChangePwdModal()">
|
||||||
<clr-icon shape="edit" size="16"></clr-icon
|
<clr-icon shape="edit" size="16"></clr-icon
|
||||||
> {{ 'RESET_PWD.TITLE' | translate }}
|
> {{ 'RESET_PWD.TITLE' | translate }}
|
||||||
|
@ -32,7 +32,11 @@ describe('ArtifactListPageComponent', () => {
|
|||||||
}),
|
}),
|
||||||
params: {
|
params: {
|
||||||
subscribe: () => {
|
subscribe: () => {
|
||||||
return of(null);
|
return {
|
||||||
|
unsubscribe() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -11,30 +11,33 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Project } from '../../../project';
|
import { Project } from '../../../project';
|
||||||
import { ArtifactListPageService } from './artifact-list-page.service';
|
import { ArtifactListPageService } from './artifact-list-page.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'artifact-list-page',
|
selector: 'artifact-list-page',
|
||||||
templateUrl: 'artifact-list-page.component.html',
|
templateUrl: 'artifact-list-page.component.html',
|
||||||
styleUrls: ['./artifact-list-page.component.scss'],
|
styleUrls: ['./artifact-list-page.component.scss'],
|
||||||
})
|
})
|
||||||
export class ArtifactListPageComponent implements OnInit {
|
export class ArtifactListPageComponent implements OnDestroy {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
repoName: string;
|
repoName: string;
|
||||||
referArtifactNameArray: string[] = [];
|
referArtifactNameArray: string[] = [];
|
||||||
depth: string;
|
depth: string;
|
||||||
artifactDigest: string;
|
artifactDigest: string;
|
||||||
|
routeParamsSub: Subscription;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private artifactListPageService: ArtifactListPageService
|
private artifactListPageService: ArtifactListPageService
|
||||||
) {
|
) {
|
||||||
this.route.params.subscribe(params => {
|
if (!this.routeParamsSub) {
|
||||||
this.depth = this.route.snapshot.params['depth'];
|
this.routeParamsSub = this.route.params.subscribe(params => {
|
||||||
|
this.depth = this.route.snapshot.firstChild.params['depth'];
|
||||||
if (this.depth) {
|
if (this.depth) {
|
||||||
const arr: string[] = this.depth.split('-');
|
const arr: string[] = this.depth.split('-');
|
||||||
this.referArtifactNameArray = arr.slice(0, arr.length - 1);
|
this.referArtifactNameArray = arr.slice(0, arr.length - 1);
|
||||||
@ -43,10 +46,18 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
this.referArtifactNameArray = [];
|
this.referArtifactNameArray = [];
|
||||||
this.artifactDigest = null;
|
this.artifactDigest = null;
|
||||||
}
|
}
|
||||||
|
this.init();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.routeParamsSub) {
|
||||||
|
this.routeParamsSub.unsubscribe();
|
||||||
|
this.routeParamsSub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
init() {
|
||||||
this.projectId = this.route.snapshot.parent.params['id'];
|
this.projectId = this.route.snapshot.parent.params['id'];
|
||||||
let resolverData = this.route.snapshot.parent.data;
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
@ -55,7 +66,6 @@ export class ArtifactListPageComponent implements OnInit {
|
|||||||
this.repoName = this.route.snapshot.params['repo'];
|
this.repoName = this.route.snapshot.params['repo'];
|
||||||
this.artifactListPageService.init(+this.projectId);
|
this.artifactListPageService.init(+this.projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
watchGoBackEvt(projectId: string | number): void {
|
watchGoBackEvt(projectId: string | number): void {
|
||||||
this.router.navigate(['harbor', 'projects', projectId, 'repositories']);
|
this.router.navigate(['harbor', 'projects', projectId, 'repositories']);
|
||||||
}
|
}
|
||||||
|
@ -55,152 +55,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
<div class="row tag-row">
|
<div class="row tag-row">
|
||||||
<div>
|
|
||||||
<div class="row flex-items-xs-right rightPos">
|
|
||||||
<div id="filterArea" *ngIf="!depth">
|
|
||||||
<div
|
|
||||||
class="filterLabelPiece"
|
|
||||||
*ngIf="openLabelFilterPiece && filterByType === 'labels'"
|
|
||||||
[style.left.px]="110">
|
|
||||||
<hbr-label-piece
|
|
||||||
*ngIf="showlabel"
|
|
||||||
[hidden]="!filterOneLabel"
|
|
||||||
[label]="filterOneLabel"
|
|
||||||
[labelWidth]="130"></hbr-label-piece>
|
|
||||||
</div>
|
|
||||||
<div class="flex-xs-middle">
|
|
||||||
<div class="execution-select">
|
|
||||||
<div
|
|
||||||
class="select filter-tag"
|
|
||||||
[hidden]="!openSelectFilterPiece">
|
|
||||||
<clr-select-container>
|
|
||||||
<select
|
|
||||||
clrSelect
|
|
||||||
[(ngModel)]="filterByType"
|
|
||||||
(change)="selectFilterType()">
|
|
||||||
<option
|
|
||||||
*ngFor="let filter of mutipleFilter"
|
|
||||||
value="{{ filter.filterBy }}">
|
|
||||||
{{
|
|
||||||
filter.filterByShowText | translate
|
|
||||||
}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
</div>
|
|
||||||
<div class="flex-xs-middle">
|
|
||||||
<hbr-filter
|
|
||||||
[withDivider]="true"
|
|
||||||
[readonly]="isFilterReadonly"
|
|
||||||
filterPlaceholder="{{
|
|
||||||
getFilterPlaceholder() | translate
|
|
||||||
}}"
|
|
||||||
(filterEvt)="doSearchArtifactByFilter($event)"
|
|
||||||
(openFlag)="openFlagEvent($event)"
|
|
||||||
[currentValue]="lastFilteredTagName">
|
|
||||||
</hbr-filter>
|
|
||||||
<div
|
|
||||||
[hidden]="!openSelectFilterPiece"
|
|
||||||
class="label-filter-panel list-filter">
|
|
||||||
<div *ngFor="let filter of mutipleFilter">
|
|
||||||
<ul
|
|
||||||
class="list-unstyled"
|
|
||||||
*ngIf="
|
|
||||||
filterByType === filter.filterBy
|
|
||||||
">
|
|
||||||
<li
|
|
||||||
class="cursor-pointer"
|
|
||||||
(click)="
|
|
||||||
selectFilter(
|
|
||||||
item.showItem,
|
|
||||||
item.filterText
|
|
||||||
)
|
|
||||||
"
|
|
||||||
*ngFor="
|
|
||||||
let item of filter.listItem
|
|
||||||
">
|
|
||||||
{{ item.showItem | translate }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="label-filter-panel"
|
|
||||||
[hidden]="
|
|
||||||
!(
|
|
||||||
openLabelFilterPanel &&
|
|
||||||
filterByType === 'labels'
|
|
||||||
)
|
|
||||||
">
|
|
||||||
<a class="filterClose" (click)="closeFilter()"
|
|
||||||
>×</a
|
|
||||||
>
|
|
||||||
<label class="filterLabelHeader filter-dark">{{
|
|
||||||
'REPOSITORY.FILTER_ARTIFACT_BY_LABEL'
|
|
||||||
| translate
|
|
||||||
}}</label>
|
|
||||||
<div class="form-group mb-05">
|
|
||||||
<input
|
|
||||||
clrInput
|
|
||||||
type="text"
|
|
||||||
placeholder="Filter labels"
|
|
||||||
[(ngModel)]="filterName"
|
|
||||||
(keyup)="handleInputFilter()" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
[hidden]="
|
|
||||||
artifactListPageService
|
|
||||||
?.imageFilterLabels.length
|
|
||||||
"
|
|
||||||
class="no-labels">
|
|
||||||
{{ 'LABEL.NO_LABELS' | translate }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
[hidden]="
|
|
||||||
!artifactListPageService
|
|
||||||
?.imageFilterLabels.length
|
|
||||||
"
|
|
||||||
class="has-label">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="labelBtn"
|
|
||||||
*ngFor="
|
|
||||||
let label of artifactListPageService?.imageFilterLabels
|
|
||||||
"
|
|
||||||
[hidden]="!label.show"
|
|
||||||
(click)="rightFilterLabel(label)">
|
|
||||||
<clr-icon
|
|
||||||
shape="check"
|
|
||||||
class="pull-left"
|
|
||||||
[hidden]="
|
|
||||||
!label.iconsShow
|
|
||||||
"></clr-icon>
|
|
||||||
<div class="labelDiv top-3-px">
|
|
||||||
<hbr-label-piece
|
|
||||||
[label]="label.label"
|
|
||||||
[labelWidth]="
|
|
||||||
160
|
|
||||||
"></hbr-label-piece>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="refresh-btn" (click)="refresh()">
|
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-datagrid
|
<clr-datagrid
|
||||||
[clrDgLoading]="loading"
|
[clrDgLoading]="loading"
|
||||||
(clrDgRefresh)="clrDgRefresh($event)"
|
(clrDgRefresh)="clrDgRefresh($event)"
|
||||||
class="datagrid-top"
|
class="datagrid-top"
|
||||||
[(clrDgSelected)]="selectedRow">
|
[(clrDgSelected)]="selectedRow">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar class="action-bar">
|
||||||
|
<div>
|
||||||
<button
|
<button
|
||||||
id="scan-btn"
|
id="scan-btn"
|
||||||
[clrLoading]="scanBtnState"
|
[clrLoading]="scanBtnState"
|
||||||
@ -215,10 +77,10 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
(click)="scanNow()">
|
(click)="scanNow()">
|
||||||
<clr-icon shape="shield-check" size="16"></clr-icon>
|
<clr-icon shape="shield-check" size="16"></clr-icon
|
||||||
|
>
|
||||||
<span>{{ 'VULNERABILITY.SCAN_NOW' | translate }}</span>
|
<span>{{ 'VULNERABILITY.SCAN_NOW' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="stop-scan"
|
id="stop-scan"
|
||||||
[clrLoading]="stopBtnState"
|
[clrLoading]="stopBtnState"
|
||||||
@ -229,7 +91,6 @@
|
|||||||
<clr-icon shape="stop" size="16"></clr-icon>
|
<clr-icon shape="stop" size="16"></clr-icon>
|
||||||
<span>{{ 'VULNERABILITY.STOP_NOW' | translate }}</span>
|
<span>{{ 'VULNERABILITY.STOP_NOW' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<clr-dropdown class="btn btn-link" *ngIf="!depth">
|
<clr-dropdown class="btn btn-link" *ngIf="!depth">
|
||||||
<span
|
<span
|
||||||
clrDropdownTrigger
|
clrDropdownTrigger
|
||||||
@ -265,13 +126,15 @@
|
|||||||
(click)="addLabels()">
|
(click)="addLabels()">
|
||||||
{{ 'REPOSITORY.ADD_LABELS' | translate }}
|
{{ 'REPOSITORY.ADD_LABELS' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu [hidden]="!selectedRow.length">
|
<clr-dropdown-menu
|
||||||
|
[hidden]="!selectedRow.length">
|
||||||
<div class="filter-grid">
|
<div class="filter-grid">
|
||||||
<label class="dropdown-header">{{
|
<label class="dropdown-header">{{
|
||||||
'REPOSITORY.ADD_LABEL_TO_IMAGE'
|
'REPOSITORY.ADD_LABEL_TO_IMAGE'
|
||||||
| translate
|
| translate
|
||||||
}}</label>
|
}}</label>
|
||||||
<div class="form-group filter-label-input">
|
<div
|
||||||
|
class="form-group filter-label-input">
|
||||||
<input
|
<input
|
||||||
clrInput
|
clrInput
|
||||||
type="text"
|
type="text"
|
||||||
@ -347,6 +210,166 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="row flex-items-xs-right rightPos">
|
||||||
|
<div id="filterArea" *ngIf="!depth">
|
||||||
|
<div
|
||||||
|
class="filterLabelPiece"
|
||||||
|
*ngIf="
|
||||||
|
openLabelFilterPiece &&
|
||||||
|
filterByType === 'labels'
|
||||||
|
"
|
||||||
|
[style.left.px]="110">
|
||||||
|
<hbr-label-piece
|
||||||
|
*ngIf="showlabel"
|
||||||
|
[hidden]="!filterOneLabel"
|
||||||
|
[label]="filterOneLabel"
|
||||||
|
[labelWidth]="130"></hbr-label-piece>
|
||||||
|
</div>
|
||||||
|
<div class="flex-xs-middle">
|
||||||
|
<div class="execution-select">
|
||||||
|
<div
|
||||||
|
class="select filter-tag"
|
||||||
|
[hidden]="!openSelectFilterPiece">
|
||||||
|
<clr-select-container>
|
||||||
|
<select
|
||||||
|
clrSelect
|
||||||
|
[(ngModel)]="filterByType"
|
||||||
|
(change)="selectFilterType()">
|
||||||
|
<option
|
||||||
|
*ngFor="
|
||||||
|
let filter of mutipleFilter
|
||||||
|
"
|
||||||
|
value="{{ filter.filterBy }}">
|
||||||
|
{{
|
||||||
|
filter.filterByShowText
|
||||||
|
| translate
|
||||||
|
}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</clr-select-container>
|
||||||
|
</div>
|
||||||
|
<div class="flex-xs-middle">
|
||||||
|
<hbr-filter
|
||||||
|
[withDivider]="true"
|
||||||
|
[readonly]="isFilterReadonly"
|
||||||
|
filterPlaceholder="{{
|
||||||
|
getFilterPlaceholder() | translate
|
||||||
|
}}"
|
||||||
|
(filterEvt)="
|
||||||
|
doSearchArtifactByFilter($event)
|
||||||
|
"
|
||||||
|
(openFlag)="openFlagEvent($event)"
|
||||||
|
[currentValue]="lastFilteredTagName">
|
||||||
|
</hbr-filter>
|
||||||
|
<div
|
||||||
|
[hidden]="!openSelectFilterPiece"
|
||||||
|
class="label-filter-panel list-filter">
|
||||||
|
<div
|
||||||
|
*ngFor="
|
||||||
|
let filter of mutipleFilter
|
||||||
|
">
|
||||||
|
<ul
|
||||||
|
class="list-unstyled"
|
||||||
|
*ngIf="
|
||||||
|
filterByType ===
|
||||||
|
filter.filterBy
|
||||||
|
">
|
||||||
|
<li
|
||||||
|
class="cursor-pointer"
|
||||||
|
(click)="
|
||||||
|
selectFilter(
|
||||||
|
item.showItem,
|
||||||
|
item.filterText
|
||||||
|
)
|
||||||
|
"
|
||||||
|
*ngFor="
|
||||||
|
let item of filter.listItem
|
||||||
|
">
|
||||||
|
{{
|
||||||
|
item.showItem
|
||||||
|
| translate
|
||||||
|
}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="label-filter-panel"
|
||||||
|
[hidden]="
|
||||||
|
!(
|
||||||
|
openLabelFilterPanel &&
|
||||||
|
filterByType === 'labels'
|
||||||
|
)
|
||||||
|
">
|
||||||
|
<a
|
||||||
|
class="filterClose"
|
||||||
|
(click)="closeFilter()"
|
||||||
|
>×</a
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="filterLabelHeader filter-dark"
|
||||||
|
>{{
|
||||||
|
'REPOSITORY.FILTER_ARTIFACT_BY_LABEL'
|
||||||
|
| translate
|
||||||
|
}}</label
|
||||||
|
>
|
||||||
|
<div class="form-group mb-05">
|
||||||
|
<input
|
||||||
|
clrInput
|
||||||
|
type="text"
|
||||||
|
placeholder="Filter labels"
|
||||||
|
[(ngModel)]="filterName"
|
||||||
|
(keyup)="handleInputFilter()" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
[hidden]="
|
||||||
|
artifactListPageService
|
||||||
|
?.imageFilterLabels.length
|
||||||
|
"
|
||||||
|
class="no-labels">
|
||||||
|
{{ 'LABEL.NO_LABELS' | translate }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
[hidden]="
|
||||||
|
!artifactListPageService
|
||||||
|
?.imageFilterLabels.length
|
||||||
|
"
|
||||||
|
class="has-label">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="labelBtn"
|
||||||
|
*ngFor="
|
||||||
|
let label of artifactListPageService?.imageFilterLabels
|
||||||
|
"
|
||||||
|
[hidden]="!label.show"
|
||||||
|
(click)="
|
||||||
|
rightFilterLabel(label)
|
||||||
|
">
|
||||||
|
<clr-icon
|
||||||
|
shape="check"
|
||||||
|
class="pull-left"
|
||||||
|
[hidden]="
|
||||||
|
!label.iconsShow
|
||||||
|
"></clr-icon>
|
||||||
|
<div class="labelDiv top-3-px">
|
||||||
|
<hbr-label-piece
|
||||||
|
[label]="label.label"
|
||||||
|
[labelWidth]="
|
||||||
|
160
|
||||||
|
"></hbr-label-piece>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="refresh-btn" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
|
|
||||||
<clr-dg-column class="flex-max-width" [clrDgSortBy]="'digest'"
|
<clr-dg-column class="flex-max-width" [clrDgSortBy]="'digest'"
|
||||||
|
@ -95,7 +95,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
right: 35px;
|
right: 35px;
|
||||||
margin-top: 34px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,3 +484,8 @@ clr-datagrid {
|
|||||||
.width-p-75 {
|
.width-p-75 {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
}
|
}
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
@ -20,10 +20,6 @@ import {
|
|||||||
USERSTATICPERMISSION,
|
USERSTATICPERMISSION,
|
||||||
} from '../../../../../../../shared/services';
|
} from '../../../../../../../shared/services';
|
||||||
import { ArtifactFront as Artifact } from '../../../artifact';
|
import { ArtifactFront as Artifact } from '../../../artifact';
|
||||||
import { LabelPieceComponent } from '../../../../../../../shared/components/label/label-piece/label-piece.component';
|
|
||||||
import { ConfirmationDialogComponent } from '../../../../../../../shared/components/confirmation-dialog';
|
|
||||||
import { ImageNameInputComponent } from '../../../../../../../shared/components/image-name-input/image-name-input.component';
|
|
||||||
import { CopyInputComponent } from '../../../../../../../shared/components/push-image/copy-input.component';
|
|
||||||
import { ErrorHandler } from '../../../../../../../shared/units/error-handler';
|
import { ErrorHandler } from '../../../../../../../shared/units/error-handler';
|
||||||
import { OperationService } from '../../../../../../../shared/components/operation/operation.service';
|
import { OperationService } from '../../../../../../../shared/components/operation/operation.service';
|
||||||
import { ArtifactService as NewArtifactService } from '../../../../../../../../../ng-swagger-gen/services/artifact.service';
|
import { ArtifactService as NewArtifactService } from '../../../../../../../../../ng-swagger-gen/services/artifact.service';
|
||||||
@ -43,19 +39,16 @@ describe('ArtifactListTabComponent (inline template)', () => {
|
|||||||
let spyLabels: jasmine.Spy;
|
let spyLabels: jasmine.Spy;
|
||||||
let spyLabels1: jasmine.Spy;
|
let spyLabels1: jasmine.Spy;
|
||||||
let spyScanner: jasmine.Spy;
|
let spyScanner: jasmine.Spy;
|
||||||
let scannerMock = {
|
const scannerMock = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
name: 'Trivy',
|
name: 'Trivy',
|
||||||
};
|
};
|
||||||
let mockActivatedRoute = {
|
const mockActivatedRoute = {
|
||||||
snapshot: {
|
snapshot: {
|
||||||
params: {
|
params: {
|
||||||
id: 1,
|
id: 1,
|
||||||
repo: 'test',
|
repo: 'test',
|
||||||
digest: 'ABC',
|
digest: 'ABC',
|
||||||
subscribe: () => {
|
|
||||||
return of(null);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
projectResolver: {
|
projectResolver: {
|
||||||
@ -70,13 +63,8 @@ describe('ArtifactListTabComponent (inline template)', () => {
|
|||||||
name: 'library',
|
name: 'library',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
params: {
|
|
||||||
subscribe: () => {
|
|
||||||
return of(null);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let mockArtifacts: Artifact[] = [
|
const mockArtifacts: Artifact[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
@ -266,6 +254,11 @@ describe('ArtifactListTabComponent (inline template)', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
const mockRouter = {
|
const mockRouter = {
|
||||||
|
events: {
|
||||||
|
subscribe: () => {
|
||||||
|
return of(null);
|
||||||
|
},
|
||||||
|
},
|
||||||
navigate: () => {},
|
navigate: () => {},
|
||||||
};
|
};
|
||||||
const mockOperationService = {
|
const mockOperationService = {
|
||||||
@ -351,13 +344,7 @@ describe('ArtifactListTabComponent (inline template)', () => {
|
|||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [SharedTestingModule],
|
imports: [SharedTestingModule],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [
|
declarations: [ArtifactListTabComponent],
|
||||||
ArtifactListTabComponent,
|
|
||||||
LabelPieceComponent,
|
|
||||||
ConfirmationDialogComponent,
|
|
||||||
ImageNameInputComponent,
|
|
||||||
CopyInputComponent,
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: ArtifactListPageService,
|
provide: ArtifactListPageService,
|
||||||
|
@ -197,11 +197,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
onSendingStopScanCommand: boolean = false;
|
onSendingStopScanCommand: boolean = false;
|
||||||
onStopScanArtifactsLength: number = 0;
|
onStopScanArtifactsLength: number = 0;
|
||||||
scanStoppedArtifactLength: number = 0;
|
scanStoppedArtifactLength: number = 0;
|
||||||
|
|
||||||
artifactDigest: string;
|
artifactDigest: string;
|
||||||
depth: string;
|
depth: string;
|
||||||
hasInit: boolean = false;
|
|
||||||
triggerSub: Subscription;
|
|
||||||
labelNameFilterSub: Subscription;
|
labelNameFilterSub: Subscription;
|
||||||
stickLabelNameFilterSub: Subscription;
|
stickLabelNameFilterSub: Subscription;
|
||||||
mutipleFilter = clone(mutipleFilter);
|
mutipleFilter = clone(mutipleFilter);
|
||||||
@ -209,7 +206,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
openSelectFilterPiece = false;
|
openSelectFilterPiece = false;
|
||||||
// could Pagination filter
|
// could Pagination filter
|
||||||
filters: string[];
|
filters: string[];
|
||||||
|
|
||||||
scanFinishedArtifactLength: number = 0;
|
scanFinishedArtifactLength: number = 0;
|
||||||
onScanArtifactsLength: number = 0;
|
onScanArtifactsLength: number = 0;
|
||||||
stopBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
stopBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
@ -226,38 +222,33 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
public artifactListPageService: ArtifactListPageService
|
public artifactListPageService: ArtifactListPageService
|
||||||
) {}
|
) {}
|
||||||
|
initRouterData() {
|
||||||
ngOnInit() {
|
|
||||||
this.artifactListPageService.resetClonedLabels();
|
|
||||||
this.projectId =
|
this.projectId =
|
||||||
this.activatedRoute.snapshot?.parent?.parent?.params['id'];
|
this.activatedRoute.snapshot?.parent?.parent?.params['id'];
|
||||||
let resolverData = this.activatedRoute.snapshot?.parent?.parent?.data;
|
if (!this.projectId) {
|
||||||
|
this.errorHandlerService.error('Project ID cannot be unset.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resolverData = this.activatedRoute.snapshot?.parent?.parent?.data;
|
||||||
if (resolverData) {
|
if (resolverData) {
|
||||||
this.projectName = (<Project>resolverData['projectResolver']).name;
|
this.projectName = (<Project>resolverData['projectResolver']).name;
|
||||||
}
|
}
|
||||||
this.repoName = this.activatedRoute.snapshot?.parent?.params['repo'];
|
this.repoName = this.activatedRoute.snapshot?.parent?.params['repo'];
|
||||||
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
if (!this.repoName) {
|
||||||
this.activatedRoute.params?.subscribe(params => {
|
this.errorHandlerService.error('Repo name cannot be unset.');
|
||||||
this.depth =
|
return;
|
||||||
this.activatedRoute.snapshot?.firstChild?.params['depth'];
|
}
|
||||||
|
this.depth = this.activatedRoute.snapshot.params['depth'];
|
||||||
if (this.depth) {
|
if (this.depth) {
|
||||||
const arr: string[] = this.depth.split('-');
|
const arr: string[] = this.depth.split('-');
|
||||||
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
||||||
}
|
}
|
||||||
if (this.hasInit) {
|
this.lastFilteredTagName = '';
|
||||||
this.currentPage = 1;
|
|
||||||
this.totalCount = 0;
|
|
||||||
const st: ClrDatagridStateInterface = {
|
|
||||||
page: {
|
|
||||||
from: 0,
|
|
||||||
to: this.pageSize - 1,
|
|
||||||
size: this.pageSize,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
this.clrLoad(st);
|
|
||||||
}
|
}
|
||||||
this.init();
|
ngOnInit() {
|
||||||
});
|
this.artifactListPageService.resetClonedLabels();
|
||||||
|
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
||||||
|
this.initRouterData();
|
||||||
if (!this.updateArtifactSub) {
|
if (!this.updateArtifactSub) {
|
||||||
this.updateArtifactSub = this.eventService.subscribe(
|
this.updateArtifactSub = this.eventService.subscribe(
|
||||||
HarborEvent.UPDATE_VULNERABILITY_INFO,
|
HarborEvent.UPDATE_VULNERABILITY_INFO,
|
||||||
@ -272,48 +263,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
if (this.triggerSub) {
|
|
||||||
this.triggerSub.unsubscribe();
|
|
||||||
this.triggerSub = null;
|
|
||||||
}
|
|
||||||
if (this.labelNameFilterSub) {
|
|
||||||
this.labelNameFilterSub.unsubscribe();
|
|
||||||
this.labelNameFilterSub = null;
|
|
||||||
}
|
|
||||||
if (this.stickLabelNameFilterSub) {
|
|
||||||
this.stickLabelNameFilterSub.unsubscribe();
|
|
||||||
this.stickLabelNameFilterSub = null;
|
|
||||||
}
|
|
||||||
if (this.updateArtifactSub) {
|
|
||||||
this.updateArtifactSub.unsubscribe();
|
|
||||||
this.updateArtifactSub = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.hasInit = true;
|
|
||||||
this.depth = this.activatedRoute.snapshot.params['depth'];
|
|
||||||
if (this.depth) {
|
|
||||||
const arr: string[] = this.depth.split('-');
|
|
||||||
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
|
||||||
}
|
|
||||||
if (!this.projectId) {
|
|
||||||
this.errorHandlerService.error('Project ID cannot be unset.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const resolverData = this.activatedRoute.snapshot.params.data;
|
|
||||||
if (resolverData) {
|
|
||||||
const pro: Project = <Project>resolverData['projectResolver'];
|
|
||||||
this.projectName = pro.name;
|
|
||||||
}
|
|
||||||
if (!this.repoName) {
|
|
||||||
this.errorHandlerService.error('Repo name cannot be unset.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.lastFilteredTagName = '';
|
|
||||||
if (!this.labelNameFilterSub) {
|
if (!this.labelNameFilterSub) {
|
||||||
this.labelNameFilterSub = this.labelNameFilter
|
this.labelNameFilterSub = this.labelNameFilter
|
||||||
.pipe(debounceTime(500))
|
.pipe(debounceTime(500))
|
||||||
@ -349,7 +298,20 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.labelNameFilterSub) {
|
||||||
|
this.labelNameFilterSub.unsubscribe();
|
||||||
|
this.labelNameFilterSub = null;
|
||||||
|
}
|
||||||
|
if (this.stickLabelNameFilterSub) {
|
||||||
|
this.stickLabelNameFilterSub.unsubscribe();
|
||||||
|
this.stickLabelNameFilterSub = null;
|
||||||
|
}
|
||||||
|
if (this.updateArtifactSub) {
|
||||||
|
this.updateArtifactSub.unsubscribe();
|
||||||
|
this.updateArtifactSub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
public get filterLabelPieceWidth() {
|
public get filterLabelPieceWidth() {
|
||||||
let len = this.lastFilteredTagName.length
|
let len = this.lastFilteredTagName.length
|
||||||
? this.lastFilteredTagName.length * 6 + 60
|
? this.lastFilteredTagName.length * 6 + 60
|
||||||
@ -381,11 +343,11 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo
|
|
||||||
clrDgRefresh(state: ClrDatagridStateInterface) {
|
clrDgRefresh(state: ClrDatagridStateInterface) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
//add setTimeout to avoid ng check error
|
||||||
this.clrLoad(state);
|
this.clrLoad(state);
|
||||||
});
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
clrLoad(state: ClrDatagridStateInterface): void {
|
clrLoad(state: ClrDatagridStateInterface): void {
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
<span class="back-icon"><</span>
|
<span class="back-icon"><</span>
|
||||||
<a (click)="goBack()">{{ repositoryName }}</a>
|
<a (click)="goBack()">{{ repositoryName }}</a>
|
||||||
|
|
||||||
<span *ngFor="let digest of referArtifactNameArray; let i = index">
|
<span
|
||||||
|
class="back-icon"
|
||||||
|
*ngFor="let digest of referArtifactNameArray; let i = index">
|
||||||
<<a (click)="jumpDigest(i)">{{ digest | slice: 0:15 }}</a></span
|
<<a (click)="jumpDigest(i)">{{ digest | slice: 0:15 }}</a></span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.download-link {
|
.download-link {
|
||||||
|
margin-bottom: 8px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,22 @@ export enum RouteConfigId {
|
|||||||
P2P_POLICIES_PAGE = 'PolicyComponent',
|
P2P_POLICIES_PAGE = 'PolicyComponent',
|
||||||
P2P_TASKS_PAGE = 'P2pTaskListComponent',
|
P2P_TASKS_PAGE = 'P2pTaskListComponent',
|
||||||
}
|
}
|
||||||
|
// should not reuse the routes that meet these RegExps
|
||||||
|
const ShouldNotReuseRouteRegExps: RegExp[] = [
|
||||||
|
/\/harbor\/projects\/(\d+)\/repositories$/,
|
||||||
|
/\/harbor\/projects\/(\d+)\/repositories\/(\S+)\/artifacts-tab$/,
|
||||||
|
/\/harbor\/projects\/(\d+)\/helm-charts\/(\S+)\/versions\/(\S+)/,
|
||||||
|
];
|
||||||
|
|
||||||
|
function testRoute(url: string) {
|
||||||
|
let flag: boolean = false;
|
||||||
|
ShouldNotReuseRouteRegExps.forEach(item => {
|
||||||
|
if (item.test(url)) {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
export class HarborRouteReuseStrategy implements RouteReuseStrategy {
|
export class HarborRouteReuseStrategy implements RouteReuseStrategy {
|
||||||
/**
|
/**
|
||||||
@ -73,6 +89,12 @@ export class HarborRouteReuseStrategy implements RouteReuseStrategy {
|
|||||||
curr: ActivatedRouteSnapshot
|
curr: ActivatedRouteSnapshot
|
||||||
): boolean {
|
): boolean {
|
||||||
this.shouldKeepCache(future, curr);
|
this.shouldKeepCache(future, curr);
|
||||||
|
if (
|
||||||
|
testRoute(curr['_routerState']?.url) &&
|
||||||
|
testRoute(future['_routerState']?.url)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return future.routeConfig === curr.routeConfig;
|
return future.routeConfig === curr.routeConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1752,7 +1752,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1752,7 +1752,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1751,7 +1751,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1721,7 +1721,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1748,7 +1748,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1752,7 +1752,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -1750,7 +1750,9 @@
|
|||||||
"SKIP_DATABASE": "跳过日志数据库",
|
"SKIP_DATABASE": "跳过日志数据库",
|
||||||
"SKIP_DATABASE_TOOLTIP": "开启此项将不会在数据库中记录日志,需先配置日志转发端点",
|
"SKIP_DATABASE_TOOLTIP": "开启此项将不会在数据库中记录日志,需先配置日志转发端点",
|
||||||
"STOP_GC_SUCCESS": "成功触发停止垃圾回收的操作",
|
"STOP_GC_SUCCESS": "成功触发停止垃圾回收的操作",
|
||||||
"STOP_PURGE_SUCCESS": "成功触发停止清理日志的操作"
|
"STOP_PURGE_SUCCESS": "成功触发停止清理日志的操作",
|
||||||
|
"NO_GC_RECORDS": "未发现任何清理记录!",
|
||||||
|
"NO_PURGE_RECORDS": "未发现任何清理记录!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "导出 CVEs - {{number}} 个项目",
|
"EXPORT_SOME_PROJECTS": "导出 CVEs - {{number}} 个项目",
|
||||||
|
@ -1743,7 +1743,9 @@
|
|||||||
"SKIP_DATABASE": "Skip Audit Log Database",
|
"SKIP_DATABASE": "Skip Audit Log Database",
|
||||||
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
|
"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_GC_SUCCESS": "Trigger stopping GC operation successfully",
|
||||||
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
|
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully",
|
||||||
|
"NO_GC_RECORDS": "We couldn't find any GC histories!",
|
||||||
|
"NO_PURGE_RECORDS": "We couldn't find any purge histories!"
|
||||||
},
|
},
|
||||||
"CVE_EXPORT": {
|
"CVE_EXPORT": {
|
||||||
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
"EXPORT_SOME_PROJECTS": "Export CVEs - {{number}} project(s)",
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
Documentation This resource provides any keywords related to the Harbor private registry appliance
|
Documentation This resource provides any keywords related to the Harbor private registry appliance
|
||||||
|
|
||||||
*** Variables ***
|
*** Variables ***
|
||||||
${artifact_action_xpath} //clr-dg-action-bar/clr-dropdown/span[contains(@class,'dropdown-toggle')]
|
${artifact_action_xpath} //*[@id='artifact-list-action']
|
||||||
${artifact_action_delete_xpath} //clr-dropdown-menu//div[contains(.,'Delete')]
|
${artifact_action_delete_xpath} //clr-dropdown-menu//div[contains(.,'Delete')]
|
||||||
${artifact_action_copy_xpath} //clr-dropdown-menu//div[contains(.,'Copy') and @aria-label='retag']
|
${artifact_action_copy_xpath} //clr-dropdown-menu//div[contains(.,'Copy') and @aria-label='retag']
|
||||||
${artifact_achieve_icon} //artifact-list-tab//clr-datagrid//clr-dg-row[contains(.,'sha256')]//clr-dg-cell[1]//clr-tooltip//a
|
${artifact_achieve_icon} //artifact-list-tab//clr-datagrid//clr-dg-row[contains(.,'sha256')]//clr-dg-cell[1]//clr-tooltip//a
|
||||||
|
Loading…
Reference in New Issue
Block a user