You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							389 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							389 lines
						
					
					
						
							11 KiB
						
					
					
				| <?php declare(strict_types = 0);
 | |
| /*
 | |
| ** Zabbix
 | |
| ** Copyright (C) 2001-2023 Zabbix SIA
 | |
| **
 | |
| ** This program is free software; you can redistribute it and/or modify
 | |
| ** it under the terms of the GNU General Public License as published by
 | |
| ** the Free Software Foundation; either version 2 of the License, or
 | |
| ** (at your option) any later version.
 | |
| **
 | |
| ** This program is distributed in the hope that it will be useful,
 | |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| ** GNU General Public License for more details.
 | |
| **
 | |
| ** You should have received a copy of the GNU General Public License
 | |
| ** along with this program; if not, write to the Free Software
 | |
| ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 | |
| **/
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * @var CView $this
 | |
|  */
 | |
| ?>
 | |
| 
 | |
| window.template_edit_popup = new class {
 | |
| 
 | |
| 	init({templateid, warnings}) {
 | |
| 		this.overlay = overlays_stack.getById('templates-form');
 | |
| 		this.dialogue = this.overlay.$dialogue[0];
 | |
| 		this.form = this.overlay.$dialogue.$body[0].querySelector('form');
 | |
| 		this.templateid = templateid;
 | |
| 		this.linked_templateids = this.#getLinkedTemplates();
 | |
| 		this.macros_templateids = null;
 | |
| 
 | |
| 		if (warnings.length > 0) {
 | |
| 			const message_box = warnings.length > 1
 | |
| 				? makeMessageBox('warning', warnings,
 | |
| 					<?= json_encode(_('Cloned template parameter values have been modified.')) ?>, true, false
 | |
| 				)[0]
 | |
| 				: makeMessageBox('warning', warnings, null, true, false)[0];
 | |
| 
 | |
| 			this.form.parentNode.insertBefore(message_box, this.form);
 | |
| 		}
 | |
| 
 | |
| 		this.#initActions();
 | |
| 		this.#initTemplateTab();
 | |
| 		this.#initMacrosTab();
 | |
| 
 | |
| 		this.initial_form_fields = getFormFields(this.form);
 | |
| 	}
 | |
| 
 | |
| 	#initActions() {
 | |
| 		this.form.addEventListener('click', (e) => {
 | |
| 			if (e.target.classList.contains('js-edit-linked-template')) {
 | |
| 				this.#editLinkedTemplate(e.target.dataset.templateid);
 | |
| 			}
 | |
| 			else if (e.target.classList.contains('js-unlink')) {
 | |
| 				this.#unlink(e);
 | |
| 			}
 | |
| 			else if (e.target.classList.contains('js-unlink-and-clear')) {
 | |
| 				this.#unlink(e);
 | |
| 				this.#clear(e.target.dataset.templateid);
 | |
| 			}
 | |
| 		});
 | |
| 
 | |
| 		// Add visible name input field placeholder.
 | |
| 		const template_name = this.form.querySelector('#template_name');
 | |
| 		const visible_name = this.form.querySelector('#visiblename');
 | |
| 
 | |
| 		template_name.addEventListener('input', () => visible_name.placeholder = template_name.value);
 | |
| 		template_name.dispatchEvent(new Event('input'));
 | |
| 	}
 | |
| 
 | |
| 	#initTemplateTab() {
 | |
| 		const $groups_ms = $('#template_groups_', this.form);
 | |
| 		const $template_ms = $('#template_add_templates_', this.form);
 | |
| 
 | |
