Upgrade angualr from 7.1.3 to 8.2.0 and clarity from 1.0 to 2.2

Signed-off-by: Yogi_Wang <yawang@vmware.com>

Signed-off-by: Meina Zhou <meinaz@vmware.com>
Signed-off-by: sshijun <sshijun@vmware.com>
This commit is contained in:
Yogi_Wang 2019-09-05 15:13:13 +08:00
parent fcc301368c
commit a7c7a8e675
155 changed files with 6764 additions and 7474 deletions

View File

@ -20,7 +20,8 @@ matrix:
- go: 1.12.5
env:
- OFFLINE=true
- node_js: 10.16.2
- language: node_js
node_js: 10.16.2
env:
- UI_UT=true
env:

View File

@ -6,6 +6,7 @@ COPY ./LICENSE /portal_src
WORKDIR /build_dir
RUN cp -r /portal_src/* /build_dir \
&& ls -la \
&& apt-get update \
@ -14,7 +15,7 @@ RUN cp -r /portal_src/* /build_dir \
&& npm install \
&& npm run build_lib \
&& npm run link_lib \
&& npm run release
&& node --max_old_space_size=8192 'node_modules/@angular/cli/bin/ng' build --prod
FROM photon:2.0

View File

@ -26,6 +26,7 @@
"node_modules/@clr/ui/clr-ui.min.css",
"node_modules/swagger-ui/dist/swagger-ui.css",
"node_modules/prismjs/themes/prism-solarizedlight.css",
"src/global.scss",
"src/styles.css"
],
"scripts": [

View File

@ -4,11 +4,6 @@
"deleteDestPath": false,
"lib": {
"entryFile": "index.ts",
"externals": {
"@ngx-translate/core": "ngx-translate-core",
"@ngx-translate/core/index": "ngx-translate-core",
"ngx-markdown": "ngx-markdown"
},
"umdModuleIds": {
"@clr/angular" : "angular",
"ngx-markdown" : "ngxMarkdown",

View File

@ -3,11 +3,6 @@
"dest": "./dist",
"lib": {
"entryFile": "index.ts",
"externals": {
"@ngx-translate/core": "ngx-translate-core",
"@ngx-translate/core/index": "ngx-translate-core",
"ngx-markdown": "ngx-markdown"
},
"umdModuleIds": {
"@clr/angular" : "angular",
"ngx-markdown" : "ngxMarkdown",

5
src/portal/lib/package-lock.json generated Normal file
View File

@ -0,0 +1,5 @@
{
"name": "@harbor/ui",
"version": "1.10.0",
"lockfileVersion": 1
}

View File

@ -1,7 +1,7 @@
{
"name": "@harbor/ui",
"version": "1.9.0",
"description": "Harbor shared UI components based on Clarity and Angular7",
"version": "1.10.0",
"description": "Harbor shared UI components based on Clarity and Angular8",
"author": "CNCF",
"module": "index.js",
"main": "bundles/harborui.umd.min.js",
@ -19,26 +19,26 @@
},
"homepage": "https://github.com/vmware/harbor#readme",
"peerDependencies": {
"@angular/animations": "^7.1.3",
"@angular/common": "^7.1.3",
"@angular/compiler": "^7.1.3",
"@angular/core": "^7.1.3",
"@angular/forms": "^7.1.3",
"@angular/http": "^7.1.3",
"@angular/platform-browser": "^7.1.3",
"@angular/platform-browser-dynamic": "^7.1.3",
"@angular/router": "^7.1.3",
"@angular/animations": "^8.2.0",
"@angular/common": "^8.2.0",
"@angular/compiler": "^8.2.0",
"@angular/core": "^8.2.0",
"@angular/forms": "^8.2.0",
"@angular/http": "^8.2.0",
"@angular/platform-browser": "^8.2.0",
"@angular/platform-browser-dynamic": "^8.2.0",
"@angular/router": "^8.2.0",
"@ngx-translate/core": "^10.0.2",
"@ngx-translate/http-loader": "^3.0.1",
"@webcomponents/custom-elements": "^1.1.3",
"@clr/angular": "^1.0.0",
"@clr/ui": "^1.0.0",
"@clr/icons": "^1.0.0",
"@clr/angular": "^2.1.0",
"@clr/icons": "^2.1.0",
"@clr/ui": "^2.1.0",
"core-js": "^2.5.4",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",
"ngx-markdown": "^6.2.0",
"ngx-markdown": "^8.1.0",
"rxjs": "^6.3.3",
"ts-helpers": "^1.1.1",
"web-animations-js": "^2.2.1",

View File

@ -100,6 +100,7 @@ export class Configuration {
oidc_scope?: StringValueItem;
count_per_project: NumberValueItem;
storage_per_project: NumberValueItem;
cfg_expiration: NumberValueItem;
public constructor() {
this.auth_mode = new StringValueItem("db_auth", true);
this.project_creation_restriction = new StringValueItem("everyone", true);

View File

@ -9,6 +9,7 @@ import { GcViewModelFactory } from './gc.viewmodel.factory';
import { CronScheduleComponent } from '../../cron-schedule/cron-schedule.component';
import { CronTooltipComponent } from "../../cron-schedule/cron-tooltip/cron-tooltip.component";
import { of } from 'rxjs';
import { GcJobData } from './gcLog';
describe('GcComponent', () => {
let component: GcComponent;
@ -18,13 +19,17 @@ describe('GcComponent', () => {
systemInfoEndpoint: "/api/system/gc"
};
let mockSchedule = [];
let mockJobs = [
let mockJobs: GcJobData[] = [
{
id: 22222,
schedule: null,
job_status: 'string',
creation_time: new Date(),
update_time: new Date(),
creation_time: new Date().toDateString(),
update_time: new Date().toDateString(),
job_name: 'string',
job_kind: 'string',
job_uuid: 'string',
delete: false
}
];
let spySchedule: jasmine.Spy;

View File

@ -32,7 +32,7 @@ export class GcComponent implements OnInit {
getText = 'CONFIG.GC';
getLabelCurrent = 'GC.CURRENT_SCHEDULE';
@Output() loadingGcStatus = new EventEmitter<boolean>();
@ViewChild(CronScheduleComponent)
@ViewChild(CronScheduleComponent, {static: false})
CronScheduleComponent: CronScheduleComponent;
constructor(
private gcRepoService: GcRepoService,

View File

@ -4,26 +4,17 @@
<div class="modal-body">
<label>{{defaultTextsObj.setQuota}}</label>
<form #quotaForm="ngForm" class="clr-form-compact"
<form #quotaForm="ngForm" class=" clr-form clr-form-horizontal"
[class.clr-form-compact-common]="!defaultTextsObj.isSystemDefaultQuota">
<div class="form-group">
<label for="count" class="required">{{ defaultTextsObj.countQuota | translate}}</label>
<label for="count" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right mr-3px"
[class.invalid]="countInput.invalid && (countInput.dirty || countInput.touched)">
<input name="count" type="text" #countInput="ngModel" class="quota-input"
[(ngModel)]="quotaHardLimitValue.countLimit" pattern="(^-1$)|(^([1-9]+)([0-9]+)*$)" required id="count"
size="40">
<span class="tooltip-content">
{{ 'PROJECT.COUNT_QUOTA_TIP' | translate }}
</span>
</label>
<div class="select-div"></div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right mr-0">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</a>
<clr-input-container>
<label class="left-label required" for="storage">{{ defaultTextsObj?.storageQuota | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</clr-tooltip-content>
</clr-tooltip>
<div class="progress-block progress-min-width progress-div" *ngIf="!defaultTextsObj.isSystemDefaultQuota">
<div class="progress success" [class.warning]="isWarningColor(+quotaHardLimitValue.countLimit, quotaHardLimitValue.countUsed)"
[class.danger]="isDangerColor(+quotaHardLimitValue.countLimit, quotaHardLimitValue.countUsed)">
@ -34,31 +25,29 @@
{{ countInput?.valid?+quotaHardLimitValue?.countLimit===-1 ? ('QUOTA.UNLIMITED' | translate): quotaHardLimitValue?.countLimit:('QUOTA.INVALID_INPUT' | translate)}}
</label>
</div>
</div>
<div class="form-group">
<label for="storage" class="required">{{ defaultTextsObj?.storageQuota | translate}}</label>
<label for="storage" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg mr-3px tooltip-top-right"
[class.invalid]="(storageInput.invalid && (storageInput.dirty || storageInput.touched))||storageInput.errors">
<input name="storage" type="text" #storageInput="ngModel" class="quota-input"
[(ngModel)]="quotaHardLimitValue.storageLimit"
id="storage" size="40">
<span class="tooltip-content">
{{ 'PROJECT.STORAGE_QUOTA_TIP' | translate }}
</span>
</label>
<div class="select-div">
<select clrSelect name="storageUnit" [(ngModel)]="quotaHardLimitValue.storageUnit">
<input clrInput type="text" name="count" #countInput="ngModel" class="quota-input"
[(ngModel)]="quotaHardLimitValue.countLimit" pattern="(^-1$)|(^([1-9]+)([0-9]+)*$)" required id="count"
size="40" />
<clr-control-error>{{ 'PROJECT.COUNT_QUOTA_TIP' | translate }}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="count" class="left-label required">{{ defaultTextsObj.countQuota | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</clr-tooltip-content>
</clr-tooltip>
<div class="clr-select-wrapper">
<select id="select-error" class="clr-select" name="storageUnit" [(ngModel)]="quotaHardLimitValue.storageUnit">
<ng-template ngFor let-quotaUnit [ngForOf]="quotaUnits" let-i="index">
<option *ngIf="i>1" [value]="quotaUnit.UNIT">{{ quotaUnit?.UNIT }}</option>
</ng-template>
</select>
</div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right mr-0">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</a>
<div class="progress-block progress-min-width progress-div" *ngIf="!defaultTextsObj.isSystemDefaultQuota">
<div class="progress success" [class.danger]="isDangerColor(+quotaHardLimitValue.storageLimit,quotaHardLimitValue.storageUsed, quotaHardLimitValue.storageUnit)"
[class.warning]="isWarningColor(+quotaHardLimitValue.storageLimit,quotaHardLimitValue.storageUsed, quotaHardLimitValue.storageUnit)" >
@ -76,7 +65,12 @@
{{+quotaHardLimitValue?.storageLimit ===-1?'':quotaHardLimitValue?.storageUnit }}
</label>
</div>
</div>
</label>
<input clrInput name="storage" type="text" #storageInput="ngModel" class="quota-input"
[(ngModel)]="quotaHardLimitValue.storageLimit"
id="storage" size="40"/>
<clr-control-error>{{ 'PROJECT.STORAGE_QUOTA_TIP' | translate }}</clr-control-error>
</clr-input-container>
</form>
</div>
<div class="modal-footer">

View File

@ -3,44 +3,61 @@
}
.modal-body {
outline: none;
padding-top: 0.8rem;
overflow-y: visible;
overflow-x: visible;
.clr-form-compact {
div.form-group {
padding-left: 8.5rem;
.clr-form {
.left-label {
width: 9.5rem;
}
.mr-3px {
margin-right: 3px;
}
.left-label {
position: relative;
}
.quota-input {
width: 2rem;
padding-right: 0.8rem;
width: 1.5rem;
margin-left: 1rem;
margin-right: 0.2rem;
}
.clr-validate-icon {
margin-left: -0.6rem;
}
.select-div {
width: 2.5rem;
}
::ng-deep .clr-form-control {
margin-top: 0.28rem;
.clr-select-wrapper {
position: absolute;
right: -5rem;
top: -0.08rem;
}
select {
padding-right: 15px;
}
}
}
}
}
.clr-form-compact-common {
div.form-group {
padding-left: 6rem;
.left-label {
width: 7.5rem;
}
.quota-input {
margin-left: 0rem;
}
.select-div {
width: 1.6rem;
}
.clr-select-wrapper {
right: -4rem;
}
}
}
@ -50,35 +67,26 @@
}
.progress-div {
position: relative;
position: absolute;
padding-right: 0.6rem;
width: 9rem;
}
::ng-deep {
.progress {
&.warning>progress {
color: orange;
&::-webkit-progress-value {
background-color: orange;
}
&::-moz-progress-bar {
background-color: orange;
}
}
}
top: 0.2rem;
right: -13.1rem;
}
.progress-label {
position: absolute;
right: -2.3rem;
top: 0;
width: 3.5rem;
right: -3rem;
margin-top: 0;
width: 4rem;
font-weight: 100;
font-size: 10px;
overflow: hidden;
text-overflow: ellipsis;
}
::ng-deep {
.clr-error {
.clr-validate-icon {
margin-left: -12px;
}
}
}

View File

@ -38,10 +38,10 @@ export class EditProjectQuotasComponent implements OnInit {
staticBackdrop = true;
closable = false;
quotaForm: NgForm;
@ViewChild(InlineAlertComponent)
@ViewChild(InlineAlertComponent, {static: false})
inlineAlert: InlineAlertComponent;
@ViewChild('quotaForm')
@ViewChild('quotaForm', {static: true})
currentForm: NgForm;
@Output() confirmAction = new EventEmitter();
quotaDangerCoefficient: number = QUOTA_DANGER_COEFFICIENT;

View File

@ -40,3 +40,19 @@
margin-top: auto;
cursor: pointer;
}
::ng-deep {
.progress {
&.warning>progress {
color: orange;
&::-webkit-progress-value {
background-color: orange;
}
&::-moz-progress-bar {
background-color: orange;
}
}
}
}

View File

@ -33,7 +33,7 @@ const QuotaType = 'project';
export class ProjectQuotasComponent implements OnChanges {
config: Configuration = new Configuration();
@ViewChild('editProjectQuotas')
@ViewChild('editProjectQuotas', {static: false})
editQuotaDialog: EditProjectQuotasComponent;
loading = true;
quotaHardLimitValue: QuotaHardLimitInterface;

View File

@ -29,10 +29,10 @@ export class RegistryConfigComponent implements OnInit {
@Input() hasAdminRole: boolean = false;
@ViewChild("systemSettings") systemSettings: SystemSettingsComponent;
@ViewChild("vulnerabilityConfig") vulnerabilityCfg: VulnerabilityConfigComponent;
@ViewChild("gc") gc: GcComponent;
@ViewChild("cfgConfirmationDialog") confirmationDlg: ConfirmationDialogComponent;
@ViewChild("systemSettings", {static: false}) systemSettings: SystemSettingsComponent;
@ViewChild("vulnerabilityConfig", {static: false}) vulnerabilityCfg: VulnerabilityConfigComponent;
@ViewChild("gc", {static: false}) gc: GcComponent;
@ViewChild("cfgConfirmationDialog", {static: false}) confirmationDlg: ConfirmationDialogComponent;
constructor(
private configService: ConfigurationService,

View File

@ -22,7 +22,7 @@ export class ReplicationConfigComponent {
@Input() showSubTitle: boolean = false;
@ViewChild("replicationConfigFrom") replicationConfigForm: NgForm;
@ViewChild("replicationConfigFrom", { static: false }) replicationConfigForm: NgForm;
get editable(): boolean {
return this.replicationConfig &&

View File

@ -1,107 +1,107 @@
<form #systemConfigFrom="ngForm" class="compact">
<section class="form-block">
<form #systemConfigFrom="ngForm" class="clr-form clr-form-horizontal">
<section>
<label class="subtitle" *ngIf="showSubTitle">{{'CONFIG.SYSTEM' | translate}}</label>
<div class="form-group">
<label for="proCreation">{{'CONFIG.PRO_CREATION_RESTRICTION' | translate}}</label>
<div class="select">
<select id="proCreation" name="proCreation"
<clr-select-container>
<label for="proCreation">{{'CONFIG.PRO_CREATION_RESTRICTION' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<select clrSelect id="proCreation" name="proCreation"
[(ngModel)]="systemSettings.project_creation_restriction.value"
[disabled]="disabled(systemSettings.project_creation_restriction)">
<option value="everyone">{{'CONFIG.PRO_CREATION_EVERYONE' | translate }}</option>
<option value="adminonly">{{'CONFIG.PRO_CREATION_ADMIN' | translate }}</option>
</select>
</div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}}</span>
</a>
</div>
<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)">
<input name="tokenExpiration" type="text" #tokenExpirationInput="ngModel"
[(ngModel)]="systemSettings.token_expiration.value"
required pattern="^[1-9]{1}[0-9]*$" id="tokenExpiration" size="20" [disabled]="!editable">
<span class="tooltip-content">
{{'TOOLTIP.NUMBER_REQUIRED' | translate}}
</span>
</clr-select-container>
<clr-input-container>
<label for="tokenExpiration" class="required">{{'CONFIG.TOKEN_EXPIRATION' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<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.TOKEN_EXPIRATION' | translate}}</span>
</a>
</div>
<div class="form-group">
<label for="robotTokenExpiration" class="required">{{'ROBOT_ACCOUNT.TOKEN_EXPIRATION' | translate}}</label>
<label for="robotTokenExpiration" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="robotTokenExpirationInput.invalid && (robotTokenExpirationInput.dirty || robotTokenExpirationInput.touched)">
<input name="robotTokenExpiration" type="text" #robotTokenExpirationInput="ngModel"
(ngModelChange)="changeToken($event)" [(ngModel)]="robotTokenExpiration"
required pattern="^[1-9]{1}[0-9]*$" id="robotTokenExpiration" size="20"
[disabled]="!robotExpirationEditable">
<span class="tooltip-content">
{{'ROBOT_ACCOUNT.NUMBER_REQUIRED' | translate}}
</span>
<input clrInput name="tokenExpiration" type="text" #tokenExpirationInput="ngModel"
[(ngModel)]="systemSettings.token_expiration.value" required pattern="^[1-9]{1}[0-9]*$"
id="tokenExpiration" size="20" [disabled]="!editable" />
<clr-control-error>{{'TOOLTIP.NUMBER_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="robotTokenExpiration" class="required">{{'ROBOT_ACCOUNT.TOKEN_EXPIRATION' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.ROBOT_TOKEN_EXPIRATION' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<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.ROBOT_TOKEN_EXPIRATION' | translate}}</span>
</a>
</div>
<div class="form-group" *ngIf="canDownloadCert">
<label for="certDownloadLink" class="required">{{'CONFIG.ROOT_CERT' | translate}}</label>
<input clrInput name="robotTokenExpiration" type="text" #robotTokenExpirationInput="ngModel"
(ngModelChange)="changeToken($event)" [(ngModel)]="robotTokenExpiration" required
pattern="^[1-9]{1}[0-9]*$" id="robotTokenExpiration" size="20" [disabled]="!robotExpirationEditable" />
<clr-control-error>{{'ROBOT_ACCOUNT.NUMBER_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<label *ngIf="canDownloadCert" for="certDownloadLink"
class="required clr-control-label mt-1">{{'CONFIG.ROOT_CERT' | translate}}
<a #certDownloadLink [href]="downloadLink" 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>
<div *ngIf="!withAdmiral" class="form-group">
<label for="repoReadOnly">{{'CONFIG.REPO_READ_ONLY' | translate}}</label>
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.ROOT_CERT_DOWNLOAD' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-container *ngIf="!withAdmiral">
<label for="repoReadOnly">{{'CONFIG.REPO_READ_ONLY' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.REPO_TOOLTIP' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="repoReadOnly" id="repoReadOnly"
[ngModel]="systemSettings.read_only.value"
(ngModelChange)="setRepoReadOnlyValue($event)"/>
<label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-top-right read-tooltip">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.REPO_TOOLTIP' | translate}}</span>
</a>
</label>
[ngModel]="systemSettings.read_only.value" (ngModelChange)="setRepoReadOnlyValue($event)" />
</clr-checkbox-wrapper>
</clr-checkbox-container>
<div class="clr-form-control d-f" *ngIf="withClair">
<label for="systemWhitelist"
class="clr-control-label">{{'CVE_WHITELIST.DEPLOYMENT_SECURITY'|translate}}</label>
<div class="form-content">
<div class="font-size-13">
<div class="mt-05">
<span class="title font-size-13">{{'CVE_WHITELIST.CVE_WHITELIST'|translate}}</span>
</div>
<div class="form-group" *ngIf="withClair">
<label for="systemWhitelist">{{'CVE_WHITELIST.DEPLOYMENT_SECURITY'|translate}}</label>
<div class="form-content w-100">
<div>
<div>
<span class="title">{{'CVE_WHITELIST.CVE_WHITELIST'|translate}}</span>
</div>
<div>
<div class="mt-05">
<span>{{'CVE_WHITELIST.SYS_WHITELIST_EXPLAIN'|translate}}</span>
</div>
<div>
<div class="mt-05">
<span>{{'CVE_WHITELIST.ADD_SYS'|translate}}</span>
</div>
<div *ngIf="hasExpired">
<div class="mt-05" *ngIf="hasExpired">
<span class="label label-warning">{{'CVE_WHITELIST.WARNING_SYS'|translate}}</span>
</div>
</div>
<div class="clr-row width-70per">
<div class="clr-col position-relative">
<div class="clr-row width-90per">
<div class="position-relative pl-05">
<div>
<button id="show-add-modal-button" (click)="showAddModal=!showAddModal"
class="btn btn-link">{{'CVE_WHITELIST.ADD'|translate}}</button>
</div>
<div class="add-modal" *ngIf="showAddModal">
<clr-icon (click)="showAddModal=false" class="float-lg-right margin-top-4" shape="window-close"></clr-icon>
<clr-icon (click)="showAddModal=false" class="float-lg-right margin-top-4"
shape="window-close"></clr-icon>
<div>
<clr-textarea-container>
<clr-textarea-container class="flex-direction-column">
<label>{{'CVE_WHITELIST.ENTER'|translate}}</label>
<textarea id="whitelist-textarea" class="w-100 font-italic" clrTextarea [(ngModel)]="cveIds"
name="cveIds"></textarea>
@ -114,8 +114,8 @@
</div>
</div>
<ul class="whitelist-window">
<li *ngIf="systemWhitelist?.items?.length<1"
class="none">{{'CVE_WHITELIST.NONE'|translate}}</li>
<li *ngIf="systemWhitelist?.items?.length<1" class="none">{{'CVE_WHITELIST.NONE'|translate}}
</li>
<li *ngFor="let item of systemWhitelist?.items;let i = index;">
<span class="hand" (click)="goToDetail(item.cve_id)">{{item.cve_id}}</span>
<clr-icon (click)="deleteItem(i)" class="float-lg-right margin-top-4"
@ -124,14 +124,16 @@
</ul>
</div>
<div class="clr-col padding-top-8">
<div class="form-group padding-left-80">
<label for="expires">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
<div class="underline">
<div class="clr-row expire-data">
<label class="bottom-line clr-col-4"
for="expires">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
<div>
<input #dateInput placeholder="{{'CVE_WHITELIST.NEVER_EXPIRES'|translate}}" readonly
type="date" [(clrDate)]="expiresDate" newFormLayout="true">
</div>
</div>
<div class="form-group padding-left-80">
<div class="clr-row">
<label class="clr-col-4"></label>
<clr-checkbox-wrapper>
<input [checked]="neverExpires" [(ngModel)]="neverExpires" type="checkbox" clrCheckbox
name="neverExpires" id="neverExpires" />
@ -144,19 +146,22 @@
</div>
</div>
</div>
<div class="form-group">
<label for="webhookNotificationEnabled">{{'CONFIG.WEBHOOK_NOTIFICATION_ENABLED' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="webhookNotificationEnabled" id="webhookNotificationEnabled" [ngModel]="systemSettings.notification_enable.value"
(ngModelChange)="setWebhookNotificationEnabledValue($event)" [ngModel]="systemSettings.notification_enable.value"/>
<label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right read-tooltip">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.WEBHOOK_TOOLTIP' | translate}}</span>
</a>
<clr-checkbox-container *ngIf="!withAdmiral">
<label for="webhookNotificationEnabled">{{'CONFIG.WEBHOOK_NOTIFICATION_ENABLED' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.WEBHOOK_TOOLTIP' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="webhookNotificationEnabled" id="webhookNotificationEnabled"
[ngModel]="systemSettings.notification_enable.value"
(ngModelChange)="setWebhookNotificationEnabledValue($event)"
[ngModel]="systemSettings.notification_enable.value" />
</clr-checkbox-wrapper>
</div>
</clr-checkbox-container>
</section>
</form>
<div>

View File

@ -3,12 +3,43 @@
font-weight: 600;
}
.create-tooltip {
top: -1;
.clr-form-horizontal {
.clr-form-control {
& >.clr-control-label {
width: 10rem;
}
}
.flex-direction-column {
flex-direction: column;
}
}
.bottom-line {
display: flex;
flex-direction: column-reverse;
}
.expire-data {
min-width: 12.5rem;
margin-top: -1rem;
}
.position-relative {
position: relative;
}
.pl-05 {
padding-left: 0.5rem;
}
.mt-1 {
margin-top: 1rem;
}
.read-tooltip {
top: -7px;
.d-f {
display: flex;
}
.font-size-13 {
font-size: 13px;
}
.mt-05 {
margin-bottom: 0.5rem;
}
.title {
@ -34,18 +65,14 @@
}
}
.width-70per {
width: 70%;
.width-90per {
width: 90%;
}
.none {
color: #ccc;
}
.underline {
border-bottom: 1px solid;
}
.color-0079bb {
color: #0079bb;
}

View File

@ -66,9 +66,9 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
@Input() hasCAFile: boolean = false;
@Input() withAdmiral = false;
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
@ViewChild("cfgConfirmationDialog") confirmationDlg: ConfirmationDialogComponent;
@ViewChild('dateInput') dateInput: ElementRef;
@ViewChild("systemConfigFrom", {static: false}) systemSettingsForm: NgForm;
@ViewChild("cfgConfirmationDialog", {static: false}) confirmationDlg: ConfirmationDialogComponent;
@ViewChild('dateInput', {static: false}) dateInput: ElementRef;
get editable(): boolean {
return this.systemSettings &&

View File

@ -34,7 +34,7 @@ export class VulnerabilityConfigComponent implements OnInit {
openState: boolean = false;
getLabelCurrent: string;
@ViewChild(CronScheduleComponent)
@ViewChild(CronScheduleComponent, {static: false})
CronScheduleComponent: CronScheduleComponent;
@Input()
@ -109,7 +109,7 @@ export class VulnerabilityConfigComponent implements OnInit {
};
}
}
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
@ViewChild("systemConfigFrom", {static: false}) systemSettingsForm: NgForm;
get isValid(): boolean {
return this.systemSettingsForm && this.systemSettingsForm.valid;

View File

@ -9,98 +9,96 @@
</span>
</div>
</div>
<form #targetForm="ngForm">
<section class="form-block">
<form #targetForm="ngForm" class="clr-form clr-form-horizontal">
<!-- provider -->
<div class="form-group">
<label class="form-group-label-override required">{{'DESTINATION.PROVIDER' | translate}}</label>
<div class="form-select">
<div class="select inputWidth pull-left">
<select name="adapter" id="adapter" (change)="adapterChange($event)" [(ngModel)]="target.type" [disabled]="testOngoing || editDisabled">
<clr-select-container>
<label class="required">{{'DESTINATION.PROVIDER' | translate}}</label>
<select clrSelect name="adapter" id="adapter" (change)="adapterChange($event)" [(ngModel)]="target.type" [disabled]="testOngoing || editDisabled">
<option *ngFor="let adapter of adapterList" value="{{adapter}}">{{adapter}}</option>
</select>
</div>
</div>
</div>
</clr-select-container>
<!-- Endpoint name -->
<div class="form-group">
<label for="destination_name" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.NAME' |
translate }}</label>
<label class="col-md-8" for="destination_name" aria-haspopup="true" role="tooltip" [class.invalid]="targetName.errors && (targetName.dirty || targetName.touched)"
[class.valid]="targetName.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
<input type="text" id="destination_name" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.name"
name="targetName" size="25" #targetName="ngModel" required>
<span class="tooltip-content" *ngIf="targetName.errors && targetName.errors.required && (targetName.dirty || targetName.touched)">
<clr-input-container>
<label class="required">{{ 'DESTINATION.NAME' | translate }}</label>
<input clrInput type="text" id="destination_name" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.name"
name="targetName" size="30" #targetName="ngModel" required>
<clr-control-error *ngIf="targetName.errors && targetName.errors.required && (targetName.dirty || targetName.touched)">
{{ 'DESTINATION.NAME_IS_REQUIRED' | translate }}
</span>
</label>
</div>
</clr-control-error>
</clr-input-container>
<!--Description-->
<div class="form-group">
<label class="form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
<textarea type="text" class="inputWidth" row=3 name="description" [(ngModel)]="target.description"></textarea>
</div>
<clr-textarea-container>
<label>{{'REPLICATION.DESCRIPTION' | translate}}</label>
<textarea clrTextarea type="text" class="inputWidth" row=3 name="description" [(ngModel)]="target.description"></textarea>
</clr-textarea-container>
<!-- Endpoint Url -->
<div class="form-group">
<label for="destination_url" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.URL' |
translate }}</label>
<label class="col-md-8" for="destination_url" aria-haspopup="true" role="tooltip" [class.invalid]="targetEndpoint?.errors && (targetEndpoint?.dirty || targetEndpoint?.touched)"
[class.valid]="targetEndpoint?.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left clr-select-wrapper">
<input *ngIf="!endpointList.length" type="text" id="destination_url" [disabled]="testOngoing || urlDisabled" [readonly]="!editable" [(ngModel)]="target.url"
size="25" name="endpointUrl" #targetEndpoint="ngModel" required placeholder="http(s)://192.168.1.1">
<select id="destination_url" *ngIf="endpointList.length" [(ngModel)]="target.url" class="clr-select" name="endpointUrl" #targetEndpoint="ngModel">
<div class="clr-form-control">
<label for="destination_url" class="required clr-control-label">{{ 'DESTINATION.URL' | translate }}</label>
<div class="clr-control-container" [class.clr-error]="targetEndpoint?.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)">
<div class="clr-input-wrapper" *ngIf="!endpointList.length">
<input class="clr-input" type="text" id="destination_url" [disabled]="testOngoing || urlDisabled" [readonly]="!editable"
[(ngModel)]="target.url" size="30" name="endpointUrl" #targetEndpoint="ngModel" required placeholder="http(s)://192.168.1.1">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
</div>
<div class="clr-select-wrapper" *ngIf="endpointList.length">
<select id="destination_url" class="clr-select" *ngIf="endpointList.length" [(ngModel)]="target.url"
name="endpointUrl" #targetEndpoint="ngModel">
<option class="display-none" value=""></option>
<option *ngFor="let endpoint of endpointList" value="{{endpoint.value}}">{{endpoint.key}}</option>
</select>
<span class="tooltip-content" *ngIf="targetEndpoint?.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)">
</div>
<clr-control-error *ngIf="targetEndpoint?.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)">
{{ 'DESTINATION.URL_IS_REQUIRED' | translate }}
</span>
</label>
</clr-control-error>
</div>
</div>
<!-- access_key -->
<div class="form-group">
<label for="destination_access_key" class="col-md-4 form-group-label-override">{{ 'DESTINATION.ACCESS_ID' |
translate }}</label>
<input type="text" placeholder="Access ID" class="col-md-8" id="destination_access_key" [disabled]="testOngoing" [readonly]="target.type ==='google-gcr' || !editable"
[(ngModel)]="target.credential.access_key" size="28" name="access_key" #access_key="ngModel">
</div>
<clr-input-container>
<label>{{ 'DESTINATION.ACCESS_ID' | translate }}</label>
<input clrInput type="text" placeholder="Access ID" id="destination_access_key" [disabled]="testOngoing" [readonly]="target.type ==='google-gcr' || !editable"
[(ngModel)]="target.credential.access_key" size="30" name="access_key" #access_key="ngModel">
</clr-input-container>
<!-- access_secret -->
<div class="form-group">
<label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.ACCESS_SECRET' |
translate }}</label>
<input *ngIf="target.type !=='google-gcr';else gcr_secret" type="password" placeholder="Access Secret" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="!editable"
[(ngModel)]="target.credential.access_secret" size="28" name="access_secret" #access_secret="ngModel">
<div class="clr-form-control">
<label for="destination_password" class="clr-control-label">{{ 'DESTINATION.ACCESS_SECRET' | translate }}</label>
<div class="clr-control-container">
<div class="clr-textarea-wrapper">
<input class="clr-input" *ngIf="target.type !=='google-gcr';else gcr_secret" type="password" placeholder="Access Secret"
id="destination_password" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.credential.access_secret"
size="30" name="access_secret" #access_secret="ngModel">
<ng-template #gcr_secret>
<textarea type="text" row="3" placeholder="Json Secret" class="inputWidth" id="destination_password" [disabled]="testOngoing" [readonly]="!editable"
[(ngModel)]="target.credential.access_secret" name="access_secret" #access_secret="ngModel"></textarea>
<textarea type="text" row="3" placeholder="Json Secret" class="clr-textarea" id="destination_password" [disabled]="testOngoing"
[readonly]="!editable" [(ngModel)]="target.credential.access_secret" name="access_secret" #access_secret="ngModel"></textarea>
</ng-template>
</div>
</div>
</div>
<!-- Verify Remote Cert -->
<div class="form-group">
<label for="destination_insecure" id="destination_insecure_checkbox">{{'CONFIG.VERIFY_REMOTE_CERT' |
translate }}</label>
<input type="checkbox" clrCheckbox #insecure id="destination_insecure" [disabled]="testOngoing || !editable"
name="insecure" [ngModel]="!target.insecure" (ngModelChange)="setInsecureValue($event)" class="clr-checkbox">
<clr-checkbox-container>
<label id="destination_insecure_checkbox"
for="destination_insecure">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen>
{{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate}}
</clr-tooltip-content>
</clr-tooltip>
</div>
<div class="form-group" class="form-height">
</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox #insecure id="destination_insecure" [disabled]="testOngoing || !editable" name="insecure"
[ngModel]="!target.insecure" (ngModelChange)="setInsecureValue($event)">
</clr-checkbox-wrapper>
</clr-checkbox-container>
<div class="clr-form-control" class="form-height">
<label for="spin" class="col-md-4"></label>
<span class="col-md-8 spinner spinner-inline" [hidden]="!inProgress"></span>
</div>
</section>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="testConnection()" [disabled]="inProgress || (targetEndpoint?.errors)">{{
'DESTINATION.TEST_CONNECTION' | translate }}</button>
<button type="button" class="btn btn-outline" (click)="onCancel()" [disabled]="inProgress">{{ 'BUTTON.CANCEL' |
translate }}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()" [disabled]="!isValid">{{ 'BUTTON.OK' | translate
}}</button>
<button type="button" class="btn btn-outline" (click)="testConnection()" [disabled]="inProgress || (targetEndpoint?.errors)">{{ 'DESTINATION.TEST_CONNECTION' | translate }}</button>
<button type="button" class="btn btn-outline" (click)="onCancel()" [disabled]="inProgress">{{ 'BUTTON.CANCEL' | translate }}</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()" [disabled]="!isValid">{{ 'BUTTON.OK' | translate }}
</button>
</div>
</clr-modal>

View File

@ -63,13 +63,13 @@ export class CreateEditEndpointComponent
selectedType: string;
initVal: Endpoint;
targetForm: NgForm;
@ViewChild("targetForm") currentForm: NgForm;
@ViewChild("targetForm", {static: false}) currentForm: NgForm;
targetEndpoint;
testOngoing: boolean;
onGoing: boolean;
endpointId: number | string;
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
@ViewChild(InlineAlertComponent, {static: false}) inlineAlert: InlineAlertComponent;
@Output() reload = new EventEmitter<boolean>();

View File

@ -3,13 +3,13 @@
<section>
<label>
<label for="name">{{'LABEL.LABEL_NAME' | translate}}</label>
<label aria-haspopup="true" role="tooltip" [class.invalid]="isLabelNameExist"
class="tooltip tooltip-validation tooltip-md tooltip-bottom-left">
<input type="text" id="name" name="name" required size="20" autocomplete="off"
<label class="clr-control-container" [class.clr-error]="isLabelNameExist">
<input clrInput type="text" id="name" name="name" required size="20" autocomplete="off"
[(ngModel)]="labelModel.name" #name="ngModel" (keyup)="existValid(labelModel.name)">
<span class="tooltip-content">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<clr-control-error class="position-ab" *ngIf="isLabelNameExist">
{{'LABEL.NAME_ALREADY_EXISTS' | translate }}
</span>
</clr-control-error>
</label>
</label>
<label>
@ -21,11 +21,11 @@
[class.borderSty]="i.color == '#FFFFFF'" [ngStyle]="{'background-color': i.color, 'color': i.textColor }">Aa</label>
</clr-dropdown-menu>
</clr-dropdown>
<input type="text" id="color" size="8" name="color" disabled [(ngModel)]="labelModel.color" #color="ngModel">
<input clrInput type="text" id="color" size="8" name="color" disabled [(ngModel)]="labelModel.color" #color="ngModel">
</label>
<label>
<label for="description">{{'LABEL.DESCRIPTION' | translate}}</label>
<input type="text" id="description" name="description" size="30" [(ngModel)]="labelModel.description"
<input clrInput type="text" id="description" name="description" size="30" [(ngModel)]="labelModel.description"
#description="ngModel">
</label>
<label>

View File

@ -62,4 +62,11 @@ form {
line-height: 22px;
}
}
::ng-deep {
.clr-form-control {
display: inherit;
}
}
.position-ab {
position: absolute;
}

View File

@ -58,7 +58,7 @@ describe("CreateEditLabelComponent (inline template)", () => {
labelService = fixture.debugElement.injector.get(LabelService);
spy = spyOn(labelService, "getLabels").and.returnValue(
of(mockOneData)
of([mockOneData])
);
spyOne = spyOn(labelService, "createLabel").and.returnValue(
of(mockOneData)

View File

@ -52,7 +52,7 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
nameChecker = new Subject<string>();
labelForm: NgForm;
@ViewChild("labelForm") currentForm: NgForm;
@ViewChild("labelForm", {static: true}) currentForm: NgForm;
@Input() projectId: number;
@Input() scope: string;
@ -71,7 +71,7 @@ export class CreateEditLabelComponent implements OnInit, OnDestroy {
this.isLabelNameExist = false;
if (targets && targets.length) {
if (targets.find((target) => {
return target.name === name;
return target.name === name && target.id !== this.labelId;
})) {
this.isLabelNameExist = true;
}

View File

@ -2,65 +2,74 @@
<h3 class="modal-title">{{headerTitle | translate}}</h3>
<hbr-inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></hbr-inline-alert>
<div class="modal-body modal-body-height">
<form [formGroup]="ruleForm" novalidate>
<section class="form-block">
<div class="form-group form-group-override">
<label class="form-group-label-override required">{{'REPLICATION.NAME' | translate}}</label>
<label aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || !isRuleNameValid'>
<input type="text" id="ruleName" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" class="inputWidth" required maxlength="255" formControlName="name"
#ruleName (keyup)='checkRuleName()' autocomplete="off">
<span class="tooltip-content">{{ruleNameTooltip | translate}}</span>
</label>
<form [formGroup]="ruleForm" novalidate class="clr-form clr-form-horizontal">
<div class="clr-form-control" [class.clr-error]="(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || !isRuleNameValid">
<label class="clr-control-label required">{{'REPLICATION.NAME' | translate}}</label>
<div class="clr-control-container">
<div class="clr-input-wrapper">
<input class="clr-input" type="text" id="ruleName" size="35" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" required maxlength="255"
formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<span class="spinner spinner-inline spinner-pos" [hidden]="!inNameChecking"></span>
</div>
<!--Description-->
<div class="form-group form-group-override">
<label class="form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
<textarea type="text" id="ruleDescription" class="inputWidth" row=3 formControlName="description"></textarea>
<clr-control-error *ngIf="(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || !isRuleNameValid">{{ruleNameTooltip | translate}}</clr-control-error>
</div>
</div>
<!--Description-->
<clr-textarea-container>
<label>{{'REPLICATION.DESCRIPTION' | translate}}</label>
<textarea clrTextarea type="text" id="ruleDescription" class="inputWidth" row=3 formControlName="description"></textarea>
</clr-textarea-container>
<!-- replication mode -->
<div class="form-group form-group-override">
<label class="form-group-label-override">{{'REPLICATION.REPLI_MODE' | translate}}</label>
<div class="radio-inline" [class.disabled]="policyId >= 0 || onGoing">
<input type="radio" id="push_base" name="replicationMode" [value]=true [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode" (change)="pushModeChange()" [ngModelOptions]="{standalone: true}">
<label for="push_base">Push-based</label>
<clr-radio-container clrInline>
<label>{{'REPLICATION.REPLI_MODE' | translate}}</label>
<clr-radio-wrapper>
<input clrRadio type="radio" id="push_base" name="replicationMode" [value]=true [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode"
(change)="pushModeChange()" [ngModelOptions]="{standalone: true}">
<label for="push_base">Push-based
<clr-tooltip class="mode-tooltip">
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.PUSH_BASED' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
<div class="radio-inline" [class.disabled]="policyId >= 0 || onGoing">
<input type="radio" id="pull_base" name="replicationMode" [value]=false [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode" (change)="pullModeChange()" [ngModelOptions]="{standalone: true}">
<label for="pull_base">Pull-based</label>
</label>
</clr-radio-wrapper>
<clr-radio-wrapper>
<input clrRadio type="radio" id="pull_base" name="replicationMode" [value]=false [disabled]="policyId >= 0 || onGoing" [(ngModel)]="isPushMode"
(change)="pullModeChange()" [ngModelOptions]="{standalone: true}">
<label for="pull_base">Pull-based
<clr-tooltip class="mode-tooltip">
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.PULL_BASED' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
</div>
</label>
</clr-radio-wrapper>
</clr-radio-container>
<!--source registry-->
<div *ngIf="!isPushMode" class="form-group form-group-override">
<label class="form-group-label-override required">{{'REPLICATION.SOURCE_REGISTRY' | translate}}</label>
<div class="form-select">
<div class="select endpointSelect pull-left">
<select id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry" [compareWith]="equals">
<div class="clr-form-control" *ngIf="!isPushMode">
<label class="required clr-control-label">{{'REPLICATION.SOURCE_REGISTRY' | translate}}</label>
<div class="clr-control-container">
<div class="clr-select-wrapper">
<select class="clr-select select-width" id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry"
[compareWith]="equals">
<option class="display-none"></option>
<option *ngFor="let source of sourceList" [ngValue]="source">{{source.name}}-{{source.url}}</option>
</select>
</div>
<div class="space-between">
<span *ngIf="noEndpointInfo.length != 0" class="alert-label">{{noEndpointInfo | translate}}</span>
<span class="alert-label go-link" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
</div>
</div>
<label *ngIf="noEndpointInfo.length != 0" class="colorRed alertLabel">{{noEndpointInfo | translate}}</label>
<span class="alertLabel goLink" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
</div>
<!--images/Filter-->
<div class="form-group form-group-override">
<label class="form-group-label-override">{{'REPLICATION.SOURCE_RESOURCE_FILTER' | translate}}</label>
<div class="clr-form-control">
<label class="clr-control-label">{{'REPLICATION.SOURCE_RESOURCE_FILTER' | translate}}</label>
<span class="spinner spinner-inline spinner-position" [hidden]="onGoing === false"></span>
<div formArrayName="filters">
<div formArrayName="filters" class="clr-control-container">
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index">
<div [formGroupName]="i" *ngIf="supportedFilters[i]?.type !=='label' || (supportedFilters[i]?.type==='label' && supportedFilterLabels?.length)">
<div class="width-70">
@ -68,17 +77,17 @@
</div>
<label *ngIf="supportedFilters[i]?.style==='input'" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
[class.invalid]='(filter.value.dirty || filter.value.touched) && filter.value.invalid'>
<input (input)="trimText($event)" type="text" #filterValue required size="14" formControlName="value" id="{{'filter_'+ supportedFilters[i]?.type}}">
<span class="tooltip-content">{{'TOOLTIP.EMPTY' | translate}}</span>
<input class="clr-input" (input)="trimText($event)" type="text" #filterValue size="14" formControlName="value" id="{{'filter_'+ supportedFilters[i]?.type}}">
</label>
<div class="select resource-box" *ngIf="supportedFilters[i]?.style==='radio' && supportedFilters[i]?.values.length > 1">
<select formControlName="value" #selectedValue id="{{'select_'+ supportedFilters[i]?.type}}" name="{{supportedFilters[i]?.type}}">
<div class="select resource-box clr-select-wrapper" *ngIf="supportedFilters[i]?.style==='radio' && supportedFilters[i]?.values.length > 1">
<select class="clr-select width-100" formControlName="value" #selectedValue id="{{'select_'+ supportedFilters[i]?.type}}"
name="{{supportedFilters[i]?.type}}">
<option value="">{{'REPLICATION.BOTH' | translate}}</option>
<option *ngFor="let value of supportedFilters[i]?.values;" value="{{value}}">{{value}}</option>
</select>
</div>
<div class="select resource-box" *ngIf="supportedFilters[i]?.type==='label'&& supportedFilters[i]?.style==='list'">
<div class="dropdown width-100" formArrayName="value">
<div class="dropdown width-100 clr-select-wrapper" formArrayName="value">
<clr-dropdown class="width-100">
<button type="button" class="width-100 dropdown-toggle btn btn-link statistic-data label-text" clrDropdownTrigger>
<ng-template ngFor let-label [ngForOf]="filter.value.value" let-m="index">
@ -86,13 +95,16 @@
</ng-template>
<span class="ellipsis" *ngIf="filter.value.value.length>1">···</span>
<div *ngFor="let label1 of filter.value.value;let k = index" hidden="true">
<input type="text" [formControlName]="k" #labelValue id="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}" name="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}" placeholder="select labels" >
<input type="text" [formControlName]="k" #labelValue id="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}" name="{{'label_'+ supportedFilters[i]?.type + '_' + label1}}"
placeholder="select labels">
</div>
</button>
<clr-dropdown-menu class="width-100" clrPosition="bottom-left" *clrIfOpen>
<button type="button" class="dropdown-item" *ngFor="let value of supportedFilterLabels" (click)="stickLabel(value,i)">
<clr-icon shape="check" [hidden]="!value.select" class='pull-left'></clr-icon>
<div class='labelDiv'><hbr-label-piece [label]="value" [labelWidth]="130"></hbr-label-piece></div>
<div class='labelDiv'>
<hbr-label-piece [label]="value" [labelWidth]="130"></hbr-label-piece>
</div>
</button>
</clr-dropdown-menu>
</clr-dropdown>
@ -115,56 +127,60 @@
</div>
</div>
<!--destination registry-->
<div *ngIf="isPushMode" class="form-group form-group-override">
<label class="form-group-label-override required">{{'REPLICATION.DEST_REGISTRY' | translate}}</label>
<div class="form-select">
<div class="select endpointSelect pull-left">
<select id="dest_registry" (change)="targetChange($event)" formControlName="dest_registry" [compareWith]="equals">
<option class="display-none"></option>
<option *ngFor="let target of targetList" [ngValue]="target">{{target.name}}-{{target.url}}</option>
</select>
</div>
</div>
<label *ngIf="noEndpointInfo.length != 0" class="colorRed alertLabel">{{noEndpointInfo | translate}}</label>
<span class="alertLabel goLink" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
</div>
<!--destination namespaces -->
<div class="form-group form-group-override">
<label class="form-group-label-override">{{'REPLICATION.DEST_NAMESPACE' | translate}}</label>
<div class="form-select">
<div class="endpointSelect pull-left">
<label aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='ruleForm.controls.dest_namespace.touched && ruleForm.controls.dest_namespace.invalid'>
<input formControlName="dest_namespace" type="text" id="dest_namespace" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" class="inputWidth"
maxlength="255">
<span class="tooltip-content">{{'REPLICATION.DESTINATION_NAME_TOOLTIP' | translate}}</span>
</label>
</div>
<div *ngIf="isPushMode" class="clr-form-control">
<label class="clr-control-label required">{{'REPLICATION.DEST_REGISTRY' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.DESTINATION_NAMESPACE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<div class="form-select clr-control-container">
<div class="clr-select-wrapper">
<select class="clr-select select-width" id="dest_registry" (change)="targetChange($event)" formControlName="dest_registry"
[compareWith]="equals">
<option class="display-none"></option>
<option *ngFor="let target of targetList" [ngValue]="target">{{target.name}}-{{target.url}}</option>
</select>
</div>
<div class="space-between">
<label *ngIf="noEndpointInfo.length != 0" class="alert-label">{{noEndpointInfo | translate}}</label>
<span class="alert-label go-link" *ngIf="noEndpointInfo.length != 0" (click)="goRegistry()">{{'REPLICATION.ENDPOINTS' | translate}}</span>
</div>
</div>
</div>
<!--destination namespaces -->
<clr-input-container>
<label>{{'REPLICATION.DEST_NAMESPACE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.DESTINATION_NAMESPACE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<input clrInput formControlName="dest_namespace" type="text" id="dest_namespace" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
class="inputWidth" maxlength="255">
<clr-control-error *ngIf='ruleForm.controls.dest_namespace.touched && ruleForm.controls.dest_namespace.invalid'>{{'REPLICATION.DESTINATION_NAME_TOOLTIP' | translate}}</clr-control-error>
</clr-input-container>
<!--Trigger-->
<div class="form-group form-group-override">
<label class="form-group-label-override required">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
<div class="clr-form-control">
<label class="clr-control-label required">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
<div class="clr-control-container">
<div formGroupName="trigger">
<!--on trigger-->
<div class="select width-115">
<select id="ruleTrigger" formControlName="type">
<div class="select width-115 clr-select-wrapper">
<select id="ruleTrigger" formControlName="type" class="clr-select">
<option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{'REPLICATION.' + trigger.toUpperCase() | translate }}</option>
</select>
</div>
<!--on push-->
<div formGroupName="trigger_settings">
<div [hidden]="isNotSchedule()" class="form-group form-cron">
<div formGroupName="trigger_settings" class="clr-form-control">
<div [hidden]="isNotSchedule()">
<label class="required">Cron String</label>
<label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-sm tooltip-top-right"
[class.invalid]="!isNotSchedule() && cronTouched && !cronInputValid(ruleForm.value.trigger?.trigger_settings?.cron || '')">
<input type="text" name=targetCron id="targetCron" required class="form-control cron-input" formControlName="cron">
<input type="text" name=targetCron id="targetCron" required class="form-control cron-input clr-input" formControlName="cron">
<span class="tooltip-content">
{{'TOOLTIP.CRON_REQUIRED' | translate }}
</span>
@ -178,35 +194,30 @@
</div>
</div>
</div>
<div [hidden]="isNotEventBased()" class="clr-form-control rule-width">
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox [checked]="false" id="ruleDeletion" formControlName="deletion" class="clr-checkbox">
<label for="ruleDeletion" class="clr-control-label">{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}</label>
</clr-checkbox-wrapper>
<div class="clr-checkbox-wrapper clr-form-control" [hidden]="isNotEventBased()">
<input type="checkbox" class="clr-checkbox" [checked]="false" id="ruleDeletion" formControlName="deletion">
<label for="ruleDeletion">{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}</label>
</div>
<div class="clr-form-control rule-width override-box">
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox [checked]="true" id="overridePolicy" formControlName="override" class="clr-checkbox">
<label for="overridePolicy" class="clr-control-label">{{'REPLICATION.OVERRIDE_INFO' | translate}}</label>
</clr-checkbox-wrapper>
<div class="clr-checkbox-wrapper clr-form-control">
<input type="checkbox" class="clr-checkbox" [checked]="true" id="overridePolicy" formControlName="override">
<label for="overridePolicy">{{'REPLICATION.OVERRIDE_INFO' | translate}}
<clr-tooltip class="override-tooltip">
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
<span>{{'TOOLTIP.OVERRIDE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</div>
<div class="clr-form-control rule-width">
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox [checked]="true" id="enablePolicy" formControlName="enabled" class="clr-checkbox">
<div class="clr-checkbox-wrapper clr-form-control">
<input type="checkbox" [checked]="true" id="enablePolicy" formControlName="enabled" class="clr-checkbox">
<label for="enablePolicy" class="clr-control-label">{{'REPLICATION.ENABLED_RULE' | translate}}</label>
</clr-checkbox-wrapper>
</div>
</div>
</div>
<div class="loading-center">
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
</div>
</section>
</form>
</div>
<div class="modal-footer">

View File

@ -14,19 +14,8 @@ h4 {
color: #666;
}
.colorRed {
.alert-label {
color:red;
}
.colorRed a {
text-decoration: underline;
color: #007CBB;
}
.alertLabel {
display: block;
margin-top: 2px;
line-height: 1em;
font-size: 12px;
}
@ -42,6 +31,7 @@ h4 {
.filterSelect {
position: relative;
width: 315px;
margin-bottom:0.5rem;
}
.filterSelect clr-icon {
@ -86,10 +76,6 @@ h4 {
margin-right: 4px;
}
.form-group {
min-height: 36px;
}
.projectInput {
float: left;
position: relative;
@ -167,10 +153,10 @@ h4 {
margin-right: 120px;
}
.goLink {
.go-link {
color: blue;
border-bottom: 1px solid blue;
line-height: 14px;
line-height: 1rem;
cursor: pointer;
}
@ -294,3 +280,12 @@ clr-modal {
font-size: 16px;
font-weight: 700;
}
.space-between {
display: flex;
justify-content: space-between;
}
.select-width {
min-width:11rem;
}

View File

@ -75,7 +75,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
@Output() goToRegistry = new EventEmitter<any>();
@Output() reload = new EventEmitter<boolean>();
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
@ViewChild(InlineAlertComponent, {static: true}) inlineAlert: InlineAlertComponent;
constructor(
private fb: FormBuilder,
private repService: ReplicationService,

View File

@ -23,7 +23,7 @@
</div>
<div class="setting-wrapper flex-layout" *ngIf="isEditMode">
<span class="font-style">{{ labelEdit | translate }}</span>
<div class="select select-schedule">
<div class="select select-schedule clr-select-wrapper">
<select name="selectPolicy" id="selectPolicy" [(ngModel)]="scheduleType">
<option [value]="SCHEDULE_TYPE.NONE">{{'SCHEDULE.NONE' | translate}}</option>
<option [value]="SCHEDULE_TYPE.HOURLY">{{'SCHEDULE.HOURLY' | translate}}</option>
@ -35,7 +35,7 @@
<span class="required" [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }}</span>
<div [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM" class="cron-input">
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="dateInvalid" class="tooltip tooltip-validation tooltip-md tooltip-top-left cron-label">
<input type="text" (blur)="blurInvalid()" (input)="inputInvalid()" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="form-control"
<input type="text" (blur)="blurInvalid()" (input)="inputInvalid()" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="clr-input form-control"
[(ngModel)]="cronString">
<span class="tooltip-content" *ngIf="dateInvalid">
{{'TOOLTIP.CRON_REQUIRED' | translate }}

View File

@ -17,7 +17,7 @@ export class DatePickerComponent implements OnChanges {
@Input() dateInput: string;
@Input() oneDayOffset: boolean;
@ViewChild("searchTime") searchTime: NgModel;
@ViewChild("searchTime", {static: true}) searchTime: NgModel;
@Output() search = new EventEmitter<string>();

View File

@ -142,7 +142,7 @@ describe("EndpointComponent (inline template)", () => {
spyOnRules = spyOn(
endpointService,
"getEndpointWithReplicationRules"
).and.returnValue([]);
).and.returnValue(of([]));
spyOne = spyOn(endpointService, "getEndpoint").and.returnValue(
of(mockOne[0])
);

View File

@ -53,10 +53,10 @@ import { OperationService } from "../operation/operation.service";
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EndpointComponent implements OnInit, OnDestroy {
@ViewChild(CreateEditEndpointComponent)
@ViewChild(CreateEditEndpointComponent, {static: false})
createEditEndpointComponent: CreateEditEndpointComponent;
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialogComponent: ConfirmationDialogComponent;
targets: Endpoint[];

View File

@ -1,6 +1,6 @@
<span>
<clr-icon shape="search" size="20" class="search-btn" [class.filter-icon]="isShowSearchBox" (click)="onClick()"></clr-icon>
<input [hidden]="!isShowSearchBox" type="text" class="filter-input" autofocus (keyup)="valueChange()" (focus)="inputFocus()"
<input [hidden]="!isShowSearchBox" type="text" class="filter-input clr-input" autofocus (keyup)="valueChange()" (focus)="inputFocus()"
placeholder="{{placeHolder}}" [(ngModel)]="currentValue" />
<span class="filter-divider" *ngIf="withDivider"></span>
</span>

View File

@ -61,8 +61,8 @@ export class GridViewComponent implements AfterViewInit {
@Output() loadNextPageEvent = new EventEmitter<any>();
@ViewChildren("cardItem") cards: any;
@ViewChild("itemsHolder") itemsHolder: any;
@ContentChild(TemplateRef) gridItemTmpl: any;
@ViewChild("itemsHolder", {static: false}) itemsHolder: any;
@ContentChild(TemplateRef, {static: false}) gridItemTmpl: any;
_items: any[] = [];

View File

@ -9,6 +9,7 @@ import { ChannelService } from "../channel/index";
import { Project } from "../project-policy-config/project";
import { IServiceConfig, SERVICE_CONFIG } from "../service.config";
import { of } from "rxjs";
import { HttpResponse } from "@angular/common/http";
describe("ImageNameInputComponent (inline template)", () => {
let comp: ImageNameInputComponent;
@ -55,7 +56,7 @@ describe("ImageNameInputComponent (inline template)", () => {
let projectService: ProjectService;
projectService = fixture.debugElement.injector.get(ProjectService);
spy = spyOn(projectService, "listProjects").and.returnValues(of(mockProjects));
spy = spyOn(projectService, "listProjects").and.returnValues(of(new HttpResponse({ body: mockProjects })));
});
it("should load data", async(() => {

View File

@ -56,9 +56,9 @@ export class LabelComponent implements OnInit {
@Input() hasUpdateLabelPermission: boolean;
@Input() hasDeleteLabelPermission: boolean;
@ViewChild(CreateEditLabelComponent)
@ViewChild(CreateEditLabelComponent, {static: false})
createEditLabel: CreateEditLabelComponent;
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialogComponent: ConfirmationDialogComponent;
constructor(private labelService: LabelService,

View File

@ -85,10 +85,10 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
selectedRow: ReplicationRule;
@ViewChild("toggleConfirmDialog")
@ViewChild("toggleConfirmDialog", {static: false})
toggleConfirmDialog: ConfirmationDialogComponent;
@ViewChild("deletionConfirmDialog")
@ViewChild("deletionConfirmDialog", {static: false})
deletionConfirmDialog: ConfirmationDialogComponent;
startTimeComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("start_time", "date");

View File

@ -2,7 +2,7 @@
<div class="row flex-items-xs-between flex-items-xs-bottom">
<div></div>
<div class="action-head-pos">
<div class="select filterTag" [hidden]="!isOpenFilterTag">
<div class="select filter-tag clr-select-wrapper" [hidden]="!isOpenFilterTag">
<select id="selectKey" (change)="selectFilterKey($event)">
<option value="username">{{"AUDIT_LOG.USERNAME" | translate | lowercase}}</option>
<option value="repository">{{"CONFIG.REPOSITORY" | translate | lowercase}}</option>
@ -20,11 +20,11 @@
</div>
<div>
<clr-datagrid (clrDgRefresh)="load($event)" [clrDgLoading]="loading">
<clr-dg-column [clrDgField]="'username'">{{'AUDIT_LOG.USERNAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'repo_name'">{{'AUDIT_LOG.REPOSITORY_NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'repo_tag'">{{'AUDIT_LOG.TAGS' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'operation'">{{'AUDIT_LOG.OPERATION' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="opTimeComparator">{{'AUDIT_LOG.TIMESTAMP' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.USERNAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.REPOSITORY_NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.TAGS' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.OPERATION' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.TIMESTAMP' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{ 'AUDIT_LOG.NOT_FOUND' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let l of recentLogs">
<clr-dg-cell>{{l.username}}</clr-dg-cell>

View File

@ -52,7 +52,7 @@
margin-top: 4px;
}
.filterTag {
.filter-tag {
float: left;
margin-top: 8px;
}

View File

@ -76,7 +76,7 @@ describe('RecentLogComponent (inline template)', () => {
spy = spyOn(logService, 'getRecentLogs')
.and.callFake(function (params: RequestQueryParams) {
if (params && params.get('username')) {
return of(mockData2).pipe(delay(0));
return of(mockData2);
} else {
if (params.get('page') === '1') {
mockData.data = mockItems.slice(0, 15);

View File

@ -24,10 +24,8 @@ import { ErrorHandler } from '../error-handler/index';
import { CustomComparator } from '../utils';
import {
DEFAULT_PAGE_SIZE,
calculatePage,
doFiltering,
doSorting
} from '../utils';
import { finalize } from "rxjs/operators";
@Component({
selector: 'hbr-log',
@ -43,14 +41,8 @@ export class RecentLogComponent implements OnInit {
defaultFilter = "username";
isOpenFilterTag: boolean;
@Input() withTitle: boolean = false;
pageSize: number = DEFAULT_PAGE_SIZE;
pageSize: number = 3;
currentPage: number = 1; // Double bound to pagination component
currentPagePvt: number = 0; // Used to confirm whether page is changed
currentState: State;
opTimeComparator: Comparator<AccessLogItem> = new CustomComparator<AccessLogItem>('op_time', 'date');
constructor(
private logService: AccessLogService,
private errorHandler: ErrorHandler) { }
@ -67,31 +59,14 @@ export class RecentLogComponent implements OnInit {
}
public doFilter(terms: string): void {
// allow search by null characters
if (terms === undefined || terms === null) {
return;
}
this.currentTerm = terms.trim();
// Trigger data loading and start from first page
this.loading = true;
this.currentPage = 1;
if (this.currentPagePvt === 1) {
// Force reloading
let st: State = this.currentState;
if (!st) {
st = {
page: {}
};
}
st.page.from = 0;
st.page.to = this.pageSize - 1;
st.page.size = this.pageSize;
this.currentPagePvt = 0; // Reset pvt
this.load(st);
}
this.load({page: {}});
}
public refresh(): void {
@ -111,57 +86,23 @@ export class RecentLogComponent implements OnInit {
this.doFilter(this.currentTerm);
}
load(state: State) {
load(state) {
if (!state || !state.page) {
return;
}
// Keep it for future filter
this.currentState = state;
let pageNumber: number = calculatePage(state);
if (pageNumber !== this.currentPagePvt) {
// load data
let params: RequestQueryParams = new RequestQueryParams().set("page", '' + pageNumber).set("page_size", '' + this.pageSize);
// this.currentState = state;
let params: RequestQueryParams = new RequestQueryParams().set("page", '' + this.currentPage).set("page_size", '' + this.pageSize);
if (this.currentTerm && this.currentTerm !== "") {
params = params.set(this.defaultFilter, this.currentTerm);
}
this.loading = true;
this.logService.getRecentLogs(params)
this.logService.getRecentLogs(params).pipe(finalize(() => (this.loading = false)))
.subscribe(response => {
this.logsCache = response; // Keep the data
this.recentLogs = this.logsCache.data.filter(log => log.username !== ""); // To display
// Do customized filter
this.recentLogs = doFiltering<AccessLogItem>(this.recentLogs, state);
// Do customized sorting
this.recentLogs = doSorting<AccessLogItem>(this.recentLogs, state);
this.currentPagePvt = pageNumber;
this.loading = false;
this.recentLogs = response.data;
}, error => {
this.loading = false;
this.errorHandler.error(error);
});
} else {
// Column sorting and filtering
this.recentLogs = this.logsCache.data.filter(log => log.username !== ""); // Reset data
// Do customized filter
this.recentLogs = doFiltering<AccessLogItem>(this.recentLogs, state);
// Do customized sorting
this.recentLogs = doSorting<AccessLogItem>(this.recentLogs, state);
}
}
isMatched(terms: string, log: AccessLogItem): boolean {
let reg = new RegExp('.*' + terms + '.*', 'i');
return reg.test(log.username) ||
reg.test(log.repo_name) ||
reg.test(log.operation) ||
reg.test(log.repo_tag);
}
}

View File

@ -1,86 +1,75 @@
<form #projectPolicyForm="ngForm">
<form #projectPolicyForm="ngForm" class="clr-form clr-form-horizontal">
<section class="form-block">
<div class="form-group">
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.REGISTRY' | translate }}</label>
<div class="form-content" id="clr-wrapper-public">
<clr-checkbox-container id="clr-wrapper-public">
<label>{{ 'PROJECT_CONFIG.REGISTRY' | translate }}</label>
<clr-checkbox-wrapper>
<input type="checkbox" id="clr-checkbox-wrapper-public" clrCheckbox
[(ngModel)]="projectPolicy.Public" name="public"
[disabled]="!hasChangeConfigRole"/>
<label for="clr-checkbox-wrapper-public">{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}</label>
<input type="checkbox" id="clr-checkbox-wrapper-public" clrCheckbox [(ngModel)]="projectPolicy.Public"
name="public" [disabled]="!hasChangeConfigRole" />
<label>{{ 'PROJECT_CONFIG.PUBLIC_TOGGLE' | translate }}</label>
</clr-checkbox-wrapper>
<div>
<label> {{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }} </label>
</div>
</div>
</div>
<div class="form-group" *ngIf="withNotary || withClair">
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SECURITY' | translate }}</label>
<div class="form-content">
<div *ngIf="withNotary">
<clr-checkbox-wrapper>
<clr-control-helper class="config-subtext"> {{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }}
</clr-control-helper>
</clr-checkbox-container>
<clr-checkbox-container *ngIf="withNotary || withClair">
<label><span *ngIf="withNotary">{{ 'PROJECT_CONFIG.SECURITY' | translate }}</span></label>
<clr-checkbox-wrapper *ngIf="withNotary">
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ContentTrust" name="content-trust"
[disabled]="!hasChangeConfigRole" />
<label>{{ 'PROJECT_CONFIG.CONTENT_TRUST_TOGGLE' | translate }}</label>
</clr-checkbox-wrapper>
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.CONTENT_TRUST_POLCIY' | translate }} </label>
</div>
</div>
<div *ngIf="withClair" id="prevent-vulenrability-image">
<clr-checkbox-wrapper>
<clr-control-helper class="config-subtext"> {{ 'PROJECT_CONFIG.CONTENT_TRUST_POLCIY' | translate }}
</clr-control-helper>
</clr-checkbox-container>
<clr-checkbox-container id="prevent-vulenrability-image" *ngIf="withNotary || withClair">
<label><span *ngIf="!withNotary">{{ 'PROJECT_CONFIG.SECURITY' | translate }}</span></label>
<clr-checkbox-wrapper *ngIf="withClair">
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.PreventVulImg"
name="prevent-vulenrability-image-input"
[disabled]="!hasChangeConfigRole"/>
name="prevent-vulenrability-image-input" [disabled]="!hasChangeConfigRole" />
<label>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_TOGGLE' | translate }}</label>
</clr-checkbox-wrapper>
<clr-control-helper class="config-subtext">
<div class="chk-explain">
<label>
<label class="config-subtext">
<div id="severity-blk">
<div>{{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_1' | translate }}</div>
<div class="select">
<select id="severity" name="severity"
<div class="clr-select-wrapper">
<select id="severity" name="severity" class="clr-select"
[(ngModel)]="projectPolicy.PreventVulImgSeverity"
[disabled]="!projectPolicy.PreventVulImg">
<option *ngFor='let s of severityOptions'
[ngValue]="s.severity">{{ s.severityLevel | translate }}</option>
<option *ngFor='let s of severityOptions' [ngValue]="s.severity">
{{ s.severityLevel | translate }}</option>
</select>
</div>
<div> {{ 'PROJECT_CONFIG.PREVENT_VULNERABLE_2' | translate }} </div>
</div>
</label>
</div>
</div>
</div>
</div>
<div class="form-group" *ngIf="withClair">
<label for="projectPolicyForm">{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
<div class="form-content">
</clr-control-helper>
</clr-checkbox-container>
<clr-checkbox-container *ngIf="withClair">
<label>{{ 'PROJECT_CONFIG.SCAN' | translate }}</label>
<clr-checkbox-wrapper id="scan-image-on-push-wrapper">
<input type="checkbox" clrCheckbox [(ngModel)]="projectPolicy.ScanImgOnPush"
[disabled]="!hasChangeConfigRole"
name="scan-image-on-push"/>
[disabled]="!hasChangeConfigRole" name="scan-image-on-push" />
<label>{{ 'PROJECT_CONFIG.AUTOSCAN_TOGGLE' | translate }}</label>
</clr-checkbox-wrapper>
<div class="chk-explain"><label> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}</label></div>
</div>
</div>
<div class="form-group" *ngIf="withClair">
<label for="systemWhitelist">{{'CVE_WHITELIST.CVE_WHITELIST'|translate}}</label>
<div class="form-content w-100">
<div>
<div>
<clr-control-helper class="config-subtext"> {{ 'PROJECT_CONFIG.AUTOSCAN_POLICY' | translate }}
</clr-control-helper>
</clr-checkbox-container>
<div class="clr-form-control" *ngIf="withClair">
<label for="systemWhitelist" class="clr-control-label">{{'CVE_WHITELIST.CVE_WHITELIST'|translate}}</label>
<div class="w-100 clr-control-container">
<div class="config-subtext">
<div class="mt-05">
<span>{{'CVE_WHITELIST.PRO_WHITELIST_EXPLAIN'|translate}}</span>
</div>
<div>
<div class="mt-05">
<span>{{'CVE_WHITELIST.PRO_OR_SYS'|translate}}</span>
</div>
<div>
<div class="mt-05">
<span>{{'CVE_WHITELIST.MERGE_INTO'|translate}}</span>
</div>
<div *ngIf="hasExpired">
<div *ngIf="hasExpired" class="mt-05">
<span *ngIf="isUseSystemWhitelist()"
class="label label-warning">{{'CVE_WHITELIST.WARNING_SYS'|translate}}</span>
<span *ngIf="!isUseSystemWhitelist()"
@ -89,49 +78,55 @@
</div>
<clr-radio-container clrInline>
<clr-radio-wrapper>
<input id="use-system" [disabled]="!hasChangeConfigRole" type="radio" clrRadio name="systemWhitelistOrProjectWhitelist" required value="true"
<input id="use-system" [disabled]="!hasChangeConfigRole" type="radio" clrRadio
name="systemWhitelistOrProjectWhitelist" required value="true"
[(ngModel)]="systemWhitelistOrProjectWhitelist" />
<label>{{'CVE_WHITELIST.SYS_WHITELIST'|translate}}</label>
</clr-radio-wrapper>
<clr-radio-wrapper>
<input id="use-project" [disabled]="!hasChangeConfigRole" type="radio" clrRadio name="systemWhitelistOrProjectWhitelist" required value="false"
<input id="use-project" [disabled]="!hasChangeConfigRole" type="radio" clrRadio
name="systemWhitelistOrProjectWhitelist" required value="false"
[(ngModel)]="systemWhitelistOrProjectWhitelist" />
<label>{{'CVE_WHITELIST.PRO_WHITELIST'|translate}}</label>
</clr-radio-wrapper>
</clr-radio-container>
<div class="clr-row width-90per">
<div class="clr-col position-relative">
<div class="clr-col position-relative col-flex-grow-0 ">
<div>
<button id="show-add-modal" [disabled]="isUseSystemWhitelist() || !hasChangeConfigRole"
(click)="showAddModal=!showAddModal"
class="btn btn-link">{{'CVE_WHITELIST.ADD'|translate}}</button>
<button id="add-system" [disabled]="isUseSystemWhitelist() || !hasChangeConfigRole" (click)="addSystem()"
<button id="add-system" [disabled]="isUseSystemWhitelist() || !hasChangeConfigRole"
(click)="addSystem()"
class="btn btn-link ml-1">{{'CVE_WHITELIST.ADD_SYSTEM'|translate}}</button>
</div>
<div class="add-modal" *ngIf="showAddModal && !isUseSystemWhitelist()">
<clr-icon (click)="showAddModal=false" class="float-lg-right margin-top-4" shape="window-close"></clr-icon>
<clr-icon (click)="showAddModal=false" class="float-lg-right margin-top-4"
shape="window-close"></clr-icon>
<div>
<clr-textarea-container>
<clr-textarea-container class="flex-direction-column">
<label>{{'CVE_WHITELIST.ENTER'|translate}}</label>
<textarea id="whitelist-textarea" class="w-100" clrTextarea [(ngModel)]="cveIds" name="cveIds"></textarea>
<textarea id="whitelist-textarea" class="w-100" clrTextarea [(ngModel)]="cveIds"
name="cveIds"></textarea>
<clr-control-helper>{{'CVE_WHITELIST.HELP'|translate}}</clr-control-helper>
</clr-textarea-container>
</div>
<div>
<button id="add-to-whitelist" [disabled]="isDisabled()" (click)="addToProjectWhitelist()"
<button id="add-to-whitelist" [disabled]="isDisabled()"
(click)="addToProjectWhitelist()"
class="btn btn-link">{{'CVE_WHITELIST.ADD'|translate}}</button>
</div>
</div>
<ul class="whitelist-window" *ngIf="isUseSystemWhitelist()">
<li *ngIf="systemWhitelist?.items?.length<1"
class="none">{{'CVE_WHITELIST.NONE'|translate}}</li>
<li *ngIf="systemWhitelist?.items?.length<1" class="none">{{'CVE_WHITELIST.NONE'|translate}}
</li>
<li *ngFor="let item of systemWhitelist?.items">
<span class="hand" (click)="goToDetail(item.cve_id)">{{item.cve_id}}</span>
</li>
</ul>
<ul class="whitelist-window" *ngIf="!isUseSystemWhitelist()">
<li class="none"
*ngIf="projectWhitelist?.items?.length<1">{{'CVE_WHITELIST.NONE'|translate}}</li>
<li class="none" *ngIf="projectWhitelist?.items?.length<1">
{{'CVE_WHITELIST.NONE'|translate}}</li>
<li *ngFor="let item of projectWhitelist?.items;let i = index;">
<span class="hand" (click)="goToDetail(item.cve_id)">{{item.cve_id}}</span>
<clr-icon (click)="deleteItem(i)" class="float-lg-right margin-top-4"
@ -140,23 +135,25 @@
</ul>
</div>
<div class="clr-col padding-top-8 ">
<div class="form-group padding-left-80">
<label for="expires">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
<div class="clr-row expire-data">
<label for="expires"
class="bottom-line clr-col-4">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
<div class="underline">
<input #dateSystemInput readonly type="date" [(clrDate)]="systemExpiresDate">
<input [disabled]="!hasChangeConfigRole" *ngIf="!isUseSystemWhitelist()" #dateInput
placeholder="{{'CVE_WHITELIST.NEVER_EXPIRES'|translate}}" readonly type="date"
[(clrDate)]="expiresDate" newFormLayout="true">
<input [disabled]="!hasChangeConfigRole" *ngIf="isUseSystemWhitelist()"
<input clrInput [disabled]="!hasChangeConfigRole" *ngIf="isUseSystemWhitelist()"
placeholder="{{'CVE_WHITELIST.NEVER_EXPIRES'|translate}}" readonly type="text"
value="{{systemExpiresDateString}}">
</div>
</div>
<div class="form-group padding-left-80">
<div class="clr-row">
<label for="expires" class="clr-col-4"></label>
<clr-checkbox-wrapper>
<input [disabled]="isUseSystemWhitelist() || !hasChangeConfigRole" [checked]="neverExpires"
[(ngModel)]="neverExpires" type="checkbox" clrCheckbox name="neverExpires"
id="neverExpires"/>
<input [disabled]="isUseSystemWhitelist() || !hasChangeConfigRole"
[checked]="neverExpires" [(ngModel)]="neverExpires" type="checkbox" clrCheckbox
name="neverExpires" id="neverExpires" />
<label>
{{'CVE_WHITELIST.NEVER_EXPIRES'|translate}}
</label>
@ -164,7 +161,6 @@
</div>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-primary" (click)="save()"

View File

@ -5,6 +5,7 @@
.select {
width: 120px;
}
.margin-top-4 {
margin-top: 4px;
}
@ -17,6 +18,7 @@
width: 270px;
color: #0079bb;
overflow-y: auto;
li {
height: 24px;
line-height: 24px;
@ -43,16 +45,16 @@
.padding-top-8 {
padding-top: 8px;
}
.padding-left-80 {
padding-left: 80px;
.position-relative {
position: relative;
}
.add-modal {
position: absolute;
padding: 0 8px;
background-color: rgb(238, 238, 238);
.flex-direction-column {
flex-direction: column;
}
input {
width: 100%;
border: 1px solid;
@ -68,3 +70,29 @@
margin: 0;
}
.config-subtext {
font-size: 0.55rem;
line-height: 1.2rem;
color: rgb(86, 86, 86);
font-weight: 300;
}
.mt-05 {
margin-bottom: 0.5rem;
}
.col-flex-grow-0 {
flex-grow: 0;
}
.expire-data {
min-width: 12.5rem;
margin-top: -1rem;
}
.bottom-line {
display: flex;
flex-direction: column-reverse;
font-size: 13px;
color: #000;
}

View File

@ -61,9 +61,9 @@ export class ProjectPolicyConfigComponent implements OnInit {
@Input() hasSignedIn: boolean;
@Input() hasProjectAdminRole: boolean;
@ViewChild('cfgConfirmationDialog') confirmationDlg: ConfirmationDialogComponent;
@ViewChild('dateInput') dateInput: ElementRef;
@ViewChild('dateSystemInput') dateSystemInput: ElementRef;
@ViewChild('cfgConfirmationDialog', {static: false}) confirmationDlg: ConfirmationDialogComponent;
@ViewChild('dateInput', {static: false}) dateInput: ElementRef;
@ViewChild('dateSystemInput', {static: false}) dateSystemInput: ElementRef;
systemInfo: SystemInfo;
orgProjectPolicy = new ProjectPolicy();

View File

@ -39,7 +39,7 @@ describe('PushImageButtonComponent (inline template)', () => {
expect(component).toBeTruthy();
});
it('should open the drop-down panel', fakeAsync(() => {
it('should open the drop-down panel', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
@ -57,6 +57,6 @@ describe('PushImageButtonComponent (inline template)', () => {
expect(copyInputs[1].value.trim()).toEqual(`docker push ${component.registryUrl}/${component.projectName}/IMAGE[:TAG]`);
});
});
}));
});
});

View File

@ -13,9 +13,9 @@ export class PushImageButtonComponent {
@Input() registryUrl: string = "unknown";
@Input() projectName: string = "unknown";
@ViewChild("tagCopy") tagCopyInput: CopyInputComponent;
@ViewChild("pushCopy") pushCopyInput: CopyInputComponent;
@ViewChild("copyAlert") copyAlert: InlineAlertComponent;
@ViewChild("tagCopy", {static: false}) tagCopyInput: CopyInputComponent;
@ViewChild("pushCopy", {static: false}) pushCopyInput: CopyInputComponent;
@ViewChild("copyAlert", {static: false}) copyAlert: InlineAlertComponent;
public get tagCommand(): string {
return `docker tag SOURCE_IMAGE[:TAG] ${this.registryUrl}/${

View File

@ -32,6 +32,7 @@
.command-input {
font-size: 14px;
font-weight: 500;
border: 0;
}
:host>>>.dropdown-menu {

View File

@ -120,16 +120,16 @@ export class ReplicationComponent implements OnInit, OnDestroy {
jobs: ReplicationJobItem[];
@ViewChild(ListReplicationRuleComponent)
@ViewChild(ListReplicationRuleComponent, {static: false})
listReplicationRule: ListReplicationRuleComponent;
@ViewChild(CreateEditRuleComponent)
@ViewChild(CreateEditRuleComponent, {static: false})
createEditPolicyComponent: CreateEditRuleComponent;
@ViewChild("replicationConfirmDialog")
@ViewChild("replicationConfirmDialog", {static: false})
replicationConfirmDialog: ConfirmationDialogComponent;
@ViewChild("StopConfirmDialog")
@ViewChild("StopConfirmDialog", {static: false})
StopConfirmDialog: ConfirmationDialogComponent;
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<

View File

@ -80,10 +80,10 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit {
totalCount = 0;
currentState: State;
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialog: ConfirmationDialogComponent;
@ViewChild("gridView") gridView: GridViewComponent;
@ViewChild("gridView", {static: false}) gridView: GridViewComponent;
hasCreateRepositoryPermission: boolean;
hasDeleteRepositoryPermission: boolean;
constructor(@Inject(SERVICE_CONFIG) private configInfo: IServiceConfig,

View File

@ -33,7 +33,7 @@
</svg>
<span>{{ 'REPOSITORY.MARKDOWN' | translate }}</span>
</div>
<div *ngIf="!editing">
<div id="no-editing" *ngIf="!editing">
<div *ngIf="!hasInfo()" class="no-info-div">
<p>{{'REPOSITORY.NO_INFO' | translate }}<p>
</div>
@ -42,7 +42,8 @@
</div>
</div>
<div *ngIf="editing">
<textarea rows="5" name="info-edit-textarea" [(ngModel)]="imageInfo"></textarea>
<textarea id="info-edit-textarea" class="clr-textarea w-100" rows="5" name="info-edit-textarea"
[(ngModel)]="imageInfo"></textarea>
</div>
<div class="btn-sm" *ngIf="editing">
<button class="btn btn-primary" [disabled]="!hasChanges()" (click)="saveInfo()">{{'BUTTON.SAVE' | translate}}</button>

View File

@ -56,7 +56,7 @@ export class RepositoryComponent implements OnInit {
timerHandler: any;
@ViewChild('confirmationDialog')
@ViewChild('confirmationDialog', {static: false})
confirmationDlg: ConfirmationDialogComponent;
constructor(

View File

@ -89,8 +89,8 @@
<hbr-vulnerabilities-grid [repositoryId]="repositoryId" [projectId]="projectId" [tagId]="tagId"></hbr-vulnerabilities-grid>
</clr-tab-content>
</clr-tab>
<clr-tab>
<button [clrTabLinkInOverflow]="false" *ngIf="hasBuildHistoryPermission" id="tag-history" clrTabLink class="btn btn-link nav-link" [class.active]='isCurrentTabLink("tag-history")' type="button" (click)='tabLinkClick("tag-history")'>{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
<clr-tab *ngIf="hasBuildHistoryPermission">
<button [clrTabLinkInOverflow]="false" id="tag-history" clrTabLink class="btn btn-link nav-link" [class.active]='isCurrentTabLink("tag-history")' type="button" (click)='tabLinkClick("tag-history")'>{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
<clr-tab-content *clrIfActive>
<hbr-tag-history [repositoryId]="repositoryId" [tagId]="tagId">{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</hbr-tag-history>
</clr-tab-content>

View File

@ -3,7 +3,7 @@
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
<div class="modal-body">
<div class="row col-md-12">
<textarea rows="2" #digestTarget>{{digestId}}</textarea>
<textarea class="clr-textarea w-100" rows="2" #digestTarget>{{digestId}}</textarea>
</div>
</div>
<div class="modal-footer">
@ -34,7 +34,7 @@
<div class="labelFilterPanel" *ngIf="!withAdmiral" [hidden]="!openLabelFilterPanel">
<a class="filterClose" (click)="closeFilter()">&times;</a>
<label class="filterLabelHeader">{{'REPOSITORY.FILTER_BY_LABEL' | translate}}</label>
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]="filterName" (keyup)="handleInputFilter()"></div>
<div class="form-group"><input clrInput type="text" placeholder="Filter labels" [(ngModel)]="filterName" (keyup)="handleInputFilter()"></div>
<div [hidden]='imageFilterLabels.length' class="no-labels">{{'LABEL.NO_LABELS' | translate }}</div>
<div [hidden]='!imageFilterLabels.length' class="has-label">
<button type="button" class="labelBtn" *ngFor='let label of imageFilterLabels' [hidden]="!label.show" (click)="rightFilterLabel(label)">
@ -60,9 +60,10 @@
<clr-dropdown *ngIf="!withAdmiral">
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1)||!hasAddLabelImagePermission" (click)="addLabels(selectedRow)"><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
<clr-dropdown>
<div class="filter-grid">
<label class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label>
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]="stickName" (keyup)="handleStickInputFilter()"></div>
<div class="form-group"><input clrInput type="text" placeholder="Filter labels" [(ngModel)]="stickName" (keyup)="handleStickInputFilter()"></div>
<div [hidden]='imageStickLabels.length' class="no-labels">{{'LABEL.NO_LABELS' | translate }}</div>
<div [hidden]='!imageStickLabels.length' class="has-label">
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' [hidden]='!label.show' (click)="stickLabel(label)">
@ -71,6 +72,7 @@
</button>
</div>
</div>
</clr-dropdown>
</clr-dropdown-menu>
</clr-dropdown>
<button type="button" class="btn btn-sm btn-secondary" *ngIf="!withAdmiral" [disabled]="!(selectedRow.length===1)|| !hasRetagImagePermission" (click)="retag(selectedRow)"><clr-icon shape="copy" size="16"></clr-icon>&nbsp;{{'REPOSITORY.RETAG' | translate}}</button>

View File

@ -246,3 +246,7 @@ clr-datagrid {
position: static;
}
}
::ng-deep .clr-form-control {
margin-top: 0;
}

View File

@ -134,14 +134,14 @@ export class TagComponent implements OnInit, AfterViewInit {
};
filterOneLabel: Label = this.initFilter;
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialog: ConfirmationDialogComponent;
@ViewChild("imageNameInput")
@ViewChild("imageNameInput", {static: false})
imageNameInput: ImageNameInputComponent;
@ViewChild("digestTarget") textInput: ElementRef;
@ViewChild("copyInput") copyInput: CopyInputComponent;
@ViewChild("digestTarget", {static: false}) textInput: ElementRef;
@ViewChild("copyInput", {static: false}) copyInput: CopyInputComponent;
pageSize: number = DEFAULT_PAGE_SIZE;
currentPage = 1;

View File

@ -7,7 +7,7 @@ import {
Renderer,
SkipSelf
} from "@angular/core";
import { DOCUMENT } from "@angular/platform-browser";
import { DOCUMENT } from "@angular/common";
import { WINDOW } from "../ngx-window-token/index";
@Injectable()

View File

@ -245,7 +245,12 @@ export function calculatePage(state: State): number {
return 1;
}
return Math.ceil((state.page.to + 1) / state.page.size);
let pageNumber = Math.ceil((state.page.to + 1) / state.page.size);
if (pageNumber === 0) {
return 1;
} else {
return pageNumber;
}
}
/**

View File

@ -1,6 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { VulnerabilityItem, VulnerabilitySeverity } from '../service/index';
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { ResultGridComponent } from './result-grid.component';
import { ScanningResultService, ScanningResultDefaultService } from '../service/scanning.service';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
@ -26,7 +26,8 @@ describe('ResultGridComponent (inline template)', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
SharedModule
SharedModule,
NoopAnimationsModule
],
declarations: [ResultGridComponent, FilterComponent],
providers: [

View File

@ -15,7 +15,10 @@
"lib": [
"dom",
"es2015"
]
],
"paths": {
"@angular/*": [ "../node_modules/@angular/*"]
}
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "harbor",
"version": "1.9.0",
"version": "1.10.0",
"description": "Harbor UI with Clarity",
"angular-cli": {},
"scripts": {
@ -20,24 +20,23 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^7.1.3",
"@angular/common": "^7.1.3",
"@angular/compiler": "^7.1.3",
"@angular/core": "^7.1.3",
"@angular/forms": "^7.1.3",
"@angular/http": "^7.1.3",
"@angular/platform-browser": "^7.1.3",
"@angular/platform-browser-dynamic": "^7.1.3",
"@angular/router": "^7.1.3",
"@clr/angular": "^1.0.0",
"@clr/icons": "^1.0.0",
"@clr/ui": "^1.0.0",
"@angular/animations": "^8.2.0",
"@angular/common": "^8.2.0",
"@angular/compiler": "^8.2.0",
"@angular/core": "^8.2.0",
"@angular/forms": "^8.2.0",
"@angular/platform-browser": "^8.2.0",
"@angular/platform-browser-dynamic": "^8.2.0",
"@angular/router": "^8.2.0",
"@clr/angular": "^2.2.0",
"@clr/icons": "^2.2.0",
"@clr/ui": "^2.2.0",
"@fortawesome/fontawesome-free": "^5.1.0-4",
"@ng-bootstrap/ng-bootstrap": "^4.0.0",
"@ng-bootstrap/ng-bootstrap": "^5.1.0",
"@ngx-translate/core": "^10.0.2",
"@ngx-translate/http-loader": "^3.0.1",
"@types/jquery": "^2.0.41",
"@webcomponents/custom-elements": "^1.1.3",
"@webcomponents/custom-elements": "^1.2.4",
"bootstrap": "^4.1.1",
"buffer": "^5.2.1",
"core-js": "^2.5.4",
@ -45,26 +44,26 @@
"jasmine-core": "^3.3.0",
"jquery": "^3.4.1",
"mutationobserver-shim": "^0.3.2",
"ng-packagr": "^4.5.0",
"ng-packagr": "^5.3.0",
"ngx-clipboard": "^12.0.0",
"ngx-cookie": "^1.0.0",
"ngx-markdown": "^7.1.5",
"ngx-markdown": "^8.1.0",
"popper.js": "^1.14.3",
"rxjs": "^6.3.3",
"rxjs": "^6.5.2",
"stream": "^0.0.2",
"swagger-ui": "^3.22.1",
"swagger-ui": "^3.23.4",
"ts-helpers": "^1.1.1",
"tslib": "^1.9.0",
"types": "^0.1.1",
"web-animations-js": "^2.2.1",
"zone.js": "^0.8.26"
"web-animations-js": "^2.3.2",
"zone.js": "^0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.13.8",
"@angular-devkit/build-ng-packagr": "~0.13.8",
"@angular/cli": "^7.1.3",
"@angular/compiler-cli": "^7.1.3",
"@angular/language-service": "^7.1.3",
"@angular-devkit/build-angular": "~0.802.0",
"@angular-devkit/build-ng-packagr": "~0.802.0",
"@angular/cli": "^8.2.0",
"@angular/compiler-cli": "^8.2.0",
"@angular/language-service": "^8.2.0",
"@types/core-js": "^0.9.41",
"@types/jasmine": "^3.3.1",
"@types/jasminewd2": "~2.0.3",
@ -86,9 +85,9 @@
"rollup-plugin-node-resolve": "^3.0.0",
"rollup-plugin-uglify": "^1.0.1",
"ts-node": "~5.0.1",
"tsickle": "^0.33.1",
"tsickle": "^0.36.0",
"tslint": "~5.9.1",
"typescript": "3.1.3",
"typescript": "^3.5.3",
"typings": "^1.5.0",
"uglify-js": "3.3.18",
"webdriver-manager": "10.2.5"

View File

@ -1,12 +1,13 @@
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="false">
<h3 class="modal-title">{{'PROFILE.TITLE' | translate}}</h3>
<inline-alert class="modal-title" (confirmEvt)="confirmYes($event)" (closeEvt)="confirmNo($event)"></inline-alert>
<div class="modal-body body-format">
<form #accountSettingsFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group form-group-override">
<label for="account_settings_username" aria-haspopup="true" class="form-group-label-override">{{'PROFILE.USER_NAME' | translate}}</label>
<input type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username" size="33">
<inline-alert (confirmEvt)="confirmYes($event)" (closeEvt)="confirmNo($event)"></inline-alert>
<form #accountSettingsFrom="ngForm" class="clr-form clr-form-horizontal">
<div class="clr-form-control">
<label for="account_settings_username" aria-haspopup="true" class="clr-control-label">{{'PROFILE.USER_NAME' | translate}}</label>
<div class="clr-control-container display-flex">
<input class="clr-input" type="text" name="account_settings_username" [(ngModel)]="account.username" disabled id="account_settings_username"
size="30">
<div *ngIf="canRename" class="rename-tool">
<button [disabled]="RenameOnGoing" (click)="onRename()" class="btn btn-outline btn-sm">
{{'PROFILE.ADMIN_RENAME_BUTTON' | translate}}
@ -19,47 +20,50 @@
</clr-tooltip>
</div>
</div>
<div class="form-group form-group-override">
<label for="account_settings_email" class="required form-group-label-override">{{'PROFILE.EMAIL' | translate}}</label>
<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
email
id="account_settings_email" size="30"
(input)='handleValidation("account_settings_email", false)'
</div>
<div class="clr-form-control">
<label for="account_settings_email" class="required clr-control-label">{{'PROFILE.EMAIL' | translate}}</label>
<div class="clr-control-container" [class.clr-error]="!getValidationState('account_settings_email')">
<div class="clr-input-wrapper">
<input name="account_settings_email" type="text" #eamilInput="ngModel" class="clr-input" [(ngModel)]="account.email" required
email id="account_settings_email" size="30" (input)='handleValidation("account_settings_email", false)'
(blur)='handleValidation("account_settings_email", true)'>
<span class="tooltip-content">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<span class="spinner spinner-inline" [hidden]="!checkProgress"></span>
</div>
<clr-control-error *ngIf="!getValidationState('account_settings_email')">
{{emailTooltip | translate}}
</span>
</label><span class="spinner spinner-inline" [hidden]="!checkProgress"></span>
</clr-control-error>
</div>
<div class="form-group form-group-override">
<label for="account_settings_full_name" class="form-group-label-override" [class.required]="!account.oidc_user_meta">{{'PROFILE.FULL_NAME' | translate}}</label>
<label for="account_settings_full_name" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("account_settings_full_name")'>
<input type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" [required]="!account.oidc_user_meta" maxLengthExt="20" id="account_settings_full_name" size="30" (input)='handleValidation("account_settings_full_name", false)' (blur)='handleValidation("account_settings_full_name", true)'>
<span class="tooltip-content">
</div>
<clr-input-container>
<label [class.required]="!account.oidc_user_meta">{{'PROFILE.FULL_NAME' | translate}}</label>
<input clrInput type="text" name="account_settings_full_name" #fullNameInput="ngModel" [(ngModel)]="account.realname" [required]="!account.oidc_user_meta"
maxLengthExt="20" id="account_settings_full_name" size="30" (input)='handleValidation("account_settings_full_name", false)'
(blur)='handleValidation("account_settings_full_name", true)'>
<clr-control-error *ngIf="!getValidationState('account_settings_full_name')">
{{'TOOLTIP.FULL_NAME' | translate}}
</span>
</label>
</div>
<div class="form-group form-group-override">
<label for="account_settings_comments" class="form-group-label-override">{{'PROFILE.COMMENT' | translate}}</label>
<label for="account_settings_comments" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]="commentInput.invalid && (commentInput.dirty || commentInput.touched)">
<input type="text" #commentInput="ngModel" maxLengthExt="30" name="account_settings_comments" [(ngModel)]="account.comment" id="account_settings_comments" size="30">
<span class="tooltip-content">
</clr-control-error>
</clr-input-container>
<clr-input-container>
<label>{{'PROFILE.COMMENT' | translate}}</label>
<input clrInput type="text" #commentInput="ngModel" maxlength="30" size="30" name="account_settings_comments" [(ngModel)]="account.comment"
id="account_settings_comments">
<clr-control-error *ngIf="commentInput.invalid && (commentInput.dirty || commentInput.touched)">
{{'TOOLTIP.COMMENT' | translate}}
</span>
</label>
</div>
<div class="form-group form-group-override cli-secret" *ngIf="account.oidc_user_meta">
<label for="cli_password" aria-haspopup="true" class="form-group-label-override"><span class="label-inner-text">{{'PROFILE.CLI_PASSWORD' | translate}}</span>
</clr-control-error>
</clr-input-container>
<div class="clr-form-control cli-secret" *ngIf="account.oidc_user_meta">
<label class="clr-control-label">{{'PROFILE.CLI_PASSWORD' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="20"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen>
<span> {{'PROFILE.CLI_PASSWORD_TIP' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip></label>
<input type="password" name="cli_password" disabled [ngModel]="'account.oidc_user_meta.secret'" size="33">
</clr-tooltip>
</label>
<input class="clr-input" type="password" name="cli_password" disabled [ngModel]="'account.oidc_user_meta.secret'" size="33">
<button (click)="generateCli(account.user_id)" id="generate-cli-btn" class="btn btn-outline btn-sm btn-padding-less" *ngIf="showGenerateCli">
{{'PROFILE.ADMIN_CIL_SECRET_BUTTON' | translate}}
</button>
@ -68,7 +72,6 @@
</div>
<div (click)="showGenerateCliFn()" *ngIf="!showGenerateCli" id="hidden-generate-cli" class="hidden-generate-cli">···</div>
</div>
</section>
</form>
</div>
<div class="modal-footer">

View File

@ -32,3 +32,7 @@ clr-modal {
line-height: 30px;
}
}
.display-flex {
display: flex;
}

View File

@ -51,13 +51,13 @@ export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
renameConfirmation = false;
// confirmRename = false;
showGenerateCli: boolean = false;
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialogComponent: ConfirmationDialogComponent;
accountFormRef: NgForm;
@ViewChild("accountSettingsFrom") accountForm: NgForm;
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
@ViewChild("copyInput") copyInput: CopyInputComponent;
@ViewChild("accountSettingsFrom", {static: true}) accountForm: NgForm;
@ViewChild(InlineAlertComponent, {static: false}) inlineAlert: InlineAlertComponent;
@ViewChild("copyInput", {static: false}) copyInput: CopyInputComponent;
constructor(
private session: SessionService,

View File

@ -1,26 +1,17 @@
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
<h3 class="modal-title">{{'RESET_PWD.TITLE' | translate}}</h3>
<label class="modal-title reset-modal-title-override">{{'RESET_PWD.CAPTION' | translate}}</label>
<inline-alert class="modal-title"></inline-alert>
<div class="modal-body body-format password-body">
<form #forgotPasswordFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group form-group-override">
<label for="reset_pwd_email" class="required form-group-label-override">{{'RESET_PWD.EMAIL' | translate}}</label>
<label for="reset_pwd_email" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="!validationState">
<input [disabled]="isSuccess" name="reset_pwd_email" type="text" #eamilInput="ngModel" [(ngModel)]="email"
required
email
id="reset_pwd_email"
size="30"
(input)="handleValidation(true)"
(blur)="handleValidation(false)">
<span class="tooltip-content">
<inline-alert></inline-alert>
<label>{{'RESET_PWD.CAPTION' | translate}}</label>
<form #forgotPasswordFrom="ngForm" clrForm>
<clr-input-container>
<label class="required">{{'RESET_PWD.EMAIL' | translate}}</label>
<input clrInput [disabled]="isSuccess" name="reset_pwd_email" type="text" #eamilInput="ngModel" [(ngModel)]="email" required email
id="reset_pwd_email" size="50" (input)="handleValidation(true)" (blur)="handleValidation(false)">
<clr-control-error *ngIf="!validationState">
{{'TOOLTIP.EMAIL' | translate}}
</span>
</label>
</div>
</section>
</clr-control-error>
</clr-input-container>
</form>
</div>
<div class="modal-footer">

View File

@ -29,8 +29,8 @@ export class ForgotPasswordComponent {
validationState: boolean = true;
isSuccess: boolean = false;
@ViewChild("forgotPasswordFrom") forgotPwdForm: NgForm;
@ViewChild(InlineAlertComponent)
@ViewChild("forgotPasswordFrom", {static: true}) forgotPwdForm: NgForm;
@ViewChild(InlineAlertComponent, {static: false})
inlineAlert: InlineAlertComponent;
constructor(private pwdService: PasswordSettingService) { }

View File

@ -1,56 +1,34 @@
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
<h3 class="modal-title">{{'CHANGE_PWD.TITLE' | translate}}</h3>
<inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></inline-alert>
<div class="modal-body body-format">
<form #changepwdForm="ngForm" class="form">
<section class="form-block">
<div class="form-group form-group-override">
<label for="oldPassword" class="required form-group-label-override">{{'CHANGE_PWD.CURRENT_PWD' | translate}}</label>
<label for="oldPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
<input type="password" id="oldPassword"
required
name="oldPassword"
[(ngModel)]="oldPwd"
#oldPassInput="ngModel" size="30">
<span class="tooltip-content">
<inline-alert (confirmEvt)="confirmCancel($event)"></inline-alert>
<form #changepwdForm="ngForm" clrForm>
<clr-input-container>
<label class="required">{{'CHANGE_PWD.CURRENT_PWD' | translate}}</label>
<input clrInput type="password" id="oldPassword" required name="oldPassword" [(ngModel)]="oldPwd" #oldPassInput="ngModel"
size="40">
<clr-control-error *ngIf="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
{{'TOOLTIP.CURRENT_PWD' | translate}}
</span>
</label>
</div>
<div class="form-group form-group-override">
<label for="newPassword" class="required form-group-label-override">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<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="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
name="newPassword"
[(ngModel)]="newPwd"
#newPassInput="ngModel" size="30"
(input)='handleValidation("newPassword", false)'
(blur)='handleValidation("newPassword", true)'>
<span class="tooltip-content">
</clr-control-error>
</clr-input-container>
<clr-input-container>
<label class="required">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<input clrInput type="password" id="newPassword" required pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$" name="newPassword"
[(ngModel)]="newPwd" #newPassInput="ngModel" size="40" (input)='handleValidation("newPassword", false)' (blur)='handleValidation("newPassword", true)'>
<clr-control-helper>{{'CHANGE_PWD.PASS_TIPS' | translate}}</clr-control-helper>
<clr-control-error *ngIf="!getValidationState('newPassword')">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
</label>
<label class="sub-label-for-input">{{'CHANGE_PWD.PASS_TIPS' | translate}}</label>
</div>
<div class="form-group form-group-override">
<label for="reNewPassword" class="required form-group-label-override">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<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="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
name="reNewPassword"
[(ngModel)]="reNewPwd"
#reNewPassInput="ngModel" size="30"
(input)='handleValidation("reNewPassword", false)'
</clr-control-error>
</clr-input-container>
<clr-input-container>
<label class="required">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<input clrInput type="password" id="reNewPassword" required pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$" name="reNewPassword"
[(ngModel)]="reNewPwd" #reNewPassInput="ngModel" size="40" (input)='handleValidation("reNewPassword", false)'
(blur)='handleValidation("reNewPassword", true)'>
<span class="tooltip-content">
<clr-control-error *ngIf='!getValidationState("reNewPassword")'>
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>
</label>
</div>
</section>
</clr-control-error>
</clr-input-container>
</form>
</div>
<div class="modal-footer">

View File

@ -40,8 +40,8 @@ export class PasswordSettingComponent implements AfterViewChecked {
};
pwdFormRef: NgForm;
@ViewChild("changepwdForm") pwdForm: NgForm;
@ViewChild(InlineAlertComponent)
@ViewChild("changepwdForm", {static: true}) pwdForm: NgForm;
@ViewChild(InlineAlertComponent, {static: true})
inlineAlert: InlineAlertComponent;
constructor(

View File

@ -7,16 +7,11 @@
<section class="form-block">
<div class="form-group">
<label for="newPassword" class="form-group-label-override">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='!getValidationState("newPassword")'>
<input [disabled]="resetOk" type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
name="newPassword"
[(ngModel)]="password"
#newPassInput="ngModel"
size="25"
(input)='handleValidation("newPassword", false)'
(blur)='handleValidation("newPassword", true)'>
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
[class.invalid]='!getValidationState("newPassword")'>
<input [disabled]="resetOk" type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}' required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$" name="newPassword" [(ngModel)]="password"
#newPassInput="ngModel" size="25" (input)='handleValidation("newPassword", false)' (blur)='handleValidation("newPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.PASSWORD' | translate}}
</span>
@ -24,16 +19,11 @@
</div>
<div class="form-group">
<label for="reNewPassword" class="form-group-label-override">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("reNewPassword")'>
<input [disabled]="resetOk" type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
name="reNewPassword"
[(ngModel)]="confirmPwd"
#reNewPassInput
size="25"
(input)='handleValidation("reNewPassword", false)'
(blur)='handleValidation("reNewPassword", true)'>
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left"
[class.invalid]='!getValidationState("reNewPassword")'>
<input [disabled]="resetOk" type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}' required
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$" name="reNewPassword" [(ngModel)]="confirmPwd"
#reNewPassInput size="25" (input)='handleValidation("reNewPassword", false)' (blur)='handleValidation("reNewPassword", true)'>
<span class="tooltip-content">
{{'TOOLTIP.CONFIRM_PWD' | translate}}
</span>

View File

@ -37,8 +37,8 @@ export class ResetPasswordComponent implements OnInit {
resetOk: boolean = false;
confirmPwd: string = "";
@ViewChild("resetPwdForm") resetPwdForm: NgForm;
@ViewChild(InlineAlertComponent)
@ViewChild("resetPwdForm", {static: false}) resetPwdForm: NgForm;
@ViewChild(InlineAlertComponent, {static: false})
inlineAlert: InlineAlertComponent;
constructor(

View File

@ -35,7 +35,7 @@ export class SignUpPageComponent implements OnInit {
private msgService: MessageService,
private router: Router) { }
@ViewChild(NewUserFormComponent)
@ViewChild(NewUserFormComponent, {static: false})
newUserForm: NewUserFormComponent;
getNewUser(): User {

View File

@ -39,13 +39,13 @@ export class SignUpComponent {
private session: SessionService,
private userService: UserService) { }
@ViewChild(NewUserFormComponent)
@ViewChild(NewUserFormComponent, {static: true})
newUserForm: NewUserFormComponent;
@ViewChild(InlineAlertComponent)
@ViewChild(InlineAlertComponent, {static: false})
inlienAlert: InlineAlertComponent;
@ViewChild(Modal)
@ViewChild(Modal, {static: false})
modal: Modal;
getNewUser(): User {

View File

@ -45,7 +45,6 @@ registerLocaleData(localeFr, 'fr-fr');
registerLocaleData(localePt, 'pt-br');
registerLocaleData(localeTr, 'tr-tr');
export function initConfig(configService: AppConfigService, skinableService: SkinableConfig) {
return () => {
skinableService.getCustomFile().subscribe();
@ -87,11 +86,7 @@ export function getCurrentLanguage(translateService: TranslateService) {
deps: [ AppConfigService, SkinableConfig],
multi: true
},
{
provide: LOCALE_ID,
useFactory: getCurrentLanguage,
deps: [ TranslateService ]
}
{provide: LOCALE_ID, useValue: "en-US"}
],
bootstrap: [AppComponent]
})

View File

@ -36,16 +36,16 @@ import { CommonRoutes } from '@harbor/ui';
export class HarborShellComponent implements OnInit, OnDestroy {
@ViewChild(AccountSettingsModalComponent)
@ViewChild(AccountSettingsModalComponent, {static: false})
accountSettingsModal: AccountSettingsModalComponent;
@ViewChild(PasswordSettingComponent)
@ViewChild(PasswordSettingComponent, {static: false})
pwdSetting: PasswordSettingComponent;
@ViewChild(NavigatorComponent)
@ViewChild(NavigatorComponent, {static: false})
navigator: NavigatorComponent;
@ViewChild(AboutDialogComponent)
@ViewChild(AboutDialogComponent, {static: false})
aboutDialog: AboutDialogComponent;
// To indicator whwther or not the search results page is displayed

View File

@ -1,9 +1,16 @@
<form #authConfigFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="authMode">{{'CONFIG.AUTH_MODE' | translate }}</label>
<div class="select">
<select id="authMode" name="authMode" (change)="handleOnChange($event)"
<form #authConfigFrom="ngForm" class="clr-form clr-form-horizontal">
<clr-select-container>
<label >{{'CONFIG.AUTH_MODE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.AUTH_MODE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
<a href="https://openid.net/connect/" *ngIf="currentConfig?.auth_mode?.value==='oidc_auth'" target="_blank"
class="more-info-link">{{ 'BUTTON.MORE_INFO' | translate }}</a>
</label>
<select clrSelect id="authMode" name="authMode" (change)="handleOnChange($event)"
[disabled]="disabled(currentConfig.auth_mode)" [(ngModel)]="currentConfig.auth_mode.value">
<option value="db_auth">{{'CONFIG.AUTH_MODE_DB' | translate }}</option>
<option value="ldap_auth">{{'CONFIG.AUTH_MODE_LDAP' | translate }}</option>
@ -11,411 +18,362 @@
<option *ngIf="showHttpAuth" value="http_auth">{{'CONFIG.AUTH_MODE_HTTP' | translate }}</option>
<option value="oidc_auth">{{'CONFIG.AUTH_MODE_OIDC' | translate }}</option>
</select>
</div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" size="24" class="info-tips-icon"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.AUTH_MODE' | translate}}</span>
</a>
<a href="https://openid.net/connect/" *ngIf="currentConfig?.auth_mode?.value==='oidc_auth'" target="_blank"
class="more-info-link">{{ 'BUTTON.MORE_INFO' | translate }}</a>
</div>
</section>
<section class="form-block" *ngIf="showUAA">
<div class="form-group">
</clr-select-container>
<section *ngIf="showUAA">
<clr-input-container>
<label for="uaa-endpoint" class="required">{{'CONFIG.UAA.ENDPOINT' | translate}}</label>
<input type="text" id="uaa-endpoint" name="uaa-endpoint" size="35"
<input type="text" id="uaa-endpoint" clrInput name="uaa-endpoint" size="35"
[(ngModel)]="currentConfig.uaa_endpoint.value" [disabled]="!currentConfig.uaa_verify_cert.editable">
</div>
<div class="form-group">
</clr-input-container>
<clr-input-container>
<label for="uaa-id" class="required">{{'CONFIG.UAA.CLIENT_ID' | translate}}</label>
<input type="text" id="uaa-cid" name="uaa-client-id" size="35"
<input clrInput type="text" id="uaa-id" name="uaa-client-id" size="35"
[(ngModel)]="currentConfig.uaa_client_id.value" [disabled]="!currentConfig.uaa_verify_cert.editable">
</div>
<div class="form-group">
<label for="uaa-id" class="required">{{'CONFIG.UAA.CLIENT_SECRET' | translate}}</label>
<input id="uaa-secret" name="uaa-client-secret" type="password" size="35"
</clr-input-container>
<clr-input-container>
<label for="uaa-secret" class="required">{{'CONFIG.UAA.CLIENT_SECRET' | translate}}</label>
<input clrInput id="uaa-secret" name="uaa-client-secret" type="password" size="35"
[(ngModel)]="currentConfig.uaa_client_secret.value"
[disabled]="!currentConfig.uaa_verify_cert.editable">
</div>
<div class="form-group">
</clr-input-container>
<clr-checkbox-container>
<label for="uaa-cert">{{'CONFIG.UAA.VERIFY_CERT' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="uaa-cert" id="uaa-cert" required
<input type="checkbox" clrCheckbox name="uaa-cert" id="uaa-cert"
[(ngModel)]="currentConfig.uaa_verify_cert.value"
[disabled]="!currentConfig.uaa_verify_cert.editable" />
<label for="uaa-cert" class="required">{{'CONFIG.UAA.VERIFY_CERT' | translate}}</label>
</clr-checkbox-wrapper>
</div>
</clr-checkbox-container>
</section>
<section class="form-block" *ngIf="showLdap">
<div class="form-group">
<label for="ldapUrl" class="required">{{'CONFIG.LDAP.URL' | translate}}</label>
<label for="ldapUrl" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right"
[class.invalid]="ldapUrlInput.invalid && (ldapUrlInput.dirty || ldapUrlInput.touched)">
<input name="ldapUrl" type="text" #ldapUrlInput="ngModel" [(ngModel)]="currentConfig.ldap_url.value"
required id="ldapUrl" size="40" [disabled]="disabled(currentConfig.ldap_url)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
<section *ngIf="showLdap">
<clr-input-container>
<label for="ldapUrl" class="required">{{'CONFIG.LDAP.URL' | translate}}
</label>
</div>
<div class="form-group">
<label for="ldapSearchDN">{{'CONFIG.LDAP.SEARCH_DN' | translate}}</label>
<label for="ldapSearchDN" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="ldapSearchDN" type="text" #ldapSearchDNInput="ngModel"
<input clrInput name="ldapUrl" type="text" #ldapUrlInput="ngModel"
[(ngModel)]="currentConfig.ldap_url.value" required id="ldapUrl" size="40"
[disabled]="disabled(currentConfig.ldap_url)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="ldapSearchDN" >{{'CONFIG.LDAP.SEARCH_DN' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.LDAP_SEARCH_DN' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<input clrInput name="ldapSearchDN" type="text" #ldapSearchDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_search_dn.value" id="ldapSearchDN" size="40"
[disabled]="disabled(currentConfig.ldap_search_dn)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_SEARCH_DN' | translate}}</span>
</a>
</div>
<div class="form-group">
[disabled]="disabled(currentConfig.ldap_search_dn)" />
</clr-input-container>
<clr-input-container>
<label for="ldapSearchPwd">{{'CONFIG.LDAP.SEARCH_PWD' | translate}}</label>
<label for="ldapSearchPwd" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="ldapSearchPwd" type="password" #ldapSearchPwdInput="ngModel"
<input clrInput name="ldapSearchPwd" type="password" #ldapSearchPwdInput="ngModel"
[(ngModel)]="currentConfig.ldap_search_password.value" id="ldapSearchPwd" size="40"
[disabled]="disabled(currentConfig.ldap_search_password)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
[disabled]="disabled(currentConfig.ldap_search_password)" />
</clr-input-container>
<clr-input-container>
<label for="ldapBaseDN" class="required">{{'CONFIG.LDAP.BASE_DN' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.LDAP_BASE_DN' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</div>
<div class="form-group">
<label for="ldapBaseDN" class="required">{{'CONFIG.LDAP.BASE_DN' | translate}}</label>
<label for="ldapBaseDN" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right"
[class.invalid]="ldapBaseDNInput.invalid && (ldapBaseDNInput.dirty || ldapBaseDNInput.touched)">
<input name="ldapBaseDN" type="text" #ldapBaseDNInput="ngModel"
<input clrInput name="ldapBaseDN" type="text" #ldapBaseDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_base_dn.value" required id="ldapBaseDN" size="40"
[disabled]="disabled(currentConfig.ldap_base_dn)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_BASE_DN' | translate}}</span>
</a>
</div>
<div class="form-group">
[disabled]="disabled(currentConfig.ldap_base_dn)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="ldapFilter">{{'CONFIG.LDAP.FILTER' | translate}}</label>
<label for="ldapFilter" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="ldapFilter" type="text" #ldapFilterInput="ngModel"
<input clrInput name="ldapFilter" type="text" #ldapFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_filter.value" id="ldapFilter" size="40"
[disabled]="disabled(currentConfig.ldap_filter)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
[disabled]="disabled(currentConfig.ldap_filter)" />
</clr-input-container>
<clr-input-container>
<label for="ldapUid" class="required">{{'CONFIG.LDAP.UID' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.LDAP_UID' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</div>
<div class="form-group">
<label for="ldapUid" class="required">{{'CONFIG.LDAP.UID' | translate}}</label>
<label for="ldapUid" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right"
[class.invalid]="ldapUidInput.invalid && (ldapUidInput.dirty || ldapUidInput.touched)">
<input name="ldapUid" type="text" #ldapUidInput="ngModel" [(ngModel)]="currentConfig.ldap_uid.value"
required id="ldapUid" size="40" [disabled]="disabled(currentConfig.ldap_uid)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
<input clrInput name="ldapUid" type="text" #ldapUidInput="ngModel"
[(ngModel)]="currentConfig.ldap_uid.value" required id="ldapUid" size="40"
[disabled]="disabled(currentConfig.ldap_uid)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-select-container>
<label for="ldapScope">{{'CONFIG.LDAP.SCOPE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.LDAP_SCOPE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_UID' | translate}}</span>
</a>
</div>
<div class="form-group">
<label for="ldapScope">{{'CONFIG.LDAP.SCOPE' | translate}}</label>
<div class="select">
<select id="ldapScope" name="ldapScope" [(ngModel)]="currentConfig.ldap_scope.value"
<select clrSelect id="ldapScope" name="ldapScope" [(ngModel)]="currentConfig.ldap_scope.value"
[disabled]="disabled(currentConfig.ldap_scope)">
<option value="0">{{'CONFIG.SCOPE_BASE' | translate }}</option>
<option value="1">{{'CONFIG.SCOPE_ONE_LEVEL' | translate }}</option>
<option value="2">{{'CONFIG.SCOPE_SUBTREE' | translate }}</option>
</select>
</div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.LDAP_SCOPE' | translate}}</span>
</a>
</div>
<div class="form-group">
<label for="ldapGroupBaseDN">{{'CONFIG.LDAP.LDAP_GROUP_BASE_DN' | translate}}</label>
<input name="ldapGroupBaseDN" class="padding-right-28" type="text" #ldapGroupDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_base_dn.value" id="ldapGroupBaseDN" size="40"
[disabled]="disabled(currentConfig.ldap_group_base_dn)">
</clr-select-container>
<clr-input-container>
<label for="ldapGroupBaseDN">{{'CONFIG.LDAP.LDAP_GROUP_BASE_DN' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.LDAP_GROUP_BASE_DN_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
<div class="form-group">
<label for="ldapGroupFilter">{{'CONFIG.LDAP.LDAP_GROUP_FILTER' | translate}}</label>
<input name="ldapGroupFilter" type="text" class="padding-right-28" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_search_filter.value" id="ldapGroupFilter" size="40"
[disabled]="disabled(currentConfig.ldap_group_search_filter)">
</label>
<input clrInput name="ldapGroupBaseDN" type="text" #ldapGroupDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_base_dn.value" id="ldapGroupBaseDN" size="40"
[disabled]="disabled(currentConfig.ldap_group_base_dn)" />
</clr-input-container>
<clr-input-container>
<label for="ldapGroupFilter">{{'CONFIG.LDAP.LDAP_GROUP_FILTER' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.LDAP_GROUP_FILTER_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
<div class="form-group">
<label for="ldapGroupGID">{{'CONFIG.LDAP.LDAP_GROUP_GID' | translate}}</label>
<input name="ldapGroupGID" class="padding-right-28" type="text" #ldapGroupDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_attribute_name.value" id="ldapGroupGID" size="40"
[disabled]="disabled(currentConfig.ldap_group_attribute_name)">
</label>
<input clrInput name="ldapGroupFilter" type="text" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_search_filter.value" id="ldapGroupFilter" size="40"
[disabled]="disabled(currentConfig.ldap_group_search_filter)" />
</clr-input-container>
<clr-input-container>
<label for="ldapGroupGID">{{'CONFIG.LDAP.LDAP_GROUP_GID' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.LDAP_GROUP_GID_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
</label>
<input clrInput name="ldapGroupGID" type="text" #ldapGroupDNInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_attribute_name.value" id="ldapGroupGID" size="40"
[disabled]="disabled(currentConfig.ldap_group_attribute_name)" />
</clr-input-container>
<!-- This is for ldap group admin dn -->
<div class="form-group">
<label>{{'CONFIG.LDAP.LDAP_GROUP_ADMIN_DN' | translate}}</label>
<input name="ldapGroupAdminDN" class="padding-right-28" type="text" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_admin_dn.value" id="ldapGroupAdminDN" size="40"
[disabled]="disabled(currentConfig.ldap_group_admin_dn)">
<clr-input-container>
<label for="ldapGroupAdminDN">{{'CONFIG.LDAP.LDAP_GROUP_ADMIN_DN' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.LDAP_GROUP_ADMIN_DN_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
</label>
<input clrInput name="ldapGroupAdminDN" type="text" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_admin_dn.value" id="ldapGroupAdminDN" size="40"
[disabled]="disabled(currentConfig.ldap_group_admin_dn)" />
</clr-input-container>
<!-- End of ldap group admin dn -->
<div class="form-group">
<label>{{'CONFIG.LDAP.LDAP_GROUP_MEMBERSHIP' | translate}}</label>
<input name="ldapGroupMembership" class="padding-right-28" type="text" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_membership_attribute.value" id="ldapGroupMembership" size="40"
[disabled]="disabled(currentConfig.ldap_group_membership_attribute)">
<clr-input-container>
<label for="ldapGroupAdminDN">{{'CONFIG.LDAP.LDAP_GROUP_MEMBERSHIP' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.LDAP_GROUP_MEMBERSHIP_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
<div class="form-group">
<label for="ldapGroupScope">{{'CONFIG.LDAP.GROUP_SCOPE' | translate}}</label>
<div class="select">
<select id="ldapGroupScope" name="ldapGroupScope"
[(ngModel)]="currentConfig.ldap_group_search_scope.value"
[disabled]="disabled(currentConfig.ldap_group_search_scope)">
<option value="0">{{'CONFIG.SCOPE_BASE' | translate }}</option>
<option value="1">{{'CONFIG.SCOPE_ONE_LEVEL' | translate }}</option>
<option value="2">{{'CONFIG.SCOPE_SUBTREE' | translate }}</option>
</select>
</div>
</label>
<input clrInput name="ldapGroupMembership" type="text" #ldapGroupFilterInput="ngModel"
[(ngModel)]="currentConfig.ldap_group_membership_attribute.value" id="ldapGroupMembership" size="40"
[disabled]="disabled(currentConfig.ldap_group_membership_attribute)" />
</clr-input-container>
<clr-select-container>
<label for="ldapGroupScope">{{'CONFIG.LDAP.SCOPE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.LDAP.GROUP_SCOPE_INFO' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</div>
</label>
<select clrSelect id="ldapGroupScope" name="ldapGroupScope"
[(ngModel)]="currentConfig.ldap_group_search_scope.value"
[disabled]="disabled(currentConfig.ldap_group_search_scope)">
<option value="0">{{'CONFIG.SCOPE_BASE' | translate }}</option>
<option value="1">{{'CONFIG.SCOPE_ONE_LEVEL' | translate }}</option>
<option value="2">{{'CONFIG.SCOPE_SUBTREE' | translate }}</option>
</select>
</clr-select-container>
</section>
<section class="form-block">
<div class="form-group" *ngIf="showSelfReg">
<label for="selfReg">{{'CONFIG.SELF_REGISTRATION' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="selfReg" id="selfReg"
[(ngModel)]="currentConfig.self_registration.value"
[disabled]="disabled(currentConfig.self_registration)" />
<label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-top-right top-5">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<clr-checkbox-container *ngIf="showSelfReg">
<label for="selfReg">{{'CONFIG.SELF_REGISTRATION' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span *ngIf="checkable; else elseBlock" class="tooltip-content">{{'CONFIG.TOOLTIP.SELF_REGISTRATION_ENABLE'
| translate}}</span>
<ng-template #elseBlock>
<span
class="tooltip-content">{{'CONFIG.TOOLTIP.SELF_REGISTRATION_DISABLE' | translate}}</span>
</ng-template>
</a>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="selfReg" id="selfReg"
[(ngModel)]="currentConfig.self_registration.value"
[disabled]="disabled(currentConfig.self_registration)" />
</clr-checkbox-wrapper>
</div>
</section>
</clr-checkbox-container>
<section *ngIf="showLdap">
<div class="form-group">
<label for="ldapVerifyCert">{{'CONFIG.LDAP.VERIFY_CERT' | translate}}</label>
<clr-checkbox-container>
<label for="ldapVerifyCert">{{'CONFIG.LDAP.VERIFY_CERT' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.TOOLTIP.VERIFY_CERT' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="ldapVerifyCert" id="ldapVerifyCert"
[ngModel]="currentConfig.ldap_verify_cert.value" [disabled]="disabled(currentConfig.ldap_scope)"
(ngModelChange)="setVerifyCertValue($event)" />
<label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-top-right top-5">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.VERIFY_CERT' | translate}}</span>
</a>
</label>
</clr-checkbox-wrapper>
</div>
</clr-checkbox-container>
</section>
<section class="form-block" *ngIf="showHttpAuth">
<div class="form-group">
<section *ngIf="showHttpAuth">
<clr-input-container>
<label for="http_authproxy_endpoint" class="required">{{'CONFIG.HTTP_AUTH.ENDPOINT' | translate}}</label>
<label for="http_authproxy_endpoint" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="httpAuthproxyEndpointInput.invalid && (httpAuthproxyEndpointInput.dirty || httpAuthproxyEndpointInput.touched)">
<input type="text" pattern="^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.*?)*$"
<input clrInput type="text" pattern="^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.*?)*$"
#httpAuthproxyEndpointInput="ngModel" required id="http_authproxy_endpoint"
name="http_authproxy_endpoint" size="35" [(ngModel)]="currentConfig.http_authproxy_endpoint.value"
[disabled]="!currentConfig.http_authproxy_endpoint.editable">
<span class="tooltip-content">
{{'TOOLTIP.ENDPOINT_FORMAT' | translate}}
</span>
</label>
</div>
<div class="form-group">
<label for="http_authproxy_tokenreview_endpoint" class="required">{{'CONFIG.HTTP_AUTH.TOKEN_REVIEW' | translate}}</label>
<label for="http_authproxy_tokenreview_endpoint" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="httpAuthproxyTokenReviewInput.invalid && (httpAuthproxyTokenReviewInput.dirty || httpAuthproxyTokenReviewInput.touched)">
<input type="text" #httpAuthproxyTokenReviewInput="ngModel" pattern="^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.*?)*$"
required id="http_authproxy_tokenreview_endpoint" name="http_authproxy_tokenreview_endpoint" size="35"
[(ngModel)]="currentConfig.http_authproxy_tokenreview_endpoint.value" [disabled]="!currentConfig.http_authproxy_tokenreview_endpoint.editable">
<span class="tooltip-content">
{{'TOOLTIP.ENDPOINT_FORMAT' | translate}}
</span>
</label>
</div>
<div class="form-group">
[disabled]="!currentConfig.http_authproxy_endpoint.editable" />
<clr-control-error>{{'TOOLTIP.ENDPOINT_FORMAT' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="http_authproxy_tokenreview_endpoint"
class="required">{{'CONFIG.HTTP_AUTH.TOKEN_REVIEW' | translate}}</label>
<input clrInput type="text" #httpAuthproxyTokenReviewInput="ngModel"
pattern="^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.*?)*$" required
id="http_authproxy_tokenreview_endpoint" name="http_authproxy_tokenreview_endpoint" size="35"
[(ngModel)]="currentConfig.http_authproxy_tokenreview_endpoint.value"
[disabled]="!currentConfig.http_authproxy_tokenreview_endpoint.editable" />
<clr-control-error>{{'TOOLTIP.ENDPOINT_FORMAT' | translate}}</clr-control-error>
</clr-input-container>
<clr-checkbox-container clrInline>
<label for="http_authproxy_verify_cert"
class="required">{{'CONFIG.HTTP_AUTH.VERIFY_CERT' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="http_authproxy_verify_cert"
id="http_authproxy_verify_cert"
<input type="checkbox" clrCheckbox name="http_authproxy_verify_cert" id="http_authproxy_verify_cert"
[(ngModel)]="currentConfig.http_authproxy_verify_cert.value"
[disabled]="!currentConfig.http_authproxy_verify_cert.editable" />
</clr-checkbox-wrapper>
</div>
<div class="form-group">
</clr-checkbox-container>
<clr-checkbox-container clrInline>
<label for="http_authproxy_skip_search"
class="required">{{'CONFIG.HTTP_AUTH.SKIP_SEARCH' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="http_authproxy_skip_search"
id="http_authproxy_skip_search"
<input type="checkbox" clrCheckbox name="http_authproxy_skip_search" id="http_authproxy_skip_search"
[disabled]="!currentConfig.http_authproxy_skip_search.editable"
[(ngModel)]="currentConfig.http_authproxy_skip_search.value" />
</clr-checkbox-wrapper>
</div>
</clr-checkbox-container>
</section>
<section class="form-block" *ngIf="showOIDC">
<div class="form-group">
<label for="oidcName" class="required">{{'CONFIG.OIDC.OIDC_PROVIDER' | translate}}</label>
<label for="oidcName" aria-haspopup="true" role="tooltip"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right"
[class.invalid]="oidcNameInput.invalid && (oidcNameInput.dirty || oidcNameInput.touched)">
<input name="oidcName" required type="text" #oidcNameInput="ngModel"
[(ngModel)]="currentConfig.oidc_name.value" required id="oidcName" size="40"
[disabled]="disabled(currentConfig.oidc_name)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
<section *ngIf="showOIDC">
<clr-input-container>
<label class="required" for="oidcName">{{'CONFIG.OIDC.OIDC_PROVIDER' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'TOOLTIP.OIDC_NAME' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'TOOLTIP.OIDC_NAME' | translate}}</span>
</a>
</div>
<div class="form-group">
<label for="oidcEndpoint" class="required">{{'CONFIG.OIDC.ENDPOINT' | translate}}</label>
<label for="oidcEndpoint" aria-haspopup="true" role="tooltip"
[class.invalid]="oidcEndpointInput.invalid && (oidcEndpointInput.dirty || oidcEndpointInput.touched)"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="oidcEndpoint" type="text" #oidcEndpointInput="ngModel" required
<input clrInput name="oidcName" required type="text" #oidcNameInput="ngModel"
[(ngModel)]="currentConfig.oidc_name.value" id="oidcName" size="40"
[disabled]="disabled(currentConfig.oidc_name)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label class="required" for="oidcEndpoint">{{'CONFIG.OIDC.ENDPOINT' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'TOOLTIP.OIDC_ENDPOINT' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<input clrInput name="oidcEndpoint" type="text" #oidcEndpointInput="ngModel" required
[(ngModel)]="currentConfig.oidc_endpoint.value" id="oidcEndpoint" size="40"
[disabled]="disabled(currentConfig.oidc_endpoint)" pattern="^([hH][tT]{2}[pP][sS]:\/\/)(.*?)*$">
<span class="tooltip-content">
{{'TOOLTIP.OIDC_ENDPOIT_FORMAT' | translate}}
</span>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'TOOLTIP.OIDC_ENDPOINT' | translate}}</span>
</a>
</div>
<div class="form-group">
[disabled]="disabled(currentConfig.oidc_endpoint)"
pattern="^([hH][tT]{2}[pP][sS]:\/\/)(.*?)*$" />
<clr-control-error>{{'TOOLTIP.OIDC_ENDPOIT_FORMAT' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="oidcClientId" class="required">{{'CONFIG.OIDC.CLIENT_ID' | translate}}</label>
<label for="oidcClientId" aria-haspopup="true" role="tooltip"
[class.invalid]="oidcClientIdInput.invalid && (oidcClientIdInput.dirty || oidcClientIdInput.touched)"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="oidcClientId" type="text" #oidcClientIdInput="ngModel" required
<input clrInput name="oidcClientId" type="text" #oidcClientIdInput="ngModel" required
[(ngModel)]="currentConfig.oidc_client_id.value" id="oidcClientId" size="40"
[disabled]="disabled(currentConfig.oidc_client_id)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
</div>
<div class="form-group">
[disabled]="disabled(currentConfig.oidc_client_id)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="oidcClientSecret" class="required">{{'CONFIG.OIDC.CLIENTSECRET' | translate}}</label>
<label for="oidcClientSecret" aria-haspopup="true" role="tooltip"
[class.invalid]="oidcClientSecretInput.invalid && (oidcClientSecretInput.dirty || oidcClientSecretInput.touched)"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="oidcClientSecret" type="password" #oidcClientSecretInput="ngModel" required
<input clrInput name="oidcClientSecret" type="password" #oidcClientSecretInput="ngModel" required
[(ngModel)]="currentConfig.oidc_client_secret.value" id="oidcClientSecret" size="40"
[disabled]="disabled(currentConfig.oidc_client_secret)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
[disabled]="disabled(currentConfig.oidc_client_secret)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label class="required" for="oidcScope">{{'CONFIG.OIDC.SCOPE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'TOOLTIP.OIDC_SCOPE' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</div>
<div class="form-group">
<label for="oidcScope" class="required">{{'CONFIG.OIDC.SCOPE' | translate}}</label>
<label for="oidcScope" aria-haspopup="true" role="tooltip"
[class.invalid]="oidcScopeInput.invalid && (oidcScopeInput.dirty || oidcScopeInput.touched)"
class="tooltip tooltip-validation tooltip-lg tooltip-top-right">
<input name="oidcScope" type="text" #oidcScopeInput="ngModel"
<input clrInput name="oidcScope" type="text" #oidcScopeInput="ngModel"
[(ngModel)]="currentConfig.oidc_scope.value" id="oidcScope" size="40" required
[disabled]="disabled(currentConfig.oidc_scope)" pattern="^(\w+,){0,}openid(,\w+){0,}$">
<span class="tooltip-content">
{{'TOOLTIP.SCOPE_REQUIRED' | translate}}
</span>
[disabled]="disabled(currentConfig.oidc_scope)" pattern="^(\w+,){0,}openid(,\w+){0,}$" />
<clr-control-error>{{'TOOLTIP.SCOPE_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-checkbox-container>
<label for="oidc_verify_cert">{{'CONFIG.OIDC.OIDC_VERIFYCERT' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'TOOLTIP.OIDC_VERIFYCERT' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'TOOLTIP.OIDC_SCOPE' | translate}}</span>
</a>
</div>
<div class="form-group">
<label for="oidc_verify_cert">{{'CONFIG.OIDC.OIDC_VERIFYCERT' | translate}}</label>
<clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox name="oidc_verify_cert" id="oidc_verify_cert"
[disabled]="disabled(currentConfig.oidc_verify_cert)"
[(ngModel)]="currentConfig.oidc_verify_cert.value" />
</clr-checkbox-wrapper>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
class="tooltip tooltip-lg tooltip-top-right top-1px">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'TOOLTIP.OIDC_VERIFYCERT' | translate}}</span>
</a>
</div>
<div class="oidc-tip">{{ 'CONFIG.OIDC.OIDC_REDIREC_URL' | translate}} <span>{{redirectUrl}}/c/oidc/callback</span></div>
</clr-checkbox-container>
<div class="oidc-tip">{{ 'CONFIG.OIDC.OIDC_REDIREC_URL' | translate}}
<span>{{redirectUrl}}/c/oidc/callback</span></div>
</section>
</form>
<div>

View File

@ -1,23 +1,11 @@
clr-tooltip {
top: -1px;
}
.padding-right-28 {
padding-right:28px;
}
.top-1 {
top: -1px;
}
.top-1px {
top: 1px;
}
.top-5 {
top: -5px;
}
.oidc-tip {
color: rgb(10, 10, 10);
margin-top: 1rem;
}
.more-info-link {
color: #101010;
}
.ml-04 {
margin-left: 0.4rem;
}

View File

@ -37,7 +37,7 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
// tslint:disable-next-line:no-input-rename
@Input('allConfig') currentConfig: Configuration = new Configuration();
private originalConfig: Configuration;
@ViewChild('authConfigFrom') authForm: NgForm;
@ViewChild('authConfigFrom', {static: false}) authForm: NgForm;
@Output() refreshAllconfig = new EventEmitter<any>();
constructor(

View File

@ -11,10 +11,15 @@ clr-icon {
color: grey;
margin-top: -3px;
}
clr-icon:hover {
color: #007CBB;
}
.clr-validate-icon {
color: red;
}
.config-title {
display: inline-block;
}
@ -22,3 +27,6 @@ clr-icon:hover {
.tooltip-position {
top: -7px;
}
.clr-form-control-disabled {
opacity: 1;
}

View File

@ -48,9 +48,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
originalCopy: Configuration = new Configuration();
confirmSub: Subscription;
@ViewChild(SystemSettingsComponent) systemSettingsConfig: SystemSettingsComponent;
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
@ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent;
@ViewChild(SystemSettingsComponent, {static: false}) systemSettingsConfig: SystemSettingsComponent;
@ViewChild(ConfigurationEmailComponent, {static: false}) mailConfig: ConfigurationEmailComponent;
@ViewChild(ConfigurationAuthComponent, {static: false}) authConfig: ConfigurationAuthComponent;
constructor(
private msgHandler: MessageHandlerService,

View File

@ -1,91 +1,81 @@
<form #mailConfigFrom="ngForm" class="form">
<form #mailConfigFrom="ngForm" class="clr-form clr-form-horizontal">
<section class="form-block">
<div class="form-group">
<clr-input-container>
<label for="mailServer" class="required">{{'CONFIG.MAIL_SERVER' | translate}}</label>
<label for="mailServer" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="mailServerInput.invalid && (mailServerInput.dirty || mailServerInput.touched)">
<input name="mailServer" type="text" #mailServerInput="ngModel" [(ngModel)]="currentConfig.email_host.value"
required id="mailServer" size="40" [disabled]="disabled(currentConfig.email_host)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
</div>
<div class="form-group">
<input clrInput name="mailServer" type="text" #mailServerInput="ngModel"
[(ngModel)]="currentConfig.email_host.value" required id="mailServer" size="40"
[disabled]="disabled(currentConfig.email_host)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="emailPort" class="required">{{'CONFIG.MAIL_SERVER_PORT' | translate}}</label>
<label for="emailPort" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="emailPortInput.invalid && (emailPortInput.dirty || emailPortInput.touched)">
<input name="emailPort" type="text" #emailPortInput="ngModel" [(ngModel)]="currentConfig.email_port.value"
required port id="emailPort" size="40" [disabled]="disabled(currentConfig.email_port)">
<span class="tooltip-content">
{{'TOOLTIP.PORT_REQUIRED' | translate}}
</span>
</label>
</div>
<div class="form-group">
<input clrInput name="emailPort" type="text" #emailPortInput="ngModel"
[(ngModel)]="currentConfig.email_port.value" required port id="emailPort" size="40"
[disabled]="disabled(currentConfig.email_port)" />
<clr-control-error>{{'TOOLTIP.PORT_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<clr-input-container>
<label for="emailUsername">{{'CONFIG.MAIL_USERNAME' | translate}}</label>
<label for="emailUsername" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="false">
<input name="emailUsername" type="text" #emailUsernameInput="ngModel" [(ngModel)]="currentConfig.email_username.value"
id="emailUsername" size="40" [disabled]="disabled(currentConfig.email_username)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
</div>
<div class="form-group">
<input clrInput name="emailUsername" type="text" #emailUsernameInput="ngModel"
[(ngModel)]="currentConfig.email_username.value" id="emailUsername" size="40"
[disabled]="disabled(currentConfig.email_username)" />
</clr-input-container>
<clr-input-container>
<label for="emailPassword">{{'CONFIG.MAIL_PASSWORD' | translate}}</label>
<label for="emailPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="false">
<input name="emailPassword" type="password" #emailPasswordInput="ngModel" [(ngModel)]="currentConfig.email_password.value"
id="emailPassword" size="40" [disabled]="disabled(currentConfig.email_password)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
</label>
</div>
<div class="form-group">
<input clrInput name="emailPassword" type="password" #emailPasswordInput="ngModel"
[(ngModel)]="currentConfig.email_password.value" id="emailPassword" size="40"
[disabled]="disabled(currentConfig.email_password)" />
</clr-input-container>
<clr-input-container>
<label for="emailFrom" class="required">{{'CONFIG.MAIL_FROM' | translate}}</label>
<label for="emailFrom" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"
[class.invalid]="emailFromInput.invalid && (emailFromInput.dirty || emailFromInput.touched)">
<input name="emailFrom" type="text" #emailFromInput="ngModel" [(ngModel)]="currentConfig.email_from.value"
required id="emailFrom" size="40" [disabled]="disabled(currentConfig.email_from)">
<span class="tooltip-content">
{{'TOOLTIP.ITEM_REQUIRED' | translate}}
</span>
<input clrInput name="emailFrom" type="text" #emailFromInput="ngModel"
[(ngModel)]="currentConfig.email_from.value" required id="emailFrom" size="40"
[disabled]="disabled(currentConfig.email_from)" />
<clr-control-error>{{'TOOLTIP.ITEM_REQUIRED' | translate}}</clr-control-error>
</clr-input-container>
<div class="clr-form-control">
<label class="clr-control-label" for="emailSSL">{{'CONFIG.MAIL_SSL' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.SSL_TOOLTIP' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</div>
<div class="form-group">
<label for="selfReg">{{'CONFIG.MAIL_SSL' | translate}}</label>
<!--for = selfReg?-->
<clr-checkbox-wrapper id="emailSSL-wrapper">
<input type="checkbox" clrCheckbox name="emailSSL" id="emailSSL" [(ngModel)]="currentConfig.email_ssl.value"
<div class="clr-control-container">
<div class="clr-checkbox-wrapper" id="emailSSL-wrapper">
<input type="checkbox" name="emailSSL" id="emailSSL" [(ngModel)]="currentConfig.email_ssl.value"
[disabled]="disabled(currentConfig.email_ssl)" />
<label>
<a role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right top-7">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.SSL_TOOLTIP' | translate}}</span>
</a>
<label class="clr-control-label" for="emailSSL">
</label>
</clr-checkbox-wrapper>
</div>
<div class="form-group">
<label for="insecure">{{'CONFIG.MAIL_INSECURE' | translate}}</label>
<clr-checkbox-wrapper id="emailInsecure-wrapper">
<input type="checkbox" clrCheckbox name="emaiInsecure" id="emailInsecure" [ngModel]="!currentConfig.email_insecure.value"
[disabled]="disabled(currentConfig.email_insecure)" (ngModelChange)="setInsecureValue($event)" />
<label>
<a role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right top-7">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.INSECURE_TOOLTIP' | translate}}</span>
</a>
</div>
</div>
<div class="clr-form-control">
<label class="clr-control-label" for="emailInsecure">{{'CONFIG.MAIL_INSECURE' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
<span>{{'CONFIG.INSECURE_TOOLTIP' | translate}}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
</clr-checkbox-wrapper>
<div class="clr-control-container">
<div class="clr-checkbox-wrapper" id="emailInsecure-wrapper">
<input type="checkbox" name="emailInsecure" id="emailInsecure"
[ngModel]="!currentConfig.email_insecure.value"
[disabled]="disabled(currentConfig.email_insecure)"
(ngModelChange)="setInsecureValue($event)" />
<label class="clr-control-label" for="emailInsecure"></label>
</div>
</div>
</div>
</section>
</form>
<div>
<button type="button" id="config_email_save" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE'
<button type="button" id="config_email_save" class="btn btn-primary" (click)="save()"
[disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE'
| translate}}</button>
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL'
| translate}}</button>

View File

@ -31,7 +31,7 @@ export class ConfigurationEmailComponent implements OnChanges {
private originalConfig: Configuration;
testingMailOnGoing = false;
onGoing = false;
@ViewChild("mailConfigFrom") mailForm: NgForm;
@ViewChild("mailConfigFrom", {static: true}) mailForm: NgForm;
constructor(
private msgHandler: MessageHandlerService,

View File

@ -25,7 +25,7 @@ export class AddGroupModalComponent implements OnInit, OnDestroy {
formChangeSubscription: Subscription;
@ViewChild('groupForm')
@ViewChild('groupForm', {static: false})
groupForm: NgForm;
submitted = false;

View File

@ -39,7 +39,7 @@ export class GroupComponent implements OnInit, OnDestroy {
batchInfos = new Map();
isLdapMode: boolean;
@ViewChild(AddGroupModalComponent) newGroupModal: AddGroupModalComponent;
@ViewChild(AddGroupModalComponent, {static: false}) newGroupModal: AddGroupModalComponent;
constructor(
private operationService: OperationService,

View File

@ -30,7 +30,7 @@
</div>
</div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 datagrid-margin-top ">
<clr-datagrid (clrDgRefresh)="retrievePage($event)">
<clr-datagrid (clrDgRefresh)="retrievePage()">
<clr-dg-column>{{'AUDIT_LOG.USERNAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.REPOSITORY_NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.TAGS' | translate}}</clr-dg-column>

View File

@ -109,13 +109,10 @@ export class AuditLogComponent implements OnInit {
);
}
retrievePage(state: State) {
if (state && state.page) {
this.queryParam.page = Math.ceil((state.page.to + 1) / this.pageSize);
this.currentPage = this.queryParam.page;
retrievePage() {
this.queryParam.page = this.currentPage;
this.retrieve();
}
}
doSearchAuditLogs(searchUsername: string): void {
this.queryParam.username = searchUsername;

View File

@ -1,85 +1,75 @@
<clr-modal [(clrModalOpen)]="createProjectOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
<h3 class="modal-title">{{'PROJECT.NEW_PROJECT' | translate}}</h3>
<inline-alert class="modal-title"></inline-alert>
<div class="modal-body modal-height">
<form #projectForm="ngForm">
<section class="form-block">
<div class="form-group">
<label for="create_project_name" class="col-md-3 form-group-label-override required">{{'PROJECT.NAME' | translate}}</label>
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="!isNameValid" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left">
<input type="text" id="create_project_name" [(ngModel)]="project.name"
name="create_project_name" size="255" class="input-width"
required
pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
minlength="2"
#projectName="ngModel"
autocomplete="off"
<inline-alert></inline-alert>
<form #projectForm="ngForm" class="clr-form clr-form-horizontal">
<clr-input-container>
<label class="required">{{'PROJECT.NAME' | translate}}</label>
<input clrInput type="text" id="create_project_name" [(ngModel)]="project.name" name="create_project_name" class="input-width"
required pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" minlength="2" #projectName="ngModel" autocomplete="off"
(keyup)='handleValidation()'>
<span class="tooltip-content">
<clr-control-error *ngIf="!isNameValid" class="tooltip-content">
{{ nameTooltipText | translate }}
</span>
</label>
</clr-control-error>
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
</div>
<div class="form-group">
<label class="col-md-4 form-group-label-override">{{'PROJECT.ACCESS_LEVEL' | translate}}</label>
<div class="checkbox-inline">
<input type="checkbox" id="create_project_public" [(ngModel)]="project.metadata.public" name="public">
<label for="create_project_public"></label>
<span class="access-level-label">{{ 'PROJECT.PUBLIC' | translate}}</span>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-md tooltip-right public-tooltip">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content inline-help-public">{{'PROJECT.INLINE_HELP_PUBLIC' | translate }}</span>
</a>
</div>
</div>
<div class="form-group" *ngIf="isSystemAdmin">
<label for="create_project_count_limit" class="required col-md-3 form-group-label-override">{{'PROJECT.COUNT_QUOTA' | translate}}</label>
<label for="create_project_count_limit" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left"
[class.invalid]="projectCountLimit.invalid && (projectCountLimit.dirty || projectCountLimit.touched)" >
<input type="text" id="create_project_count_limit" [(ngModel)]="countLimit"
name="create_project_count_limit" class="input-width"
pattern="(^-1$)|(^([1-9]+)([0-9]+)*$)"
required
#projectCountLimit="ngModel"
autocomplete="off" >
<span class="tooltip-content">
</clr-input-container>
<clr-checkbox-container>
<label class="form-group-label-override">{{'PROJECT.ACCESS_LEVEL' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="bottom-left" clrSize="lg" *clrIfOpen>
<span>{{'PROJECT.INLINE_HELP_PUBLIC' | translate }}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<clr-checkbox-wrapper>
<input clrCheckbox type="checkbox" id="create_project_public" [(ngModel)]="project.metadata.public" name="public">
<label>{{ 'PROJECT.PUBLIC' | translate}}</label>
</clr-checkbox-wrapper>
</clr-checkbox-container>
<clr-input-container *ngIf="isSystemAdmin">
<label for="create_project_count_limit" class="required">{{'PROJECT.COUNT_QUOTA' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="bottom-left" clrSize="lg" *clrIfOpen>
<span>{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<input clrInput type="text" id="create_project_count_limit" [(ngModel)]="countLimit" name="create_project_count_limit" class="input-width"
#projectCountLimit="ngModel" autocomplete="off">
<clr-control-error class="tooltip-content">
{{ 'PROJECT.COUNT_QUOTA_TIP' | translate }}
</span>
</clr-control-error>
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
</clr-input-container>
<div class="clr-form-control" *ngIf="isSystemAdmin">
<label for="create_project_storage_limit" class="required clr-control-label">{{'PROJECT.STORAGE_QUOTA' | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content clrPosition="bottom-left" clrSize="lg" *clrIfOpen>
<span>{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</clr-tooltip-content>
</clr-tooltip>
</label>
<div class="checkbox-inline">
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-md tooltip-top-left public-tooltip">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content inline-help-public">{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</a>
</div>
</div>
<div class="form-group" *ngIf="isSystemAdmin">
<label for="create_project_storage_limit" class="required col-md-3 form-group-label-override">{{'PROJECT.STORAGE_QUOTA' | translate}}</label>
<label for="create_project_storage_limit" aria-haspopup="true" role="tooltip" class="tooltip-quota-storage tooltip tooltip-validation tooltip-md tooltip-top-left"
[class.invalid]="(projectStorageLimit.invalid && (projectStorageLimit.dirty || projectStorageLimit.touched))||projectStorageLimit.errors" >
<input type="text" id="create_project_storage_limit" [(ngModel)]="storageLimit"
name="create_project_storage_limit" size="255" class="input-width"
#projectStorageLimit="ngModel"
autocomplete="off" >
<span class="tooltip-content">
{{ 'PROJECT.STORAGE_QUOTA_TIP' | translate }}
</span>
</label>
<select clrSelect id="create_project_storage_limit_unit" name="create_project_storage_limit_unit" [(ngModel)]="storageLimitUnit">
<div class="clr-control-container" [class.clr-error]="(projectStorageLimit.invalid && (projectStorageLimit.dirty || projectStorageLimit.touched))||projectStorageLimit.errors">
<input type="text" id="create_project_storage_limit" [(ngModel)]="storageLimit" name="create_project_storage_limit" class="mr-10 clr-input"
#projectStorageLimit="ngModel" autocomplete="off">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<div class="clr-select-wrapper">
<select id="create_project_storage_limit_unit" name="create_project_storage_limit_unit" [(ngModel)]="storageLimitUnit">
<ng-template ngFor let-quotaUnit [ngForOf]="quotaUnits" let-i="index">
<option *ngIf="i>1" [value]="quotaUnit.UNIT">{{ quotaUnit.UNIT }}</option>
</ng-template>
</select>
<div class="checkbox-inline">
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-md tooltip-top-left public-tooltip">
<clr-icon shape="info-circle" size="24"></clr-icon>
<span class="tooltip-content inline-help-public">{{'PROJECT.QUOTA_UNLIMIT_TIP' | translate }}</span>
</a>
</div>
<clr-control-error *ngIf="(projectStorageLimit.invalid && (projectStorageLimit.dirty || projectStorageLimit.touched))||projectStorageLimit.errors"
class="tooltip-content">
{{ 'PROJECT.STORAGE_QUOTA_TIP' | translate }}
</clr-control-error>
</div>
</div>
</section>
</form>
</div>
<div class="modal-footer">

View File

@ -34,8 +34,7 @@ import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.com
import { Project } from "../project";
import { ProjectService, QuotaUnits, QuotaHardInterface, QuotaUnlimited, getByte
, GetIntegerAndUnit, clone, StorageMultipleConstant, validateLimit, validateCountLimit} from "@harbor/ui";
import { errorHandler } from '@angular/platform-browser/src/browser';
, GetIntegerAndUnit, clone, validateLimit, validateCountLimit} from "@harbor/ui";
@Component({
selector: "create-project",
@ -46,7 +45,7 @@ export class CreateProjectComponent implements OnInit, OnChanges, OnDestroy {
projectForm: NgForm;
@ViewChild("projectForm")
@ViewChild("projectForm", {static: true})
currentForm: NgForm;
quotaUnits = QuotaUnits;
project: Project = new Project();
@ -74,7 +73,7 @@ export class CreateProjectComponent implements OnInit, OnChanges, OnDestroy {
@Output() create = new EventEmitter<boolean>();
@Input() quotaObj: QuotaHardInterface;
@Input() isSystemAdmin: boolean;
@ViewChild(InlineAlertComponent)
@ViewChild(InlineAlertComponent, {static: true})
inlineAlert: InlineAlertComponent;
constructor(private projectService: ProjectService,

View File

@ -1,47 +1,18 @@
.form-group-label-override {
font-size: 14px;
font-weight: 400;
.mr-10 {
margin-right:0.5rem;
}
.access-level-label {
font-size: 14px;
font-weight: 400;
margin-left: -4px;
margin-right: 12px;
top: -6px;
position: relative;
}
.modal-height {
height: 15.3em;
overflow-y: hidden;
.form-block > div {
padding-left: 135px;
.input-width {
width: 196px;
}
.public-tooltip {
top: -8px;
left: -8px;
}
.inline-help-public {
margin-left: 5px;
}
.display-flex {
display: flex;
align-items:center;
}
.pos-inherit {
position: inherit;
}
.form-group {
::ng-deep {
clr-select-container {
margin-top: 0.3rem;
.clr-error {
.clr-select-wrapper::after {
right: 0.25rem !important;
}
}
select {
display: inline;
}
.checkbox-inline {
margin-left: 5px;
height: 1rem;
}
}

View File

@ -17,7 +17,7 @@ export class LabelFilterComponent implements ClrDatagridFilterInterface<any>, On
@Input() labels: Label[] = [];
@Input() resourceType: ResourceType;
@ViewChild('filterInput') filterInputRef: ElementRef;
@ViewChild('filterInput', {static: false}) filterInputRef: ElementRef;
selectedLabels: Map<number, boolean> = new Map<number, boolean>();

View File

@ -30,7 +30,7 @@ export class LabelMarkerComponent implements OnInit {
labelChangeDebouncer: Subject<any> = new Subject();
@ViewChild('filterInput') filterInputRef: ElementRef;
@ViewChild('filterInput', {static: false}) filterInputRef: ElementRef;
ngOnInit(): void {
this.sortedLabels = this.labels;

View File

@ -82,7 +82,7 @@ export class ChartVersionComponent implements OnInit {
addLabelHeaders = 'HELM_CHART.ADD_LABEL_TO_CHART_VERSION';
@ViewChild("confirmationDialog")
@ViewChild("confirmationDialog", {static: false})
confirmationDialog: ConfirmationDialogComponent;
hasAddRemoveHelmChartVersionPermission: boolean;
hasDownloadHelmChartVersionPermission: boolean;

View File

@ -3,8 +3,8 @@
<div class="toolbar">
<div class="row flex-items-xs-right option-right rightPos">
<div class="flex-xs-middle">
<hbr-filter [withDivider]="true" filterPlaceholder="{{'HELM_CHART.FILTER_FOR_CHARTS' | translate}}"
[currentValue]="lastFilteredChartName" (filterEvt)="updateFilterValue($event)"></hbr-filter>
<hbr-filter [withDivider]="true" filterPlaceholder="{{'HELM_CHART.FILTER_FOR_CHARTS' | translate}}" [currentValue]="lastFilteredChartName"
(filterEvt)="updateFilterValue($event)"></hbr-filter>
<span class="card-btn" (click)="showCard(true)" (mouseenter)="mouseEnter('card') " (mouseleave)="mouseLeave('card')">
<clr-icon [ngClass]="{'is-highlight': isCardView || isHovering('card') }" shape="view-cards"></clr-icon>
</span>
@ -23,14 +23,16 @@
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
<clr-dg-action-bar>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasUploadHelmChartsPermission" (click)="onChartUpload()">
<button type="button" id="helm-chart-upload" class="btn btn-sm btn-secondary"
[disabled]="!hasUploadHelmChartsPermission" (click)="onChartUpload()">
<clr-icon shape="upload" size="16"></clr-icon>{{'HELM_CHART.UPLOAD' | translate}}
</button>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasDeleteHelmChartsPermission || selectedRows.length<1"
(click)="openChartDeleteModal(selectedRows)">
<clr-icon shape="trash" size="16"></clr-icon>{{'BUTTON.DELETE' | translate}}
</button>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasDownloadHelmChartsPermission ||selectedRows.length!==1" (click)="downloadLatestVersion()">
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!hasDownloadHelmChartsPermission ||selectedRows.length!==1"
(click)="downloadLatestVersion()">
<clr-icon shape="download" size="16"></clr-icon>{{'HELM_CHART.DOWNLOAD' | translate}}
</button>
</clr-dg-action-bar>
@ -42,8 +44,7 @@
<clr-dg-row *ngFor="let chart of charts" [clrDgItem]="chart">
<clr-dg-cell>
<span class="list-img">
<img class="size-24 margin-right-12" [src]="chart.icon ?chart.icon:chartDefaultIcon"
(error)="getDefaultIcon(chart);" />
<img class="size-24 margin-right-12" [src]="chart.icon ?chart.icon:chartDefaultIcon" (error)="getDefaultIcon(chart);" />
</span>
<a href="javascript:void(0)" (click)="onChartClick(chart)">{{ chart.name }}</a>
</clr-dg-cell>
@ -75,14 +76,13 @@
<div class="row flex-items-xs-between">
<div>
<span class="version-text">{{item.total_versions}}</span>
<label class="card-label" *ngIf="item.total_versions !== 1">{{'HELM_CHART.CHARTVERSIONS' |
translate}}</label>
<label class="card-label" *ngIf="item.total_versions === 1">{{'HELM_CHART.VERSION' |
translate}}</label>
<label class="card-label" *ngIf="item.total_versions !== 1">{{'HELM_CHART.CHARTVERSIONS' | translate}}
</label>
<label class="card-label" *ngIf="item.total_versions === 1">{{'HELM_CHART.VERSION' | translate}}
</label>
</div>
<div>
<span class="label" [class.label-danger]="item.deprecated" [class.label-success]="!item.deprecated">{{getStatusString(item)
| translate}}</span>
<span class="label" [class.label-danger]="item.deprecated" [class.label-success]="!item.deprecated">{{getStatusString(item) | translate}}</span>
</div>
</div>
</div>
@ -97,37 +97,39 @@
<clr-modal [(clrModalOpen)]="isUploadModalOpen" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
<h3 class="modal-title">{{'HELM_CHART.UPLOAD_TITLE' | translate | titlecase}}</h3>
<div class="modal-body">
<form #chartUploadForm="ngForm" enctype="multipart/form-data" (ngSubmit)="upload()">
<section class="form-block">
<div class="form-group">
<label class="filename-label"> {{'HELM_CHART.CHART_FILE' | translate}} </label>
<input class="filename-input" type="text" placeholder="{{this.chartFile?.name || 'BUTTON.NO_FILE' | translate}}"
<form #chartUploadForm="ngForm" clrForm enctype="multipart/form-data">
<div class="clr-form-control">
<label class="filename-label clr-control-label clr-col-md-3"> {{'HELM_CHART.CHART_FILE' | translate}}</label>
<div class="clr-input-wrapper">
<input class="filename-input clr-input" type="text" placeholder="{{this.chartFile?.name || 'BUTTON.NO_FILE' | translate}}"
disabled>
<label for="chart" class="btn btn-secondary btn-sm file-browser-btn">{{'BUTTON.BROWSE' |
translate}}</label>
<label for="chart" class="btn btn-secondary btn-sm file-browser-btn">{{'BUTTON.BROWSE' | translate}}
</label>
<input class="file-input" type="file" id="chart" name="chart" ngModel (change)="onChartFileChangeEvent($event)">
</div>
<div class="form-group">
<label class="filename-label"> {{'HELM_CHART.CHART_PROV' | translate}} </label>
<input class="filename-input" type="text" placeholder="{{this.provFile?.name || 'BUTTON.NO_FILE' | translate}}"
</div>
<div class="clr-form-control mb-10">
<label class="filename-label clr-control-label clr-col-md-3"> {{'HELM_CHART.CHART_PROV' | translate}} </label>
<div class="clr-input-wrapper">
<input class="filename-input clr-input" type="text" placeholder="{{this.provFile?.name || 'BUTTON.NO_FILE' | translate}}"
disabled>
<label for="prov" class="btn btn-secondary btn-sm file-browser-btn">{{'BUTTON.BROWSE' |
translate}}</label>
<label for="prov" class="btn btn-secondary btn-sm file-browser-btn">{{'BUTTON.BROWSE' | translate}}
</label>
<input class="file-input" type="file" id="prov" name="prov" ngModel (change)="onProvFileChangeEvent($event)">
</div>
</section>
<div class="row flex-items-xs-right">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" [disabled]="isUploading" (click)="cancelUpload()">
<span>{{'BUTTON.CANCEL' | translate}}</span>
</button>
<button type="submit" class="btn btn-primary" [disabled]="isUploading">
<button type="submit" class="btn btn-primary" id="upload-chart" [disabled]="isUploading" (click)="upload()">
<span>{{'HELM_CHART.UPLOAD' | translate}}</span>
<span *ngIf="isUploading" class="spinner spinner-inline">
Loading...
</span>
</button>
</div>
</form>
</div>
</clr-modal>
</div>

View File

@ -92,9 +92,6 @@ $size60:60px;
}
clr-modal {
.form-group {
padding-left: 6rem;
.filename-label {
padding-top: 9px;
}
@ -106,8 +103,7 @@ clr-modal {
.file-browser-btn {
margin-left: 15px;
max-width: 25%;
}
max-width: 32%;
}
}
@ -116,3 +112,7 @@ button {
margin-right: 6px;
}
}
.mb-10 {
margin-bottom:10px;
}

View File

@ -68,9 +68,9 @@ export class HelmChartComponent implements OnInit {
totalCount = 0;
currentState: State;
@ViewChild('chartUploadForm') uploadForm: NgForm;
@ViewChild('chartUploadForm', {static: false}) uploadForm: NgForm;
@ViewChild("confirmationDialog") confirmationDialog: ConfirmationDialogComponent;
@ViewChild("confirmationDialog", {static: false}) confirmationDialog: ConfirmationDialogComponent;
hasUploadHelmChartsPermission: boolean;
hasDownloadHelmChartsPermission: boolean;
hasDeleteHelmChartsPermission: boolean;

View File

@ -2,7 +2,7 @@
<clr-dg-action-bar>
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewProject()" *ngIf="projectCreationRestriction">
<clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'PROJECT.NEW_PROJECT' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!canDelete"
<button id="delete-project" type="button" class="btn btn-sm btn-secondary" [disabled]="!canDelete"
(click)="deleteProjects(selectedRow)">
<clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'PROJECT.DELETE' | translate}}</button>
</clr-dg-action-bar>

View File

@ -2,55 +2,57 @@
<h3 class="modal-title">{{'MEMBER.IMPORT_GROUP' | translate}}</h3>
<div class="modal-body">
<label>{{ 'MEMBER.NEW_GROUP_INFO' | translate}}</label>
<div class="form-group modeSelectradios">
<div class="radio">
<input type="radio" name="modeRadios" [value]="false" id="select_group" [(ngModel)]="createGroupMode">
<div class="modeSelectradios">
<clr-radio-wrapper>
<input clrRadio type="radio" name="modeRadios" [value]="false" id="select_group" [(ngModel)]="createGroupMode">
<label for="select_group">{{'MEMBER.ADD_GROUP_SELECT' | translate}}</label>
</div>
<div class="radio">
<input type="radio" name="modeRadios" [value]="true" id="create_group" [(ngModel)]="createGroupMode">
</clr-radio-wrapper>
<clr-radio-wrapper>
<input clrRadio type="radio" name="modeRadios" [value]="true" id="create_group" [(ngModel)]="createGroupMode">
<label for="create_group">{{'MEMBER.CREATE_GROUP_SELECT' | translate}}</label>
</div>
</clr-radio-wrapper>
</div>
<div *ngIf="createGroupMode">
<form #groupForm="ngForm">
<section class="form-block">
<div class="form-group">
<label for="ldap_group_dn" class="required">{{ 'MEMBER.LDAP_SEARCH_DN' | translate}}</label>
<label for="ldap_group_dn"
aria-haspopup="true"
role="tooltip"
class="tooltip tooltip-validation tooltip-md tooltip-right"
[class.invalid]="isDNInvalid">
<input type="text" name="ldap_group_dn" size="45"
<form #groupForm="ngForm" class="clr-form clr-form-horizontal">
<div class="clr-form-control">
<label class="required clr-control-label">{{ 'MEMBER.LDAP_SEARCH_DN' | translate}}</label>
<div class="clr-control-container" [class.clr-error]="isDNInvalid">
<div class="clr-input-wrapper">
<input class="clr-input" type="text" name="ldap_group_dn" size="45"
required
[(ngModel)]="group.ldap_group_dn"
#groupDN="ngModel">
<span class="tooltip-content">
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
</div>
<clr-control-error *ngIf="isDNInvalid" class="tooltip-content">
{{dnTooltip | translate}}
</span>
</label>
</clr-control-error>
</div>
<div class="form-group">
<label for="name">{{'MEMBER.LDAP_SEARCH_NAME' | translate}}</label>
<input type="text" name="ldap_group_name" size="35" [(ngModel)]="group.group_name">
</div>
<div class="form-group">
<label for="member_role1">{{ 'MEMBER.ROLE' | translate}}</label>
<div class="select">
<select id="member_role1" name="member_role" [(ngModel)]="selectedRole">
<div class="clr-form-control">
<label class="clr-control-label">{{'MEMBER.LDAP_SEARCH_NAME' | translate}}</label>
<div class="clr-control-container">
<div class="clr-input-wrapper">
<input class="clr-input" type="text" name="ldap_group_name" size="35" [(ngModel)]="group.group_name">
</div>
</div>
</div>
<div class="clr-form-control">
<label class="clr-control-label" for="member_role1">{{ 'MEMBER.ROLE' | translate}}</label>
<div class="clr-select-wrapper">
<select class="clr-select" id="member_role1" name="member_role" [(ngModel)]="selectedRole">
<option *ngFor="let role of roles" [ngValue]="role.id"> {{role.value | translate}}</option>
</select>
</div>
</div>
</section>
</form>
</div>
<div *ngIf="!createGroupMode">
<div class='row flex-items-xs-between'>
<div></div>
<div class="filterTool">
<div>
<div class="row flex-items-xs-between">
<div class="left">
</div>
<div class="right">
<hbr-filter [withDivider]="true" filterPlaceholder='{{"MEMBER.FILTER_PLACEHOLDER" | translate}}' (filterEvt)="doFilter($event)"
[currentValue]="currentTerm"></hbr-filter>
<span class="refresh-btn" (click)="loadGroups()">
@ -58,6 +60,7 @@
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-1 col-md-1 col-sm-1 col-xs-1">
<label>{{'MEMBER.LDAP_GROUP' | translate}}</label>
@ -85,13 +88,11 @@
</clr-datagrid>
</div>
</div>
<div class="row">
<div class="col-lg-1 col-md-1 col-sm-1 col-xs-1">
<label>{{ 'MEMBER.ROLE' | translate}}</label>
</div>
<div class="class=col-lg-4 col-md-4 col-sm-2 col-xs-1">
<div class="select">
<select id="member_role2" [(ngModel)]="selectedRole">
<div class="clr-form clr-form-horizontal">
<div class="clr-form-control">
<label class="clr-control-label" for="member_role1">{{ 'MEMBER.ROLE' | translate}}</label>
<div class="clr-select-wrapper">
<select class="clr-select" id="member_role2" [(ngModel)]="selectedRole">
<option *ngFor="let role of roles" [ngValue]="role.id"> {{role.value | translate}}</option>
</select>
</div>

View File

@ -13,7 +13,10 @@ clr-datagrid {
.modeSelectradios {
margin-top: 21px;
}
.filterTool {
.left {
flex: 1;
}
.right {
position: relative;
z-index: 100;
right: 15px;

View File

@ -41,7 +41,7 @@ export class AddGroupComponent implements OnInit {
@Input() memberList: Member[] = [];
@Output() added = new EventEmitter<boolean>();
@ViewChild('groupForm')
@ViewChild('groupForm', {static: false})
groupForm: NgForm;
constructor(

Some files were not shown because too many files have changed in this diff Show More