@c8y/ngx-components 1022.16.2 → 1022.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/asset-properties/asset-properties.model.d.ts +117 -0
- package/asset-properties/asset-properties.model.d.ts.map +1 -0
- package/asset-properties/asset-properties.service.d.ts +72 -0
- package/asset-properties/asset-properties.service.d.ts.map +1 -0
- package/asset-properties/asset-property-list/asset-property-action.directive.d.ts +11 -0
- package/asset-properties/asset-property-list/asset-property-action.directive.d.ts.map +1 -0
- package/asset-properties/asset-property-list/asset-property-icon.pipe.d.ts +11 -0
- package/asset-properties/asset-property-list/asset-property-icon.pipe.d.ts.map +1 -0
- package/asset-properties/asset-property-list/asset-property-list.component.d.ts +195 -0
- package/asset-properties/asset-property-list/asset-property-list.component.d.ts.map +1 -0
- package/asset-properties/asset-property-list/asset-property-value.pipe.d.ts +17 -0
- package/asset-properties/asset-property-list/asset-property-value.pipe.d.ts.map +1 -0
- package/asset-properties/asset-property-list/tree-data-source.d.ts +19 -0
- package/asset-properties/asset-property-list/tree-data-source.d.ts.map +1 -0
- package/asset-properties/asset-property-selector-drawer/asset-property-selector-drawer.component.d.ts +75 -0
- package/asset-properties/asset-property-selector-drawer/asset-property-selector-drawer.component.d.ts.map +1 -0
- package/asset-properties/c8y-ngx-components-asset-properties.d.ts.map +1 -0
- package/asset-properties/index.d.ts +6 -0
- package/asset-properties/index.d.ts.map +1 -0
- package/core/asset-property/asset-property.model.d.ts +0 -8
- package/core/asset-property/asset-property.model.d.ts.map +1 -1
- package/core/authentication/new-password.component.d.ts +3 -2
- package/core/authentication/new-password.component.d.ts.map +1 -1
- package/core/modal/modal.service.d.ts +1 -0
- package/core/modal/modal.service.d.ts.map +1 -1
- package/core/user/user-edit-modal.component.d.ts +1 -0
- package/core/user/user-edit-modal.component.d.ts.map +1 -1
- package/core/user/user-edit.component.d.ts +4 -1
- package/core/user/user-edit.component.d.ts.map +1 -1
- package/datapoint-explorer/datapoint-explorer.module.d.ts +2 -0
- package/datapoint-explorer/datapoint-explorer.module.d.ts.map +1 -1
- package/datapoint-explorer/devicemanagement/c8y-ngx-components-datapoint-explorer-devicemanagement.d.ts.map +1 -0
- package/datapoint-explorer/devicemanagement/index.d.ts +2 -0
- package/datapoint-explorer/devicemanagement/index.d.ts.map +1 -0
- package/datapoint-explorer/view/datapoint-explorer.component.d.ts +8 -3
- package/datapoint-explorer/view/datapoint-explorer.component.d.ts.map +1 -1
- package/datapoint-explorer/view/datapoint-explorer.model.d.ts +8 -0
- package/datapoint-explorer/view/datapoint-explorer.model.d.ts.map +1 -0
- package/datapoint-explorer/view/index.d.ts +1 -0
- package/datapoint-explorer/view/index.d.ts.map +1 -1
- package/device-provisioned-certificates/device-tab-provisioned-certificates.component.d.ts +8 -2
- package/device-provisioned-certificates/device-tab-provisioned-certificates.component.d.ts.map +1 -1
- package/echart/charts.component.d.ts +1 -1
- package/echart/charts.component.d.ts.map +1 -1
- package/echart/index.d.ts +1 -0
- package/echart/index.d.ts.map +1 -1
- package/echart/models/datapoints-graph-widget.model.d.ts +13 -0
- package/echart/models/datapoints-graph-widget.model.d.ts.map +1 -1
- package/echart/services/chart-helpers.service.d.ts +23 -0
- package/echart/services/chart-helpers.service.d.ts.map +1 -0
- package/echart/services/chart-realtime.service.d.ts +2 -0
- package/echart/services/chart-realtime.service.d.ts.map +1 -1
- package/echart/services/echarts-options.service.d.ts +5 -5
- package/echart/services/echarts-options.service.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-alarms.mjs +2 -2
- package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-asset-properties.mjs +1573 -0
- package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-datapoint-explorer-devicemanagement.mjs +38 -0
- package/fesm2022/c8y-ngx-components-datapoint-explorer-devicemanagement.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +157 -141
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-explorer.mjs +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-explorer.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-list.mjs +2 -2
- package/fesm2022/c8y-ngx-components-device-list.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs +32 -18
- package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart-models.mjs +14 -1
- package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +141 -46
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +3 -3
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-time-context.mjs +12 -5
- package/fesm2022/c8y-ngx-components-time-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +2 -2
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs +6 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +14 -6
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +51 -10
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +1152 -1128
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/locales/de.po +61 -46
- package/locales/es.po +44 -27
- package/locales/fr.po +72 -55
- package/locales/ja_JP.po +106 -88
- package/locales/ko.po +43 -27
- package/locales/locales.pot +49 -27
- package/locales/nl.po +44 -27
- package/locales/pl.po +44 -27
- package/locales/pt_BR.po +44 -27
- package/locales/zh_CN.po +44 -27
- package/locales/zh_TW.po +44 -27
- package/package.json +1 -1
- package/time-context/index.d.ts +1 -0
- package/time-context/index.d.ts.map +1 -1
- package/time-context/time-context.component.d.ts +6 -3
- package/time-context/time-context.component.d.ts.map +1 -1
- package/time-context/time-context.model.d.ts +10 -0
- package/time-context/time-context.model.d.ts.map +1 -0
- package/time-context/time-context.service.d.ts +2 -6
- package/time-context/time-context.service.d.ts.map +1 -1
- package/widgets/definitions/html-widget/html-widget-config.factory.d.ts.map +1 -1
- package/widgets/implementations/datapoints-graph/datapoints-graph-config/datapoints-graph-widget-config.component.d.ts +2 -0
- package/widgets/implementations/datapoints-graph/datapoints-graph-config/datapoints-graph-widget-config.component.d.ts.map +1 -1
- package/widgets/implementations/html-widget/html-widget-properties-selector/html-widget-properties-selector.component.d.ts +17 -0
- package/widgets/implementations/html-widget/html-widget-properties-selector/html-widget-properties-selector.component.d.ts.map +1 -0
- package/widgets/implementations/html-widget/html-widget.model.d.ts +2 -2
- package/widgets/implementations/html-widget/html-widget.model.d.ts.map +1 -1
- package/widgets/implementations/html-widget/index.d.ts +1 -0
- package/widgets/implementations/html-widget/index.d.ts.map +1 -1
- package/device-parameters/c8y-ngx-components-device-parameters.d.ts.map +0 -1
- package/device-parameters/device-parameter-details.component.d.ts +0 -22
- package/device-parameters/device-parameter-details.component.d.ts.map +0 -1
- package/device-parameters/device-parameter-value.component.d.ts +0 -12
- package/device-parameters/device-parameter-value.component.d.ts.map +0 -1
- package/device-parameters/device-parameters-list.component.d.ts +0 -23
- package/device-parameters/device-parameters-list.component.d.ts.map +0 -1
- package/device-parameters/device-parameters-tab.guard.d.ts +0 -11
- package/device-parameters/device-parameters-tab.guard.d.ts.map +0 -1
- package/device-parameters/index.d.ts +0 -7
- package/device-parameters/index.d.ts.map +0 -1
- package/device-parameters/parameter-type-cell-renderer.components.d.ts +0 -8
- package/device-parameters/parameter-type-cell-renderer.components.d.ts.map +0 -1
- package/fesm2022/c8y-ngx-components-device-parameters.mjs +0 -215
- package/fesm2022/c8y-ngx-components-device-parameters.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Input, Directive, Injectable, Injector, InjectionToken, NgModuleRef, createNgModule, Optional, Inject, isDevMode, inject, Pipe, EventEmitter, NgModule, LOCALE_ID, EnvironmentInjector, createEnvironmentInjector, HostListener, Component, HostBinding, Output, forwardRef, DestroyRef, ViewChild, SecurityContext, TemplateRef, provideAppInitializer, Self, SkipSelf, Attribute, ContentChild, ViewContainerRef, ElementRef, ContentChildren, ViewChildren, createComponent, runInInjectionContext, importProvidersFrom, ChangeDetectionStrategy, SimpleChange, reflectComponentType, signal,
|
|
2
|
+
import { Input, Directive, Injectable, Injector, InjectionToken, NgModuleRef, createNgModule, Optional, Inject, isDevMode, inject, Pipe, EventEmitter, NgModule, LOCALE_ID, EnvironmentInjector, createEnvironmentInjector, HostListener, Component, HostBinding, Output, forwardRef, DestroyRef, ViewChild, SecurityContext, TemplateRef, provideAppInitializer, Self, SkipSelf, Attribute, ContentChild, ViewContainerRef, ElementRef, ContentChildren, ViewChildren, viewChild, effect, createComponent, runInInjectionContext, importProvidersFrom, ChangeDetectionStrategy, SimpleChange, reflectComponentType, signal, Type, computed, input, output } from '@angular/core';
|
|
3
3
|
import * as i1$3 from 'ngx-bootstrap/dropdown';
|
|
4
4
|
import { BsDropdownModule, BsDropdownDirective } from 'ngx-bootstrap/dropdown';
|
|
5
5
|
import { CdkTrapFocus, A11yModule } from '@angular/cdk/a11y';
|
|
@@ -19106,1296 +19106,1320 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
19106
19106
|
}]
|
|
19107
19107
|
}] });
|
|
19108
19108
|
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
this.
|
|
19115
|
-
this.
|
|
19109
|
+
class PasswordConfirmModalComponent {
|
|
19110
|
+
constructor(user, ui, client, alert) {
|
|
19111
|
+
this.user = user;
|
|
19112
|
+
this.ui = ui;
|
|
19113
|
+
this.client = client;
|
|
19114
|
+
this.alert = alert;
|
|
19115
|
+
this.passwordConfirmedEmitter = new EventEmitter();
|
|
19116
|
+
this.loading = false;
|
|
19116
19117
|
}
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19118
|
+
async passwordConfirm() {
|
|
19119
|
+
if (this.password) {
|
|
19120
|
+
const supportUserName = this.ui.currentSupportUserName.value;
|
|
19121
|
+
const userId = this.ui.currentUser.value.id;
|
|
19122
|
+
const credentials = {
|
|
19123
|
+
password: this.password,
|
|
19124
|
+
user: `${supportUserName ? `${supportUserName}$` : ''}${userId}`,
|
|
19125
|
+
tenant: this.client.tenant
|
|
19126
|
+
};
|
|
19127
|
+
try {
|
|
19128
|
+
this.loading = true;
|
|
19129
|
+
const auth = new BasicAuth(credentials);
|
|
19130
|
+
const newClient = this.createNewClient(auth, this.client.baseUrl);
|
|
19131
|
+
await newClient.user.current();
|
|
19132
|
+
this.emitSuccessAndClose();
|
|
19133
|
+
}
|
|
19134
|
+
catch (e) {
|
|
19135
|
+
if (e.res && e.res.status === 401 && e.data && /pin|totp/i.test(e.data.message)) {
|
|
19136
|
+
this.emitSuccessAndClose();
|
|
19137
|
+
}
|
|
19138
|
+
else {
|
|
19139
|
+
this.alert.danger(gettext$1("Provided password doesn't match your current one."));
|
|
19140
|
+
}
|
|
19141
|
+
}
|
|
19142
|
+
finally {
|
|
19143
|
+
this.loading = false;
|
|
19144
|
+
}
|
|
19145
|
+
}
|
|
19146
|
+
}
|
|
19147
|
+
cancel() {
|
|
19148
|
+
this.passwordConfirmedEmitter.emit(false);
|
|
19149
|
+
this.modal._dismiss();
|
|
19150
|
+
}
|
|
19151
|
+
createNewClient(auth, baseUrl) {
|
|
19152
|
+
return new Client(auth, baseUrl);
|
|
19153
|
+
}
|
|
19154
|
+
emitSuccessAndClose() {
|
|
19155
|
+
this.passwordConfirmedEmitter.emit(true);
|
|
19156
|
+
this.modal._dismiss();
|
|
19157
|
+
}
|
|
19158
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirmModalComponent, deps: [{ token: i1.UserService }, { token: AppStateService }, { token: i1.FetchClient }, { token: AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19159
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasswordConfirmModalComponent, isStandalone: true, selector: "c8y-password-confirm-modal", outputs: { passwordConfirmedEmitter: "passwordConfirmedEmitter" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Confirm your current password' | translate\" #modal>\r\n <form #confirmForm=\"ngForm\" (ngSubmit)=\"confirmForm.form.valid && passwordConfirm()\">\r\n <div class=\"d-block p-24 p-b-0\">\r\n <c8y-form-group [hasWarning]=\"true\">\r\n <label translate for=\"currentPassword\">Enter your password</label>\r\n <input\r\n id=\"currentPassword\"\r\n [(ngModel)]=\"password\"\r\n type=\"password\"\r\n name=\"password\"\r\n class=\"form-control\"\r\n placeholder=\"{{ 'Enter your password' | translate }}\"\r\n required\r\n />\r\n <c8y-messages>\r\n <c8y-message translate>\r\n Enter the password of the user that you are currently logged in with.\r\n </c8y-message>\r\n </c8y-messages>\r\n </c8y-form-group>\r\n </div>\r\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\r\n <button\r\n title=\"{{ 'Cancel' | translate }}\"\r\n class=\"btn btn-default\"\r\n type=\"button\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ 'Cancel' | translate }}\r\n </button>\r\n <button\r\n title=\"{{ 'Confirm' | translate }}\"\r\n class=\"btn btn-primary\"\r\n type=\"submit\"\r\n [disabled]=\"!confirmForm.form.valid || loading\"\r\n >\r\n {{ 'Confirm' | translate }}\r\n </button>\r\n </div>\r\n </form>\r\n</c8y-modal>\r\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19160
|
+
}
|
|
19161
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirmModalComponent, decorators: [{
|
|
19162
|
+
type: Component,
|
|
19163
|
+
args: [{ selector: 'c8y-password-confirm-modal', standalone: true, imports: [
|
|
19164
|
+
ModalComponent,
|
|
19165
|
+
FormsModule$1,
|
|
19166
|
+
FormGroupComponent,
|
|
19167
|
+
C8yTranslateDirective,
|
|
19168
|
+
RequiredInputPlaceholderDirective,
|
|
19169
|
+
MessagesComponent,
|
|
19170
|
+
MessageDirective,
|
|
19171
|
+
C8yTranslatePipe
|
|
19172
|
+
], template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Confirm your current password' | translate\" #modal>\r\n <form #confirmForm=\"ngForm\" (ngSubmit)=\"confirmForm.form.valid && passwordConfirm()\">\r\n <div class=\"d-block p-24 p-b-0\">\r\n <c8y-form-group [hasWarning]=\"true\">\r\n <label translate for=\"currentPassword\">Enter your password</label>\r\n <input\r\n id=\"currentPassword\"\r\n [(ngModel)]=\"password\"\r\n type=\"password\"\r\n name=\"password\"\r\n class=\"form-control\"\r\n placeholder=\"{{ 'Enter your password' | translate }}\"\r\n required\r\n />\r\n <c8y-messages>\r\n <c8y-message translate>\r\n Enter the password of the user that you are currently logged in with.\r\n </c8y-message>\r\n </c8y-messages>\r\n </c8y-form-group>\r\n </div>\r\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\r\n <button\r\n title=\"{{ 'Cancel' | translate }}\"\r\n class=\"btn btn-default\"\r\n type=\"button\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ 'Cancel' | translate }}\r\n </button>\r\n <button\r\n title=\"{{ 'Confirm' | translate }}\"\r\n class=\"btn btn-primary\"\r\n type=\"submit\"\r\n [disabled]=\"!confirmForm.form.valid || loading\"\r\n >\r\n {{ 'Confirm' | translate }}\r\n </button>\r\n </div>\r\n </form>\r\n</c8y-modal>\r\n" }]
|
|
19173
|
+
}], ctorParameters: () => [{ type: i1.UserService }, { type: AppStateService }, { type: i1.FetchClient }, { type: AlertService }], propDecorators: { passwordConfirmedEmitter: [{
|
|
19174
|
+
type: Output
|
|
19175
|
+
}], modal: [{
|
|
19176
|
+
type: ViewChild,
|
|
19177
|
+
args: ['modal', { static: false }]
|
|
19178
|
+
}] } });
|
|
19179
|
+
|
|
19180
|
+
class CurrentPasswordModalComponent {
|
|
19181
|
+
constructor(modal) {
|
|
19182
|
+
this.modal = modal;
|
|
19183
|
+
this.currentPasswordEmitter = new EventEmitter();
|
|
19184
|
+
}
|
|
19185
|
+
passwordConfirm() {
|
|
19186
|
+
this.currentPasswordEmitter.emit(this.password);
|
|
19187
|
+
this.modal.hide();
|
|
19188
|
+
}
|
|
19189
|
+
cancel() {
|
|
19190
|
+
this.currentPasswordEmitter.emit(null);
|
|
19191
|
+
this.modal.hide();
|
|
19192
|
+
}
|
|
19193
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CurrentPasswordModalComponent, deps: [{ token: i1$7.BsModalRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19194
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CurrentPasswordModalComponent, isStandalone: true, selector: "c8y-current-password-modal", outputs: { currentPasswordEmitter: "currentPasswordEmitter" }, ngImport: i0, template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Confirm your current password' | translate\" #modal>\n <form #confirmForm=\"ngForm\" (ngSubmit)=\"confirmForm.form.valid && passwordConfirm()\">\n <div class=\"d-block p-24\">\n <c8y-form-group [hasWarning]=\"true\">\n <label translate for=\"currentPassword\">Enter your password</label>\n <input\n id=\"currentPassword\"\n [(ngModel)]=\"password\"\n type=\"password\"\n name=\"password\"\n class=\"form-control\"\n required\n />\n <c8y-messages>\n <c8y-message translate>\n The password of the user that you are currently logged in with.\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Confirm' | translate }}\"\n class=\"btn btn-primary\"\n [attr.data-cy]=\"'confirm-current-password-confirm-button'\"\n type=\"submit\"\n [disabled]=\"!confirmForm.form.valid\"\n >\n {{ 'Confirm' | translate }}\n </button>\n </div>\n </form>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19195
|
+
}
|
|
19196
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CurrentPasswordModalComponent, decorators: [{
|
|
19197
|
+
type: Component,
|
|
19198
|
+
args: [{ selector: 'c8y-current-password-modal', standalone: true, imports: [
|
|
19199
|
+
ModalComponent,
|
|
19200
|
+
FormsModule$1,
|
|
19201
|
+
FormGroupComponent,
|
|
19202
|
+
C8yTranslateDirective,
|
|
19203
|
+
RequiredInputPlaceholderDirective,
|
|
19204
|
+
MessagesComponent,
|
|
19205
|
+
MessageDirective,
|
|
19206
|
+
C8yTranslatePipe
|
|
19207
|
+
], template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Confirm your current password' | translate\" #modal>\n <form #confirmForm=\"ngForm\" (ngSubmit)=\"confirmForm.form.valid && passwordConfirm()\">\n <div class=\"d-block p-24\">\n <c8y-form-group [hasWarning]=\"true\">\n <label translate for=\"currentPassword\">Enter your password</label>\n <input\n id=\"currentPassword\"\n [(ngModel)]=\"password\"\n type=\"password\"\n name=\"password\"\n class=\"form-control\"\n required\n />\n <c8y-messages>\n <c8y-message translate>\n The password of the user that you are currently logged in with.\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Confirm' | translate }}\"\n class=\"btn btn-primary\"\n [attr.data-cy]=\"'confirm-current-password-confirm-button'\"\n type=\"submit\"\n [disabled]=\"!confirmForm.form.valid\"\n >\n {{ 'Confirm' | translate }}\n </button>\n </div>\n </form>\n</c8y-modal>\n" }]
|
|
19208
|
+
}], ctorParameters: () => [{ type: i1$7.BsModalRef }], propDecorators: { currentPasswordEmitter: [{
|
|
19209
|
+
type: Output
|
|
19210
|
+
}] } });
|
|
19211
|
+
|
|
19212
|
+
class PasswordService {
|
|
19213
|
+
constructor(modalService) {
|
|
19214
|
+
this.modalService = modalService;
|
|
19215
|
+
this.DEFAULT_PASSWORD_MIN_LENGTH = 8;
|
|
19216
|
+
this.GREEN = {
|
|
19217
|
+
colorName: 'green',
|
|
19218
|
+
color: 'rgb(0, 128, 0)',
|
|
19219
|
+
description: gettext$1('strong'),
|
|
19220
|
+
passwordStrength: PasswordStrength.GREEN
|
|
19130
19221
|
};
|
|
19131
|
-
|
|
19132
|
-
|
|
19222
|
+
this.YELLOW = {
|
|
19223
|
+
colorName: 'yellow',
|
|
19224
|
+
color: 'rgb(255, 204, 51)',
|
|
19225
|
+
description: gettext$1('medium'),
|
|
19226
|
+
passwordStrength: PasswordStrength.YELLOW
|
|
19227
|
+
};
|
|
19228
|
+
this.RED = {
|
|
19229
|
+
colorName: 'red',
|
|
19230
|
+
color: 'rgb(170, 0, 51)',
|
|
19231
|
+
description: gettext$1('weak'),
|
|
19232
|
+
passwordStrength: PasswordStrength.RED
|
|
19233
|
+
};
|
|
19234
|
+
}
|
|
19235
|
+
confirmPassword() {
|
|
19236
|
+
const modalInstance = this.modalService.show(PasswordConfirmModalComponent, {
|
|
19133
19237
|
ariaDescribedby: 'modal-body',
|
|
19134
|
-
ariaLabelledBy: 'modal-title'
|
|
19135
|
-
ignoreBackdropClick: true
|
|
19238
|
+
ariaLabelledBy: 'modal-title'
|
|
19136
19239
|
});
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
}
|
|
19140
|
-
this.triggerEvent(modalRef.content.result, modalLabels, productExperienceEvent);
|
|
19141
|
-
return await modalRef.content.result;
|
|
19240
|
+
const passwordConfirmedEmitter = modalInstance.content.passwordConfirmedEmitter;
|
|
19241
|
+
return passwordConfirmedEmitter.pipe(take(1));
|
|
19142
19242
|
}
|
|
19143
19243
|
/**
|
|
19144
|
-
*
|
|
19145
|
-
*
|
|
19146
|
-
*
|
|
19147
|
-
* @param status The status.
|
|
19148
|
-
* @param acknowledgeLabel The label to use.
|
|
19149
|
-
* @param productExperienceEvent Additional data to attach to custom product experience events.
|
|
19244
|
+
* Returns an observable with the password provided by user:
|
|
19245
|
+
* - `string` when user provided a value
|
|
19246
|
+
* - `null` when user cancelled the modal
|
|
19150
19247
|
*/
|
|
19151
|
-
|
|
19152
|
-
const
|
|
19153
|
-
|
|
19154
|
-
initialState: { title, body, labels, status },
|
|
19248
|
+
currentPassword() {
|
|
19249
|
+
const modalInstance = this.modalService.show(CurrentPasswordModalComponent, {
|
|
19250
|
+
class: 'modal-sm',
|
|
19155
19251
|
ariaDescribedby: 'modal-body',
|
|
19156
|
-
ariaLabelledBy: 'modal-title'
|
|
19157
|
-
ignoreBackdropClick: true
|
|
19252
|
+
ariaLabelledBy: 'modal-title'
|
|
19158
19253
|
});
|
|
19159
|
-
|
|
19160
|
-
|
|
19161
|
-
}
|
|
19162
|
-
this.triggerEvent(modalRef.content.result, labels, productExperienceEvent);
|
|
19163
|
-
return await modalRef.content.result;
|
|
19254
|
+
const currentPasswordEmitter = modalInstance.content.currentPasswordEmitter;
|
|
19255
|
+
return currentPasswordEmitter.pipe(take(1));
|
|
19164
19256
|
}
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
* @param body The text body to display. Default: 'You will be logged out to apply your changes. Do you want to proceed?'
|
|
19168
|
-
* @param status The status.
|
|
19169
|
-
* @param labels The labels to use. Default: { ok: 'Confirm and log out', cancel: 'Cancel' }
|
|
19170
|
-
*/
|
|
19171
|
-
async confirmLogout(body, status = Status.WARNING, labels = {}) {
|
|
19172
|
-
const modalLabels = {
|
|
19173
|
-
ok: labels.ok || gettext$1('Confirm and log out'),
|
|
19174
|
-
cancel: labels.cancel || gettext$1('Cancel')
|
|
19175
|
-
};
|
|
19176
|
-
const modalBody = body || gettext$1('You must log out to apply your changes. Do you want to proceed?');
|
|
19177
|
-
return await this.confirm(gettext$1('Logout required'), modalBody, status, modalLabels);
|
|
19257
|
+
hasLowerCase(password) {
|
|
19258
|
+
return password.search(/[a-z]/) !== -1;
|
|
19178
19259
|
}
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
result
|
|
19182
|
-
.then(() => {
|
|
19183
|
-
this.gainsightService.triggerEvent(productExperienceEvent.eventName, {
|
|
19184
|
-
...data,
|
|
19185
|
-
result: labels.ok
|
|
19186
|
-
});
|
|
19187
|
-
})
|
|
19188
|
-
.catch(() => {
|
|
19189
|
-
this.gainsightService.triggerEvent(productExperienceEvent.eventName, {
|
|
19190
|
-
...data,
|
|
19191
|
-
result: labels.cancel
|
|
19192
|
-
});
|
|
19193
|
-
});
|
|
19260
|
+
hasUpperCase(password) {
|
|
19261
|
+
return password.search(/[A-Z]/) !== -1;
|
|
19194
19262
|
}
|
|
19195
|
-
|
|
19196
|
-
|
|
19263
|
+
hasNumbers(password) {
|
|
19264
|
+
return password.search(/[0-9]/) !== -1;
|
|
19265
|
+
}
|
|
19266
|
+
hasSpecialChars(password) {
|
|
19267
|
+
return password.search(/[^0-9a-zA-Z]+/) !== -1;
|
|
19268
|
+
}
|
|
19269
|
+
getStrengthColor(password) {
|
|
19270
|
+
const passwordStrength = filter$2([
|
|
19271
|
+
this.hasLowerCase(password),
|
|
19272
|
+
this.hasUpperCase(password),
|
|
19273
|
+
this.hasNumbers(password),
|
|
19274
|
+
this.hasSpecialChars(password)
|
|
19275
|
+
]).length;
|
|
19276
|
+
if (passwordStrength > 3) {
|
|
19277
|
+
return this.GREEN;
|
|
19278
|
+
}
|
|
19279
|
+
else if (passwordStrength >= 3) {
|
|
19280
|
+
return this.YELLOW;
|
|
19281
|
+
}
|
|
19282
|
+
else {
|
|
19283
|
+
return this.RED;
|
|
19284
|
+
}
|
|
19285
|
+
}
|
|
19286
|
+
getDefaultPasswordMinLength() {
|
|
19287
|
+
return this.DEFAULT_PASSWORD_MIN_LENGTH;
|
|
19288
|
+
}
|
|
19289
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordService, deps: [{ token: i1$7.BsModalService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
19290
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordService, providedIn: 'root' }); }
|
|
19197
19291
|
}
|
|
19198
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19292
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordService, decorators: [{
|
|
19199
19293
|
type: Injectable,
|
|
19200
|
-
args: [{
|
|
19201
|
-
|
|
19202
|
-
}]
|
|
19203
|
-
}], ctorParameters: () => [{ type: i1$7.BsModalService }, { type: GainsightService }] });
|
|
19294
|
+
args: [{ providedIn: 'root' }]
|
|
19295
|
+
}], ctorParameters: () => [{ type: i1$7.BsModalService }] });
|
|
19204
19296
|
|
|
19205
|
-
class
|
|
19206
|
-
|
|
19207
|
-
this.
|
|
19208
|
-
this.darkThemeClass = `c8y-dark-theme`;
|
|
19209
|
-
this.themeOptions = [
|
|
19210
|
-
{
|
|
19211
|
-
label: gettext$1('Light'),
|
|
19212
|
-
value: 'light',
|
|
19213
|
-
icon: 'sun'
|
|
19214
|
-
},
|
|
19215
|
-
{
|
|
19216
|
-
label: gettext$1('Dark'),
|
|
19217
|
-
value: 'dark',
|
|
19218
|
-
icon: 'moon'
|
|
19219
|
-
},
|
|
19220
|
-
{
|
|
19221
|
-
label: gettext$1('System'),
|
|
19222
|
-
value: 'system',
|
|
19223
|
-
icon: 'imac-settings'
|
|
19224
|
-
}
|
|
19225
|
-
];
|
|
19226
|
-
this._userSelectedThemePreference$ = new BehaviorSubject(this.getCurrentThemePreference());
|
|
19227
|
-
this._temporaryThemePreference$ = new BehaviorSubject('none');
|
|
19228
|
-
this.userSelectedThemePreference$ = this._userSelectedThemePreference$.asObservable();
|
|
19229
|
-
const userSelectedTheme$ = this.userSelectedThemePreference$.pipe(switchMap(preference => {
|
|
19230
|
-
if (preference === 'system') {
|
|
19231
|
-
return this.getUsersSystemPreferenceForTheme$();
|
|
19232
|
-
}
|
|
19233
|
-
return of(preference);
|
|
19234
|
-
}));
|
|
19235
|
-
this.disableThemeSelection$ = this._temporaryThemePreference$.pipe(map(preference => preference !== 'none'));
|
|
19236
|
-
this.currentlyAppliedTheme$ = this._temporaryThemePreference$.pipe(switchMap(temporaryPreference => {
|
|
19237
|
-
if (temporaryPreference !== 'none') {
|
|
19238
|
-
return of(temporaryPreference);
|
|
19239
|
-
}
|
|
19240
|
-
return userSelectedTheme$;
|
|
19241
|
-
}));
|
|
19242
|
-
this.darkThemeAvailable$ = this.options.get$('darkThemeAvailable').pipe(map(value => !!value));
|
|
19243
|
-
}
|
|
19244
|
-
getCurrentThemePreference() {
|
|
19245
|
-
const value = getThemePreference();
|
|
19246
|
-
if (value === 'system' || value === 'dark') {
|
|
19247
|
-
return value;
|
|
19248
|
-
}
|
|
19249
|
-
return 'light';
|
|
19250
|
-
}
|
|
19251
|
-
getUsersSystemPreferenceForTheme$() {
|
|
19252
|
-
return fromEvent(window.matchMedia('(prefers-color-scheme: dark)'), 'change').pipe(startWith(window.matchMedia('(prefers-color-scheme: dark)')), map((e) => (e.matches ? 'dark' : 'light')));
|
|
19297
|
+
class TotpSetupComponent {
|
|
19298
|
+
get qrCodeImage() {
|
|
19299
|
+
return this.totpSecret ? this.totpSecret.secretQrUrl : '';
|
|
19253
19300
|
}
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
this._userSelectedThemePreference$.next(preference);
|
|
19257
|
-
this.applyTheme(preference);
|
|
19301
|
+
get secret() {
|
|
19302
|
+
return this.totpSecret ? this.totpSecret.rawSecret : '';
|
|
19258
19303
|
}
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19304
|
+
get qrData() {
|
|
19305
|
+
// TODO: waiting for BE:, now we need extract it from secretQrUrl
|
|
19306
|
+
// https://cumulocity.atlassian.net/browse/MTM-36387
|
|
19307
|
+
// return this.totpSecret ? this.totpSecret.qrData : '';
|
|
19308
|
+
const url = new URL(this.qrCodeImage);
|
|
19309
|
+
const otpAuth = url.searchParams.get('chl') || url.searchParams.get('data');
|
|
19310
|
+
if (!otpAuth) {
|
|
19311
|
+
this.alert.danger(gettext$1('Failed to generate a QR code.'));
|
|
19312
|
+
return '';
|
|
19313
|
+
}
|
|
19314
|
+
return decodeURIComponent(otpAuth);
|
|
19262
19315
|
}
|
|
19263
|
-
|
|
19264
|
-
this.
|
|
19265
|
-
this.
|
|
19316
|
+
constructor(user, alert) {
|
|
19317
|
+
this.user = user;
|
|
19318
|
+
this.alert = alert;
|
|
19266
19319
|
}
|
|
19267
|
-
|
|
19268
|
-
|
|
19320
|
+
async ngOnInit() {
|
|
19321
|
+
try {
|
|
19322
|
+
const { data } = await this.user.generateTotpSecret();
|
|
19323
|
+
this.totpSecret = data;
|
|
19324
|
+
}
|
|
19325
|
+
catch (ex) {
|
|
19326
|
+
this.alert.addServerFailure(ex);
|
|
19327
|
+
}
|
|
19269
19328
|
}
|
|
19270
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19271
|
-
static { this.ɵ
|
|
19329
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TotpSetupComponent, deps: [{ token: i1.UserService }, { token: AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19330
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TotpSetupComponent, isStandalone: true, selector: "c8y-totp-setup", ngImport: i0, template: "<div class=\"text-center\">\n <p\n class=\"m-24 m-t-16 m-b-0\"\n translate\n >\n Scan this QR code with your smartphone using the authenticator application.\n </p>\n\n <div class=\"d-flex j-c-center\">\n <qrcode\n [width]=\"180\"\n *ngIf=\"totpSecret\"\n [qrdata]=\"qrData\"\n [errorCorrectionLevel]=\"'M'\"\n [elementType]=\"'svg'\"\n ></qrcode>\n </div>\n <p class=\"text-center text-muted\">\n {{ secret }}\n </p>\n</div>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: QRCodeComponent, selector: "qrcode", inputs: ["allowEmptyString", "colorDark", "colorLight", "cssClass", "elementType", "errorCorrectionLevel", "imageSrc", "imageHeight", "imageWidth", "margin", "qrdata", "scale", "version", "width", "alt", "ariaLabel", "title"], outputs: ["qrCodeURL"] }] }); }
|
|
19272
19331
|
}
|
|
19273
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19274
|
-
type:
|
|
19275
|
-
args: [{
|
|
19276
|
-
|
|
19277
|
-
}]
|
|
19278
|
-
}], ctorParameters: () => [{ type: OptionsService }] });
|
|
19332
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TotpSetupComponent, decorators: [{
|
|
19333
|
+
type: Component,
|
|
19334
|
+
args: [{ selector: 'c8y-totp-setup', standalone: true, imports: [C8yTranslateDirective, NgIf, QRCodeComponent], template: "<div class=\"text-center\">\n <p\n class=\"m-24 m-t-16 m-b-0\"\n translate\n >\n Scan this QR code with your smartphone using the authenticator application.\n </p>\n\n <div class=\"d-flex j-c-center\">\n <qrcode\n [width]=\"180\"\n *ngIf=\"totpSecret\"\n [qrdata]=\"qrData\"\n [errorCorrectionLevel]=\"'M'\"\n [elementType]=\"'svg'\"\n ></qrcode>\n </div>\n <p class=\"text-center text-muted\">\n {{ secret }}\n </p>\n</div>\n" }]
|
|
19335
|
+
}], ctorParameters: () => [{ type: i1.UserService }, { type: AlertService }] });
|
|
19279
19336
|
|
|
19280
|
-
class
|
|
19281
|
-
constructor(
|
|
19282
|
-
this.
|
|
19283
|
-
this.
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
this.
|
|
19288
|
-
|
|
19289
|
-
|
|
19290
|
-
|
|
19291
|
-
this.
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
this.
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
ngOnDestroy() {
|
|
19303
|
-
this.destroyed$.next();
|
|
19304
|
-
this.destroyed$.complete();
|
|
19337
|
+
class TotpChallengeComponent {
|
|
19338
|
+
constructor(users, alert) {
|
|
19339
|
+
this.users = users;
|
|
19340
|
+
this.alert = alert;
|
|
19341
|
+
/**
|
|
19342
|
+
* Calls the verify endpoint if set to true (default true)
|
|
19343
|
+
*/
|
|
19344
|
+
this.verify = true;
|
|
19345
|
+
/**
|
|
19346
|
+
* Emits the token on success.
|
|
19347
|
+
*/
|
|
19348
|
+
this.onSuccess = new EventEmitter();
|
|
19349
|
+
/**
|
|
19350
|
+
* Emits if set up two-factor authentication is canceled.
|
|
19351
|
+
*/
|
|
19352
|
+
this.totpUnconfirmedEmitter = new EventEmitter();
|
|
19353
|
+
this.loading = false;
|
|
19354
|
+
this.hasError = false;
|
|
19355
|
+
this.isModal = false;
|
|
19356
|
+
this.model = {
|
|
19357
|
+
token: ''
|
|
19358
|
+
};
|
|
19305
19359
|
}
|
|
19306
|
-
async
|
|
19307
|
-
|
|
19308
|
-
|
|
19360
|
+
async verifyCode() {
|
|
19361
|
+
try {
|
|
19362
|
+
this.loading = true;
|
|
19363
|
+
this.hasError = false;
|
|
19364
|
+
if (this.verify) {
|
|
19365
|
+
await this.users.verifyTotpCode(this.model.token);
|
|
19366
|
+
}
|
|
19367
|
+
this.onSuccess.emit(this.model.token);
|
|
19309
19368
|
}
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19369
|
+
catch (e) {
|
|
19370
|
+
this.hasError = true;
|
|
19371
|
+
this.alert.removeLastDanger();
|
|
19372
|
+
this.loading = false;
|
|
19313
19373
|
}
|
|
19314
19374
|
}
|
|
19315
|
-
|
|
19316
|
-
|
|
19375
|
+
cancel() {
|
|
19376
|
+
this.totpUnconfirmedEmitter.emit();
|
|
19377
|
+
}
|
|
19378
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TotpChallengeComponent, deps: [{ token: i1.UserService }, { token: AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19379
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: TotpChallengeComponent, isStandalone: true, selector: "c8y-totp-challenge", inputs: { verify: "verify", loading: "loading", hasError: "hasError", isModal: "isModal" }, outputs: { onSuccess: "onSuccess", totpUnconfirmedEmitter: "totpUnconfirmedEmitter" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<form #totpForm=\"ngForm\" class=\"loginForm\" (ngSubmit)=\"verifyCode()\" novalidate>\n <div class=\"d-block p-b-0\" [ngClass]=\"isModal ? 'p-24' : 'p-t-24'\">\n <c8y-form-group\n [hasError]=\"hasError\"\n [novalidation]=\"true\"\n [ngClass]=\"{ 'p-b-24': hasError }\"\n >\n <label translate for=\"totpToken\">Verification code</label>\n\n <input\n id=\"totpToken\"\n [(ngModel)]=\"model.token\"\n name=\"totpToken\"\n type=\"text\"\n autofocus\n autocapitalize=\"off\"\n autocorrect=\"off\"\n autocomplete=\"off\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} 624327\"\n required\n />\n\n <p id=\"helpinput\" *ngIf=\"!hasError\" class=\"help-block\" translate>\n In case of key loss, please contact your platform administrator.\n </p>\n </c8y-form-group>\n </div>\n <div [ngClass]=\"isModal ? 'modal-footer separator-top bg-level-0 sticky-bottom' : 'text-center'\">\n <button\n class=\"btn btn-default\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"submit\"\n title=\"{{ 'Verify' | translate }}\"\n *ngIf=\"!loading\"\n [disabled]=\"!totpForm.form.valid\"\n >\n {{ 'Verify' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary btn-pending\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"submit\"\n title=\"{{ 'Verifying\u2026' | translate }}\"\n *ngIf=\"loading\"\n >\n {{ 'Verifying\u2026' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
|
|
19380
|
+
}
|
|
19381
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TotpChallengeComponent, decorators: [{
|
|
19382
|
+
type: Component,
|
|
19383
|
+
args: [{ selector: 'c8y-totp-challenge', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [
|
|
19384
|
+
FormsModule$1,
|
|
19385
|
+
NgClass,
|
|
19386
|
+
FormGroupComponent,
|
|
19387
|
+
C8yTranslateDirective,
|
|
19388
|
+
RequiredInputPlaceholderDirective,
|
|
19389
|
+
NgIf,
|
|
19390
|
+
C8yTranslatePipe
|
|
19391
|
+
], template: "<form #totpForm=\"ngForm\" class=\"loginForm\" (ngSubmit)=\"verifyCode()\" novalidate>\n <div class=\"d-block p-b-0\" [ngClass]=\"isModal ? 'p-24' : 'p-t-24'\">\n <c8y-form-group\n [hasError]=\"hasError\"\n [novalidation]=\"true\"\n [ngClass]=\"{ 'p-b-24': hasError }\"\n >\n <label translate for=\"totpToken\">Verification code</label>\n\n <input\n id=\"totpToken\"\n [(ngModel)]=\"model.token\"\n name=\"totpToken\"\n type=\"text\"\n autofocus\n autocapitalize=\"off\"\n autocorrect=\"off\"\n autocomplete=\"off\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} 624327\"\n required\n />\n\n <p id=\"helpinput\" *ngIf=\"!hasError\" class=\"help-block\" translate>\n In case of key loss, please contact your platform administrator.\n </p>\n </c8y-form-group>\n </div>\n <div [ngClass]=\"isModal ? 'modal-footer separator-top bg-level-0 sticky-bottom' : 'text-center'\">\n <button\n class=\"btn btn-default\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"submit\"\n title=\"{{ 'Verify' | translate }}\"\n *ngIf=\"!loading\"\n [disabled]=\"!totpForm.form.valid\"\n >\n {{ 'Verify' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary btn-pending\"\n [ngClass]=\"{ 'btn-lg': !isModal }\"\n type=\"submit\"\n title=\"{{ 'Verifying\u2026' | translate }}\"\n *ngIf=\"loading\"\n >\n {{ 'Verifying\u2026' | translate }}\n </button>\n </div>\n</form>\n" }]
|
|
19392
|
+
}], ctorParameters: () => [{ type: i1.UserService }, { type: AlertService }], propDecorators: { verify: [{
|
|
19393
|
+
type: Input
|
|
19394
|
+
}], onSuccess: [{
|
|
19395
|
+
type: Output
|
|
19396
|
+
}], totpUnconfirmedEmitter: [{
|
|
19397
|
+
type: Output
|
|
19398
|
+
}], loading: [{
|
|
19399
|
+
type: Input
|
|
19400
|
+
}], hasError: [{
|
|
19401
|
+
type: Input
|
|
19402
|
+
}], isModal: [{
|
|
19403
|
+
type: Input
|
|
19404
|
+
}], modal: [{
|
|
19405
|
+
type: ViewChild,
|
|
19406
|
+
args: ['modal', { static: false }]
|
|
19407
|
+
}] } });
|
|
19408
|
+
|
|
19409
|
+
class UserTotpSetupComponent {
|
|
19410
|
+
constructor(user, modalService, modal, authService) {
|
|
19411
|
+
this.user = user;
|
|
19412
|
+
this.modalService = modalService;
|
|
19413
|
+
this.modal = modal;
|
|
19414
|
+
this.authService = authService;
|
|
19415
|
+
}
|
|
19416
|
+
async totpSetupVerified() {
|
|
19417
|
+
await this.user.activateTotp();
|
|
19418
|
+
this.modal.hide();
|
|
19317
19419
|
try {
|
|
19318
|
-
await this.
|
|
19319
|
-
|
|
19320
|
-
cancel: gettext$1('Reload later')
|
|
19321
|
-
});
|
|
19420
|
+
await this.modalService.acknowledge(gettext$1('Logout required'), gettext$1('You must log out in order to apply your changes'), Status.WARNING, gettext$1('Log out'));
|
|
19421
|
+
await this.authService.logout();
|
|
19322
19422
|
}
|
|
19323
19423
|
catch (ex) {
|
|
19324
|
-
|
|
19325
|
-
}
|
|
19326
|
-
finally {
|
|
19327
|
-
this.translate.saveInLocalStorage(lang);
|
|
19328
|
-
await this.userPreferences.set('language', lang);
|
|
19329
|
-
this.currentLang = lang;
|
|
19424
|
+
// intended empty
|
|
19330
19425
|
}
|
|
19331
|
-
return shouldReload;
|
|
19332
19426
|
}
|
|
19333
|
-
|
|
19334
|
-
|
|
19427
|
+
close() {
|
|
19428
|
+
this.modal.hide();
|
|
19429
|
+
}
|
|
19430
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserTotpSetupComponent, deps: [{ token: i1.UserService }, { token: ModalService }, { token: i1$7.BsModalRef }, { token: SimplifiedAuthService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19431
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: UserTotpSetupComponent, isStandalone: true, selector: "c8y-user-totp-setup", ngImport: i0, template: "<c8y-modal\n [title]=\"'Set up two-factor authentication' | translate\"\n [headerClasses]=\"'dialog-header'\"\n [customFooter]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span class=\"dlt-c8y-icon-phonelink-lock\"></span>\n </ng-container>\n\n <c8y-totp-setup></c8y-totp-setup>\n <c8y-totp-challenge [isModal]=\"true\" (onSuccess)=\"totpSetupVerified()\" (totpUnconfirmedEmitter)=\"close()\"></c8y-totp-challenge>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: TotpSetupComponent, selector: "c8y-totp-setup" }, { kind: "component", type: TotpChallengeComponent, selector: "c8y-totp-challenge", inputs: ["verify", "loading", "hasError", "isModal"], outputs: ["onSuccess", "totpUnconfirmedEmitter"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19335
19432
|
}
|
|
19336
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19433
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserTotpSetupComponent, decorators: [{
|
|
19337
19434
|
type: Component,
|
|
19338
|
-
args: [{ selector: 'c8y-
|
|
19339
|
-
|
|
19340
|
-
NgIf,
|
|
19341
|
-
C8yTranslateDirective,
|
|
19342
|
-
NgFor,
|
|
19343
|
-
FormsModule$1,
|
|
19344
|
-
C8yTranslatePipe,
|
|
19345
|
-
AsyncPipe
|
|
19346
|
-
], template: "<div class=\"separator-top p-t-8 p-b-8\">\n <div class=\"c8y-right-drawer__item sticky-top\">\n <i c8yIcon=\"eyedropper\"></i>\n <span class=\"text-bold\">{{ 'UI settings' | translate }}</span>\n </div>\n\n <div\n class=\"p-l-16 p-r-16 p-b-16\"\n *ngIf=\"themeSwitcher.darkThemeAvailable$ | async\"\n >\n <p translate>Theme</p>\n <div\n class=\"c8y-switch-multistate\"\n *ngIf=\"themeSwitcher.userSelectedThemePreference$ | async as themePreference\"\n >\n <ng-container *ngFor=\"let themeOption of themeSwitcher.themeOptions; index as i\">\n <input\n [attr.aria-label]=\"themeOption.label\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n name=\"theme-switcher\"\n type=\"radio\"\n [id]=\"'theme-option-' + i\"\n [disabled]=\"themeSwitcher.disableThemeSelection$ | async\"\n [checked]=\"themePreference === themeOption.value\"\n (click)=\"themeSwitcher.changeUserPreference(themeOption.value)\"\n />\n <label\n title=\"{{ themeOption.label | translate }}\"\n [for]=\"'theme-option-' + i\"\n >\n <i [c8yIcon]=\"themeOption.icon\"></i>\n </label>\n </ng-container>\n <div class=\"c8y-switch-multistate__handle\"></div>\n </div>\n </div>\n\n <div class=\"form-group p-l-16 p-r-16\">\n <label\n for=\"userLang\"\n translate\n >\n Language\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n #selectLang\n [ngModel]=\"currentLang\"\n (change)=\"onLanguageChange(selectLang.value)\"\n >\n <option\n *ngFor=\"let language of languages\"\n [value]=\"language.lang\"\n >\n {{ language.nativeLanguage }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n</div>\n" }]
|
|
19347
|
-
}], ctorParameters: () => [{ type: TranslateService }, { type: AppStateService }, { type: AppStateService }, { type: UserPreferencesService }, { type: ModalService }, { type: HeaderService }, { type: ThemeSwitcherService }] });
|
|
19435
|
+
args: [{ selector: 'c8y-user-totp-setup', standalone: true, imports: [ModalComponent, TotpSetupComponent, TotpChallengeComponent, C8yTranslatePipe], template: "<c8y-modal\n [title]=\"'Set up two-factor authentication' | translate\"\n [headerClasses]=\"'dialog-header'\"\n [customFooter]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span class=\"dlt-c8y-icon-phonelink-lock\"></span>\n </ng-container>\n\n <c8y-totp-setup></c8y-totp-setup>\n <c8y-totp-challenge [isModal]=\"true\" (onSuccess)=\"totpSetupVerified()\" (totpUnconfirmedEmitter)=\"close()\"></c8y-totp-challenge>\n</c8y-modal>\n" }]
|
|
19436
|
+
}], ctorParameters: () => [{ type: i1.UserService }, { type: ModalService }, { type: i1$7.BsModalRef }, { type: SimplifiedAuthService }] });
|
|
19348
19437
|
|
|
19349
|
-
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19353
|
-
|
|
19354
|
-
|
|
19355
|
-
|
|
19356
|
-
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19367
|
-
|
|
19368
|
-
|
|
19369
|
-
}]
|
|
19370
|
-
}] });
|
|
19371
|
-
|
|
19372
|
-
class UserMenuItemComponent {
|
|
19373
|
-
constructor(userService, headerService) {
|
|
19374
|
-
this.userService = userService;
|
|
19375
|
-
this.headerService = headerService;
|
|
19376
|
-
this.priority = 0;
|
|
19377
|
-
this.click = new EventEmitter();
|
|
19378
|
-
this.open$ = this.headerService.rightDrawerOpen$;
|
|
19438
|
+
/**
|
|
19439
|
+
* The component is used to display a password control that includes the functionality of hiding and showing the input value.
|
|
19440
|
+
*/
|
|
19441
|
+
class PasswordInputComponent {
|
|
19442
|
+
constructor() {
|
|
19443
|
+
/**
|
|
19444
|
+
* Id of input
|
|
19445
|
+
*/
|
|
19446
|
+
this.id = '';
|
|
19447
|
+
/**
|
|
19448
|
+
* Value for autocomplete attribute of input
|
|
19449
|
+
*/
|
|
19450
|
+
this.autocomplete = 'off';
|
|
19451
|
+
this.value = '';
|
|
19452
|
+
this.disabled = false;
|
|
19453
|
+
this.type = 'password';
|
|
19454
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
19455
|
+
this.onChange = value => { };
|
|
19456
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
19457
|
+
this.onTouched = () => { };
|
|
19379
19458
|
}
|
|
19380
|
-
|
|
19381
|
-
this.
|
|
19459
|
+
writeValue(value) {
|
|
19460
|
+
this.value = value ? value : '';
|
|
19382
19461
|
}
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
this.userService.remove(this);
|
|
19462
|
+
registerOnChange(fn) {
|
|
19463
|
+
this.onChange = fn;
|
|
19386
19464
|
}
|
|
19387
|
-
|
|
19388
|
-
this.
|
|
19465
|
+
registerOnTouched(fn) {
|
|
19466
|
+
this.onTouched = fn;
|
|
19389
19467
|
}
|
|
19390
|
-
|
|
19391
|
-
|
|
19468
|
+
setDisabledState(isDisabled) {
|
|
19469
|
+
this.disabled = isDisabled;
|
|
19470
|
+
}
|
|
19471
|
+
onInput($event) {
|
|
19472
|
+
this.value = $event.currentTarget.value;
|
|
19473
|
+
this.onChange(this.value);
|
|
19474
|
+
}
|
|
19475
|
+
onFocusOut() {
|
|
19476
|
+
this.onTouched();
|
|
19477
|
+
}
|
|
19478
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19479
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasswordInputComponent, isStandalone: true, selector: "c8y-password-input", inputs: { id: "id", autocomplete: "autocomplete" }, providers: [
|
|
19480
|
+
{
|
|
19481
|
+
provide: NG_VALUE_ACCESSOR,
|
|
19482
|
+
useExisting: forwardRef(() => PasswordInputComponent),
|
|
19483
|
+
multi: true
|
|
19484
|
+
}
|
|
19485
|
+
], ngImport: i0, template: "<div class=\"input-group input-group-lg input-group-password\">\n <input\n class=\"form-control input-lg\"\n [type]=\"type\"\n [value]=\"value\"\n [id]=\"id\"\n (input)=\"onInput($event)\"\n (focusout)=\"onFocusOut()\"\n [disabled]=\"disabled\"\n [autocomplete]=\"autocomplete\"\n />\n <span class=\"input-group-btn\">\n <button\n *ngIf=\"type === 'password'\"\n class=\"btn btn-clean\"\n title=\"{{ 'Show password' | translate }}\"\n type=\"button\"\n (click)=\"type = 'text'\"\n >\n <i class=\"dlt-c8y-icon-eye\"></i>\n </button>\n <button\n *ngIf=\"type === 'text'\"\n class=\"btn btn-clean\"\n title=\"{{ 'Hide password' | translate }}\"\n type=\"button\"\n (click)=\"type = 'password'\"\n >\n <i class=\"dlt-c8y-icon-eye-slash\"></i>\n </button>\n </span>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19392
19486
|
}
|
|
19393
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19487
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordInputComponent, decorators: [{
|
|
19394
19488
|
type: Component,
|
|
19395
|
-
args: [{ selector: 'c8y-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
type: Input
|
|
19404
|
-
}], priority: [{
|
|
19489
|
+
args: [{ selector: 'c8y-password-input', providers: [
|
|
19490
|
+
{
|
|
19491
|
+
provide: NG_VALUE_ACCESSOR,
|
|
19492
|
+
useExisting: forwardRef(() => PasswordInputComponent),
|
|
19493
|
+
multi: true
|
|
19494
|
+
}
|
|
19495
|
+
], standalone: true, imports: [NgIf, C8yTranslatePipe], template: "<div class=\"input-group input-group-lg input-group-password\">\n <input\n class=\"form-control input-lg\"\n [type]=\"type\"\n [value]=\"value\"\n [id]=\"id\"\n (input)=\"onInput($event)\"\n (focusout)=\"onFocusOut()\"\n [disabled]=\"disabled\"\n [autocomplete]=\"autocomplete\"\n />\n <span class=\"input-group-btn\">\n <button\n *ngIf=\"type === 'password'\"\n class=\"btn btn-clean\"\n title=\"{{ 'Show password' | translate }}\"\n type=\"button\"\n (click)=\"type = 'text'\"\n >\n <i class=\"dlt-c8y-icon-eye\"></i>\n </button>\n <button\n *ngIf=\"type === 'text'\"\n class=\"btn btn-clean\"\n title=\"{{ 'Hide password' | translate }}\"\n type=\"button\"\n (click)=\"type = 'password'\"\n >\n <i class=\"dlt-c8y-icon-eye-slash\"></i>\n </button>\n </span>\n</div>\n" }]
|
|
19496
|
+
}], propDecorators: { id: [{
|
|
19405
19497
|
type: Input
|
|
19406
|
-
}],
|
|
19498
|
+
}], autocomplete: [{
|
|
19407
19499
|
type: Input
|
|
19408
|
-
}], template: [{
|
|
19409
|
-
type: ViewChild,
|
|
19410
|
-
args: ['template', { static: false }]
|
|
19411
|
-
}], click: [{
|
|
19412
|
-
type: Output
|
|
19413
19500
|
}] } });
|
|
19414
19501
|
|
|
19415
|
-
class
|
|
19416
|
-
constructor(
|
|
19417
|
-
this.
|
|
19418
|
-
this.ui = ui;
|
|
19419
|
-
this.client = client;
|
|
19420
|
-
this.alert = alert;
|
|
19421
|
-
this.passwordConfirmedEmitter = new EventEmitter();
|
|
19422
|
-
this.loading = false;
|
|
19502
|
+
class PasswordConfirm {
|
|
19503
|
+
constructor(passwordConfirm) {
|
|
19504
|
+
this.passwordConfirm = passwordConfirm;
|
|
19423
19505
|
}
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
password: this.password,
|
|
19430
|
-
user: `${supportUserName ? `${supportUserName}$` : ''}${userId}`,
|
|
19431
|
-
tenant: this.client.tenant
|
|
19432
|
-
};
|
|
19433
|
-
try {
|
|
19434
|
-
this.loading = true;
|
|
19435
|
-
const auth = new BasicAuth(credentials);
|
|
19436
|
-
const newClient = this.createNewClient(auth, this.client.baseUrl);
|
|
19437
|
-
await newClient.user.current();
|
|
19438
|
-
this.emitSuccessAndClose();
|
|
19439
|
-
}
|
|
19440
|
-
catch (e) {
|
|
19441
|
-
if (e.res && e.res.status === 401 && e.data && /pin|totp/i.test(e.data.message)) {
|
|
19442
|
-
this.emitSuccessAndClose();
|
|
19443
|
-
}
|
|
19444
|
-
else {
|
|
19445
|
-
this.alert.danger(gettext$1("Provided password doesn't match your current one."));
|
|
19446
|
-
}
|
|
19447
|
-
}
|
|
19448
|
-
finally {
|
|
19449
|
-
this.loading = false;
|
|
19450
|
-
}
|
|
19506
|
+
validate(abControl) {
|
|
19507
|
+
const value = abControl.value;
|
|
19508
|
+
const controlToCompareWith = abControl.root.get(this.passwordConfirm);
|
|
19509
|
+
if (controlToCompareWith && value !== controlToCompareWith.value) {
|
|
19510
|
+
return { passwordConfirm: true };
|
|
19451
19511
|
}
|
|
19512
|
+
return null;
|
|
19452
19513
|
}
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
this.passwordConfirmedEmitter.emit(true);
|
|
19462
|
-
this.modal._dismiss();
|
|
19463
|
-
}
|
|
19464
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirmModalComponent, deps: [{ token: i1.UserService }, { token: AppStateService }, { token: i1.FetchClient }, { token: AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19465
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasswordConfirmModalComponent, isStandalone: true, selector: "c8y-password-confirm-modal", outputs: { passwordConfirmedEmitter: "passwordConfirmedEmitter" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<c8y-modal [customFooter]=\"true\" [title]=\"'Confirm your current password' | translate\" #modal>\r\n <form #confirmForm=\"ngForm\" (ngSubmit)=\"confirmForm.form.valid && passwordConfirm()\">\r\n <div class=\"d-block p-24 p-b-0\">\r\n <c8y-form-group [hasWarning]=\"true\">\r\n <label translate for=\"currentPassword\">Enter your password</label>\r\n <input\r\n id=\"currentPassword\"\r\n [(ngModel)]=\"password\"\r\n type=\"password\"\r\n name=\"password\"\r\n class=\"form-control\"\r\n placeholder=\"{{ 'Enter your password' | translate }}\"\r\n required\r\n />\r\n <c8y-messages>\r\n <c8y-message translate>\r\n Enter the password of the user that you are currently logged in with.\r\n </c8y-message>\r\n </c8y-messages>\r\n </c8y-form-group>\r\n </div>\r\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\r\n <button\r\n title=\"{{ 'Cancel' | translate }}\"\r\n class=\"btn btn-default\"\r\n type=\"button\"\r\n (click)=\"cancel()\"\r\n >\r\n {{ 'Cancel' | translate }}\r\n </button>\r\n <button\r\n title=\"{{ 'Confirm' | translate }}\"\r\n class=\"btn btn-primary\"\r\n type=\"submit\"\r\n [disabled]=\"!confirmForm.form.valid || loading\"\r\n >\r\n {{ 'Confirm' | translate }}\r\n </button>\r\n </div>\r\n </form>\r\n</c8y-modal>\r\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19514
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirm, deps: [{ token: 'passwordConfirm', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
19515
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PasswordConfirm, isStandalone: true, selector: "[passwordConfirm]", providers: [
|
|
19516
|
+
{
|
|
19517
|
+
provide: NG_VALIDATORS,
|
|
19518
|
+
useExisting: PasswordConfirm,
|
|
19519
|
+
multi: true
|
|
19520
|
+
}
|
|
19521
|
+
], ngImport: i0 }); }
|
|
19466
19522
|
}
|
|
19467
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19468
|
-
type:
|
|
19469
|
-
args: [{
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
}] } });
|
|
19523
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirm, decorators: [{
|
|
19524
|
+
type: Directive,
|
|
19525
|
+
args: [{
|
|
19526
|
+
selector: '[passwordConfirm]',
|
|
19527
|
+
providers: [
|
|
19528
|
+
{
|
|
19529
|
+
provide: NG_VALIDATORS,
|
|
19530
|
+
useExisting: PasswordConfirm,
|
|
19531
|
+
multi: true
|
|
19532
|
+
}
|
|
19533
|
+
],
|
|
19534
|
+
standalone: true
|
|
19535
|
+
}]
|
|
19536
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
19537
|
+
type: Attribute,
|
|
19538
|
+
args: ['passwordConfirm']
|
|
19539
|
+
}] }] });
|
|
19485
19540
|
|
|
19486
|
-
class
|
|
19487
|
-
constructor(
|
|
19488
|
-
this.
|
|
19489
|
-
this.
|
|
19541
|
+
class PasswordStrengthService {
|
|
19542
|
+
constructor(ui) {
|
|
19543
|
+
this.ui = ui;
|
|
19544
|
+
this.GREEN_MIN_LENGTH_DEFAULT = 8;
|
|
19545
|
+
this.passwordStrengthSetting = {
|
|
19546
|
+
enforcePasswordStrength: false,
|
|
19547
|
+
greenMinLength: this.GREEN_MIN_LENGTH_DEFAULT,
|
|
19548
|
+
passwordStrengthValidity: false
|
|
19549
|
+
};
|
|
19490
19550
|
}
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19551
|
+
/**
|
|
19552
|
+
* Gets the minimal number of characters that a password should have to be considered a “green” strong one.
|
|
19553
|
+
* @return The min length for password or default value.
|
|
19554
|
+
*/
|
|
19555
|
+
async getGreenMinLength() {
|
|
19556
|
+
const { greenMinLength } = (await this.getBasicAuthLoginOption()) || { greenMinLength: null };
|
|
19557
|
+
this.passwordStrengthSetting.greenMinLength = greenMinLength || this.GREEN_MIN_LENGTH_DEFAULT;
|
|
19558
|
+
return this.passwordStrengthSetting.greenMinLength;
|
|
19494
19559
|
}
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
}], ctorParameters: () => [{ type: i1$7.BsModalRef }], propDecorators: { currentPasswordEmitter: [{
|
|
19515
|
-
type: Output
|
|
19516
|
-
}] } });
|
|
19517
|
-
|
|
19518
|
-
class PasswordService {
|
|
19519
|
-
constructor(modalService) {
|
|
19520
|
-
this.modalService = modalService;
|
|
19521
|
-
this.DEFAULT_PASSWORD_MIN_LENGTH = 8;
|
|
19522
|
-
this.GREEN = {
|
|
19523
|
-
colorName: 'green',
|
|
19524
|
-
color: 'rgb(0, 128, 0)',
|
|
19525
|
-
description: gettext$1('strong'),
|
|
19526
|
-
passwordStrength: PasswordStrength.GREEN
|
|
19527
|
-
};
|
|
19528
|
-
this.YELLOW = {
|
|
19529
|
-
colorName: 'yellow',
|
|
19530
|
-
color: 'rgb(255, 204, 51)',
|
|
19531
|
-
description: gettext$1('medium'),
|
|
19532
|
-
passwordStrength: PasswordStrength.YELLOW
|
|
19533
|
-
};
|
|
19534
|
-
this.RED = {
|
|
19535
|
-
colorName: 'red',
|
|
19536
|
-
color: 'rgb(170, 0, 51)',
|
|
19537
|
-
description: gettext$1('weak'),
|
|
19538
|
-
passwordStrength: PasswordStrength.RED
|
|
19539
|
-
};
|
|
19540
|
-
}
|
|
19541
|
-
confirmPassword() {
|
|
19542
|
-
const modalInstance = this.modalService.show(PasswordConfirmModalComponent, {
|
|
19543
|
-
ariaDescribedby: 'modal-body',
|
|
19544
|
-
ariaLabelledBy: 'modal-title'
|
|
19545
|
-
});
|
|
19546
|
-
const passwordConfirmedEmitter = modalInstance.content.passwordConfirmedEmitter;
|
|
19547
|
-
return passwordConfirmedEmitter.pipe(take(1));
|
|
19560
|
+
/**
|
|
19561
|
+
* Checks if password strength is enforced for system
|
|
19562
|
+
* by retrieving value of `enforceStrength` property from loginOptions response
|
|
19563
|
+
* @param refresh boolean used to refresh the app state where result of loginOptions response is stored.
|
|
19564
|
+
* If false, it takes value from memory,
|
|
19565
|
+
* if true, it refresh the app state value and then retrives data.
|
|
19566
|
+
* @return boolean value, true if enforced, false otherwise.
|
|
19567
|
+
*/
|
|
19568
|
+
async getEnforcePasswordStrength(refresh) {
|
|
19569
|
+
const loginOption = await this.getBasicAuthLoginOption(refresh);
|
|
19570
|
+
const enforcePasswordStrength = loginOption?.enforceStrength;
|
|
19571
|
+
if (typeof enforcePasswordStrength === 'string') {
|
|
19572
|
+
this.passwordStrengthSetting.enforcePasswordStrength =
|
|
19573
|
+
enforcePasswordStrength === 'true' ? true : false;
|
|
19574
|
+
}
|
|
19575
|
+
else {
|
|
19576
|
+
this.passwordStrengthSetting.enforcePasswordStrength = !!enforcePasswordStrength;
|
|
19577
|
+
}
|
|
19578
|
+
return this.passwordStrengthSetting.enforcePasswordStrength;
|
|
19548
19579
|
}
|
|
19549
19580
|
/**
|
|
19550
|
-
*
|
|
19551
|
-
*
|
|
19552
|
-
*
|
|
19581
|
+
* Checks if password strength is enforced for particular tenant
|
|
19582
|
+
* by retrieving value of `strengthValidity` property from loginOptions response
|
|
19583
|
+
* @param refresh boolean used to refresh the app state where result of loginOptions response is stored.
|
|
19584
|
+
* If false, it takes value from memory,
|
|
19585
|
+
* if true, it refresh the app state value and then retrives data.
|
|
19586
|
+
* @return boolean value, true if enforced, false otherwise.
|
|
19553
19587
|
*/
|
|
19554
|
-
|
|
19555
|
-
const
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
|
|
19559
|
-
|
|
19560
|
-
const currentPasswordEmitter = modalInstance.content.currentPasswordEmitter;
|
|
19561
|
-
return currentPasswordEmitter.pipe(take(1));
|
|
19562
|
-
}
|
|
19563
|
-
hasLowerCase(password) {
|
|
19564
|
-
return password.search(/[a-z]/) !== -1;
|
|
19565
|
-
}
|
|
19566
|
-
hasUpperCase(password) {
|
|
19567
|
-
return password.search(/[A-Z]/) !== -1;
|
|
19568
|
-
}
|
|
19569
|
-
hasNumbers(password) {
|
|
19570
|
-
return password.search(/[0-9]/) !== -1;
|
|
19571
|
-
}
|
|
19572
|
-
hasSpecialChars(password) {
|
|
19573
|
-
return password.search(/[^0-9a-zA-Z]+/) !== -1;
|
|
19574
|
-
}
|
|
19575
|
-
getStrengthColor(password) {
|
|
19576
|
-
const passwordStrength = filter$2([
|
|
19577
|
-
this.hasLowerCase(password),
|
|
19578
|
-
this.hasUpperCase(password),
|
|
19579
|
-
this.hasNumbers(password),
|
|
19580
|
-
this.hasSpecialChars(password)
|
|
19581
|
-
]).length;
|
|
19582
|
-
if (passwordStrength > 3) {
|
|
19583
|
-
return this.GREEN;
|
|
19584
|
-
}
|
|
19585
|
-
else if (passwordStrength >= 3) {
|
|
19586
|
-
return this.YELLOW;
|
|
19588
|
+
async getPasswordStrengthValidity(refresh = false) {
|
|
19589
|
+
const loginOption = await this.getBasicAuthLoginOption(refresh);
|
|
19590
|
+
const strengthValidity = loginOption?.strengthValidity;
|
|
19591
|
+
if (typeof strengthValidity === 'string') {
|
|
19592
|
+
this.passwordStrengthSetting.passwordStrengthValidity =
|
|
19593
|
+
strengthValidity === 'true' ? true : false;
|
|
19587
19594
|
}
|
|
19588
19595
|
else {
|
|
19589
|
-
|
|
19596
|
+
this.passwordStrengthSetting.passwordStrengthValidity = !!strengthValidity;
|
|
19590
19597
|
}
|
|
19598
|
+
return this.passwordStrengthSetting.passwordStrengthValidity;
|
|
19591
19599
|
}
|
|
19592
|
-
|
|
19593
|
-
|
|
19600
|
+
/**
|
|
19601
|
+
* Function determines if enforced strength checks should be enabled for current tenant
|
|
19602
|
+
* based on properties retrieved from loginOptions
|
|
19603
|
+
* @param options object containing specific options:
|
|
19604
|
+
* - {refresh: true} - refreshes values of app state and returns fresh values as result of call
|
|
19605
|
+
* @return boolean value, true if strength is enforced for tenant, false otherwise.
|
|
19606
|
+
*/
|
|
19607
|
+
async getPasswordStrengthEnforced(options) {
|
|
19608
|
+
const refresh = options && options.refresh;
|
|
19609
|
+
return Promise.all([
|
|
19610
|
+
this.getEnforcePasswordStrength(refresh),
|
|
19611
|
+
this.getPasswordStrengthValidity(refresh)
|
|
19612
|
+
]).then(values => {
|
|
19613
|
+
const [enforcePasswordStrength, passwordStrengthValidity] = values;
|
|
19614
|
+
return enforcePasswordStrength || passwordStrengthValidity;
|
|
19615
|
+
});
|
|
19594
19616
|
}
|
|
19595
|
-
|
|
19596
|
-
|
|
19617
|
+
async getBasicAuthLoginOption(refresh) {
|
|
19618
|
+
if (refresh) {
|
|
19619
|
+
await this.ui.refreshLoginOptions();
|
|
19620
|
+
}
|
|
19621
|
+
const loginOptions = this.ui.state.loginOptions || [];
|
|
19622
|
+
const basicAuthLoginOption = loginOptions.find(({ type }) => type === 'BASIC');
|
|
19623
|
+
return basicAuthLoginOption;
|
|
19624
|
+
}
|
|
19625
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordStrengthService, deps: [{ token: AppStateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
19626
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordStrengthService, providedIn: 'root' }); }
|
|
19597
19627
|
}
|
|
19598
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19628
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordStrengthService, decorators: [{
|
|
19599
19629
|
type: Injectable,
|
|
19600
|
-
args: [{
|
|
19601
|
-
|
|
19630
|
+
args: [{
|
|
19631
|
+
providedIn: 'root'
|
|
19632
|
+
}]
|
|
19633
|
+
}], ctorParameters: () => [{ type: AppStateService }] });
|
|
19602
19634
|
|
|
19603
|
-
class
|
|
19604
|
-
|
|
19605
|
-
|
|
19606
|
-
|
|
19607
|
-
get secret() {
|
|
19608
|
-
return this.totpSecret ? this.totpSecret.rawSecret : '';
|
|
19609
|
-
}
|
|
19610
|
-
get qrData() {
|
|
19611
|
-
// TODO: waiting for BE:, now we need extract it from secretQrUrl
|
|
19612
|
-
// https://cumulocity.atlassian.net/browse/MTM-36387
|
|
19613
|
-
// return this.totpSecret ? this.totpSecret.qrData : '';
|
|
19614
|
-
const url = new URL(this.qrCodeImage);
|
|
19615
|
-
const otpAuth = url.searchParams.get('chl') || url.searchParams.get('data');
|
|
19616
|
-
if (!otpAuth) {
|
|
19617
|
-
this.alert.danger(gettext$1('Failed to generate a QR code.'));
|
|
19618
|
-
return '';
|
|
19635
|
+
class PasswordCheckListComponent {
|
|
19636
|
+
set password(password) {
|
|
19637
|
+
if (typeof password === 'string') {
|
|
19638
|
+
this.onPasswordChange(password);
|
|
19619
19639
|
}
|
|
19620
|
-
return decodeURIComponent(otpAuth);
|
|
19621
19640
|
}
|
|
19622
|
-
constructor(
|
|
19623
|
-
this.
|
|
19624
|
-
this.
|
|
19641
|
+
constructor(passwordStrength, passwordStrengthChecker, passwordService) {
|
|
19642
|
+
this.passwordStrength = passwordStrength;
|
|
19643
|
+
this.passwordStrengthChecker = passwordStrengthChecker;
|
|
19644
|
+
this.passwordService = passwordService;
|
|
19645
|
+
this.strengthEnforced = false;
|
|
19646
|
+
this.onRequirementsFulfilled = new EventEmitter();
|
|
19647
|
+
this.minGreenLength = 8;
|
|
19648
|
+
this.enhancedStrengthCheckList = [
|
|
19649
|
+
{
|
|
19650
|
+
label: gettext$1('Include lowercase characters (for example, abcdef)'),
|
|
19651
|
+
check: this.passwordStrengthChecker.hasLowerCase,
|
|
19652
|
+
icon: '',
|
|
19653
|
+
contextualColor: '',
|
|
19654
|
+
textColor: ''
|
|
19655
|
+
},
|
|
19656
|
+
{
|
|
19657
|
+
label: gettext$1('Include uppercase characters (for example, ABCDEF)'),
|
|
19658
|
+
check: this.passwordStrengthChecker.hasUpperCase,
|
|
19659
|
+
icon: '',
|
|
19660
|
+
contextualColor: '',
|
|
19661
|
+
textColor: ''
|
|
19662
|
+
},
|
|
19663
|
+
{
|
|
19664
|
+
label: gettext$1('Include numbers (for example, 123456)'),
|
|
19665
|
+
check: this.passwordStrengthChecker.hasNumbers,
|
|
19666
|
+
icon: '',
|
|
19667
|
+
contextualColor: '',
|
|
19668
|
+
textColor: ''
|
|
19669
|
+
},
|
|
19670
|
+
{
|
|
19671
|
+
label: gettext$1('Include symbols (for example, !@#$%^)'),
|
|
19672
|
+
check: this.passwordStrengthChecker.hasSpecialChars,
|
|
19673
|
+
icon: '',
|
|
19674
|
+
contextualColor: '',
|
|
19675
|
+
textColor: ''
|
|
19676
|
+
}
|
|
19677
|
+
];
|
|
19678
|
+
this.basicChecklist = [
|
|
19679
|
+
{
|
|
19680
|
+
label: gettext$1('Must have at least {{length}} characters'),
|
|
19681
|
+
check: password => password.length >= this.minGreenLength,
|
|
19682
|
+
icon: '',
|
|
19683
|
+
contextualColor: '',
|
|
19684
|
+
textColor: ''
|
|
19685
|
+
}
|
|
19686
|
+
];
|
|
19687
|
+
this.combinedChecklist = [];
|
|
19625
19688
|
}
|
|
19626
19689
|
async ngOnInit() {
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
this.
|
|
19630
|
-
}
|
|
19631
|
-
catch (ex) {
|
|
19632
|
-
this.alert.addServerFailure(ex);
|
|
19690
|
+
this.minGreenLength = await this.passwordStrength.getGreenMinLength();
|
|
19691
|
+
if (!this.minGreenLength) {
|
|
19692
|
+
this.minGreenLength = this.passwordService.getDefaultPasswordMinLength();
|
|
19633
19693
|
}
|
|
19694
|
+
this.onPasswordChange('');
|
|
19634
19695
|
}
|
|
19635
|
-
|
|
19636
|
-
|
|
19696
|
+
get translateParams() {
|
|
19697
|
+
return {
|
|
19698
|
+
length: this.minGreenLength
|
|
19699
|
+
};
|
|
19700
|
+
}
|
|
19701
|
+
checkRequirement(requirement, password) {
|
|
19702
|
+
const checked = requirement.check(password);
|
|
19703
|
+
assign(requirement, {
|
|
19704
|
+
icon: checked ? 'check-circle' : 'radio-button-unchecked',
|
|
19705
|
+
contextualColor: checked ? 'text-success' : 'text-muted',
|
|
19706
|
+
textColor: checked ? '' : 'text-muted'
|
|
19707
|
+
});
|
|
19708
|
+
return requirement;
|
|
19709
|
+
}
|
|
19710
|
+
onPasswordChange(password) {
|
|
19711
|
+
this.basicChecklist.forEach(requirement => {
|
|
19712
|
+
this.checkRequirement(requirement, password);
|
|
19713
|
+
});
|
|
19714
|
+
this.enhancedStrengthCheckList.forEach(requirement => {
|
|
19715
|
+
this.checkRequirement(requirement, password);
|
|
19716
|
+
});
|
|
19717
|
+
this.combinedChecklist = [...this.basicChecklist, ...this.enhancedStrengthCheckList];
|
|
19718
|
+
this.onRequirementsFulfilled.emit(this.isPasswordValid());
|
|
19719
|
+
}
|
|
19720
|
+
isPasswordValid() {
|
|
19721
|
+
const checklist = this.strengthEnforced ? this.combinedChecklist : this.basicChecklist;
|
|
19722
|
+
return checklist.every(requirement => requirement.icon !== 'radio-button-unchecked');
|
|
19723
|
+
}
|
|
19724
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordCheckListComponent, deps: [{ token: PasswordStrengthService }, { token: PasswordService }, { token: PasswordService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19725
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasswordCheckListComponent, isStandalone: true, selector: "c8y-password-check-list", inputs: { strengthEnforced: "strengthEnforced", password: "password" }, outputs: { onRequirementsFulfilled: "onRequirementsFulfilled" }, ngImport: i0, template: "<div *ngIf=\"strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of combinedChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n\n<div *ngIf=\"!strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of basicChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n\n <div class=\"m-b-8\">\n {{ 'We recommend you to meet these conditions for a stronger password:' | translate }}\n </div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of enhancedStrengthCheckList\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19637
19726
|
}
|
|
19638
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19727
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordCheckListComponent, decorators: [{
|
|
19639
19728
|
type: Component,
|
|
19640
|
-
args: [{ selector: 'c8y-
|
|
19641
|
-
}], ctorParameters: () => [{ type:
|
|
19729
|
+
args: [{ selector: 'c8y-password-check-list', standalone: true, imports: [NgIf, NgFor, IconDirective, C8yTranslateDirective, C8yTranslatePipe], template: "<div *ngIf=\"strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of combinedChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n\n<div *ngIf=\"!strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of basicChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n\n <div class=\"m-b-8\">\n {{ 'We recommend you to meet these conditions for a stronger password:' | translate }}\n </div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of enhancedStrengthCheckList\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n" }]
|
|
19730
|
+
}], ctorParameters: () => [{ type: PasswordStrengthService }, { type: PasswordService }, { type: PasswordService }], propDecorators: { strengthEnforced: [{
|
|
19731
|
+
type: Input
|
|
19732
|
+
}], password: [{
|
|
19733
|
+
type: Input,
|
|
19734
|
+
args: ['password']
|
|
19735
|
+
}], onRequirementsFulfilled: [{
|
|
19736
|
+
type: Output
|
|
19737
|
+
}] } });
|
|
19642
19738
|
|
|
19643
|
-
class
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
*/
|
|
19650
|
-
this.verify = true;
|
|
19651
|
-
/**
|
|
19652
|
-
* Emits the token on success.
|
|
19653
|
-
*/
|
|
19654
|
-
this.onSuccess = new EventEmitter();
|
|
19655
|
-
/**
|
|
19656
|
-
* Emits if set up two-factor authentication is canceled.
|
|
19657
|
-
*/
|
|
19658
|
-
this.totpUnconfirmedEmitter = new EventEmitter();
|
|
19659
|
-
this.loading = false;
|
|
19660
|
-
this.hasError = false;
|
|
19661
|
-
this.isModal = false;
|
|
19662
|
-
this.model = {
|
|
19663
|
-
token: ''
|
|
19664
|
-
};
|
|
19739
|
+
class NewPasswordComponent {
|
|
19740
|
+
set _newPasswordModel(ngModel) {
|
|
19741
|
+
if (ngModel) {
|
|
19742
|
+
this.newPasswordModel = ngModel;
|
|
19743
|
+
ngModel.control.addValidators(this.passwordChecklistValidator);
|
|
19744
|
+
}
|
|
19665
19745
|
}
|
|
19666
|
-
|
|
19667
|
-
|
|
19668
|
-
|
|
19669
|
-
|
|
19670
|
-
|
|
19671
|
-
|
|
19672
|
-
|
|
19673
|
-
|
|
19746
|
+
constructor(passwordStrength, cdRef, elementRef) {
|
|
19747
|
+
this.passwordStrength = passwordStrength;
|
|
19748
|
+
this.cdRef = cdRef;
|
|
19749
|
+
this.elementRef = elementRef;
|
|
19750
|
+
this.password = new EventEmitter();
|
|
19751
|
+
this.showChangePasswordButton = true;
|
|
19752
|
+
this.model = {};
|
|
19753
|
+
this.changePassword = false;
|
|
19754
|
+
this.passwordEnforced = false;
|
|
19755
|
+
this.passwordChecklistValidator = () => this.requirementsFulfilled ? null : { passwordStrengthChecklist: true };
|
|
19756
|
+
}
|
|
19757
|
+
ngOnInit() {
|
|
19758
|
+
this.loadPasswordStrengthSettings();
|
|
19759
|
+
this.passwordStrength.getGreenMinLength().then(value => {
|
|
19760
|
+
this.minlength = value;
|
|
19761
|
+
});
|
|
19762
|
+
}
|
|
19763
|
+
async ngOnChanges(changes) {
|
|
19764
|
+
if (changes.showChangePasswordButton) {
|
|
19765
|
+
this.changePassword = !this.showChangePasswordButton;
|
|
19674
19766
|
}
|
|
19675
|
-
|
|
19676
|
-
this.
|
|
19677
|
-
this.alert.removeLastDanger();
|
|
19678
|
-
this.loading = false;
|
|
19767
|
+
if (changes.requireStrongPassword?.previousValue !== changes.requireStrongPassword?.currentValue) {
|
|
19768
|
+
await this.loadPasswordStrengthSettings();
|
|
19679
19769
|
}
|
|
19680
19770
|
}
|
|
19681
|
-
|
|
19682
|
-
this.
|
|
19771
|
+
newPasswordChanged() {
|
|
19772
|
+
this.password.emit({
|
|
19773
|
+
password: this.model.newPassword,
|
|
19774
|
+
passwordStrength: this.model.strength
|
|
19775
|
+
});
|
|
19683
19776
|
}
|
|
19684
|
-
|
|
19685
|
-
|
|
19777
|
+
updateValidity(requirementsFulfilled) {
|
|
19778
|
+
this.requirementsFulfilled = requirementsFulfilled;
|
|
19779
|
+
this.cdRef.detectChanges();
|
|
19780
|
+
this.newPasswordModel.control.updateValueAndValidity();
|
|
19781
|
+
// There are two validators checking password validity, but we only want to show one of these errors at a time,
|
|
19782
|
+
// where checklist validator takes priority.
|
|
19783
|
+
if (!this.requirementsFulfilled) {
|
|
19784
|
+
delete this.newPasswordModel.control.errors['password'];
|
|
19785
|
+
}
|
|
19786
|
+
}
|
|
19787
|
+
async loadPasswordStrengthSettings() {
|
|
19788
|
+
if (this.requireStrongPassword) {
|
|
19789
|
+
this.passwordEnforced = this.requireStrongPassword;
|
|
19790
|
+
}
|
|
19791
|
+
else {
|
|
19792
|
+
const passwordStrengthSettings = await this.passwordStrength.getPasswordStrengthEnforced({
|
|
19793
|
+
refresh: true
|
|
19794
|
+
});
|
|
19795
|
+
this.passwordEnforced = passwordStrengthSettings;
|
|
19796
|
+
}
|
|
19797
|
+
}
|
|
19798
|
+
toggleChangePassword() {
|
|
19799
|
+
this.changePassword = !this.changePassword;
|
|
19800
|
+
if (!this.changePassword) {
|
|
19801
|
+
this.password.emit({});
|
|
19802
|
+
this.model = {};
|
|
19803
|
+
}
|
|
19804
|
+
}
|
|
19805
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewPasswordComponent, deps: [{ token: PasswordStrengthService }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19806
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: NewPasswordComponent, isStandalone: true, selector: "c8y-new-password", inputs: { showChangePasswordButton: "showChangePasswordButton", requireStrongPassword: "requireStrongPassword" }, outputs: { password: "password" }, viewQueries: [{ propertyName: "_newPasswordModel", first: true, predicate: ["newPassword"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"showChangePasswordButton\" class=\"form-group\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"toggleChangePassword()\"\n data-cy=\"c8y-new-password--change-button\"\n >\n <ng-container *ngIf=\"!changePassword\">\n {{ 'Change password' | translate }}\n </ng-container>\n <ng-container *ngIf=\"changePassword\">\n {{ 'Cancel password change' | translate }}\n </ng-container>\n </button>\n</div>\n\n<div\n class=\"row content-flex-50\"\n *ngIf=\"changePassword\"\n>\n <div class=\"col-6\">\n <c8y-form-group>\n <label\n for=\"newPassword\"\n translate\n >\n Password\n </label>\n <c8y-password-input\n name=\"newPassword\"\n required\n [id]=\"'newPassword'\"\n #newPassword=\"ngModel\"\n [(ngModel)]=\"model.newPassword\"\n (change)=\"newPasswordChanged()\"\n (input)=\"newPasswordConfirm.control.updateValueAndValidity()\"\n c8yDefaultValidation=\"password\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"newConfirmPassword\"\n translate\n >\n Confirm password\n </label>\n <c8y-password-input\n name=\"newPasswordConfirm\"\n required\n [id]=\"'newConfirmPassword'\"\n #newPasswordConfirm=\"ngModel\"\n [(ngModel)]=\"model.newPasswordConfirm\"\n passwordConfirm=\"newPassword\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-password-check-list\n [password]=\"model.newPassword\"\n [strengthEnforced]=\"passwordEnforced\"\n (onRequirementsFulfilled)=\"updateValidity($event)\"\n ></c8y-password-check-list>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: PasswordInputComponent, selector: "c8y-password-input", inputs: ["id", "autocomplete"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: PasswordConfirm, selector: "[passwordConfirm]" }, { kind: "component", type: PasswordCheckListComponent, selector: "c8y-password-check-list", inputs: ["strengthEnforced", "password"], outputs: ["onRequirementsFulfilled"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
|
|
19686
19807
|
}
|
|
19687
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewPasswordComponent, decorators: [{
|
|
19688
19809
|
type: Component,
|
|
19689
|
-
args: [{ selector: 'c8y-
|
|
19690
|
-
|
|
19691
|
-
NgClass,
|
|
19810
|
+
args: [{ selector: 'c8y-new-password', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [
|
|
19811
|
+
NgIf,
|
|
19692
19812
|
FormGroupComponent,
|
|
19693
19813
|
C8yTranslateDirective,
|
|
19694
|
-
|
|
19695
|
-
|
|
19814
|
+
PasswordInputComponent,
|
|
19815
|
+
FormsModule$1,
|
|
19816
|
+
DefaultValidationDirective,
|
|
19817
|
+
PasswordConfirm,
|
|
19818
|
+
PasswordCheckListComponent,
|
|
19696
19819
|
C8yTranslatePipe
|
|
19697
|
-
], template: "<
|
|
19698
|
-
}], ctorParameters: () => [{ type:
|
|
19699
|
-
type: Input
|
|
19700
|
-
}], onSuccess: [{
|
|
19701
|
-
type: Output
|
|
19702
|
-
}], totpUnconfirmedEmitter: [{
|
|
19820
|
+
], template: "<div *ngIf=\"showChangePasswordButton\" class=\"form-group\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"toggleChangePassword()\"\n data-cy=\"c8y-new-password--change-button\"\n >\n <ng-container *ngIf=\"!changePassword\">\n {{ 'Change password' | translate }}\n </ng-container>\n <ng-container *ngIf=\"changePassword\">\n {{ 'Cancel password change' | translate }}\n </ng-container>\n </button>\n</div>\n\n<div\n class=\"row content-flex-50\"\n *ngIf=\"changePassword\"\n>\n <div class=\"col-6\">\n <c8y-form-group>\n <label\n for=\"newPassword\"\n translate\n >\n Password\n </label>\n <c8y-password-input\n name=\"newPassword\"\n required\n [id]=\"'newPassword'\"\n #newPassword=\"ngModel\"\n [(ngModel)]=\"model.newPassword\"\n (change)=\"newPasswordChanged()\"\n (input)=\"newPasswordConfirm.control.updateValueAndValidity()\"\n c8yDefaultValidation=\"password\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"newConfirmPassword\"\n translate\n >\n Confirm password\n </label>\n <c8y-password-input\n name=\"newPasswordConfirm\"\n required\n [id]=\"'newConfirmPassword'\"\n #newPasswordConfirm=\"ngModel\"\n [(ngModel)]=\"model.newPasswordConfirm\"\n passwordConfirm=\"newPassword\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-password-check-list\n [password]=\"model.newPassword\"\n [strengthEnforced]=\"passwordEnforced\"\n (onRequirementsFulfilled)=\"updateValidity($event)\"\n ></c8y-password-check-list>\n </div>\n</div>\n" }]
|
|
19821
|
+
}], ctorParameters: () => [{ type: PasswordStrengthService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { password: [{
|
|
19703
19822
|
type: Output
|
|
19704
|
-
}],
|
|
19705
|
-
type: Input
|
|
19706
|
-
}], hasError: [{
|
|
19823
|
+
}], showChangePasswordButton: [{
|
|
19707
19824
|
type: Input
|
|
19708
|
-
}],
|
|
19825
|
+
}], requireStrongPassword: [{
|
|
19709
19826
|
type: Input
|
|
19710
|
-
}],
|
|
19827
|
+
}], _newPasswordModel: [{
|
|
19711
19828
|
type: ViewChild,
|
|
19712
|
-
args: ['
|
|
19829
|
+
args: ['newPassword']
|
|
19713
19830
|
}] } });
|
|
19714
19831
|
|
|
19715
|
-
class
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
}
|
|
19722
|
-
async totpSetupVerified() {
|
|
19723
|
-
await this.user.activateTotp();
|
|
19724
|
-
this.modal.hide();
|
|
19725
|
-
try {
|
|
19726
|
-
await this.modalService.acknowledge(gettext$1('Logout required'), gettext$1('You must log out in order to apply your changes'), Status.WARNING, gettext$1('Log out'));
|
|
19727
|
-
await this.authService.logout();
|
|
19728
|
-
}
|
|
19729
|
-
catch (ex) {
|
|
19730
|
-
// intended empty
|
|
19832
|
+
class UserEditComponent {
|
|
19833
|
+
set user(u) {
|
|
19834
|
+
if (u) {
|
|
19835
|
+
this._user = clone(u);
|
|
19836
|
+
this.userIsExternal = u.customProperties.userOrigin === 'OAUTH2';
|
|
19837
|
+
this.isPhoneRequired = this.isPhoneRequired && u.twoFactorAuthenticationEnabled;
|
|
19731
19838
|
}
|
|
19732
19839
|
}
|
|
19733
|
-
|
|
19734
|
-
this.
|
|
19735
|
-
}
|
|
19736
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserTotpSetupComponent, deps: [{ token: i1.UserService }, { token: ModalService }, { token: i1$7.BsModalRef }, { token: SimplifiedAuthService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19737
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: UserTotpSetupComponent, isStandalone: true, selector: "c8y-user-totp-setup", ngImport: i0, template: "<c8y-modal\n [title]=\"'Set up two-factor authentication' | translate\"\n [headerClasses]=\"'dialog-header'\"\n [customFooter]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span class=\"dlt-c8y-icon-phonelink-lock\"></span>\n </ng-container>\n\n <c8y-totp-setup></c8y-totp-setup>\n <c8y-totp-challenge [isModal]=\"true\" (onSuccess)=\"totpSetupVerified()\" (totpUnconfirmedEmitter)=\"close()\"></c8y-totp-challenge>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: TotpSetupComponent, selector: "c8y-totp-setup" }, { kind: "component", type: TotpChallengeComponent, selector: "c8y-totp-challenge", inputs: ["verify", "loading", "hasError", "isModal"], outputs: ["onSuccess", "totpUnconfirmedEmitter"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
19738
|
-
}
|
|
19739
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserTotpSetupComponent, decorators: [{
|
|
19740
|
-
type: Component,
|
|
19741
|
-
args: [{ selector: 'c8y-user-totp-setup', standalone: true, imports: [ModalComponent, TotpSetupComponent, TotpChallengeComponent, C8yTranslatePipe], template: "<c8y-modal\n [title]=\"'Set up two-factor authentication' | translate\"\n [headerClasses]=\"'dialog-header'\"\n [customFooter]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span class=\"dlt-c8y-icon-phonelink-lock\"></span>\n </ng-container>\n\n <c8y-totp-setup></c8y-totp-setup>\n <c8y-totp-challenge [isModal]=\"true\" (onSuccess)=\"totpSetupVerified()\" (totpUnconfirmedEmitter)=\"close()\"></c8y-totp-challenge>\n</c8y-modal>\n" }]
|
|
19742
|
-
}], ctorParameters: () => [{ type: i1.UserService }, { type: ModalService }, { type: i1$7.BsModalRef }, { type: SimplifiedAuthService }] });
|
|
19743
|
-
|
|
19744
|
-
/**
|
|
19745
|
-
* The component is used to display a password control that includes the functionality of hiding and showing the input value.
|
|
19746
|
-
*/
|
|
19747
|
-
class PasswordInputComponent {
|
|
19748
|
-
constructor() {
|
|
19749
|
-
/**
|
|
19750
|
-
* Id of input
|
|
19751
|
-
*/
|
|
19752
|
-
this.id = '';
|
|
19753
|
-
/**
|
|
19754
|
-
* Value for autocomplete attribute of input
|
|
19755
|
-
*/
|
|
19756
|
-
this.autocomplete = 'off';
|
|
19757
|
-
this.value = '';
|
|
19758
|
-
this.disabled = false;
|
|
19759
|
-
this.type = 'password';
|
|
19760
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
19761
|
-
this.onChange = value => { };
|
|
19762
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
19763
|
-
this.onTouched = () => { };
|
|
19840
|
+
get user() {
|
|
19841
|
+
return this._user;
|
|
19764
19842
|
}
|
|
19765
|
-
|
|
19766
|
-
this.
|
|
19843
|
+
constructor(state, translate, bsModalService, alert, userService, tenantLoginOptionsService, tenantService) {
|
|
19844
|
+
this.state = state;
|
|
19845
|
+
this.translate = translate;
|
|
19846
|
+
this.bsModalService = bsModalService;
|
|
19847
|
+
this.alert = alert;
|
|
19848
|
+
this.userService = userService;
|
|
19849
|
+
this.tenantLoginOptionsService = tenantLoginOptionsService;
|
|
19850
|
+
this.tenantService = tenantService;
|
|
19851
|
+
this.loading = false;
|
|
19852
|
+
this.showProductExperienceOptions = false;
|
|
19853
|
+
this.isUsageTrackingEnabled = true;
|
|
19854
|
+
this.isUserEngagementPreferenceEnabled = true;
|
|
19855
|
+
this.focusOnNewPassword = false;
|
|
19856
|
+
this.onUser = new EventEmitter();
|
|
19857
|
+
this.onUsageTrackingChange = new EventEmitter();
|
|
19858
|
+
this.onUserEngagementPreferenceChange = new EventEmitter();
|
|
19859
|
+
this.onCancel = new EventEmitter();
|
|
19860
|
+
this.newPasswordComponent = viewChild(NewPasswordComponent);
|
|
19861
|
+
this.userHasActiveTotp = false;
|
|
19862
|
+
this.userCanSetupTotp = false;
|
|
19863
|
+
this.isPhoneRequired = false;
|
|
19864
|
+
effect(() => {
|
|
19865
|
+
if (this.focusOnNewPassword && this.newPasswordComponent()) {
|
|
19866
|
+
this.newPasswordComponent().toggleChangePassword();
|
|
19867
|
+
setTimeout(() => {
|
|
19868
|
+
this.newPasswordComponent().elementRef.nativeElement.scrollIntoView({
|
|
19869
|
+
behavior: 'smooth',
|
|
19870
|
+
block: 'center'
|
|
19871
|
+
});
|
|
19872
|
+
}, 100);
|
|
19873
|
+
}
|
|
19874
|
+
});
|
|
19767
19875
|
}
|
|
19768
|
-
|
|
19769
|
-
|
|
19876
|
+
async ngOnInit() {
|
|
19877
|
+
const currentTenant = (await this.tenantService.current()).data;
|
|
19878
|
+
const { enabledOnSystemLevel, enabledOnTenantLevel } = await this.tenantService.getTfaSettings(currentTenant);
|
|
19879
|
+
this.isTfaEnabled = enabledOnSystemLevel || enabledOnTenantLevel;
|
|
19880
|
+
await this.initializeTotpSettings();
|
|
19881
|
+
if (this.user.twoFactorAuthenticationEnabled && !this.userCanSetupTotp) {
|
|
19882
|
+
this.isPhoneRequired = true;
|
|
19883
|
+
}
|
|
19770
19884
|
}
|
|
19771
|
-
|
|
19772
|
-
this.
|
|
19885
|
+
setupTotp() {
|
|
19886
|
+
this.bsModalService.show(UserTotpSetupComponent, {
|
|
19887
|
+
class: 'modal-sm',
|
|
19888
|
+
ariaDescribedby: 'modal-body',
|
|
19889
|
+
ariaLabelledBy: 'modal-title'
|
|
19890
|
+
});
|
|
19891
|
+
this.cancel(); // to close the user edit modal and prevent console errors on logout
|
|
19773
19892
|
}
|
|
19774
|
-
|
|
19775
|
-
this.
|
|
19893
|
+
cancel() {
|
|
19894
|
+
this.onCancel.emit();
|
|
19776
19895
|
}
|
|
19777
|
-
|
|
19778
|
-
this.
|
|
19779
|
-
|
|
19896
|
+
async save() {
|
|
19897
|
+
if (this.loading) {
|
|
19898
|
+
return;
|
|
19899
|
+
}
|
|
19900
|
+
if (this.showProductExperienceOptions) {
|
|
19901
|
+
this.onUsageTrackingChange.emit(this.isUsageTrackingEnabled);
|
|
19902
|
+
/**
|
|
19903
|
+
* Emits a user engagement preference change event.
|
|
19904
|
+
* If usage tracking is disabled, it emits `false`. Otherwise, it emits the current state of the user engagement preference.
|
|
19905
|
+
*/
|
|
19906
|
+
this.onUserEngagementPreferenceChange.emit(this.isUsageTrackingEnabled === false ? false : this.isUserEngagementPreferenceEnabled);
|
|
19907
|
+
}
|
|
19908
|
+
this.onUser.emit(this._user);
|
|
19780
19909
|
}
|
|
19781
|
-
|
|
19782
|
-
this.
|
|
19910
|
+
onNewPasswordChanged(newPassword) {
|
|
19911
|
+
this._user.password = newPassword.password;
|
|
19783
19912
|
}
|
|
19784
|
-
|
|
19785
|
-
|
|
19786
|
-
|
|
19787
|
-
|
|
19788
|
-
|
|
19789
|
-
|
|
19913
|
+
async initializeTotpSettings() {
|
|
19914
|
+
try {
|
|
19915
|
+
this.userCanSetupTotp = await this.canUserSetupTotp();
|
|
19916
|
+
if (this.userCanSetupTotp) {
|
|
19917
|
+
const { data: totpActivity } = await this.userService.getActivityTotp();
|
|
19918
|
+
this.userHasActiveTotp = totpActivity.isActive;
|
|
19790
19919
|
}
|
|
19791
|
-
|
|
19920
|
+
}
|
|
19921
|
+
catch (ex) {
|
|
19922
|
+
this.alert.removeLastDanger();
|
|
19923
|
+
}
|
|
19924
|
+
}
|
|
19925
|
+
async canUserSetupTotp() {
|
|
19926
|
+
// we don't check for tenant options here due to permission restrictions on that end-point
|
|
19927
|
+
const loginOptions = (await this.tenantLoginOptionsService.listForCurrentTenant()).data;
|
|
19928
|
+
return loginOptions.some(({ tfaStrategy = '' }) => tfaStrategy.toLowerCase() === 'totp');
|
|
19929
|
+
}
|
|
19930
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserEditComponent, deps: [{ token: AppStateService }, { token: TranslateService }, { token: i1$7.BsModalService }, { token: AlertService }, { token: i1.UserService }, { token: i1.TenantLoginOptionsService }, { token: i1.TenantService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19931
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.14", type: UserEditComponent, isStandalone: true, selector: "c8y-user-edit", inputs: { loading: "loading", user: "user", showProductExperienceOptions: "showProductExperienceOptions", isUsageTrackingEnabled: "isUsageTrackingEnabled", isUserEngagementPreferenceEnabled: "isUserEngagementPreferenceEnabled", focusOnNewPassword: "focusOnNewPassword" }, outputs: { onUser: "onUser", onUsageTrackingChange: "onUsageTrackingChange", onUserEngagementPreferenceChange: "onUserEngagementPreferenceChange", onCancel: "onCancel" }, viewQueries: [{ propertyName: "newPasswordComponent", first: true, predicate: NewPasswordComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Joe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Doe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n title=\"{{ 'Save' | translate }}\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$8.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: PhoneValidationDirective, selector: "[c8yPhoneValidation]" }, { kind: "component", type: NewPasswordComponent, selector: "c8y-new-password", inputs: ["showChangePasswordButton", "requireStrongPassword"], outputs: ["password"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
|
|
19792
19932
|
}
|
|
19793
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
19933
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserEditComponent, decorators: [{
|
|
19794
19934
|
type: Component,
|
|
19795
|
-
args: [{ selector: 'c8y-
|
|
19796
|
-
|
|
19797
|
-
|
|
19798
|
-
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19802
|
-
|
|
19935
|
+
args: [{ selector: 'c8y-user-edit', standalone: true, imports: [
|
|
19936
|
+
FormsModule$1,
|
|
19937
|
+
NgIf,
|
|
19938
|
+
C8yTranslateDirective,
|
|
19939
|
+
FormGroupComponent,
|
|
19940
|
+
RequiredInputPlaceholderDirective,
|
|
19941
|
+
DefaultValidationDirective,
|
|
19942
|
+
PhoneValidationDirective,
|
|
19943
|
+
NewPasswordComponent,
|
|
19944
|
+
C8yTranslatePipe,
|
|
19945
|
+
AsyncPipe
|
|
19946
|
+
], template: "<form #userForm=\"ngForm\" (ngSubmit)=\"userForm.form.valid && save()\">\n <div class=\"d-block p-24 p-b-0\">\n <div class=\"alert alert-warning\" role=\"alert\" *ngIf=\"userIsExternal\" translate>\n Some of the user settings are not editable here because they are managed via your\n authorization server.\n </div>\n <c8y-form-group>\n <label translate for=\"userName\">Username</label>\n <input\n id=\"userName\"\n class=\"form-control\"\n [(ngModel)]=\"user.userName\"\n name=\"userName\"\n autocomplete=\"off\"\n required\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [disabled]=\"user.id\"\n c8yDefaultValidation=\"user\"\n />\n </c8y-form-group>\n\n <c8y-form-group>\n <label translate for=\"displayName\">Login alias</label>\n <input\n id=\"displayName\"\n class=\"form-control\"\n [(ngModel)]=\"user.displayName\"\n name=\"displayName\"\n autocomplete=\"off\"\n maxlength=\"254\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [disabled]=\"userIsExternal\"\n c8yDefaultValidation=\"loginAlias\"\n />\n </c8y-form-group>\n\n <c8y-form-group [hasWarning]=\"!user.email\">\n <label translate for=\"userEmail\">Email</label>\n <input\n id=\"userEmail\"\n class=\"form-control\"\n type=\"email\"\n name=\"email\"\n [maxlength]=\"254\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. joe.doe@example.com`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"user.email\"\n email\n [required]=\"true\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userFirstName\">First name</label>\n <input\n id=\"userFirstName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Joe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"firstName\"\n [(ngModel)]=\"user.firstName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label translate for=\"userLastName\">Last name</label>\n <input\n id=\"userLastName\"\n class=\"form-control\"\n autocomplete=\"off\"\n placeholder=\"{{ 'e.g. Doe`LOCALIZE`' | translate }}\"\n maxlength=\"50\"\n name=\"lastName\"\n [(ngModel)]=\"user.lastName\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n </div>\n </div>\n\n <c8y-form-group>\n <label translate for=\"userTelephone\">Telephone</label>\n <input\n id=\"userTelephone\"\n class=\"form-control\"\n autocomplete=\"off\"\n name=\"phone\"\n maxlength=\"254\"\n [(ngModel)]=\"user.phone\"\n placeholder=\"{{ 'e.g. +49 9 876 543 210`LOCALIZE`' | translate }}\"\n c8yPhoneValidation\n [required]=\"isPhoneRequired\"\n [disabled]=\"userIsExternal\"\n />\n </c8y-form-group>\n\n <c8y-form-group class=\"p-t-16 separator-top\" *ngIf=\"showProductExperienceOptions\">\n <label translate>Product experience</label>\n <label class=\"c8y-switch\" for=\"productUsageTracking\">\n <input\n id=\"productUsageTracking\"\n name=\"productUsageTracking\"\n type=\"checkbox\"\n [(ngModel)]=\"isUsageTrackingEnabled\"\n />\n <span></span>\n {{ 'Enable personalized product experience tracking' | translate }}\n </label>\n <ng-container *ngIf=\"isUsageTrackingEnabled\">\n <label class=\"c8y-switch m-l-0\" for=\"userEngagementPreference\">\n <input\n id=\"userEngagementPreference\"\n name=\"userEngagementPreference\"\n type=\"checkbox\"\n [(ngModel)]=\"isUserEngagementPreferenceEnabled\"\n />\n <span></span>\n {{ 'Enable in-product information & communication' | translate }}\n </label>\n </ng-container>\n </c8y-form-group>\n\n <div class=\"form-group p-t-16 separator-top\" *ngIf=\"!userIsExternal\">\n <label class=\"control-label\">{{ 'Login options' | translate }}</label>\n <c8y-new-password (password)=\"onNewPasswordChanged($event)\"></c8y-new-password>\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Set up two-factor authentication' | translate }}\"\n (click)=\"setupTotp()\"\n *ngIf=\"userCanSetupTotp && !userHasActiveTotp && isTfaEnabled\"\n >\n {{ 'Set up two-factor authentication' | translate }}\n </button>\n </div>\n\n <c8y-form-group *ngIf=\"!!(state.state$ | async).newsletter\">\n <label translate>Newsletter</label>\n <label\n title=\"{{ 'Send me information about outages, maintenance or updates.' | translate }}\"\n class=\"c8y-checkbox\"\n >\n <input\n type=\"checkbox\"\n name=\"newsletter\"\n [(ngModel)]=\"user.newsletter\"\n [disabled]=\"userIsExternal\"\n />\n <span></span>\n <span>\n {{ 'Send me information about outages, maintenance or updates.' | translate }}\n </span>\n </label>\n </c8y-form-group>\n </div>\n <div class=\"modal-footer separator-top bg-level-0 sticky-bottom\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Cancel' | translate }}\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n title=\"{{ 'Save' | translate }}\"\n [disabled]=\"!userForm.form.valid || userForm.form.pristine || loading\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }]
|
|
19947
|
+
}], ctorParameters: () => [{ type: AppStateService }, { type: TranslateService }, { type: i1$7.BsModalService }, { type: AlertService }, { type: i1.UserService }, { type: i1.TenantLoginOptionsService }, { type: i1.TenantService }], propDecorators: { loading: [{
|
|
19803
19948
|
type: Input
|
|
19804
|
-
}],
|
|
19949
|
+
}], user: [{
|
|
19950
|
+
type: Input
|
|
19951
|
+
}], showProductExperienceOptions: [{
|
|
19952
|
+
type: Input
|
|
19953
|
+
}], isUsageTrackingEnabled: [{
|
|
19954
|
+
type: Input
|
|
19955
|
+
}], isUserEngagementPreferenceEnabled: [{
|
|
19956
|
+
type: Input
|
|
19957
|
+
}], focusOnNewPassword: [{
|
|
19805
19958
|
type: Input
|
|
19959
|
+
}], onUser: [{
|
|
19960
|
+
type: Output
|
|
19961
|
+
}], onUsageTrackingChange: [{
|
|
19962
|
+
type: Output
|
|
19963
|
+
}], onUserEngagementPreferenceChange: [{
|
|
19964
|
+
type: Output
|
|
19965
|
+
}], onCancel: [{
|
|
19966
|
+
type: Output
|
|
19806
19967
|
}] } });
|
|
19807
19968
|
|
|
19808
|
-
class
|
|
19809
|
-
constructor(
|
|
19810
|
-
this.
|
|
19811
|
-
|
|
19812
|
-
validate(abControl) {
|
|
19813
|
-
const value = abControl.value;
|
|
19814
|
-
const controlToCompareWith = abControl.root.get(this.passwordConfirm);
|
|
19815
|
-
if (controlToCompareWith && value !== controlToCompareWith.value) {
|
|
19816
|
-
return { passwordConfirm: true };
|
|
19817
|
-
}
|
|
19818
|
-
return null;
|
|
19819
|
-
}
|
|
19820
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirm, deps: [{ token: 'passwordConfirm', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
19821
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: PasswordConfirm, isStandalone: true, selector: "[passwordConfirm]", providers: [
|
|
19822
|
-
{
|
|
19823
|
-
provide: NG_VALIDATORS,
|
|
19824
|
-
useExisting: PasswordConfirm,
|
|
19825
|
-
multi: true
|
|
19826
|
-
}
|
|
19827
|
-
], ngImport: i0 }); }
|
|
19828
|
-
}
|
|
19829
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordConfirm, decorators: [{
|
|
19830
|
-
type: Directive,
|
|
19831
|
-
args: [{
|
|
19832
|
-
selector: '[passwordConfirm]',
|
|
19833
|
-
providers: [
|
|
19834
|
-
{
|
|
19835
|
-
provide: NG_VALIDATORS,
|
|
19836
|
-
useExisting: PasswordConfirm,
|
|
19837
|
-
multi: true
|
|
19838
|
-
}
|
|
19839
|
-
],
|
|
19840
|
-
standalone: true
|
|
19841
|
-
}]
|
|
19842
|
-
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
19843
|
-
type: Attribute,
|
|
19844
|
-
args: ['passwordConfirm']
|
|
19845
|
-
}] }] });
|
|
19846
|
-
|
|
19847
|
-
class PasswordStrengthService {
|
|
19848
|
-
constructor(ui) {
|
|
19969
|
+
class UserEditModalComponent {
|
|
19970
|
+
constructor(modal, user, ui, auth, client, alert, userPreferences, c8yModalService, gainsightService, cookieBannerService, passwordService, userEngagementsService) {
|
|
19971
|
+
this.modal = modal;
|
|
19972
|
+
this.user = user;
|
|
19849
19973
|
this.ui = ui;
|
|
19850
|
-
this.
|
|
19851
|
-
this.
|
|
19852
|
-
|
|
19853
|
-
|
|
19854
|
-
|
|
19855
|
-
|
|
19974
|
+
this.auth = auth;
|
|
19975
|
+
this.client = client;
|
|
19976
|
+
this.alert = alert;
|
|
19977
|
+
this.userPreferences = userPreferences;
|
|
19978
|
+
this.c8yModalService = c8yModalService;
|
|
19979
|
+
this.gainsightService = gainsightService;
|
|
19980
|
+
this.cookieBannerService = cookieBannerService;
|
|
19981
|
+
this.passwordService = passwordService;
|
|
19982
|
+
this.userEngagementsService = userEngagementsService;
|
|
19983
|
+
this.loading = false;
|
|
19984
|
+
this.showProductExperienceOptions = false;
|
|
19985
|
+
this.passwordChange = false;
|
|
19856
19986
|
}
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
*/
|
|
19861
|
-
async getGreenMinLength() {
|
|
19862
|
-
const { greenMinLength } = (await this.getBasicAuthLoginOption()) || { greenMinLength: null };
|
|
19863
|
-
this.passwordStrengthSetting.greenMinLength = greenMinLength || this.GREEN_MIN_LENGTH_DEFAULT;
|
|
19864
|
-
return this.passwordStrengthSetting.greenMinLength;
|
|
19987
|
+
async ngOnInit() {
|
|
19988
|
+
this.updateUserInAppState();
|
|
19989
|
+
await this.setInitialProductExperienceOptions();
|
|
19865
19990
|
}
|
|
19866
19991
|
/**
|
|
19867
|
-
*
|
|
19868
|
-
*
|
|
19869
|
-
*
|
|
19870
|
-
*
|
|
19871
|
-
*
|
|
19872
|
-
*
|
|
19992
|
+
* Initializes product experience options for the user.
|
|
19993
|
+
*
|
|
19994
|
+
* This function performs the following operations:
|
|
19995
|
+
* - Determines if the user has the permission to edit product experience options.
|
|
19996
|
+
* - If the user has the permission and functional cookies are enabled:
|
|
19997
|
+
* - Checks whether personalized product experience tracking is active.
|
|
19998
|
+
* - Checks whether in-product information and communication is active.
|
|
19873
19999
|
*/
|
|
19874
|
-
async
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
if (
|
|
19878
|
-
|
|
19879
|
-
|
|
20000
|
+
async setInitialProductExperienceOptions() {
|
|
20001
|
+
this.showProductExperienceOptions =
|
|
20002
|
+
await this.gainsightService.canEditProductExperienceSettings();
|
|
20003
|
+
if (this.showProductExperienceOptions && this.cookieBannerService.isFunctionalCookieEnabled()) {
|
|
20004
|
+
// Enable personalized product experience tracking option
|
|
20005
|
+
this.currentUsageTrackingState =
|
|
20006
|
+
!(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY));
|
|
20007
|
+
// Enable in-product information & communication option
|
|
20008
|
+
this.currentUserEngagementPreferenceInitialState =
|
|
20009
|
+
this.userEngagementsService.userEngagementsEnabled$.value;
|
|
19880
20010
|
}
|
|
19881
|
-
|
|
19882
|
-
|
|
20011
|
+
}
|
|
20012
|
+
async onDismiss() {
|
|
20013
|
+
this.modal.hide();
|
|
20014
|
+
}
|
|
20015
|
+
onUsageTrackingChange(isEnabled) {
|
|
20016
|
+
this.usageTrackingState = isEnabled;
|
|
20017
|
+
}
|
|
20018
|
+
onUserEngagementPreferenceChange(isEnabled) {
|
|
20019
|
+
this.userEngagementPreferenceNewState = isEnabled;
|
|
20020
|
+
}
|
|
20021
|
+
async updateAndClose(user) {
|
|
20022
|
+
this.loading = true;
|
|
20023
|
+
try {
|
|
20024
|
+
const passwordChanged = Boolean(user.password);
|
|
20025
|
+
const isExternalUser = user.customProperties.userOrigin === 'OAUTH2';
|
|
20026
|
+
if (!isExternalUser && passwordChanged) {
|
|
20027
|
+
const currentPassword = await this.passwordService.currentPassword().toPromise();
|
|
20028
|
+
if (!currentPassword) {
|
|
20029
|
+
return;
|
|
20030
|
+
}
|
|
20031
|
+
await this.user.changeCurrentUserPassword(user.password, currentPassword);
|
|
20032
|
+
this.updateCredentials(user.password);
|
|
20033
|
+
}
|
|
20034
|
+
if (user.customProperties.userOrigin !== 'OAUTH2') {
|
|
20035
|
+
await this.user.updateCurrent(omit(user, 'password'));
|
|
20036
|
+
await this.updateUserInAppState();
|
|
20037
|
+
}
|
|
20038
|
+
await this.updateProductExperienceOptions();
|
|
20039
|
+
this.modal.hide();
|
|
20040
|
+
this.alert.success(gettext$1('User saved.'));
|
|
20041
|
+
}
|
|
20042
|
+
catch (e) {
|
|
20043
|
+
if (e) {
|
|
20044
|
+
this.alert.addServerFailure(e);
|
|
20045
|
+
}
|
|
20046
|
+
}
|
|
20047
|
+
finally {
|
|
20048
|
+
this.loading = false;
|
|
19883
20049
|
}
|
|
19884
|
-
return this.passwordStrengthSetting.enforcePasswordStrength;
|
|
19885
20050
|
}
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
*/
|
|
19894
|
-
async getPasswordStrengthValidity(refresh = false) {
|
|
19895
|
-
const loginOption = await this.getBasicAuthLoginOption(refresh);
|
|
19896
|
-
const strengthValidity = loginOption?.strengthValidity;
|
|
19897
|
-
if (typeof strengthValidity === 'string') {
|
|
19898
|
-
this.passwordStrengthSetting.passwordStrengthValidity =
|
|
19899
|
-
strengthValidity === 'true' ? true : false;
|
|
20051
|
+
async gainsightTrackingAppReload() {
|
|
20052
|
+
try {
|
|
20053
|
+
await this.c8yModalService.confirm(gettext$1('Reload required'), gettext$1('To change the tracking option in the entire application, you need to reload the page. If you have any unsaved changes, you can reload later. How would you like to proceed?'), Status.WARNING, {
|
|
20054
|
+
ok: gettext$1('Reload now'),
|
|
20055
|
+
cancel: gettext$1('Reload later')
|
|
20056
|
+
});
|
|
20057
|
+
location.reload();
|
|
19900
20058
|
}
|
|
19901
|
-
|
|
19902
|
-
|
|
20059
|
+
catch (ex) {
|
|
20060
|
+
// do nothing
|
|
19903
20061
|
}
|
|
19904
|
-
|
|
20062
|
+
}
|
|
20063
|
+
async updateProductExperienceOptions() {
|
|
20064
|
+
this.updateUserEngagementsPreference();
|
|
20065
|
+
await this.updateTrackingOption();
|
|
19905
20066
|
}
|
|
19906
20067
|
/**
|
|
19907
|
-
*
|
|
19908
|
-
*
|
|
19909
|
-
*
|
|
19910
|
-
*
|
|
19911
|
-
* @return boolean value, true if strength is enforced for tenant, false otherwise.
|
|
20068
|
+
* Updates the user engagement preference if it has changed from the initial state.
|
|
20069
|
+
* Calls the user engagements service to update the preference.
|
|
20070
|
+
*
|
|
20071
|
+
* The update only occurs if the current preference differs from the new state.
|
|
19912
20072
|
*/
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
-
|
|
19917
|
-
this.getPasswordStrengthValidity(refresh)
|
|
19918
|
-
]).then(values => {
|
|
19919
|
-
const [enforcePasswordStrength, passwordStrengthValidity] = values;
|
|
19920
|
-
return enforcePasswordStrength || passwordStrengthValidity;
|
|
19921
|
-
});
|
|
20073
|
+
updateUserEngagementsPreference() {
|
|
20074
|
+
if (this.currentUserEngagementPreferenceInitialState !== this.userEngagementPreferenceNewState) {
|
|
20075
|
+
this.userEngagementsService.updateUserEngagementPreference(this.userEngagementPreferenceNewState);
|
|
20076
|
+
}
|
|
19922
20077
|
}
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
19933
|
-
|
|
19934
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordStrengthService, decorators: [{
|
|
19935
|
-
type: Injectable,
|
|
19936
|
-
args: [{
|
|
19937
|
-
providedIn: 'root'
|
|
19938
|
-
}]
|
|
19939
|
-
}], ctorParameters: () => [{ type: AppStateService }] });
|
|
19940
|
-
|
|
19941
|
-
class PasswordCheckListComponent {
|
|
19942
|
-
set password(password) {
|
|
19943
|
-
if (typeof password === 'string') {
|
|
19944
|
-
this.onPasswordChange(password);
|
|
20078
|
+
/**
|
|
20079
|
+
* Asynchronously updates the tracking option for user preferences.
|
|
20080
|
+
* If the current usage tracking state differs from the new state,
|
|
20081
|
+
* it updates the Gainsight preferences and sets a functional cookie
|
|
20082
|
+
* before triggering a reload of the application.
|
|
20083
|
+
*/
|
|
20084
|
+
async updateTrackingOption() {
|
|
20085
|
+
if (this.currentUsageTrackingState !== this.usageTrackingState) {
|
|
20086
|
+
await this.userPreferences.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY, this.usageTrackingState);
|
|
20087
|
+
this.gainsightService.setFunctionalCookie(this.usageTrackingState);
|
|
20088
|
+
await this.gainsightTrackingAppReload();
|
|
19945
20089
|
}
|
|
19946
20090
|
}
|
|
19947
|
-
|
|
19948
|
-
|
|
19949
|
-
this.
|
|
19950
|
-
this.passwordService = passwordService;
|
|
19951
|
-
this.strengthEnforced = false;
|
|
19952
|
-
this.onRequirementsFulfilled = new EventEmitter();
|
|
19953
|
-
this.minGreenLength = 8;
|
|
19954
|
-
this.enhancedStrengthCheckList = [
|
|
19955
|
-
{
|
|
19956
|
-
label: gettext$1('Include lowercase characters (for example, abcdef)'),
|
|
19957
|
-
check: this.passwordStrengthChecker.hasLowerCase,
|
|
19958
|
-
icon: '',
|
|
19959
|
-
contextualColor: '',
|
|
19960
|
-
textColor: ''
|
|
19961
|
-
},
|
|
19962
|
-
{
|
|
19963
|
-
label: gettext$1('Include uppercase characters (for example, ABCDEF)'),
|
|
19964
|
-
check: this.passwordStrengthChecker.hasUpperCase,
|
|
19965
|
-
icon: '',
|
|
19966
|
-
contextualColor: '',
|
|
19967
|
-
textColor: ''
|
|
19968
|
-
},
|
|
19969
|
-
{
|
|
19970
|
-
label: gettext$1('Include numbers (for example, 123456)'),
|
|
19971
|
-
check: this.passwordStrengthChecker.hasNumbers,
|
|
19972
|
-
icon: '',
|
|
19973
|
-
contextualColor: '',
|
|
19974
|
-
textColor: ''
|
|
19975
|
-
},
|
|
19976
|
-
{
|
|
19977
|
-
label: gettext$1('Include symbols (for example, !@#$%^)'),
|
|
19978
|
-
check: this.passwordStrengthChecker.hasSpecialChars,
|
|
19979
|
-
icon: '',
|
|
19980
|
-
contextualColor: '',
|
|
19981
|
-
textColor: ''
|
|
19982
|
-
}
|
|
19983
|
-
];
|
|
19984
|
-
this.basicChecklist = [
|
|
19985
|
-
{
|
|
19986
|
-
label: gettext$1('Must have at least {{length}} characters'),
|
|
19987
|
-
check: password => password.length >= this.minGreenLength,
|
|
19988
|
-
icon: '',
|
|
19989
|
-
contextualColor: '',
|
|
19990
|
-
textColor: ''
|
|
19991
|
-
}
|
|
19992
|
-
];
|
|
19993
|
-
this.combinedChecklist = [];
|
|
19994
|
-
}
|
|
19995
|
-
async ngOnInit() {
|
|
19996
|
-
this.minGreenLength = await this.passwordStrength.getGreenMinLength();
|
|
19997
|
-
if (!this.minGreenLength) {
|
|
19998
|
-
this.minGreenLength = this.passwordService.getDefaultPasswordMinLength();
|
|
19999
|
-
}
|
|
20000
|
-
this.onPasswordChange('');
|
|
20091
|
+
async updateUserInAppState() {
|
|
20092
|
+
const currentUserResult = await this.user.current();
|
|
20093
|
+
this.ui.currentUser.next(currentUserResult.data);
|
|
20001
20094
|
}
|
|
20002
|
-
|
|
20003
|
-
|
|
20004
|
-
|
|
20095
|
+
updateCredentials(password) {
|
|
20096
|
+
const newCredentials = {
|
|
20097
|
+
password,
|
|
20098
|
+
user: this.ui.currentUser.value.id,
|
|
20099
|
+
tenant: this.client.tenant
|
|
20005
20100
|
};
|
|
20101
|
+
this.auth.updateCredentials(newCredentials);
|
|
20006
20102
|
}
|
|
20007
|
-
|
|
20008
|
-
|
|
20009
|
-
assign(requirement, {
|
|
20010
|
-
icon: checked ? 'check-circle' : 'radio-button-unchecked',
|
|
20011
|
-
contextualColor: checked ? 'text-success' : 'text-muted',
|
|
20012
|
-
textColor: checked ? '' : 'text-muted'
|
|
20013
|
-
});
|
|
20014
|
-
return requirement;
|
|
20015
|
-
}
|
|
20016
|
-
onPasswordChange(password) {
|
|
20017
|
-
this.basicChecklist.forEach(requirement => {
|
|
20018
|
-
this.checkRequirement(requirement, password);
|
|
20019
|
-
});
|
|
20020
|
-
this.enhancedStrengthCheckList.forEach(requirement => {
|
|
20021
|
-
this.checkRequirement(requirement, password);
|
|
20022
|
-
});
|
|
20023
|
-
this.combinedChecklist = [...this.basicChecklist, ...this.enhancedStrengthCheckList];
|
|
20024
|
-
this.onRequirementsFulfilled.emit(this.isPasswordValid());
|
|
20025
|
-
}
|
|
20026
|
-
isPasswordValid() {
|
|
20027
|
-
const checklist = this.strengthEnforced ? this.combinedChecklist : this.basicChecklist;
|
|
20028
|
-
return checklist.every(requirement => requirement.icon !== 'radio-button-unchecked');
|
|
20029
|
-
}
|
|
20030
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: PasswordCheckListComponent, deps: [{ token: PasswordStrengthService }, { token: PasswordService }, { token: PasswordService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20031
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: PasswordCheckListComponent, isStandalone: true, selector: "c8y-password-check-list", inputs: { strengthEnforced: "strengthEnforced", password: "password" }, outputs: { onRequirementsFulfilled: "onRequirementsFulfilled" }, ngImport: i0, template: "<div *ngIf=\"strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of combinedChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n\n<div *ngIf=\"!strengthEnforced\">\n <div class=\"m-b-8\">{{ 'Password must meet the requirements below:' | translate }}</div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of basicChecklist\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n\n <div class=\"m-b-8\">\n {{ 'We recommend you to meet these conditions for a stronger password:' | translate }}\n </div>\n <ul class=\"list-unstyled\">\n <li\n class=\"small d-flex\"\n *ngFor=\"let requirement of enhancedStrengthCheckList\"\n >\n <i\n class=\"{{ requirement.contextualColor }}\"\n [c8yIcon]=\"requirement.icon\"\n ></i>\n <span\n class=\"m-l-4 small {{ requirement.textColor }}\"\n [translate]=\"requirement.label\"\n [translateParams]=\"this.translateParams\"\n ></span>\n </li>\n </ul>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
20103
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserEditModalComponent, deps: [{ token: i1$7.BsModalRef }, { token: i1.UserService }, { token: AppStateService }, { token: i1.BasicAuth }, { token: i1.FetchClient }, { token: AlertService }, { token: UserPreferencesService }, { token: ModalService }, { token: GainsightService }, { token: CookieBannerService }, { token: PasswordService }, { token: UserEngagementsService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20104
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: UserEditModalComponent, isStandalone: true, selector: "c8y-user-edit-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"'Edit user' | translate\"\n [customFooter]=\"true\"\n (onDismiss)=\"onDismiss()\"\n>\n <c8y-user-edit\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isUserEngagementPreferenceEnabled]=\"currentUserEngagementPreferenceInitialState\"\n [showProductExperienceOptions]=\"showProductExperienceOptions\"\n [focusOnNewPassword]=\"passwordChange\"\n (onUsageTrackingChange)=\"onUsageTrackingChange($event)\"\n (onUserEngagementPreferenceChange)=\"onUserEngagementPreferenceChange($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n ></c8y-user-edit>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: UserEditComponent, selector: "c8y-user-edit", inputs: ["loading", "user", "showProductExperienceOptions", "isUsageTrackingEnabled", "isUserEngagementPreferenceEnabled", "focusOnNewPassword"], outputs: ["onUser", "onUsageTrackingChange", "onUserEngagementPreferenceChange", "onCancel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
|
|
20032
20105
|
}
|
|
20033
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
20106
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserEditModalComponent, decorators: [{
|
|
20034
20107
|
type: Component,
|
|
20035
|
-
args: [{ selector: 'c8y-
|
|
20036
|
-
}], ctorParameters: () => [{ type:
|
|
20037
|
-
type: Input
|
|
20038
|
-
}], password: [{
|
|
20039
|
-
type: Input,
|
|
20040
|
-
args: ['password']
|
|
20041
|
-
}], onRequirementsFulfilled: [{
|
|
20042
|
-
type: Output
|
|
20043
|
-
}] } });
|
|
20108
|
+
args: [{ selector: 'c8y-user-edit-modal', standalone: true, imports: [ModalComponent, UserEditComponent, C8yTranslatePipe, AsyncPipe], template: "<c8y-modal\n [title]=\"'Edit user' | translate\"\n [customFooter]=\"true\"\n (onDismiss)=\"onDismiss()\"\n>\n <c8y-user-edit\n [user]=\"ui.currentUser | async\"\n [loading]=\"loading\"\n [isUsageTrackingEnabled]=\"currentUsageTrackingState\"\n [isUserEngagementPreferenceEnabled]=\"currentUserEngagementPreferenceInitialState\"\n [showProductExperienceOptions]=\"showProductExperienceOptions\"\n [focusOnNewPassword]=\"passwordChange\"\n (onUsageTrackingChange)=\"onUsageTrackingChange($event)\"\n (onUserEngagementPreferenceChange)=\"onUserEngagementPreferenceChange($event)\"\n (onUser)=\"updateAndClose($event)\"\n (onCancel)=\"onDismiss()\"\n ></c8y-user-edit>\n</c8y-modal>\n" }]
|
|
20109
|
+
}], ctorParameters: () => [{ type: i1$7.BsModalRef }, { type: i1.UserService }, { type: AppStateService }, { type: i1.BasicAuth }, { type: i1.FetchClient }, { type: AlertService }, { type: UserPreferencesService }, { type: ModalService }, { type: GainsightService }, { type: CookieBannerService }, { type: PasswordService }, { type: UserEngagementsService }] });
|
|
20044
20110
|
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
constructor(passwordStrength, cdRef) {
|
|
20053
|
-
this.passwordStrength = passwordStrength;
|
|
20054
|
-
this.cdRef = cdRef;
|
|
20055
|
-
this.password = new EventEmitter();
|
|
20056
|
-
this.showChangePasswordButton = true;
|
|
20057
|
-
this.model = {};
|
|
20058
|
-
this.changePassword = false;
|
|
20059
|
-
this.passwordEnforced = false;
|
|
20060
|
-
this.passwordChecklistValidator = () => this.requirementsFulfilled ? null : { passwordStrengthChecklist: true };
|
|
20111
|
+
/**
|
|
20112
|
+
* Service to show a modal.
|
|
20113
|
+
*/
|
|
20114
|
+
class ModalService {
|
|
20115
|
+
constructor(modalService, gainsightService) {
|
|
20116
|
+
this.modalService = modalService;
|
|
20117
|
+
this.gainsightService = gainsightService;
|
|
20061
20118
|
}
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20119
|
+
/**
|
|
20120
|
+
* Shows a quick confirm message modal.
|
|
20121
|
+
* @param title The title of that modal.
|
|
20122
|
+
* @param body The text body to display.
|
|
20123
|
+
* @param status The status.
|
|
20124
|
+
* @param labels The labels to use. Default: { ok: 'Confirm', cancel: 'Cancel'}
|
|
20125
|
+
* @param confirmOptions Selection options to display as checkbox list.
|
|
20126
|
+
* @param productExperienceEvent Additional data to attach to custom product experience events.
|
|
20127
|
+
*/
|
|
20128
|
+
async confirm(title, body, status = Status.INFO, labels = {}, confirmOptions = {}, productExperienceEvent = { eventName: 'confirmModal' }) {
|
|
20129
|
+
const modalLabels = {
|
|
20130
|
+
ok: labels.ok || gettext$1('Confirm'),
|
|
20131
|
+
cancel: labels.cancel || gettext$1('Cancel')
|
|
20132
|
+
};
|
|
20133
|
+
const modalRef = this.modalService.show(ConfirmModalComponent, {
|
|
20134
|
+
initialState: { title, body, labels: modalLabels, status, confirmOptions },
|
|
20135
|
+
ariaDescribedby: 'modal-body',
|
|
20136
|
+
ariaLabelledBy: 'modal-title',
|
|
20137
|
+
ignoreBackdropClick: true
|
|
20066
20138
|
});
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
if (changes.showChangePasswordButton) {
|
|
20070
|
-
this.changePassword = !this.showChangePasswordButton;
|
|
20071
|
-
}
|
|
20072
|
-
if (changes.requireStrongPassword?.previousValue !== changes.requireStrongPassword?.currentValue) {
|
|
20073
|
-
await this.loadPasswordStrengthSettings();
|
|
20139
|
+
if (productExperienceEvent) {
|
|
20140
|
+
productExperienceEvent.data = { ...productExperienceEvent.data, title };
|
|
20074
20141
|
}
|
|
20142
|
+
this.triggerEvent(modalRef.content.result, modalLabels, productExperienceEvent);
|
|
20143
|
+
return await modalRef.content.result;
|
|
20075
20144
|
}
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20145
|
+
/**
|
|
20146
|
+
* Shows a quick acknowledge message modal.
|
|
20147
|
+
* @param title The title of that modal.
|
|
20148
|
+
* @param body The text body to display.
|
|
20149
|
+
* @param status The status.
|
|
20150
|
+
* @param acknowledgeLabel The label to use.
|
|
20151
|
+
* @param productExperienceEvent Additional data to attach to custom product experience events.
|
|
20152
|
+
*/
|
|
20153
|
+
async acknowledge(title, body, status = Status.INFO, acknowledgeLabel = gettext$1('Confirm'), productExperienceEvent = { eventName: 'confirmModal' }) {
|
|
20154
|
+
const labels = { ok: acknowledgeLabel, cancel: null };
|
|
20155
|
+
const modalRef = this.modalService.show(ConfirmModalComponent, {
|
|
20156
|
+
initialState: { title, body, labels, status },
|
|
20157
|
+
ariaDescribedby: 'modal-body',
|
|
20158
|
+
ariaLabelledBy: 'modal-title',
|
|
20159
|
+
ignoreBackdropClick: true
|
|
20080
20160
|
});
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
this.requirementsFulfilled = requirementsFulfilled;
|
|
20084
|
-
this.cdRef.detectChanges();
|
|
20085
|
-
this.newPasswordModel.control.updateValueAndValidity();
|
|
20086
|
-
// There are two validators checking password validity, but we only want to show one of these errors at a time,
|
|
20087
|
-
// where checklist validator takes priority.
|
|
20088
|
-
if (!this.requirementsFulfilled) {
|
|
20089
|
-
delete this.newPasswordModel.control.errors['password'];
|
|
20090
|
-
}
|
|
20091
|
-
}
|
|
20092
|
-
async loadPasswordStrengthSettings() {
|
|
20093
|
-
if (this.requireStrongPassword) {
|
|
20094
|
-
this.passwordEnforced = this.requireStrongPassword;
|
|
20095
|
-
}
|
|
20096
|
-
else {
|
|
20097
|
-
const passwordStrengthSettings = await this.passwordStrength.getPasswordStrengthEnforced({
|
|
20098
|
-
refresh: true
|
|
20099
|
-
});
|
|
20100
|
-
this.passwordEnforced = passwordStrengthSettings;
|
|
20101
|
-
}
|
|
20102
|
-
}
|
|
20103
|
-
toggleChangePassword() {
|
|
20104
|
-
this.changePassword = !this.changePassword;
|
|
20105
|
-
if (!this.changePassword) {
|
|
20106
|
-
this.password.emit({});
|
|
20107
|
-
this.model = {};
|
|
20108
|
-
}
|
|
20109
|
-
}
|
|
20110
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewPasswordComponent, deps: [{ token: PasswordStrengthService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20111
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: NewPasswordComponent, isStandalone: true, selector: "c8y-new-password", inputs: { showChangePasswordButton: "showChangePasswordButton", requireStrongPassword: "requireStrongPassword" }, outputs: { password: "password" }, viewQueries: [{ propertyName: "_newPasswordModel", first: true, predicate: ["newPassword"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"showChangePasswordButton\" class=\"form-group\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"toggleChangePassword()\"\n data-cy=\"c8y-new-password--change-button\"\n >\n <ng-container *ngIf=\"!changePassword\">\n {{ 'Change password' | translate }}\n </ng-container>\n <ng-container *ngIf=\"changePassword\">\n {{ 'Cancel password change' | translate }}\n </ng-container>\n </button>\n</div>\n\n<div\n class=\"row content-flex-50\"\n *ngIf=\"changePassword\"\n>\n <div class=\"col-6\">\n <c8y-form-group>\n <label\n for=\"newPassword\"\n translate\n >\n Password\n </label>\n <c8y-password-input\n name=\"newPassword\"\n required\n [id]=\"'newPassword'\"\n #newPassword=\"ngModel\"\n [(ngModel)]=\"model.newPassword\"\n (change)=\"newPasswordChanged()\"\n (input)=\"newPasswordConfirm.control.updateValueAndValidity()\"\n c8yDefaultValidation=\"password\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"newConfirmPassword\"\n translate\n >\n Confirm password\n </label>\n <c8y-password-input\n name=\"newPasswordConfirm\"\n required\n [id]=\"'newConfirmPassword'\"\n #newPasswordConfirm=\"ngModel\"\n [(ngModel)]=\"model.newPasswordConfirm\"\n passwordConfirm=\"newPassword\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-password-check-list\n [password]=\"model.newPassword\"\n [strengthEnforced]=\"passwordEnforced\"\n (onRequirementsFulfilled)=\"updateValidity($event)\"\n ></c8y-password-check-list>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: PasswordInputComponent, selector: "c8y-password-input", inputs: ["id", "autocomplete"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: PasswordConfirm, selector: "[passwordConfirm]" }, { kind: "component", type: PasswordCheckListComponent, selector: "c8y-password-check-list", inputs: ["strengthEnforced", "password"], outputs: ["onRequirementsFulfilled"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
|
|
20112
|
-
}
|
|
20113
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NewPasswordComponent, decorators: [{
|
|
20114
|
-
type: Component,
|
|
20115
|
-
args: [{ selector: 'c8y-new-password', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [
|
|
20116
|
-
NgIf,
|
|
20117
|
-
FormGroupComponent,
|
|
20118
|
-
C8yTranslateDirective,
|
|
20119
|
-
PasswordInputComponent,
|
|
20120
|
-
FormsModule$1,
|
|
20121
|
-
DefaultValidationDirective,
|
|
20122
|
-
PasswordConfirm,
|
|
20123
|
-
PasswordCheckListComponent,
|
|
20124
|
-
C8yTranslatePipe
|
|
20125
|
-
], template: "<div *ngIf=\"showChangePasswordButton\" class=\"form-group\">\n <button\n class=\"btn btn-default\"\n type=\"button\"\n (click)=\"toggleChangePassword()\"\n data-cy=\"c8y-new-password--change-button\"\n >\n <ng-container *ngIf=\"!changePassword\">\n {{ 'Change password' | translate }}\n </ng-container>\n <ng-container *ngIf=\"changePassword\">\n {{ 'Cancel password change' | translate }}\n </ng-container>\n </button>\n</div>\n\n<div\n class=\"row content-flex-50\"\n *ngIf=\"changePassword\"\n>\n <div class=\"col-6\">\n <c8y-form-group>\n <label\n for=\"newPassword\"\n translate\n >\n Password\n </label>\n <c8y-password-input\n name=\"newPassword\"\n required\n [id]=\"'newPassword'\"\n #newPassword=\"ngModel\"\n [(ngModel)]=\"model.newPassword\"\n (change)=\"newPasswordChanged()\"\n (input)=\"newPasswordConfirm.control.updateValueAndValidity()\"\n c8yDefaultValidation=\"password\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n\n <c8y-form-group>\n <label\n for=\"newConfirmPassword\"\n translate\n >\n Confirm password\n </label>\n <c8y-password-input\n name=\"newPasswordConfirm\"\n required\n [id]=\"'newConfirmPassword'\"\n #newPasswordConfirm=\"ngModel\"\n [(ngModel)]=\"model.newPasswordConfirm\"\n passwordConfirm=\"newPassword\"\n [autocomplete]=\"'new-password'\"\n ></c8y-password-input>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-password-check-list\n [password]=\"model.newPassword\"\n [strengthEnforced]=\"passwordEnforced\"\n (onRequirementsFulfilled)=\"updateValidity($event)\"\n ></c8y-password-check-list>\n </div>\n</div>\n" }]
|
|
20126
|
-
}], ctorParameters: () => [{ type: PasswordStrengthService }, { type: i0.ChangeDetectorRef }], propDecorators: { password: [{
|
|
20127
|
-
type: Output
|
|
20128
|
-
}], showChangePasswordButton: [{
|
|
20129
|
-
type: Input
|
|
20130
|
-
}], requireStrongPassword: [{
|
|
20131
|
-
type: Input
|
|
20132
|
-
}], _newPasswordModel: [{
|
|
20133
|
-
type: ViewChild,
|
|
20134
|
-
args: ['newPassword']
|
|
20135
|
-
}] } });
|
|
20136
|
-
|
|
20137
|
-
class UserEditComponent {
|
|
20138
|
-
set user(u) {
|
|
20139
|
-
if (u) {
|
|
20140
|
-
this._user = clone(u);
|
|
20141
|
-
this.userIsExternal = u.customProperties.userOrigin === 'OAUTH2';
|
|
20142
|
-
this.isPhoneRequired = this.isPhoneRequired && u.twoFactorAuthenticationEnabled;
|
|
20161
|
+
if (productExperienceEvent) {
|
|
20162
|
+
productExperienceEvent.data = { ...productExperienceEvent.data, title };
|
|
20143
20163
|
}
|
|
20164
|
+
this.triggerEvent(modalRef.content.result, labels, productExperienceEvent);
|
|
20165
|
+
return await modalRef.content.result;
|
|
20144
20166
|
}
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
this.
|
|
20158
|
-
this.isUsageTrackingEnabled = true;
|
|
20159
|
-
this.isUserEngagementPreferenceEnabled = true;
|
|
20160
|
-
this.onUser = new EventEmitter();
|
|
20161
|
-
this.onUsageTrackingChange = new EventEmitter();
|
|
20162
|
-
this.onUserEngagementPreferenceChange = new EventEmitter();
|
|
20163
|
-
this.onCancel = new EventEmitter();
|
|
20164
|
-
this.userHasActiveTotp = false;
|
|
20165
|
-
this.userCanSetupTotp = false;
|
|
20166
|
-
this.isPhoneRequired = false;
|
|
20167
|
-
}
|
|
20168
|
-
async ngOnInit() {
|
|
20169
|
-
const currentTenant = (await this.tenantService.current()).data;
|
|
20170
|
-
const { enabledOnSystemLevel, enabledOnTenantLevel } = await this.tenantService.getTfaSettings(currentTenant);
|
|
20171
|
-
this.isTfaEnabled = enabledOnSystemLevel || enabledOnTenantLevel;
|
|
20172
|
-
await this.initializeTotpSettings();
|
|
20173
|
-
if (this.user.twoFactorAuthenticationEnabled && !this.userCanSetupTotp) {
|
|
20174
|
-
this.isPhoneRequired = true;
|
|
20175
|
-
}
|
|
20167
|
+
/**
|
|
20168
|
+
* Shows a quick logout confirmation modal.
|
|
20169
|
+
* @param body The text body to display. Default: 'You will be logged out to apply your changes. Do you want to proceed?'
|
|
20170
|
+
* @param status The status.
|
|
20171
|
+
* @param labels The labels to use. Default: { ok: 'Confirm and log out', cancel: 'Cancel' }
|
|
20172
|
+
*/
|
|
20173
|
+
async confirmLogout(body, status = Status.WARNING, labels = {}) {
|
|
20174
|
+
const modalLabels = {
|
|
20175
|
+
ok: labels.ok || gettext$1('Confirm and log out'),
|
|
20176
|
+
cancel: labels.cancel || gettext$1('Cancel')
|
|
20177
|
+
};
|
|
20178
|
+
const modalBody = body || gettext$1('You must log out to apply your changes. Do you want to proceed?');
|
|
20179
|
+
return await this.confirm(gettext$1('Logout required'), modalBody, status, modalLabels);
|
|
20176
20180
|
}
|
|
20177
|
-
|
|
20178
|
-
this.
|
|
20179
|
-
|
|
20181
|
+
async changeCurrentUserPassword() {
|
|
20182
|
+
this.modalService.show(UserEditModalComponent, {
|
|
20183
|
+
initialState: { passwordChange: true },
|
|
20180
20184
|
ariaDescribedby: 'modal-body',
|
|
20181
20185
|
ariaLabelledBy: 'modal-title'
|
|
20182
20186
|
});
|
|
20183
|
-
this.cancel(); // to close the user edit modal and prevent console errors on logout
|
|
20184
|
-
}
|
|
20185
|
-
cancel() {
|
|
20186
|
-
this.onCancel.emit();
|
|
20187
|
-
}
|
|
20188
|
-
async save() {
|
|
20189
|
-
if (this.loading) {
|
|
20190
|
-
return;
|
|
20191
|
-
}
|
|
20192
|
-
if (this.showProductExperienceOptions) {
|
|
20193
|
-
this.onUsageTrackingChange.emit(this.isUsageTrackingEnabled);
|
|
20194
|
-
/**
|
|
20195
|
-
* Emits a user engagement preference change event.
|
|
20196
|
-
* If usage tracking is disabled, it emits `false`. Otherwise, it emits the current state of the user engagement preference.
|
|
20197
|
-
*/
|
|
20198
|
-
this.onUserEngagementPreferenceChange.emit(this.isUsageTrackingEnabled === false ? false : this.isUserEngagementPreferenceEnabled);
|
|
20199
|
-
}
|
|
20200
|
-
this.onUser.emit(this._user);
|
|
20201
20187
|
}
|
|
20202
|
-
|
|
20203
|
-
|
|
20188
|
+
triggerEvent(result, labels, productExperienceEvent) {
|
|
20189
|
+
const data = { ...productExperienceEvent.data, url: window.location.href };
|
|
20190
|
+
result
|
|
20191
|
+
.then(() => {
|
|
20192
|
+
this.gainsightService.triggerEvent(productExperienceEvent.eventName, {
|
|
20193
|
+
...data,
|
|
20194
|
+
result: labels.ok
|
|
20195
|
+
});
|
|
20196
|
+
})
|
|
20197
|
+
.catch(() => {
|
|
20198
|
+
this.gainsightService.triggerEvent(productExperienceEvent.eventName, {
|
|
20199
|
+
...data,
|
|
20200
|
+
result: labels.cancel
|
|
20201
|
+
});
|
|
20202
|
+
});
|
|
20204
20203
|
}
|
|
20205
|
-
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20204
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ModalService, deps: [{ token: i1$7.BsModalService }, { token: GainsightService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
20205
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ModalService, providedIn: 'root' }); }
|
|
20206
|
+
}
|
|
20207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ModalService, decorators: [{
|
|
20208
|
+
type: Injectable,
|
|
20209
|
+
args: [{
|
|
20210
|
+
providedIn: 'root'
|
|
20211
|
+
}]
|
|
20212
|
+
}], ctorParameters: () => [{ type: i1$7.BsModalService }, { type: GainsightService }] });
|
|
20213
|
+
|
|
20214
|
+
class ThemeSwitcherService {
|
|
20215
|
+
constructor(options) {
|
|
20216
|
+
this.options = options;
|
|
20217
|
+
this.darkThemeClass = `c8y-dark-theme`;
|
|
20218
|
+
this.themeOptions = [
|
|
20219
|
+
{
|
|
20220
|
+
label: gettext$1('Light'),
|
|
20221
|
+
value: 'light',
|
|
20222
|
+
icon: 'sun'
|
|
20223
|
+
},
|
|
20224
|
+
{
|
|
20225
|
+
label: gettext$1('Dark'),
|
|
20226
|
+
value: 'dark',
|
|
20227
|
+
icon: 'moon'
|
|
20228
|
+
},
|
|
20229
|
+
{
|
|
20230
|
+
label: gettext$1('System'),
|
|
20231
|
+
value: 'system',
|
|
20232
|
+
icon: 'imac-settings'
|
|
20211
20233
|
}
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20234
|
+
];
|
|
20235
|
+
this._userSelectedThemePreference$ = new BehaviorSubject(this.getCurrentThemePreference());
|
|
20236
|
+
this._temporaryThemePreference$ = new BehaviorSubject('none');
|
|
20237
|
+
this.userSelectedThemePreference$ = this._userSelectedThemePreference$.asObservable();
|
|
20238
|
+
const userSelectedTheme$ = this.userSelectedThemePreference$.pipe(switchMap(preference => {
|
|
20239
|
+
if (preference === 'system') {
|
|
20240
|
+
return this.getUsersSystemPreferenceForTheme$();
|
|
20241
|
+
}
|
|
20242
|
+
return of(preference);
|
|
20243
|
+
}));
|
|
20244
|
+
this.disableThemeSelection$ = this._temporaryThemePreference$.pipe(map(preference => preference !== 'none'));
|
|
20245
|
+
this.currentlyAppliedTheme$ = this._temporaryThemePreference$.pipe(switchMap(temporaryPreference => {
|
|
20246
|
+
if (temporaryPreference !== 'none') {
|
|
20247
|
+
return of(temporaryPreference);
|
|
20248
|
+
}
|
|
20249
|
+
return userSelectedTheme$;
|
|
20250
|
+
}));
|
|
20251
|
+
this.darkThemeAvailable$ = this.options.get$('darkThemeAvailable').pipe(map(value => !!value));
|
|
20221
20252
|
}
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20257
|
-
}] } });
|
|
20253
|
+
getCurrentThemePreference() {
|
|
20254
|
+
const value = getThemePreference();
|
|
20255
|
+
if (value === 'system' || value === 'dark') {
|
|
20256
|
+
return value;
|
|
20257
|
+
}
|
|
20258
|
+
return 'light';
|
|
20259
|
+
}
|
|
20260
|
+
getUsersSystemPreferenceForTheme$() {
|
|
20261
|
+
return fromEvent(window.matchMedia('(prefers-color-scheme: dark)'), 'change').pipe(startWith(window.matchMedia('(prefers-color-scheme: dark)')), map((e) => (e.matches ? 'dark' : 'light')));
|
|
20262
|
+
}
|
|
20263
|
+
changeUserPreference(preference) {
|
|
20264
|
+
setThemePreference(preference);
|
|
20265
|
+
this._userSelectedThemePreference$.next(preference);
|
|
20266
|
+
this.applyTheme(preference);
|
|
20267
|
+
}
|
|
20268
|
+
temporaryChangeTheme(preference) {
|
|
20269
|
+
this._temporaryThemePreference$.next(preference);
|
|
20270
|
+
this.applyTheme(preference);
|
|
20271
|
+
}
|
|
20272
|
+
resetTemporaryTheme() {
|
|
20273
|
+
this._temporaryThemePreference$.next('none');
|
|
20274
|
+
this.applyTheme(this.getCurrentThemePreference());
|
|
20275
|
+
}
|
|
20276
|
+
applyTheme(preference) {
|
|
20277
|
+
applyTheme(preference);
|
|
20278
|
+
}
|
|
20279
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThemeSwitcherService, deps: [{ token: OptionsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
20280
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThemeSwitcherService, providedIn: 'root' }); }
|
|
20281
|
+
}
|
|
20282
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThemeSwitcherService, decorators: [{
|
|
20283
|
+
type: Injectable,
|
|
20284
|
+
args: [{
|
|
20285
|
+
providedIn: 'root'
|
|
20286
|
+
}]
|
|
20287
|
+
}], ctorParameters: () => [{ type: OptionsService }] });
|
|
20258
20288
|
|
|
20259
|
-
class
|
|
20260
|
-
constructor(
|
|
20261
|
-
this.
|
|
20262
|
-
this.
|
|
20289
|
+
class UiSettingsComponent {
|
|
20290
|
+
constructor(translate, state, ui, userPreferences, c8yModalService, headerService, themeSwitcher) {
|
|
20291
|
+
this.translate = translate;
|
|
20292
|
+
this.state = state;
|
|
20263
20293
|
this.ui = ui;
|
|
20264
|
-
this.auth = auth;
|
|
20265
|
-
this.client = client;
|
|
20266
|
-
this.alert = alert;
|
|
20267
20294
|
this.userPreferences = userPreferences;
|
|
20268
20295
|
this.c8yModalService = c8yModalService;
|
|
20269
|
-
this.
|
|
20270
|
-
this.
|
|
20271
|
-
this.
|
|
20272
|
-
this.
|
|
20273
|
-
this.
|
|
20274
|
-
|
|
20275
|
-
|
|
20276
|
-
|
|
20277
|
-
this.updateUserInAppState();
|
|
20278
|
-
await this.setInitialProductExperienceOptions();
|
|
20279
|
-
}
|
|
20280
|
-
/**
|
|
20281
|
-
* Initializes product experience options for the user.
|
|
20282
|
-
*
|
|
20283
|
-
* This function performs the following operations:
|
|
20284
|
-
* - Determines if the user has the permission to edit product experience options.
|
|
20285
|
-
* - If the user has the permission and functional cookies are enabled:
|
|
20286
|
-
* - Checks whether personalized product experience tracking is active.
|
|
20287
|
-
* - Checks whether in-product information and communication is active.
|
|
20288
|
-
*/
|
|
20289
|
-
async setInitialProductExperienceOptions() {
|
|
20290
|
-
this.showProductExperienceOptions =
|
|
20291
|
-
await this.gainsightService.canEditProductExperienceSettings();
|
|
20292
|
-
if (this.showProductExperienceOptions && this.cookieBannerService.isFunctionalCookieEnabled()) {
|
|
20293
|
-
// Enable personalized product experience tracking option
|
|
20294
|
-
this.currentUsageTrackingState =
|
|
20295
|
-
!(await this.gainsightService.isGainsightPreferenceDisabledInUserPreferences(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY));
|
|
20296
|
-
// Enable in-product information & communication option
|
|
20297
|
-
this.currentUserEngagementPreferenceInitialState =
|
|
20298
|
-
this.userEngagementsService.userEngagementsEnabled$.value;
|
|
20299
|
-
}
|
|
20300
|
-
}
|
|
20301
|
-
async onDismiss() {
|
|
20302
|
-
this.modal.hide();
|
|
20296
|
+
this.headerService = headerService;
|
|
20297
|
+
this.themeSwitcher = themeSwitcher;
|
|
20298
|
+
this.destroyed$ = new Subject();
|
|
20299
|
+
this.currentLang = this.ui.state.lang;
|
|
20300
|
+
this.ui.state$
|
|
20301
|
+
.pipe(filter(({ lang }) => lang !== this.currentLang), takeUntil(this.destroyed$), first$1())
|
|
20302
|
+
.subscribe(({ lang }) => (this.currentLang = lang));
|
|
20303
|
+
this.open$ = this.headerService.rightDrawerOpen$;
|
|
20303
20304
|
}
|
|
20304
|
-
|
|
20305
|
-
this.
|
|
20305
|
+
ngOnInit() {
|
|
20306
|
+
this.languages = this.state.state.langs.map(l => ({
|
|
20307
|
+
lang: l,
|
|
20308
|
+
nativeLanguage: this.translate.getNativeLanguage(l)
|
|
20309
|
+
}));
|
|
20306
20310
|
}
|
|
20307
|
-
|
|
20308
|
-
this.
|
|
20311
|
+
ngOnDestroy() {
|
|
20312
|
+
this.destroyed$.next();
|
|
20313
|
+
this.destroyed$.complete();
|
|
20309
20314
|
}
|
|
20310
|
-
async
|
|
20311
|
-
|
|
20312
|
-
|
|
20313
|
-
const passwordChanged = Boolean(user.password);
|
|
20314
|
-
const isExternalUser = user.customProperties.userOrigin === 'OAUTH2';
|
|
20315
|
-
if (!isExternalUser && passwordChanged) {
|
|
20316
|
-
const currentPassword = await this.passwordService.currentPassword().toPromise();
|
|
20317
|
-
if (!currentPassword) {
|
|
20318
|
-
return;
|
|
20319
|
-
}
|
|
20320
|
-
await this.user.changeCurrentUserPassword(user.password, currentPassword);
|
|
20321
|
-
this.updateCredentials(user.password);
|
|
20322
|
-
}
|
|
20323
|
-
await this.updateProductExperienceOptions();
|
|
20324
|
-
if (user.customProperties.userOrigin !== 'OAUTH2') {
|
|
20325
|
-
await this.user.updateCurrent(omit(user, 'password'));
|
|
20326
|
-
await this.updateUserInAppState();
|
|
20327
|
-
}
|
|
20328
|
-
this.modal.hide();
|
|
20329
|
-
this.alert.success(gettext$1('User saved.'));
|
|
20330
|
-
}
|
|
20331
|
-
catch (e) {
|
|
20332
|
-
if (e) {
|
|
20333
|
-
this.alert.addServerFailure(e);
|
|
20334
|
-
}
|
|
20315
|
+
async onLanguageChange(changedLang) {
|
|
20316
|
+
if (!changedLang) {
|
|
20317
|
+
return;
|
|
20335
20318
|
}
|
|
20336
|
-
|
|
20337
|
-
|
|
20319
|
+
await this.translate.switchToLanguage(changedLang);
|
|
20320
|
+
if (await this.persistLanguage(changedLang)) {
|
|
20321
|
+
location.reload();
|
|
20338
20322
|
}
|
|
20339
20323
|
}
|
|
20340
|
-
async
|
|
20324
|
+
async persistLanguage(lang) {
|
|
20325
|
+
let shouldReload = true;
|
|
20341
20326
|
try {
|
|
20342
|
-
await this.c8yModalService.confirm(gettext$1('Reload
|
|
20327
|
+
await this.c8yModalService.confirm(gettext$1('Reload recommended'), gettext$1('To change the language in the entire application, we recommend you to reload the page. If you have any unsaved changes, you can reload later. How would you like to proceed?'), Status.WARNING, {
|
|
20343
20328
|
ok: gettext$1('Reload now'),
|
|
20344
20329
|
cancel: gettext$1('Reload later')
|
|
20345
20330
|
});
|
|
20346
|
-
location.reload();
|
|
20347
20331
|
}
|
|
20348
20332
|
catch (ex) {
|
|
20349
|
-
|
|
20333
|
+
shouldReload = false;
|
|
20334
|
+
}
|
|
20335
|
+
finally {
|
|
20336
|
+
this.translate.saveInLocalStorage(lang);
|
|
20337
|
+
await this.userPreferences.set('language', lang);
|
|
20338
|
+
this.currentLang = lang;
|
|
20350
20339
|
}
|
|
20340
|
+
return shouldReload;
|
|
20351
20341
|
}
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20342
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsComponent, deps: [{ token: TranslateService }, { token: AppStateService }, { token: AppStateService }, { token: UserPreferencesService }, { token: ModalService }, { token: HeaderService }, { token: ThemeSwitcherService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20343
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: UiSettingsComponent, isStandalone: true, selector: "c8y-ui-settings", ngImport: i0, template: "<div class=\"separator-top p-t-8 p-b-8\">\n <div class=\"c8y-right-drawer__item sticky-top\">\n <i c8yIcon=\"eyedropper\"></i>\n <span class=\"text-bold\">{{ 'UI settings' | translate }}</span>\n </div>\n\n <div\n class=\"p-l-16 p-r-16 p-b-16\"\n *ngIf=\"themeSwitcher.darkThemeAvailable$ | async\"\n >\n <p translate>Theme</p>\n <div\n class=\"c8y-switch-multistate\"\n *ngIf=\"themeSwitcher.userSelectedThemePreference$ | async as themePreference\"\n >\n <ng-container *ngFor=\"let themeOption of themeSwitcher.themeOptions; index as i\">\n <input\n [attr.aria-label]=\"themeOption.label\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n name=\"theme-switcher\"\n type=\"radio\"\n [id]=\"'theme-option-' + i\"\n [disabled]=\"themeSwitcher.disableThemeSelection$ | async\"\n [checked]=\"themePreference === themeOption.value\"\n (click)=\"themeSwitcher.changeUserPreference(themeOption.value)\"\n />\n <label\n title=\"{{ themeOption.label | translate }}\"\n [for]=\"'theme-option-' + i\"\n >\n <i [c8yIcon]=\"themeOption.icon\"></i>\n </label>\n </ng-container>\n <div class=\"c8y-switch-multistate__handle\"></div>\n </div>\n </div>\n\n <div class=\"form-group p-l-16 p-r-16\">\n <label\n for=\"userLang\"\n translate\n >\n Language\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n #selectLang\n [ngModel]=\"currentLang\"\n (change)=\"onLanguageChange(selectLang.value)\"\n >\n <option\n *ngFor=\"let language of languages\"\n [value]=\"language.lang\"\n >\n {{ language.nativeLanguage }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$8.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$8.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
|
|
20344
|
+
}
|
|
20345
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsComponent, decorators: [{
|
|
20346
|
+
type: Component,
|
|
20347
|
+
args: [{ selector: 'c8y-ui-settings', standalone: true, imports: [
|
|
20348
|
+
IconDirective,
|
|
20349
|
+
NgIf,
|
|
20350
|
+
C8yTranslateDirective,
|
|
20351
|
+
NgFor,
|
|
20352
|
+
FormsModule$1,
|
|
20353
|
+
C8yTranslatePipe,
|
|
20354
|
+
AsyncPipe
|
|
20355
|
+
], template: "<div class=\"separator-top p-t-8 p-b-8\">\n <div class=\"c8y-right-drawer__item sticky-top\">\n <i c8yIcon=\"eyedropper\"></i>\n <span class=\"text-bold\">{{ 'UI settings' | translate }}</span>\n </div>\n\n <div\n class=\"p-l-16 p-r-16 p-b-16\"\n *ngIf=\"themeSwitcher.darkThemeAvailable$ | async\"\n >\n <p translate>Theme</p>\n <div\n class=\"c8y-switch-multistate\"\n *ngIf=\"themeSwitcher.userSelectedThemePreference$ | async as themePreference\"\n >\n <ng-container *ngFor=\"let themeOption of themeSwitcher.themeOptions; index as i\">\n <input\n [attr.aria-label]=\"themeOption.label\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n name=\"theme-switcher\"\n type=\"radio\"\n [id]=\"'theme-option-' + i\"\n [disabled]=\"themeSwitcher.disableThemeSelection$ | async\"\n [checked]=\"themePreference === themeOption.value\"\n (click)=\"themeSwitcher.changeUserPreference(themeOption.value)\"\n />\n <label\n title=\"{{ themeOption.label | translate }}\"\n [for]=\"'theme-option-' + i\"\n >\n <i [c8yIcon]=\"themeOption.icon\"></i>\n </label>\n </ng-container>\n <div class=\"c8y-switch-multistate__handle\"></div>\n </div>\n </div>\n\n <div class=\"form-group p-l-16 p-r-16\">\n <label\n for=\"userLang\"\n translate\n >\n Language\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"userLang\"\n tabindex=\"{{ (open$ | async) ? '0' : '-1' }}\"\n #selectLang\n [ngModel]=\"currentLang\"\n (change)=\"onLanguageChange(selectLang.value)\"\n >\n <option\n *ngFor=\"let language of languages\"\n [value]=\"language.lang\"\n >\n {{ language.nativeLanguage }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n</div>\n" }]
|
|
20356
|
+
}], ctorParameters: () => [{ type: TranslateService }, { type: AppStateService }, { type: AppStateService }, { type: UserPreferencesService }, { type: ModalService }, { type: HeaderService }, { type: ThemeSwitcherService }] });
|
|
20357
|
+
|
|
20358
|
+
class UiSettingsModule {
|
|
20359
|
+
static providers() {
|
|
20360
|
+
return [
|
|
20361
|
+
hookDrawer({
|
|
20362
|
+
component: UiSettingsComponent,
|
|
20363
|
+
position: 'right',
|
|
20364
|
+
priority: 90,
|
|
20365
|
+
id: 'uiSettings'
|
|
20366
|
+
})
|
|
20367
|
+
];
|
|
20355
20368
|
}
|
|
20356
|
-
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
|
|
20360
|
-
|
|
20361
|
-
|
|
20362
|
-
|
|
20363
|
-
|
|
20364
|
-
|
|
20365
|
-
|
|
20369
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
20370
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsModule, imports: [CommonModule, FormsModule$1, UiSettingsComponent], exports: [UiSettingsComponent] }); }
|
|
20371
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsModule, imports: [CommonModule, FormsModule$1, UiSettingsComponent] }); }
|
|
20372
|
+
}
|
|
20373
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UiSettingsModule, decorators: [{
|
|
20374
|
+
type: NgModule,
|
|
20375
|
+
args: [{
|
|
20376
|
+
imports: [CommonModule, FormsModule$1, UiSettingsComponent],
|
|
20377
|
+
exports: [UiSettingsComponent]
|
|
20378
|
+
}]
|
|
20379
|
+
}] });
|
|
20380
|
+
|
|
20381
|
+
class UserMenuItemComponent {
|
|
20382
|
+
constructor(userService, headerService) {
|
|
20383
|
+
this.userService = userService;
|
|
20384
|
+
this.headerService = headerService;
|
|
20385
|
+
this.priority = 0;
|
|
20386
|
+
this.click = new EventEmitter();
|
|
20387
|
+
this.open$ = this.headerService.rightDrawerOpen$;
|
|
20366
20388
|
}
|
|
20367
|
-
|
|
20368
|
-
|
|
20369
|
-
* If the current usage tracking state differs from the new state,
|
|
20370
|
-
* it updates the Gainsight preferences and sets a functional cookie
|
|
20371
|
-
* before triggering a reload of the application.
|
|
20372
|
-
*/
|
|
20373
|
-
async updateTrackingOption() {
|
|
20374
|
-
if (this.currentUsageTrackingState !== this.usageTrackingState) {
|
|
20375
|
-
await this.userPreferences.set(this.gainsightService.USER_PREFERENCES_GAINSIGHT_KEY, this.usageTrackingState);
|
|
20376
|
-
this.gainsightService.setFunctionalCookie(this.usageTrackingState);
|
|
20377
|
-
await this.gainsightTrackingAppReload();
|
|
20378
|
-
}
|
|
20389
|
+
ngAfterViewInit() {
|
|
20390
|
+
this.viewInitTimeout = setTimeout(() => this.userService.add(this));
|
|
20379
20391
|
}
|
|
20380
|
-
|
|
20381
|
-
|
|
20382
|
-
this.
|
|
20392
|
+
ngOnDestroy() {
|
|
20393
|
+
clearTimeout(this.viewInitTimeout);
|
|
20394
|
+
this.userService.remove(this);
|
|
20383
20395
|
}
|
|
20384
|
-
|
|
20385
|
-
|
|
20386
|
-
password,
|
|
20387
|
-
user: this.ui.currentUser.value.id,
|
|
20388
|
-
tenant: this.client.tenant
|
|
20389
|
-
};
|
|
20390
|
-
this.auth.updateCredentials(newCredentials);
|
|
20396
|
+
onClick() {
|
|
20397
|
+
this.click.emit(this);
|
|
20391
20398
|
}
|
|
20392
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
20393
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type:
|
|
20399
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserMenuItemComponent, deps: [{ token: UserMenuService }, { token: HeaderService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20400
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: UserMenuItemComponent, isStandalone: true, selector: "c8y-user-menu-item", inputs: { icon: "icon", label: "label", link: "link", target: "target", priority: "priority", dataCy: "dataCy" }, outputs: { click: "click" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["template"], descendants: true }], ngImport: i0, template: "<ng-template #template>\n <li>\n <a\n class=\"c8y-right-drawer__link\"\n [attr.tabindex]=\"(open$ | async) ? '0' : '-1'\"\n (click)=\"onClick()\"\n [attr.data-cy]=\"dataCy\"\n *ngIf=\"link\"\n [attr.href]=\"link\"\n [attr.target]=\"target\"\n >\n {{ label | translate }}\n <ng-content></ng-content>\n </a>\n <button\n class=\"c8y-right-drawer__link\"\n [attr.tabindex]=\"(open$ | async) ? '0' : '-1'\"\n type=\"button\"\n *ngIf=\"!link\"\n (click)=\"onClick()\"\n [attr.data-cy]=\"dataCy\"\n >\n {{ label | translate }}\n <ng-content></ng-content>\n </button>\n </li>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
|
|
20394
20401
|
}
|
|
20395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
20402
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UserMenuItemComponent, decorators: [{
|
|
20396
20403
|
type: Component,
|
|
20397
|
-
args: [{ selector: 'c8y-user-
|
|
20398
|
-
}], ctorParameters: () => [{ type:
|
|
20404
|
+
args: [{ selector: 'c8y-user-menu-item', standalone: true, imports: [NgIf, C8yTranslatePipe, AsyncPipe], template: "<ng-template #template>\n <li>\n <a\n class=\"c8y-right-drawer__link\"\n [attr.tabindex]=\"(open$ | async) ? '0' : '-1'\"\n (click)=\"onClick()\"\n [attr.data-cy]=\"dataCy\"\n *ngIf=\"link\"\n [attr.href]=\"link\"\n [attr.target]=\"target\"\n >\n {{ label | translate }}\n <ng-content></ng-content>\n </a>\n <button\n class=\"c8y-right-drawer__link\"\n [attr.tabindex]=\"(open$ | async) ? '0' : '-1'\"\n type=\"button\"\n *ngIf=\"!link\"\n (click)=\"onClick()\"\n [attr.data-cy]=\"dataCy\"\n >\n {{ label | translate }}\n <ng-content></ng-content>\n </button>\n </li>\n</ng-template>\n" }]
|
|
20405
|
+
}], ctorParameters: () => [{ type: UserMenuService }, { type: HeaderService }], propDecorators: { icon: [{
|
|
20406
|
+
type: Input
|
|
20407
|
+
}], label: [{
|
|
20408
|
+
type: Input
|
|
20409
|
+
}], link: [{
|
|
20410
|
+
type: Input
|
|
20411
|
+
}], target: [{
|
|
20412
|
+
type: Input
|
|
20413
|
+
}], priority: [{
|
|
20414
|
+
type: Input
|
|
20415
|
+
}], dataCy: [{
|
|
20416
|
+
type: Input
|
|
20417
|
+
}], template: [{
|
|
20418
|
+
type: ViewChild,
|
|
20419
|
+
args: ['template', { static: false }]
|
|
20420
|
+
}], click: [{
|
|
20421
|
+
type: Output
|
|
20422
|
+
}] } });
|
|
20399
20423
|
|
|
20400
20424
|
class UserMenuOutletComponent {
|
|
20401
20425
|
constructor(ui, bsModalService, authService, userMenu, headerService) {
|
|
@@ -22999,7 +23023,7 @@ class FilePickerComponent {
|
|
|
22999
23023
|
this.onFilesPicked.emit(this.fileToSave);
|
|
23000
23024
|
}
|
|
23001
23025
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
23002
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FilePickerComponent, isStandalone: true, selector: "c8y-file-picker", inputs: { maxAllowedFiles: "maxAllowedFiles", uploadChoice: "uploadChoice", allowedUploadChoices: "allowedUploadChoices", fileUrl: "fileUrl", fileBinary: "fileBinary", config: "config", filePickerIndex: "filePickerIndex", fileUrlPopover: "fileUrlPopover" }, outputs: { onFilesPicked: "onFilesPicked" }, viewQueries: [{ propertyName: "dropArea", first: true, predicate: DropAreaComponent, descendants: true, static: true }], ngImport: i0, template: "<div class=\"form-group\">\n <label\n class=\"c8y-radio\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearInputFromUrl()\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n data-cy=\"file-picker--file-path-input\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearSelectedFiles()\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ fileUrlPopover | translate }}\"\n placement=\"top\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"isPopoverUsed()\"\n ></button>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Mark as provided' | translate }}\"\n *ngIf=\"allowedUploadChoices.includes('provided')\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"provided\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"setProvidedOption()\"\n />\n <span></span>\n <span>{{ 'Provided' | translate }}</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ providedPopover | translate }}\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [attr.aria-label]=\"'Drop file or click to browse' | translate\"\n (dropped)=\"onFileDropped($event)\"\n [maxAllowedFiles]=\"maxAllowedFiles\"\n [files]=\"droppedFiles\"\n ></c8y-drop-area>\n </c8y-form-group>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n name=\"fileUrl\"\n type=\"text\"\n required\n data-cy=\"file-picker--fileUrl\"\n [(ngModel)]=\"fileUrl\"\n (ngModelChange)=\"onFileUrlChange($event)\"\n maxlength=\"{{ config.maxlength }}\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </div>\n </div>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$8.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1$9.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
23026
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FilePickerComponent, isStandalone: true, selector: "c8y-file-picker", inputs: { maxAllowedFiles: "maxAllowedFiles", uploadChoice: "uploadChoice", allowedUploadChoices: "allowedUploadChoices", fileUrl: "fileUrl", fileBinary: "fileBinary", config: "config", filePickerIndex: "filePickerIndex", fileUrlPopover: "fileUrlPopover" }, outputs: { onFilesPicked: "onFilesPicked" }, viewQueries: [{ propertyName: "dropArea", first: true, predicate: DropAreaComponent, descendants: true, static: true }], ngImport: i0, template: "<div class=\"form-group\">\n <label\n class=\"c8y-radio\"\n [class.m-l-8]=\"allowedUploadChoices.includes('provided')\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearInputFromUrl()\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n data-cy=\"file-picker--file-path-input\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearSelectedFiles()\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ fileUrlPopover | translate }}\"\n placement=\"top\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"isPopoverUsed()\"\n ></button>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Mark as provided' | translate }}\"\n *ngIf=\"allowedUploadChoices.includes('provided')\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"provided\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"setProvidedOption()\"\n />\n <span></span>\n <span>{{ 'Provided' | translate }}</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ providedPopover | translate }}\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [attr.aria-label]=\"'Drop file or click to browse' | translate\"\n (dropped)=\"onFileDropped($event)\"\n [maxAllowedFiles]=\"maxAllowedFiles\"\n [files]=\"droppedFiles\"\n ></c8y-drop-area>\n </c8y-form-group>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n name=\"fileUrl\"\n type=\"text\"\n required\n data-cy=\"file-picker--fileUrl\"\n [(ngModel)]=\"fileUrl\"\n (ngModelChange)=\"onFileUrlChange($event)\"\n maxlength=\"{{ config.maxlength }}\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </div>\n </div>\n </c8y-form-group>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i1$8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$8.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$8.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$8.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1$8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1$9.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
23003
23027
|
}
|
|
23004
23028
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FilePickerComponent, decorators: [{
|
|
23005
23029
|
type: Component,
|
|
@@ -23012,7 +23036,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
23012
23036
|
IconDirective,
|
|
23013
23037
|
RequiredInputPlaceholderDirective,
|
|
23014
23038
|
C8yTranslatePipe
|
|
23015
|
-
], template: "<div class=\"form-group\">\n <label\n class=\"c8y-radio\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearInputFromUrl()\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n data-cy=\"file-picker--file-path-input\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearSelectedFiles()\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ fileUrlPopover | translate }}\"\n placement=\"top\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"isPopoverUsed()\"\n ></button>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Mark as provided' | translate }}\"\n *ngIf=\"allowedUploadChoices.includes('provided')\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"provided\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"setProvidedOption()\"\n />\n <span></span>\n <span>{{ 'Provided' | translate }}</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ providedPopover | translate }}\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [attr.aria-label]=\"'Drop file or click to browse' | translate\"\n (dropped)=\"onFileDropped($event)\"\n [maxAllowedFiles]=\"maxAllowedFiles\"\n [files]=\"droppedFiles\"\n ></c8y-drop-area>\n </c8y-form-group>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n name=\"fileUrl\"\n type=\"text\"\n required\n data-cy=\"file-picker--fileUrl\"\n [(ngModel)]=\"fileUrl\"\n (ngModelChange)=\"onFileUrlChange($event)\"\n maxlength=\"{{ config.maxlength }}\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </div>\n </div>\n </c8y-form-group>\n</div>\n" }]
|
|
23039
|
+
], template: "<div class=\"form-group\">\n <label\n class=\"c8y-radio\"\n [class.m-l-8]=\"allowedUploadChoices.includes('provided')\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearInputFromUrl()\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n data-cy=\"file-picker--file-path-input\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"clearSelectedFiles()\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ fileUrlPopover | translate }}\"\n placement=\"top\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n *ngIf=\"isPopoverUsed()\"\n ></button>\n </label>\n <label\n class=\"c8y-radio m-l-8\"\n title=\"{{ 'Mark as provided' | translate }}\"\n *ngIf=\"allowedUploadChoices.includes('provided')\"\n >\n <input\n name=\"uploadChoice-{{ filePickerIndex }}\"\n type=\"radio\"\n value=\"provided\"\n #radio\n [(ngModel)]=\"uploadChoice\"\n (click)=\"setProvidedOption()\"\n />\n <span></span>\n <span>{{ 'Provided' | translate }}</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ providedPopover | translate }}\"\n placement=\"top\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [attr.aria-label]=\"'Drop file or click to browse' | translate\"\n (dropped)=\"onFileDropped($event)\"\n [maxAllowedFiles]=\"maxAllowedFiles\"\n [files]=\"droppedFiles\"\n ></c8y-drop-area>\n </c8y-form-group>\n</div>\n\n<div [hidden]=\"uploadChoice !== 'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n name=\"fileUrl\"\n type=\"text\"\n required\n data-cy=\"file-picker--fileUrl\"\n [(ngModel)]=\"fileUrl\"\n (ngModelChange)=\"onFileUrlChange($event)\"\n maxlength=\"{{ config.maxlength }}\"\n [pattern]=\"ValidationPattern.rules.noWhiteSpaceOnly.pattern\"\n />\n </div>\n </div>\n </c8y-form-group>\n</div>\n" }]
|
|
23016
23040
|
}], propDecorators: { dropArea: [{
|
|
23017
23041
|
type: ViewChild,
|
|
23018
23042
|
args: [DropAreaComponent, { static: true }]
|