mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 09:08:26 +01:00
Refactor repo and tag view with components in harbor-ui lib
This commit is contained in:
parent
8e20e66f8c
commit
44e208f027
@ -30,7 +30,7 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
|
|||||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
import { Tag } from '../service/interface';
|
import { Tag, TagClickEvent } from '../service/interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-repository-stackview',
|
selector: 'hbr-repository-stackview',
|
||||||
@ -44,7 +44,7 @@ export class RepositoryStackviewComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() hasSignedIn: boolean;
|
@Input() hasSignedIn: boolean;
|
||||||
@Input() hasProjectAdminRole: boolean;
|
@Input() hasProjectAdminRole: boolean;
|
||||||
@Output() tagClickEvent = new EventEmitter<Tag>();
|
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||||
|
|
||||||
lastFilteredRepoName: string;
|
lastFilteredRepoName: string;
|
||||||
repositories: Repository[];
|
repositories: Repository[];
|
||||||
@ -132,7 +132,7 @@ export class RepositoryStackviewComponent implements OnInit {
|
|||||||
this.retrieve();
|
this.retrieve();
|
||||||
}
|
}
|
||||||
|
|
||||||
watchTagClickEvt(tag: Tag): void {
|
watchTagClickEvt(tagClickEvt: TagClickEvent): void {
|
||||||
this.tagClickEvent.emit(tag);
|
this.tagClickEvent.emit(tagClickEvt);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -183,3 +183,9 @@ export interface VulnerabilitySummary {
|
|||||||
package_with_unknown?: number;
|
package_with_unknown?: number;
|
||||||
complete_timestamp: Date;
|
complete_timestamp: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TagClickEvent {
|
||||||
|
project_id: string | number;
|
||||||
|
repository_name: string;
|
||||||
|
tag_name: string;
|
||||||
|
}
|
@ -22,7 +22,7 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
|
|||||||
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
|
|
||||||
import { Tag } from '../service/interface';
|
import { Tag, TagClickEvent } from '../service/interface';
|
||||||
|
|
||||||
import { TAG_TEMPLATE } from './tag.component.html';
|
import { TAG_TEMPLATE } from './tag.component.html';
|
||||||
import { TAG_STYLE } from './tag.component.css';
|
import { TAG_STYLE } from './tag.component.css';
|
||||||
@ -51,7 +51,7 @@ export class TagComponent implements OnInit {
|
|||||||
@Input() withNotary: boolean;
|
@Input() withNotary: boolean;
|
||||||
|
|
||||||
@Output() refreshRepo = new EventEmitter<boolean>();
|
@Output() refreshRepo = new EventEmitter<boolean>();
|
||||||
@Output() tagClickEvent = new EventEmitter<Tag>();
|
@Output() tagClickEvent = new EventEmitter<TagClickEvent>();
|
||||||
|
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
|
|
||||||
@ -169,7 +169,12 @@ export class TagComponent implements OnInit {
|
|||||||
|
|
||||||
onTagClick(tag: Tag): void {
|
onTagClick(tag: Tag): void {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.tagClickEvent.emit(tag);
|
let evt: TagClickEvent = {
|
||||||
|
project_id: this.projectId,
|
||||||
|
repository_name: this.repoName,
|
||||||
|
tag_name: tag.name
|
||||||
|
};
|
||||||
|
this.tagClickEvent.emit(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,7 +32,7 @@
|
|||||||
"clarity-icons": "^0.9.0",
|
"clarity-icons": "^0.9.0",
|
||||||
"clarity-ui": "^0.9.0",
|
"clarity-ui": "^0.9.0",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "^0.1.83",
|
"harbor-ui": "^0.1.85",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-clipboard": "^8.0.2",
|
"ngx-clipboard": "^8.0.2",
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
// 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 { Project } from '../../project/project';
|
import { Project } from '../../project/project';
|
||||||
import { Repository } from '../../repository/repository';
|
import { Repository } from 'harbor-ui';
|
||||||
|
|
||||||
export class SearchResults {
|
export class SearchResults {
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -26,7 +26,7 @@ import { DestinationPageComponent } from './replication/destination/destination-
|
|||||||
|
|
||||||
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
||||||
|
|
||||||
import { RepositoryComponent } from './repository/repository.component';
|
import { RepositoryPageComponent } from './repository/repository-page.component';
|
||||||
import { TagRepositoryComponent } from './repository/tag-repository/tag-repository.component';
|
import { TagRepositoryComponent } from './repository/tag-repository/tag-repository.component';
|
||||||
import { ReplicationPageComponent } from './replication/replication-page.component';
|
import { ReplicationPageComponent } from './replication/replication-page.component';
|
||||||
import { MemberComponent } from './project/member/member.component';
|
import { MemberComponent } from './project/member/member.component';
|
||||||
@ -48,6 +48,8 @@ import { LeavingConfigRouteDeactivate } from './shared/route/leaving-config-deac
|
|||||||
|
|
||||||
import { MemberGuard } from './shared/route/member-guard-activate.service';
|
import { MemberGuard } from './shared/route/member-guard-activate.service';
|
||||||
|
|
||||||
|
import { TagDetailPageComponent } from './repository/tag-detail/tag-detail-page.component';
|
||||||
|
|
||||||
const harborRoutes: Routes = [
|
const harborRoutes: Routes = [
|
||||||
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
||||||
{ path: 'reset_password', component: ResetPasswordComponent },
|
{ path: 'reset_password', component: ResetPasswordComponent },
|
||||||
@ -108,20 +110,24 @@ const harborRoutes: Routes = [
|
|||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'repository',
|
path: 'repositories',
|
||||||
component: RepositoryComponent
|
component: RepositoryPageComponent
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'replication',
|
path: 'repositories/:repo/tags/:tag',
|
||||||
|
component: TagDetailPageComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'replications',
|
||||||
component: ReplicationPageComponent,
|
component: ReplicationPageComponent,
|
||||||
canActivate: [SystemAdminGuard]
|
canActivate: [SystemAdminGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'member',
|
path: 'members',
|
||||||
component: MemberComponent
|
component: MemberComponent
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'log',
|
path: 'logs',
|
||||||
component: AuditLogComponent
|
component: AuditLogComponent
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -68,7 +68,7 @@ export class ListProjectComponent {
|
|||||||
goToLink(proId: number): void {
|
goToLink(proId: number): void {
|
||||||
this.searchTrigger.closeSearch(true);
|
this.searchTrigger.closeSearch(true);
|
||||||
|
|
||||||
let linkUrl = ['harbor', 'projects', proId, 'repository'];
|
let linkUrl = ['harbor', 'projects', proId, 'repositories'];
|
||||||
this.router.navigate(linkUrl);
|
this.router.navigate(linkUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,16 +5,16 @@
|
|||||||
<nav class="subnav sub-nav-bg-color">
|
<nav class="subnav sub-nav-bg-color">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" routerLink="repository" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="member" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="log" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
||||||
<a class="nav-link" routerLink="replication" routerLinkActive="active">{{'PROJECT_DETAIL.REPLICATION' | translate}}</a>
|
<a class="nav-link" routerLink="replications" routerLinkActive="active">{{'PROJECT_DETAIL.REPLICATION' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -25,27 +25,31 @@ export class ProjectRoutingResolver implements Resolve<Project>{
|
|||||||
constructor(
|
constructor(
|
||||||
private sessionService: SessionService,
|
private sessionService: SessionService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private router: Router) {}
|
private router: Router) { }
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Project> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Project> {
|
||||||
|
//Support both parameters and query parameters
|
||||||
let projectId = route.params['id'];
|
let projectId = route.params['id'];
|
||||||
|
if (!projectId) {
|
||||||
|
projectId = route.queryParams['project_id'];
|
||||||
|
}
|
||||||
return this.projectService
|
return this.projectService
|
||||||
.getProject(projectId)
|
.getProject(projectId)
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then((project: Project)=> {
|
.then((project: Project) => {
|
||||||
if(project) {
|
if (project) {
|
||||||
let currentUser = this.sessionService.getCurrentUser();
|
let currentUser = this.sessionService.getCurrentUser();
|
||||||
if(currentUser) {
|
if (currentUser) {
|
||||||
let projectMembers = this.sessionService.getProjectMembers();
|
let projectMembers = this.sessionService.getProjectMembers();
|
||||||
if(projectMembers) {
|
if (projectMembers) {
|
||||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
let currentMember = projectMembers.find(m => m.user_id === currentUser.user_id);
|
||||||
if(currentMember) {
|
if (currentMember) {
|
||||||
project.is_member = true;
|
project.is_member = true;
|
||||||
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin');
|
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin');
|
||||||
project.role_name = currentMember.role_name;
|
project.role_name = currentMember.role_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(currentUser.has_admin_role === 1) {
|
if (currentUser.has_admin_role === 1) {
|
||||||
project.has_project_admin_role = true;
|
project.has_project_admin_role = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +58,7 @@ export class ProjectRoutingResolver implements Resolve<Project>{
|
|||||||
this.router.navigate(['/harbor', 'projects']);
|
this.router.navigate(['/harbor', 'projects']);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).catch(error=>{
|
}).catch(error => {
|
||||||
this.router.navigate(['/harbor', 'projects']);
|
this.router.navigate(['/harbor', 'projects']);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
|
||||||
<clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-row *clrDgItems="let r of repositories" [clrDgItem]='r'>
|
|
||||||
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
|
|
||||||
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
|
||||||
</clr-dg-action-overflow>
|
|
||||||
<clr-dg-cell><a href="javascript:void(0)" (click)="gotoLink(projectId || r.project_id, r.name || r.repository_name)">{{r.name || r.repository_name}}</a></clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{(repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}}
|
|
||||||
<clr-dg-pagination [clrDgPageSize]="15"></clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
@ -1,65 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
import { Component, Input, Output, EventEmitter, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { Repository } from '../repository';
|
|
||||||
import { State } from 'clarity-angular';
|
|
||||||
|
|
||||||
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'list-repository',
|
|
||||||
templateUrl: 'list-repository.component.html',
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
|
||||||
})
|
|
||||||
export class ListRepositoryComponent implements OnInit {
|
|
||||||
|
|
||||||
@Input() projectId: number;
|
|
||||||
@Input() repositories: Repository[];
|
|
||||||
|
|
||||||
@Output() delete = new EventEmitter<string>();
|
|
||||||
@Output() paginate = new EventEmitter<State>();
|
|
||||||
|
|
||||||
@Input() hasProjectAdminRole: boolean;
|
|
||||||
|
|
||||||
pageOffset: number = 1;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private router: Router,
|
|
||||||
private searchTrigger: SearchTriggerService,
|
|
||||||
private ref: ChangeDetectorRef) {
|
|
||||||
let hnd = setInterval(()=>ref.markForCheck(), 100);
|
|
||||||
setTimeout(()=>clearInterval(hnd), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() { }
|
|
||||||
|
|
||||||
deleteRepo(repoName: string) {
|
|
||||||
this.delete.emit(repoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh(state: State) {
|
|
||||||
if (this.repositories) {
|
|
||||||
this.paginate.emit(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public gotoLink(projectId: number, repoName: string): void {
|
|
||||||
this.searchTrigger.closeSearch(true);
|
|
||||||
|
|
||||||
let linkUrl = ['harbor', 'tags', projectId, repoName];
|
|
||||||
this.router.navigate(linkUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,3 @@
|
|||||||
|
<div style="margin-top: 24px;">
|
||||||
|
<hbr-repository-stackview [projectId]="projectId" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" (tagClickEvent)="watchTagClickEvent($event)"></hbr-repository-stackview>
|
||||||
|
</div>
|
51
src/ui_ng/src/app/repository/repository-page.component.ts
Normal file
51
src/ui_ng/src/app/repository/repository-page.component.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { Project } from '../project/project';
|
||||||
|
import { SessionService } from '../shared/session.service';
|
||||||
|
|
||||||
|
import { TagClickEvent } from 'harbor-ui';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'repository',
|
||||||
|
templateUrl: 'repository-page.component.html'
|
||||||
|
})
|
||||||
|
export class RepositoryPageComponent implements OnInit {
|
||||||
|
projectId: number;
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private session: SessionService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.projectId = this.route.snapshot.parent.params['id'];
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if (resolverData) {
|
||||||
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
|
}
|
||||||
|
this.hasSignedIn = this.session.getCurrentUser() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchTagClickEvent(tagEvt: TagClickEvent): void {
|
||||||
|
let linkUrl = ['harbor', 'projects', tagEvt.project_id, 'repositories', tagEvt.repository_name, 'tags', tagEvt.tag_name];
|
||||||
|
this.router.navigate(linkUrl);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
.option-right {
|
|
||||||
padding-right: 16px;
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<div class="row">
|
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
|
||||||
<div class="row flex-items-xs-right option-right">
|
|
||||||
<div class="flex-xs-middle">
|
|
||||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>
|
|
||||||
<a href="javascript:void(0)" (click)="refresh()">
|
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
|
||||||
<list-repository [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [hasProjectAdminRole]="hasProjectAdminRole" (paginate)="retrieve($event)"></list-repository>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,126 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
|
|
||||||
import { RepositoryService } from './repository.service';
|
|
||||||
import { Repository } from './repository';
|
|
||||||
|
|
||||||
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
|
||||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
|
||||||
|
|
||||||
|
|
||||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
|
||||||
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message';
|
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
|
||||||
|
|
||||||
import { State } from 'clarity-angular';
|
|
||||||
|
|
||||||
import { Project } from '../project/project';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'repository',
|
|
||||||
templateUrl: 'repository.component.html',
|
|
||||||
styleUrls: ['./repository.component.css']
|
|
||||||
})
|
|
||||||
export class RepositoryComponent implements OnInit {
|
|
||||||
changedRepositories: Repository[];
|
|
||||||
|
|
||||||
projectId: number;
|
|
||||||
|
|
||||||
lastFilteredRepoName: string;
|
|
||||||
|
|
||||||
totalPage: number;
|
|
||||||
totalRecordCount: number;
|
|
||||||
|
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
|
|
||||||
subscription: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private repositoryService: RepositoryService,
|
|
||||||
private messageHandlerService: MessageHandlerService,
|
|
||||||
private deletionDialogService: ConfirmationDialogService
|
|
||||||
) {
|
|
||||||
this.subscription = this.deletionDialogService
|
|
||||||
.confirmationConfirm$
|
|
||||||
.subscribe(
|
|
||||||
message => {
|
|
||||||
if (message &&
|
|
||||||
message.source === ConfirmationTargets.REPOSITORY &&
|
|
||||||
message.state === ConfirmationState.CONFIRMED) {
|
|
||||||
let repoName = message.data;
|
|
||||||
this.repositoryService
|
|
||||||
.deleteRepository(repoName)
|
|
||||||
.subscribe(
|
|
||||||
response => {
|
|
||||||
this.refresh();
|
|
||||||
this.messageHandlerService.showSuccess('REPOSITORY.DELETED_REPO_SUCCESS');
|
|
||||||
},
|
|
||||||
error => this.messageHandlerService.handleError(error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.projectId = this.route.snapshot.parent.params['id'];
|
|
||||||
let resolverData = this.route.snapshot.parent.data;
|
|
||||||
if(resolverData) {
|
|
||||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
|
||||||
}
|
|
||||||
this.lastFilteredRepoName = '';
|
|
||||||
this.retrieve();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.subscription) {
|
|
||||||
this.subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
retrieve(state?: State) {
|
|
||||||
this.repositoryService
|
|
||||||
.listRepositories(this.projectId, this.lastFilteredRepoName)
|
|
||||||
.subscribe(
|
|
||||||
response => {
|
|
||||||
this.changedRepositories = response.json();
|
|
||||||
},
|
|
||||||
error => this.messageHandlerService.handleError(error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
doSearchRepoNames(repoName: string) {
|
|
||||||
this.lastFilteredRepoName = repoName;
|
|
||||||
this.retrieve();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRepo(repoName: string) {
|
|
||||||
let message = new ConfirmationMessage(
|
|
||||||
'REPOSITORY.DELETION_TITLE_REPO',
|
|
||||||
'REPOSITORY.DELETION_SUMMARY_REPO',
|
|
||||||
repoName,
|
|
||||||
repoName,
|
|
||||||
ConfirmationTargets.REPOSITORY,
|
|
||||||
ConfirmationButtons.DELETE_CANCEL);
|
|
||||||
this.deletionDialogService.openComfirmDialog(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.retrieve();
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,10 @@ import { RouterModule } from '@angular/router';
|
|||||||
|
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
import { RepositoryComponent } from './repository.component';
|
import { RepositoryPageComponent } from './repository-page.component';
|
||||||
import { ListRepositoryComponent } from './list-repository/list-repository.component';
|
|
||||||
import { TagRepositoryComponent } from './tag-repository/tag-repository.component';
|
import { TagRepositoryComponent } from './tag-repository/tag-repository.component';
|
||||||
import { TopRepoComponent } from './top-repo/top-repo.component';
|
import { TopRepoComponent } from './top-repo/top-repo.component';
|
||||||
|
import { TagDetailPageComponent } from './tag-detail/tag-detail-page.component';
|
||||||
import { RepositoryService } from './repository.service';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -29,12 +27,16 @@ import { RepositoryService } from './repository.service';
|
|||||||
RouterModule
|
RouterModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
RepositoryComponent,
|
RepositoryPageComponent,
|
||||||
ListRepositoryComponent,
|
|
||||||
TagRepositoryComponent,
|
TagRepositoryComponent,
|
||||||
TopRepoComponent
|
TopRepoComponent,
|
||||||
|
TagDetailPageComponent
|
||||||
],
|
],
|
||||||
exports: [RepositoryComponent, ListRepositoryComponent, TopRepoComponent],
|
exports: [
|
||||||
providers: [RepositoryService]
|
RepositoryPageComponent,
|
||||||
|
TopRepoComponent,
|
||||||
|
TagDetailPageComponent
|
||||||
|
],
|
||||||
|
providers: []
|
||||||
})
|
})
|
||||||
export class RepositoryModule { }
|
export class RepositoryModule { }
|
@ -1,62 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Http, URLSearchParams, Response } from '@angular/http';
|
|
||||||
|
|
||||||
import { Repository } from './repository';
|
|
||||||
import { Tag } from './tag';
|
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable'
|
|
||||||
import 'rxjs/add/observable/of';
|
|
||||||
import 'rxjs/add/operator/mergeMap';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class RepositoryService {
|
|
||||||
|
|
||||||
constructor(private http: Http){}
|
|
||||||
|
|
||||||
listRepositories(projectId: number, repoName: string, page?: number, pageSize?: number): Observable<any> {
|
|
||||||
let params = new URLSearchParams();
|
|
||||||
if(page && pageSize) {
|
|
||||||
params.set('page', page + '');
|
|
||||||
params.set('page_size', pageSize + '');
|
|
||||||
}
|
|
||||||
return this.http
|
|
||||||
.get(`/api/repositories?project_id=${projectId}&q=${repoName}`, {search: params})
|
|
||||||
.map(response=>response)
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
listTags(repoName: string): Observable<Tag[]> {
|
|
||||||
return this.http
|
|
||||||
.get(`/api/repositories/${repoName}/tags`)
|
|
||||||
.map(response=>response.json())
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRepository(repoName: string): Observable<any> {
|
|
||||||
return this.http
|
|
||||||
.delete(`/api/repositories/${repoName}`)
|
|
||||||
.map(response=>response.status)
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRepoByTag(repoName: string, tag: string): Observable<any> {
|
|
||||||
return this.http
|
|
||||||
.delete(`/api/repositories/${repoName}/tags/${tag}`)
|
|
||||||
.map(response=>response.status)
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"name": "library/mysql",
|
|
||||||
"owner_id": 1,
|
|
||||||
"project_id": 1,
|
|
||||||
"description": "",
|
|
||||||
"pull_count": 0,
|
|
||||||
"star_count": 0,
|
|
||||||
"tags_count": 1,
|
|
||||||
"creation_time": "2017-02-14T09:22:58Z",
|
|
||||||
"update_time": "0001-01-01T00:00:00Z"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class Repository {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
owner_id: number;
|
|
||||||
project_id: number;
|
|
||||||
description: string;
|
|
||||||
pull_count: number;
|
|
||||||
start_count: number;
|
|
||||||
tags_count: number;
|
|
||||||
creation_time: Date;
|
|
||||||
update_time: Date;
|
|
||||||
|
|
||||||
constructor(name: string, tags_count: number) {
|
|
||||||
this.name = name;
|
|
||||||
this.tags_count = tags_count;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,3 @@
|
|||||||
|
<div style="margin-top: 24px;">
|
||||||
|
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [repositoryId]="repositoryId"></hbr-tag-detail>
|
||||||
|
</div>
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'repository',
|
||||||
|
templateUrl: 'tag-detail-page.component.html'
|
||||||
|
})
|
||||||
|
export class TagDetailPageComponent implements OnInit {
|
||||||
|
tagId: string;
|
||||||
|
repositoryId: string;
|
||||||
|
projectId: string | number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.repositoryId = this.route.snapshot.params["repo"];
|
||||||
|
this.tagId = this.route.snapshot.params["tag"];
|
||||||
|
this.projectId = this.route.snapshot.parent.params["id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
goBack(tag: string): void {
|
||||||
|
this.router.navigate(["harbor", "projects", this.projectId, "repositories"]);
|
||||||
|
}
|
||||||
|
}
|
@ -1,45 +1,5 @@
|
|||||||
|
<div>
|
||||||
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects', projectId, 'repository']">< {{'REPOSITORY.REPOSITORIES' | translate}}</a>
|
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects', projectId, 'repositories']">< {{'REPOSITORY.REPOSITORIES' | translate}}</a>
|
||||||
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'SEARCH.BACK' | translate}}</a>
|
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'SEARCH.BACK' | translate}}</a>
|
||||||
|
<hbr-tag (tagClickEvent)="watchTagClickEvt($event)" [repoName]="repoName" [registryUrl]="registryUrl" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="false"></hbr-tag>
|
||||||
<clr-modal [(clrModalOpen)]="showTagManifestOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
</div>
|
||||||
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row col-md-12">
|
|
||||||
<textarea rows="3" (click)="selectAndCopy($event)">{{digestId}}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-primary" (click)="showTagManifestOpened = false">{{'BUTTON.OK' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<h2 class="sub-header-title">{{repoName}}</h2>
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.PULL_COMMAND' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column *ngIf="withNotary">{{'REPOSITORY.SIGNED' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.AUTHOR' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.CREATED' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.ARCHITECTURE' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'REPOSITORY.OS' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-row *clrDgItems="let t of tags">
|
|
||||||
<clr-dg-action-overflow>
|
|
||||||
<button class="action-item" (click)="showDigestId(t)">{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
|
||||||
<button class="action-item" [hidden]="!hasProjectAdminRole" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</button>
|
|
||||||
</clr-dg-action-overflow>
|
|
||||||
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>docker pull {{registryUrl}}/{{repoName}}:{{t.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell *ngIf="withNotary">
|
|
||||||
<clr-icon *ngIf="t.signature" shape="check" style="color: #1D5100;"></clr-icon>
|
|
||||||
<clr-icon *ngIf="!t.signature" shape="close" style="color: #C92100;"></clr-icon>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{t.author}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{t.created | date: 'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{t.docker_version}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
<clr-dg-footer>{{tags ? tags.length : 0}} {{'REPOSITORY.ITEMS' | translate}}</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
@ -11,92 +11,38 @@
|
|||||||
// 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, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { RepositoryService } from '../repository.service';
|
|
||||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
|
||||||
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../../shared/shared.const';
|
|
||||||
|
|
||||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
|
||||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
|
||||||
|
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
|
||||||
|
|
||||||
import { Tag } from '../tag';
|
|
||||||
|
|
||||||
import { AppConfigService } from '../../app-config.service';
|
import { AppConfigService } from '../../app-config.service';
|
||||||
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
import { TagClickEvent } from 'harbor-ui';
|
||||||
import { Project } from '../../project/project';
|
import { Project } from '../../project/project';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tag-repository',
|
selector: 'tag-repository',
|
||||||
templateUrl: 'tag-repository.component.html',
|
templateUrl: 'tag-repository.component.html',
|
||||||
styleUrls: ['./tag-repository.component.css'],
|
styleUrls: ['./tag-repository.component.css']
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
|
||||||
})
|
})
|
||||||
export class TagRepositoryComponent implements OnInit, OnDestroy {
|
export class TagRepositoryComponent implements OnInit {
|
||||||
|
|
||||||
projectId: number;
|
projectId: number;
|
||||||
repoName: string;
|
repoName: string;
|
||||||
|
|
||||||
hasProjectAdminRole: boolean = false;
|
hasProjectAdminRole: boolean = false;
|
||||||
|
|
||||||
tags: Tag[];
|
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
withNotary: boolean;
|
withNotary: boolean;
|
||||||
|
|
||||||
hasSignedIn: boolean;
|
hasSignedIn: boolean;
|
||||||
|
|
||||||
showTagManifestOpened: boolean;
|
|
||||||
manifestInfoTitle: string;
|
|
||||||
digestId: string;
|
|
||||||
staticBackdrop: boolean = true;
|
|
||||||
closable: boolean = false;
|
|
||||||
|
|
||||||
selectAll: boolean = false;
|
|
||||||
|
|
||||||
subscription: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private messageHandlerService: MessageHandlerService,
|
private router: Router,
|
||||||
private deletionDialogService: ConfirmationDialogService,
|
|
||||||
private repositoryService: RepositoryService,
|
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private session: SessionService,
|
private session: SessionService) {
|
||||||
private ref: ChangeDetectorRef){
|
|
||||||
this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe(
|
|
||||||
message => {
|
|
||||||
if (message &&
|
|
||||||
message.source === ConfirmationTargets.TAG
|
|
||||||
&& message.state === ConfirmationState.CONFIRMED) {
|
|
||||||
let tag = message.data;
|
|
||||||
if (tag) {
|
|
||||||
if (tag.signed) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.repositoryService
|
|
||||||
.deleteRepoByTag(this.repoName, tag.name)
|
|
||||||
.subscribe(
|
|
||||||
response => {
|
|
||||||
this.retrieve();
|
|
||||||
this.messageHandlerService.showSuccess('REPOSITORY.DELETED_TAG_SUCCESS');
|
|
||||||
},
|
|
||||||
error => this.messageHandlerService.handleError(error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.hasSignedIn = (this.session.getCurrentUser() !== null);
|
this.hasSignedIn = (this.session.getCurrentUser() !== null);
|
||||||
let resolverData = this.route.snapshot.data;
|
let resolverData = this.route.snapshot.data;
|
||||||
if(resolverData) {
|
if (resolverData) {
|
||||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
}
|
}
|
||||||
this.projectId = this.route.snapshot.params['id'];
|
this.projectId = this.route.snapshot.params['id'];
|
||||||
@ -104,60 +50,10 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
||||||
this.withNotary = this.appConfigService.getConfig().with_notary;
|
this.withNotary = this.appConfigService.getConfig().with_notary;
|
||||||
this.retrieve();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
watchTagClickEvt(tagEvt: TagClickEvent): void {
|
||||||
if (this.subscription) {
|
let linkUrl = ['harbor', 'projects', tagEvt.project_id, 'repositories', tagEvt.repository_name, 'tags', tagEvt.tag_name];
|
||||||
this.subscription.unsubscribe();
|
this.router.navigate(linkUrl);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
retrieve() {
|
|
||||||
this.tags = [];
|
|
||||||
this.repositoryService
|
|
||||||
.listTags(this.repoName)
|
|
||||||
.subscribe(
|
|
||||||
tags => this.tags = tags,
|
|
||||||
error => this.messageHandlerService.handleError(error));
|
|
||||||
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
|
|
||||||
setTimeout(()=>clearInterval(hnd), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteTag(tag: Tag) {
|
|
||||||
if (tag) {
|
|
||||||
let titleKey: string, summaryKey: string, content: string, buttons: ConfirmationButtons;
|
|
||||||
if (tag.signature) {
|
|
||||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED';
|
|
||||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED';
|
|
||||||
buttons = ConfirmationButtons.CLOSE;
|
|
||||||
content = 'notary -s https://' + this.registryUrl + ':4443 -d ~/.docker/trust remove -p ' + this.registryUrl + '/' + this.repoName + ' ' + tag.name;
|
|
||||||
} else {
|
|
||||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG';
|
|
||||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG';
|
|
||||||
buttons = ConfirmationButtons.DELETE_CANCEL;
|
|
||||||
content = tag.name;
|
|
||||||
}
|
|
||||||
let message = new ConfirmationMessage(
|
|
||||||
titleKey,
|
|
||||||
summaryKey,
|
|
||||||
content,
|
|
||||||
tag,
|
|
||||||
ConfirmationTargets.TAG,
|
|
||||||
buttons);
|
|
||||||
this.deletionDialogService.openComfirmDialog(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showDigestId(tag: Tag) {
|
|
||||||
if(tag) {
|
|
||||||
this.manifestInfoTitle = 'REPOSITORY.COPY_DIGEST_ID';
|
|
||||||
this.digestId = tag.digest;
|
|
||||||
this.showTagManifestOpened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selectAndCopy($event: any) {
|
|
||||||
$event.target.select();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
export class Tag {
|
|
||||||
digest: string;
|
|
||||||
name: string;
|
|
||||||
architecture: string;
|
|
||||||
os: string;
|
|
||||||
docker_version: string;
|
|
||||||
author: string;
|
|
||||||
created: Date;
|
|
||||||
signature?: {[key: string]: any | any[]}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ import { errorHandler } from '../../shared/shared.utils';
|
|||||||
import { AlertType, ListMode } from '../../shared/shared.const';
|
import { AlertType, ListMode } from '../../shared/shared.const';
|
||||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||||
import { TopRepoService } from './top-repository.service';
|
import { TopRepoService } from './top-repository.service';
|
||||||
import { Repository } from '../repository';
|
import { Repository } from 'harbor-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'top-repo',
|
selector: 'top-repo',
|
||||||
|
@ -15,7 +15,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Headers, Http, RequestOptions } from '@angular/http';
|
import { Headers, Http, RequestOptions } from '@angular/http';
|
||||||
import 'rxjs/add/operator/toPromise';
|
import 'rxjs/add/operator/toPromise';
|
||||||
|
|
||||||
import { Repository } from '../repository';
|
import { Repository } from 'harbor-ui';
|
||||||
|
|
||||||
export const topRepoEndpoint = "/api/repositories/top";
|
export const topRepoEndpoint = "/api/repositories/top";
|
||||||
/**
|
/**
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Router, NavigationExtras } from '@angular/router';
|
import { Router, NavigationExtras } from '@angular/router';
|
||||||
import { Repository } from '../../repository/repository';
|
import { Repository } from 'harbor-ui';
|
||||||
import { State } from 'clarity-angular';
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||||
|
@ -330,6 +330,7 @@
|
|||||||
"OS": "OS",
|
"OS": "OS",
|
||||||
"SHOW_DETAILS": "Show Details",
|
"SHOW_DETAILS": "Show Details",
|
||||||
"REPOSITORIES": "Repositories",
|
"REPOSITORIES": "Repositories",
|
||||||
|
"OF": "of",
|
||||||
"ITEMS": "item(s)",
|
"ITEMS": "item(s)",
|
||||||
"POP_REPOS": "Popular Repositories",
|
"POP_REPOS": "Popular Repositories",
|
||||||
"DELETED_REPO_SUCCESS": "Deleted repository successfully.",
|
"DELETED_REPO_SUCCESS": "Deleted repository successfully.",
|
||||||
|
@ -331,6 +331,7 @@
|
|||||||
"OS": "SO",
|
"OS": "SO",
|
||||||
"SHOW_DETAILS": "Mostrar Detalles",
|
"SHOW_DETAILS": "Mostrar Detalles",
|
||||||
"REPOSITORIES": "Repositorios",
|
"REPOSITORIES": "Repositorios",
|
||||||
|
"OF": "of",
|
||||||
"ITEMS": "elemento(s)",
|
"ITEMS": "elemento(s)",
|
||||||
"POP_REPOS": "Repositorios Populares",
|
"POP_REPOS": "Repositorios Populares",
|
||||||
"DELETED_REPO_SUCCESS": "Repositorio eliminado satisfactoriamente.",
|
"DELETED_REPO_SUCCESS": "Repositorio eliminado satisfactoriamente.",
|
||||||
|
@ -330,6 +330,7 @@
|
|||||||
"OS": "操作系统",
|
"OS": "操作系统",
|
||||||
"SHOW_DETAILS": "显示详细",
|
"SHOW_DETAILS": "显示详细",
|
||||||
"REPOSITORIES": "镜像仓库",
|
"REPOSITORIES": "镜像仓库",
|
||||||
|
"OF": "共计",
|
||||||
"ITEMS": "条记录",
|
"ITEMS": "条记录",
|
||||||
"POP_REPOS": "受欢迎的镜像仓库",
|
"POP_REPOS": "受欢迎的镜像仓库",
|
||||||
"DELETED_REPO_SUCCESS": "成功删除镜像仓库。",
|
"DELETED_REPO_SUCCESS": "成功删除镜像仓库。",
|
||||||
|
Loading…
Reference in New Issue
Block a user