UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

335 lines 88.2 kB
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core'; import { gettext, DropAreaComponent, ModalService, Status, AlertService } from '@c8y/ngx-components'; import { cloneDeep, has } from 'lodash-es'; import { OpcuaService } from './opcuaService'; import * as i0 from "@angular/core"; import * as i1 from "./opcuaService"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "@angular/common"; import * as i4 from "@angular/forms"; import * as i5 from "ngx-bootstrap/buttons"; export class OpcuaServerConfigComponent { set server(server) { this._server = cloneDeep(server); if (server) { this.model = cloneDeep(server); this.fileName = this.model.config.keystoreFilename; if (server.id && server.id === 'new') { // enabled connection state this.targetConnectionState = '1'; this.model.config.targetConnectionState = 'enabled'; } else { this.targetConnectionState = this.model.config.targetConnectionState === 'enabled' ? '1' : '0'; } this.updateConnectionStatusLabel(this._server); this.setNewPassword(); } } get server() { return this._server; } constructor(opcuaService, modalService, alertService) { this.modalService = modalService; this.alertService = alertService; this.fileName = ''; this.targetConnectionState = '1'; this.minIntervalNumber = 1; this.connectionStatusLabel = ''; this.canceled = new EventEmitter(); this.removed = new EventEmitter(); this.saved = new EventEmitter(); this.changePassword = false; this.initialPasswordRequired = true; this.NONE = 'NONE'; this.SIGN = 'SIGN'; this.SIGN_ENC = 'SIGN_ENCRYPT'; this.securityPolicies = { sign: [`BASIC256_${this.SIGN}`, `BASIC128RSA15_${this.SIGN}`, `BASIC256SHA256_${this.SIGN}`], sign_enc: [ `BASIC256_${this.SIGN_ENC}`, `BASIC128RSA15_${this.SIGN_ENC}`, `BASIC256SHA256_${this.SIGN_ENC}` ] }; this.ANONYM = { id: 1, value: gettext('Anonymous') }; this.USER_PASSWORD = { id: 2, value: gettext('Username/Password') }; this.KEY_BASED = { id: 3, value: gettext('Key-based Authentication') }; this.initialKeystore = { lastModified: 0, name: '', type: '', slice: null, size: 0 }; this.keystore = this.initialKeystore; this.authSwitch = false; this.opcuaService = opcuaService; } async ngOnInit() { this.authSwitch = false; this.securityModes = [this.NONE, this.SIGN, this.SIGN_ENC]; this.authenticationModes = [this.ANONYM, this.USER_PASSWORD, this.KEY_BASED]; this.setCurrentAuthenticationMode(); this.setCurrentSecurityMode(); } ngOnChanges() { this.setCurrentSecurityMode(); this.setCurrentAuthenticationMode(); } cancel() { this.canceled.emit(this.model); this._server = null; } async remove() { try { await this.modalService.confirm(gettext('Delete server'), gettext("You're about to delete server. Do you want to proceed?"), Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') }); await this.removeKeystore(this.model); this.removed.emit(this.model); this._server = null; } catch (ex) { if (ex) { this.alertService.addServerFailure(ex); } } } async save() { if (this.keystore && this.keystore.size > 0 && this.keystore.name && this.keystore.name.length > 0) { const response = await this.uploadKeystore(this.model.config.keystoreBinaryId); if (response && response.data && response.data.id) { this.model.config.keystoreBinaryId = response.data.id; } // if the keystore was uploaded successful we can remove // the local keystore. This will prevent another request to binary api // when the user will edit other inputs in the form and hit save again. this.keystore = this.initialKeystore; } // will remove keystore (binary) when the user switched // authentication settings from key-based to anonymous or username/password if (this.authSwitch) { this.removeKeystore(this.server); } // when the user sets a new password, make sure to mark it as // "not encrypted" by setting passwordEncrypted to false const userPassword = this.getModelConfig('userPassword'); if (userPassword && userPassword.length > 0) { this.model.config.passwordEncrypted = false; } this.saved.emit(this.model); } uploadFile(droppedFiles) { if (droppedFiles.length === 1) { this.keystore = droppedFiles[0].file; this.fileName = this.keystore.name; } else { // dropped more than one file console.warn('Tried to import... Import aborted.'); } } setPolicy(data) { if (data === this.NONE) { this.model.config.securityMode = this.NONE; } else if (data === this.SIGN) { this.model.config.securityMode = this.securityPolicies.sign[0]; } else if (data === this.SIGN_ENC) { this.model.config.securityMode = this.securityPolicies.sign_enc[0]; } } setServerConnection(data) { this.model.config.targetConnectionState = data !== '0' ? 'enabled' : 'disabled'; } updateAuthentication(data) { if (data && data.id) { switch (data.id) { // Anonymous case 1: this.resetUserAuthentication(); this.resetKeyBasedAuthentication(); break; // User/Password case 2: this.resetKeyBasedAuthentication(); this.restoreUserData(); this.setNewPassword(); break; // Key-based case 3: this.resetUserAuthentication(); this.restoreKeyBasedData(); break; default: console.warn('Invalid authentication id', data.id); break; } } } updateConnectionStatusLabel(server) { const connected = server.c8y_Connection && server.c8y_Connection.status === 'CONNECTED'; const label = connected ? gettext('Connected') : gettext('Disconnected'); this.connectionStatusLabel = label; } setNewPassword() { const username = this.getModelConfig('userName'); if (username && username.length > 0) { // userName is given, NO need to change the password because it is already set this.changePassword = false; this.initialPasswordRequired = false; } else { // no userName in response, so require the user to set the initial pw this.changePassword = true; this.initialPasswordRequired = true; } } toggleChangePassword() { this.changePassword = !this.changePassword; // When the user hides the pw-input field but has entered a // string to it before, we need to discard the changes reflected in the model // otherwise we PUT it with the model when user hits the save button if (!this.changePassword) { if (this.getModelConfig('userPassword')) { delete this.model.config.userPassword; } } } uploadKeystore(binaryId) { if (!binaryId) { return this.opcuaService.uploadKeystore(this.keystore); } else if (binaryId && binaryId.length > 0) { // update existing binary return this.opcuaService.updateKeystore(binaryId, this.keystore); } } removeKeystore(server) { if (server && server.config && server.config.keystoreBinaryId && server.config.keystoreBinaryId.length > 0) { this.authSwitch = false; return this.opcuaService.removeKeystore(this.server.config.keystoreBinaryId); } } resetUserAuthentication() { this.model.config.userName = null; this.model.config.userPassword = null; this.model.config.userIdentityMode = 'none'; } resetKeyBasedAuthentication() { this.authSwitch = true; this.model.config.keystorePass = null; this.model.config.certificatePass = null; this.model.config.keystoreBinaryId = ''; this.model.config.keystoreFilename = ''; this.model.config.userIdentityMode = 'none'; } restoreUserData() { this.model.config.userName = this._server.config.userName; this.model.config.userIdentityMode = 'userAndPassword'; } restoreKeyBasedData() { this.authSwitch = false; this.model.config.keystorePass = this._server.config.keystorePass; this.model.config.certificatePass = this._server.config.certificatePass; this.model.config.keystoreBinaryId = this._server.config.keystoreBinaryId; this.model.config.keystoreFilename = this._server.config.keystoreFilename; this.model.config.userIdentityMode = 'certificate'; } getServerConfig() { let cfg = { securityMode: this.NONE, userIdentityMode: 'none' }; if (this.server && this.server.config) { cfg = this.server.config; } return cfg; } setCurrentSecurityMode() { const { securityMode } = this.getServerConfig(); if (securityMode) { const foundInSign = this.securityPolicies.sign.find(el => el === securityMode); if (foundInSign) { this.currentSecMode = this.SIGN; } else { const foundInSignEncrypt = this.securityPolicies.sign_enc.find(el => el === securityMode); foundInSignEncrypt ? (this.currentSecMode = this.SIGN_ENC) : (this.currentSecMode = this.NONE); } } } setCurrentAuthenticationMode() { const { userIdentityMode } = this.getServerConfig(); switch (userIdentityMode) { case 'certificate': this.authenticationMode = this.KEY_BASED; break; case 'userAndPassword': this.authenticationMode = this.USER_PASSWORD; break; case 'Anonymous': this.authenticationMode = this.ANONYM; break; case 'UserName': this.authenticationMode = this.USER_PASSWORD; break; case 'Certificate': this.authenticationMode = this.KEY_BASED; break; default: this.authenticationMode = this.ANONYM; break; } } getModelConfig(fragment) { if (this.model && this.model.config) { if (fragment && fragment.length > 0) { return has(this.model.config, fragment) ? this.model.config[`${fragment}`] : undefined; } } return undefined; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaServerConfigComponent, deps: [{ token: i1.OpcuaService }, { token: i2.ModalService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: OpcuaServerConfigComponent, selector: "opcua-server-config", inputs: { server: "server" }, outputs: { canceled: "canceled", removed: "removed", saved: "saved" }, viewQueries: [{ propertyName: "opcuaConfigForm", first: true, predicate: ["opcuaConfigForm"], descendants: true }, { propertyName: "dropArea", first: true, predicate: DropAreaComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"c8y-empty-state m-t-4\" *ngIf=\"!server\">\n <h1 class=\"c8y-icon c8y-icon-duocolor\" c8yIcon=\"server\"></h1>\n <div>\n <p class=\"text-medium\">{{ 'No server to display.' | translate }}</p>\n <p>{{ 'Add or select a server.' | translate }}</p>\n </div>\n</div>\n\n<form #opcuaConfigForm=\"ngForm\" class=\"d-contents\" *ngIf=\"server\">\n <div class=\"card-header large-padding separator sticky-top visible-sm visible-xs\">\n <button\n class=\"btn btn-clean text-primary visible-sm visible-xs\"\n title=\"{{ 'Back' | translate }}\"\n (click)=\"cancel()\"\n >\n <i c8yIcon=\"chevron-left\"></i>\n {{ 'Back' | translate }}\n </button>\n </div>\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <!-- SERVER NAME -->\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Server name' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My server' | translate }}\"\n id=\"name\"\n name=\"name\"\n [(ngModel)]=\"model.name\"\n required\n />\n <c8y-messages>\n <c8y-message\n name=\"required\"\n text=\"{{ 'Server name is required' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"card-block large-padding bg-level-2\">\n <div class=\"tight-grid\">\n <div class=\"col-sm-6\">\n <label class=\"fit-w\" translate>Server connection</label>\n <button\n type=\"button\"\n class=\"btn m-t-4\"\n name=\"serverConnection\"\n [(ngModel)]=\"targetConnectionState\"\n (ngModelChange)=\"setServerConnection($event)\"\n btnCheckbox\n btnCheckboxTrue=\"1\"\n btnCheckboxFalse=\"0\"\n >\n <span title=\"{{ 'Enabled' | translate }}\" [hidden]=\"targetConnectionState !== '1'\">\n {{ 'Enabled' | translate }}\n </span>\n <span title=\"{{ 'Disabled' | translate }}\" [hidden]=\"targetConnectionState !== '0'\">\n {{ 'Disabled' | translate }}\n </span>\n </button>\n </div>\n <div class=\"col-sm-6\">\n <label translate>Connection status</label>\n <div class=\"form-control-static\">\n <device-status class=\"p-r-8\" [mo]=\"server\"></device-status>\n <span>{{ connectionStatusLabel | translate }}</span>\n </div>\n </div>\n </div>\n </div>\n <div class=\"card-block large-padding\">\n <!-- SERVER URL-->\n <c8y-form-group>\n <label for=\"configServerUrl\" translate>Server URL</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"configServerUrl\"\n name=\"serverUrl\"\n [(ngModel)]=\"model.config.serverUrl\"\n c8yDefaultValidation=\"opcuaBrowsePath\"\n required\n />\n </c8y-form-group>\n\n <!-- TIMEOUT & STATUS-CHECK-INTERVAL-->\n <div class=\"content-flex-32\">\n <div class=\"col-6\">\n <c8y-form-group>\n <label for=\"config.timeout\" translate>Timeout</label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"config.timeout\"\n name=\"timeout\"\n [min]=\"minIntervalNumber\"\n placeholder=\"{{ 'e.g.' | translate }} 30\"\n [(ngModel)]=\"model.config.timeout\"\n required\n />\n <span class=\"input-group-addon units\" translate>seconds</span>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-form-group>\n <label for=\"config.statusCheckInterval\" translate>Status check interval</label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"config.statusCheckInterval\"\n name=\"statusCheckInterval\"\n [min]=\"minIntervalNumber\"\n placeholder=\"{{ 'e.g.' | translate }} 40\"\n [(ngModel)]=\"model.config.statusCheckInterval\"\n required\n />\n <span class=\"input-group-addon units\" translate>seconds</span>\n </div>\n </c8y-form-group>\n </div>\n </div>\n\n <!-- SECURITY MODE -->\n <div class=\"tight-grid\">\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <!-- NONE, SIGN, SIGN & ENCRYPT-->\n <label for=\"config.securityMode\" translate>Security mode</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"config.securityMode\"\n [(ngModel)]=\"currentSecMode\"\n (ngModelChange)=\"setPolicy($event)\"\n name=\"securityMode\"\n required\n >\n <option *ngFor=\"let mode of securityModes\" [ngValue]=\"mode\">{{ mode }}</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div *ngIf=\"currentSecMode === NONE\" class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.securityPolicy\" translate>Security policy</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n name=\"securityPolicy\"\n [readonly]=\"true\"\n [(ngModel)]=\"model.config.securityMode\"\n required\n />\n </div>\n </div>\n <div *ngIf=\"currentSecMode !== NONE\" class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.securityPolicy\" translate>Security policy</label>\n <div class=\"c8y-select-wrapper\">\n <select\n *ngIf=\"currentSecMode === SIGN\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n [(ngModel)]=\"model.config.securityMode\"\n name=\"securityPolicy\"\n required\n >\n <option *ngFor=\"let policy of securityPolicies.sign\" [ngValue]=\"policy\">\n {{ policy }}\n </option>\n </select>\n <select\n *ngIf=\"currentSecMode === SIGN_ENC\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n [(ngModel)]=\"model.config.securityMode\"\n name=\"securityPolicy\"\n required\n >\n <option *ngFor=\"let policy of securityPolicies.sign_enc\" [ngValue]=\"policy\">\n {{ policy }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- AUTHENTICATION -->\n <div class=\"form-group\">\n <label for=\"config.authenticationMode\" translate>Authentication</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"config.authenticationMode\"\n [(ngModel)]=\"authenticationMode\"\n name=\"authenticationMode\"\n (ngModelChange)=\"updateAuthentication($event)\"\n required\n >\n <option *ngFor=\"let auth of authenticationModes\" [ngValue]=\"auth\">\n {{ auth.value | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n\n <!-- User/Pw-->\n <div *ngIf=\"authenticationMode.id === 2\" class=\"tight-grid\">\n <div class=\"col-md-12\">\n <div class=\"form-group\">\n <label for=\"config.userName\" translate>Username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"config.userName\"\n name=\"userName\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"model.config.userName\"\n autocomplete=\"new-password\"\n required\n />\n </div>\n </div>\n <!-- change password section BEGINS-->\n <div class=\"col-md-6\">\n <div class=\"form-group\" *ngIf=\"!initialPasswordRequired\">\n <button type=\"button\" class=\"btn btn-default\" (click)=\"toggleChangePassword()\">\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 class=\"form-group\">\n <div *ngIf=\"changePassword\">\n <label for=\"config.password\" translate>Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.userPassword\"\n name=\"password\"\n [(ngModel)]=\"model.config.userPassword\"\n autocomplete=\"new-password\"\n required\n />\n </div>\n </div>\n </div>\n <!-- change password section ENDS-->\n </div>\n <!-- Key-based -->\n <div *ngIf=\"authenticationMode.id === 3\" class=\"tight-grid\">\n <!-- KEYSTORE PASSWORD -->\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.keystorePass\" translate>Keystore password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.keystorePass\"\n name=\"keystorePass\"\n [(ngModel)]=\"model.config.keystorePass\"\n required\n />\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.certificatePass\" translate>Certificate password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.certificatePass\"\n name=\"keystorePass\"\n [(ngModel)]=\"model.config.certificatePass\"\n required\n />\n </div>\n </div>\n <!-- UPLOAD KEYSTORE -->\n <div class=\"col-md-12\">\n <div class=\"form-group\">\n <label for=\"certificateUpload\" translate>Upload keystore</label>\n <input\n type=\"text\"\n [readonly]=\"true\"\n name=\"certificateUpload\"\n class=\"form-control m-b-8\"\n [ngModel]=\"fileName\"\n placeholder=\"{{ 'e.g.' | translate }} yourKeystore.jks\"\n required\n />\n <c8y-drop-area\n (dropped)=\"uploadFile($event)\"\n [loadingMessage]=\"'Importing, please wait.' | translate\"\n [title]=\"'Import keystore with jks file extension' | translate\"\n ></c8y-drop-area>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"card-footer large-padding separator sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n class=\"btn btn-default\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n class=\"btn btn-danger\"\n (click)=\"remove()\"\n >\n {{ 'Remove' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n class=\"btn btn-primary\"\n (click)=\"save()\"\n [disabled]=\"!opcuaConfigForm.valid\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.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: i2.DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: i2.MinValidationDirective, selector: "[min]", inputs: ["min"] }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.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: i4.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.DeviceStatusComponent, selector: "device-status, c8y-device-status", inputs: ["mo", "size"] }, { kind: "directive", type: i5.ButtonCheckboxDirective, selector: "[btnCheckbox]", inputs: ["btnCheckboxTrue", "btnCheckboxFalse"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OpcuaServerConfigComponent, decorators: [{ type: Component, args: [{ selector: 'opcua-server-config', template: "<div class=\"c8y-empty-state m-t-4\" *ngIf=\"!server\">\n <h1 class=\"c8y-icon c8y-icon-duocolor\" c8yIcon=\"server\"></h1>\n <div>\n <p class=\"text-medium\">{{ 'No server to display.' | translate }}</p>\n <p>{{ 'Add or select a server.' | translate }}</p>\n </div>\n</div>\n\n<form #opcuaConfigForm=\"ngForm\" class=\"d-contents\" *ngIf=\"server\">\n <div class=\"card-header large-padding separator sticky-top visible-sm visible-xs\">\n <button\n class=\"btn btn-clean text-primary visible-sm visible-xs\"\n title=\"{{ 'Back' | translate }}\"\n (click)=\"cancel()\"\n >\n <i c8yIcon=\"chevron-left\"></i>\n {{ 'Back' | translate }}\n </button>\n </div>\n <div class=\"flex-grow\">\n <div class=\"card-block large-padding\">\n <!-- SERVER NAME -->\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Server name' | translate }}\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My server' | translate }}\"\n id=\"name\"\n name=\"name\"\n [(ngModel)]=\"model.name\"\n required\n />\n <c8y-messages>\n <c8y-message\n name=\"required\"\n text=\"{{ 'Server name is required' | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n\n <div class=\"card-block large-padding bg-level-2\">\n <div class=\"tight-grid\">\n <div class=\"col-sm-6\">\n <label class=\"fit-w\" translate>Server connection</label>\n <button\n type=\"button\"\n class=\"btn m-t-4\"\n name=\"serverConnection\"\n [(ngModel)]=\"targetConnectionState\"\n (ngModelChange)=\"setServerConnection($event)\"\n btnCheckbox\n btnCheckboxTrue=\"1\"\n btnCheckboxFalse=\"0\"\n >\n <span title=\"{{ 'Enabled' | translate }}\" [hidden]=\"targetConnectionState !== '1'\">\n {{ 'Enabled' | translate }}\n </span>\n <span title=\"{{ 'Disabled' | translate }}\" [hidden]=\"targetConnectionState !== '0'\">\n {{ 'Disabled' | translate }}\n </span>\n </button>\n </div>\n <div class=\"col-sm-6\">\n <label translate>Connection status</label>\n <div class=\"form-control-static\">\n <device-status class=\"p-r-8\" [mo]=\"server\"></device-status>\n <span>{{ connectionStatusLabel | translate }}</span>\n </div>\n </div>\n </div>\n </div>\n <div class=\"card-block large-padding\">\n <!-- SERVER URL-->\n <c8y-form-group>\n <label for=\"configServerUrl\" translate>Server URL</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"configServerUrl\"\n name=\"serverUrl\"\n [(ngModel)]=\"model.config.serverUrl\"\n c8yDefaultValidation=\"opcuaBrowsePath\"\n required\n />\n </c8y-form-group>\n\n <!-- TIMEOUT & STATUS-CHECK-INTERVAL-->\n <div class=\"content-flex-32\">\n <div class=\"col-6\">\n <c8y-form-group>\n <label for=\"config.timeout\" translate>Timeout</label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"config.timeout\"\n name=\"timeout\"\n [min]=\"minIntervalNumber\"\n placeholder=\"{{ 'e.g.' | translate }} 30\"\n [(ngModel)]=\"model.config.timeout\"\n required\n />\n <span class=\"input-group-addon units\" translate>seconds</span>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-6\">\n <c8y-form-group>\n <label for=\"config.statusCheckInterval\" translate>Status check interval</label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"config.statusCheckInterval\"\n name=\"statusCheckInterval\"\n [min]=\"minIntervalNumber\"\n placeholder=\"{{ 'e.g.' | translate }} 40\"\n [(ngModel)]=\"model.config.statusCheckInterval\"\n required\n />\n <span class=\"input-group-addon units\" translate>seconds</span>\n </div>\n </c8y-form-group>\n </div>\n </div>\n\n <!-- SECURITY MODE -->\n <div class=\"tight-grid\">\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <!-- NONE, SIGN, SIGN & ENCRYPT-->\n <label for=\"config.securityMode\" translate>Security mode</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"config.securityMode\"\n [(ngModel)]=\"currentSecMode\"\n (ngModelChange)=\"setPolicy($event)\"\n name=\"securityMode\"\n required\n >\n <option *ngFor=\"let mode of securityModes\" [ngValue]=\"mode\">{{ mode }}</option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n <div *ngIf=\"currentSecMode === NONE\" class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.securityPolicy\" translate>Security policy</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n name=\"securityPolicy\"\n [readonly]=\"true\"\n [(ngModel)]=\"model.config.securityMode\"\n required\n />\n </div>\n </div>\n <div *ngIf=\"currentSecMode !== NONE\" class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.securityPolicy\" translate>Security policy</label>\n <div class=\"c8y-select-wrapper\">\n <select\n *ngIf=\"currentSecMode === SIGN\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n [(ngModel)]=\"model.config.securityMode\"\n name=\"securityPolicy\"\n required\n >\n <option *ngFor=\"let policy of securityPolicies.sign\" [ngValue]=\"policy\">\n {{ policy }}\n </option>\n </select>\n <select\n *ngIf=\"currentSecMode === SIGN_ENC\"\n class=\"form-control\"\n id=\"config.securityPolicy\"\n [(ngModel)]=\"model.config.securityMode\"\n name=\"securityPolicy\"\n required\n >\n <option *ngFor=\"let policy of securityPolicies.sign_enc\" [ngValue]=\"policy\">\n {{ policy }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- AUTHENTICATION -->\n <div class=\"form-group\">\n <label for=\"config.authenticationMode\" translate>Authentication</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"config.authenticationMode\"\n [(ngModel)]=\"authenticationMode\"\n name=\"authenticationMode\"\n (ngModelChange)=\"updateAuthentication($event)\"\n required\n >\n <option *ngFor=\"let auth of authenticationModes\" [ngValue]=\"auth\">\n {{ auth.value | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n\n <!-- User/Pw-->\n <div *ngIf=\"authenticationMode.id === 2\" class=\"tight-grid\">\n <div class=\"col-md-12\">\n <div class=\"form-group\">\n <label for=\"config.userName\" translate>Username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"config.userName\"\n name=\"userName\"\n placeholder=\"{{ 'e.g. joe.doe`LOCALIZE`' | translate }}\"\n [(ngModel)]=\"model.config.userName\"\n autocomplete=\"new-password\"\n required\n />\n </div>\n </div>\n <!-- change password section BEGINS-->\n <div class=\"col-md-6\">\n <div class=\"form-group\" *ngIf=\"!initialPasswordRequired\">\n <button type=\"button\" class=\"btn btn-default\" (click)=\"toggleChangePassword()\">\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 class=\"form-group\">\n <div *ngIf=\"changePassword\">\n <label for=\"config.password\" translate>Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.userPassword\"\n name=\"password\"\n [(ngModel)]=\"model.config.userPassword\"\n autocomplete=\"new-password\"\n required\n />\n </div>\n </div>\n </div>\n <!-- change password section ENDS-->\n </div>\n <!-- Key-based -->\n <div *ngIf=\"authenticationMode.id === 3\" class=\"tight-grid\">\n <!-- KEYSTORE PASSWORD -->\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.keystorePass\" translate>Keystore password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.keystorePass\"\n name=\"keystorePass\"\n [(ngModel)]=\"model.config.keystorePass\"\n required\n />\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-group\">\n <label for=\"config.certificatePass\" translate>Certificate password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n id=\"config.certificatePass\"\n name=\"keystorePass\"\n [(ngModel)]=\"model.config.certificatePass\"\n required\n />\n </div>\n </div>\n <!-- UPLOAD KEYSTORE -->\n <div class=\"col-md-12\">\n <div class=\"form-group\">\n <label for=\"certificateUpload\" translate>Upload keystore</label>\n <input\n type=\"text\"\n [readonly]=\"true\"\n name=\"certificateUpload\"\n class=\"form-control m-b-8\"\n [ngModel]=\"fileName\"\n placeholder=\"{{ 'e.g.' | translate }} yourKeystore.jks\"\n required\n />\n <c8y-drop-area\n (dropped)=\"uploadFile($event)\"\n [loadingMessage]=\"'Importing, please wait.' | translate\"\n [title]=\"'Import keystore with jks file extension' | translate\"\n ></c8y-drop-area>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"card-footer large-padding separator sticky-bottom\">\n <button\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n class=\"btn btn-default\"\n (click)=\"cancel()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n class=\"btn btn-danger\"\n (click)=\"remove()\"\n >\n {{ 'Remove' | translate }}\n </button>\n <button\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n class=\"btn btn-primary\"\n (click)=\"save()\"\n [disabled]=\"!opcuaConfigForm.valid\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.OpcuaService }, { type: i2.ModalService }, { type: i2.AlertService }], propDecorators: { opcuaConfigForm: [{ type: ViewChild, args: ['opcuaConfigForm', { static: false }] }], dropArea: [{ type: ViewChild, args: [DropAreaComponent, { static: false }] }], canceled: [{ type: Output }], removed: [{ type: Output }], saved: [{ type: Output }], server: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BjdWEtc2VydmVyLWNvbmZpZy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9wcm90b2NvbC1vcGN1YS9vcGN1YS1zZXJ2ZXItY29uZmlnLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL3Byb3RvY29sLW9wY3VhL29wY3VhLXNlcnZlci1jb25maWcuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFDVCxLQUFLLEVBRUwsTUFBTSxFQUNOLFlBQVksRUFFWixTQUFTLEVBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNMLE9BQU8sRUFFUCxpQkFBaUIsRUFDakIsWUFBWSxFQUNaLE1BQU0sRUFDTixZQUFZLEVBQ2IsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUMzQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7Ozs7QUFNOUMsTUFBTSxPQUFPLDBCQUEwQjtJQVdyQyxJQUFhLE1BQU0sQ0FBQyxNQUFtQjtRQUNyQyxJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNqQyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsSUFBSSxDQUFDLEtBQUssR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDL0IsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztZQUVuRCxJQUFJLE1BQU0sQ0FBQyxFQUFFLElBQUksTUFBTSxDQUFDLEVBQUUsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDckMsMkJBQTJCO2dCQUMzQixJQUFJLENBQUMscUJBQXFCLEdBQUcsR0FBRyxDQUFDO2dCQUNqQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIs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