mirror of
https://github.com/goharbor/harbor
synced 2025-04-22 21:32:36 +00:00
add customSkin about #3241
This commit is contained in:
parent
eacb8e5658
commit
a5a62e1b19
@ -8,7 +8,8 @@
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"images",
|
||||
"favicon.ico"
|
||||
"favicon.ico",
|
||||
"setting.json"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
|
@ -24,7 +24,7 @@
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
position: relative;
|
||||
margin-left: 1px;
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
.login-wrapper-override {
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="login-wrapper login-wrapper-override">
|
||||
<div class="login-wrapper login-wrapper-override" [ngStyle]="{'background-image': 'url(' + customLoginBgImg + ')'}">
|
||||
<form #signInForm="ngForm" class="login">
|
||||
<label class="title">{{appTitle | translate}}<span class="trademark tm-font">™</span>
|
||||
</label>
|
||||
@ -12,7 +12,7 @@
|
||||
{{ 'TOOLTIP.SIGN_IN_USERNAME' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left">
|
||||
<label for="username" aria-haspopup="true" role="tpopular-repo-wrapperooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left">
|
||||
<input class="password" type="password" required
|
||||
[(ngModel)]="signInCredential.password"
|
||||
name="login_password" id="login_password" placeholder='{{"PLACEHOLDER.SIGN_IN_PWD" | translate}}'
|
||||
|
@ -28,6 +28,7 @@ import { AppConfig } from '../../app-config';
|
||||
import { User } from '../../user/user';
|
||||
|
||||
import { CookieService, CookieOptions } from 'ngx-cookie';
|
||||
import {SkinableConfig} from "../../skinable-config.service";
|
||||
|
||||
//Define status flags for signing in states
|
||||
export const signInStatusNormal = 0;
|
||||
@ -48,6 +49,8 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
||||
//Remeber me indicator
|
||||
rememberMe: boolean = false;
|
||||
rememberedName: string = "";
|
||||
|
||||
customLoginBgImg: string;
|
||||
//Form reference
|
||||
signInForm: NgForm;
|
||||
@ViewChild('signInForm') currentForm: NgForm;
|
||||
@ -68,10 +71,16 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
||||
private session: SessionService,
|
||||
private route: ActivatedRoute,
|
||||
private appConfigService: AppConfigService,
|
||||
private cookie: CookieService
|
||||
) { }
|
||||
private cookie: CookieService,
|
||||
private skinableConfig: SkinableConfig) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// custom skin
|
||||
let customSkinObj = this.skinableConfig.getSkinConfig();
|
||||
if (customSkinObj && customSkinObj.loginBgImg) {
|
||||
this.customLoginBgImg = customSkinObj.loginBgImg;
|
||||
}
|
||||
|
||||
//Make sure the updated configuration can be loaded
|
||||
this.appConfigService.load()
|
||||
.then(updatedConfig => this.appConfig = updatedConfig);
|
||||
|
@ -23,9 +23,13 @@ import { ConfigurationModule } from './config/config.module';
|
||||
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { AppConfigService } from './app-config.service';
|
||||
import {SkinableConfig} from "./skinable-config.service";
|
||||
|
||||
export function initConfig(configService: AppConfigService) {
|
||||
return () => configService.load();
|
||||
export function initConfig(configService: AppConfigService, skinableService: SkinableConfig) {
|
||||
return () => {
|
||||
skinableService.getCustomFile();
|
||||
configService.load();
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentLanguage(translateService: TranslateService) {
|
||||
@ -41,14 +45,15 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
||||
BaseModule,
|
||||
AccountModule,
|
||||
HarborRoutingModule,
|
||||
ConfigurationModule
|
||||
ConfigurationModule,
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
SkinableConfig,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initConfig,
|
||||
deps: [ AppConfigService ],
|
||||
deps: [ AppConfigService, SkinableConfig],
|
||||
multi: true
|
||||
},
|
||||
{
|
||||
|
@ -23,6 +23,8 @@ import { AppConfigService } from '../../app-config.service';
|
||||
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {SkinableConfig} from "../../skinable-config.service";
|
||||
|
||||
const deBounceTime = 500; //ms
|
||||
|
||||
@ -43,16 +45,31 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
isResPanelOpened: boolean = false;
|
||||
searchTerm: string = "";
|
||||
|
||||
//Placeholder text
|
||||
placeholderText: string = "GLOBAL_SEARCH.PLACEHOLDER";
|
||||
placeholderText: string;
|
||||
|
||||
constructor(
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private router: Router,
|
||||
private appConfigService: AppConfigService) { }
|
||||
private appConfigService: AppConfigService,
|
||||
private translate: TranslateService,
|
||||
private skinableConfig: SkinableConfig) {
|
||||
}
|
||||
|
||||
//Implement ngOnIni
|
||||
ngOnInit(): void {
|
||||
//custom skin
|
||||
let customSkinObj = this.skinableConfig.getProjects();
|
||||
if (customSkinObj && customSkinObj.projectName) {
|
||||
this.translate.get('GLOBAL_SEARCH.PLACEHOLDER', {'param': customSkinObj.projectName}).subscribe(res => {
|
||||
//Placeholder text
|
||||
this.placeholderText = res;
|
||||
});
|
||||
}else {
|
||||
this.translate.get('GLOBAL_SEARCH.PLACEHOLDER', {'param': 'Harbor'}).subscribe(res => {
|
||||
//Placeholder text
|
||||
this.placeholderText = res;
|
||||
});
|
||||
}
|
||||
|
||||
this.searchSub = this.searchTerms
|
||||
.debounceTime(deBounceTime)
|
||||
//.distinctUntilChanged()
|
||||
|
@ -1,8 +1,9 @@
|
||||
<clr-header class="header-5 header">
|
||||
<clr-header class="header-5 header" [ngStyle]='{"background-color": customStyle?.headerBgColor?customStyle?.headerBgColor:"#004a70" }'>
|
||||
<div class="branding">
|
||||
<a href="javascript:void(0)" class="nav-link" (click)="homeAction()">
|
||||
<clr-icon shape="vm-bug"></clr-icon>
|
||||
<span class="title">{{ appTitle | translate}}</span>
|
||||
<clr-icon shape="vm-bug" *ngIf="!customStyle?.headerLogo"></clr-icon>
|
||||
<img [attr.src]="customStyle?.headerLogo" *ngIf="customStyle?.headerLogo" style="width: 36px;height: 36px; object-fit: fill;">
|
||||
<span class="title">{{customProjectName?.projectName? customProjectName?.projectName:(appTitle | translate)}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-nav">
|
||||
|
@ -26,6 +26,7 @@ import { supportedLangs, enLang, languageNames, CommonRoutes } from '../../share
|
||||
import { AppConfigService } from '../../app-config.service';
|
||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import {SkinableConfig} from "../../skinable-config.service";
|
||||
|
||||
@Component({
|
||||
selector: 'navigator',
|
||||
@ -40,6 +41,8 @@ export class NavigatorComponent implements OnInit {
|
||||
|
||||
selectedLang: string = enLang;
|
||||
appTitle: string = 'APP_TITLE.HARBOR';
|
||||
customStyle: {[key: string]: any};
|
||||
customProjectName: {[key: string]: any};
|
||||
|
||||
constructor(
|
||||
private session: SessionService,
|
||||
@ -48,11 +51,20 @@ export class NavigatorComponent implements OnInit {
|
||||
private cookie: CookieService,
|
||||
private appConfigService: AppConfigService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private searchTrigger: SearchTriggerService) {
|
||||
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private skinableConfig: SkinableConfig) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// custom skin
|
||||
let customSkinObj = this.skinableConfig.getSkinConfig();
|
||||
if (customSkinObj) {
|
||||
if (customSkinObj.projects) {
|
||||
this.customProjectName = customSkinObj.projects;
|
||||
}
|
||||
this.customStyle = customSkinObj;
|
||||
}
|
||||
|
||||
this.selectedLang = this.translate.currentLang;
|
||||
this.translate.onLangChange.subscribe((langChange: {lang: string}) => {
|
||||
this.selectedLang = langChange.lang;
|
||||
|
@ -1,15 +1,15 @@
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalClosable]="false" [clrModalStaticBackdrop]="false">
|
||||
<h3 class="modal-title margin-left-override">vmware</h3>
|
||||
<h3 class="modal-title margin-left-override">{{customName?.companyName? customName?.companyName : 'vmware'}}</h3>
|
||||
<div class="modal-body margin-left-override">
|
||||
<div>
|
||||
<h2>Harbor</h2>
|
||||
<h2>{{customName?.projectName? customName?.projectName : ('APP_TITLE.HARBOR' | translate)}}</h2>
|
||||
</div>
|
||||
<div style="height: 12px;"></div>
|
||||
<div>
|
||||
<span class="p5 about-version">{{'ABOUT.VERSION' | translate}} {{version}}</span>
|
||||
</div>
|
||||
<div style="height: 12px;"></div>
|
||||
<div>
|
||||
<div *ngIf="!customIntroduction">
|
||||
<p class="p5">{{'ABOUT.COPYRIGHT' | translate}} <a href="http://www.vmware.com/go/patents" target="_blank" class="about-text-link">http://www.vmware.com/go/patents</a> {{'ABOUT.COPYRIGHT_SUFIX' | translate}}</p>
|
||||
<p class="p5">{{'ABOUT.TRADEMARK' | translate}}</p>
|
||||
<p class="p5">
|
||||
@ -17,6 +17,9 @@
|
||||
</p>
|
||||
<div style="height: 24px;"></div>
|
||||
</div>
|
||||
<div *ngIf="customIntroduction">
|
||||
<p class="p5">{{customIntroduction}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer margin-left-override">
|
||||
<button type="button" class="btn btn-primary" (click)="close()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
|
@ -11,20 +11,39 @@
|
||||
// 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 } from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
import { AppConfigService } from '../../app-config.service';
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {SkinableConfig} from "../../skinable-config.service";
|
||||
|
||||
@Component({
|
||||
selector: 'about-dialog',
|
||||
templateUrl: "about-dialog.component.html",
|
||||
styleUrls: ["about-dialog.component.css"]
|
||||
})
|
||||
export class AboutDialogComponent {
|
||||
export class AboutDialogComponent implements OnInit{
|
||||
opened: boolean = false;
|
||||
build: string = "4276418";
|
||||
customIntroduction: string;
|
||||
customName: {[key: string]: any };
|
||||
|
||||
constructor(private appConfigService: AppConfigService) { }
|
||||
constructor(private appConfigService: AppConfigService,
|
||||
private translate: TranslateService,
|
||||
private skinableConfig: SkinableConfig) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// custom skin
|
||||
let customSkinObj = this.skinableConfig.getProjects();
|
||||
if (customSkinObj) {
|
||||
let selectedLang = this.translate.currentLang;
|
||||
this.customName = customSkinObj;
|
||||
if (customSkinObj.introduction && customSkinObj.introduction[selectedLang]) {
|
||||
this.customIntroduction = customSkinObj.introduction[selectedLang];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get version(): string {
|
||||
let appConfig = this.appConfigService.getConfig();
|
||||
|
34
src/ui_ng/src/app/skinable-config.service.ts
Normal file
34
src/ui_ng/src/app/skinable-config.service.ts
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
import {Injectable} from "@angular/core";
|
||||
import {Http} from "@angular/http";
|
||||
import {Observable} from "rxjs/Observable";
|
||||
/**
|
||||
* Created by pengf on 9/15/2017.
|
||||
*/
|
||||
|
||||
@Injectable()
|
||||
export class SkinableConfig {
|
||||
customSkinData: {[key: string]: any};
|
||||
constructor(private http: Http) {}
|
||||
|
||||
public getCustomFile(): Promise<any> {
|
||||
return this.http.get('../setting.json')
|
||||
.toPromise()
|
||||
.then(response => { this.customSkinData = response.json(); return this.customSkinData; })
|
||||
.catch(error => {
|
||||
console.error('custom skin json file load failed');
|
||||
});
|
||||
}
|
||||
|
||||
public getSkinConfig() {
|
||||
return this.customSkinData;
|
||||
}
|
||||
|
||||
public getProjects() {
|
||||
if (this.customSkinData) {
|
||||
return this.customSkinData.projects;
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -86,7 +86,7 @@
|
||||
"LOGOUT": "Log Out"
|
||||
},
|
||||
"GLOBAL_SEARCH": {
|
||||
"PLACEHOLDER": "Search Harbor...",
|
||||
"PLACEHOLDER": "Search {{param}}...",
|
||||
"PLACEHOLDER_VIC": "Search Registry..."
|
||||
},
|
||||
"SIDE_NAV": {
|
||||
|
@ -86,7 +86,7 @@
|
||||
"LOGOUT": "Cerrar sesión"
|
||||
},
|
||||
"GLOBAL_SEARCH": {
|
||||
"PLACEHOLDER": "Buscar en Harbor...",
|
||||
"PLACEHOLDER": "Buscar en {{param}}...",
|
||||
"PLACEHOLDER_VIC": "Buscar en el registro..."
|
||||
},
|
||||
"SIDE_NAV": {
|
||||
|
@ -86,7 +86,7 @@
|
||||
"LOGOUT": "退出"
|
||||
},
|
||||
"GLOBAL_SEARCH": {
|
||||
"PLACEHOLDER": "搜索 Harbor...",
|
||||
"PLACEHOLDER": "搜索 {{param}}...",
|
||||
"PLACEHOLDER_VIC": "搜索 Registry..."
|
||||
},
|
||||
"SIDE_NAV": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user