UI: Support root cert downloading in configuration component (#2742)

* refine the test case of scheduler

* Fix bug

* Support root cert downloaded

* Fix code conflicts
This commit is contained in:
Steven Zou 2017-07-12 13:23:19 +08:00 committed by Yan
parent 005d783463
commit 76bb28d3d0
18 changed files with 185 additions and 60 deletions

View File

@ -6,6 +6,7 @@ Wrap the following Harbor UI components into a sharable library and published as
* Replication endpoints management view
* Access log list view
* Vulnerability scanning result bar chart and list view (Embedded in tag management view)
* Registry(Harbor) related configuration options
The Harbor UI library is built on **[Angular ](https://angular.io/)** 4.x and **[Clarity ](https://vmware.github.io/clarity/)** 0.9.x .
@ -22,12 +23,20 @@ Execute the testing specs with command:
npm run test
```
Install the package
```
npm install harbor-ui[@version]
```
## Usage
**Add dependency to application**
Execute install command to add dependency to package.json
```
npm install harbor-ui --save
//OR
npm install harbor-ui@0.2.x --save
```
The latest version of the library will be installed.
@ -113,6 +122,16 @@ This view is linked by the repository stack view only when the Clair is enabled
```
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="..." [repositoryId]="..."></hbr-tag-detail>
```
* **Registry related configuration**
This component provides some options for registry(Harbor) related configurations.
**hasAdminRole** is an @Input property to indicate if the current logged user has administrator role.
```
<hbr-registry-config [hasAdminRole]="***"></hbr-registry-config>
```
## Configurations
All the related configurations are defined in the **HarborModuleConfig** interface.
@ -127,6 +146,7 @@ export const DefaultServiceConfig: IServiceConfig = {
replicationRuleEndpoint: "/api/policies/replication",
replicationJobEndpoint: "/api/jobs/replication",
vulnerabilityScanningBaseEndpoint: "/api/repositories",
configurationEndpoint: "/api/configurations",
enablei18Support: false,
defaultLang: DEFAULT_LANG, //'en-us'
langCookieKey: DEFAULT_LANG_COOKIE_KEY, //'harbor-lang'
@ -165,6 +185,8 @@ It supports partially overriding. For the items not overridden, default values w
* **vulnerabilityScanningBaseEndpoint:** The base endpoint of the service used to handle the vulnerability scanning results.Default value is "/api/repositories".
* **configurationEndpoint:** The base endpoint of the service used to configure registry related options. Default is "/api/configurations".
* **langCookieKey:** The cookie key used to store the current used language preference. Default is "harbor-lang".
* **supportedLangs:** Declare what languages are supported. Default is ['en-us', 'zh-cn', 'es-es'].
@ -254,7 +276,6 @@ export class MyAccessLogService extends AccessLogService {
* - page
* - pageSize
*
* @abstract
* @param {(number | string)} projectId
* @param {RequestQueryParams} [queryParams]
* @returns {(Observable<AccessLog[]> | Promise<AccessLog[]> | AccessLog[])}
@ -268,7 +289,6 @@ export class MyAccessLogService extends AccessLogService {
/**
* Get the recent logs.
*
* @abstract
* @param {number} lines : Specify how many lines should be returned.
* @returns {(Observable<AccessLog[]> | Promise<AccessLog[]> | AccessLog[])}
*
@ -295,7 +315,6 @@ export class MyEndpointService extends EndpointService {
* Get all the endpoints.
* Set the argument 'endpointName' to return only the endpoints match the name pattern.
*
* @abstract
* @param {string} [endpointName]
* @param {RequestQueryParams} [queryParams]
* @returns {(Observable<Endpoint[]> | Endpoint[])}
@ -309,7 +328,6 @@ export class MyEndpointService extends EndpointService {
/**
* Get the specified endpoint.
*
* @abstract
* @param {(number | string)} endpointId
* @returns {(Observable<Endpoint> | Endpoint)}
*
@ -322,7 +340,6 @@ export class MyEndpointService extends EndpointService {
/**
* Create new endpoint.
*
* @abstract
* @param {Endpoint} endpoint
* @returns {(Observable<any> | any)}
*
@ -335,7 +352,6 @@ export class MyEndpointService extends EndpointService {
/**
* Update the specified endpoint.
*
* @abstract
* @param {(number | string)} endpointId
* @param {Endpoint} endpoint
* @returns {(Observable<any> | any)}
@ -349,7 +365,6 @@ export class MyEndpointService extends EndpointService {
/**
* Delete the specified endpoint.
*
* @abstract
* @param {(number | string)} endpointId
* @returns {(Observable<any> | any)}
*
@ -362,7 +377,6 @@ export class MyEndpointService extends EndpointService {
/**
* Ping the specified endpoint.
*
* @abstract
* @param {Endpoint} endpoint
* @returns {(Observable<any> | any)}
*
@ -375,7 +389,6 @@ export class MyEndpointService extends EndpointService {
/**
* Check endpoint whether in used with specific replication rule.
*
* @abstract
* @param {{number | string}} endpointId
* @returns {{Observable<any> | any}}
*/
@ -402,7 +415,6 @@ export class MyReplicationService extends ReplicationService {
* set the argument 'ruleName' to return the rule only match the name pattern;
* if pagination needed, use the queryParams to add query parameters.
*
* @abstract
* @param {(number | string)} [projectId]
* @param {string} [ruleName]
* @param {RequestQueryParams} [queryParams]
@ -417,7 +429,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Get the specified replication rule.
*
* @abstract
* @param {(number | string)} ruleId
* @returns {(Observable<ReplicationRule> | Promise<ReplicationRule> | ReplicationRule)}
*
@ -430,7 +441,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Create new replication rule.
*
* @abstract
* @param {ReplicationRule} replicationRule
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -443,7 +453,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Update the specified replication rule.
*
* @abstract
* @param {ReplicationRule} replicationRule
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -456,7 +465,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Delete the specified replication rule.
*
* @abstract
* @param {(number | string)} ruleId
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -469,7 +477,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Enable the specified replication rule.
*
* @abstract
* @param {(number | string)} ruleId
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -482,7 +489,6 @@ export class MyReplicationService extends ReplicationService {
/**
* Disable the specified replication rule.
*
* @abstract
* @param {(number | string)} ruleId
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -501,7 +507,6 @@ export class MyReplicationService extends ReplicationService {
* - page
* - pageSize
*
* @abstract
* @param {(number | string)} ruleId
* @param {RequestQueryParams} [queryParams]
* @returns {(Observable<ReplicationJob> | Promise<ReplicationJob[]> | ReplicationJob)}
@ -532,7 +537,6 @@ export class MyRepositoryService extends RepositoryService {
* 'page': current page,
* 'page_size': page size.
*
* @abstract
* @param {(number | string)} projectId
* @param {string} repositoryName
* @param {RequestQueryParams} [queryParams]
@ -547,7 +551,6 @@ export class MyRepositoryService extends RepositoryService {
/**
* DELETE the specified repository.
*
* @abstract
* @param {string} repositoryName
* @returns {(Observable<any> | Promise<any> | any)}
*
@ -572,8 +575,7 @@ export class MyTagService extends TagService {
/**
* Get all the tags under the specified repository.
* NOTES: If the Notary is enabled, the signatures should be included in the returned data.
*
* @abstract
*
* @param {string} repositoryName
* @param {RequestQueryParams} [queryParams]
* @returns {(Observable<Tag[]> | Promise<Tag[]> | Tag[])}
@ -587,7 +589,6 @@ export class MyTagService extends TagService {
/**
* Delete the specified tag.
*
* @abstract
* @param {string} repositoryName
* @param {string} tag
* @returns {(Observable<any> | any)}
@ -614,14 +615,12 @@ HarborLibraryModule.forRoot({
* Get the vulnerabilities scanning results for the specified tag.
*
* @export
* @abstract
* @class ScanningResultService
*/
export class MyScanningResultService extends ScanningResultService {
/**
* Get the summary of vulnerability scanning result.
*
* @abstract
* @param {string} tagId
* @returns {(Observable<VulnerabilitySummary> | Promise<VulnerabilitySummary> | VulnerabilitySummary)}
*
@ -634,7 +633,6 @@ export class MyScanningResultService extends ScanningResultService {
/**
* Get the detailed vulnerabilities scanning results.
*
* @abstract
* @param {string} tagId
* @returns {(Observable<VulnerabilityItem[]> | Promise<VulnerabilityItem[]> | VulnerabilityItem[])}
*
@ -648,7 +646,6 @@ export class MyScanningResultService extends ScanningResultService {
/**
* Start a new vulnerability scanning
*
* @abstract
* @param {string} repoName
* @param {string} tagId
* @returns {(Observable<any> | Promise<any> | any)}
@ -672,13 +669,11 @@ HarborLibraryModule.forRoot({
```
/**
* Get System information about current backend server.
* @abstract
* @class
*/
export class MySystemInfoService extends SystemInfoService {
/**
* Get global system information.
* @abstract
* @returns
*/
getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
@ -693,3 +688,46 @@ HarborLibraryModule.forRoot({
...
```
* **ConfigurationService:** Get and save the registry related configuration options.
```
/**
* Service used to get and save registry-related configurations.
*
* @export
* @class MyConfigurationService
*/
export class MyConfigurationService extends ConfigurationService{
/**
* Get configurations.
*
* @returns {(Observable<Configuration> | Promise<Configuration> | Configuration)}
*
* @memberOf ConfigurationService
*/
getConfigurations(): Observable<Configuration> | Promise<Configuration> | Configuration{
...
}
/**
* Save configurations.
*
* @returns {(Observable<Configuration> | Promise<Configuration> | Configuration)}
*
* @memberOf ConfigurationService
*/
saveConfigurations(changedConfigs: any | { [key: string]: any | any[] }): Observable<any> | Promise<any> | any{
...
}
}
...
HarborLibraryModule.forRoot({
config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService }
})
...
```

View File

@ -1,8 +1,8 @@
export const REGISTRY_CONFIG_HTML: string = `
<div>
<replication-config #replicationConfig [(replicationConfig)]="config"></replication-config>
<system-settings #systemSettings [(systemSettings)]="config"></system-settings>
<vulnerability-config #vulnerabilityConfig [(vulnerabilityConfig)]="config"></vulnerability-config>
<replication-config #replicationConfig [(replicationConfig)]="config" [showSubTitle]="true"></replication-config>
<system-settings #systemSettings [(systemSettings)]="config" [showSubTitle]="true" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true"></vulnerability-config>
<div>
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="shouldDisable">{{'BUTTON.SAVE' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="shouldDisable">{{'BUTTON.CANCEL' | translate}}</button>

View File

@ -10,12 +10,15 @@ import { VulnerabilityConfigComponent } from './vulnerability/vulnerability-conf
import { RegistryConfigComponent } from './registry-config.component';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import {
ConfigurationService,
import {
ConfigurationService,
ConfigurationDefaultService,
ScanningResultService,
ScanningResultDefaultService
} from '../service/index';
ScanningResultDefaultService,
SystemInfoService,
SystemInfoDefaultService,
SystemInfo
} from '../service/index';
import { Configuration } from './config';
describe('RegistryConfigComponent (inline template)', () => {
@ -23,8 +26,10 @@ describe('RegistryConfigComponent (inline template)', () => {
let comp: RegistryConfigComponent;
let fixture: ComponentFixture<RegistryConfigComponent>;
let cfgService: ConfigurationService;
let systemInfoService: SystemInfoService;
let spy: jasmine.Spy;
let saveSpy: jasmine.Spy;
let spySystemInfo: jasmine.Spy;
let mockConfig: Configuration = new Configuration();
mockConfig.token_expiration.value = 90;
mockConfig.verify_remote_cert.value = true;
@ -37,6 +42,18 @@ describe('RegistryConfigComponent (inline template)', () => {
let config: IServiceConfig = {
configurationEndpoint: '/api/configurations/testing'
};
let mockSystemInfo: SystemInfo = {
"with_notary": true,
"with_admiral": false,
"with_clair": true,
"admiral_endpoint": "NA",
"auth_mode": "db_auth",
"registry_url": "10.112.122.56",
"project_creation_restriction": "everyone",
"self_registration": true,
"has_ca_root": true,
"harbor_version": "v1.1.1-rc1-160-g565110d"
};
beforeEach(async(() => {
TestBed.configureTestingModule({
@ -54,7 +71,8 @@ describe('RegistryConfigComponent (inline template)', () => {
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue: config },
{ provide: ConfigurationService, useClass: ConfigurationDefaultService },
{ provide: ScanningResultService, useClass: ScanningResultDefaultService }
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
{ provide: SystemInfoService, useClass: SystemInfoDefaultService }
]
});
}));
@ -64,30 +82,34 @@ describe('RegistryConfigComponent (inline template)', () => {
comp = fixture.componentInstance;
cfgService = fixture.debugElement.injector.get(ConfigurationService);
systemInfoService = fixture.debugElement.injector.get(SystemInfoService);
spy = spyOn(cfgService, 'getConfigurations').and.returnValue(Promise.resolve(mockConfig));
saveSpy = spyOn(cfgService, 'saveConfigurations').and.returnValue(Promise.resolve(true));
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
fixture.detectChanges();
});
it('should render configurations to the view', async(() => {
expect(spy.calls.count()).toEqual(1);
expect(spySystemInfo.calls.count()).toEqual(1);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let el: HTMLInputElement = fixture.nativeElement.querySelector('input[type="text"]');
expect(el).toBeTruthy();
expect(el).not.toBeFalsy();
expect(el.value).toEqual('30');
let el2: HTMLInputElement = fixture.nativeElement.querySelector('input[type="checkbox"]');
expect(el2).toBeTruthy();
expect(el2.value).toEqual('on');
fixture.detectChanges();
let el3: HTMLInputElement = fixture.nativeElement.querySelector('input[type="time"]');
expect(el3).toBeTruthy();
expect(el3.value).toBeTruthy();
expect(el3).not.toBeFalsy();
});
}));

View File

@ -1,8 +1,8 @@
import { Component, OnInit, EventEmitter, Output, ViewChild } from '@angular/core';
import { Component, OnInit, EventEmitter, Output, ViewChild, Input } from '@angular/core';
import { Configuration, ComplexValueItem } from './config';
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
import { ConfigurationService } from '../service/index';
import { ConfigurationService, SystemInfoService, SystemInfo } from '../service/index';
import { toPromise } from '../utils';
import { ErrorHandler } from '../error-handler';
import {
@ -26,6 +26,9 @@ export class RegistryConfigComponent implements OnInit {
config: Configuration = new Configuration();
configCopy: Configuration;
onGoing: boolean = false;
systemInfo: SystemInfo;
@Input() hasAdminRole: boolean = false;
@ViewChild("replicationConfig") replicationCfg: ReplicationConfigComponent;
@ViewChild("systemSettings") systemSettings: SystemSettingsComponent;
@ -35,14 +38,28 @@ export class RegistryConfigComponent implements OnInit {
constructor(
private configService: ConfigurationService,
private errorHandler: ErrorHandler,
private translate: TranslateService
private translate: TranslateService,
private systemInfoService: SystemInfoService
) { }
get shouldDisable(): boolean {
return !this.isValid() || !this.hasChanges() || this.onGoing;
}
get hasCAFile(): boolean {
return this.systemInfo && this.systemInfo.has_ca_root;
}
get withClair(): boolean {
return this.systemInfo && this.systemInfo.with_clair;
}
ngOnInit(): void {
//Get system info
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
.then((info: SystemInfo) => this.systemInfo = info)
.catch(error => this.errorHandler.error(error));
//Initialize
this.load();
}

View File

@ -1,7 +1,7 @@
export const REPLICATION_CONFIG_HTML: string = `
<form #replicationConfigFrom="ngForm" class="compact">
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
<label style="font-size:14px;font-weight:600;">Image Replication</label>
<label style="font-size:14px;font-weight:600;" *ngIf="showSubTitle">{{'CONFIG.REPLICATION' | translate}}</label>
<div class="form-group">
<label for="verifyRemoteCert">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</label>
<clr-checkbox name="verifyRemoteCert" id="verifyRemoteCert" [(ngModel)]="replicationConfig.verify_remote_cert.value" [disabled]="!editable">

View File

@ -21,6 +21,8 @@ export class ReplicationConfigComponent {
this.configChange.emit(this.config);
}
@Input() showSubTitle: boolean = false
@ViewChild("replicationConfigFrom") replicationConfigForm: NgForm;
get editable(): boolean {

View File

@ -1,7 +1,7 @@
export const SYSTEM_SETTINGS_HTML: string = `
<form #systemConfigFrom="ngForm" class="compact">
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
<label style="font-size:14px;font-weight:600;">System Settings</label>
<label style="font-size:14px;font-weight:600;" *ngIf="showSubTitle">{{'CONFIG.SYSTEM' | translate}}</label>
<div class="form-group">
<label for="tokenExpiration" class="required">{{'CONFIG.TOKEN_EXPIRATION' | translate}}</label>
<label for="tokenExpiration" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right" [class.invalid]="tokenExpirationInput.invalid && (tokenExpirationInput.dirty || tokenExpirationInput.touched)">
@ -19,6 +19,14 @@ export const SYSTEM_SETTINGS_HTML: string = `
<span class="tooltip-content">{{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}}</span>
</a>
</div>
<div class="form-group" *ngIf="canDownloadCert">
<label for="certDownloadLink" class="required">{{'CONFIG.ROOT_CERT' | translate}}</label>
<a #certDownloadLink href="/api/systeminfo/getcert" target="_blank">{{'CONFIG.ROOT_CERT_LINK' | translate}}</a>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.ROOT_CERT_DOWNLOAD' | translate}}</span>
</a>
</div>
</section>
</form>
`;

View File

@ -21,6 +21,10 @@ export class SystemSettingsComponent {
this.configChange.emit(this.config);
}
@Input() showSubTitle: boolean = false;
@Input() hasAdminRole: boolean = false;
@Input() hasCAFile: boolean = false;
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
get editable(): boolean {
@ -32,4 +36,8 @@ export class SystemSettingsComponent {
get isValid(): boolean {
return this.systemSettingsForm && this.systemSettingsForm.valid;
}
get canDownloadCert(): boolean {
return this.hasAdminRole && this.hasCAFile;
}
}

View File

@ -1,7 +1,7 @@
export const VULNERABILITY_CONFIG_HTML: string = `
<form #systemConfigFrom="ngForm" class="compact">
<section class="form-block" style="margin-top:0px;margin-bottom:0px;">
<label class="section-title">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
<label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
<div class="form-group">
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
<div class="select">
@ -12,6 +12,10 @@ export const VULNERABILITY_CONFIG_HTML: string = `
</select>
</div>
<input type="time" name="dailyTimePicker" [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.SCANNING_POLICY' | translate}}</span>
</a>
</div>
<div class="form-group form-group-override">
<button class="btn btn-primary btn-sm" style="width:160px;" (click)="scanNow()">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</button>

View File

@ -41,6 +41,8 @@ export class VulnerabilityConfigComponent {
this.configChange.emit(this.config);
}
@Input() showSubTitle: boolean = false
//UTC time
get dailyTime(): string {
if (!(this.config &&

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8",
"core-js": "^2.4.1",
"harbor-ui": "^0.2.52",
"harbor-ui": "^0.2.55",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",

View File

@ -16,6 +16,7 @@ export class AppConfig {
//Set default value
this.with_notary = false;
this.with_admiral = false;
this.with_clair = false;
this.admiral_endpoint = "";
this.auth_mode = "db_auth";
this.registry_url = "";
@ -27,6 +28,7 @@ export class AppConfig {
with_notary: boolean;
with_admiral: boolean;
with_clair: boolean;
admiral_endpoint: string;
auth_mode: string;
registry_url: string;

View File

@ -15,7 +15,7 @@
<li role="presentation" class="nav-item">
<button id="config-system" class="btn btn-link nav-link" aria-controls="system_settings" [class.active]='isCurrentTabLink("config-system")' type="button" (click)='tabLinkClick("config-system")'>{{'CONFIG.SYSTEM' | translate }}</button>
</li>
<li role="presentation" class="nav-item">
<li role="presentation" class="nav-item" *ngIf="withClair">
<button id="config-vulnerability" class="btn btn-link nav-link" aria-controls="vulnerability" [class.active]='isCurrentTabLink("config-vulnerability")' type="button" (click)='tabLinkClick("config-vulnerability")'>{{'VULNERABILITY.SINGULAR' | translate}}</button>
</li>
</ul>
@ -29,9 +29,9 @@
<config-email [mailConfig]="allConfig"></config-email>
</section>
<section id="system_settings" role="tabpanel" aria-labelledby="config-system" [hidden]='!isCurrentTabContent("system_settings")'>
<system-settings [(systemSettings)]="allConfig"></system-settings>
<system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>
</section>
<section id="vulnerability" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
<section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
</section>
<div>

View File

@ -71,6 +71,19 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
private appConfigService: AppConfigService,
private session: SessionService) { }
public get hasAdminRole(): boolean {
return this.session.getCurrentUser() &&
this.session.getCurrentUser().has_admin_role > 0;
}
public get hasCAFile(): boolean {
return this.appConfigService.getConfig().has_ca_root;
}
public get withClair(): boolean {
return this.appConfigService.getConfig().with_clair;
}
isCurrentTabLink(tabId: string): boolean {
return this.currentTabId === tabId;
}

View File

@ -40,7 +40,7 @@ export class ListProjectROComponent {
goToLink(proId: number): void {
this.searchTrigger.closeSearch(true);
let linkUrl = ['harbor', 'projects', proId, 'repository'];
let linkUrl = ['harbor', 'projects', proId, 'repositories'];
this.router.navigate(linkUrl);
}

View File

@ -83,8 +83,7 @@
"PROFILE": "User Profile",
"CHANGE_PWD": "Change Password",
"ABOUT": "About",
"LOGOUT": "Log Out",
"ROOT_CERT": "Download Root Cert"
"LOGOUT": "Log Out"
},
"GLOBAL_SEARCH": {
"PLACEHOLDER": "Search Harbor...",
@ -383,6 +382,8 @@
"SCOPE_SUBTREE": "Subtree",
"PRO_CREATION_EVERYONE": "Everyone",
"PRO_CREATION_ADMIN": "Admin Only",
"ROOT_CERT": "Registry Root Certificate",
"ROOT_CERT_LINK": "Download",
"TOOLTIP": {
"SELF_REGISTRATION": "Enable sign up.",
"VERIFY_REMOTE_CERT": "Determine whether the image replication should verify the certificate of a remote Harbor registry. Uncheck this box when the remote registry uses a self-signed or untrusted certificate.",
@ -390,9 +391,11 @@
"LDAP_SEARCH_DN": "A user's DN who has the permission to search the LDAP/AD server. If your LDAP/AD server does not support anonymous search, you should configure this DN and ldap_search_pwd.",
"LDAP_BASE_DN": "The base DN from which to look up a user in LDAP/AD.",
"LDAP_UID": "The attribute used in a search to match a user. It could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD.",
"LDAP_SCOPE": "The scope to search for users",
"LDAP_SCOPE": "The scope to search for users.",
"TOKEN_EXPIRATION": "The expiration time (in minutes) of a token created by the token service. Default is 30 minutes.",
"PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project."
"PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project.",
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday; 'Upon Refresh': Triggering scanning when database refreshed."
},
"LDAP": {
"URL": "LDAP URL",

View File

@ -83,8 +83,7 @@
"PROFILE": "Perfil de usuario",
"CHANGE_PWD": "Cambiar contraseña",
"ABOUT": "Acerca de",
"LOGOUT": "Cerrar sesión",
"ROOT_CERT": "Descargar Certificado Raíz"
"LOGOUT": "Cerrar sesión"
},
"GLOBAL_SEARCH": {
"PLACEHOLDER": "Buscar en Harbor...",
@ -384,6 +383,8 @@
"SCOPE_SUBTREE": "Subárbol",
"PRO_CREATION_EVERYONE": "Todos",
"PRO_CREATION_ADMIN": "Solo Administradores",
"ROOT_CERT": "Registro Certificado Raíz",
"ROOT_CERT_LINK": "Descargar",
"TOOLTIP": {
"SELF_REGISTRATION": "Activar registro.",
"VERIFY_REMOTE_CERT": "Determina si la replicación de la imagen debería verificar el certificado de un registro Harbor remoto. Desmarque esta opción cuando el registro remoto use un certificado de confianza o autofirmado.",
@ -393,7 +394,9 @@
"LDAP_UID": "El atributo usado en una búsqueda para encontrar un usuario. Debe ser el uid, cn, email, sAMAccountName u otro atributo dependiendo del LDAP/AD.",
"LDAP_SCOPE": "El ámbito de búsqueda para usuarios",
"TOKEN_EXPIRATION": "El tiempo de expiración (en minutos) del token creado por el servicio de tokens. Por defecto son 30 minutos.",
"PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos."
"PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos.",
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday; 'Upon Refresh': Triggering scanning when database refreshed."
},
"LDAP": {
"URL": "LDAP URL",

View File

@ -83,8 +83,7 @@
"PROFILE": "用户设置",
"CHANGE_PWD": "修改密码",
"ABOUT": "关于",
"LOGOUT": "退出",
"ROOT_CERT": "下载根证书"
"LOGOUT": "退出"
},
"GLOBAL_SEARCH": {
"PLACEHOLDER": "搜索 Harbor...",
@ -383,6 +382,8 @@
"SCOPE_SUBTREE": "子树",
"PRO_CREATION_EVERYONE": "所有人",
"PRO_CREATION_ADMIN": "仅管理员",
"ROOT_CERT": "镜像库根证书",
"ROOT_CERT_LINK": "下载",
"TOOLTIP": {
"SELF_REGISTRATION": "激活注册功能。",
"VERIFY_REMOTE_CERT": "确定镜像复制是否要验证远程Harbor实例的证书。如果远程实例使用的是自签或者非信任证书不要勾选此项。",
@ -392,7 +393,9 @@
"LDAP_UID": "在搜索中用来匹配用户的属性可以是uid,cn,email,sAMAccountName或者其它LDAP/AD服务器支持的属性。",
"LDAP_SCOPE": "搜索用户的范围。",
"TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间分钟默认为30分钟。",
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。"
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。",
"ROOT_CERT_DOWNLOAD": "下载镜像库根证书.",
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描;‘缺陷库刷新后’:当缺陷数据库刷新后。"
},
"LDAP": {
"URL": "LDAP URL",
@ -409,7 +412,7 @@
"SCAN_ALL": "扫描所有",
"SCAN_NOW": "开始扫描",
"NONE_POLICY": "无",
"DAILY_POLICY": "每日",
"DAILY_POLICY": "每日定时",
"REFRESH_POLICY": "缺陷库刷新后"
},
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常。",