mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-19 07:07:42 +01:00
Merge pull request #4494 from pengpengshui/tagdetail
Modify tag detail page and fix label bugs
This commit is contained in:
commit
a12c213cd1
@ -81,6 +81,7 @@ export class Configuration {
|
||||
token_expiration: NumberValueItem;
|
||||
cfg_expiration: NumberValueItem;
|
||||
scan_all_policy: ComplexValueItem;
|
||||
read_only: BoolValueItem;
|
||||
|
||||
public constructor() {
|
||||
this.auth_mode = new StringValueItem("db_auth", true);
|
||||
@ -116,5 +117,6 @@ export class Configuration {
|
||||
daily_time: 0
|
||||
}
|
||||
}, true);
|
||||
this.read_only = new BoolValueItem(false, true);
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ export const CREATE_EDIT_LABEL_STYLE: string = `
|
||||
section> label{margin-left: 20px;}
|
||||
|
||||
.dropdown-menu {display:inline-block;width:166px; padding:6px;}
|
||||
.dropdown-item{ display:inline-flex; margin:2px 4px;
|
||||
.dropdown-menu .dropdown-item{ display:inline-flex; margin:2px 4px;
|
||||
display: inline-block;padding: 0px; width:30px;height:24px; text-align: center;line-height: 24px;}
|
||||
.btnColor{
|
||||
margin: 0 !important;
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
Output,
|
||||
EventEmitter,
|
||||
OnDestroy,
|
||||
Input, OnInit, ViewChild
|
||||
Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
|
||||
|
||||
@ -27,16 +27,15 @@ import { CREATE_EDIT_LABEL_TEMPLATE } from './create-edit-label.component.html';
|
||||
|
||||
import {toPromise, clone, compareValue} from '../utils';
|
||||
|
||||
import {Subject} from "rxjs/Subject";
|
||||
|
||||
import {LabelService} from "../service/label.service";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {NgForm} from "@angular/forms";
|
||||
import {Subject} from "rxjs/Subject";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-create-edit-label',
|
||||
template: CREATE_EDIT_LABEL_TEMPLATE,
|
||||
styles: [CREATE_EDIT_LABEL_STYLE]
|
||||
styles: [CREATE_EDIT_LABEL_STYLE],
|
||||
})
|
||||
|
||||
export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
@ -46,12 +45,13 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
labelModel: Label = this.initLabel();
|
||||
labelId = 0;
|
||||
|
||||
nameChecker: Subject<string> = new Subject<string>();
|
||||
checkOnGoing: boolean;
|
||||
isLabelNameExist = false;
|
||||
|
||||
labelColor = ['#00ab9a', '#9da3db', '#be90d6', '#9b0d54', '#f52f22', '#747474', '#0095d3', '#f38b00', ' #62a420', '#89cbdf', '#004a70', '#9460b8'];
|
||||
|
||||
nameChecker = new Subject<string>();
|
||||
|
||||
labelForm: NgForm;
|
||||
@ViewChild('labelForm')
|
||||
currentForm: NgForm;
|
||||
@ -66,18 +66,14 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.nameChecker.debounceTime(500).distinctUntilChanged().subscribe((name: string) => {
|
||||
this.nameChecker.debounceTime(500).subscribe((name: string) => {
|
||||
this.checkOnGoing = true;
|
||||
toPromise<Label[]>(this.labelService.getLabels(this.scope, this.projectId))
|
||||
toPromise<Label[]>(this.labelService.getLabels(this.scope, this.projectId, name))
|
||||
.then(targets => {
|
||||
if (targets && targets.length) {
|
||||
if (targets.find(m => m.name === name)) {
|
||||
this.isLabelNameExist = true;
|
||||
}else {
|
||||
this.isLabelNameExist = false;
|
||||
};
|
||||
}else {
|
||||
this.isLabelNameExist = false;
|
||||
}
|
||||
this.checkOnGoing = false;
|
||||
}).catch(error => {
|
||||
|
@ -46,7 +46,7 @@ export const REPOSITORY_TEMPLATE = `
|
||||
</section>
|
||||
<section id="image" role="tabpanel" aria-labelledby="repo-image" [hidden]='!isCurrentTabContent("image")'>
|
||||
<div id=images-container>
|
||||
<hbr-tag ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)" class="sub-grid-custom" [repoName]="repoName" [registryUrl]="registryUrl" [withNotary]="withNotary" [withClair]="withClair" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [isGuest]="isGuest" [projectId]="projectId"></hbr-tag>
|
||||
<hbr-tag ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)" class="sub-grid-custom" [repoName]="repoName" [registryUrl]="registryUrl" [withNotary]="withNotary" [withClair]="withClair" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [isGuest]="isGuest" [projectId]="projectId"></hbr-tag>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
@ -54,6 +54,7 @@ export class RepositoryComponent implements OnInit {
|
||||
@Input() isGuest: boolean;
|
||||
@Input() withNotary: boolean;
|
||||
@Input() withClair: boolean;
|
||||
@Input() withAdmiral: boolean;
|
||||
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||
@Output() backEvt: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
|
@ -140,6 +140,6 @@ export class RepositoryDefaultService extends RepositoryService {
|
||||
|
||||
return this.http.delete(url, HTTP_JSON_OPTIONS).toPromise()
|
||||
.then(response => response)
|
||||
.catch(error => { Promise.reject(error); });
|
||||
.catch(error => {return Promise.reject(error); });
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
export const TAG_DETAIL_STYLES: string = `
|
||||
.overview-section {
|
||||
background-color: white;
|
||||
padding-bottom: 36px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
}
|
||||
@ -78,27 +77,37 @@ export const TAG_DETAIL_STYLES: string = `
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.vulnerabilities-info .third-column {
|
||||
.third-column {
|
||||
margin-left: 36px;
|
||||
}
|
||||
.vulnerability{
|
||||
margin-left: 50px;
|
||||
margin-top: -12px;
|
||||
margin-bottom: 20px;}
|
||||
|
||||
.vulnerabilities-info .second-column,
|
||||
.vulnerabilities-info .fourth-column {
|
||||
.vulnerabilities-info .second-column {
|
||||
text-align: left;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.fourth-column{
|
||||
float: left;
|
||||
margin-left:20px;}
|
||||
|
||||
.vulnerabilities-info .second-row {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 500;
|
||||
float:left;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.image-detail-label {
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.image-detail-value {
|
||||
|
@ -7,26 +7,22 @@ export const TAG_DETAIL_HTML: string = `
|
||||
</div>
|
||||
<div class="title-block">
|
||||
<div class="tag-name">
|
||||
<h1>{{tagDetails.name}}</h1>
|
||||
</div>
|
||||
<div class="tag-timestamp">
|
||||
{{'TAG.CREATION_TIME_PREFIX' | translate }} {{tagDetails.created | date }} {{'TAG.CREATOR_PREFIX' | translate }} {{author | translate}}
|
||||
<h1>{{repositoryId}}:{{tagDetails.name}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-block">
|
||||
<div class="image-summary">
|
||||
<div class="detail-title">
|
||||
{{'TAG.IMAGE_DETAILS' | translate }}
|
||||
</div>
|
||||
<div class="flex-block">
|
||||
<div class="image-detail-label">
|
||||
<div>{{'TAG.AUTHOR' | translate }}</div>
|
||||
<div>{{'TAG.ARCHITECTURE' | translate }}</div>
|
||||
<div>{{'TAG.OS' | translate }}</div>
|
||||
<div>{{'TAG.DOCKER_VERSION' | translate }}</div>
|
||||
<div>{{'TAG.SCAN_COMPLETION_TIME' | translate }}</div>
|
||||
</div>
|
||||
<div class="image-detail-value">
|
||||
<div>{{author | translate}}</div>
|
||||
<div>{{tagDetails.architecture}}</div>
|
||||
<div>{{tagDetails.os}}</div>
|
||||
<div>{{tagDetails.docker_version}}</div>
|
||||
@ -35,8 +31,8 @@ export const TAG_DETAIL_HTML: string = `
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="detail-title">
|
||||
{{'TAG.IMAGE_VULNERABILITIES' | translate }}
|
||||
<div class="vulnerability">
|
||||
<hbr-vulnerability-bar [repoName]="repositoryId" [tagId]="tagDetails.name" [summary]="tagDetails.scan_overview"></hbr-vulnerability-bar>
|
||||
</div>
|
||||
<div class="flex-block vulnerabilities-info">
|
||||
<div>
|
||||
@ -46,12 +42,6 @@ export const TAG_DETAIL_HTML: string = `
|
||||
<div class="second-row">
|
||||
<clr-icon shape="exclamation-triangle" size="24" class="tip-icon-medium"></clr-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="second-column">
|
||||
<div>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}</div>
|
||||
<div class="second-row">{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}</div>
|
||||
</div>
|
||||
<div class="third-column">
|
||||
<div>
|
||||
<clr-icon shape="play" size="20" class="tip-icon-low rotate-90"></clr-icon>
|
||||
</div>
|
||||
@ -59,11 +49,20 @@ export const TAG_DETAIL_HTML: string = `
|
||||
<clr-icon shape="help" size="18" style="margin-left: 2px;"></clr-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fourth-column">
|
||||
<div>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }}</div>
|
||||
<div class="second-row">{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}</div>
|
||||
<div class="second-column">
|
||||
<div>{{highCount}} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||
<div class="second-row">{{mediumCount}} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||
<div>{{lowCount}} {{'VULNERABILITY.SEVERITY.LOW' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||
<div class="second-row">{{unknownCount}} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}{{'TAG.LEVEL_VULNERABILITIES' | translate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="tagDetails.labels.length && !withAdmiral">
|
||||
<div class="third-column detail-title">{{'TAG.LABELS' | translate }}</div>
|
||||
<div class="fourth-column">
|
||||
<div *ngFor="let label of tagDetails.labels" style="margin-bottom: 2px;"><hbr-label-piece [label]="label"></hbr-label-piece></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -10,6 +10,11 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { TagService, TagDefaultService, ScanningResultService, ScanningResultDefaultService } from '../service/index';
|
||||
import { FilterComponent } from '../filter/index';
|
||||
import { VULNERABILITY_SCAN_STATUS } from '../utils';
|
||||
import {VULNERABILITY_DIRECTIVES} from "../vulnerability-scanning/index";
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
import {JobLogViewerComponent} from "../job-log-viewer/job-log-viewer.component";
|
||||
import {ChannelService} from "../channel/channel.service";
|
||||
import {JobLogService, JobLogDefaultService} from "../service/job-log.service";
|
||||
|
||||
describe('TagDetailComponent (inline template)', () => {
|
||||
|
||||
@ -66,10 +71,16 @@ describe('TagDetailComponent (inline template)', () => {
|
||||
declarations: [
|
||||
TagDetailComponent,
|
||||
ResultGridComponent,
|
||||
VULNERABILITY_DIRECTIVES,
|
||||
LabelPieceComponent,
|
||||
JobLogViewerComponent,
|
||||
FilterComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
ChannelService,
|
||||
JobLogDefaultService,
|
||||
{provide: JobLogService, useClass: JobLogDefaultService},
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
|
||||
@ -119,7 +130,7 @@ describe('TagDetailComponent (inline template)', () => {
|
||||
|
||||
let el: HTMLElement = fixture.nativeElement.querySelector('.tag-name');
|
||||
expect(el).toBeTruthy();
|
||||
expect(el.textContent.trim()).toEqual('nginx');
|
||||
expect(el.textContent.trim()).toEqual('mock_repo:nginx');
|
||||
});
|
||||
}));
|
||||
|
||||
@ -133,7 +144,7 @@ describe('TagDetailComponent (inline template)', () => {
|
||||
expect(el).toBeTruthy();
|
||||
let el2: HTMLElement = el.querySelector('div');
|
||||
expect(el2).toBeTruthy();
|
||||
expect(el2.textContent).toEqual("amd64");
|
||||
expect(el2.textContent).toEqual("steven");
|
||||
});
|
||||
}));
|
||||
|
||||
@ -147,7 +158,7 @@ describe('TagDetailComponent (inline template)', () => {
|
||||
expect(el).toBeTruthy();
|
||||
let el2: HTMLElement = el.querySelector('div');
|
||||
expect(el2).toBeTruthy();
|
||||
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGH");
|
||||
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGHTAG.LEVEL_VULNERABILITIES");
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { TAG_DETAIL_HTML } from './tag-detail.component.html';
|
||||
import { TagService, Tag, VulnerabilitySeverity } from '../service/index';
|
||||
import { toPromise } from '../utils';
|
||||
import { ErrorHandler } from '../error-handler/index';
|
||||
import {Label} from "../service/interface";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-tag-detail',
|
||||
@ -19,9 +20,11 @@ export class TagDetailComponent implements OnInit {
|
||||
_mediumCount: number = 0;
|
||||
_lowCount: number = 0;
|
||||
_unknownCount: number = 0;
|
||||
labels: Label;
|
||||
|
||||
@Input() tagId: string;
|
||||
@Input() repositoryId: string;
|
||||
@Input() withAdmiral: boolean;
|
||||
tagDetails: Tag = {
|
||||
name: "--",
|
||||
size: "--",
|
||||
@ -74,7 +77,7 @@ export class TagDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
onBack(): void {
|
||||
this.backEvt.emit(this.tagId);
|
||||
this.backEvt.emit(this.repositoryId);
|
||||
}
|
||||
|
||||
getPackageText(count: number): string {
|
||||
|
@ -66,4 +66,11 @@ export const TAG_STYLE = `
|
||||
:host >>> .signpost-content-body{padding:0 .4rem;}
|
||||
:host >>> .signpost-content-header{display:none;}
|
||||
.filterLabelPiece{position: absolute; bottom :0px;z-index:1;}
|
||||
.dropdown .dropdown-toggle.btn {
|
||||
padding-right: 1rem;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
border-radius: 0;
|
||||
margin-top: -2px;
|
||||
}
|
||||
`;
|
@ -17,11 +17,12 @@ export const TAG_TEMPLATE = `
|
||||
<div class="row flex-items-xs-right rightPos">
|
||||
<div class='filterLabelPiece' [style.left.px]='filterLabelPieceWidth' ><hbr-label-piece [hidden]='!filterOneLabel' [label]="filterOneLabel"></hbr-label-piece></div>
|
||||
<div class="flex-xs-middle">
|
||||
<clr-dropdown>
|
||||
<hbr-filter *ngIf="withAdmiral" [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filter)="doSearchTagNames($event)" [currentValue]="lastFilteredTagName"></hbr-filter>
|
||||
<clr-dropdown *ngIf="!withAdmiral">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filter)="doSearchTagNames($event)" [currentValue]="lastFilteredTagName" clrDropdownTrigger></hbr-filter>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
<div style='display:grid'>
|
||||
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
|
||||
<label class="dropdown-header">{{'REPOSITORY.FILTER_BY_LABEL' | translate}}</label>
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" #labelNamePiece (keyup)="handleInputFilter(labelNamePiece.value)"></div>
|
||||
<div [hidden]='imageFilterLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageFilterLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
@ -44,7 +45,7 @@ export const TAG_TEMPLATE = `
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1)" (click)="scanNow(selectedRow)"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||
<clr-dropdown>
|
||||
<clr-dropdown *ngIf="!withAdmiral">
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1) || isGuest" (click)="addLabels(selectedRow)" >{{'REPOSITORY.ADD_LABELS' | translate}}</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
<div style='display:grid'>
|
||||
@ -52,10 +53,10 @@ export const TAG_TEMPLATE = `
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" #stickLabelNamePiece (keyup)="handleStickInputFilter(stickLabelNamePiece.value)"></div>
|
||||
<div [hidden]='imageStickLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="label.iconsShow = true; selectLabel(label)">
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="selectLabel(label); label.iconsShow = true">
|
||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||
<div class='labelDiv'><hbr-label-piece [label]="label.label"></hbr-label-piece></div>
|
||||
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); label.iconsShow = false; unSelectLabel(label)"></clr-icon>
|
||||
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); unSelectLabel(label); label.iconsShow = false"></clr-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -140,13 +140,6 @@ describe('TagComponent (inline template)', () => {
|
||||
|
||||
labelService = fixture.debugElement.injector.get(LabelService);
|
||||
|
||||
/*spyLabels = spyOn(labelService, 'getLabels').and.callFake(function (param) {
|
||||
if (param === 'g') {
|
||||
return Promise.resolve(mockLabels);
|
||||
}else {
|
||||
Promise.resolve(mockLabels1)
|
||||
}
|
||||
})*/
|
||||
spyLabels = spyOn(labelService, 'getGLabels').and.returnValues(Promise.resolve(mockLabels));
|
||||
spyLabels1 = spyOn(labelService, 'getPLabels').and.returnValues(Promise.resolve(mockLabels1));
|
||||
|
||||
|
@ -80,7 +80,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
@Input() registryUrl: string;
|
||||
@Input() withNotary: boolean;
|
||||
@Input() withClair: boolean;
|
||||
|
||||
@Input() withAdmiral: boolean;
|
||||
@Output() refreshRepo = new EventEmitter<boolean>();
|
||||
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||
@Output() signatureOutput = new EventEmitter<any>();
|
||||
@ -180,11 +180,11 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
.subscribe((name: string) => {
|
||||
if (name && name.length) {
|
||||
this.filterOnGoing = true;
|
||||
this.imageFilterLabels = [];
|
||||
this.imageStickLabels = [];
|
||||
|
||||
this.imageLabels.forEach(data => {
|
||||
if (data.label.name.indexOf(name) !== -1) {
|
||||
this.imageFilterLabels.push(data);
|
||||
this.imageStickLabels.push(data);
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
@ -302,7 +302,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
this.selectedChange(tag);
|
||||
}
|
||||
selectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
if (labelInfo && labelInfo.iconsShow) {
|
||||
if (labelInfo && !labelInfo.iconsShow) {
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.addLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
@ -314,7 +314,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
unSelectLabel(labelInfo: {[key: string]: any | string[]}): void {
|
||||
if (labelInfo && !labelInfo.iconsShow) {
|
||||
if (labelInfo && labelInfo.iconsShow) {
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
toPromise<any>(this.tagService.deleteLabelToImages(this.repoName, this.selectedRow[0].name, labelId)).then(res => {
|
||||
|
@ -31,7 +31,7 @@
|
||||
"clarity-icons": "^0.10.17",
|
||||
"clarity-ui": "^0.10.27",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.6.53",
|
||||
"harbor-ui": "0.6.58",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
@ -12,7 +12,7 @@
|
||||
<li role="presentation" class="nav-item">
|
||||
<button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<li role="presentation" class="nav-item" *ngIf="!withAdmiral">
|
||||
<button id="config-label" class="btn btn-link nav-link" aria-controls="system_label" [class.active]='isCurrentTabLink("config-label")' type="button" (click)='tabLinkClick("config-label")'>{{'CONFIG.LABEL' | translate }}</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item" *ngIf="withClair">
|
||||
@ -28,9 +28,8 @@
|
||||
<section id="system_settings" role="tabpanel" aria-labelledby="config-system" [hidden]='!isCurrentTabContent("system_settings")'>
|
||||
<system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>
|
||||
</section>
|
||||
<section id="system_label" role="tabpanel" aria-labelledby="config-label" [hidden]='!isCurrentTabContent("system_label")' style="padding-top: 16px;">
|
||||
<section id="system_label" role="tabpanel" aria-labelledby="config-label" *ngIf="!withAdmiral" [hidden]='!isCurrentTabContent("system_label")' style="padding-top: 16px;">
|
||||
<hbr-label [scope]="'g'"></hbr-label>
|
||||
<!--<system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>-->
|
||||
</section>
|
||||
<section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
|
||||
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
|
||||
|
@ -81,6 +81,10 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
||||
return this.appConfigService.getConfig().with_clair;
|
||||
}
|
||||
|
||||
public get withAdmiral(): boolean {
|
||||
return this.appConfigService.getConfig().with_admiral;
|
||||
}
|
||||
|
||||
isCurrentTabLink(tabId: string): boolean {
|
||||
return this.currentTabId === tabId;
|
||||
}
|
||||
|
@ -108,6 +108,14 @@ const harborRoutes: Routes = [
|
||||
projectResolver: ProjectRoutingResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:id/repositories/:repo/tags/:tag',
|
||||
component: TagDetailPageComponent,
|
||||
canActivate: [MemberGuard],
|
||||
resolve: {
|
||||
projectResolver: ProjectRoutingResolver
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'projects/:id',
|
||||
component: ProjectDetailComponent,
|
||||
@ -124,10 +132,6 @@ const harborRoutes: Routes = [
|
||||
path: 'repositories/:repo/tags',
|
||||
component: TagRepositoryComponent,
|
||||
},
|
||||
{
|
||||
path: 'repositories/:repo/tags/:tag',
|
||||
component: TagDetailPageComponent
|
||||
},
|
||||
{
|
||||
path: 'replications',
|
||||
component: ReplicationPageComponent,
|
||||
|
@ -13,7 +13,7 @@
|
||||
<li class="nav-item" *ngIf="isSProjectAdmin || isSystemAdmin">
|
||||
<a class="nav-link" routerLink="replications" routerLinkActive="active">{{'PROJECT_DETAIL.REPLICATION' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSProjectAdmin || isSystemAdmin">
|
||||
<li class="nav-item" *ngIf="(isSProjectAdmin || isSystemAdmin) && !withAdmiral">
|
||||
<a class="nav-link" routerLink="labels" routerLinkActive="active">{{'PROJECT_DETAIL.LABELS' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||
|
@ -20,6 +20,7 @@ import { SessionService } from '../../shared/session.service';
|
||||
import { ProjectService } from '../../project/project.service';
|
||||
|
||||
import { RoleMapping } from '../../shared/shared.const';
|
||||
import {AppConfigService} from "../../app-config.service";
|
||||
|
||||
@Component({
|
||||
selector: 'project-detail',
|
||||
@ -38,6 +39,7 @@ export class ProjectDetailComponent {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private sessionService: SessionService,
|
||||
private appConfigService: AppConfigService,
|
||||
private projectService: ProjectService) {
|
||||
|
||||
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
||||
@ -61,6 +63,10 @@ export class ProjectDetailComponent {
|
||||
return this.sessionService.getCurrentUser() != null;
|
||||
}
|
||||
|
||||
public get withAdmiral(): boolean {
|
||||
return this.appConfigService.getConfig().with_admiral;
|
||||
}
|
||||
|
||||
backToProject(): void {
|
||||
if (window.sessionStorage) {
|
||||
window.sessionStorage.setItem('fromDetails', 'true');
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div style="margin-top: 24px;">
|
||||
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [repositoryId]="repositoryId"></hbr-tag-detail>
|
||||
<div>
|
||||
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [withAdmiral]="withAdmiral" [repositoryId]="repositoryId"></hbr-tag-detail>
|
||||
</div>
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {AppConfigService} from "../../app-config.service";
|
||||
|
||||
@Component({
|
||||
selector: 'repository',
|
||||
@ -25,6 +26,7 @@ export class TagDetailPageComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private appConfigService: AppConfigService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
@ -32,10 +34,14 @@ export class TagDetailPageComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
this.repositoryId = this.route.snapshot.params["repo"];
|
||||
this.tagId = this.route.snapshot.params["tag"];
|
||||
this.projectId = this.route.snapshot.parent.params["id"];
|
||||
this.projectId = this.route.snapshot.params["id"];
|
||||
}
|
||||
|
||||
get withAdmiral(): boolean {
|
||||
return this.appConfigService.getConfig().with_admiral;
|
||||
}
|
||||
|
||||
goBack(tag: string): void {
|
||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories"]);
|
||||
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
<div>
|
||||
<hbr-repository (tagClickEvent)="watchTagClickEvt($event)" (backEvt)="goBack($event)" [repoName]="repoName" [withClair]="withClair" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [isGuest]="isGuest" [projectId]="projectId"></hbr-repository>
|
||||
<hbr-repository (tagClickEvent)="watchTagClickEvt($event)" (backEvt)="goBack($event)" [repoName]="repoName" [withClair]="withClair" [withNotary]="withNotary" [withAdmiral]="withAdmiral" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [isGuest]="isGuest" [projectId]="projectId"></hbr-repository>
|
||||
</div>
|
@ -68,6 +68,10 @@ export class TagRepositoryComponent implements OnInit {
|
||||
return this.appConfigService.getConfig().with_clair;
|
||||
}
|
||||
|
||||
get withAdmiral(): boolean {
|
||||
return this.appConfigService.getConfig().with_admiral;
|
||||
}
|
||||
|
||||
get hasSignedIn(): boolean {
|
||||
return this.session.getCurrentUser() !== null;
|
||||
}
|
||||
|
@ -415,6 +415,7 @@
|
||||
"IMAGE": "Images",
|
||||
"LABELS": ":labels",
|
||||
"ADD_TO_IMAGE": "Add labels to this image",
|
||||
"FILTER_BY_LABEL": "Filter projects by label",
|
||||
"ADD_LABELS": "Add labels"
|
||||
},
|
||||
"ALERT": {
|
||||
@ -438,6 +439,8 @@
|
||||
"REPLICATION": "Replication",
|
||||
"EMAIL": "Email",
|
||||
"LABEL": "Label",
|
||||
"REPOSITORY": "Repository",
|
||||
"REPO_READ_ONLY": "Repository Read Only",
|
||||
"SYSTEM": "System Settings",
|
||||
"VULNERABILITY": "Vulnerability",
|
||||
"CONFIRM_TITLE": "Confirm to cancel",
|
||||
|
@ -413,7 +413,7 @@
|
||||
"INFO": "Información",
|
||||
"NO_INFO": "Sin información de descripción para este repositorio",
|
||||
"IMAGE": "Imágenes",
|
||||
"LABELS": ":labels",
|
||||
"LABELS": "Labels",
|
||||
"ADD_TO_IMAGE": "Add labels to this image",
|
||||
"ADD_LABELS": "Add labels"
|
||||
},
|
||||
@ -438,6 +438,8 @@
|
||||
"REPLICATION": "Replicación",
|
||||
"EMAIL": "Email",
|
||||
"LABEL": "Label",
|
||||
"REPOSITORY": "Repository",
|
||||
"REPO_READ_ONLY": "Repository Read Only",
|
||||
"SYSTEM": "Opciones del Sistema",
|
||||
"VULNERABILITY": "Vulnerability",
|
||||
"CONFIRM_TITLE": "Confirma cancelación",
|
||||
@ -613,9 +615,12 @@
|
||||
"OS": "OS",
|
||||
"SCAN_COMPLETION_TIME": "Scan Completed",
|
||||
"IMAGE_VULNERABILITIES": "Image Vulnerabilities",
|
||||
"LEVEL_VULNERABILITIES": "Level Vulnerabilities",
|
||||
"PLACEHOLDER": "We couldn't find any tags!",
|
||||
"COPY_ERROR": "Copy failed, please try to manually copy.",
|
||||
"FILTER_FOR_TAGS": "Etiquetas de filtro"
|
||||
"FILTER_FOR_TAGS": "Etiquetas de filtro",
|
||||
"AUTHOR": "Author",
|
||||
"LABELS": "LABELS"
|
||||
},
|
||||
"LABEL": {
|
||||
"LABEL": "Label",
|
||||
|
@ -438,6 +438,8 @@
|
||||
"REPLICATION": "复制",
|
||||
"EMAIL": "邮箱",
|
||||
"LABEL": "标签",
|
||||
"REPOSITORY": "仓库",
|
||||
"REPO_READ_ONLY": "仓库只读",
|
||||
"SYSTEM": "系统设置",
|
||||
"VULNERABILITY": "漏洞",
|
||||
"CONFIRM_TITLE": "确认取消",
|
||||
@ -613,9 +615,12 @@
|
||||
"OS": "操作系统",
|
||||
"SCAN_COMPLETION_TIME": "扫描完成时间",
|
||||
"IMAGE_VULNERABILITIES": "镜像缺陷",
|
||||
"LEVEL_VULNERABILITIES": "缺陷等级",
|
||||
"PLACEHOLDER": "未发现任何标签!",
|
||||
"COPY_ERROR": "拷贝失败,请尝试手动拷贝。",
|
||||
"FILTER_FOR_TAGS": "过滤项目"
|
||||
"FILTER_FOR_TAGS": "过滤项目",
|
||||
"AUTHOR": "作者",
|
||||
"LABELS": "标签"
|
||||
},
|
||||
"LABEL": {
|
||||
"LABEL": "标签",
|
||||
|
Loading…
Reference in New Issue
Block a user