Merge pull request #3068 from pengpengshui/master

fix #3046 about delete ID from 'copy Digest ID'
This commit is contained in:
Steven Zou 2017-08-18 18:40:29 +08:00 committed by GitHub
commit e3174b8e67
14 changed files with 138 additions and 31 deletions

View File

@ -9,7 +9,6 @@ export const REPLICATION_STYLE: string = `
.option-left {
padding-left: 16px;
margin-top: 18px;
}
.option-right {
padding-right: 16px;

View File

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

View File

@ -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) {
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(
'REPOSITORY.DELETION_TITLE_REPO',
'REPOSITORY.DELETION_SUMMARY_REPO',
summaryTitle,
summaryKey,
repoName,
repoName,
ConfirmationTargets.REPOSITORY,
ConfirmationButtons.DELETE_CANCEL);
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);
@ -232,3 +295,4 @@ export class RepositoryStackviewComponent implements OnInit {
return st;
}
}

View File

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

View File

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

View File

@ -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
@ -154,8 +156,14 @@ export class TagComponent implements OnInit {
}
};
}
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);

View File

@ -17,3 +17,5 @@
font-style: italic;
letter-spacing: 0.01em;
}
.cursor{
cursor: pointer;}

View File

@ -1,4 +1,4 @@
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects']">&lt; {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
<a *ngIf="hasSignedIn" (click)="backToProject()" class="cursor">&lt; {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">&lt; {{'SEARCH.BACK' | translate}}</a>
<h1 class="sub-header-title">{{currentProject.name}} <span class="role-label" *ngIf="isMember">{{roleName | translate}}</span></h1>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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