Merge pull request #4494 from pengpengshui/tagdetail

Modify tag detail page and fix label bugs
This commit is contained in:
Qian Deng 2018-03-28 14:10:01 +08:00 committed by GitHub
commit a12c213cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 137 additions and 79 deletions

View File

@ -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);
}
}

View File

@ -10,8 +10,8 @@ export const CREATE_EDIT_LABEL_STYLE: string = `
section{padding:.5rem 0;}
section> label{margin-left: 20px;}
.dropdown-menu{display:inline-block;width:166px; padding:6px;}
.dropdown-item{ display:inline-flex; margin:2px 4px;
.dropdown-menu {display:inline-block;width:166px; padding:6px;}
.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;

View File

@ -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,16 +66,12 @@ 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;
};
this.isLabelNameExist = true;
}else {
this.isLabelNameExist = false;
}

View File

@ -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>

View File

@ -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>();

View File

@ -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); });
}
}

View File

@ -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 {

View File

@ -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>

View File

@ -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");
});
}));

View File

@ -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 {

View File

@ -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;
}
`;

View File

@ -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>&nbsp;{{'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>&nbsp;{{'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>

View File

@ -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));

View File

@ -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 => {

View File

@ -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",

View File

@ -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,10 +28,9 @@
<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>
<section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
</section>

View File

@ -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;
}

View File

@ -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,

View File

@ -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">

View File

@ -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');

View File

@ -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>

View File

@ -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]);
}
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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",

View File

@ -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",

View File

@ -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": "标签",