1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22/**
23 * @var CView $this
24 */
25?>
26<script type="text/x-jquery-tmpl" id="host-interface-row-tmpl">
27<div class="<?= ZBX_STYLE_HOST_INTERFACE_ROW ?> <?= ZBX_STYLE_LIST_ACCORDION_ITEM ?> <?= ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED ?>" id="interface_row_#{iface.interfaceid}" data-type="#{iface.type}" data-interfaceid="#{iface.interfaceid}">
28	<input type="hidden" name="interfaces[#{iface.interfaceid}][items]" value="#{iface.items}" />
29	<input type="hidden" name="interfaces[#{iface.interfaceid}][isNew]" value="#{iface.isNew}" />
30	<input type="hidden" name="interfaces[#{iface.interfaceid}][interfaceid]" value="#{iface.interfaceid}" />
31	<input type="hidden" id="interface_type_#{iface.interfaceid}" name="interfaces[#{iface.interfaceid}][type]" value="#{iface.type}" />
32
33	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?>">
34		<button type="button" class="<?= ZBX_STYLE_HOST_INTERFACE_BTN_TOGGLE ?>"></button>
35	</div>
36	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_TYPE ?>">
37		#{iface.type_name}
38	</div>
39	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_IP ?>">
40		<?= (new CTextBox('interfaces[#{iface.interfaceid}][ip]', '#{iface.ip}', false, DB::getFieldLength('interface', 'ip')))
41				->addClass(ZBX_STYLE_HOST_INTERFACE_INPUT_EXPAND)
42				->setWidth(ZBX_TEXTAREA_INTERFACE_IP_WIDTH)
43		?>
44	</div>
45	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_DNS ?>">
46		<?= (new CTextBox('interfaces[#{iface.interfaceid}][dns]', '#{iface.dns}', false, DB::getFieldLength('interface', 'dns')))
47				->addClass(ZBX_STYLE_HOST_INTERFACE_INPUT_EXPAND)
48				->setWidth(ZBX_TEXTAREA_INTERFACE_DNS_WIDTH)
49		?>
50	</div>
51	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_USEIP ?>">
52		<?= (new CRadioButtonList('interfaces[#{iface.interfaceid}][useip]', null))
53				->addValue('IP', INTERFACE_USE_IP, 'interfaces[#{iface.interfaceid}][useip]['.INTERFACE_USE_IP.']')
54				->addValue('DNS', INTERFACE_USE_DNS, 'interfaces[#{iface.interfaceid}][useip]['.INTERFACE_USE_DNS.']')
55				->addClass(ZBX_STYLE_HOST_INTERFACE_CELL_USEIP.' '.ZBX_STYLE_HOST_INTERFACE_INPUT_EXPAND)
56				->setModern(true)
57		?>
58	</div>
59	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_PORT ?>">
60		<?= (new CTextBox('interfaces[#{iface.interfaceid}][port]', '#{iface.port}', false, DB::getFieldLength('interface', 'port')))
61				->setWidth(ZBX_TEXTAREA_INTERFACE_PORT_WIDTH)
62				->addClass(ZBX_STYLE_HOST_INTERFACE_INPUT_EXPAND)
63				->setAriaRequired()
64		?>
65	</div>
66	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_DEFAULT ?>">
67		<input type="radio" class="<?= ZBX_STYLE_CHECKBOX_RADIO ?> <?= ZBX_STYLE_HOST_INTERFACE_BTN_MAIN_INTERFACE ?>" id="interface_main_#{iface.interfaceid}" name="mainInterfaces[#{iface.type}]" value="#{iface.interfaceid}">
68		<label class="checkboxLikeLabel" for="interface_main_#{iface.interfaceid}" style="height: 16px; width: 16px;"><span></span></label>
69	</div>
70	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_ACTION ?>">
71		<button type="button" class="<?= ZBX_STYLE_BTN_LINK ?> <?= ZBX_STYLE_HOST_INTERFACE_BTN_REMOVE ?>"><?= _('Remove') ?></button>
72	</div>
73	<div class="<?= ZBX_STYLE_HOST_INTERFACE_CELL ?> <?= ZBX_STYLE_HOST_INTERFACE_CELL_DETAILS ?> <?= ZBX_STYLE_LIST_ACCORDION_ITEM_BODY ?>">
74		<?= (new CFormList('snmp_details_#{iface.interfaceid}'))
75				->cleanItems()
76				->addRow((new CLabel(_('SNMP version'), 'label_interfaces_#{iface.interfaceid}_details_version'))
77						->setAsteriskMark(),
78					(new CSelect('interfaces[#{iface.interfaceid}][details][version]'))
79						->addOptions(CSelect::createOptionsFromArray([
80							SNMP_V1 => _('SNMPv1'),
81							SNMP_V2C => _('SNMPv2'),
82							SNMP_V3 => _('SNMPv3')
83						]))
84						->setValue(SNMP_V2C)
85						->setFocusableElementId('label_interfaces_#{iface.interfaceid}_details_version')
86						->setId('interfaces_#{iface.interfaceid}_details_version'),
87					'row_snmp_version_#{iface.interfaceid}'
88				)
89				->addRow((new CLabel(_('SNMP community'), 'interfaces[#{iface.interfaceid}][details][community]'))->setAsteriskMark(),
90					(new CTextBox('interfaces[#{iface.interfaceid}][details][community]', '#{iface.details.community}', false, DB::getFieldLength('interface_snmp', 'community')))
91						->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
92						->setAriaRequired(),
93					'row_snmp_community_#{iface.interfaceid}'
94				)
95				->addRow(new CLabel(_('Context name'), 'interfaces[#{iface.interfaceid}][details][contextname]'),
96					(new CTextBox('interfaces[#{iface.interfaceid}][details][contextname]', '#{iface.details.contextname}', false, DB::getFieldLength('interface_snmp', 'contextname')))
97						->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
98					'row_snmpv3_contextname_#{iface.interfaceid}'
99				)
100				->addRow(new CLabel(_('Security name'), 'interfaces[#{iface.interfaceid}][details][securityname]'),
101					(new CTextBox('interfaces[#{iface.interfaceid}][details][securityname]', '#{iface.details.securityname}', false, DB::getFieldLength('interface_snmp', 'securityname')))
102						->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH),
103					'row_snmpv3_securityname_#{iface.interfaceid}'
104				)
105				->addRow(new CLabel(_('Security level'), 'label_interfaces_#{iface.interfaceid}_details_securitylevel'),
106					(new CSelect('interfaces[#{iface.interfaceid}][details][securitylevel]'))
107						->addOptions(CSelect::createOptionsFromArray([
108							ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV => 'noAuthNoPriv',
109							ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV => 'authNoPriv',
110							ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV => 'authPriv'
111						]))
112						->setValue(ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV)
113						->setFocusableElementId('label_interfaces_#{iface.interfaceid}_details_securitylevel')
114						->setId('interfaces_#{iface.interfaceid}_details_securitylevel'),
115					'row_snmpv3_securitylevel_#{iface.interfaceid}'
116				)
117				->addRow(new CLabel(_('Authentication protocol'), 'label-authprotocol-#{iface.interfaceid}'),
118					(new CSelect('interfaces[#{iface.interfaceid}][details][authprotocol]'))
119						->setFocusableElementId('label-authprotocol-#{iface.interfaceid}')
120						->addOptions(CSelect::createOptionsFromArray(getSnmpV3AuthProtocols())),
121					'row_snmpv3_authprotocol_#{iface.interfaceid}'
122				)
123				->addRow(new CLabel(_('Authentication passphrase'), 'interfaces[#{iface.interfaceid}][details][authpassphrase]'),
124					(new CTextBox('interfaces[#{iface.interfaceid}][details][authpassphrase]', '#{iface.details.authpassphrase}', false, DB::getFieldLength('interface_snmp', 'authpassphrase')))
125						->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
126						->disableAutocomplete(),
127					'row_snmpv3_authpassphrase_#{iface.interfaceid}'
128				)
129				->addRow(new CLabel(_('Privacy protocol'), 'label-privprotocol-#{iface.interfaceid}'),
130					(new CSelect('interfaces[#{iface.interfaceid}][details][privprotocol]'))
131						->setFocusableElementId('label-privprotocol-#{iface.interfaceid}')
132						->addOptions(CSelect::createOptionsFromArray(getSnmpV3PrivProtocols())),
133					'row_snmpv3_privprotocol_#{iface.interfaceid}'
134				)
135				->addRow(new CLabel(_('Privacy passphrase'), 'interfaces[#{iface.interfaceid}][details][privpassphrase]'),
136					(new CTextBox('interfaces[#{iface.interfaceid}][details][privpassphrase]', '#{iface.details.privpassphrase}', false, DB::getFieldLength('interface_snmp', 'privpassphrase')))
137						->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
138						->disableAutocomplete(),
139					'row_snmpv3_privpassphrase_#{iface.interfaceid}'
140				)
141				->addRow('', (new CCheckBox('interfaces[#{iface.interfaceid}][details][bulk]', SNMP_BULK_ENABLED))->setLabel(_('Use bulk requests'), 'interfaces[#{iface.interfaceid}][details][bulk]'),
142					'row_snmp_bulk_#{iface.interfaceid}'
143				);
144		?>
145	</div>
146</div>
147</script>
148
149<script type="text/javascript">
150	'use strict';
151
152	class HostInterfaceManager {
153		constructor(data) {
154			// Constants.
155			this.TEMPLATE = new Template(document.getElementById('host-interface-row-tmpl').innerHTML);
156			this.DEFAULT_PORTS = {
157				agent: 10050,
158				snmp: 161,
159				jmx: 12345,
160				ipmi: 623
161			};
162			this.CONTAINER_IDS = {
163				<?= INTERFACE_TYPE_AGENT ?>: '#agentInterfaces',
164				<?= INTERFACE_TYPE_SNMP ?>: '#SNMPInterfaces',
165				<?= INTERFACE_TYPE_JMX ?>: '#JMXInterfaces',
166				<?= INTERFACE_TYPE_IPMI ?>: '#IPMIInterfaces'
167			};
168			this.INTERFACE_TYPES = {
169				'agent': '<?= INTERFACE_TYPE_AGENT ?>',
170				'snmp': '<?= INTERFACE_TYPE_SNMP ?>',
171				'jmx': '<?= INTERFACE_TYPE_JMX ?>',
172				'ipmi': '<?= INTERFACE_TYPE_IPMI ?>'
173			};
174			this.INTERFACE_NAMES = {
175				<?= INTERFACE_TYPE_AGENT ?>: '<?= _('Agent') ?>',
176				<?= INTERFACE_TYPE_SNMP ?>: '<?= _('SNMP') ?>',
177				<?= INTERFACE_TYPE_JMX ?>: '<?= _('JMX') ?>',
178				<?= INTERFACE_TYPE_IPMI ?>: '<?= _('IPMI') ?>'
179			};
180
181			this.allow_empty_message = true;
182			this.$noInterfacesMsg = jQuery('<div>', {class: '<?= ZBX_STYLE_GREY ?>'})
183				.text('<?= _('No interfaces are defined.') ?>')
184				.css('padding', '5px 0px')
185				.insertAfter(jQuery('.<?= ZBX_STYLE_HOST_INTERFACE_CONTAINER_HEADER ?>'));
186
187			// Variables.
188			this.interfaces = {};
189
190			this.data = data;
191		}
192
193		/**
194		 * Setter for interface store.
195		 *
196		 * @param object new_data
197		 */
198		set data(new_data) {
199			if (typeof new_data  !== 'object') {
200				throw new Error('Incorrect data.');
201			}
202
203			Object
204				.entries(new_data)
205				.forEach(([_, value]) => {
206					if (!('interfaceid' in value)) {
207						value.interfaceid = this.generateId();
208					}
209
210					this.interfaces[value.interfaceid] = value;
211				});
212
213			return this;
214		}
215
216		/**
217		 * Getter for interface store.
218		 */
219		get data() {
220			return this.interfaces;
221		}
222
223		setAllowEmptyMessage(value) {
224			this.allow_empty_message = value;
225		}
226
227		setSnmpFields(elem, iface) {
228			if (iface.type != <?= INTERFACE_TYPE_SNMP ?>) {
229				return elem
230					.querySelector('.<?= ZBX_STYLE_HOST_INTERFACE_CELL_DETAILS ?>')
231					.remove();
232			}
233
234			elem
235				.querySelector(`#interfaces_${iface.interfaceid}_details_version`)
236				.value = iface.details.version;
237
238			if (iface.details.securitylevel !== undefined) {
239				elem
240					.querySelector(`#interfaces_${iface.interfaceid}_details_securitylevel`)
241					.value = iface.details.securitylevel;
242			}
243
244			if (iface.details.privprotocol !== undefined) {
245				elem.querySelector(`[name="interfaces[${iface.interfaceid}][details][privprotocol]"]`).value
246					= iface.details.privprotocol;
247			}
248
249			if (iface.details.authprotocol !== undefined) {
250				elem.querySelector(`[name="interfaces[${iface.interfaceid}][details][authprotocol]"]`).value
251					= iface.details.authprotocol;
252			}
253
254			if (iface.details.bulk == <?= SNMP_BULK_ENABLED ?>) {
255				elem
256					.querySelector(`#interfaces_${iface.interfaceid}_details_bulk`)
257					.checked = true;
258			}
259
260			new CViewSwitcher(`interfaces_${iface.interfaceid}_details_version`, 'change',
261				{
262					<?= SNMP_V1 ?>: [`row_snmp_community_${iface.interfaceid}`],
263					<?= SNMP_V2C ?>: [`row_snmp_community_${iface.interfaceid}`],
264					<?= SNMP_V3 ?>: [
265						`row_snmpv3_contextname_${iface.interfaceid}`,
266						`row_snmpv3_securityname_${iface.interfaceid}`,
267						`row_snmpv3_securitylevel_${iface.interfaceid}`,
268						`row_snmpv3_authprotocol_${iface.interfaceid}`,
269						`row_snmpv3_authpassphrase_${iface.interfaceid}`,
270						`row_snmpv3_privprotocol_${iface.interfaceid}`,
271						`row_snmpv3_privpassphrase_${iface.interfaceid}`,
272					]
273				}
274			);
275
276			jQuery(`#interfaces_${iface.interfaceid}_details_version`).on('change', function() {
277				jQuery(`#interfaces_${iface.interfaceid}_details_securitylevel`).off('change');
278
279				if (jQuery(this).val() == <?= SNMP_V3 ?>) {
280					new CViewSwitcher(`interfaces_${iface.interfaceid}_details_securitylevel`, 'change',
281						{
282							<?= ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV ?>: [],
283							<?= ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV ?>: [
284								'row_snmpv3_authprotocol_' + iface.interfaceid,
285								'row_snmpv3_authpassphrase_' + iface.interfaceid,
286							],
287							<?= ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV ?>: [
288								'row_snmpv3_authprotocol_' + iface.interfaceid,
289								'row_snmpv3_authpassphrase_' + iface.interfaceid,
290								'row_snmpv3_privprotocol_' + iface.interfaceid,
291								'row_snmpv3_privpassphrase_' + iface.interfaceid
292							]
293						}
294					);
295				}
296			}).trigger('change');
297		}
298
299		generateId() {
300			let id = 1;
301
302			while (this.data[id] !== undefined) {
303				id++;
304			}
305
306			return id;
307		}
308
309		getNewData(type) {
310			return {
311				interfaceid: this.generateId(),
312				isNew: true,
313				useip: 1,
314				type: this.INTERFACE_TYPES[type],
315				type_name: this.INTERFACE_NAMES[this.INTERFACE_TYPES[type]],
316				port: this.DEFAULT_PORTS[type],
317				ip: '127.0.0.1',
318				main: '0',
319				details: {
320					version: <?= SNMP_V2C ?>,
321					community: '{$SNMP_COMMUNITY}',
322					bulk: <?= SNMP_BULK_ENABLED ?>,
323					securitylevel: <?= ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV ?>,
324					authprotocol: <?= ITEM_SNMPV3_AUTHPROTOCOL_MD5 ?>,
325					privprotocol: <?= ITEM_SNMPV3_PRIVPROTOCOL_DES ?>
326				}
327			};
328		}
329
330		getInterfaces() {
331			let types = {
332					<?= INTERFACE_TYPE_AGENT ?>: {main: null, all: []},
333					<?= INTERFACE_TYPE_SNMP ?>: {main: null, all: []},
334					<?= INTERFACE_TYPE_JMX ?>: {main: null, all: []},
335					<?= INTERFACE_TYPE_IPMI ?>: {main: null, all: []}
336				};
337
338			Object
339				.entries(this.data)
340				.forEach(([_, value]) => {
341					types[value.type].all.push(value.interfaceid);
342
343					if (value.main == <?= INTERFACE_PRIMARY ?>) {
344						if (types[value.type].main !== null) {
345							throw new Error('Multiple default interfaces for same type.');
346						}
347
348						types[value.type].main = value.interfaceid;
349					}
350				});
351
352			return types;
353		}
354
355		renderRow(iface) {
356			const container = document.querySelector(this.CONTAINER_IDS[iface.type]);
357			const disabled = (typeof iface.items !== 'undefined' && iface.items > 0);
358
359			iface.type_name = this.INTERFACE_NAMES[iface.type];
360
361			/*
362			 * New line break css selector :empty. Trim used to avoid this.
363			 * Template added with new line. Because template <script> tag it contain for code readability.
364			 */
365			container.insertAdjacentHTML('beforeend', this.TEMPLATE.evaluate({iface: iface}).trim());
366
367			const elem = document.getElementById(`interface_row_${iface.interfaceid}`);
368
369			// Select proper use ip radio element.
370			elem
371				.querySelector(`#interfaces_${iface.interfaceid}_useip_${iface.useip}`)
372				.checked = true;
373
374			if (disabled) {
375				elem
376					.querySelector('.<?= ZBX_STYLE_HOST_INTERFACE_BTN_REMOVE ?>')
377					.disabled = true;
378			}
379
380			this.setSnmpFields(elem, iface);
381
382			// Set onclick actions.
383			elem
384				.querySelector('.<?= ZBX_STYLE_HOST_INTERFACE_BTN_REMOVE ?>')
385				.addEventListener('click', () => this.removeById(iface.interfaceid));
386
387			elem
388				.querySelector('.<?= ZBX_STYLE_HOST_INTERFACE_BTN_MAIN_INTERFACE ?>')
389				.addEventListener('click', () => this.setMainInterfaceById(iface.interfaceid));
390
391			[...elem.querySelectorAll('.<?= ZBX_STYLE_HOST_INTERFACE_CELL_USEIP ?> input')].map(
392				(el) => el.addEventListener('click', (event) => this.setUseIp(elem, event.currentTarget.value))
393			);
394
395			return true;
396		}
397
398		removeById(id) {
399			const elem = document.getElementById(`interface_row_${id}`);
400
401			if (!elem) {
402				return false;
403			}
404
405			elem.remove();
406			delete this.data[id];
407
408			this.resetMainInterfaces();
409			this.renderLayout();
410
411			return true;
412		}
413
414		createRowByTypeName(type) {
415			const new_data = this.getNewData(type);
416			let data = {};
417
418			data[new_data.interfaceid] = new_data;
419
420			this.data = data;
421			this.renderRow(new_data);
422
423			this.resetMainInterfaces();
424			this.renderLayout();
425
426			if (new_data.type == <?= INTERFACE_TYPE_SNMP ?>) {
427				const elem = document.getElementById(`interface_row_${new_data.interfaceid}`);
428				const index = [...elem.parentElement.children].indexOf(elem)
429
430				jQuery(this.CONTAINER_IDS[<?= INTERFACE_TYPE_SNMP ?>]).zbx_vertical_accordion('expandNth', index);
431			}
432
433			return true;
434		}
435
436		resetMainInterfaces() {
437			const interfaces = this.getInterfaces();
438
439			for (let type in interfaces) {
440				if (!interfaces.hasOwnProperty(type)) {
441					continue;
442				}
443
444				let type_interfaces = interfaces[type];
445
446				if (!type_interfaces.main && type_interfaces.all.length) {
447					for (let i = 0; i < type_interfaces.all.length; i++) {
448						if (this.data[type_interfaces.all[i]].main == <?= INTERFACE_PRIMARY ?>) {
449							interfaces[type].main = this.data[type_interfaces.all[i]].interfaceid;
450						}
451					}
452
453					if (!type_interfaces.main) {
454						type_interfaces.main = type_interfaces.all[0];
455						this.data[type_interfaces.main].main = '<?= INTERFACE_PRIMARY ?>';
456					}
457				}
458			}
459
460			for (let type in interfaces) {
461				if (interfaces.hasOwnProperty(type)) {
462					let type_interfaces = interfaces[type];
463
464					if (type_interfaces.main) {
465						document
466							.getElementById(`interface_main_${type_interfaces.main}`)
467							.checked = true;
468					}
469				}
470			}
471
472			return true;
473		}
474
475		setMainInterfaceById(id) {
476			const interfaces = this.getInterfaces();
477			const type = this.data[id].type;
478			const old = interfaces[type].main;
479
480			if (id != old) {
481				this.data[id].main = '<?= INTERFACE_PRIMARY ?>';
482				this.data[old].main = '<?= INTERFACE_SECONDARY ?>';
483			}
484
485			return true;
486		}
487
488		setUseIp(elem, use_ip) {
489			const interfaceid = elem.dataset.interfaceid;
490
491			this.data[interfaceid].useip = use_ip;
492
493			[...elem.querySelectorAll('input[name$="[ip]"], input[name$="[dns]"]')].map((el) => {
494				el.removeAttribute('aria-required')
495				return el;
496			});
497
498			elem
499				.querySelector((use_ip == <?= INTERFACE_USE_IP ?>) ? '[name$="[ip]"]' : '[name$="[dns]"]')
500				.setAttribute('aria-required', true);
501
502			return true;
503		}
504
505		addAgent() {
506			this.createRowByTypeName('agent');
507		}
508
509		addSnmp() {
510			this.createRowByTypeName('snmp');
511		}
512
513		addJmx() {
514			this.createRowByTypeName('jmx');
515		}
516
517		addIpmi() {
518			this.createRowByTypeName('ipmi');
519		}
520
521		render() {
522			for (let i in this.data) {
523				if (this.data.hasOwnProperty(i)) {
524					this.renderRow(this.data[i]);
525				}
526			}
527
528			this.resetMainInterfaces();
529			this.renderLayout();
530
531			// Add accordion functionality to SNMP interfaces.
532			jQuery(this.CONTAINER_IDS[<?= INTERFACE_TYPE_SNMP ?>])
533				.zbx_vertical_accordion({handler: '.<?= ZBX_STYLE_HOST_INTERFACE_BTN_TOGGLE ?>'});
534
535			// Add event to expand SNMP interface accordion if focused or clicked on inputs.
536			jQuery(this.CONTAINER_IDS[<?= INTERFACE_TYPE_SNMP ?>]).on("focus", ".<?= ZBX_STYLE_LIST_ACCORDION_ITEM ?>:not(.<?= ZBX_STYLE_LIST_ACCORDION_ITEM_OPENED ?>) .<?= ZBX_STYLE_HOST_INTERFACE_INPUT_EXPAND ?>", (event) => {
537				var index = jQuery(event.currentTarget).closest('.<?= ZBX_STYLE_LIST_ACCORDION_ITEM ?>').index();
538
539				jQuery(this.CONTAINER_IDS[<?= INTERFACE_TYPE_SNMP ?>]).zbx_vertical_accordion("expandNth", index);
540			});
541
542			return true;
543		}
544
545		renderLayout() {
546			if (Object.keys(this.data).length > 0) {
547				jQuery('.<?= ZBX_STYLE_HOST_INTERFACE_CONTAINER ?>').show();
548				this.$noInterfacesMsg.hide();
549			}
550			else {
551				jQuery('.<?= ZBX_STYLE_HOST_INTERFACE_CONTAINER ?>').hide();
552				this.$noInterfacesMsg.toggle(this.allow_empty_message);
553			}
554		}
555
556		/**
557		 * Converts form field to readonly.
558		 *
559		 * @param {Element} el  Native JavaScript element for form field.
560		 */
561		static setReadonly(el) {
562			const tag_name = el.tagName;
563
564			if (tag_name === 'INPUT') {
565				const type = el.getAttribute('type');
566
567				switch (type) {
568					case 'text':
569						el.readOnly = true;
570						break;
571					case 'radio':
572					case 'checkbox':
573						const {checked, name, value} = el;
574						el.disabled = true;
575
576						if (checked) {
577							const input = document.createElement('input');
578							input.type = 'hidden';
579							input.name = name;
580							input.value = value;
581
582							el.insertAdjacentElement('beforebegin', input);
583						}
584
585						break;
586				}
587			}
588			else if (tag_name === 'Z-SELECT') {
589				el.readOnly = true;
590			}
591		}
592
593		static makeReadonly() {
594			[...document.querySelectorAll('.<?= ZBX_STYLE_HOST_INTERFACE_ROW ?>')].map((row) => {
595				[...row.querySelectorAll('input, z-select')].map((el) => {
596					this.setReadonly(el);
597				});
598
599				[...row.querySelectorAll('.<?= ZBX_STYLE_HOST_INTERFACE_BTN_REMOVE ?>')].map((el) => el.remove());
600			});
601
602			return true;
603		}
604	}
605
606	jQuery(document).ready(function() {
607		'use strict';
608
609		jQuery('#tls_connect, #tls_in_psk, #tls_in_cert').change(function() {
610			// If certificate is selected or checked.
611			if (jQuery('input[name=tls_connect]:checked').val() == <?= HOST_ENCRYPTION_CERTIFICATE ?>
612					|| jQuery('#tls_in_cert').is(':checked')) {
613				jQuery('#tls_issuer, #tls_subject').closest('li').show();
614			}
615			else {
616				jQuery('#tls_issuer, #tls_subject').closest('li').hide();
617			}
618
619			// If PSK is selected or checked.
620			if (jQuery('input[name=tls_connect]:checked').val() == <?= HOST_ENCRYPTION_PSK ?>
621					|| jQuery('#tls_in_psk').is(':checked')) {
622				jQuery('#tls_psk, #tls_psk_identity, .tls_psk').closest('li').show();
623			}
624			else {
625				jQuery('#tls_psk, #tls_psk_identity, .tls_psk').closest('li').hide();
626			}
627		});
628
629		// radio button of inventory modes was clicked
630		jQuery('input[name=inventory_mode]').click(function() {
631			// action depending on which button was clicked
632			var inventoryFields = jQuery('#inventorylist :input:gt(2)');
633
634			switch (jQuery(this).val()) {
635				case '<?= HOST_INVENTORY_DISABLED ?>':
636					inventoryFields.prop('disabled', true);
637					jQuery('.populating_item').hide();
638					break;
639				case '<?= HOST_INVENTORY_MANUAL ?>':
640					inventoryFields.prop('disabled', false);
641					jQuery('.populating_item').hide();
642					break;
643				case '<?= HOST_INVENTORY_AUTOMATIC ?>':
644					inventoryFields.prop('disabled', false);
645					inventoryFields.filter('.linked_to_item').prop('disabled', true);
646					jQuery('.populating_item').show();
647					break;
648			}
649		});
650
651		// Refresh field visibility on document load.
652		if ((jQuery('#tls_accept').val() & <?= HOST_ENCRYPTION_NONE ?>) == <?= HOST_ENCRYPTION_NONE ?>) {
653			jQuery('#tls_in_none').prop('checked', true);
654		}
655		if ((jQuery('#tls_accept').val() & <?= HOST_ENCRYPTION_PSK ?>) == <?= HOST_ENCRYPTION_PSK ?>) {
656			jQuery('#tls_in_psk').prop('checked', true);
657		}
658		if ((jQuery('#tls_accept').val() & <?= HOST_ENCRYPTION_CERTIFICATE ?>) == <?= HOST_ENCRYPTION_CERTIFICATE ?>) {
659			jQuery('#tls_in_cert').prop('checked', true);
660		}
661
662		jQuery('input[name=tls_connect]').trigger('change');
663
664		// Depending on checkboxes, create a value for hidden field 'tls_accept'.
665		jQuery('#hosts-form').submit(function() {
666			var tls_accept = 0x00;
667
668			if (jQuery('#tls_in_none').is(':checked')) {
669				tls_accept |= <?= HOST_ENCRYPTION_NONE ?>;
670			}
671			if (jQuery('#tls_in_psk').is(':checked')) {
672				tls_accept |= <?= HOST_ENCRYPTION_PSK ?>;
673			}
674			if (jQuery('#tls_in_cert').is(':checked')) {
675				tls_accept |= <?= HOST_ENCRYPTION_CERTIFICATE ?>;
676			}
677
678			jQuery('#tls_accept').val(tls_accept);
679		});
680	});
681</script>
682