mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 02:35:17 +01:00
Fix issues per testing and merge latest updates.
This commit is contained in:
parent
74e7b66aa0
commit
9a4ba69026
21
src/ui/views/reset-password-mail.tpl
Normal file
21
src/ui/views/reset-password-mail.tpl
Normal file
@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Copyright (c) 2016 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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<p>Please click this link to reset your password:</p>
|
||||
<a href="{{.URL}}/reset_password?reset_uuid={{.UUID}}">{{.URL}}/reset_password?reset_uuid={{.UUID}}</a>
|
||||
</body>
|
||||
</html>
|
@ -76,7 +76,10 @@ const harborRoutes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'tags/:id/:repo',
|
||||
component: TagRepositoryComponent
|
||||
component: TagRepositoryComponent,
|
||||
resolve: {
|
||||
projectResolver: ProjectRoutingResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:id',
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||
|
||||
import { BaseService } from '../service/base.service';
|
||||
|
||||
import { AuditLog } from './audit-log';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@ -13,7 +11,7 @@ import 'rxjs/add/observable/throw';
|
||||
export const logEndpoint = "/api/logs";
|
||||
|
||||
@Injectable()
|
||||
export class AuditLogService extends BaseService {
|
||||
export class AuditLogService {
|
||||
private httpOptions = new RequestOptions({
|
||||
headers: new Headers({
|
||||
"Content-Type": 'application/json',
|
||||
@ -21,9 +19,7 @@ export class AuditLogService extends BaseService {
|
||||
})
|
||||
});
|
||||
|
||||
constructor(private http: Http) {
|
||||
super();
|
||||
}
|
||||
constructor(private http: Http) {}
|
||||
|
||||
listAuditLogs(queryParam: AuditLog): Observable<any> {
|
||||
return this.http
|
||||
@ -36,12 +32,12 @@ export class AuditLogService extends BaseService {
|
||||
username: queryParam.username
|
||||
})
|
||||
.map(response => response)
|
||||
.catch(error => this.handleError(error));
|
||||
.catch(error => Observable.throw(error));
|
||||
}
|
||||
|
||||
getRecentLogs(lines: number): Observable<AuditLog[]> {
|
||||
return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions)
|
||||
.map(response => response.json() as AuditLog[])
|
||||
.catch(error => this.handleError(error));
|
||||
.catch(error => Observable.throw(error));
|
||||
}
|
||||
}
|
@ -31,6 +31,6 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!projectForm.form.valid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="projectForm.form.invalid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column *ngIf="showRoleInfo">{{'PROJECT.ROLE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'PROJECT.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
||||
<clr-dg-action-overflow [hidden]="!listFullMode || p.current_user_role_id !== 1">
|
||||
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
||||
@ -12,9 +12,9 @@
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="showRoleInfo">{{roleInfo[p.current_user_role_id] | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.creation_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.description}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
||||
|
@ -5,7 +5,7 @@ import { ProjectService } from '../project.service';
|
||||
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||
import { ListMode } from '../../shared/shared.const';
|
||||
import { ListMode, ProjectTypes, RoleInfo } from '../../shared/shared.const';
|
||||
|
||||
import { State } from 'clarity-angular';
|
||||
|
||||
@ -24,6 +24,8 @@ export class ListProjectComponent implements OnInit {
|
||||
@Input() totalRecordCount: number;
|
||||
pageOffset: number = 1;
|
||||
|
||||
@Input() filteredType: string;
|
||||
|
||||
@Output() paginate = new EventEmitter<State>();
|
||||
|
||||
@Output() toggle = new EventEmitter<Project>();
|
||||
@ -31,6 +33,8 @@ export class ListProjectComponent implements OnInit {
|
||||
|
||||
@Input() mode: string = ListMode.FULL;
|
||||
|
||||
roleInfo = RoleInfo;
|
||||
|
||||
constructor(
|
||||
private session: SessionService,
|
||||
private router: Router,
|
||||
@ -43,6 +47,10 @@ export class ListProjectComponent implements OnInit {
|
||||
return this.mode === ListMode.FULL && this.session.getCurrentUser() != null;
|
||||
}
|
||||
|
||||
get showRoleInfo(): boolean {
|
||||
return this.listFullMode && this.filteredType === ProjectTypes[0];
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
let account = this.session.getCurrentUser();
|
||||
return account != null && account.has_admin_role > 0;
|
||||
|
@ -36,6 +36,6 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!memberForm.form.valid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="memberForm.form.invalid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
|
@ -17,15 +17,15 @@
|
||||
<clr-datagrid>
|
||||
<clr-dg-column>{{'MEMBER.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'MEMBER.ROLE' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *ngFor="let u of members">
|
||||
<clr-dg-action-overflow [hidden]="u.user_id === currentUser.user_id || !hasProjectAdminRole">
|
||||
<button class="action-item" (click)="changeRole(u.user_id, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(u.user_id, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(u.user_id, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
||||
<button class="action-item" (click)="deleteMember(u.user_id)">{{'MEMBER.DELETE' | translate}}</button>
|
||||
<clr-dg-row *ngFor="let m of members">
|
||||
<clr-dg-action-overflow [hidden]="m.user_id === currentUser.user_id || !hasProjectAdminRole">
|
||||
<button class="action-item" (click)="changeRole(m, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(m, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||
<button class="action-item" (click)="changeRole(m, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
||||
<button class="action-item" (click)="deleteMember(m)">{{'MEMBER.DELETE' | translate}}</button>
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell>{{u.username}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{roleInfo[u.role_id] | translate}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{m.username}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{roleInfo[m.role_id] | translate}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>{{ (members ? members.length : 0) }} {{'MEMBER.ITEMS' | translate}}</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
|
@ -15,6 +15,8 @@ import { ConfirmationDialogService } from '../../shared/confirmation-dialog/conf
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
|
||||
import { RoleInfo } from '../../shared/shared.const';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import 'rxjs/add/operator/catch';
|
||||
@ -22,7 +24,7 @@ import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/observable/throw';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
export const roleInfo: {} = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||
import { Project } from '../../project/project';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@ -31,31 +33,22 @@ export const roleInfo: {} = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER',
|
||||
})
|
||||
export class MemberComponent implements OnInit, OnDestroy {
|
||||
|
||||
currentUser: SessionUser;
|
||||
members: Member[];
|
||||
projectId: number;
|
||||
roleInfo = roleInfo;
|
||||
roleInfo = RoleInfo;
|
||||
private delSub: Subscription;
|
||||
|
||||
@ViewChild(AddMemberComponent)
|
||||
addMemberComponent: AddMemberComponent;
|
||||
|
||||
currentUser: SessionUser;
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router,
|
||||
private memberService: MemberService, private messageService: MessageService,
|
||||
private deletionDialogService: ConfirmationDialogService,
|
||||
session: SessionService) {
|
||||
//Get current user from registered resolver.
|
||||
this.currentUser = session.getCurrentUser();
|
||||
let projectMembers: Member[] = session.getProjectMembers();
|
||||
if(this.currentUser && projectMembers) {
|
||||
let currentMember = projectMembers.find(m=>m.user_id === this.currentUser.user_id);
|
||||
if(currentMember) {
|
||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
||||
}
|
||||
}
|
||||
|
||||
private session: SessionService) {
|
||||
|
||||
this.delSub = deletionDialogService.confirmationConfirm$.subscribe(message => {
|
||||
if (message &&
|
||||
message.state === ConfirmationState.CONFIRMED &&
|
||||
@ -82,8 +75,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
error => {
|
||||
this.router.navigate(['/harbor', 'projects']);
|
||||
this.messageService.announceMessage(error.status, 'Failed to get project member with project ID:' + projectId, AlertType.DANGER);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@ -97,6 +89,15 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||
console.log('Get projectId from route params snapshot:' + this.projectId);
|
||||
|
||||
this.currentUser = this.session.getCurrentUser();
|
||||
//Get current user from registered resolver.
|
||||
let resolverData = this.route.snapshot.parent.data;
|
||||
if(resolverData) {
|
||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.retrieve(this.projectId, '');
|
||||
}
|
||||
|
||||
@ -108,25 +109,27 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
this.retrieve(this.projectId, '');
|
||||
}
|
||||
|
||||
changeRole(userId: number, roleId: number) {
|
||||
this.memberService
|
||||
.changeMemberRole(this.projectId, userId, roleId)
|
||||
.subscribe(
|
||||
response => {
|
||||
this.messageService.announceMessage(response, 'MEMBER.SWITCHED_SUCCESS', AlertType.SUCCESS);
|
||||
console.log('Successful change role with user ' + userId + ' to roleId ' + roleId);
|
||||
this.retrieve(this.projectId, '');
|
||||
},
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to change role with user ' + userId + ' to roleId ' + roleId, AlertType.DANGER)
|
||||
);
|
||||
changeRole(m: Member, roleId: number) {
|
||||
if(m) {
|
||||
this.memberService
|
||||
.changeMemberRole(this.projectId, m.user_id, roleId)
|
||||
.subscribe(
|
||||
response => {
|
||||
this.messageService.announceMessage(response, 'MEMBER.SWITCHED_SUCCESS', AlertType.SUCCESS);
|
||||
console.log('Successful change role with user ' + m.user_id + ' to roleId ' + roleId);
|
||||
this.retrieve(this.projectId, '');
|
||||
},
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to change role with user ' + m.user_id + ' to roleId ' + roleId, AlertType.DANGER)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
deleteMember(userId: number) {
|
||||
deleteMember(m: Member) {
|
||||
let deletionMessage: ConfirmationMessage = new ConfirmationMessage(
|
||||
'MEMBER.DELETION_TITLE',
|
||||
'MEMBER.DELETION_SUMMARY',
|
||||
userId + "",
|
||||
userId,
|
||||
m.username,
|
||||
m.user_id,
|
||||
ConfirmationTargets.PROJECT_MEMBER
|
||||
);
|
||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||
|
@ -6,22 +6,19 @@ import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/observable/throw';
|
||||
|
||||
import { BaseService } from '../../service/base.service';
|
||||
import { Member } from './member';
|
||||
|
||||
@Injectable()
|
||||
export class MemberService extends BaseService {
|
||||
export class MemberService {
|
||||
|
||||
constructor(private http: Http) {
|
||||
super();
|
||||
}
|
||||
constructor(private http: Http) {}
|
||||
|
||||
listMembers(projectId: number, username: string): Observable<Member[]> {
|
||||
console.log('Get member from project_id:' + projectId + ', username:' + username);
|
||||
return this.http
|
||||
.get(`/api/projects/${projectId}/members?username=${username}`)
|
||||
.map(response=>response.json())
|
||||
.catch(error=>this.handleError(error));
|
||||
.map(response=>response.json() as Member[])
|
||||
.catch(error=>Observable.throw(error));
|
||||
}
|
||||
|
||||
addMember(projectId: number, username: string, roleId: number): Observable<any> {
|
||||
|
@ -5,10 +5,10 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="repository" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSessionValid">
|
||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||
<a class="nav-link" routerLink="member" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSessionValid">
|
||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||
<a class="nav-link" routerLink="log" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
||||
|
@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Project } from '../project';
|
||||
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { ProjectService } from '../../project/project.service';
|
||||
|
||||
@Component({
|
||||
selector: 'project-detail',
|
||||
@ -13,13 +14,18 @@ import { SessionService } from '../../shared/session.service';
|
||||
export class ProjectDetailComponent {
|
||||
|
||||
currentProject: Project;
|
||||
isMember: boolean;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private sessionService: SessionService) {
|
||||
this.route.data.subscribe(data=>this.currentProject = <Project>data['projectResolver']);
|
||||
private sessionService: SessionService,
|
||||
private projectService: ProjectService) {
|
||||
|
||||
this.route.data.subscribe(data=>{
|
||||
this.currentProject = <Project>data['projectResolver'];
|
||||
this.isMember = this.currentProject.is_member;
|
||||
});
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
|
@ -3,23 +3,43 @@ import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@a
|
||||
|
||||
import { Project } from './project';
|
||||
import { ProjectService } from './project.service';
|
||||
import { SessionService } from '../shared/session.service';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectRoutingResolver implements Resolve<Project>{
|
||||
|
||||
constructor(private projectService: ProjectService, private router: Router) {}
|
||||
constructor(
|
||||
private sessionService: SessionService,
|
||||
private projectService: ProjectService,
|
||||
private router: Router) {}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Project> {
|
||||
let projectId = route.params['id'];
|
||||
let projectId = route.params['id'];
|
||||
console.log('Project resolver, projectID:' + projectId);
|
||||
return this.projectService
|
||||
.getProject(projectId)
|
||||
.then(project=> {
|
||||
if(project) {
|
||||
return project;
|
||||
} else {
|
||||
this.router.navigate(['/harbor', 'projects']);
|
||||
return null;
|
||||
}
|
||||
.toPromise()
|
||||
.then((project: Project)=> {
|
||||
if(project) {
|
||||
let currentUser = this.sessionService.getCurrentUser();
|
||||
let projectMembers = this.sessionService.getProjectMembers();
|
||||
if(currentUser && projectMembers) {
|
||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
||||
if(currentMember) {
|
||||
project.is_member = true;
|
||||
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin') || currentUser.has_admin_role === 1;
|
||||
}
|
||||
}
|
||||
return project;
|
||||
} else {
|
||||
this.router.navigate(['/harbor', 'projects']);
|
||||
return null;
|
||||
}
|
||||
}).catch(error=>{
|
||||
this.router.navigate(['/harbor', 'projects']);
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
.header-title {
|
||||
margin-top: 0;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.option-left {
|
||||
padding-left: 12px;
|
||||
margin-top: 12px;
|
||||
padding-left: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.option-right {
|
||||
padding-right: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
padding-right: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
<div class="row">
|
||||
<div class="row" style="margin-right: 12px;">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<h2 class="header-title">{{'PROJECT.PROJECTS' | translate}}</h2>
|
||||
<div>
|
||||
<statistics-panel></statistics-panel>
|
||||
</div>
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="option-left">
|
||||
<button *ngIf="projectCreationRestriction" class="btn btn-primary" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'PROJECT.PROJECT' | translate}}</button>
|
||||
@ -18,11 +21,13 @@
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<grid-filter filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filter)="doSearchProjects($event)"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></a>
|
||||
<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-project [projects]="changedProjects" (toggle)="toggleProject($event)" (delete)="deleteProject($event)" (paginate)="retrieve($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount"></list-project>
|
||||
</div>
|
||||
<list-project [projects]="changedProjects" [filteredType]="projectTypes[currentFilteredType]" (toggle)="toggleProject($event)" (delete)="deleteProject($event)" (paginate)="retrieve($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount"></list-project>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -25,9 +25,7 @@ import { State } from 'clarity-angular';
|
||||
|
||||
import { AppConfigService } from '../app-config.service';
|
||||
import { SessionService } from '../shared/session.service';
|
||||
|
||||
|
||||
const types: {} = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
||||
import { ProjectTypes } from '../shared/shared.const';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@ -39,7 +37,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
|
||||
selected = [];
|
||||
changedProjects: Project[];
|
||||
projectTypes = types;
|
||||
projectTypes = ProjectTypes;
|
||||
|
||||
@ViewChild(CreateProjectComponent)
|
||||
creationProject: CreateProjectComponent;
|
||||
@ -145,7 +143,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
doFilterProjects(filteredType: number): void {
|
||||
console.log('Filter projects with type:' + types[filteredType]);
|
||||
console.log('Filter projects with type:' + this.projectTypes[filteredType]);
|
||||
this.isPublic = filteredType;
|
||||
this.currentFilteredType = filteredType;
|
||||
this.retrieve();
|
||||
|
@ -3,8 +3,6 @@ import { Injectable } from '@angular/core';
|
||||
import { Http, Headers, RequestOptions, Response, URLSearchParams } from '@angular/http';
|
||||
import { Project } from './project';
|
||||
|
||||
import { BaseService } from '../service/base.service';
|
||||
|
||||
import { Message } from '../global-message/message';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@ -22,11 +20,10 @@ export class ProjectService {
|
||||
|
||||
constructor(private http: Http) {}
|
||||
|
||||
getProject(projectId: number): Promise<Project> {
|
||||
getProject(projectId: number): Observable<any> {
|
||||
return this.http
|
||||
.get(`/api/projects/${projectId}`)
|
||||
.toPromise()
|
||||
.then(response=>response.json() as Project)
|
||||
.map(response=>response.json())
|
||||
.catch(error=>Observable.throw(error));
|
||||
}
|
||||
|
||||
|
@ -29,4 +29,6 @@ export class Project {
|
||||
update_time: Date;
|
||||
current_user_role_id: number;
|
||||
repo_count: number;
|
||||
has_project_admin_role: boolean;
|
||||
is_member: boolean;
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, URLSearchParams, Response } from '@angular/http';
|
||||
|
||||
import { BaseService } from '../service/base.service';
|
||||
|
||||
import { Policy } from './policy';
|
||||
import { Job } from './job';
|
||||
import { Target } from './target';
|
||||
@ -14,10 +12,8 @@ import 'rxjs/add/observable/throw';
|
||||
import 'rxjs/add/operator/mergeMap';
|
||||
|
||||
@Injectable()
|
||||
export class ReplicationService extends BaseService {
|
||||
constructor(private http: Http) {
|
||||
super();
|
||||
}
|
||||
export class ReplicationService {
|
||||
constructor(private http: Http) {}
|
||||
|
||||
listPolicies(policyName: string, projectId?: any): Observable<Policy[]> {
|
||||
if(!projectId) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { Router, NavigationExtras } from '@angular/router';
|
||||
import { Repository } from '../repository';
|
||||
import { State } from 'clarity-angular';
|
||||
@ -8,16 +8,17 @@ import { SessionService } from '../../shared/session.service';
|
||||
import { ListMode } from '../../shared/shared.const';
|
||||
|
||||
import { SessionUser } from '../../shared/session-user';
|
||||
import { Member } from '../../project/member/member';
|
||||
|
||||
@Component({
|
||||
selector: 'list-repository',
|
||||
templateUrl: 'list-repository.component.html'
|
||||
})
|
||||
export class ListRepositoryComponent {
|
||||
export class ListRepositoryComponent implements OnInit {
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() repositories: Repository[];
|
||||
|
||||
|
||||
@Output() delete = new EventEmitter<string>();
|
||||
|
||||
@Input() totalPage: number;
|
||||
@ -25,25 +26,16 @@ export class ListRepositoryComponent {
|
||||
@Output() paginate = new EventEmitter<State>();
|
||||
|
||||
@Input() mode: string = ListMode.FULL;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
|
||||
pageOffset: number = 1;
|
||||
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private session: SessionService) {
|
||||
//Get current user from registered resolver.
|
||||
let currentUser = session.getCurrentUser();
|
||||
let projectMembers: Member[] = session.getProjectMembers();
|
||||
if(currentUser && projectMembers) {
|
||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
||||
if(currentMember) {
|
||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
||||
}
|
||||
}
|
||||
}
|
||||
private session: SessionService) { }
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
deleteRepo(repoName: string) {
|
||||
this.delete.emit(repoName);
|
||||
|
@ -8,6 +8,6 @@
|
||||
</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)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount" (paginate)="retrieve($event)"></list-repository>
|
||||
<list-repository [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount" [hasProjectAdminRole]="hasProjectAdminRole" (paginate)="retrieve($event)"></list-repository>
|
||||
</div>
|
||||
</div>
|
@ -14,6 +14,8 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { State } from 'clarity-angular';
|
||||
|
||||
import { Project } from '../project/project';
|
||||
|
||||
const repositoryTypes = [
|
||||
{ key: '0', description: 'REPOSITORY.MY_REPOSITORY' },
|
||||
{ key: '1', description: 'REPOSITORY.PUBLIC_REPOSITORY' }
|
||||
@ -39,6 +41,8 @@ export class RepositoryComponent implements OnInit {
|
||||
totalPage: number;
|
||||
totalRecordCount: number;
|
||||
|
||||
hasProjectAdminRole: boolean;
|
||||
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
@ -66,12 +70,16 @@ export class RepositoryComponent implements OnInit {
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to delete repo:' + repoName, AlertType.DANGER)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
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.currentRepositoryType = this.repositoryTypes[0];
|
||||
this.lastFilteredRepoName = '';
|
||||
this.retrieve();
|
||||
|
@ -16,7 +16,8 @@ import { TagView } from '../tag-view';
|
||||
import { AppConfigService } from '../../app-config.service';
|
||||
|
||||
import { SessionService } from '../../shared/session.service';
|
||||
import { Member } from '../../project/member/member';
|
||||
|
||||
import { Project } from '../../project/project';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@ -29,7 +30,7 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
||||
projectId: number;
|
||||
repoName: string;
|
||||
|
||||
hasProjectAdminRole: boolean;
|
||||
hasProjectAdminRole: boolean = false;
|
||||
|
||||
tags: TagView[];
|
||||
registryUrl: string;
|
||||
@ -45,15 +46,6 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
||||
private appConfigService: AppConfigService,
|
||||
private session: SessionService){
|
||||
|
||||
let currentUser = session.getCurrentUser();
|
||||
let projectMembers: Member[] = session.getProjectMembers();
|
||||
if(currentUser && projectMembers) {
|
||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
||||
if(currentMember) {
|
||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
||||
}
|
||||
}
|
||||
|
||||
this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (message &&
|
||||
@ -78,11 +70,15 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
let resolverData = this.route.snapshot.data;
|
||||
console.log(JSON.stringify(resolverData));
|
||||
if(resolverData) {
|
||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||
}
|
||||
this.projectId = this.route.snapshot.params['id'];
|
||||
this.repoName = this.route.snapshot.params['repo'];
|
||||
this.tags = [];
|
||||
@ -100,17 +96,17 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
||||
retrieve() {
|
||||
this.tags = [];
|
||||
if(this.withNotary) {
|
||||
this.repositoryService
|
||||
.listTagsWithVerifiedSignatures(this.repoName)
|
||||
.subscribe(
|
||||
items => this.listTags(items),
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||
this.repositoryService
|
||||
.listTagsWithVerifiedSignatures(this.repoName)
|
||||
.subscribe(
|
||||
items => this.listTags(items),
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||
} else {
|
||||
this.repositoryService
|
||||
.listTags(this.repoName)
|
||||
.subscribe(
|
||||
items => this.listTags(items),
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||
.listTags(this.repoName)
|
||||
.subscribe(
|
||||
items => this.listTags(items),
|
||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate } from '@angular/router';
|
||||
|
||||
export class AuthGuard implements CanActivate {
|
||||
canActivate() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { Http, Response,} from '@angular/http';
|
||||
|
||||
export class BaseService {
|
||||
|
||||
protected handleError(error: Response | any): Promise<any> {
|
||||
// In a real world app, we might use a remote logging infrastructure
|
||||
let errMsg: string;
|
||||
console.log(typeof error);
|
||||
if (error instanceof Response) {
|
||||
const body = error.json() || '';
|
||||
const err = body.error || JSON.stringify(body);
|
||||
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
|
||||
} else {
|
||||
errMsg = error.message ? error.message : error.toString();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
@ -17,7 +17,8 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
||||
private router: Router) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
|
||||
let projectId: number = route.params['id'];
|
||||
let projectId = route.params['id'];
|
||||
this.sessionService.setProjectMembers([]);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.projectService.checkProjectMember(projectId)
|
||||
.subscribe(
|
||||
@ -26,6 +27,10 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
||||
return resolve(true)
|
||||
},
|
||||
error => {
|
||||
//Add exception for repository in project detail router activation.
|
||||
if(state.url.endsWith('repository')) {
|
||||
return resolve(true);
|
||||
}
|
||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
||||
return resolve(false);
|
||||
});
|
||||
|
@ -52,4 +52,7 @@ export const CookieKeyOfAdmiral = "admiral.endpoint.latest";
|
||||
|
||||
export const enum ConfirmationState {
|
||||
NA, CONFIRMED, CANCEL
|
||||
}
|
||||
}
|
||||
|
||||
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
||||
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||
|
@ -1,25 +1,41 @@
|
||||
<div class="card card-block">
|
||||
<h3 class="card-title">{{'STATISTICS.TITLE' | translate }}</h3>
|
||||
<span class="card-text">
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||
<span class="statistic-column-title">{{'STATISTICS.PRO_ITEM' | translate }}</span>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-10 col-lg-10 col-xl-10">
|
||||
<statistics [data]='{number: originalCopy.my_project_count, label: "my"}'></statistics>
|
||||
<statistics [data]='{number: originalCopy.public_project_count, label: "pub"}'></statistics>
|
||||
<statistics [data]='{number: originalCopy.total_project_count, label: "total"}' *ngIf="isValidSession"></statistics>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||
<span class="statistic-column-title">{{'STATISTICS.REPO_ITEM' | translate }}</span>
|
||||
<div class="row flex-items-xs-between flex-items-xs-middle">
|
||||
<div></div>
|
||||
<div id="right_statistic_panel" style="margin-right: 18px;">
|
||||
<div class="statistic-block">
|
||||
<div class="statistic-column-block">
|
||||
<div>
|
||||
<span class="statistic-column-title statistic-column-title-pro">{{'STATISTICS.PRO_ITEM' | translate }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="statistic-column-title statistic-column-title-repo">{{'STATISTICS.REPO_ITEM' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic-column-block" style="margin-left: 16px;">
|
||||
<div>
|
||||
<statistics [data]='originalCopy.my_project_count' [label]='"STATISTICS.INDEX_MY" | translate'></statistics>
|
||||
</div>
|
||||
<div>
|
||||
<statistics [data]='originalCopy.my_repo_count' [label]='"STATISTICS.INDEX_MY" | translate'></statistics>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic-column-block" style="margin-left: 28px;">
|
||||
<div>
|
||||
<statistics [data]='originalCopy.public_project_count' [label]='"STATISTICS.INDEX_PUB" | translate'></statistics>
|
||||
</div>
|
||||
<div>
|
||||
<statistics [data]='originalCopy.public_repo_count' [label]='"STATISTICS.INDEX_PUB" | translate'></statistics>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic-column-block" style="margin-left: 28px;">
|
||||
<div>
|
||||
<statistics [data]='originalCopy.total_project_count' [label]='"STATISTICS.INDEX_TOTAL" | translate' *ngIf="isValidSession"></statistics>
|
||||
</div>
|
||||
<div>
|
||||
<statistics [data]='originalCopy.total_repo_count' [label]='"STATISTICS.INDEX_TOTAL" | translate' *ngIf="isValidSession"></statistics>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistic-item-divider"></div>
|
||||
<div class="statistic-block">Storage</div>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-10 col-lg-10 col-xl-10">
|
||||
<statistics [data]='{number: originalCopy.my_repo_count, label: "my"}'></statistics>
|
||||
<statistics [data]='{number: originalCopy.public_repo_count, label: "pub"}'></statistics>
|
||||
<statistics [data]='{number: originalCopy.total_repo_count, label: "total"}' *ngIf="isValidSession"></statistics>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
@ -1,30 +1,57 @@
|
||||
.statistic-wrapper {
|
||||
padding: 12px;
|
||||
margin: 12px;
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
margin: 4px;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
height: 72px;
|
||||
min-width: 108px;
|
||||
max-width: 216px;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.statistic-data {
|
||||
font-size: 48px;
|
||||
font-weight: bolder;
|
||||
font-family: "Metropolis";
|
||||
line-height: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
font-family: "semibold";
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.statistic-text {
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
text-transform: uppercase;
|
||||
font-family: "Metropolis";
|
||||
font-family: "semibold";
|
||||
}
|
||||
|
||||
.statistic-column-block {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.statistic-column-title {
|
||||
position: relative;
|
||||
top: 40%;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.statistic-column-title-pro {
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.statistic-column-title-repo {
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.statistic-item-divider {
|
||||
height: 54px;
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
background-color: #ccc;
|
||||
opacity: 0.55;
|
||||
margin-left: 4px;
|
||||
margin-right: 12px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.statistic-block {
|
||||
display: inline-block;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<div class="statistic-wrapper">
|
||||
<span class="statistic-data">{{data.number}}</span>
|
||||
<span class="statistic-text">{{data.label}}</span>
|
||||
<span class="statistic-data">{{data}}</span>
|
||||
<span class="statistic-text">{{label}}</span>
|
||||
</div>
|
@ -7,5 +7,6 @@ import { Component, Input } from '@angular/core';
|
||||
})
|
||||
|
||||
export class StatisticsComponent {
|
||||
@Input() data: any;
|
||||
@Input() label: string;
|
||||
@Input() data: number = 0;
|
||||
}
|
@ -110,10 +110,10 @@
|
||||
"PROJECT": {
|
||||
"PROJECTS": "Projects",
|
||||
"NAME": "Project Name",
|
||||
"ROLE": "Role",
|
||||
"PUBLIC_OR_PRIVATE": "Public",
|
||||
"REPO_COUNT": "Repositories Count",
|
||||
"CREATION_TIME": "Creation Time",
|
||||
"DESCRIPTION": "Description",
|
||||
"PUBLIC": "Public",
|
||||
"PRIVATE": "Private",
|
||||
"MAKE": "Make",
|
||||
@ -290,7 +290,7 @@
|
||||
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
||||
"DELETION_TITLE_TAG_DENIED": "Signed Tag can't be deleted",
|
||||
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted. {{param}}",
|
||||
"FILTER_FOR_REPOSITORIES": "Filter for repositories",
|
||||
"FILTER_FOR_REPOSITORIES": "Filter Repositories",
|
||||
"TAG": "Tag",
|
||||
"SIGNED": "Signed",
|
||||
"AUTHOR": "Author",
|
||||
|
@ -110,10 +110,10 @@
|
||||
"PROJECT": {
|
||||
"PROJECTS": "项目",
|
||||
"NAME": "项目名称",
|
||||
"ROLE": "角色",
|
||||
"PUBLIC_OR_PRIVATE": "公开",
|
||||
"REPO_COUNT": "镜像仓库数",
|
||||
"CREATION_TIME": "创建时间",
|
||||
"DESCRIPTION": "描述",
|
||||
"PUBLIC": "公开",
|
||||
"PRIVATE": "私有",
|
||||
"MAKE": "设为",
|
||||
@ -395,8 +395,8 @@
|
||||
"TITLE": "统计",
|
||||
"PRO_ITEM": "项目",
|
||||
"REPO_ITEM": "镜像库",
|
||||
"INDEX_MY": "私有的",
|
||||
"INDEX_PUB": "公开的",
|
||||
"INDEX_MY": "私有",
|
||||
"INDEX_PUB": "公开",
|
||||
"INDEX_TOTAL": "总计"
|
||||
},
|
||||
"SEARCH": {
|
||||
|
Loading…
Reference in New Issue
Block a user