mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
Merge remote-tracking branch 'upstream/dev' into 170329_send_email
This commit is contained in:
commit
4236d47653
@ -74,8 +74,8 @@ before_script:
|
||||
- sudo chmod 777 /tmp/registry.db
|
||||
|
||||
script:
|
||||
- sudo mkdir -p /harbor_storage/ca_download
|
||||
- sudo mv ./tests/ca.crt /harbor_storage/ca_download
|
||||
- sudo mkdir -p /etc/ui/ca/
|
||||
- sudo mv ./tests/ca.crt /etc/ui/ca/
|
||||
- sudo mkdir -p /harbor
|
||||
- sudo mv ./VERSION /harbor/VERSION
|
||||
- sudo service mysql stop
|
||||
|
@ -33,7 +33,6 @@ UI_SECRET=$ui_secret
|
||||
JOBSERVICE_SECRET=$jobservice_secret
|
||||
TOKEN_EXPIRATION=$token_expiration
|
||||
CFG_EXPIRATION=5
|
||||
USE_COMPRESSED_JS=$use_compressed_js
|
||||
GODEBUG=netdns=cgo
|
||||
ADMIRAL_URL=$admiral_url
|
||||
WITH_NOTARY=$with_notary
|
||||
|
@ -69,6 +69,7 @@ services:
|
||||
- ../common/config/ui/app.conf:/etc/ui/app.conf
|
||||
- ../common/config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
- /data/secretkey:/etc/ui/key
|
||||
- /data/ca_download/:/etc/ui/ca/
|
||||
depends_on:
|
||||
- log
|
||||
- adminserver
|
||||
|
@ -76,6 +76,7 @@ services:
|
||||
- ./common/config/ui/app.conf:/etc/ui/app.conf
|
||||
- ./common/config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
- /data/secretkey:/etc/ui/key
|
||||
- /data/ca_download/:/etc/ui/ca/
|
||||
networks:
|
||||
- harbor
|
||||
depends_on:
|
||||
|
@ -11,10 +11,6 @@ ui_url_protocol = http
|
||||
#The password for the root user of mysql db, change this before any production use.
|
||||
db_password = root123
|
||||
|
||||
#Determine whether the UI should use compressed js files.
|
||||
#For production, set it to on. For development, set it to off.
|
||||
use_compressed_js = on
|
||||
|
||||
#Maximum number of job workers in job service
|
||||
max_job_workers = 3
|
||||
|
||||
|
42
make/prepare
42
make/prepare
@ -138,7 +138,6 @@ ldap_scope = rcp.get("configuration", "ldap_scope")
|
||||
ldap_timeout = rcp.get("configuration", "ldap_timeout")
|
||||
db_password = rcp.get("configuration", "db_password")
|
||||
self_registration = rcp.get("configuration", "self_registration")
|
||||
use_compressed_js = rcp.get("configuration", "use_compressed_js")
|
||||
if protocol == "https":
|
||||
cert_path = rcp.get("configuration", "ssl_cert")
|
||||
cert_key_path = rcp.get("configuration", "ssl_cert_key")
|
||||
@ -223,8 +222,7 @@ render(os.path.join(templates_dir, "adminserver", "env"),
|
||||
jobservice_secret=jobservice_secret,
|
||||
token_expiration=token_expiration,
|
||||
admiral_url=admiral_url,
|
||||
with_notary=args.notary_mode,
|
||||
use_compressed_js=use_compressed_js
|
||||
with_notary=args.notary_mode
|
||||
)
|
||||
|
||||
render(os.path.join(templates_dir, "ui", "env"),
|
||||
@ -319,27 +317,33 @@ if args.notary_mode:
|
||||
shutil.rmtree(os.path.join(notary_config_dir, "mysql-initdb.d"))
|
||||
shutil.copytree(os.path.join(notary_temp_dir, "mysql-initdb.d"), os.path.join(notary_config_dir, "mysql-initdb.d"))
|
||||
if customize_crt == 'on' and openssl_installed():
|
||||
temp_cert_dir = os.path.join(base_dir, "cert_tmp")
|
||||
if not os.path.exists(temp_cert_dir):
|
||||
os.makedirs(temp_cert_dir)
|
||||
ca_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=Self-signed by VMware, Inc."
|
||||
cert_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=notarysigner"
|
||||
signer_ca_cert = os.path.join(temp_cert_dir, "notary-signer-ca.crt")
|
||||
signer_ca_key = os.path.join(temp_cert_dir, "notary-signer-ca.key")
|
||||
signer_cert_path = os.path.join(temp_cert_dir, "notary-signer.crt")
|
||||
signer_key_path = os.path.join(temp_cert_dir, "notary-signer.key")
|
||||
create_root_cert(ca_subj, key_path=signer_ca_key, cert_path=signer_ca_cert)
|
||||
create_cert(cert_subj, signer_ca_key, signer_ca_cert, key_path=signer_key_path, cert_path=signer_cert_path)
|
||||
print("Copying certs for notary signer")
|
||||
shutil.copy2(signer_cert_path, notary_config_dir)
|
||||
shutil.copy2(signer_key_path, notary_config_dir)
|
||||
shutil.copy2(signer_ca_cert, notary_config_dir)
|
||||
try:
|
||||
temp_cert_dir = os.path.join(base_dir, "cert_tmp")
|
||||
if not os.path.exists(temp_cert_dir):
|
||||
os.makedirs(temp_cert_dir)
|
||||
ca_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=Self-signed by VMware, Inc."
|
||||
cert_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=notarysigner"
|
||||
signer_ca_cert = os.path.join(temp_cert_dir, "notary-signer-ca.crt")
|
||||
signer_ca_key = os.path.join(temp_cert_dir, "notary-signer-ca.key")
|
||||
signer_cert_path = os.path.join(temp_cert_dir, "notary-signer.crt")
|
||||
signer_key_path = os.path.join(temp_cert_dir, "notary-signer.key")
|
||||
create_root_cert(ca_subj, key_path=signer_ca_key, cert_path=signer_ca_cert)
|
||||
create_cert(cert_subj, signer_ca_key, signer_ca_cert, key_path=signer_key_path, cert_path=signer_cert_path)
|
||||
print("Copying certs for notary signer")
|
||||
shutil.copy2(signer_cert_path, notary_config_dir)
|
||||
shutil.copy2(signer_key_path, notary_config_dir)
|
||||
shutil.copy2(signer_ca_cert, notary_config_dir)
|
||||
finally:
|
||||
srl_tmp = os.path.join(os.getcwd(), ".srl")
|
||||
if os.path.isfile(srl_tmp):
|
||||
os.remove(srl_tmp)
|
||||
if os.path.isdir(temp_cert_dir):
|
||||
shutil.rmtree(temp_cert_dir, True)
|
||||
else:
|
||||
print("Copying certs for notary signer")
|
||||
shutil.copy2(os.path.join(notary_temp_dir, "notary-signer.crt"), notary_config_dir)
|
||||
shutil.copy2(os.path.join(notary_temp_dir, "notary-signer.key"), notary_config_dir)
|
||||
shutil.copy2(os.path.join(notary_temp_dir, "notary-signer-ca.crt"), notary_config_dir)
|
||||
|
||||
shutil.copy2(os.path.join(registry_config_dir, "root.crt"), notary_config_dir)
|
||||
print("Copying notary signer configuration file")
|
||||
shutil.copy2(os.path.join(notary_temp_dir, "signer-config.json"), notary_config_dir)
|
||||
|
@ -98,10 +98,6 @@ var (
|
||||
env: "TOKEN_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
common.UseCompressedJS: &parser{
|
||||
env: "USE_COMPRESSED_JS",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.CfgExpiration: &parser{
|
||||
env: "CFG_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
@ -132,11 +128,6 @@ var (
|
||||
env: "MAX_JOB_WORKERS",
|
||||
parse: parseStringToInt,
|
||||
},
|
||||
// TODO remove this config?
|
||||
common.UseCompressedJS: &parser{
|
||||
env: "USE_COMPRESSED_JS",
|
||||
parse: parseStringToBool,
|
||||
},
|
||||
common.CfgExpiration: &parser{
|
||||
env: "CFG_EXPIRATION",
|
||||
parse: parseStringToInt,
|
||||
|
@ -58,7 +58,6 @@ const (
|
||||
TokenExpiration = "token_expiration"
|
||||
CfgExpiration = "cfg_expiration"
|
||||
JobLogDir = "job_log_dir"
|
||||
UseCompressedJS = "use_compressed_js"
|
||||
AdminInitialPassword = "admin_initial_password"
|
||||
AdmiralEndpoint = "admiral_url"
|
||||
WithNotary = "with_notary"
|
||||
|
@ -92,7 +92,6 @@ type SystemCfg struct {
|
||||
MaxJobWorkers int `json:"max_job_workers"`
|
||||
JobLogDir string `json:"job_log_dir"`
|
||||
InitialAdminPwd string `json:"initial_admin_pwd,omitempty"`
|
||||
CompressJS bool `json:"compress_js"` //TODO remove
|
||||
TokenExpiration int `json:"token_expiration"` // in minute
|
||||
SecretKey string `json:"secret_key,omitempty"`
|
||||
CfgExpiration int `json:"cfg_expiration"`
|
||||
|
@ -63,7 +63,6 @@ var adminServerLdapTestConfig = map[string]interface{}{
|
||||
// config.TokenExpiration: 30,
|
||||
common.CfgExpiration: 5,
|
||||
// config.JobLogDir: "/var/log/jobs",
|
||||
// config.UseCompressedJS: true,
|
||||
common.AdminInitialPassword: "password",
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,6 @@ var adminServerDefaultConfig = map[string]interface{}{
|
||||
common.MaxJobWorkers: 3,
|
||||
common.TokenExpiration: 30,
|
||||
common.CfgExpiration: 5,
|
||||
common.UseCompressedJS: true,
|
||||
common.AdminInitialPassword: "password",
|
||||
common.AdmiralEndpoint: "http://www.vmware.com",
|
||||
common.WithNotary: false,
|
||||
|
@ -63,7 +63,6 @@ var (
|
||||
common.TokenExpiration,
|
||||
common.CfgExpiration,
|
||||
common.JobLogDir,
|
||||
common.UseCompressedJS,
|
||||
common.AdminInitialPassword,
|
||||
}
|
||||
|
||||
@ -81,7 +80,6 @@ var (
|
||||
common.EmailSSL,
|
||||
common.SelfRegistration,
|
||||
common.VerifyRemoteCert,
|
||||
common.UseCompressedJS,
|
||||
}
|
||||
|
||||
passwordKeys = []string{
|
||||
|
@ -20,7 +20,7 @@ type SystemInfoAPI struct {
|
||||
isAdmin bool
|
||||
}
|
||||
|
||||
const defaultRootCert = "/harbor_storage/ca_download/ca.crt"
|
||||
const defaultRootCert = "/etc/ui/ca/ca.crt"
|
||||
const harborVersionFile = "/harbor/VERSION"
|
||||
|
||||
//SystemInfo models for system info.
|
||||
|
@ -48,7 +48,6 @@ var adminServerLdapTestConfig = map[string]interface{}{
|
||||
// config.TokenExpiration: 30,
|
||||
common.CfgExpiration: 5,
|
||||
// config.JobLogDir: "/var/log/jobs",
|
||||
// config.UseCompressedJS: true,
|
||||
common.AdminInitialPassword: "password",
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
<label for="account_settings_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("account_settings_email")'>
|
||||
<input name="account_settings_email" type="text" #eamilInput="ngModel" [(ngModel)]="account.email"
|
||||
required
|
||||
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$' id="account_settings_email" size="30"
|
||||
pattern='^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$' id="account_settings_email" size="30"
|
||||
(input)='handleValidation("account_settings_email", false)'
|
||||
(focusout)='handleValidation("account_settings_email", true)'>
|
||||
<span class="tooltip-content">
|
||||
|
@ -10,7 +10,7 @@
|
||||
<label for="reset_pwd_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="validationState === false">
|
||||
<input name="reset_pwd_email" type="text" #eamilInput="ngModel" [(ngModel)]="email"
|
||||
required
|
||||
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$'
|
||||
pattern='^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'
|
||||
id="reset_pwd_email"
|
||||
size="40"
|
||||
(input)="handleValidation(true)"
|
||||
|
@ -22,7 +22,7 @@
|
||||
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("newPassword")'>
|
||||
<input type="password" id="newPassword"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="newPassword"
|
||||
[(ngModel)]="newPwd"
|
||||
#newPassInput="ngModel" size="42"
|
||||
@ -39,7 +39,7 @@
|
||||
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("reNewPassword")'>
|
||||
<input type="password" id="reNewPassword"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="reNewPassword"
|
||||
[(ngModel)]="reNewPwd"
|
||||
#reNewPassInput="ngModel" size="42"
|
||||
|
@ -9,7 +9,7 @@
|
||||
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("newPassword") === false'>
|
||||
<input type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="newPassword"
|
||||
[(ngModel)]="password"
|
||||
#newPassInput="ngModel"
|
||||
@ -26,7 +26,7 @@
|
||||
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("reNewPassword") === false'>
|
||||
<input type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="reNewPassword"
|
||||
[(ngModel)]="ngModel"
|
||||
#reNewPassInput
|
||||
|
@ -1,7 +1,6 @@
|
||||
<div class="login-wrapper login-wrapper-override">
|
||||
<form #signInForm="ngForm" class="login">
|
||||
<label class="title">
|
||||
VMware Harbor<span class="trademark">™</span>
|
||||
<label class="title">{{appTitle | translate}}<span class="trademark">™</span>
|
||||
</label>
|
||||
<div class="login-group">
|
||||
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="userNameInput.invalid && (userNameInput.dirty || userNameInput.touched)">
|
||||
|
@ -80,6 +80,15 @@ export class SignInComponent implements AfterViewChecked, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
//App title
|
||||
public get appTitle(): string {
|
||||
if(this.appConfig && this.appConfig.with_admiral){
|
||||
return "APP_TITLE.VIC";
|
||||
}
|
||||
|
||||
return "APP_TITLE.VMW_HARBOR";
|
||||
}
|
||||
|
||||
//For template accessing
|
||||
public get isError(): boolean {
|
||||
return this.signInStatus === signInStatusError;
|
||||
|
@ -85,6 +85,7 @@ export class SignUpComponent {
|
||||
}
|
||||
|
||||
confirmCancel(): void {
|
||||
this.opened = false;
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
@ -108,6 +109,7 @@ export class SignUpComponent {
|
||||
this.userService.addUser(u)
|
||||
.then(() => {
|
||||
this.onGoing = false;
|
||||
this.opened = false;
|
||||
this.modal.close();
|
||||
this.userCreation.emit(u);
|
||||
})
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, ReflectiveInjector, LOCALE_ID } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CookieService } from 'angular2-cookie/core';
|
||||
|
||||
import { supportedLangs, enLang } from './shared/shared.const';
|
||||
import { SessionService } from './shared/session.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'harbor-app',
|
||||
templateUrl: 'app.component.html'
|
||||
@ -14,6 +15,7 @@ export class AppComponent {
|
||||
private translate: TranslateService,
|
||||
private cookie: CookieService,
|
||||
private session: SessionService) {
|
||||
|
||||
translate.addLangs(supportedLangs);
|
||||
translate.setDefaultLang(enLang);
|
||||
|
||||
@ -21,7 +23,7 @@ export class AppComponent {
|
||||
let langSetting = this.cookie.get("harbor-lang");
|
||||
if (!langSetting || langSetting.trim() === "") {
|
||||
//Use browser lang
|
||||
langSetting = translate.getBrowserLang();
|
||||
langSetting = translate.getBrowserCultureLang().toLowerCase();
|
||||
}
|
||||
|
||||
let selectedLang = this.isLangMatch(langSetting, supportedLangs) ? langSetting : enLang;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, APP_INITIALIZER } from '@angular/core';
|
||||
import { NgModule, APP_INITIALIZER, LOCALE_ID } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { ClarityModule } from 'clarity-angular';
|
||||
@ -11,7 +11,7 @@ import { SharedModule } from './shared/shared.module';
|
||||
import { AccountModule } from './account/account.module';
|
||||
import { ConfigurationModule } from './config/config.module';
|
||||
|
||||
import { TranslateModule, TranslateLoader, MissingTranslationHandler } from "@ngx-translate/core";
|
||||
import { TranslateModule, TranslateLoader, TranslateService, MissingTranslationHandler } from "@ngx-translate/core";
|
||||
import { MyMissingTranslationHandler } from './i18n/missing-trans.handler';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { Http } from '@angular/http';
|
||||
@ -26,6 +26,10 @@ export function initConfig(configService: AppConfigService) {
|
||||
return () => configService.load();
|
||||
}
|
||||
|
||||
export function getCurrentLanguage(translateService: TranslateService) {
|
||||
return translateService.currentLang;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
@ -49,14 +53,19 @@ export function initConfig(configService: AppConfigService) {
|
||||
})
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initConfig,
|
||||
deps: [AppConfigService],
|
||||
AppConfigService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initConfig,
|
||||
deps: [ AppConfigService ],
|
||||
multi: true
|
||||
}],
|
||||
},
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: getCurrentLanguage,
|
||||
deps:[ TranslateService ]
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
export class AppModule {}
|
||||
|
@ -11,15 +11,15 @@
|
||||
</div>
|
||||
<global-search></global-search>
|
||||
<div class="header-actions">
|
||||
<clr-dropdown class="dropdown bottom-left">
|
||||
<clr-dropdown class="dropdown bottom-left" *ngIf="!isIntegrationMode">
|
||||
<button class="nav-icon" clrDropdownToggle style="width: 98px;">
|
||||
<clr-icon shape="world" style="left:-8px;"></clr-icon>
|
||||
<span style="padding-right: 8px;">{{currentLang}}</span>
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)='switchLanguage("en")' [class.lang-selected]='matchLang("en")'>English</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)='switchLanguage("zh")' [class.lang-selected]='matchLang("zh")'>中文简体</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)='switchLanguage("en-us")' [class.lang-selected]='matchLang("en")'>English</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)='switchLanguage("zh-cn")' [class.lang-selected]='matchLang("zh")'>中文简体</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-right'" class="dropdown" *ngIf="isSessionValid">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Output, EventEmitter, OnInit, Inject } from '@angular/core';
|
||||
import { Component, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { Router, NavigationExtras } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@ -27,7 +27,7 @@ export class NavigatorComponent implements OnInit {
|
||||
|
||||
private selectedLang: string = enLang;
|
||||
private appTitle: string = 'APP_TITLE.HARBOR';
|
||||
|
||||
|
||||
constructor(
|
||||
private session: SessionService,
|
||||
private router: Router,
|
||||
@ -35,7 +35,9 @@ export class NavigatorComponent implements OnInit {
|
||||
private cookie: CookieService,
|
||||
private appConfigService: AppConfigService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private searchTrigger: SearchTriggerService) { }
|
||||
private searchTrigger: SearchTriggerService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.selectedLang = this.translate.currentLang;
|
||||
@ -118,10 +120,10 @@ export class NavigatorComponent implements OnInit {
|
||||
this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN]);
|
||||
})
|
||||
.catch(error => {
|
||||
this.msgHandler.handleError(error);
|
||||
this.msgHandler.handleError(error);
|
||||
});
|
||||
//Confirm search result panel is close
|
||||
this.searchTrigger.closeSearch(true);
|
||||
this.searchTrigger.closeSearch(true);
|
||||
}
|
||||
|
||||
//Switch languages
|
||||
@ -133,8 +135,9 @@ export class NavigatorComponent implements OnInit {
|
||||
//TODO:
|
||||
console.error('Language ' + lang.trim() + ' is not suppoted');
|
||||
}
|
||||
//Try to switch backend lang
|
||||
//this.session.switchLanguage(lang).catch(error => console.error(error));
|
||||
setTimeout(()=>{
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
//Handle the home action
|
||||
|
@ -35,7 +35,7 @@
|
||||
<clr-dg-cell>{{l.repo_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.repo_tag}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.operation}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.op_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{totalRecordCount}} {{'AUDIT_LOG.ITEMS' | translate}}
|
||||
|
@ -30,7 +30,7 @@
|
||||
<clr-dg-cell>{{l.repo_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.repo_tag}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.operation}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{formatDateTime(l.op_time)}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>{{ (recentLogs ? recentLogs.length : 0) }} {{'AUDIT_LOG.ITEMS' | translate}}</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
|
@ -65,11 +65,6 @@ export class RecentLogComponent implements OnInit {
|
||||
this.retrieveLogs();
|
||||
}
|
||||
|
||||
public formatDateTime(dateTime: string) {
|
||||
let dt: Date = new Date(dateTime);
|
||||
return dt.toLocaleString();
|
||||
}
|
||||
|
||||
private retrieveLogs(): void {
|
||||
if (this.lines < 10) {
|
||||
this.lines = 10;
|
||||
|
@ -14,7 +14,7 @@
|
||||
<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.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button *ngIf="hasProjectAdminRole" class="btn btn-primary" (click)="openAddMemberModal()"><clr-icon shape="add"></clr-icon> {{'MEMBER.MEMBER' | translate }}</button>
|
||||
<button *ngIf="hasProjectAdminRole" class="btn btn-link" (click)="openAddMemberModal()"><clr-icon shape="add"></clr-icon> {{'MEMBER.MEMBER' | translate }}</button>
|
||||
<add-member [projectId]="projectId" (added)="addedMember($event)"></add-member>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
|
@ -1,33 +1,31 @@
|
||||
<div class="row">
|
||||
<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>
|
||||
<create-project (create)="createProject($event)"></create-project>
|
||||
</div>
|
||||
<div class="option-right">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
<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-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'PROJECT.PROJECT' | translate}}</button>
|
||||
<create-project (create)="createProject($event)"></create-project>
|
||||
</div>
|
||||
<div class="option-right">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
{{projectTypes[currentFilteredType] | translate}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="doFilterProjects(0)">{{projectTypes[0] | translate}}</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="doFilterProjects(1)">{{projectTypes[1] | translate}}</a>
|
||||
</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>
|
||||
</div>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="doFilterProjects(0)">{{projectTypes[0] | translate}}</a>
|
||||
<a href="javascript:void(0)" clrDropdownItem (click)="doFilterProjects(1)">{{projectTypes[1] | translate}}</a>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
<list-project [projects]="changedProjects" [filteredType]="projectTypes[currentFilteredType]" (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>
|
||||
|
||||
|
||||
</div>
|
@ -2,7 +2,7 @@
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-items-xs-middle option-left">
|
||||
<button class="btn btn-primary" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'DESTINATION.ENDPOINT' | translate}}</button>
|
||||
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'DESTINATION.ENDPOINT' | translate}}</button>
|
||||
<create-edit-destination (reload)="reload($event)"></create-edit-destination>
|
||||
</div>
|
||||
<div class="flex-items-xs-middle option-right">
|
||||
@ -25,7 +25,7 @@
|
||||
</clr-dg-action-overflow>
|
||||
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.endpoint}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.creation_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>{{ (targets ? targets.length : 0) }} {{'DESTINATION.ITEMS' | translate}}</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
|
@ -9,8 +9,8 @@
|
||||
<clr-dg-cell>{{j.repository}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.operation}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.creation_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.update_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.update_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<a href="/api/jobs/replication/{{j.id}}/log" target="_BLANK">
|
||||
<clr-icon shape="clipboard"></clr-icon>
|
||||
|
@ -1,54 +1,58 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button class="btn btn-primary" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||
<create-edit-policy [projectId]="projectId" (reload)="reloadPolicies($event)"></create-edit-policy>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||
<create-edit-policy [projectId]="projectId" (reload)="reloadPolicies($event)"></create-edit-policy>
|
||||
</div>
|
||||
<div class="flex-xs-middle option-right">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
{{currentRuleStatus.description | translate}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let r of ruleStatus" (click)="doFilterPolicyStatus(r.key)"> {{r.description | translate}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchPolicies($event)"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshPolicies()"><clr-icon shape="refresh"></clr-icon></a>
|
||||
</div>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let r of ruleStatus" (click)="doFilterPolicyStatus(r.key)"> {{r.description | translate}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchPolicies($event)"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshPolicies()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-policy [policies]="changedPolicies" [projectless]="false" [selectedId]="initSelectedId" (selectOne)="selectOne($event)" (editOne)="openEditPolicy($event)" (reload)="reloadPolicies($event)"></list-policy>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshJobs()"><clr-icon shape="refresh"></clr-icon></a>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-policy [policies]="changedPolicies" [projectless]="false" [selectedId]="initSelectedId" (selectOne)="selectOne($event)" (editOne)="openEditPolicy($event)" (reload)="reloadPolicies($event)"></list-policy>
|
||||
</div>
|
||||
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between">
|
||||
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<grid-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)"></grid-filter>
|
||||
<a href="javascript:void(0)" (click)="refreshJobs()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||
<clr-dropdown [clrMenuPosition]="'bottom-left'">
|
||||
<button class="btn btn-link" clrDropdownToggle>
|
||||
{{currentJobStatus.description | translate}}
|
||||
<clr-icon shape="caret down"></clr-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let j of jobStatus" (click)="doFilterJobStatus(j.key)"> {{j.description | translate}}</a>
|
||||
<div class="dropdown-menu">
|
||||
<a href="javascript:void(0)" clrDropdownItem *ngFor="let j of jobStatus" (click)="doFilterJobStatus(j.key)"> {{j.description | translate}}</a>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<div class="flex-items-xs-middle">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #fromTime (change)="doJobSearchByTimeRange(fromTime.value, 'begin')">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #toTime (change)="doJobSearchByTimeRange(toTime.value, 'end')">
|
||||
</div>
|
||||
</div>
|
||||
</clr-dropdown>
|
||||
<div class="flex-items-xs-middle">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #fromTime (change)="doJobSearchByTimeRange(fromTime.value, 'begin')">
|
||||
<clr-icon shape="date"></clr-icon><input type="date" #toTime (change)="doJobSearchByTimeRange(toTime.value, 'end')">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-job [jobs]="changedJobs" [totalPage]="jobsTotalPage" [totalRecordCount]="jobsTotalRecordCount" (paginate)="fetchPolicyJobs($event)"></list-job>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<list-job [jobs]="changedJobs" [totalPage]="jobsTotalPage" [totalRecordCount]="jobsTotalRecordCount" (paginate)="fetchPolicyJobs($event)"></list-job>
|
||||
</div>
|
||||
</div>
|
@ -37,7 +37,7 @@
|
||||
<clr-icon shape="close" *ngIf="!t.signed" style="color: #C92100;"></clr-icon>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.author}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.created | date: 'yyyy/MM/dd'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.created | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.dockerVersion}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
||||
|
@ -13,8 +13,7 @@
|
||||
<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></p>
|
||||
<p class="p5">{{'ABOUT.TRADEMARK' | translate}}</p>
|
||||
<p class="p5">
|
||||
<a href="#" target="_blank">{{'ABOUT.END_USER_LICENSE' | translate}}</a><br>
|
||||
<a href="#" target="_blank">{{'ABOUT.OPEN_SOURCE_LICENSE' | translate}}</a>
|
||||
<a href="https://raw.githubusercontent.com/vmware/harbor/master/LICENSE" target="_blank">{{'ABOUT.OPEN_SOURCE_LICENSE' | translate}}</a>
|
||||
</p>
|
||||
<div style="height: 24px;"></div>
|
||||
</div>
|
||||
|
@ -15,4 +15,5 @@
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 80%;
|
||||
white-space: pre-wrap;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.NEGATIVE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.CONFIRM' | translate}}</button>
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.YES' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
@ -1,4 +1,9 @@
|
||||
.alert-text-blink {
|
||||
color: red;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.alert-btn-link {
|
||||
padding: 0px !important;
|
||||
min-width: 30px !important;
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
{{errorMessage}}
|
||||
</span>
|
||||
<div class="alert-actions" *ngIf="showCancelAction">
|
||||
<button class="btn alert-action" (click)="close()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button class="btn alert-action" (click)="confirmCancel()">{{'BUTTON.YES' | translate}}</button>
|
||||
<button class="btn btn-sm btn-link alert-btn-link" (click)="close()">{{'BUTTON.NO' | translate}}</button>
|
||||
<button class="btn btn-sm btn-link alert-btn-link" (click)="confirmCancel()">{{'BUTTON.YES' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</clr-alert>
|
@ -20,9 +20,12 @@
|
||||
</template>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="projectless">{{p.project_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.description}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.description ? p.description : '-'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.target_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.start_time}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<template [ngIf]="p.start_time === nullTime">-</template>
|
||||
<template [ngIf]="p.start_time !== nullTime">{{p.start_time | date: 'short'}}</template>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
{{ (p.enabled === 1 ? 'REPLICATION.ENABLED' : 'REPLICATION.DISABLED') | translate}}
|
||||
</clr-dg-cell>
|
||||
|
@ -18,6 +18,8 @@ import { Subscription } from 'rxjs/Subscription';
|
||||
})
|
||||
export class ListPolicyComponent implements OnDestroy {
|
||||
|
||||
nullTime: string = '0001-01-01T00:00:00Z';
|
||||
|
||||
@Input() policies: Policy[];
|
||||
@Input() projectless: boolean;
|
||||
@Input() selectedId: number;
|
||||
|
@ -7,7 +7,7 @@
|
||||
<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-cell>{{p.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="form-group form-group-override">
|
||||
<label for="username" class="required form-group-label-override">{{'PROFILE.USER_NAME' | translate}}</label>
|
||||
<label for="username" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("username")'>
|
||||
<input type="text" required pattern='[^"~#$%]+' maxLengthExt="20" #usernameInput="ngModel" name="username" [(ngModel)]="newUser.username" id="username" size="40"
|
||||
<input type="text" required pattern='[^"~#$%]+' maxLengthExt="20" #usernameInput="ngModel" name="username" [(ngModel)]="newUser.username" id="username" size="36"
|
||||
(input)='handleValidation("username", false)'
|
||||
(focusout)='handleValidation("username", true)'>
|
||||
<span class="tooltip-content">
|
||||
@ -17,7 +17,7 @@
|
||||
<label for="email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("email")'>
|
||||
<input name="email" type="text" #eamilInput="ngModel" [(ngModel)]="newUser.email"
|
||||
required
|
||||
pattern='^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$' id="email" size="40"
|
||||
pattern='^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$' id="email" size="36"
|
||||
(input)='handleValidation("email", false)'
|
||||
(focusout)='handleValidation("email", true)'>
|
||||
<span class="tooltip-content">
|
||||
@ -30,7 +30,7 @@
|
||||
<div class="form-group form-group-override">
|
||||
<label for="realname" class="required form-group-label-override">{{'PROFILE.FULL_NAME' | translate}}</label>
|
||||
<label for="realname" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("realname")'>
|
||||
<input type="text" name="realname" #fullNameInput="ngModel" [(ngModel)]="newUser.realname" required maxLengthExt="20" id="realname" size="40"
|
||||
<input type="text" name="realname" #fullNameInput="ngModel" [(ngModel)]="newUser.realname" required maxLengthExt="20" id="realname" size="36"
|
||||
(input)='handleValidation("realname", false)'
|
||||
(focusout)='handleValidation("realname", true)'>
|
||||
<span class="tooltip-content">
|
||||
@ -43,10 +43,10 @@
|
||||
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("newPassword")'>
|
||||
<input type="password" id="newPassword"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="newPassword"
|
||||
[(ngModel)]="newUser.password"
|
||||
#newPassInput="ngModel" size="40"
|
||||
#newPassInput="ngModel" size="36"
|
||||
(input)='handleValidation("newPassword", false)'
|
||||
(focusout)='handleValidation("newPassword", true)'>
|
||||
<span class="tooltip-content">
|
||||
@ -60,10 +60,10 @@
|
||||
<label for="confirmPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("confirmPassword")'>
|
||||
<input type="password" id="confirmPassword"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="confirmPassword"
|
||||
[(ngModel)]="confirmedPwd"
|
||||
#confirmPassInput="ngModel" size="40"
|
||||
#confirmPassInput="ngModel" size="36"
|
||||
(input)='handleValidation("confirmPassword", false)'
|
||||
(focusout)='handleValidation("confirmPassword", true)'>
|
||||
<span class="tooltip-content">
|
||||
@ -74,7 +74,7 @@
|
||||
<div class="form-group form-group-override">
|
||||
<label for="comment" class="form-group-label-override">{{'PROFILE.COMMENT' | translate}}</label>
|
||||
<label for="comment" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='getValidationState("comment")'>
|
||||
<input type="text" #commentInput="ngModel" name="comment" [(ngModel)]="newUser.comment" maxLengthExt="20" id="comment" size="40"
|
||||
<input type="text" #commentInput="ngModel" name="comment" [(ngModel)]="newUser.comment" maxLengthExt="20" id="comment" size="36"
|
||||
(input)='handleValidation("comment", false)'
|
||||
(focusout)='handleValidation("comment", true)'>
|
||||
<span class="tooltip-content">
|
||||
|
@ -161,4 +161,5 @@ export class SessionService {
|
||||
getProjectMembers(): Member[] {
|
||||
return this.projectMembers;
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
export const supportedLangs = ['en', 'zh'];
|
||||
export const enLang = "en";
|
||||
export const supportedLangs = ['en-us', 'zh-cn'];
|
||||
export const enLang = "en-us";
|
||||
export const languageNames = {
|
||||
"en": "English",
|
||||
"zh": "中文简体"
|
||||
"en-us": "English",
|
||||
"zh-cn": "中文简体"
|
||||
};
|
||||
export const enum AlertType {
|
||||
DANGER, WARNING, INFO, SUCCESS
|
||||
|
@ -11,8 +11,7 @@ export const errorHandler = function (error: any): string {
|
||||
if (!error) {
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
console.log(JSON.stringify(error));
|
||||
|
||||
console.log(error);
|
||||
if (!(error.statusCode || error.status)) {
|
||||
//treat as string message
|
||||
return '' + error;
|
||||
|
@ -30,4 +30,8 @@
|
||||
right: 2px;
|
||||
top: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hide-create {
|
||||
visibility: hidden !important;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<h2 class="custom-h2">{{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}}</h2>
|
||||
<div class="action-panel-pos">
|
||||
<span>
|
||||
<button *ngIf="canCreateUser" type="submit" class="btn btn-primary custom-add-button" (click)="addNewUser()"><clr-icon shape="add"></clr-icon> {{'USER.ADD_ACTION' | translate}}</button>
|
||||
<button [class.hide-create]="!canCreateUser" type="submit" class="btn btn-link custom-add-button" (click)="addNewUser()"><clr-icon shape="add"></clr-icon> {{'USER.ADD_ACTION' | translate}}</button>
|
||||
</span>
|
||||
<grid-filter class="filter-pos" filterPlaceholder='{{"USER.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)"></grid-filter>
|
||||
<span class="refresh-btn" (click)="refreshUser()">
|
||||
@ -25,9 +25,7 @@
|
||||
<clr-dg-cell>{{user.username}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{isSystemAdmin(user)}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{user.email}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
{{user.creation_time}}
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{user.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>{{users.length}} {{'USER.ITEMS' | translate}}</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
|
@ -206,6 +206,9 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
|
||||
//Add new user
|
||||
addNewUser(): void {
|
||||
if (!this.canCreateUser) {
|
||||
return;// No response to this hacking action
|
||||
}
|
||||
this.newUserDialog.open();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"APP_TITLE": {
|
||||
"VMW_HARBOR": "VMware Harbor",
|
||||
"HARBOR": "Harbor",
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Management",
|
||||
@ -110,7 +111,7 @@
|
||||
"COLUMN_REG_NAME": "Registration time",
|
||||
"IS_ADMIN": "Yes",
|
||||
"IS_NOT_ADMIN": "No",
|
||||
"ADD_USER_TITLE": "Add User",
|
||||
"ADD_USER_TITLE": "New User",
|
||||
"SAVE_SUCCESS": "New user added successfully",
|
||||
"DELETION_TITLE": "Confirm user deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete user {{param}}?",
|
||||
@ -386,7 +387,7 @@
|
||||
"TEST_MAIL_FAILED": "Failed to verify mail server with error: {{param}}",
|
||||
"TEST_LDAP_FAILED": "Failed to verify LDAP server with error: {{param}}",
|
||||
"LEAVING_CONFIRMATION_TITLE": "Confirm to leave",
|
||||
"LEAVING_CONFIRMATION_SUMMARY": "Changes have not been saved yet, do you really want to leave currnet page?"
|
||||
"LEAVING_CONFIRMATION_SUMMARY": "Changes have not been saved yet,do you really want to leave currnet page?"
|
||||
},
|
||||
"PAGE_NOT_FOUND": {
|
||||
"MAIN_TITLE": "Page not found",
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"APP_TITLE": {
|
||||
"VMW_HARBOR": "VMware Harbor",
|
||||
"HARBOR": "Harbor",
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Management",
|
||||
@ -110,7 +111,7 @@
|
||||
"COLUMN_REG_NAME": "注册时间",
|
||||
"IS_ADMIN": "是",
|
||||
"IS_NOT_ADMIN": "否",
|
||||
"ADD_USER_TITLE": "添加用户",
|
||||
"ADD_USER_TITLE": "创建用户",
|
||||
"SAVE_SUCCESS": "添加用户成功",
|
||||
"DELETION_TITLE": "删除用户确认",
|
||||
"DELETION_SUMMARY": "你确认删除用户 {{param}}?",
|
@ -7,7 +7,6 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,9 +0,0 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
.datagrid-content-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
set -e
|
||||
cp tests/docker-compose.test.yml make/.
|
||||
|
||||
mkdir /etc/ui
|
||||
mkdir -p /etc/ui
|
||||
cp make/common/config/ui/private_key.pem /etc/ui/.
|
||||
|
||||
mkdir conf
|
||||
|
Loading…
Reference in New Issue
Block a user