mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
Merge pull request #3068 from pengpengshui/master
fix #3046 about delete ID from 'copy Digest ID'
This commit is contained in:
commit
e3174b8e67
@ -9,7 +9,6 @@ export const REPLICATION_STYLE: string = `
|
||||
|
||||
.option-left {
|
||||
padding-left: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
|
@ -23,7 +23,7 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [withClair]="withClair" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true"></hbr-tag>
|
||||
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" (signatureOutput)="saveSignatures($event)" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [withClair]="withClair" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true"></hbr-tag>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="showDBStatusWarning" class="db-status-warning">
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
EventEmitter
|
||||
EventEmitter, OnChanges, SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Comparator } from 'clarity-angular';
|
||||
@ -41,6 +41,7 @@ import {
|
||||
doFiltering,
|
||||
doSorting
|
||||
} from '../utils';
|
||||
import {TagService} from '../service/index';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-repository-stackview',
|
||||
@ -48,7 +49,8 @@ import {
|
||||
styles: [REPOSITORY_STACKVIEW_STYLES],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RepositoryStackviewComponent implements OnInit {
|
||||
export class RepositoryStackviewComponent implements OnChanges, OnInit {
|
||||
signedCon: {[key: string]: any | string[]} = {};
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() projectName: string = "unknown";
|
||||
@ -80,6 +82,8 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
private translateService: TranslateService,
|
||||
private repositoryService: RepositoryService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private translate: TranslateService,
|
||||
private tagService: TagService,
|
||||
private ref: ChangeDetectorRef) { }
|
||||
|
||||
public get registryUrl(): string {
|
||||
@ -122,15 +126,24 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
}
|
||||
this.translateService.get('REPOSITORY.DELETED_REPO_SUCCESS')
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
}).catch(error => this.errorHandler.error(error));
|
||||
}).catch(error => {
|
||||
if (error.status === "412"){
|
||||
this.translateService.get('REPOSITORY.TAGS_SIGNED')
|
||||
.subscribe(res => this.errorHandler.info(res));
|
||||
return;
|
||||
}
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['projectId'] && changes['projectId'].currentValue) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.projectId) {
|
||||
this.errorHandler.error('Project ID cannot be unset.');
|
||||
return;
|
||||
}
|
||||
//Get system info for tag views
|
||||
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
|
||||
.then(systemInfo => this.systemInfo = systemInfo)
|
||||
@ -153,15 +166,64 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
this.clrLoad(st);
|
||||
}
|
||||
|
||||
saveSignatures(event: {[key: string]: string[]}): void {
|
||||
Object.assign(this.signedCon, event);
|
||||
}
|
||||
|
||||
deleteRepo(repoName: string) {
|
||||
let message = new ConfirmationMessage(
|
||||
'REPOSITORY.DELETION_TITLE_REPO',
|
||||
'REPOSITORY.DELETION_SUMMARY_REPO',
|
||||
repoName,
|
||||
repoName,
|
||||
ConfirmationTargets.REPOSITORY,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.confirmationDialog.open(message);
|
||||
if (this.signedCon[repoName]) {
|
||||
this.signedDataSet(repoName);
|
||||
} else {
|
||||
this.getTagInfo(repoName).then(() => {
|
||||
this.signedDataSet(repoName);
|
||||
});
|
||||
}
|
||||
}
|
||||
getTagInfo(repoName: string): Promise<void> {
|
||||
// this.signedNameArr = [];
|
||||
this.signedCon[repoName] = [];
|
||||
return toPromise<Tag[]>(this.tagService
|
||||
.getTags(repoName))
|
||||
.then(items => {
|
||||
items.forEach((t: Tag) => {
|
||||
if (t.signature !== null) {
|
||||
this.signedCon[repoName].push(t.name);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
signedDataSet(repoName: string): void {
|
||||
let signature: string = '';
|
||||
if (this.signedCon[repoName].length === 0) {
|
||||
this.confirmationDialogSet('DELETION_TITLE_REPO', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO', ConfirmationButtons.DELETE_CANCEL);
|
||||
return;
|
||||
}
|
||||
signature = this.signedCon[repoName].join(',');
|
||||
this.confirmationDialogSet('DELETION_TITLE_REPO_SIGNED', signature, repoName, 'REPOSITORY.DELETION_SUMMARY_REPO_SIGNED', ConfirmationButtons.CLOSE);
|
||||
}
|
||||
|
||||
confirmationDialogSet(summaryTitle: string, signature: string, repoName: string, summaryKey: string, button: ConfirmationButtons): void {
|
||||
this.translate.get(summaryKey,
|
||||
{
|
||||
repoName: repoName,
|
||||
signedImages: signature,
|
||||
})
|
||||
.subscribe((res: string) => {
|
||||
summaryKey = res;
|
||||
let message = new ConfirmationMessage(
|
||||
summaryTitle,
|
||||
summaryKey,
|
||||
repoName,
|
||||
repoName,
|
||||
ConfirmationTargets.REPOSITORY,
|
||||
button);
|
||||
this.confirmationDialog.open(message);
|
||||
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
@ -194,6 +256,7 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
this.totalCount = repo.metadata.xTotalCount;
|
||||
this.repositories = repo.data;
|
||||
|
||||
this.signedCon = {};
|
||||
//Do filtering and sorting
|
||||
this.repositories = doFiltering<RepositoryItem>(this.repositories, state);
|
||||
this.repositories = doSorting<RepositoryItem>(this.repositories, state);
|
||||
@ -231,4 +294,5 @@ export class RepositoryStackviewComponent implements OnInit {
|
||||
|
||||
return st;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,4 +112,8 @@ export const TAG_DETAIL_STYLES: string = `
|
||||
.tip-icon-low{
|
||||
color:yellow;
|
||||
}
|
||||
.second-column div, .fourth-column div, .image-detail-value div{
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
`;
|
@ -101,7 +101,7 @@ export class TagDetailComponent implements OnInit {
|
||||
|
||||
public get scanCompletedDatetime(): Date {
|
||||
return this.tagDetails && this.tagDetails.scan_overview ?
|
||||
this.tagDetails.scan_overview.update_time : new Date();
|
||||
this.tagDetails.scan_overview.update_time : null;
|
||||
}
|
||||
|
||||
public get suffixForHigh(): string {
|
||||
|
@ -71,10 +71,11 @@ export class TagComponent implements OnInit {
|
||||
|
||||
@Output() refreshRepo = new EventEmitter<boolean>();
|
||||
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||
@Output() signatureOutput = new EventEmitter<any>();
|
||||
|
||||
|
||||
tags: Tag[];
|
||||
|
||||
|
||||
showTagManifestOpened: boolean;
|
||||
manifestInfoTitle: string;
|
||||
digestId: string;
|
||||
@ -136,6 +137,7 @@ export class TagComponent implements OnInit {
|
||||
|
||||
retrieve() {
|
||||
this.tags = [];
|
||||
let signatures: string[] = [] ;
|
||||
this.loading = true;
|
||||
|
||||
toPromise<Tag[]>(this.tagService
|
||||
@ -151,11 +153,17 @@ export class TagComponent implements OnInit {
|
||||
components: {
|
||||
total: 0,
|
||||
summary: []
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
this.tags = items;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (t.signature !== null) {
|
||||
signatures.push(t.name);
|
||||
}
|
||||
});
|
||||
this.tags = items;
|
||||
let signedName: {[key: string]: string[]} = {};
|
||||
signedName[this.repoName] = signatures;
|
||||
this.signatureOutput.emit(signedName);
|
||||
this.loading = false;
|
||||
if (this.tags && this.tags.length === 0) {
|
||||
this.refreshRepo.emit(true);
|
||||
|
@ -16,4 +16,6 @@
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
}
|
||||
.cursor{
|
||||
cursor: pointer;}
|
@ -1,4 +1,4 @@
|
||||
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects']">< {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
|
||||
<a *ngIf="hasSignedIn" (click)="backToProject()" class="cursor">< {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
|
||||
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'SEARCH.BACK' | translate}}</a>
|
||||
|
||||
<h1 class="sub-header-title">{{currentProject.name}} <span class="role-label" *ngIf="isMember">{{roleName | translate}}</span></h1>
|
||||
|
@ -57,4 +57,11 @@ export class ProjectDetailComponent {
|
||||
return this.sessionService.getCurrentUser() != null;
|
||||
}
|
||||
|
||||
}
|
||||
backToProject(): void {
|
||||
if (window.sessionStorage) {
|
||||
window.sessionStorage.setItem('fromDetails', 'true');
|
||||
}
|
||||
this.router.navigate(['/harbor', 'projects']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
<div class="option-right">
|
||||
<div class="select" style="float: left; left:-6px; top:8px;">
|
||||
<select (change)="doFilterProjects($event)">
|
||||
<select (change)="doFilterProjects($event)" [(ngModel)]="selecteType">
|
||||
<option value="0" [selected]="currentFilteredType === 0">{{projectTypes[0] | translate}}</option>
|
||||
<option value="1">{{projectTypes[1] | translate}}</option>
|
||||
<option value="2">{{projectTypes[2] | translate}}</option>
|
||||
|
@ -63,6 +63,15 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
subscription: Subscription;
|
||||
loading: boolean = true;
|
||||
|
||||
get selecteType (): number {
|
||||
return this.currentFilteredType;
|
||||
}
|
||||
set selecteType(_project: number) {
|
||||
if (window.sessionStorage) {
|
||||
window.sessionStorage['projectTypeValue'] = _project;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private projectService: ProjectService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
@ -98,6 +107,10 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (window.sessionStorage && window.sessionStorage['projectTypeValue'] && window.sessionStorage['fromDetails']) {
|
||||
this.currentFilteredType = +window.sessionStorage['projectTypeValue'];
|
||||
window.sessionStorage.removeItem('fromDetails');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -121,6 +134,10 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
|
||||
retrieve(state?: State): void {
|
||||
this.projectName = "";
|
||||
if (this.currentFilteredType !== 0) {
|
||||
this.getProjects('', this.currentFilteredType - 1);
|
||||
return;
|
||||
}
|
||||
this.getProjects();
|
||||
}
|
||||
|
||||
|
@ -312,7 +312,7 @@
|
||||
"PLACEHOLDER": "We couldn't find any endpoints!"
|
||||
},
|
||||
"REPOSITORY": {
|
||||
"COPY_DIGEST_ID": "Copy Digest ID",
|
||||
"COPY_DIGEST_ID": "Copy Digest",
|
||||
"DELETE": "Delete",
|
||||
"NAME": "Name",
|
||||
"TAGS_COUNT": "Tags",
|
||||
@ -321,6 +321,8 @@
|
||||
"MY_REPOSITORY": "My Repository",
|
||||
"PUBLIC_REPOSITORY": "Public Repository",
|
||||
"DELETION_TITLE_REPO": "Confirm Repository Deletion",
|
||||
"DELETION_TITLE_REPO_SIGNED": "Repository cannot be deleted",
|
||||
"DELETION_SUMMARY_REPO_SIGNED": "Repository '{{repoName}}' cannot be deleted because the following signed images existing.\n{{signedImages}} \nYou should unsign all the signed images before deleting the repository!",
|
||||
"DELETION_SUMMARY_REPO": "Do you want to delete repository {{param}}?",
|
||||
"DELETION_TITLE_TAG": "Confirm Tag Deletion",
|
||||
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
||||
|
@ -322,6 +322,8 @@
|
||||
"MY_REPOSITORY": "Mi Repositorio",
|
||||
"PUBLIC_REPOSITORY": "Repositorio Público",
|
||||
"DELETION_TITLE_REPO": "Confirmar Eliminación de Repositorio",
|
||||
"DELETION_TITLE_REPO_SIGNED": "Repository cannot be deleted",
|
||||
"DELETION_SUMMARY_REPO_SIGNED": "Repository '{{repoName}}' cannot be deleted because the following signed images existing.\n{{signedImages}} \nYou should unsign all the signed images before deleting the repository!",
|
||||
"DELETION_SUMMARY_REPO": "¿Quiere eliminar el repositorio {{param}}?",
|
||||
"DELETION_TITLE_TAG": "Confirmación de Eliminación de Etiqueta",
|
||||
"DELETION_SUMMARY_TAG": "¿Quiere eliminar la etiqueta {{param}}?",
|
||||
|
@ -312,7 +312,7 @@
|
||||
"PLACEHOLDER": "未发现任何复制目标!"
|
||||
},
|
||||
"REPOSITORY": {
|
||||
"COPY_DIGEST_ID": "复制摘要ID",
|
||||
"COPY_DIGEST_ID": "复制摘要",
|
||||
"DELETE": "删除",
|
||||
"NAME": "名称",
|
||||
"TAGS_COUNT": "标签数",
|
||||
@ -321,6 +321,8 @@
|
||||
"MY_REPOSITORY": "我的仓库",
|
||||
"PUBLIC_REPOSITORY": "公共仓库",
|
||||
"DELETION_TITLE_REPO": "删除镜像仓库确认",
|
||||
"DELETION_TITLE_REPO_SIGNED": "仓库不能被删除",
|
||||
"DELETION_SUMMARY_REPO_SIGNED": "镜像仓库 '{{repoName}}' 不能被删除,因为存在以下签名镜像.\n{{signedImages}} \n在删除镜像仓库前需先删除所有的签名镜像",
|
||||
"DELETION_SUMMARY_REPO": "确认删除镜像仓库 {{param}}?",
|
||||
"DELETION_TITLE_TAG": "删除镜像标签确认",
|
||||
"DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?",
|
||||
|
Loading…
Reference in New Issue
Block a user