mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-27 02:58:05 +01:00
Merge pull request #1791 from wknet123/dev-revised
Merge latest updates of UI.
This commit is contained in:
commit
28a513f900
@ -51,7 +51,9 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.appConfig = this.appConfigService.getConfig();
|
//Make sure the updated configuration can be loaded
|
||||||
|
this.appConfigService.load()
|
||||||
|
.then(updatedConfig => this.appConfig = updatedConfig);
|
||||||
this.route.queryParams
|
this.route.queryParams
|
||||||
.subscribe(params => {
|
.subscribe(params => {
|
||||||
this.redirectUrl = params["redirect_url"] || "";
|
this.redirectUrl = params["redirect_url"] || "";
|
||||||
@ -108,7 +110,7 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
|||||||
|
|
||||||
//Fill the new user info into the sign in form
|
//Fill the new user info into the sign in form
|
||||||
private handleUserCreation(user: User): void {
|
private handleUserCreation(user: User): void {
|
||||||
if(user){
|
if (user) {
|
||||||
this.currentForm.setValue({
|
this.currentForm.setValue({
|
||||||
"login_username": user.username,
|
"login_username": user.username,
|
||||||
"login_password": user.password
|
"login_password": user.password
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<form class="search">
|
<form class="search">
|
||||||
<label for="search_input">
|
<label for="search_input">
|
||||||
<input #globalSearchBox id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{"GLOBAL_SEARCH.PLACEHOLDER" | translate}}'>
|
<input #globalSearchBox name="globalSearchBox" [(ngModel)]="searchTerm" id="search_input" type="text" (keyup)="search(globalSearchBox.value)" placeholder='{{"GLOBAL_SEARCH.PLACEHOLDER" | translate}}'>
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
@ -21,10 +21,11 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
//Keep subscription for future use
|
//Keep subscription for future use
|
||||||
private searchSub: Subscription;
|
private searchSub: Subscription;
|
||||||
private stateSub: Subscription;
|
private closeSub: Subscription;
|
||||||
|
|
||||||
//To indicate if the result panel is opened
|
//To indicate if the result panel is opened
|
||||||
private isResPanelOpened: boolean = false;
|
private isResPanelOpened: boolean = false;
|
||||||
|
private searchTerm: string = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private searchTrigger: SearchTriggerService,
|
private searchTrigger: SearchTriggerService,
|
||||||
@ -38,6 +39,9 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
|||||||
.subscribe(term => {
|
.subscribe(term => {
|
||||||
this.searchTrigger.triggerSearch(term);
|
this.searchTrigger.triggerSearch(term);
|
||||||
});
|
});
|
||||||
|
this.closeSub = this.searchTrigger.searchClearChan$.subscribe(clear => {
|
||||||
|
this.searchTerm = "";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
<div class="spinner spinner-lg search-spinner" [hidden]="done">{{'SEARCH.IN_PROGRESS' | translate}}</div>
|
<div class="spinner spinner-lg search-spinner" [hidden]="done">{{'SEARCH.IN_PROGRESS' | translate}}</div>
|
||||||
<div id="results">
|
<div id="results">
|
||||||
<h2>{{'PROJECT.PROJECTS' | translate}}</h2>
|
<h2>{{'PROJECT.PROJECTS' | translate}}</h2>
|
||||||
<list-project [projects]="searchResults.project" [mode]="listMode"></list-project>
|
<list-project-ro [projects]="searchResults.project"></list-project-ro>
|
||||||
<h2>{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</h2>
|
<h2>{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</h2>
|
||||||
<list-repository [repositories]="searchResults.repository" [mode]="listMode"></list-repository>
|
<list-repository-ro [repositories]="searchResults.repository"></list-repository-ro>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Output, EventEmitter } from '@angular/core';
|
import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
import { GlobalSearchService } from './global-search.service';
|
import { GlobalSearchService } from './global-search.service';
|
||||||
import { SearchResults } from './search-results';
|
import { SearchResults } from './search-results';
|
||||||
@ -8,6 +8,8 @@ import { MessageService } from '../../global-message/message.service';
|
|||||||
|
|
||||||
import { SearchTriggerService } from './search-trigger.service';
|
import { SearchTriggerService } from './search-trigger.service';
|
||||||
|
|
||||||
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "search-result",
|
selector: "search-result",
|
||||||
templateUrl: "search-result.component.html",
|
templateUrl: "search-result.component.html",
|
||||||
@ -16,7 +18,7 @@ import { SearchTriggerService } from './search-trigger.service';
|
|||||||
providers: [GlobalSearchService]
|
providers: [GlobalSearchService]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class SearchResultComponent {
|
export class SearchResultComponent implements OnInit, OnDestroy {
|
||||||
private searchResults: SearchResults = new SearchResults();
|
private searchResults: SearchResults = new SearchResults();
|
||||||
private originalCopy: SearchResults;
|
private originalCopy: SearchResults;
|
||||||
|
|
||||||
@ -30,11 +32,34 @@ export class SearchResultComponent {
|
|||||||
//Whether or not mouse point is onto the close indicator
|
//Whether or not mouse point is onto the close indicator
|
||||||
private mouseOn: boolean = false;
|
private mouseOn: boolean = false;
|
||||||
|
|
||||||
|
//Watch message channel
|
||||||
|
private searchSub: Subscription;
|
||||||
|
private closeSearchSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private search: GlobalSearchService,
|
private search: GlobalSearchService,
|
||||||
private msgService: MessageService,
|
private msgService: MessageService,
|
||||||
private searchTrigger: SearchTriggerService) { }
|
private searchTrigger: SearchTriggerService) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(term => {
|
||||||
|
this.doSearch(term);
|
||||||
|
});
|
||||||
|
this.closeSearchSub = this.searchTrigger.searchCloseChan$.subscribe(close => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.searchSub) {
|
||||||
|
this.searchSub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.closeSearchSub) {
|
||||||
|
this.closeSearchSub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private clone(src: SearchResults): SearchResults {
|
private clone(src: SearchResults): SearchResults {
|
||||||
let res: SearchResults = new SearchResults();
|
let res: SearchResults = new SearchResults();
|
||||||
|
|
||||||
@ -64,27 +89,23 @@ export class SearchResultComponent {
|
|||||||
return this.mouseOn;
|
return this.mouseOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle mouse event of close indicator
|
|
||||||
mouseAction(over: boolean): void {
|
|
||||||
this.mouseOn = over;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Show the results
|
//Show the results
|
||||||
show(): void {
|
show(): void {
|
||||||
this.stateIndicator = true;
|
this.stateIndicator = true;
|
||||||
this.searchTrigger.searchInputStat(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Close the result page
|
//Close the result page
|
||||||
close(): void {
|
close(): void {
|
||||||
//Tell shell close
|
|
||||||
this.searchTrigger.closeSearch(true);
|
|
||||||
this.searchTrigger.searchInputStat(false);
|
|
||||||
this.stateIndicator = false;
|
this.stateIndicator = false;
|
||||||
|
this.searchTrigger.clear(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Call search service to complete the search request
|
//Call search service to complete the search request
|
||||||
doSearch(term: string): void {
|
doSearch(term: string): void {
|
||||||
|
//Only search none empty term
|
||||||
|
if (!term || term.trim() === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
//Do nothing if search is ongoing
|
//Do nothing if search is ongoing
|
||||||
if (this.onGoing) {
|
if (this.onGoing) {
|
||||||
return;
|
return;
|
||||||
|
@ -7,11 +7,11 @@ export class SearchTriggerService {
|
|||||||
|
|
||||||
private searchTriggerSource = new Subject<string>();
|
private searchTriggerSource = new Subject<string>();
|
||||||
private searchCloseSource = new Subject<boolean>();
|
private searchCloseSource = new Subject<boolean>();
|
||||||
private searchInputSource = new Subject<boolean>();
|
private searchClearSource = new Subject<boolean>();
|
||||||
|
|
||||||
searchTriggerChan$ = this.searchTriggerSource.asObservable();
|
searchTriggerChan$ = this.searchTriggerSource.asObservable();
|
||||||
searchCloseChan$ = this.searchCloseSource.asObservable();
|
searchCloseChan$ = this.searchCloseSource.asObservable();
|
||||||
searchInputChan$ = this.searchInputSource.asObservable();
|
searchClearChan$ = this.searchClearSource.asObservable();
|
||||||
|
|
||||||
triggerSearch(event: string) {
|
triggerSearch(event: string) {
|
||||||
this.searchTriggerSource.next(event);
|
this.searchTriggerSource.next(event);
|
||||||
@ -23,9 +23,9 @@ export class SearchTriggerService {
|
|||||||
this.searchCloseSource.next(event);
|
this.searchCloseSource.next(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Notify the state change of search box in home start page
|
//Clear search term
|
||||||
searchInputStat(event: boolean) {
|
clear(event): void {
|
||||||
this.searchInputSource.next(event);
|
this.searchClearSource.next(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -8,7 +8,7 @@
|
|||||||
<search-result></search-result>
|
<search-result></search-result>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
<nav class="sidenav" *ngIf="isUserExisting" [class.side-nav-override]="showSearch" (click)='watchClickEvt()'>
|
<nav class="sidenav" *ngIf="isUserExisting">
|
||||||
<section class="sidenav-content">
|
<section class="sidenav-content">
|
||||||
<a routerLink="/harbor/projects" routerLinkActive="active" class="nav-link">{{'SIDE_NAV.PROJECTS' | translate}}</a>
|
<a routerLink="/harbor/projects" routerLinkActive="active" class="nav-link">{{'SIDE_NAV.PROJECTS' | translate}}</a>
|
||||||
<a routerLink="/harbor/logs" routerLinkActive="active" class="nav-link" style="margin-top: 4px;">{{'SIDE_NAV.LOGS' | translate}}</a>
|
<a routerLink="/harbor/logs" routerLinkActive="active" class="nav-link" style="margin-top: 4px;">{{'SIDE_NAV.LOGS' | translate}}</a>
|
||||||
|
@ -5,13 +5,11 @@ import { ModalEvent } from '../modal-event';
|
|||||||
import { modalEvents } from '../modal-events.const';
|
import { modalEvents } from '../modal-events.const';
|
||||||
|
|
||||||
import { AccountSettingsModalComponent } from '../../account/account-settings/account-settings-modal.component';
|
import { AccountSettingsModalComponent } from '../../account/account-settings/account-settings-modal.component';
|
||||||
import { SearchResultComponent } from '../global-search/search-result.component';
|
|
||||||
import { PasswordSettingComponent } from '../../account/password/password-setting.component';
|
import { PasswordSettingComponent } from '../../account/password/password-setting.component';
|
||||||
import { NavigatorComponent } from '../navigator/navigator.component';
|
import { NavigatorComponent } from '../navigator/navigator.component';
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
|
||||||
import { AboutDialogComponent } from '../../shared/about-dialog/about-dialog.component';
|
import { AboutDialogComponent } from '../../shared/about-dialog/about-dialog.component';
|
||||||
import { StartPageComponent } from '../start-page/start.component';
|
|
||||||
|
|
||||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||||
|
|
||||||
@ -30,9 +28,6 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
@ViewChild(AccountSettingsModalComponent)
|
@ViewChild(AccountSettingsModalComponent)
|
||||||
private accountSettingsModal: AccountSettingsModalComponent;
|
private accountSettingsModal: AccountSettingsModalComponent;
|
||||||
|
|
||||||
@ViewChild(SearchResultComponent)
|
|
||||||
private searchResultComponet: SearchResultComponent;
|
|
||||||
|
|
||||||
@ViewChild(PasswordSettingComponent)
|
@ViewChild(PasswordSettingComponent)
|
||||||
private pwdSetting: PasswordSettingComponent;
|
private pwdSetting: PasswordSettingComponent;
|
||||||
|
|
||||||
@ -42,9 +37,6 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
@ViewChild(AboutDialogComponent)
|
@ViewChild(AboutDialogComponent)
|
||||||
private aboutDialog: AboutDialogComponent;
|
private aboutDialog: AboutDialogComponent;
|
||||||
|
|
||||||
@ViewChild(StartPageComponent)
|
|
||||||
private searchSatrt: StartPageComponent;
|
|
||||||
|
|
||||||
//To indicator whwther or not the search results page is displayed
|
//To indicator whwther or not the search results page is displayed
|
||||||
//We need to use this property to do some overriding work
|
//We need to use this property to do some overriding work
|
||||||
private isSearchResultsOpened: boolean = false;
|
private isSearchResultsOpened: boolean = false;
|
||||||
@ -60,15 +52,13 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(searchEvt => {
|
this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(searchEvt => {
|
||||||
this.doSearch(searchEvt);
|
if(searchEvt && searchEvt.trim() != ""){
|
||||||
|
this.isSearchResultsOpened = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.searchCloseSub = this.searchTrigger.searchCloseChan$.subscribe(close => {
|
this.searchCloseSub = this.searchTrigger.searchCloseChan$.subscribe(close => {
|
||||||
if (close) {
|
this.isSearchResultsOpened = false;
|
||||||
this.searchClose();
|
|
||||||
}else{
|
|
||||||
this.watchClickEvt();//reuse
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,30 +106,4 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle the global search event and then let the result page to trigger api
|
|
||||||
doSearch(event: string): void {
|
|
||||||
if (event === "") {
|
|
||||||
//Do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Once this method is called
|
|
||||||
//the search results page must be opened
|
|
||||||
this.isSearchResultsOpened = true;
|
|
||||||
|
|
||||||
//Call the child component to do the real work
|
|
||||||
this.searchResultComponet.doSearch(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Search results page closed
|
|
||||||
//remove the related ovevriding things
|
|
||||||
searchClose(): void {
|
|
||||||
this.isSearchResultsOpened = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Close serch result panel if existing
|
|
||||||
watchClickEvt(): void {
|
|
||||||
this.searchResultComponet.close();
|
|
||||||
this.isSearchResultsOpened = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -105,7 +105,7 @@ export class NavigatorComponent implements OnInit {
|
|||||||
this.msgService.announceMessage(error.status | 500, errorHandler(error), AlertType.WARNING);
|
this.msgService.announceMessage(error.status | 500, errorHandler(error), AlertType.WARNING);
|
||||||
});
|
});
|
||||||
//Confirm search result panel is close
|
//Confirm search result panel is close
|
||||||
this.searchTrigger.closeSearch(false);
|
this.searchTrigger.closeSearch(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Switch languages
|
//Switch languages
|
||||||
@ -132,10 +132,10 @@ export class NavigatorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Confirm search result panel is close
|
//Confirm search result panel is close
|
||||||
this.searchTrigger.closeSearch(false);
|
this.searchTrigger.closeSearch(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
registryAction(): void {
|
registryAction(): void {
|
||||||
this.searchTrigger.closeSearch(false);
|
this.searchTrigger.closeSearch(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -125,7 +125,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
||||||
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
<clr-icon shape="info-circle" class="is-info" size="24"></clr-icon>
|
||||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.AUTH_MODE' | translate}}</span>
|
<span class="tooltip-content">{{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -243,12 +243,20 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
public testMailServer(): void {
|
public testMailServer(): void {
|
||||||
let mailSettings = {};
|
let mailSettings = {};
|
||||||
let allChanges = this.getChanges();
|
for (let prop in this.allConfig) {
|
||||||
for (let prop in allChanges) {
|
|
||||||
if (prop.startsWith("email_")) {
|
if (prop.startsWith("email_")) {
|
||||||
mailSettings[prop] = allChanges[prop];
|
mailSettings[prop] = this.allConfig[prop].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Confirm port is number
|
||||||
|
mailSettings["email_port"] = +mailSettings["email_port"];
|
||||||
|
let allChanges = this.getChanges();
|
||||||
|
let password = allChanges["email_password"]
|
||||||
|
if (password) {
|
||||||
|
mailSettings["email_password"] = password;
|
||||||
|
} else {
|
||||||
|
delete mailSettings["email_password"];
|
||||||
|
}
|
||||||
|
|
||||||
this.testingOnGoing = true;
|
this.testingOnGoing = true;
|
||||||
this.configService.testMailServer(mailSettings)
|
this.configService.testMailServer(mailSettings)
|
||||||
@ -264,14 +272,21 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public testLDAPServer(): void {
|
public testLDAPServer(): void {
|
||||||
let ldapSettings = {};
|
let ldapSettings = {};
|
||||||
|
for (let prop in this.allConfig) {
|
||||||
|
if (prop.startsWith("ldap_")) {
|
||||||
|
ldapSettings[prop] = this.allConfig[prop].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let allChanges = this.getChanges();
|
let allChanges = this.getChanges();
|
||||||
for (let prop in allChanges) {
|
for(let prop in allChanges){
|
||||||
if (prop.startsWith("ldap_")) {
|
if (prop.startsWith("ldap_")) {
|
||||||
ldapSettings[prop] = allChanges[prop];
|
ldapSettings[prop] = allChanges[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info(ldapSettings);
|
console.info(ldapSettings);
|
||||||
|
|
||||||
this.testingOnGoing = true;
|
this.testingOnGoing = true;
|
||||||
this.configService.testLDAPServer(ldapSettings)
|
this.configService.testLDAPServer(ldapSettings)
|
||||||
.then(respone => {
|
.then(respone => {
|
||||||
@ -296,7 +311,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
|
|||||||
this.confirmService.openComfirmDialog(msg);
|
this.confirmService.openComfirmDialog(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private confirmUnsavedTabChanges(changes: any, tabId: string){
|
private confirmUnsavedTabChanges(changes: any, tabId: string) {
|
||||||
let msg = new ConfirmationMessage(
|
let msg = new ConfirmationMessage(
|
||||||
"CONFIG.CONFIRM_TITLE",
|
"CONFIG.CONFIRM_TITLE",
|
||||||
"CONFIG.CONFIRM_SUMMARY",
|
"CONFIG.CONFIRM_SUMMARY",
|
||||||
|
@ -35,8 +35,8 @@ export class RecentLogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleOnchange($event: any) {
|
private handleOnchange($event: any) {
|
||||||
if (event && event.target && event.srcElement["value"]) {
|
if ($event && $event.target && $event.target["value"]) {
|
||||||
this.lines = event.srcElement["value"];
|
this.lines = $event.target["value"];
|
||||||
if (this.lines < 10) {
|
if (this.lines < 10) {
|
||||||
this.lines = 10;
|
this.lines = 10;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| 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.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
||||||
<clr-dg-action-overflow [hidden]="!listFullMode || p.current_user_role_id !== 1">
|
<clr-dg-action-overflow [hidden]="p.current_user_role_id !== 1">
|
||||||
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
||||||
<button class="action-item" (click)="toggleProject(p)">{{'PROJECT.MAKE' | translate}} {{(p.public === 0 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} </button>
|
<button class="action-item" (click)="toggleProject(p)">{{'PROJECT.MAKE' | translate}} {{(p.public === 0 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} </button>
|
||||||
<button class="action-item" (click)="deleteProject(p)">{{'PROJECT.DELETE' | translate}}</button>
|
<button class="action-item" (click)="deleteProject(p)">{{'PROJECT.DELETE' | translate}}</button>
|
||||||
|
@ -5,15 +5,14 @@ import { ProjectService } from '../project.service';
|
|||||||
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||||
import { ListMode, ProjectTypes, RoleInfo } from '../../shared/shared.const';
|
import { ProjectTypes, RoleInfo } from '../../shared/shared.const';
|
||||||
|
|
||||||
import { State } from 'clarity-angular';
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
selector: 'list-project',
|
selector: 'list-project',
|
||||||
templateUrl: 'list-project.component.html',
|
templateUrl: 'list-project.component.html'
|
||||||
styleUrls: ['./list-project.component.css']
|
|
||||||
})
|
})
|
||||||
export class ListProjectComponent implements OnInit {
|
export class ListProjectComponent implements OnInit {
|
||||||
|
|
||||||
@ -31,8 +30,6 @@ export class ListProjectComponent implements OnInit {
|
|||||||
@Output() toggle = new EventEmitter<Project>();
|
@Output() toggle = new EventEmitter<Project>();
|
||||||
@Output() delete = new EventEmitter<Project>();
|
@Output() delete = new EventEmitter<Project>();
|
||||||
|
|
||||||
@Input() mode: string = ListMode.FULL;
|
|
||||||
|
|
||||||
roleInfo = RoleInfo;
|
roleInfo = RoleInfo;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -43,12 +40,8 @@ export class ListProjectComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get listFullMode(): boolean {
|
|
||||||
return this.mode === ListMode.FULL && this.session.getCurrentUser() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get showRoleInfo(): boolean {
|
get showRoleInfo(): boolean {
|
||||||
return this.listFullMode && this.filteredType === ProjectTypes[0];
|
return this.filteredType === ProjectTypes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSystemAdmin(): boolean {
|
public get isSystemAdmin(): boolean {
|
||||||
@ -57,19 +50,10 @@ export class ListProjectComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
goToLink(proId: number): void {
|
goToLink(proId: number): void {
|
||||||
this.searchTrigger.closeSearch(false);
|
this.searchTrigger.closeSearch(true);
|
||||||
|
|
||||||
let linkUrl = ['harbor', 'projects', proId, 'repository'];
|
let linkUrl = ['harbor', 'projects', proId, 'repository'];
|
||||||
if (!this.session.getCurrentUser()) {
|
this.router.navigate(linkUrl);
|
||||||
let navigatorExtra: NavigationExtras = {
|
|
||||||
queryParams: { "guest": true }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.router.navigate(linkUrl, navigatorExtra);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(linkUrl);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(state: State) {
|
refresh(state: State) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<a style="display: block;" [routerLink]="['/harbor', 'projects']">< {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
|
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects']">< {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
|
||||||
<h1 class="sub-header-title">{{currentProject.name}}</h1>
|
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'SEARCH.BACK' | translate}}</a>
|
||||||
|
|
||||||
|
<h1 class="sub-header-title">{{currentProject.name}} <span class="badge badge-light-blue" *ngIf="isMember">{{roleName | translate}}</span> <span class="badge badge-purple" *ngIf="isSystemAdmin">{{ 'MEMBER.SYS_ADMIN' | translate}}</span></h1>
|
||||||
<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">
|
||||||
|
@ -6,6 +6,8 @@ import { Project } from '../project';
|
|||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { ProjectService } from '../../project/project.service';
|
import { ProjectService } from '../../project/project.service';
|
||||||
|
|
||||||
|
import { RoleMapping } from '../../shared/shared.const';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'project-detail',
|
selector: 'project-detail',
|
||||||
templateUrl: "project-detail.component.html",
|
templateUrl: "project-detail.component.html",
|
||||||
@ -13,8 +15,11 @@ import { ProjectService } from '../../project/project.service';
|
|||||||
})
|
})
|
||||||
export class ProjectDetailComponent {
|
export class ProjectDetailComponent {
|
||||||
|
|
||||||
|
hasSignedIn: boolean;
|
||||||
currentProject: Project;
|
currentProject: Project;
|
||||||
|
|
||||||
isMember: boolean;
|
isMember: boolean;
|
||||||
|
roleName: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -22,9 +27,11 @@ export class ProjectDetailComponent {
|
|||||||
private sessionService: SessionService,
|
private sessionService: SessionService,
|
||||||
private projectService: ProjectService) {
|
private projectService: ProjectService) {
|
||||||
|
|
||||||
|
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
|
||||||
this.route.data.subscribe(data=>{
|
this.route.data.subscribe(data=>{
|
||||||
this.currentProject = <Project>data['projectResolver'];
|
this.currentProject = <Project>data['projectResolver'];
|
||||||
this.isMember = this.currentProject.is_member;
|
this.isMember = this.currentProject.is_member;
|
||||||
|
this.roleName = RoleMapping[this.currentProject.role_name];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +23,21 @@ export class ProjectRoutingResolver implements Resolve<Project>{
|
|||||||
.then((project: Project)=> {
|
.then((project: Project)=> {
|
||||||
if(project) {
|
if(project) {
|
||||||
let currentUser = this.sessionService.getCurrentUser();
|
let currentUser = this.sessionService.getCurrentUser();
|
||||||
let projectMembers = this.sessionService.getProjectMembers();
|
if(currentUser) {
|
||||||
if(currentUser && projectMembers) {
|
if(currentUser.has_admin_role === 1) {
|
||||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
project.has_project_admin_role = true;
|
||||||
if(currentMember) {
|
project.role_name = 'sysAdmin';
|
||||||
project.is_member = true;
|
} else {
|
||||||
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin') || currentUser.has_admin_role === 1;
|
let projectMembers = this.sessionService.getProjectMembers();
|
||||||
}
|
if(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');
|
||||||
|
project.role_name = currentMember.role_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return project;
|
return project;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
.header-title {
|
.header-title {
|
||||||
margin-top: 12px;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-left {
|
.option-left {
|
||||||
padding-left: 12px;
|
padding-left: 16px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,4 +31,5 @@ export class Project {
|
|||||||
repo_count: number;
|
repo_count: number;
|
||||||
has_project_admin_role: boolean;
|
has_project_admin_role: boolean;
|
||||||
is_member: boolean;
|
is_member: boolean;
|
||||||
|
role_name: string;
|
||||||
}
|
}
|
@ -174,6 +174,7 @@ export class CreateEditDestinationComponent implements AfterViewChecked {
|
|||||||
this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'});
|
this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'});
|
||||||
} else {
|
} else {
|
||||||
this.createEditDestinationOpened = false;
|
this.createEditDestinationOpened = false;
|
||||||
|
this.targetForm.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||||
<clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'REPOSITORY.TAGS_COUNT' | 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-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]='r'>
|
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]='r'>
|
||||||
<clr-dg-action-overflow *ngIf="listFullMode && hasProjectAdminRole">
|
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
|
||||||
<button class="action-item">{{'REPOSITORY.COPY_ID' | translate}}</button>
|
<button class="action-item">{{'REPOSITORY.COPY_ID' | translate}}</button>
|
||||||
<button class="action-item">{{'REPOSITORY.COPY_PARENT_ID' | translate}}</button>
|
<button class="action-item">{{'REPOSITORY.COPY_PARENT_ID' | translate}}</button>
|
||||||
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-overflow>
|
</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><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.tags_count}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
{{totalRecordCount || (repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}}
|
{{totalRecordCount || (repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}}
|
||||||
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>
|
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
@ -1,13 +1,9 @@
|
|||||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||||
import { Router, NavigationExtras } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Repository } from '../repository';
|
import { Repository } from '../repository';
|
||||||
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';
|
||||||
import { SessionService } from '../../shared/session.service';
|
|
||||||
import { ListMode } from '../../shared/shared.const';
|
|
||||||
|
|
||||||
import { SessionUser } from '../../shared/session-user';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'list-repository',
|
selector: 'list-repository',
|
||||||
@ -17,7 +13,7 @@ export class ListRepositoryComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() repositories: Repository[];
|
@Input() repositories: Repository[];
|
||||||
|
|
||||||
|
|
||||||
@Output() delete = new EventEmitter<string>();
|
@Output() delete = new EventEmitter<string>();
|
||||||
|
|
||||||
@ -25,17 +21,15 @@ export class ListRepositoryComponent implements OnInit {
|
|||||||
@Input() totalRecordCount: number;
|
@Input() totalRecordCount: number;
|
||||||
@Output() paginate = new EventEmitter<State>();
|
@Output() paginate = new EventEmitter<State>();
|
||||||
|
|
||||||
@Input() mode: string = ListMode.FULL;
|
|
||||||
@Input() hasProjectAdminRole: boolean;
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
pageOffset: number = 1;
|
pageOffset: number = 1;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private searchTrigger: SearchTriggerService,
|
private searchTrigger: SearchTriggerService) { }
|
||||||
private session: SessionService) { }
|
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() { }
|
||||||
|
|
||||||
deleteRepo(repoName: string) {
|
deleteRepo(repoName: string) {
|
||||||
this.delete.emit(repoName);
|
this.delete.emit(repoName);
|
||||||
@ -47,22 +41,11 @@ export class ListRepositoryComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get listFullMode(): boolean {
|
|
||||||
return this.mode === ListMode.FULL && this.session.getCurrentUser() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public gotoLink(projectId: number, repoName: string): void {
|
public gotoLink(projectId: number, repoName: string): void {
|
||||||
this.searchTrigger.closeSearch(false);
|
this.searchTrigger.closeSearch(true);
|
||||||
|
|
||||||
let linkUrl = ['harbor', 'tags', projectId, repoName];
|
let linkUrl = ['harbor', 'tags', projectId, repoName];
|
||||||
if (!this.session.getCurrentUser()) {
|
this.router.navigate(linkUrl);
|
||||||
let navigatorExtra: NavigationExtras = {
|
|
||||||
queryParams: { "guest": true }
|
|
||||||
};
|
|
||||||
this.router.navigate(linkUrl, navigatorExtra);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(linkUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -16,11 +16,6 @@ import { State } from 'clarity-angular';
|
|||||||
|
|
||||||
import { Project } from '../project/project';
|
import { Project } from '../project/project';
|
||||||
|
|
||||||
const repositoryTypes = [
|
|
||||||
{ key: '0', description: 'REPOSITORY.MY_REPOSITORY' },
|
|
||||||
{ key: '1', description: 'REPOSITORY.PUBLIC_REPOSITORY' }
|
|
||||||
];
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
selector: 'repository',
|
selector: 'repository',
|
||||||
@ -31,8 +26,7 @@ export class RepositoryComponent implements OnInit {
|
|||||||
changedRepositories: Repository[];
|
changedRepositories: Repository[];
|
||||||
|
|
||||||
projectId: number;
|
projectId: number;
|
||||||
repositoryTypes = repositoryTypes;
|
|
||||||
currentRepositoryType: {};
|
|
||||||
lastFilteredRepoName: string;
|
lastFilteredRepoName: string;
|
||||||
|
|
||||||
page: number = 1;
|
page: number = 1;
|
||||||
@ -80,7 +74,6 @@ export class RepositoryComponent implements OnInit {
|
|||||||
if(resolverData) {
|
if(resolverData) {
|
||||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
}
|
}
|
||||||
this.currentRepositoryType = this.repositoryTypes[0];
|
|
||||||
this.lastFilteredRepoName = '';
|
this.lastFilteredRepoName = '';
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
}
|
}
|
||||||
@ -108,10 +101,6 @@ export class RepositoryComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
doFilterRepositoryByType(type: string) {
|
|
||||||
this.currentRepositoryType = this.repositoryTypes.find(r => r.key == type);
|
|
||||||
}
|
|
||||||
|
|
||||||
doSearchRepoNames(repoName: string) {
|
doSearchRepoNames(repoName: string) {
|
||||||
this.lastFilteredRepoName = repoName;
|
this.lastFilteredRepoName = repoName;
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<a [routerLink]="['/harbor', 'projects', projectId, 'repository']">< {{'REPOSITORY.REPOSITORIES' | translate}}</a>
|
<a *ngIf="hasSignedIn" [routerLink]="['/harbor', 'projects', projectId, 'repository']">< {{'REPOSITORY.REPOSITORIES' | translate}}</a>
|
||||||
|
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'SEARCH.BACK' | translate}}</a>
|
||||||
|
|
||||||
<h2 class="sub-header-title">{{repoName}} <span class="badge">{{tags ? tags.length : 0}}</span></h2>
|
<h2 class="sub-header-title">{{repoName}} <span class="badge">{{tags ? tags.length : 0}}</span></h2>
|
||||||
<clr-datagrid>
|
<clr-datagrid>
|
||||||
<clr-dg-column>{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
||||||
|
@ -36,6 +36,8 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
withNotary: boolean;
|
withNotary: boolean;
|
||||||
|
|
||||||
|
hasSignedIn: boolean;
|
||||||
|
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -74,8 +76,8 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.hasSignedIn = (this.session.getCurrentUser() !== null);
|
||||||
let resolverData = this.route.snapshot.data;
|
let resolverData = this.route.snapshot.data;
|
||||||
console.log(JSON.stringify(resolverData));
|
|
||||||
if(resolverData) {
|
if(resolverData) {
|
||||||
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
<h3 style="margin-top: 0px;">{{'REPOSITORY.POP_REPOS' | translate}}</h3>
|
<h3 style="margin-top: 0px;">{{'REPOSITORY.POP_REPOS' | translate}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<list-repository [repositories]="topRepos" [mode]="listMode"></list-repository>
|
<list-repository-ro [repositories]="topRepos"></list-repository-ro>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -29,8 +29,8 @@ export class FilterComponent implements OnInit{
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.filterTerms
|
this.filterTerms
|
||||||
.debounceTime(300)
|
.debounceTime(500)
|
||||||
.distinctUntilChanged()
|
//.distinctUntilChanged()
|
||||||
.subscribe(terms => {
|
.subscribe(terms => {
|
||||||
this.filterEvt.emit(terms);
|
this.filterEvt.emit(terms);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
<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>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
||||||
|
<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>{{p.repo_count}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{p.creation_time}}</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
@ -0,0 +1,37 @@
|
|||||||
|
import { Component, EventEmitter, Output, Input } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||||
|
|
||||||
|
import { Project } from '../../project/project';
|
||||||
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
moduleId: module.id,
|
||||||
|
selector: 'list-project-ro',
|
||||||
|
templateUrl: 'list-project-ro.component.html'
|
||||||
|
})
|
||||||
|
export class ListProjectROComponent {
|
||||||
|
@Input() projects: Project[];
|
||||||
|
|
||||||
|
@Input() totalPage: number;
|
||||||
|
@Input() totalRecordCount: number;
|
||||||
|
pageOffset: number = 1;
|
||||||
|
|
||||||
|
@Output() paginate = new EventEmitter<State>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private searchTrigger: SearchTriggerService,
|
||||||
|
private router: Router) { }
|
||||||
|
|
||||||
|
goToLink(proId: number): void {
|
||||||
|
this.searchTrigger.closeSearch(true);
|
||||||
|
|
||||||
|
let linkUrl = ['harbor', 'projects', proId, 'repository'];
|
||||||
|
this.router.navigate(linkUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(state: State) {
|
||||||
|
this.paginate.emit(state);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<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 *ngFor="let r of repositories" [clrDgItem]='r'>
|
||||||
|
<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>
|
||||||
|
{{totalRecordCount || (repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
@ -0,0 +1,40 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { Router, NavigationExtras } from '@angular/router';
|
||||||
|
import { Repository } from '../../repository/repository';
|
||||||
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'list-repository-ro',
|
||||||
|
templateUrl: 'list-repository-ro.component.html'
|
||||||
|
})
|
||||||
|
export class ListRepositoryROComponent {
|
||||||
|
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() repositories: Repository[];
|
||||||
|
|
||||||
|
@Input() totalPage: number;
|
||||||
|
@Input() totalRecordCount: number;
|
||||||
|
@Output() paginate = new EventEmitter<State>();
|
||||||
|
pageOffset: number = 1;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private searchTrigger: SearchTriggerService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { Directive } from '@angular/core';
|
import { Directive } from '@angular/core';
|
||||||
import { ValidatorFn, AbstractControl, Validator, NG_VALIDATORS, Validators } from '@angular/forms';
|
import { ValidatorFn, AbstractControl, Validator, NG_VALIDATORS, Validators } from '@angular/forms';
|
||||||
|
|
||||||
export const portNumbers = /[\d]+/;
|
export const portNumbers = /^[\d]{1,5}$/;
|
||||||
|
|
||||||
export function portValidator(): ValidatorFn {
|
export function portValidator(): ValidatorFn {
|
||||||
return (control: AbstractControl): { [key: string]: any } => {
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
|
@ -19,15 +19,10 @@ export class AuthCheckGuard implements CanActivate, CanActivateChild {
|
|||||||
private appConfigService: AppConfigService) { }
|
private appConfigService: AppConfigService) { }
|
||||||
|
|
||||||
private isGuest(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
private isGuest(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||||
let queryParams = route.queryParams;
|
const proRegExp = /\/harbor\/projects\/[\d]+\/.+/i;
|
||||||
if (queryParams) {
|
const libRegExp = /\/harbor\/tags\/[\d]+\/.+/i;
|
||||||
if (queryParams["guest"]) {
|
if (proRegExp.test(state.url) || libRegExp.test(state.url)) {
|
||||||
let proRegExp = /\/harbor\/projects\/[\d]+\/.+/i;
|
return true;
|
||||||
const libRegExp = /\/harbor\/tags\/[\d]+\/.+/i;
|
|
||||||
if (proRegExp.test(state.url)|| libRegExp.test(state.url)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -42,8 +37,8 @@ export class AuthCheckGuard implements CanActivate, CanActivateChild {
|
|||||||
this.appConfigService.saveAdmiralEndpoint(queryParams[AdmiralQueryParamKey]);
|
this.appConfigService.saveAdmiralEndpoint(queryParams[AdmiralQueryParamKey]);
|
||||||
//Remove the query parameter key pair and redirect
|
//Remove the query parameter key pair and redirect
|
||||||
let keyRemovedUrl = maintainUrlQueryParmas(state.url, AdmiralQueryParamKey, undefined);
|
let keyRemovedUrl = maintainUrlQueryParmas(state.url, AdmiralQueryParamKey, undefined);
|
||||||
if(!/[?]{1}.+/i.test(keyRemovedUrl)){
|
if (!/[?]{1}.+/i.test(keyRemovedUrl)) {
|
||||||
keyRemovedUrl = keyRemovedUrl.replace('?','');
|
keyRemovedUrl = keyRemovedUrl.replace('?', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.router.navigateByUrl(keyRemovedUrl);
|
this.router.navigateByUrl(keyRemovedUrl);
|
||||||
|
@ -56,3 +56,4 @@ export const enum ConfirmationState {
|
|||||||
|
|
||||||
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
||||||
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||||
|
export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' };
|
||||||
|
@ -34,6 +34,9 @@ import { SignInGuard } from './route/sign-in-guard-activate.service';
|
|||||||
import { LeavingConfigRouteDeactivate } from './route/leaving-config-deactivate.service';
|
import { LeavingConfigRouteDeactivate } from './route/leaving-config-deactivate.service';
|
||||||
import { MemberGuard } from './route/member-guard-activate.service';
|
import { MemberGuard } from './route/member-guard-activate.service';
|
||||||
|
|
||||||
|
import { ListProjectROComponent } from './list-project-ro/list-project-ro.component';
|
||||||
|
import { ListRepositoryROComponent } from './list-repository-ro/list-repository-ro.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CoreModule,
|
CoreModule,
|
||||||
@ -53,7 +56,9 @@ import { MemberGuard } from './route/member-guard-activate.service';
|
|||||||
PageNotFoundComponent,
|
PageNotFoundComponent,
|
||||||
AboutDialogComponent,
|
AboutDialogComponent,
|
||||||
StatisticsComponent,
|
StatisticsComponent,
|
||||||
StatisticsPanelComponent
|
StatisticsPanelComponent,
|
||||||
|
ListProjectROComponent,
|
||||||
|
ListRepositoryROComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CoreModule,
|
CoreModule,
|
||||||
@ -70,7 +75,9 @@ import { MemberGuard } from './route/member-guard-activate.service';
|
|||||||
PageNotFoundComponent,
|
PageNotFoundComponent,
|
||||||
AboutDialogComponent,
|
AboutDialogComponent,
|
||||||
StatisticsComponent,
|
StatisticsComponent,
|
||||||
StatisticsPanelComponent
|
StatisticsPanelComponent,
|
||||||
|
ListProjectROComponent,
|
||||||
|
ListRepositoryROComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SessionService,
|
SessionService,
|
||||||
|
Loading…
Reference in New Issue
Block a user