@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
335 lines • 88.2 kB
JavaScript
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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