| 		$template_ms.on('change', () => {
 | |
| 			$template_ms.multiSelect('setDisabledEntries', this.#getLinkedTemplates().concat(this.#getNewTemplates()));
 | |
| 		});
 | |
| 
 | |
| 		$groups_ms.on('change', () => {
 | |
| 			$groups_ms.multiSelect('setDisabledEntries',
 | |
| 				[...document.querySelectorAll('[name^="template_groups["]')]
 | |
| 					.map((input) => input.value)
 | |
| 			);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	#initMacrosTab() {
 | |
| 		this.macros_manager = new HostMacrosManager({
 | |
| 			container: $('#template_macros_container .table-forms-td-right')
 | |
| 		});
 | |
| 		let macros_initialized = false;
 | |
| 
 | |
| 		$('#template-tabs', this.form).on('tabscreate tabsactivate', (event, ui) => {
 | |
| 			let panel = (event.type === 'tabscreate') ? ui.panel : ui.newPanel;
 | |
| 			const show_inherited_macros = this.form
 | |
| 				.querySelector('input[name=show_inherited_template_macros]:checked').value == 1;
 | |
| 
 | |
| 			if (panel.attr('id') === 'template-macro-tab') {
 | |
| 				// Please note that macro initialization must take place once and only when the tab is visible.
 | |
| 				if (event.type === 'tabsactivate') {
 | |
| 					const templateids = this.linked_templateids.concat(this.#getNewTemplates());
 | |
| 
 | |
| 					if (this.macros_templateids === null) {
 | |
| 						this.macros_templateids = templateids;
 | |
| 					}
 | |
| 
 | |
| 					// After initialization load inherited macros only if templates changed.
 | |
| 					if (show_inherited_macros && this.macros_templateids.xor(templateids).length > 0) {
 | |
| 						this.macros_templateids = templateids;
 | |
| 						this.macros_manager.load(show_inherited_macros, templateids);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (macros_initialized) {
 | |
| 					return;
 | |
| 				}
 | |
| 
 | |
| 				// Initialize macros.
 | |
| 				this.macros_manager.initMacroTable(show_inherited_macros);
 | |
| 
 | |
| 				macros_initialized = true;
 | |
| 			}
 | |
| 		});
 | |
| 
 | |
| 		this.form.querySelector('#show_inherited_template_macros').onchange = (e) => {
 | |
| 			this.macros_manager.load(e.target.value == 1, this.linked_templateids.concat(this.#getNewTemplates()));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	#editLinkedTemplate(templateid) {
 | |
| 		const form_fields = getFormFields(this.form);
 | |
| 
 | |
| 		if (JSON.stringify(this.initial_form_fields) !== JSON.stringify(form_fields)) {
 | |
| 			if (!window.confirm(<?= json_encode(_('Any changes made in the current form will be lost.')) ?>)) {
 | |
| 				return;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		overlayDialogueDestroy(this.overlay.dialogueid);
 | |
| 
 | |
| 		const overlay = PopUp('template.edit', {templateid: templateid}, {
 | |
| 			dialogueid: 'templates-form',
 | |
| 			dialogue_class: 'modal-popup-large',
 | |
| 			prevent_navigation: true
 | |
| 		});
 | |
| 
 | |
| 		overlay.$dialogue[0].addEventListener('dialogue.submit', (e) => {
 | |
| 			this.dialogue.dispatchEvent(new CustomEvent('dialogue.submit', {detail: e.detail}));
 | |
| 		});
 | |
| 
 | |
| 		overlay.$dialogue[0].addEventListener('dialogue.close', () => {
 | |
| 			this.dialogue.dispatchEvent(new CustomEvent('dialogue.close'));
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Removes the linked template row and the template ID from linked templates object, triggers multiselect change
 | |
| 	 * event.
 | |
| 	 *
 | |
| 	 * @param {object} e  Event object.
 | |
| 	 */
 | |
| 	#unlink(e) {
 | |
| 		e.target.closest('tr').remove();
 | |
| 
 | |
| 		this.linked_templateids = this.linked_templateids.filter(value =>
 | |
| 			value != e.target.dataset.templateid
 | |
| 		);
 | |
| 
 | |
| 		$('#template_add_templates_', this.form).trigger('change');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds template ID s a hidden input to form within the clear_templates array.
 | |
| 	 *
 | |
| 	 * @param {string} templateid
 | |
| 	 */
 | |
| 	#clear(templateid) {
 | |
| 		const clear_template = document.createElement('input');
 | |
| 
 | |
| 		clear_template.type = 'hidden';
 | |
| 		clear_template.name = 'clear_templates[]';
 | |
| 		clear_template.value = templateid;
 | |
| 		this.form.appendChild(clear_template);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Helper to get linked template IDs as an array.
 | |
| 	 *
 | |
| 	 * @return {array}  Templateids.
 | |
| 	 */
 | |
| 	#getLinkedTemplates() {
 | |
| 		const linked_templateids = [];
 | |
| 
 | |
| 		this.form.querySelectorAll('[name^="templates["').forEach((input) => {
 | |
| 			linked_templateids.push(input.value);
 | |
| 		});
 | |
| 
 | |
| 		return linked_templateids;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Helper to get added template IDs as an array.
 | |
| 	 *
 | |
| 	 * @return {array}  Templateids.
 | |
| 	 */
 | |
| 	#getNewTemplates() {
 | |
| 		const $template_multiselect = $('#template_add_templates_', this.form);
 | |
| 		const templateids = [];
 | |
| 
 | |
| 		if ($template_multiselect.length) {
 | |
| 			$template_multiselect.multiSelect('getData').forEach(template => {
 | |
| 				templateids.push(template.id);
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		return templateids;
 | |
| 	}
 | |
| 
 | |
| 	clone() {
 | |
| 		this.overlay.setLoading();
 | |
| 		const parameters = this.#trimFields(getFormFields(this.form));
 | |
| 
 | |
| 		parameters.clone = 1;
 | |
| 		parameters.templateid = this.templateid;
 | |
| 		this.#prepareFields(parameters);
 | |
| 
 | |
| 		PopUp('template.edit', parameters, {
 | |
| 			dialogueid: 'templates-form'
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	deleteAndClear() {
 | |
| 		this.delete(true);
 | |
| 	}
 | |
| 
 | |
| 	delete(clear = false) {
 | |
| 		const curl = new Curl('zabbix.php');
 | |
| 
 | |
| 		curl.setArgument('action', 'template.delete');
 | |
| 		curl.setArgument('<?= CCsrfTokenHelper::CSRF_TOKEN_NAME ?>',
 | |
| 			<?= json_encode(CCsrfTokenHelper::get('template'), JSON_THROW_ON_ERROR) ?>
 | |
| 		);
 | |
| 
 | |
| 		let data = {templateids: [this.templateid]}
 | |
| 		if (clear) {
 | |
| 			data.clear = 1;
 | |
| 		}
 | |
| 
 | |
| 		this.#post(curl.getUrl(), data, (response) => {
 | |
| 			overlayDialogueDestroy(this.overlay.dialogueid);
 | |
| 
 | |
| 			this.dialogue.dispatchEvent(new CustomEvent('dialogue.submit', {detail: response}));
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	submit() {
 | |
| 		const fields = getFormFields(this.form);
 | |
| 
 | |
| 		if (this.templateid !== null) {
 | |
| 			fields.templateid = this.templateid;
 | |
| 		}
 | |
| 
 | |
| 		this.#prepareFields(fields);
 | |
| 		this.#trimFields(fields);
 | |
| 		this.overlay.setLoading();
 | |
| 
 | |
| 		const curl = new Curl('zabbix.php');
 | |
| 
 | |
| 		curl.setArgument('action', this.templateid === null ? 'template.create' : 'template.update');
 | |
| 
 | |
| 		this.#post(curl.getUrl(), fields, (response) => {
 | |
| 			overlayDialogueDestroy(this.overlay.dialogueid);
 | |
| 
 | |
| 			this.dialogue.dispatchEvent(new CustomEvent('dialogue.submit', {detail: response}));
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	#prepareFields(parameters) {
 | |
| 		const mappings = [
 | |
| 			{from: 'template_groups', to: 'groups'},
 | |
| 			{from: 'template_add_templates', to: 'add_templates'},
 | |
| 			{from: 'show_inherited_template_macros', to: 'show_inherited_macros'}
 | |
| 		];
 | |
| 
 | |
| 		for (const mapping of mappings) {
 | |
| 			parameters[mapping.to] = parameters[mapping.from];
 | |
| 			delete parameters[mapping.from];
 | |
| 		}
 | |
| 
 | |
| 		return parameters;
 | |
| 	}
 | |
| 
 | |
| 	#trimFields(fields) {
 | |
| 		// Trim all string fields.
 | |
| 		for (let key in fields) {
 | |
| 			if (typeof fields[key] === 'string') {
 | |
| 				fields[key] = fields[key].trim();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Trim tag input fields.
 | |
| 		if ('tags' in fields) {
 | |
| 			for (const key in fields.tags) {
 | |
| 				const tag = fields.tags[key];
 | |
| 				tag.tag = tag.tag.trim();
 | |
| 
 | |
| 				if ('name' in tag) {
 | |
| 					tag.name = tag.name.trim();
 | |
| 				}
 | |
| 				if ('value' in tag) {
 | |
| 					tag.value = tag.value.trim();
 | |
| 				}
 | |
| 
 | |
| 				delete tag.automatic;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Trim macro input fields.
 | |
| 		if ('macros' in fields) {
 | |
| 			for (const key in fields.macros) {
 | |
| 				const macro = fields.macros[key];
 | |
| 				macro.macro = macro.macro.trim();
 | |
| 
 | |
| 				if ('value' in macro) {
 | |
| 					macro.value = macro.value.trim();
 | |
| 				}
 | |
| 				if ('description' in macro) {
 | |
| 					macro.description = macro.description.trim();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return fields;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sends a POST request to the specified URL with the provided data and executes the success_callback function.
 | |
| 	 *
 | |
| 	 * @param {string}   url               The URL to send the POST request to.
 | |
| 	 * @param {object}   data              The data to send with the POST request.
 | |
| 	 * @param {callback} success_callback  The function to execute when a successful response is received.
 | |
| 	 */
 | |
| 	#post(url, data, success_callback) {
 | |
| 		fetch(url, {
 | |
| 			method: 'POST',
 | |
| 			headers: {'Content-Type': 'application/json'},
 | |
| 			body: JSON.stringify(data)
 | |
| 		})
 | |
| 			.then((response) => response.json())
 | |
| 			.then((response) => {
 | |
| 				if ('error' in response) {
 | |
| 					throw {error: response.error};
 | |
| 				}
 | |
| 
 | |
| 				return response;
 | |
| 			})
 | |
| 			.then(success_callback)
 | |
| 			.catch((exception) => {
 | |
| 				for (const element of this.form.parentNode.children) {
 | |
| 					if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
 | |
| 						element.parentNode.removeChild(element);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				let title, messages;
 | |
| 
 | |
| 				if (typeof exception === 'object' && 'error' in exception) {
 | |
| 					title = exception.error.title;
 | |
| 					messages = exception.error.messages;
 | |
| 				}
 | |
| 				else {
 | |
| 					messages = [<?= json_encode(_('Unexpected server error.')) ?>];
 | |
| 				}
 | |
| 
 | |
| 				const message_box = makeMessageBox('bad', messages, title)[0];
 | |
| 
 | |
| 				this.form.parentNode.insertBefore(message_box, this.form);
 | |
| 			})
 | |
| 			.finally(() => {
 | |
| 				this.overlay.unsetLoading();
 | |
| 			});
 | |
| 	}
 | |
| }
 |