From b49e3ea92cf07e6e986924f5732976b1193b9113 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 12:43:24 +0200 Subject: [PATCH 01/94] Initial commit --- checkmk_swissknife.user.js | 462 +++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 checkmk_swissknife.user.js diff --git a/checkmk_swissknife.user.js b/checkmk_swissknife.user.js new file mode 100644 index 0000000..8a76782 --- /dev/null +++ b/checkmk_swissknife.user.js @@ -0,0 +1,462 @@ +// ==UserScript== +// @name Checkmk SwissKnife +// @namespace https://monitor.ad.aruba.it/ +// @version 2.0 +// @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. +// @author Luigi D'Acunto +// @match *://monitor.ad.aruba.it/*/index.py* +// @match *://monitor.ad.aruba.it/*/wato.py* +// @grant none +// ==/UserScript== + +(function () { + 'use strict'; + + // ========================================================================= + // INFRASTRUTTURA COMUNE + // ========================================================================= + + const LOG_PREFIX = '[CMK-SK]'; + const POLL_INTERVAL_MS = 500; + const MAX_ATTEMPTS = 60; + + // Ricava il documento su cui operare: + // - Se c'è un iframe (index.py con sidebar) → usa il contentDocument dell'iframe + // - Se wato.py è aperto direttamente → usa document solo se contiene la select target + function getWatoDoc(selectId) { + const iframe = document.querySelector('iframe[name="main"], iframe#main'); + if (iframe) { + try { return iframe.contentDocument; } catch (e) { return null; } + } + if (document.getElementById(selectId) || + document.querySelector('select[name*="folder_path"]')) { + return document; + } + return null; + } + + // Inietta CSS nel documento target (una sola volta, deduplica per id) + function injectStyles(iDoc, id, css) { + if (iDoc.getElementById(id)) return; + const style = iDoc.createElement('style'); + style.id = id; + style.textContent = css; + iDoc.head.appendChild(style); + } + + + // ========================================================================= + // FEATURE: Folder Path Select Enhancement + // + // Migliora la del folder path in WATO mostrando il path completo + // in stile "Radice › Livello › Foglia" e abilitando la ricerca su di esso. + // Si attiva solo quando la select #explicit_conditions_p_folder_path è presente. + // ========================================================================= + + const FOLDER_SELECT_ID = 'explicit_conditions_p_folder_path'; + const FOLDER_DIV_ID = 'explicit_conditions_d_folder_path'; + + function formatPath(value) { + if (!value) return 'Main'; + const parts = value.split('/'); + return parts.map((p, i) => + i === parts.length - 1 + ? p.toUpperCase() + : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase() + ).join(' › '); + } + + function enhanceFolderSelect(iDoc) { + const sel = iDoc.getElementById(FOLDER_SELECT_ID); + if (!sel) return false; + if (sel.dataset.cmkEnhanced === '1') return true; + + const iWin = iDoc.defaultView; + const S2 = iWin.Select2 || (iWin.$ && iWin.$.fn && iWin.$.fn.select2 ? iWin.$ : null); + if (!S2) return false; + + Array.from(sel.options).forEach(opt => { + if (!opt.dataset.fullPath) { + opt.dataset.fullPath = formatPath(opt.value); + } + }); + + try { + const existing = getSelect2Instance(iDoc, sel); + if (existing && typeof existing.destroy === 'function') existing.destroy(); + } catch (e) { + console.warn(LOG_PREFIX, 'Impossibile distruggere Select2:', e); + } + + initSelect2Enhanced(iDoc, sel); + sel.dataset.cmkEnhanced = '1'; + return true; + } + + function getSelect2Instance(iDoc, sel) { + const iWin = iDoc.defaultView; + if (iWin.$ && iWin.$.fn && iWin.$.fn.select2) { + try { return iWin.$(sel).data('select2'); } catch (e) {} + } + if (iWin.Select2) { + try { return iWin.Select2.getInstance(sel); } catch (e) {} + } + return null; + } + + function initSelect2Enhanced(iDoc, sel) { + const iWin = iDoc.defaultView; + + const templateResult = function (option) { + if (!option.id) return option.text; + const fullPath = option.element?.dataset?.fullPath || formatPath(option.id); + const span = iDoc.createElement('span'); + span.title = fullPath; + span.style.cssText = 'font-family: monospace; font-size: 12px;'; + span.textContent = fullPath; + return span; + }; + + const templateSelection = function (option) { + if (!option.id) return option.text; + return formatPath(option.id); + }; + + const matcher = function (params, option) { + if (!params.term || params.term.trim() === '') return option; + const term = params.term.trim().toLowerCase(); + const fullPath = (option.element?.dataset?.fullPath || formatPath(option.id || '')).toLowerCase(); + const leafName = (option.id || '').split('/').pop().toLowerCase(); + if (fullPath.includes(term) || leafName.includes(term)) return option; + return null; + }; + + const config = { + width: 'resolve', + allowClear: false, + dropdownAutoWidth: true, + templateResult, + templateSelection, + matcher, + dropdownCssClass: 'cmk-sk-folder-dropdown', + }; + + if (iWin.$ && iWin.$.fn && iWin.$.fn.select2) { + try { + iWin.$(sel).select2(config); + injectFolderStyles(iDoc); + return; + } catch (e) { + console.warn(LOG_PREFIX, 'jQuery Select2 init fallita:', e); + } + } + + if (iWin.Select2) { + try { + new iWin.Select2(sel, config); + injectFolderStyles(iDoc); + return; + } catch (e) { + console.warn(LOG_PREFIX, 'Select2 standalone init fallita:', e); + } + } + + // Fallback: overlay di ricerca custom sopra il select2 nativo + buildCustomSearchOverlay(iDoc, sel); + } + + function buildCustomSearchOverlay(iDoc, sel) { + const divContainer = iDoc.getElementById(FOLDER_DIV_ID); + if (!divContainer) return; + if (divContainer.querySelector('.cmk-sk-folder-overlay')) return; + + const options = Array.from(sel.options).map(opt => ({ + value: opt.value, + label: opt.value ? formatPath(opt.value) : 'Main', + original: opt.text.trim() + })); + + const existingContainer = divContainer.querySelector('.select2-container'); + if (existingContainer) existingContainer.style.display = 'none'; + + const wrapper = iDoc.createElement('div'); + wrapper.className = 'cmk-sk-folder-overlay'; + wrapper.style.cssText = ` + display: inline-block; + position: relative; + min-width: 300px; + max-width: 600px; + width: 100%; + font-family: var(--font-family, sans-serif); + `; + + const searchInput = iDoc.createElement('input'); + searchInput.type = 'text'; + searchInput.placeholder = 'Cerca folder per nome o path... (es: veeam, dc1/veeam)'; + searchInput.autocomplete = 'off'; + searchInput.spellcheck = false; + searchInput.style.cssText = ` + width: 100%; + padding: 4px 8px; + border: 1px solid #666; + background: #1a1a2e; + color: #e0e0e0; + font-size: 12px; + font-family: monospace; + border-radius: 3px; + box-sizing: border-box; + `; + + const currentVal = sel.value; + const currentOpt = options.find(o => o.value === currentVal); + if (currentOpt) searchInput.value = currentOpt.label; + + const dropdown = iDoc.createElement('div'); + dropdown.style.cssText = ` + position: absolute; + top: 100%; + left: 0; + right: 0; + max-height: 300px; + overflow-y: auto; + background: #1a1a2e; + border: 1px solid #555; + border-top: none; + z-index: 99999; + display: none; + font-size: 12px; + font-family: monospace; + min-width: 450px; + `; + + const badge = iDoc.createElement('div'); + badge.style.cssText = ` + font-size: 10px; + color: #aaa; + margin-top: 2px; + padding-left: 2px; + font-family: monospace; + `; + + updateBadge(); + + function updateBadge() { + const v = sel.value; + badge.textContent = v ? `Path: ${v}` : 'Path: / (Main)'; + } + + function escapeHtml(str) { + return str.replace(/&/g, '&').replace(//g, '>'); + } + + function renderDropdown(filter) { + dropdown.innerHTML = ''; + const term = filter.trim().toLowerCase(); + const filtered = term + ? options.filter(o => + o.label.toLowerCase().includes(term) || + o.value.toLowerCase().includes(term) || + o.original.toLowerCase().includes(term) + ) + : options; + + if (filtered.length === 0) { + const noRes = iDoc.createElement('div'); + noRes.textContent = 'Nessun risultato'; + noRes.style.cssText = 'padding: 6px 10px; color: #aaa;'; + dropdown.appendChild(noRes); + } + + filtered.slice(0, 200).forEach((opt) => { + const item = iDoc.createElement('div'); + item.dataset.value = opt.value; + item.style.cssText = ` + padding: 4px 10px; + cursor: pointer; + color: #e0e0e0; + border-bottom: 1px solid #333; + white-space: nowrap; + `; + + if (term) { + const labelLow = opt.label.toLowerCase(); + const idx = labelLow.indexOf(term); + if (idx >= 0) { + item.innerHTML = + escapeHtml(opt.label.substring(0, idx)) + + '' + + escapeHtml(opt.label.substring(idx, idx + term.length)) + + '' + + escapeHtml(opt.label.substring(idx + term.length)); + } else { + item.textContent = opt.label; + } + } else { + item.textContent = opt.label; + } + + if (opt.value === sel.value) { + item.style.background = '#2a4a6e'; + item.style.fontWeight = 'bold'; + } + + item.addEventListener('mouseenter', () => { item.style.background = '#3a3a5e'; }); + item.addEventListener('mouseleave', () => { + item.style.background = opt.value === sel.value ? '#2a4a6e' : ''; + }); + item.addEventListener('mousedown', (e) => { + e.preventDefault(); + selectOption(opt.value, opt.label); + }); + + dropdown.appendChild(item); + }); + + if (filtered.length > 200) { + const more = iDoc.createElement('div'); + more.textContent = `... e altri ${filtered.length - 200} risultati. Raffina la ricerca.`; + more.style.cssText = 'padding: 6px 10px; color: #aaa; font-style: italic;'; + dropdown.appendChild(more); + } + + dropdown.style.display = 'block'; + } + + function selectOption(value, label) { + sel.value = value; + sel.dispatchEvent(new Event('change', { bubbles: true })); + searchInput.value = label; + dropdown.style.display = 'none'; + updateBadge(); + } + + function highlightItem(items, idx) { + items.forEach(i => i.classList.remove('highlighted')); + if (items[idx]) { + items[idx].classList.add('highlighted'); + items[idx].style.background = '#4a6a9e'; + items[idx].scrollIntoView({ block: 'nearest' }); + } + } + + searchInput.addEventListener('focus', () => { + searchInput.select(); + renderDropdown(searchInput.value === currentOpt?.label ? '' : searchInput.value); + }); + searchInput.addEventListener('input', () => { renderDropdown(searchInput.value); }); + searchInput.addEventListener('blur', () => { + setTimeout(() => { + dropdown.style.display = 'none'; + const o = options.find(x => x.value === sel.value); + if (o) searchInput.value = o.label; + }, 200); + }); + searchInput.addEventListener('keydown', (e) => { + const items = dropdown.querySelectorAll('[data-value]'); + const current = dropdown.querySelector('[data-value].highlighted'); + const currentIdx = current ? Array.from(items).indexOf(current) : -1; + + if (e.key === 'ArrowDown') { + e.preventDefault(); + highlightItem(items, Math.min(currentIdx + 1, items.length - 1)); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + highlightItem(items, Math.max(currentIdx - 1, 0)); + } else if (e.key === 'Enter') { + e.preventDefault(); + const highlighted = dropdown.querySelector('[data-value].highlighted'); + if (highlighted) { + selectOption( + highlighted.dataset.value, + options.find(o => o.value === highlighted.dataset.value)?.label || highlighted.textContent + ); + } + } else if (e.key === 'Escape') { + dropdown.style.display = 'none'; + } + }); + + wrapper.appendChild(searchInput); + wrapper.appendChild(dropdown); + divContainer.appendChild(wrapper); + divContainer.appendChild(badge); + + injectFolderStyles(iDoc); + } + + function injectFolderStyles(iDoc) { + injectStyles(iDoc, 'cmk-sk-folder-styles', ` + .cmk-sk-folder-dropdown .select2-results__option { + font-family: monospace !important; + font-size: 12px !important; + white-space: nowrap !important; + } + .cmk-sk-folder-overlay mark { + background: #f0a500; + color: #000; + border-radius: 2px; + padding: 0 1px; + } + .cmk-sk-folder-overlay [data-value].highlighted { + background: #4a6a9e !important; + } + `); + } + + + // ========================================================================= + // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente + // ========================================================================= + + let attempts = 0; + + function tryEnhanceFolderSelect() { + const iDoc = getWatoDoc(FOLDER_SELECT_ID); + if (!iDoc || !iDoc.body) { + if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + return; + } + + const sel = iDoc.getElementById(FOLDER_SELECT_ID); + if (!sel) { + // La select non è presente in questa pagina: feature non applicabile, si ferma. + return; + } + + if (!sel.classList.contains('select2-hidden-accessible')) { + if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + return; + } + + buildCustomSearchOverlay(iDoc, sel); + } + + function init() { + attempts = 0; + setTimeout(tryEnhanceFolderSelect, 800); + } + + if (document.readyState === 'complete') { + init(); + } else { + window.addEventListener('load', init); + } + + // Rileva navigazione SPA (cambio regola senza reload di pagina) + new MutationObserver(() => { + const iDoc = getWatoDoc(FOLDER_SELECT_ID); + if (!iDoc) return; + const sel = iDoc.getElementById(FOLDER_SELECT_ID); + if (sel && sel.classList.contains('select2-hidden-accessible') && !sel.dataset.cmkEnhanced) { + attempts = 0; + setTimeout(tryEnhanceFolderSelect, 300); + } + }).observe(document.body, { childList: true, subtree: true }); + + // Riavvia al caricamento dell'iframe (layout con sidebar) + const mainIframe = document.querySelector('iframe[name="main"], iframe#main'); + if (mainIframe) { + mainIframe.addEventListener('load', init); + } + +})(); From 01a687351beae079e01574b6281b58766d846ebd Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 12:45:55 +0200 Subject: [PATCH 03/94] Aggiunta dello script utente Checkmk SwissKnife per miglioramenti all'interfaccia di Checkmk WATO --- .../checkmk_swissknife.user.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename checkmk_swissknife.user.js => user_script/checkmk_swissknife.user.js (100%) diff --git a/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js similarity index 100% rename from checkmk_swissknife.user.js rename to user_script/checkmk_swissknife.user.js From 3b1cf4a9c84b090f62c24710d7cb9bdc74b48726 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 12:45:55 +0200 Subject: [PATCH 04/94] Aggiunta dello script utente Checkmk SwissKnife per miglioramenti all'interfaccia di Checkmk WATO --- .../checkmk_swissknife.user.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename checkmk_swissknife.user.js => user_script/checkmk_swissknife.user.js (100%) diff --git a/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js similarity index 100% rename from checkmk_swissknife.user.js rename to user_script/checkmk_swissknife.user.js From 005d50eacbca4e02e51dc4c8aa3c14d478597656 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:00:46 +0200 Subject: [PATCH 05/94] Aggiunta del file .gitignore per escludere file di test e script non necessari --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75c810e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ + +### My Personal test file for Dev only ### +/.*/ +/*.bat +/*.ps1 +/*.md +!README.md +!CHANGELOG.md \ No newline at end of file From 887adcb682b277b04ad56777fb1e391fe1a7c72c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:00:46 +0200 Subject: [PATCH 06/94] Aggiunta del file .gitignore per escludere file di test e script non necessari --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75c810e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ + +### My Personal test file for Dev only ### +/.*/ +/*.bat +/*.ps1 +/*.md +!README.md +!CHANGELOG.md \ No newline at end of file From 063fa25ee6428528de553f23d57df14aeda780a8 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:03:17 +0200 Subject: [PATCH 07/94] Aggiornato il namespace e le URL di homepage, update e download nello script utente Checkmk SwissKnife --- user_script/checkmk_swissknife.user.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 8a76782..7813042 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,11 +1,13 @@ // ==UserScript== // @name Checkmk SwissKnife -// @namespace https://monitor.ad.aruba.it/ +// @namespace https://*/ // @version 2.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto -// @match *://monitor.ad.aruba.it/*/index.py* -// @match *://monitor.ad.aruba.it/*/wato.py* +// @homepageURL https://gitlab.luigidacunto.com/consultant/checkmk-swissknife +// @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js +// @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js +// @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ // @grant none // ==/UserScript== From 0956e032110548fa29a2013d9ccc894ddedb749b Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:03:17 +0200 Subject: [PATCH 08/94] Aggiornato il namespace e le URL di homepage, update e download nello script utente Checkmk SwissKnife --- user_script/checkmk_swissknife.user.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 19011ce..7813042 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,11 +1,13 @@ // ==UserScript== // @name Checkmk SwissKnife -// @namespace https://checkmk.example.com/ +// @namespace https://*/ // @version 2.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto -// @match *://checkmk.example.com/*/index.py* -// @match *://checkmk.example.com/*/wato.py* +// @homepageURL https://gitlab.luigidacunto.com/consultant/checkmk-swissknife +// @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js +// @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js +// @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ // @grant none // ==/UserScript== From 26c0a9051cf9325505dff96bac4bf32712b19d84 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:05:23 +0200 Subject: [PATCH 09/94] Aggiornato il namespace nel file dello script utente Checkmk SwissKnife --- user_script/checkmk_swissknife.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 7813042..13c2cb0 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name Checkmk SwissKnife -// @namespace https://*/ +// @namespace https://luigidacunto.com/ // @version 2.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto From 3bdb6bbd42a78aea302c06ed3c0685d839252ccb Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Thu, 4 Jun 2026 13:05:23 +0200 Subject: [PATCH 10/94] Aggiornato il namespace nel file dello script utente Checkmk SwissKnife --- user_script/checkmk_swissknife.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 7813042..13c2cb0 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name Checkmk SwissKnife -// @namespace https://*/ +// @namespace https://luigidacunto.com/ // @version 2.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto From e09e66941af8b0748153cf332b047b178b945ea3 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:24:54 +0200 Subject: [PATCH 11/94] feat: aggiunto badge con conteggio checkbox attive per ogni accordion in edit_host Ogni titolo di gruppo (es. "Services - DB") mostra ora il numero di checkbox spuntate tra parentesi arancioni. Il contatore si aggiorna in tempo reale al cambio delle checkbox. Aggiornato anche homepageURL dal vecchio GitLab al nuovo Forgejo. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 105 +++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 13c2cb0..348d57a 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,10 +1,10 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.0 +// @version 2.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto -// @homepageURL https://gitlab.luigidacunto.com/consultant/checkmk-swissknife +// @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife // @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ @@ -406,16 +406,88 @@ } + // ========================================================================= + // FEATURE: Accordion Checked Count Badge + // + // Mostra nel titolo di ogni accordion della pagina edit_host il numero + // di checkbox attive nel gruppo. Es: "Services - DB (1)". + // Il contatore si aggiorna in tempo reale al cambio delle checkbox. + // ========================================================================= + + function updateAccordionBadge(td) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const checked = tbody.querySelectorAll('input[type=checkbox]:checked').length; + const badge = td.querySelector('.cmk-sk-acc-count'); + if (!badge) return; + if (checked > 0) { + badge.textContent = `(${checked})`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + + function initAccordionCheckedCounts(iDoc) { + const form = iDoc.getElementById('form_edit_host'); + if (!form) return false; + if (form.dataset.cmkAccBadge === '1') return true; + + injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` + .cmk-sk-acc-count { + margin-left: 6px; + padding: 1px 6px; + background: #f0a500; + color: #000; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } + `); + + iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { + const table = td.closest('table.nform'); + const tbody = table?.querySelector('tbody'); + if (!tbody) return; + + const badge = iDoc.createElement('span'); + badge.className = 'cmk-sk-acc-count'; + badge.style.display = 'none'; + + const img = td.querySelector('img.treeangle'); + const afterImg = img?.nextSibling; + if (afterImg) { + afterImg.after(badge); + } else { + td.appendChild(badge); + } + + updateAccordionBadge(td); + + tbody.addEventListener('change', (e) => { + if (e.target.type === 'checkbox') updateAccordionBadge(td); + }); + }); + + form.dataset.cmkAccBadge = '1'; + return true; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attempts = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); if (!iDoc || !iDoc.body) { - if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + if (++attemptsFolder < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); return; } @@ -426,16 +498,30 @@ } if (!sel.classList.contains('select2-hidden-accessible')) { - if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + if (++attemptsFolder < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); return; } buildCustomSearchOverlay(iDoc, sel); } + function tryInitAccordionCounts() { + const iDoc = getWatoDoc('form_edit_host'); + if (!iDoc || !iDoc.body) { + if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); + return; + } + if (!iDoc.getElementById('form_edit_host')) return; + if (!initAccordionCheckedCounts(iDoc)) { + if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); + } + } + function init() { - attempts = 0; + attemptsFolder = 0; + attemptsAcc = 0; setTimeout(tryEnhanceFolderSelect, 800); + setTimeout(tryInitAccordionCounts, 800); } if (document.readyState === 'complete') { @@ -450,9 +536,14 @@ if (!iDoc) return; const sel = iDoc.getElementById(FOLDER_SELECT_ID); if (sel && sel.classList.contains('select2-hidden-accessible') && !sel.dataset.cmkEnhanced) { - attempts = 0; + attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } + const form = iDoc.getElementById('form_edit_host'); + if (form && !form.dataset.cmkAccBadge) { + attemptsAcc = 0; + setTimeout(tryInitAccordionCounts, 300); + } }).observe(document.body, { childList: true, subtree: true }); // Riavvia al caricamento dell'iframe (layout con sidebar) From 761ca89cc9a082bac82b084d30684529b5431c7b Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:24:54 +0200 Subject: [PATCH 12/94] feat: aggiunto badge con conteggio checkbox attive per ogni accordion in edit_host Ogni titolo di gruppo (es. "Services - DB") mostra ora il numero di checkbox spuntate tra parentesi arancioni. Il contatore si aggiorna in tempo reale al cambio delle checkbox. Aggiornato anche homepageURL dal vecchio GitLab al nuovo Forgejo. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 105 +++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 13c2cb0..348d57a 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,10 +1,10 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.0 +// @version 2.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto -// @homepageURL https://gitlab.luigidacunto.com/consultant/checkmk-swissknife +// @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife // @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ @@ -406,16 +406,88 @@ } + // ========================================================================= + // FEATURE: Accordion Checked Count Badge + // + // Mostra nel titolo di ogni accordion della pagina edit_host il numero + // di checkbox attive nel gruppo. Es: "Services - DB (1)". + // Il contatore si aggiorna in tempo reale al cambio delle checkbox. + // ========================================================================= + + function updateAccordionBadge(td) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const checked = tbody.querySelectorAll('input[type=checkbox]:checked').length; + const badge = td.querySelector('.cmk-sk-acc-count'); + if (!badge) return; + if (checked > 0) { + badge.textContent = `(${checked})`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + + function initAccordionCheckedCounts(iDoc) { + const form = iDoc.getElementById('form_edit_host'); + if (!form) return false; + if (form.dataset.cmkAccBadge === '1') return true; + + injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` + .cmk-sk-acc-count { + margin-left: 6px; + padding: 1px 6px; + background: #f0a500; + color: #000; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } + `); + + iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { + const table = td.closest('table.nform'); + const tbody = table?.querySelector('tbody'); + if (!tbody) return; + + const badge = iDoc.createElement('span'); + badge.className = 'cmk-sk-acc-count'; + badge.style.display = 'none'; + + const img = td.querySelector('img.treeangle'); + const afterImg = img?.nextSibling; + if (afterImg) { + afterImg.after(badge); + } else { + td.appendChild(badge); + } + + updateAccordionBadge(td); + + tbody.addEventListener('change', (e) => { + if (e.target.type === 'checkbox') updateAccordionBadge(td); + }); + }); + + form.dataset.cmkAccBadge = '1'; + return true; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attempts = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); if (!iDoc || !iDoc.body) { - if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + if (++attemptsFolder < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); return; } @@ -426,16 +498,30 @@ } if (!sel.classList.contains('select2-hidden-accessible')) { - if (++attempts < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); + if (++attemptsFolder < MAX_ATTEMPTS) setTimeout(tryEnhanceFolderSelect, POLL_INTERVAL_MS); return; } buildCustomSearchOverlay(iDoc, sel); } + function tryInitAccordionCounts() { + const iDoc = getWatoDoc('form_edit_host'); + if (!iDoc || !iDoc.body) { + if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); + return; + } + if (!iDoc.getElementById('form_edit_host')) return; + if (!initAccordionCheckedCounts(iDoc)) { + if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); + } + } + function init() { - attempts = 0; + attemptsFolder = 0; + attemptsAcc = 0; setTimeout(tryEnhanceFolderSelect, 800); + setTimeout(tryInitAccordionCounts, 800); } if (document.readyState === 'complete') { @@ -450,9 +536,14 @@ if (!iDoc) return; const sel = iDoc.getElementById(FOLDER_SELECT_ID); if (sel && sel.classList.contains('select2-hidden-accessible') && !sel.dataset.cmkEnhanced) { - attempts = 0; + attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } + const form = iDoc.getElementById('form_edit_host'); + if (form && !form.dataset.cmkAccBadge) { + attemptsAcc = 0; + setTimeout(tryInitAccordionCounts, 300); + } }).observe(document.body, { childList: true, subtree: true }); // Riavvia al caricamento dell'iframe (layout con sidebar) From 09fcac4f48e7f64ebaf9f0c464b5899374627d79 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:36:57 +0200 Subject: [PATCH 13/94] feat: aggiunto badge blu con conteggio valori ereditati da folder parent negli accordion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accanto al badge arancione (checkbox spuntate), compare ora un badge blu "↑N" che conta le proprietà con valore esplicitamente ereditato da una cartella padre ("Inherited from X"), escludendo i semplici "(Default value)". Il contatore si aggiorna in tempo reale al cambio delle checkbox. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 44 ++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 348d57a..37d906d 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.1 +// @version 2.2 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -430,11 +430,31 @@ } } + function updateInheritedBadge(td, iWin) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const count = Array.from(tbody.querySelectorAll('div.inherited')).filter(el => + iWin.getComputedStyle(el).display !== 'none' && el.textContent.includes('Inherited from') + ).length; + const badge = td.querySelector('.cmk-sk-inh-count'); + if (!badge) return; + if (count > 0) { + badge.textContent = `↑${count}`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + function initAccordionCheckedCounts(iDoc) { const form = iDoc.getElementById('form_edit_host'); if (!form) return false; if (form.dataset.cmkAccBadge === '1') return true; + const iWin = iDoc.defaultView; + injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` .cmk-sk-acc-count { margin-left: 6px; @@ -446,6 +466,16 @@ font-weight: bold; vertical-align: middle; } + .cmk-sk-inh-count { + margin-left: 4px; + padding: 1px 6px; + background: #5ba4e5; + color: #000; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } `); iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { @@ -457,18 +487,28 @@ badge.className = 'cmk-sk-acc-count'; badge.style.display = 'none'; + const badgeInh = iDoc.createElement('span'); + badgeInh.className = 'cmk-sk-inh-count'; + badgeInh.style.display = 'none'; + const img = td.querySelector('img.treeangle'); const afterImg = img?.nextSibling; if (afterImg) { afterImg.after(badge); + badge.after(badgeInh); } else { td.appendChild(badge); + td.appendChild(badgeInh); } updateAccordionBadge(td); + updateInheritedBadge(td, iWin); tbody.addEventListener('change', (e) => { - if (e.target.type === 'checkbox') updateAccordionBadge(td); + if (e.target.type === 'checkbox') { + updateAccordionBadge(td); + updateInheritedBadge(td, iWin); + } }); }); From 516950514059b4b0fa9e18585a50a5a82d965bfd Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:36:57 +0200 Subject: [PATCH 14/94] feat: aggiunto badge blu con conteggio valori ereditati da folder parent negli accordion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accanto al badge arancione (checkbox spuntate), compare ora un badge blu "↑N" che conta le proprietà con valore esplicitamente ereditato da una cartella padre ("Inherited from X"), escludendo i semplici "(Default value)". Il contatore si aggiorna in tempo reale al cambio delle checkbox. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 44 ++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 348d57a..37d906d 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.1 +// @version 2.2 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -430,11 +430,31 @@ } } + function updateInheritedBadge(td, iWin) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const count = Array.from(tbody.querySelectorAll('div.inherited')).filter(el => + iWin.getComputedStyle(el).display !== 'none' && el.textContent.includes('Inherited from') + ).length; + const badge = td.querySelector('.cmk-sk-inh-count'); + if (!badge) return; + if (count > 0) { + badge.textContent = `↑${count}`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + function initAccordionCheckedCounts(iDoc) { const form = iDoc.getElementById('form_edit_host'); if (!form) return false; if (form.dataset.cmkAccBadge === '1') return true; + const iWin = iDoc.defaultView; + injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` .cmk-sk-acc-count { margin-left: 6px; @@ -446,6 +466,16 @@ font-weight: bold; vertical-align: middle; } + .cmk-sk-inh-count { + margin-left: 4px; + padding: 1px 6px; + background: #5ba4e5; + color: #000; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } `); iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { @@ -457,18 +487,28 @@ badge.className = 'cmk-sk-acc-count'; badge.style.display = 'none'; + const badgeInh = iDoc.createElement('span'); + badgeInh.className = 'cmk-sk-inh-count'; + badgeInh.style.display = 'none'; + const img = td.querySelector('img.treeangle'); const afterImg = img?.nextSibling; if (afterImg) { afterImg.after(badge); + badge.after(badgeInh); } else { td.appendChild(badge); + td.appendChild(badgeInh); } updateAccordionBadge(td); + updateInheritedBadge(td, iWin); tbody.addEventListener('change', (e) => { - if (e.target.type === 'checkbox') updateAccordionBadge(td); + if (e.target.type === 'checkbox') { + updateAccordionBadge(td); + updateInheritedBadge(td, iWin); + } }); }); From 953cad326c41a88e60f4dc6c18e8f2e1a9dac80c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:43:33 +0200 Subject: [PATCH 15/94] fix: guard URL-based per attivare le feature solo sulle pagine target Aggiunto helper getPageMode(iDoc) che legge il parametro "mode" dall'URL senza toccare il DOM. Accordion badges ora si attiva solo su mode=edit_host (guard in tryInitAccordionCounts, init e MutationObserver), evitando operazioni inutili e potenziali errori in console sulle altre pagine. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 37d906d..979cffb 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.2 +// @version 2.3 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -37,6 +37,12 @@ return null; } + // Legge il parametro "mode" dall'URL del documento target senza accedere al DOM + function getPageMode(iDoc) { + try { return new URLSearchParams(iDoc.location.search).get('mode') || ''; } + catch (e) { return ''; } + } + // Inietta CSS nel documento target (una sola volta, deduplica per id) function injectStyles(iDoc, id, css) { if (iDoc.getElementById(id)) return; @@ -551,17 +557,25 @@ if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); return; } - if (!iDoc.getElementById('form_edit_host')) return; + // Guard URL: attiva solo sulla pagina edit_host + if (getPageMode(iDoc) !== 'edit_host') return; if (!initAccordionCheckedCounts(iDoc)) { if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); } } function init() { + const iDoc = getWatoDoc(FOLDER_SELECT_ID); + const mode = getPageMode(iDoc); attemptsFolder = 0; attemptsAcc = 0; + // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - setTimeout(tryInitAccordionCounts, 800); + // Accordion: solo su edit_host. Se mode è vuoto (iframe non ancora caricato) + // si schedula comunque: tryInitAccordionCounts farà il guard URL. + if (!mode || mode === 'edit_host') { + setTimeout(tryInitAccordionCounts, 800); + } } if (document.readyState === 'complete') { @@ -574,15 +588,18 @@ new MutationObserver(() => { const iDoc = getWatoDoc(FOLDER_SELECT_ID); if (!iDoc) return; + const mode = getPageMode(iDoc); const sel = iDoc.getElementById(FOLDER_SELECT_ID); if (sel && sel.classList.contains('select2-hidden-accessible') && !sel.dataset.cmkEnhanced) { attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } - const form = iDoc.getElementById('form_edit_host'); - if (form && !form.dataset.cmkAccBadge) { - attemptsAcc = 0; - setTimeout(tryInitAccordionCounts, 300); + if (mode === 'edit_host') { + const form = iDoc.getElementById('form_edit_host'); + if (form && !form.dataset.cmkAccBadge) { + attemptsAcc = 0; + setTimeout(tryInitAccordionCounts, 300); + } } }).observe(document.body, { childList: true, subtree: true }); From f642f805d7cc9785a1b3cd40f24111c3360ff118 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:43:33 +0200 Subject: [PATCH 16/94] fix: guard URL-based per attivare le feature solo sulle pagine target Aggiunto helper getPageMode(iDoc) che legge il parametro "mode" dall'URL senza toccare il DOM. Accordion badges ora si attiva solo su mode=edit_host (guard in tryInitAccordionCounts, init e MutationObserver), evitando operazioni inutili e potenziali errori in console sulle altre pagine. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 37d906d..979cffb 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.2 +// @version 2.3 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -37,6 +37,12 @@ return null; } + // Legge il parametro "mode" dall'URL del documento target senza accedere al DOM + function getPageMode(iDoc) { + try { return new URLSearchParams(iDoc.location.search).get('mode') || ''; } + catch (e) { return ''; } + } + // Inietta CSS nel documento target (una sola volta, deduplica per id) function injectStyles(iDoc, id, css) { if (iDoc.getElementById(id)) return; @@ -551,17 +557,25 @@ if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); return; } - if (!iDoc.getElementById('form_edit_host')) return; + // Guard URL: attiva solo sulla pagina edit_host + if (getPageMode(iDoc) !== 'edit_host') return; if (!initAccordionCheckedCounts(iDoc)) { if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); } } function init() { + const iDoc = getWatoDoc(FOLDER_SELECT_ID); + const mode = getPageMode(iDoc); attemptsFolder = 0; attemptsAcc = 0; + // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - setTimeout(tryInitAccordionCounts, 800); + // Accordion: solo su edit_host. Se mode è vuoto (iframe non ancora caricato) + // si schedula comunque: tryInitAccordionCounts farà il guard URL. + if (!mode || mode === 'edit_host') { + setTimeout(tryInitAccordionCounts, 800); + } } if (document.readyState === 'complete') { @@ -574,15 +588,18 @@ new MutationObserver(() => { const iDoc = getWatoDoc(FOLDER_SELECT_ID); if (!iDoc) return; + const mode = getPageMode(iDoc); const sel = iDoc.getElementById(FOLDER_SELECT_ID); if (sel && sel.classList.contains('select2-hidden-accessible') && !sel.dataset.cmkEnhanced) { attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } - const form = iDoc.getElementById('form_edit_host'); - if (form && !form.dataset.cmkAccBadge) { - attemptsAcc = 0; - setTimeout(tryInitAccordionCounts, 300); + if (mode === 'edit_host') { + const form = iDoc.getElementById('form_edit_host'); + if (form && !form.dataset.cmkAccBadge) { + attemptsAcc = 0; + setTimeout(tryInitAccordionCounts, 300); + } } }).observe(document.body, { childList: true, subtree: true }); From b775467d91145b2370d1c9f53c1d4ba61eed7bc2 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:51:35 +0200 Subject: [PATCH 17/94] feat: estesi accordion badge anche alla pagina bulkedit Aggiunta costante ACCORDION_MODES (Set con 'edit_host' e 'bulkedit') per centralizzare il guard URL. I badge di conteggio checkbox e valori ereditati ora si attivano su entrambe le pagine di modifica host, singola e massiva. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 979cffb..86d2a00 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.3 +// @version 2.4 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -43,6 +43,9 @@ catch (e) { return ''; } } + // Pagine che supportano gli accordion badge (stessa struttura form_edit_host + table.nform) + const ACCORDION_MODES = new Set(['edit_host', 'bulkedit']); + // Inietta CSS nel documento target (una sola volta, deduplica per id) function injectStyles(iDoc, id, css) { if (iDoc.getElementById(id)) return; @@ -557,8 +560,8 @@ if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); return; } - // Guard URL: attiva solo sulla pagina edit_host - if (getPageMode(iDoc) !== 'edit_host') return; + // Guard URL: attiva solo sulle pagine con accordion supportati + if (!ACCORDION_MODES.has(getPageMode(iDoc))) return; if (!initAccordionCheckedCounts(iDoc)) { if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); } @@ -571,9 +574,9 @@ attemptsAcc = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - // Accordion: solo su edit_host. Se mode è vuoto (iframe non ancora caricato) - // si schedula comunque: tryInitAccordionCounts farà il guard URL. - if (!mode || mode === 'edit_host') { + // Accordion: solo sulle pagine in ACCORDION_MODES. Se mode è vuoto (iframe non ancora + // caricato) si schedula comunque: tryInitAccordionCounts farà il guard URL. + if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } } @@ -594,7 +597,7 @@ attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } - if (mode === 'edit_host') { + if (ACCORDION_MODES.has(mode)) { const form = iDoc.getElementById('form_edit_host'); if (form && !form.dataset.cmkAccBadge) { attemptsAcc = 0; From cf30ac2024119f33082c79ac2f119d226e7da1ed Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:51:35 +0200 Subject: [PATCH 18/94] feat: estesi accordion badge anche alla pagina bulkedit Aggiunta costante ACCORDION_MODES (Set con 'edit_host' e 'bulkedit') per centralizzare il guard URL. I badge di conteggio checkbox e valori ereditati ora si attivano su entrambe le pagine di modifica host, singola e massiva. Co-Authored-By: Claude Sonnet 4.6 --- user_script/checkmk_swissknife.user.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 979cffb..86d2a00 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.3 +// @version 2.4 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -43,6 +43,9 @@ catch (e) { return ''; } } + // Pagine che supportano gli accordion badge (stessa struttura form_edit_host + table.nform) + const ACCORDION_MODES = new Set(['edit_host', 'bulkedit']); + // Inietta CSS nel documento target (una sola volta, deduplica per id) function injectStyles(iDoc, id, css) { if (iDoc.getElementById(id)) return; @@ -557,8 +560,8 @@ if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); return; } - // Guard URL: attiva solo sulla pagina edit_host - if (getPageMode(iDoc) !== 'edit_host') return; + // Guard URL: attiva solo sulle pagine con accordion supportati + if (!ACCORDION_MODES.has(getPageMode(iDoc))) return; if (!initAccordionCheckedCounts(iDoc)) { if (++attemptsAcc < MAX_ATTEMPTS) setTimeout(tryInitAccordionCounts, POLL_INTERVAL_MS); } @@ -571,9 +574,9 @@ attemptsAcc = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - // Accordion: solo su edit_host. Se mode è vuoto (iframe non ancora caricato) - // si schedula comunque: tryInitAccordionCounts farà il guard URL. - if (!mode || mode === 'edit_host') { + // Accordion: solo sulle pagine in ACCORDION_MODES. Se mode è vuoto (iframe non ancora + // caricato) si schedula comunque: tryInitAccordionCounts farà il guard URL. + if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } } @@ -594,7 +597,7 @@ attemptsFolder = 0; setTimeout(tryEnhanceFolderSelect, 300); } - if (mode === 'edit_host') { + if (ACCORDION_MODES.has(mode)) { const form = iDoc.getElementById('form_edit_host'); if (form && !form.dataset.cmkAccBadge) { attemptsAcc = 0; From 87a6de62e6e88bd8afded07bedcd4896e560481f Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:56:47 +0200 Subject: [PATCH 19/94] feat: aggiunto badge rosso con conteggio valori in conflitto tra host (bulkedit) Terzo badge con simbolo neq N (rosso) che conta le proprieta con This value differs between the selected hosts. Appare solo sulla pagina bulkedit dove ha senso. Aggiunto updateDiffBadge() e CSS .cmk-sk-diff-count. --- user_script/checkmk_swissknife.user.js | 40 +++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 86d2a00..bdba804 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.4 +// @version 2.5 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -457,12 +457,31 @@ } } + function updateDiffBadge(td, iWin) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const count = Array.from(tbody.querySelectorAll('div.inherited')).filter(el => + iWin.getComputedStyle(el).display !== 'none' && el.textContent.includes('This value differs') + ).length; + const badge = td.querySelector('.cmk-sk-diff-count'); + if (!badge) return; + if (count > 0) { + badge.textContent = `≠${count}`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + function initAccordionCheckedCounts(iDoc) { const form = iDoc.getElementById('form_edit_host'); if (!form) return false; if (form.dataset.cmkAccBadge === '1') return true; const iWin = iDoc.defaultView; + const isBulkEdit = getPageMode(iDoc) === 'bulkedit'; injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` .cmk-sk-acc-count { @@ -485,6 +504,16 @@ font-weight: bold; vertical-align: middle; } + .cmk-sk-diff-count { + margin-left: 4px; + padding: 1px 6px; + background: #e55b5b; + color: #fff; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } `); iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { @@ -510,13 +539,22 @@ td.appendChild(badgeInh); } + if (isBulkEdit) { + const badgeDiff = iDoc.createElement('span'); + badgeDiff.className = 'cmk-sk-diff-count'; + badgeDiff.style.display = 'none'; + badgeInh.after(badgeDiff); + } + updateAccordionBadge(td); updateInheritedBadge(td, iWin); + if (isBulkEdit) updateDiffBadge(td, iWin); tbody.addEventListener('change', (e) => { if (e.target.type === 'checkbox') { updateAccordionBadge(td); updateInheritedBadge(td, iWin); + if (isBulkEdit) updateDiffBadge(td, iWin); } }); }); From 1e0e9a4b1688bfc3fb12a98bb51b8d373c429fba Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:56:47 +0200 Subject: [PATCH 20/94] feat: aggiunto badge rosso con conteggio valori in conflitto tra host (bulkedit) Terzo badge con simbolo neq N (rosso) che conta le proprieta con This value differs between the selected hosts. Appare solo sulla pagina bulkedit dove ha senso. Aggiunto updateDiffBadge() e CSS .cmk-sk-diff-count. --- user_script/checkmk_swissknife.user.js | 40 +++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 86d2a00..bdba804 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.4 +// @version 2.5 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -457,12 +457,31 @@ } } + function updateDiffBadge(td, iWin) { + const table = td.closest('table.nform'); + if (!table) return; + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const count = Array.from(tbody.querySelectorAll('div.inherited')).filter(el => + iWin.getComputedStyle(el).display !== 'none' && el.textContent.includes('This value differs') + ).length; + const badge = td.querySelector('.cmk-sk-diff-count'); + if (!badge) return; + if (count > 0) { + badge.textContent = `≠${count}`; + badge.style.display = 'inline'; + } else { + badge.style.display = 'none'; + } + } + function initAccordionCheckedCounts(iDoc) { const form = iDoc.getElementById('form_edit_host'); if (!form) return false; if (form.dataset.cmkAccBadge === '1') return true; const iWin = iDoc.defaultView; + const isBulkEdit = getPageMode(iDoc) === 'bulkedit'; injectStyles(iDoc, 'cmk-sk-acc-badge-styles', ` .cmk-sk-acc-count { @@ -485,6 +504,16 @@ font-weight: bold; vertical-align: middle; } + .cmk-sk-diff-count { + margin-left: 4px; + padding: 1px 6px; + background: #e55b5b; + color: #fff; + border-radius: 9px; + font-size: 11px; + font-weight: bold; + vertical-align: middle; + } `); iDoc.querySelectorAll('table.nform thead tr.heading td').forEach(td => { @@ -510,13 +539,22 @@ td.appendChild(badgeInh); } + if (isBulkEdit) { + const badgeDiff = iDoc.createElement('span'); + badgeDiff.className = 'cmk-sk-diff-count'; + badgeDiff.style.display = 'none'; + badgeInh.after(badgeDiff); + } + updateAccordionBadge(td); updateInheritedBadge(td, iWin); + if (isBulkEdit) updateDiffBadge(td, iWin); tbody.addEventListener('change', (e) => { if (e.target.type === 'checkbox') { updateAccordionBadge(td); updateInheritedBadge(td, iWin); + if (isBulkEdit) updateDiffBadge(td, iWin); } }); }); From 9d4e22e9668e4aae8ad330f59575552afabceb1c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:00 +0200 Subject: [PATCH 21/94] feat: highlight visivo regole ineffective in pagine edit_ruleset Aggiunge badge arancione 'ineffective' e bordo sinistro alle righe marcate con icon_hyphen.svg (title='Ineffective rule'). Funziona sia su wato.py diretto che dentro index.py con sidebar. Aggiunto helper getTargetDoc() per gestire entrambi i casi. Attivazione via guard URL mode=edit_ruleset. --- user_script/checkmk_swissknife.user.js | 97 ++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index bdba804..537cc71 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.5 +// @version 2.6 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -43,6 +43,14 @@ catch (e) { return ''; } } + // Restituisce il documento su cui operare, gestendo sia il caso iframe (index.py con + // sidebar) che il caso direct (wato.py aperto senza sidebar, nessun iframe presente). + function getTargetDoc() { + const iframe = document.querySelector('iframe[name="main"], iframe#main'); + if (iframe) { try { return iframe.contentDocument; } catch (e) { return null; } } + return document; + } + // Pagine che supportano gli accordion badge (stessa struttura form_edit_host + table.nform) const ACCORDION_MODES = new Set(['edit_host', 'bulkedit']); @@ -564,12 +572,67 @@ } + // ========================================================================= + // FEATURE: Ineffective Rule Highlight + // + // Nelle pagine mode=edit_ruleset sostituisce l'icona poco visibile + // "icon_hyphen.svg" (title="Ineffective rule") con un badge colorato + // e aggiunge un bordo sinistro alla riga. + // Funziona sia su wato.py diretto (no iframe) che dentro index.py (iframe). + // ========================================================================= + + function highlightIneffectiveRules(doc) { + if (doc.body.dataset.cmkIneffHighlight === '1') return; + + const imgs = doc.querySelectorAll('img.icon[title="Ineffective rule"]'); + if (!imgs.length) return; + + injectStyles(doc, 'cmk-sk-ineff-styles', ` + tr.cmk-sk-ineffective > td:first-child { + border-left: 4px solid #e5a500 !important; + } + tr.cmk-sk-ineffective { + background: rgba(229, 165, 0, 0.08) !important; + } + .cmk-sk-ineff-badge { + display: inline-block; + background: #e5a500; + color: #000; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + `); + + imgs.forEach(img => { + const row = img.closest('tr'); + if (!row || row.classList.contains('cmk-sk-ineffective')) return; + row.classList.add('cmk-sk-ineffective'); + + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-ineff-badge'; + badge.title = 'Ineffective rule'; + badge.textContent = '⚠ ineffective'; + img.replaceWith(badge); + }); + + doc.body.dataset.cmkIneffHighlight = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attemptsFolder = 0; - let attemptsAcc = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; + let attemptsRuleset = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); @@ -605,18 +668,34 @@ } } + function tryHighlightIneffective() { + const doc = getTargetDoc(); + if (!doc || !doc.body) { + if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightIneffective, POLL_INTERVAL_MS); + return; + } + if (getPageMode(doc) !== 'edit_ruleset') return; + highlightIneffectiveRules(doc); + } + function init() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); const mode = getPageMode(iDoc); - attemptsFolder = 0; - attemptsAcc = 0; + const targetDoc = getTargetDoc(); + const targetMode = getPageMode(targetDoc); + attemptsFolder = 0; + attemptsAcc = 0; + attemptsRuleset = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - // Accordion: solo sulle pagine in ACCORDION_MODES. Se mode è vuoto (iframe non ancora - // caricato) si schedula comunque: tryInitAccordionCounts farà il guard URL. + // Accordion: solo sulle pagine in ACCORDION_MODES. if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } + // Ineffective rule highlight: solo su edit_ruleset. + if (!targetMode || targetMode === 'edit_ruleset') { + setTimeout(tryHighlightIneffective, 300); + } } if (document.readyState === 'complete') { @@ -642,6 +721,10 @@ setTimeout(tryInitAccordionCounts, 300); } } + if (mode === 'edit_ruleset' && iDoc.body && !iDoc.body.dataset.cmkIneffHighlight) { + attemptsRuleset = 0; + setTimeout(tryHighlightIneffective, 300); + } }).observe(document.body, { childList: true, subtree: true }); // Riavvia al caricamento dell'iframe (layout con sidebar) From 7a39b5e3f891620bee32c951b09f200af29302d6 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:00 +0200 Subject: [PATCH 22/94] feat: highlight visivo regole ineffective in pagine edit_ruleset Aggiunge badge arancione 'ineffective' e bordo sinistro alle righe marcate con icon_hyphen.svg (title='Ineffective rule'). Funziona sia su wato.py diretto che dentro index.py con sidebar. Aggiunto helper getTargetDoc() per gestire entrambi i casi. Attivazione via guard URL mode=edit_ruleset. --- user_script/checkmk_swissknife.user.js | 97 ++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index bdba804..537cc71 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.5 +// @version 2.6 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -43,6 +43,14 @@ catch (e) { return ''; } } + // Restituisce il documento su cui operare, gestendo sia il caso iframe (index.py con + // sidebar) che il caso direct (wato.py aperto senza sidebar, nessun iframe presente). + function getTargetDoc() { + const iframe = document.querySelector('iframe[name="main"], iframe#main'); + if (iframe) { try { return iframe.contentDocument; } catch (e) { return null; } } + return document; + } + // Pagine che supportano gli accordion badge (stessa struttura form_edit_host + table.nform) const ACCORDION_MODES = new Set(['edit_host', 'bulkedit']); @@ -564,12 +572,67 @@ } + // ========================================================================= + // FEATURE: Ineffective Rule Highlight + // + // Nelle pagine mode=edit_ruleset sostituisce l'icona poco visibile + // "icon_hyphen.svg" (title="Ineffective rule") con un badge colorato + // e aggiunge un bordo sinistro alla riga. + // Funziona sia su wato.py diretto (no iframe) che dentro index.py (iframe). + // ========================================================================= + + function highlightIneffectiveRules(doc) { + if (doc.body.dataset.cmkIneffHighlight === '1') return; + + const imgs = doc.querySelectorAll('img.icon[title="Ineffective rule"]'); + if (!imgs.length) return; + + injectStyles(doc, 'cmk-sk-ineff-styles', ` + tr.cmk-sk-ineffective > td:first-child { + border-left: 4px solid #e5a500 !important; + } + tr.cmk-sk-ineffective { + background: rgba(229, 165, 0, 0.08) !important; + } + .cmk-sk-ineff-badge { + display: inline-block; + background: #e5a500; + color: #000; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + `); + + imgs.forEach(img => { + const row = img.closest('tr'); + if (!row || row.classList.contains('cmk-sk-ineffective')) return; + row.classList.add('cmk-sk-ineffective'); + + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-ineff-badge'; + badge.title = 'Ineffective rule'; + badge.textContent = '⚠ ineffective'; + img.replaceWith(badge); + }); + + doc.body.dataset.cmkIneffHighlight = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attemptsFolder = 0; - let attemptsAcc = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; + let attemptsRuleset = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); @@ -605,18 +668,34 @@ } } + function tryHighlightIneffective() { + const doc = getTargetDoc(); + if (!doc || !doc.body) { + if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightIneffective, POLL_INTERVAL_MS); + return; + } + if (getPageMode(doc) !== 'edit_ruleset') return; + highlightIneffectiveRules(doc); + } + function init() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); const mode = getPageMode(iDoc); - attemptsFolder = 0; - attemptsAcc = 0; + const targetDoc = getTargetDoc(); + const targetMode = getPageMode(targetDoc); + attemptsFolder = 0; + attemptsAcc = 0; + attemptsRuleset = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); - // Accordion: solo sulle pagine in ACCORDION_MODES. Se mode è vuoto (iframe non ancora - // caricato) si schedula comunque: tryInitAccordionCounts farà il guard URL. + // Accordion: solo sulle pagine in ACCORDION_MODES. if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } + // Ineffective rule highlight: solo su edit_ruleset. + if (!targetMode || targetMode === 'edit_ruleset') { + setTimeout(tryHighlightIneffective, 300); + } } if (document.readyState === 'complete') { @@ -642,6 +721,10 @@ setTimeout(tryInitAccordionCounts, 300); } } + if (mode === 'edit_ruleset' && iDoc.body && !iDoc.body.dataset.cmkIneffHighlight) { + attemptsRuleset = 0; + setTimeout(tryHighlightIneffective, 300); + } }).observe(document.body, { childList: true, subtree: true }); // Riavvia al caricamento dell'iframe (layout con sidebar) From 718f934ba8fa335f1b6a44c492247a9ebaa3c2c6 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:40 +0200 Subject: [PATCH 23/94] chore: migrazione versioning a SemVer major.minor.patch (2.6.0) --- user_script/checkmk_swissknife.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 537cc71..cecd5e8 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.6 +// @version 2.6.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife From 0b5bfb78b207d4c73bae927438723a316793eada Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:40 +0200 Subject: [PATCH 24/94] chore: migrazione versioning a SemVer major.minor.patch (2.6.0) --- user_script/checkmk_swissknife.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 537cc71..cecd5e8 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.6 +// @version 2.6.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife From 9d479a1cb725f10dae8ffce6f261996f1bf5e49d Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:11:22 +0200 Subject: [PATCH 25/94] feat: highlight match/no-match nelle pagine edit_ruleset con contesto host Aggiunge badge verde (checkmark) alle regole che matchano e badge grigio dimmed alle regole che non matchano, sostituendo le icone poco visibili. La funzione tryHighlightRuleset ora gestisce entrambe le feature ruleset (ineffective + match status) sulle pagine mode=edit_ruleset. --- user_script/checkmk_swissknife.user.js | 101 +++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index cecd5e8..3fa9857 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.6.0 +// @version 2.7.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -626,6 +626,91 @@ } + // ========================================================================= + // FEATURE: Rule Match Status Highlight + // + // Nelle pagine mode=edit_ruleset aperte con contesto host/service (parametri + // host= e service= nell'URL), evidenzia le righe che matchano (verde) e + // attenua quelle che non matchano (grigio), sostituendo le icone poco + // visibili icon_checkmark e icon_hyphen con badge colorati. + // ========================================================================= + + function highlightRuleMatchStatus(doc) { + if (doc.body.dataset.cmkMatchHighlight === '1') return; + + const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"]'); + const noMatchImgs = doc.querySelectorAll('img.icon[title^="This rule does not match"]'); + if (!matchImgs.length && !noMatchImgs.length) return; + + injectStyles(doc, 'cmk-sk-match-styles', ` + tr.cmk-sk-rule-match > td:first-child { + border-left: 4px solid #4caf50 !important; + } + tr.cmk-sk-rule-match { + background: rgba(76, 175, 80, 0.10) !important; + } + tr.cmk-sk-rule-nomatch { + opacity: 0.45; + } + tr.cmk-sk-rule-nomatch > td:first-child { + border-left: 4px solid #555 !important; + } + .cmk-sk-match-badge { + display: inline-block; + background: #4caf50; + color: #fff; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + .cmk-sk-nomatch-badge { + display: inline-block; + background: #444; + color: #888; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + `); + + matchImgs.forEach(img => { + const row = img.closest('tr'); + if (!row) return; + row.classList.add('cmk-sk-rule-match'); + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-match-badge'; + badge.title = img.title; + badge.textContent = '✓ match'; + img.replaceWith(badge); + }); + + noMatchImgs.forEach(img => { + const row = img.closest('tr'); + if (!row) return; + row.classList.add('cmk-sk-rule-nomatch'); + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-nomatch-badge'; + badge.title = img.title; + badge.textContent = '✗ no match'; + img.replaceWith(badge); + }); + + doc.body.dataset.cmkMatchHighlight = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= @@ -668,14 +753,15 @@ } } - function tryHighlightIneffective() { + function tryHighlightRuleset() { const doc = getTargetDoc(); if (!doc || !doc.body) { - if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightIneffective, POLL_INTERVAL_MS); + if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightRuleset, POLL_INTERVAL_MS); return; } if (getPageMode(doc) !== 'edit_ruleset') return; highlightIneffectiveRules(doc); + highlightRuleMatchStatus(doc); } function init() { @@ -692,9 +778,9 @@ if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } - // Ineffective rule highlight: solo su edit_ruleset. + // Ruleset enhancements (ineffective + match status): solo su edit_ruleset. if (!targetMode || targetMode === 'edit_ruleset') { - setTimeout(tryHighlightIneffective, 300); + setTimeout(tryHighlightRuleset, 300); } } @@ -721,9 +807,10 @@ setTimeout(tryInitAccordionCounts, 300); } } - if (mode === 'edit_ruleset' && iDoc.body && !iDoc.body.dataset.cmkIneffHighlight) { + if (mode === 'edit_ruleset' && iDoc.body && + (!iDoc.body.dataset.cmkIneffHighlight || !iDoc.body.dataset.cmkMatchHighlight)) { attemptsRuleset = 0; - setTimeout(tryHighlightIneffective, 300); + setTimeout(tryHighlightRuleset, 300); } }).observe(document.body, { childList: true, subtree: true }); From 40878df517133463b4c835746cffa452cbda3abf Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:11:22 +0200 Subject: [PATCH 26/94] feat: highlight match/no-match nelle pagine edit_ruleset con contesto host Aggiunge badge verde (checkmark) alle regole che matchano e badge grigio dimmed alle regole che non matchano, sostituendo le icone poco visibili. La funzione tryHighlightRuleset ora gestisce entrambe le feature ruleset (ineffective + match status) sulle pagine mode=edit_ruleset. --- user_script/checkmk_swissknife.user.js | 101 +++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index cecd5e8..3fa9857 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.6.0 +// @version 2.7.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -626,6 +626,91 @@ } + // ========================================================================= + // FEATURE: Rule Match Status Highlight + // + // Nelle pagine mode=edit_ruleset aperte con contesto host/service (parametri + // host= e service= nell'URL), evidenzia le righe che matchano (verde) e + // attenua quelle che non matchano (grigio), sostituendo le icone poco + // visibili icon_checkmark e icon_hyphen con badge colorati. + // ========================================================================= + + function highlightRuleMatchStatus(doc) { + if (doc.body.dataset.cmkMatchHighlight === '1') return; + + const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"]'); + const noMatchImgs = doc.querySelectorAll('img.icon[title^="This rule does not match"]'); + if (!matchImgs.length && !noMatchImgs.length) return; + + injectStyles(doc, 'cmk-sk-match-styles', ` + tr.cmk-sk-rule-match > td:first-child { + border-left: 4px solid #4caf50 !important; + } + tr.cmk-sk-rule-match { + background: rgba(76, 175, 80, 0.10) !important; + } + tr.cmk-sk-rule-nomatch { + opacity: 0.45; + } + tr.cmk-sk-rule-nomatch > td:first-child { + border-left: 4px solid #555 !important; + } + .cmk-sk-match-badge { + display: inline-block; + background: #4caf50; + color: #fff; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + .cmk-sk-nomatch-badge { + display: inline-block; + background: #444; + color: #888; + font-size: 10px; + font-weight: bold; + padding: 2px 6px; + border-radius: 3px; + white-space: nowrap; + font-family: monospace; + vertical-align: middle; + cursor: default; + letter-spacing: 0.03em; + } + `); + + matchImgs.forEach(img => { + const row = img.closest('tr'); + if (!row) return; + row.classList.add('cmk-sk-rule-match'); + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-match-badge'; + badge.title = img.title; + badge.textContent = '✓ match'; + img.replaceWith(badge); + }); + + noMatchImgs.forEach(img => { + const row = img.closest('tr'); + if (!row) return; + row.classList.add('cmk-sk-rule-nomatch'); + const badge = doc.createElement('span'); + badge.className = 'cmk-sk-nomatch-badge'; + badge.title = img.title; + badge.textContent = '✗ no match'; + img.replaceWith(badge); + }); + + doc.body.dataset.cmkMatchHighlight = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= @@ -668,14 +753,15 @@ } } - function tryHighlightIneffective() { + function tryHighlightRuleset() { const doc = getTargetDoc(); if (!doc || !doc.body) { - if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightIneffective, POLL_INTERVAL_MS); + if (++attemptsRuleset < MAX_ATTEMPTS) setTimeout(tryHighlightRuleset, POLL_INTERVAL_MS); return; } if (getPageMode(doc) !== 'edit_ruleset') return; highlightIneffectiveRules(doc); + highlightRuleMatchStatus(doc); } function init() { @@ -692,9 +778,9 @@ if (!mode || ACCORDION_MODES.has(mode)) { setTimeout(tryInitAccordionCounts, 800); } - // Ineffective rule highlight: solo su edit_ruleset. + // Ruleset enhancements (ineffective + match status): solo su edit_ruleset. if (!targetMode || targetMode === 'edit_ruleset') { - setTimeout(tryHighlightIneffective, 300); + setTimeout(tryHighlightRuleset, 300); } } @@ -721,9 +807,10 @@ setTimeout(tryInitAccordionCounts, 300); } } - if (mode === 'edit_ruleset' && iDoc.body && !iDoc.body.dataset.cmkIneffHighlight) { + if (mode === 'edit_ruleset' && iDoc.body && + (!iDoc.body.dataset.cmkIneffHighlight || !iDoc.body.dataset.cmkMatchHighlight)) { attemptsRuleset = 0; - setTimeout(tryHighlightIneffective, 300); + setTimeout(tryHighlightRuleset, 300); } }).observe(document.body, { childList: true, subtree: true }); From 636860223c76e37b55cfa47f22ac8fee7b275ed3 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:17:31 +0200 Subject: [PATCH 27/94] feat: estendi badge match alle ricerche per tag su edit_ruleset Le pagine edit_ruleset aperte da una ricerca per hosttag usano title="Matches" invece di title^="This rule matches". Esteso il selettore CSS per coprire entrambi i casi. bump version to 2.7.1 --- user_script/checkmk_swissknife.user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 3fa9857..4377823 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.7.0 +// @version 2.7.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -638,7 +638,7 @@ function highlightRuleMatchStatus(doc) { if (doc.body.dataset.cmkMatchHighlight === '1') return; - const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"]'); + const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"], img.icon[title="Matches"]'); const noMatchImgs = doc.querySelectorAll('img.icon[title^="This rule does not match"]'); if (!matchImgs.length && !noMatchImgs.length) return; From 8093f2f1c83b56db085b83bcbc7b32ae8eda23ca Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:17:31 +0200 Subject: [PATCH 28/94] feat: estendi badge match alle ricerche per tag su edit_ruleset Le pagine edit_ruleset aperte da una ricerca per hosttag usano title="Matches" invece di title^="This rule matches". Esteso il selettore CSS per coprire entrambi i casi. bump version to 2.7.1 --- user_script/checkmk_swissknife.user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 3fa9857..4377823 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.7.0 +// @version 2.7.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -638,7 +638,7 @@ function highlightRuleMatchStatus(doc) { if (doc.body.dataset.cmkMatchHighlight === '1') return; - const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"]'); + const matchImgs = doc.querySelectorAll('img.icon[title^="This rule matches"], img.icon[title="Matches"]'); const noMatchImgs = doc.querySelectorAll('img.icon[title^="This rule does not match"]'); if (!matchImgs.length && !noMatchImgs.length) return; From 8444be79469e2196742e8d7bc9fc782ac719fd50 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:38:33 +0200 Subject: [PATCH 29/94] feat: aggiungi toggle filtra/mostra righe irrilevanti su edit_ruleset Aggiunge una barra in cima alle pagine edit_ruleset con un pulsante "Solo rilevanti" / "Mostra tutto" che nasconde le righe e i folder senza regole rilevanti (match, no-match, ineffective). Utile quando su centinaia di regole solo poche sono evidenziate dalla ricerca. bump version to 2.8.0 --- user_script/checkmk_swissknife.user.js | 93 +++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 4377823..1906b08 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.7.1 +// @version 2.8.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -711,6 +711,96 @@ } + // ========================================================================= + // FEATURE: Ruleset Filter Toggle + // + // Dopo che le funzioni di highlight hanno marcato le righe rilevanti + // (match, no-match, ineffective), aggiunge una barra in cima con un pulsante + // per nascondere le righe e i folder senza alcuna rilevanza. Utile su + // pagine con centinaia di regole dove quelle rilevanti sono poche. + // ========================================================================= + + function addRulesetFilterToggle(doc) { + if (doc.body.dataset.cmkFilterToggle === '1') return; + doc.body.dataset.cmkFilterToggle = '1'; + + const RELEVANT_SEL = 'tr.cmk-sk-rule-match, tr.cmk-sk-rule-nomatch, tr.cmk-sk-ineffective'; + + doc.querySelectorAll('tr.data').forEach(row => { + if (!row.matches(RELEVANT_SEL)) row.classList.add('cmk-sk-irrelevant-row'); + }); + + doc.querySelectorAll('div.foldable_wrapper').forEach(wrapper => { + if (!wrapper.querySelector(RELEVANT_SEL)) wrapper.classList.add('cmk-sk-irrelevant-folder'); + }); + + const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; + const irrelevantRows = doc.querySelectorAll('tr.cmk-sk-irrelevant-row').length; + const irrelevantFolders = doc.querySelectorAll('div.foldable_wrapper.cmk-sk-irrelevant-folder').length; + + if (!irrelevantRows && !irrelevantFolders) return; + + injectStyles(doc, 'cmk-sk-filter-toggle-styles', ` + #cmk-sk-filter-bar { + display: flex; + align-items: center; + gap: 10px; + padding: 5px 10px; + margin: 6px 0 4px 0; + background: rgba(0,0,0,0.25); + border: 1px solid #3a3a3a; + border-radius: 4px; + font-size: 11px; + color: #999; + font-family: monospace; + } + #cmk-sk-filter-toggle-btn { + cursor: pointer; + padding: 3px 10px; + border-radius: 3px; + border: 1px solid #555; + background: #2a2a2a; + color: #bbb; + font-size: 11px; + font-family: monospace; + font-weight: bold; + letter-spacing: 0.03em; + } + #cmk-sk-filter-toggle-btn:hover { background: #383838; } + #cmk-sk-filter-toggle-btn.active { + background: #1c3320; + border-color: #4caf50; + color: #4caf50; + } + body.cmk-sk-filter-active tr.cmk-sk-irrelevant-row { display: none !important; } + body.cmk-sk-filter-active div.foldable_wrapper.cmk-sk-irrelevant-folder { display: none !important; } + `); + + const bar = doc.createElement('div'); + bar.id = 'cmk-sk-filter-bar'; + + const btn = doc.createElement('button'); + btn.id = 'cmk-sk-filter-toggle-btn'; + btn.type = 'button'; + btn.textContent = 'Solo rilevanti'; + + const info = doc.createElement('span'); + info.textContent = `${relevantCount} rilevanti · ${irrelevantRows} righe e ${irrelevantFolders} folder non rilevanti`; + + btn.addEventListener('click', () => { + const isActive = doc.body.classList.toggle('cmk-sk-filter-active'); + btn.classList.toggle('active', isActive); + btn.textContent = isActive ? 'Mostra tutto' : 'Solo rilevanti'; + }); + + bar.appendChild(btn); + bar.appendChild(info); + + const anchor = doc.querySelector('div.foldable_wrapper') || doc.querySelector('div.wato'); + if (anchor) anchor.before(bar); + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= @@ -762,6 +852,7 @@ if (getPageMode(doc) !== 'edit_ruleset') return; highlightIneffectiveRules(doc); highlightRuleMatchStatus(doc); + addRulesetFilterToggle(doc); } function init() { From d76fe201d81c2824163e1688640e7a1dca2b5369 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:38:33 +0200 Subject: [PATCH 30/94] feat: aggiungi toggle filtra/mostra righe irrilevanti su edit_ruleset Aggiunge una barra in cima alle pagine edit_ruleset con un pulsante "Solo rilevanti" / "Mostra tutto" che nasconde le righe e i folder senza regole rilevanti (match, no-match, ineffective). Utile quando su centinaia di regole solo poche sono evidenziate dalla ricerca. bump version to 2.8.0 --- user_script/checkmk_swissknife.user.js | 93 +++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 4377823..1906b08 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.7.1 +// @version 2.8.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -711,6 +711,96 @@ } + // ========================================================================= + // FEATURE: Ruleset Filter Toggle + // + // Dopo che le funzioni di highlight hanno marcato le righe rilevanti + // (match, no-match, ineffective), aggiunge una barra in cima con un pulsante + // per nascondere le righe e i folder senza alcuna rilevanza. Utile su + // pagine con centinaia di regole dove quelle rilevanti sono poche. + // ========================================================================= + + function addRulesetFilterToggle(doc) { + if (doc.body.dataset.cmkFilterToggle === '1') return; + doc.body.dataset.cmkFilterToggle = '1'; + + const RELEVANT_SEL = 'tr.cmk-sk-rule-match, tr.cmk-sk-rule-nomatch, tr.cmk-sk-ineffective'; + + doc.querySelectorAll('tr.data').forEach(row => { + if (!row.matches(RELEVANT_SEL)) row.classList.add('cmk-sk-irrelevant-row'); + }); + + doc.querySelectorAll('div.foldable_wrapper').forEach(wrapper => { + if (!wrapper.querySelector(RELEVANT_SEL)) wrapper.classList.add('cmk-sk-irrelevant-folder'); + }); + + const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; + const irrelevantRows = doc.querySelectorAll('tr.cmk-sk-irrelevant-row').length; + const irrelevantFolders = doc.querySelectorAll('div.foldable_wrapper.cmk-sk-irrelevant-folder').length; + + if (!irrelevantRows && !irrelevantFolders) return; + + injectStyles(doc, 'cmk-sk-filter-toggle-styles', ` + #cmk-sk-filter-bar { + display: flex; + align-items: center; + gap: 10px; + padding: 5px 10px; + margin: 6px 0 4px 0; + background: rgba(0,0,0,0.25); + border: 1px solid #3a3a3a; + border-radius: 4px; + font-size: 11px; + color: #999; + font-family: monospace; + } + #cmk-sk-filter-toggle-btn { + cursor: pointer; + padding: 3px 10px; + border-radius: 3px; + border: 1px solid #555; + background: #2a2a2a; + color: #bbb; + font-size: 11px; + font-family: monospace; + font-weight: bold; + letter-spacing: 0.03em; + } + #cmk-sk-filter-toggle-btn:hover { background: #383838; } + #cmk-sk-filter-toggle-btn.active { + background: #1c3320; + border-color: #4caf50; + color: #4caf50; + } + body.cmk-sk-filter-active tr.cmk-sk-irrelevant-row { display: none !important; } + body.cmk-sk-filter-active div.foldable_wrapper.cmk-sk-irrelevant-folder { display: none !important; } + `); + + const bar = doc.createElement('div'); + bar.id = 'cmk-sk-filter-bar'; + + const btn = doc.createElement('button'); + btn.id = 'cmk-sk-filter-toggle-btn'; + btn.type = 'button'; + btn.textContent = 'Solo rilevanti'; + + const info = doc.createElement('span'); + info.textContent = `${relevantCount} rilevanti · ${irrelevantRows} righe e ${irrelevantFolders} folder non rilevanti`; + + btn.addEventListener('click', () => { + const isActive = doc.body.classList.toggle('cmk-sk-filter-active'); + btn.classList.toggle('active', isActive); + btn.textContent = isActive ? 'Mostra tutto' : 'Solo rilevanti'; + }); + + bar.appendChild(btn); + bar.appendChild(info); + + const anchor = doc.querySelector('div.foldable_wrapper') || doc.querySelector('div.wato'); + if (anchor) anchor.before(bar); + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= @@ -762,6 +852,7 @@ if (getPageMode(doc) !== 'edit_ruleset') return; highlightIneffectiveRules(doc); highlightRuleMatchStatus(doc); + addRulesetFilterToggle(doc); } function init() { From 1dde75917bd2c0c9e886bd7f813d0386cc5cc439 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:12:11 +0200 Subject: [PATCH 31/94] fix: nascondi toggle su pagine edit_ruleset senza ricerca attiva MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il toggle "Solo rilevanti" non deve apparire su viste normali del ruleset dove nessuna riga è evidenziata (nessuna ricerca per tag, host o ineffective). Controllo anticipato su relevantCount === 0 prima di marcare righe e folder. bump version to 2.8.1 --- user_script/checkmk_swissknife.user.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 1906b08..c41835d 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.8.0 +// @version 2.8.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -726,6 +726,10 @@ const RELEVANT_SEL = 'tr.cmk-sk-rule-match, tr.cmk-sk-rule-nomatch, tr.cmk-sk-ineffective'; + // Nessuna riga evidenziata = nessuna ricerca attiva, toggle inutile + const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; + if (!relevantCount) return; + doc.querySelectorAll('tr.data').forEach(row => { if (!row.matches(RELEVANT_SEL)) row.classList.add('cmk-sk-irrelevant-row'); }); @@ -734,7 +738,6 @@ if (!wrapper.querySelector(RELEVANT_SEL)) wrapper.classList.add('cmk-sk-irrelevant-folder'); }); - const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; const irrelevantRows = doc.querySelectorAll('tr.cmk-sk-irrelevant-row').length; const irrelevantFolders = doc.querySelectorAll('div.foldable_wrapper.cmk-sk-irrelevant-folder').length; From b99d5a85831783fb7fbbd967e6646b1124e2af4c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:12:11 +0200 Subject: [PATCH 32/94] fix: nascondi toggle su pagine edit_ruleset senza ricerca attiva MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il toggle "Solo rilevanti" non deve apparire su viste normali del ruleset dove nessuna riga è evidenziata (nessuna ricerca per tag, host o ineffective). Controllo anticipato su relevantCount === 0 prima di marcare righe e folder. bump version to 2.8.1 --- user_script/checkmk_swissknife.user.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 1906b08..c41835d 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.8.0 +// @version 2.8.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -726,6 +726,10 @@ const RELEVANT_SEL = 'tr.cmk-sk-rule-match, tr.cmk-sk-rule-nomatch, tr.cmk-sk-ineffective'; + // Nessuna riga evidenziata = nessuna ricerca attiva, toggle inutile + const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; + if (!relevantCount) return; + doc.querySelectorAll('tr.data').forEach(row => { if (!row.matches(RELEVANT_SEL)) row.classList.add('cmk-sk-irrelevant-row'); }); @@ -734,7 +738,6 @@ if (!wrapper.querySelector(RELEVANT_SEL)) wrapper.classList.add('cmk-sk-irrelevant-folder'); }); - const relevantCount = doc.querySelectorAll(RELEVANT_SEL).length; const irrelevantRows = doc.querySelectorAll('tr.cmk-sk-irrelevant-row').length; const irrelevantFolders = doc.querySelectorAll('div.foldable_wrapper.cmk-sk-irrelevant-folder').length; From 8817c25aefb626bbd13155d75180f1d6497bca29 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:29:06 +0200 Subject: [PATCH 33/94] feat: aggiungi pulsante disco su view.py per aprire Service Discovery host Nelle monitoring views (view.py), aggiunge un badge 'disco' accanto a ogni hostname nella colonna Host. Il click apre in nuova tab la pagina wato.py?host=HOSTNAME&mode=inventory per il Service Discovery diretto. Esteso @include a coprire anche view.py. bump version to 2.9.0 --- user_script/checkmk_swissknife.user.js | 85 +++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index c41835d..24262ae 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,13 +1,13 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.8.1 +// @version 2.9.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife // @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js -// @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ +// @include /^https?:\/\/.+\/check_mk\/(index|wato|view)\.py/ // @grant none // ==/UserScript== @@ -804,13 +804,69 @@ } + // ========================================================================= + // FEATURE: Inventory Button su view.py + // + // Nelle pagine view.py (monitoring views), aggiunge un piccolo pulsante + // accanto a ogni hostname nella colonna Host per aprire direttamente la + // pagina di Service Discovery dell'host in una nuova tab. + // ========================================================================= + + function addInventoryButtons(doc) { + if (doc.body.dataset.cmkInventoryBtns === '1') return; + + const hostCells = doc.querySelectorAll('table.data td.nobr'); + if (!hostCells.length) return; + + injectStyles(doc, 'cmk-sk-inv-btn-styles', ` + .cmk-sk-inv-btn { + display: inline-block; + background: #2c6fad; + color: #fff !important; + font-size: 9px; + font-weight: bold; + padding: 1px 5px; + border-radius: 3px; + text-decoration: none !important; + margin-left: 5px; + vertical-align: middle; + font-family: monospace; + white-space: nowrap; + cursor: pointer; + opacity: 0.85; + } + .cmk-sk-inv-btn:hover { opacity: 1; background: #1a5a99 !important; } + `); + + hostCells.forEach(td => { + const link = td.querySelector('a[href*="view_name=hoststatus"]'); + if (!link) return; + const params = new URLSearchParams(link.getAttribute('href').split('?')[1] || ''); + const hostname = params.get('host'); + if (!hostname) return; + + const btn = doc.createElement('a'); + btn.className = 'cmk-sk-inv-btn'; + btn.href = `wato.py?host=${encodeURIComponent(hostname)}&mode=inventory`; + btn.target = '_blank'; + btn.rel = 'noopener'; + btn.title = `Service Discovery: ${hostname}`; + btn.textContent = 'disco'; + td.appendChild(btn); + }); + + doc.body.dataset.cmkInventoryBtns = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attemptsFolder = 0; - let attemptsAcc = 0; - let attemptsRuleset = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; + let attemptsRuleset = 0; + let attemptsInventory = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); @@ -858,14 +914,25 @@ addRulesetFilterToggle(doc); } + function tryAddInventoryButtons() { + const doc = getTargetDoc(); + if (!doc || !doc.body) { + if (++attemptsInventory < MAX_ATTEMPTS) setTimeout(tryAddInventoryButtons, POLL_INTERVAL_MS); + return; + } + try { if (!/\/view\.py/.test(doc.location.pathname)) return; } catch (e) { return; } + addInventoryButtons(doc); + } + function init() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); const mode = getPageMode(iDoc); const targetDoc = getTargetDoc(); const targetMode = getPageMode(targetDoc); - attemptsFolder = 0; - attemptsAcc = 0; - attemptsRuleset = 0; + attemptsFolder = 0; + attemptsAcc = 0; + attemptsRuleset = 0; + attemptsInventory = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); // Accordion: solo sulle pagine in ACCORDION_MODES. @@ -876,6 +943,8 @@ if (!targetMode || targetMode === 'edit_ruleset') { setTimeout(tryHighlightRuleset, 300); } + // Inventory button: su view.py, si auto-ferma se non applicabile. + setTimeout(tryAddInventoryButtons, 500); } if (document.readyState === 'complete') { From 44e68acf22d9d2936aa354c4eaaa4c2f8d39d37b Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:29:06 +0200 Subject: [PATCH 34/94] feat: aggiungi pulsante disco su view.py per aprire Service Discovery host Nelle monitoring views (view.py), aggiunge un badge 'disco' accanto a ogni hostname nella colonna Host. Il click apre in nuova tab la pagina wato.py?host=HOSTNAME&mode=inventory per il Service Discovery diretto. Esteso @include a coprire anche view.py. bump version to 2.9.0 --- user_script/checkmk_swissknife.user.js | 85 +++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index c41835d..24262ae 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,13 +1,13 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.8.1 +// @version 2.9.0 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife // @updateURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js // @downloadURL https://luigidacunto.com/scripts/checkmk-swissknife/user_script/checkmk_swissknife.user.js -// @include /^https?:\/\/.+\/check_mk\/(index|wato)\.py/ +// @include /^https?:\/\/.+\/check_mk\/(index|wato|view)\.py/ // @grant none // ==/UserScript== @@ -804,13 +804,69 @@ } + // ========================================================================= + // FEATURE: Inventory Button su view.py + // + // Nelle pagine view.py (monitoring views), aggiunge un piccolo pulsante + // accanto a ogni hostname nella colonna Host per aprire direttamente la + // pagina di Service Discovery dell'host in una nuova tab. + // ========================================================================= + + function addInventoryButtons(doc) { + if (doc.body.dataset.cmkInventoryBtns === '1') return; + + const hostCells = doc.querySelectorAll('table.data td.nobr'); + if (!hostCells.length) return; + + injectStyles(doc, 'cmk-sk-inv-btn-styles', ` + .cmk-sk-inv-btn { + display: inline-block; + background: #2c6fad; + color: #fff !important; + font-size: 9px; + font-weight: bold; + padding: 1px 5px; + border-radius: 3px; + text-decoration: none !important; + margin-left: 5px; + vertical-align: middle; + font-family: monospace; + white-space: nowrap; + cursor: pointer; + opacity: 0.85; + } + .cmk-sk-inv-btn:hover { opacity: 1; background: #1a5a99 !important; } + `); + + hostCells.forEach(td => { + const link = td.querySelector('a[href*="view_name=hoststatus"]'); + if (!link) return; + const params = new URLSearchParams(link.getAttribute('href').split('?')[1] || ''); + const hostname = params.get('host'); + if (!hostname) return; + + const btn = doc.createElement('a'); + btn.className = 'cmk-sk-inv-btn'; + btn.href = `wato.py?host=${encodeURIComponent(hostname)}&mode=inventory`; + btn.target = '_blank'; + btn.rel = 'noopener'; + btn.title = `Service Discovery: ${hostname}`; + btn.textContent = 'disco'; + td.appendChild(btn); + }); + + doc.body.dataset.cmkInventoryBtns = '1'; + } + + // ========================================================================= // BOOTSTRAP: polling per ogni feature, attivato solo se la select è presente // ========================================================================= - let attemptsFolder = 0; - let attemptsAcc = 0; - let attemptsRuleset = 0; + let attemptsFolder = 0; + let attemptsAcc = 0; + let attemptsRuleset = 0; + let attemptsInventory = 0; function tryEnhanceFolderSelect() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); @@ -858,14 +914,25 @@ addRulesetFilterToggle(doc); } + function tryAddInventoryButtons() { + const doc = getTargetDoc(); + if (!doc || !doc.body) { + if (++attemptsInventory < MAX_ATTEMPTS) setTimeout(tryAddInventoryButtons, POLL_INTERVAL_MS); + return; + } + try { if (!/\/view\.py/.test(doc.location.pathname)) return; } catch (e) { return; } + addInventoryButtons(doc); + } + function init() { const iDoc = getWatoDoc(FOLDER_SELECT_ID); const mode = getPageMode(iDoc); const targetDoc = getTargetDoc(); const targetMode = getPageMode(targetDoc); - attemptsFolder = 0; - attemptsAcc = 0; - attemptsRuleset = 0; + attemptsFolder = 0; + attemptsAcc = 0; + attemptsRuleset = 0; + attemptsInventory = 0; // Folder select: si auto-ferma se non trova l'elemento, schedula sempre. setTimeout(tryEnhanceFolderSelect, 800); // Accordion: solo sulle pagine in ACCORDION_MODES. @@ -876,6 +943,8 @@ if (!targetMode || targetMode === 'edit_ruleset') { setTimeout(tryHighlightRuleset, 300); } + // Inventory button: su view.py, si auto-ferma se non applicabile. + setTimeout(tryAddInventoryButtons, 500); } if (document.readyState === 'complete') { From 309e350eb6e8aa3c5e8e4c5413cde29a52ffe031 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:32:45 +0200 Subject: [PATCH 35/94] fix: sostituisci testo 'disco' con icona SVG sul pulsante inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il pulsante Service Discovery usa ora un'icona lente SVG inline con bordino blu al posto del testo. Dimensione 16x16px, trasparente, con hover che aumenta l'opacità e schiarisce il bordo. bump version to 2.9.1 --- user_script/checkmk_swissknife.user.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 24262ae..aee1d76 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.0 +// @version 2.9.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -820,22 +820,24 @@ injectStyles(doc, 'cmk-sk-inv-btn-styles', ` .cmk-sk-inv-btn { - display: inline-block; - background: #2c6fad; - color: #fff !important; - font-size: 9px; - font-weight: bold; - padding: 1px 5px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + background: transparent; + color: #6aabde !important; + border: 1px solid #4a8fc4; border-radius: 3px; text-decoration: none !important; margin-left: 5px; vertical-align: middle; - font-family: monospace; - white-space: nowrap; cursor: pointer; - opacity: 0.85; + opacity: 0.75; + flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; background: #1a5a99 !important; } + .cmk-sk-inv-btn:hover { opacity: 1; border-color: #88c4f0; color: #88c4f0 !important; } + .cmk-sk-inv-btn svg { display: block; } `); hostCells.forEach(td => { @@ -851,7 +853,7 @@ btn.target = '_blank'; btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; - btn.textContent = 'disco'; + btn.innerHTML = ''; td.appendChild(btn); }); From 7915571ff4df164aeb79d07b0cf7833288ae9041 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:32:45 +0200 Subject: [PATCH 36/94] fix: sostituisci testo 'disco' con icona SVG sul pulsante inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il pulsante Service Discovery usa ora un'icona lente SVG inline con bordino blu al posto del testo. Dimensione 16x16px, trasparente, con hover che aumenta l'opacità e schiarisce il bordo. bump version to 2.9.1 --- user_script/checkmk_swissknife.user.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 24262ae..aee1d76 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.0 +// @version 2.9.1 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -820,22 +820,24 @@ injectStyles(doc, 'cmk-sk-inv-btn-styles', ` .cmk-sk-inv-btn { - display: inline-block; - background: #2c6fad; - color: #fff !important; - font-size: 9px; - font-weight: bold; - padding: 1px 5px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + background: transparent; + color: #6aabde !important; + border: 1px solid #4a8fc4; border-radius: 3px; text-decoration: none !important; margin-left: 5px; vertical-align: middle; - font-family: monospace; - white-space: nowrap; cursor: pointer; - opacity: 0.85; + opacity: 0.75; + flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; background: #1a5a99 !important; } + .cmk-sk-inv-btn:hover { opacity: 1; border-color: #88c4f0; color: #88c4f0 !important; } + .cmk-sk-inv-btn svg { display: block; } `); hostCells.forEach(td => { @@ -851,7 +853,7 @@ btn.target = '_blank'; btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; - btn.textContent = 'disco'; + btn.innerHTML = ''; td.appendChild(btn); }); From 9756c72dff841f24e7ca6366b816c716055ffcd1 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:39:30 +0200 Subject: [PATCH 37/94] fix: pulsante inventory con icona monitor+spunta arancione prima del nome host Sostituita lente con icona monitor+checkmark, colore arancione #e5a500, posizionata prima del nome host (prepend invece di append). bump version to 2.9.2 --- user_script/checkmk_swissknife.user.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index aee1d76..bb57390 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.1 +// @version 2.9.2 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -825,18 +825,18 @@ justify-content: center; width: 16px; height: 16px; - background: transparent; - color: #6aabde !important; - border: 1px solid #4a8fc4; + background: rgba(229,165,0,0.08); + color: #e5a500 !important; + border: 1px solid #e5a500; border-radius: 3px; text-decoration: none !important; - margin-left: 5px; + margin-right: 5px; vertical-align: middle; cursor: pointer; - opacity: 0.75; + opacity: 0.8; flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; border-color: #88c4f0; color: #88c4f0 !important; } + .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } .cmk-sk-inv-btn svg { display: block; } `); @@ -853,8 +853,8 @@ btn.target = '_blank'; btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; - btn.innerHTML = ''; - td.appendChild(btn); + btn.innerHTML = ''; + td.prepend(btn); }); doc.body.dataset.cmkInventoryBtns = '1'; From 8d40ccc511e365a05f3b5c5f40d64da9b9dcbe7e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:39:30 +0200 Subject: [PATCH 38/94] fix: pulsante inventory con icona monitor+spunta arancione prima del nome host Sostituita lente con icona monitor+checkmark, colore arancione #e5a500, posizionata prima del nome host (prepend invece di append). bump version to 2.9.2 --- user_script/checkmk_swissknife.user.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index aee1d76..bb57390 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.1 +// @version 2.9.2 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -825,18 +825,18 @@ justify-content: center; width: 16px; height: 16px; - background: transparent; - color: #6aabde !important; - border: 1px solid #4a8fc4; + background: rgba(229,165,0,0.08); + color: #e5a500 !important; + border: 1px solid #e5a500; border-radius: 3px; text-decoration: none !important; - margin-left: 5px; + margin-right: 5px; vertical-align: middle; cursor: pointer; - opacity: 0.75; + opacity: 0.8; flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; border-color: #88c4f0; color: #88c4f0 !important; } + .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } .cmk-sk-inv-btn svg { display: block; } `); @@ -853,8 +853,8 @@ btn.target = '_blank'; btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; - btn.innerHTML = ''; - td.appendChild(btn); + btn.innerHTML = ''; + td.prepend(btn); }); doc.body.dataset.cmkInventoryBtns = '1'; From 20037c1e9ba96f411661121dc68b16fee4d0c926 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:50:01 +0200 Subject: [PATCH 39/94] feat: aggiungi pulsante copia hostname in clipboard su view.py Accanto al pulsante Service Discovery, aggiunge un secondo pulsante con icona clipboard che copia il nome host negli appunti al click. Feedback visivo verde per 900ms al click confermato. bump version to 2.9.3 --- user_script/checkmk_swissknife.user.js | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index bb57390..1b41ffe 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.2 +// @version 2.9.3 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -837,7 +837,26 @@ flex-shrink: 0; } .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } - .cmk-sk-inv-btn svg { display: block; } + .cmk-sk-copy-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + background: transparent; + color: #999 !important; + border: 1px solid #666; + border-radius: 3px; + margin-right: 5px; + vertical-align: middle; + cursor: pointer; + opacity: 0.7; + flex-shrink: 0; + padding: 0; + } + .cmk-sk-copy-btn:hover { opacity: 1; border-color: #aaa; color: #ccc !important; } + .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; opacity: 1; } + .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } `); hostCells.forEach(td => { @@ -854,6 +873,20 @@ btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; btn.innerHTML = ''; + + const copyBtn = doc.createElement('button'); + copyBtn.className = 'cmk-sk-copy-btn'; + copyBtn.type = 'button'; + copyBtn.title = `Copia hostname: ${hostname}`; + copyBtn.innerHTML = ''; + copyBtn.addEventListener('click', () => { + navigator.clipboard.writeText(hostname).then(() => { + copyBtn.classList.add('copied'); + setTimeout(() => copyBtn.classList.remove('copied'), 900); + }); + }); + + td.prepend(copyBtn); td.prepend(btn); }); From 503284419d4b3b3341dd79d394aaef4721f8b98e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:50:01 +0200 Subject: [PATCH 40/94] feat: aggiungi pulsante copia hostname in clipboard su view.py Accanto al pulsante Service Discovery, aggiunge un secondo pulsante con icona clipboard che copia il nome host negli appunti al click. Feedback visivo verde per 900ms al click confermato. bump version to 2.9.3 --- user_script/checkmk_swissknife.user.js | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index bb57390..1b41ffe 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.2 +// @version 2.9.3 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -837,7 +837,26 @@ flex-shrink: 0; } .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } - .cmk-sk-inv-btn svg { display: block; } + .cmk-sk-copy-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + background: transparent; + color: #999 !important; + border: 1px solid #666; + border-radius: 3px; + margin-right: 5px; + vertical-align: middle; + cursor: pointer; + opacity: 0.7; + flex-shrink: 0; + padding: 0; + } + .cmk-sk-copy-btn:hover { opacity: 1; border-color: #aaa; color: #ccc !important; } + .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; opacity: 1; } + .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } `); hostCells.forEach(td => { @@ -854,6 +873,20 @@ btn.rel = 'noopener'; btn.title = `Service Discovery: ${hostname}`; btn.innerHTML = ''; + + const copyBtn = doc.createElement('button'); + copyBtn.className = 'cmk-sk-copy-btn'; + copyBtn.type = 'button'; + copyBtn.title = `Copia hostname: ${hostname}`; + copyBtn.innerHTML = ''; + copyBtn.addEventListener('click', () => { + navigator.clipboard.writeText(hostname).then(() => { + copyBtn.classList.add('copied'); + setTimeout(() => copyBtn.classList.remove('copied'), 900); + }); + }); + + td.prepend(copyBtn); td.prepend(btn); }); From e7bfce2e9ccd0558df198faefc17964537b582de Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:52:54 +0200 Subject: [PATCH 41/94] fix: icona clipboard blu e spacing uniforme tra i due pulsanti host Colore clipboard cambiato da grigio a #5ab4d6 (blu). Ridotto il gap tra le due icone a 2px (da 5px) per raggrupparle visivamente, mantenendo 5px di distanza dal nome host. bump version to 2.9.4 --- user_script/checkmk_swissknife.user.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 1b41ffe..80ae85f 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.3 +// @version 2.9.4 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -830,7 +830,7 @@ border: 1px solid #e5a500; border-radius: 3px; text-decoration: none !important; - margin-right: 5px; + margin-right: 2px; vertical-align: middle; cursor: pointer; opacity: 0.8; @@ -843,19 +843,19 @@ justify-content: center; width: 16px; height: 16px; - background: transparent; - color: #999 !important; - border: 1px solid #666; + background: rgba(90,180,214,0.08); + color: #5ab4d6 !important; + border: 1px solid #5ab4d6; border-radius: 3px; margin-right: 5px; vertical-align: middle; cursor: pointer; - opacity: 0.7; + opacity: 0.8; flex-shrink: 0; padding: 0; } - .cmk-sk-copy-btn:hover { opacity: 1; border-color: #aaa; color: #ccc !important; } - .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; opacity: 1; } + .cmk-sk-copy-btn:hover { opacity: 1; background: rgba(90,180,214,0.18) !important; } + .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; background: rgba(76,175,80,0.12) !important; opacity: 1; } .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } `); From 4a92b46f228ababdc02351c446f8022806a0df70 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:52:54 +0200 Subject: [PATCH 42/94] fix: icona clipboard blu e spacing uniforme tra i due pulsanti host Colore clipboard cambiato da grigio a #5ab4d6 (blu). Ridotto il gap tra le due icone a 2px (da 5px) per raggrupparle visivamente, mantenendo 5px di distanza dal nome host. bump version to 2.9.4 --- user_script/checkmk_swissknife.user.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 1b41ffe..80ae85f 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.3 +// @version 2.9.4 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -830,7 +830,7 @@ border: 1px solid #e5a500; border-radius: 3px; text-decoration: none !important; - margin-right: 5px; + margin-right: 2px; vertical-align: middle; cursor: pointer; opacity: 0.8; @@ -843,19 +843,19 @@ justify-content: center; width: 16px; height: 16px; - background: transparent; - color: #999 !important; - border: 1px solid #666; + background: rgba(90,180,214,0.08); + color: #5ab4d6 !important; + border: 1px solid #5ab4d6; border-radius: 3px; margin-right: 5px; vertical-align: middle; cursor: pointer; - opacity: 0.7; + opacity: 0.8; flex-shrink: 0; padding: 0; } - .cmk-sk-copy-btn:hover { opacity: 1; border-color: #aaa; color: #ccc !important; } - .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; opacity: 1; } + .cmk-sk-copy-btn:hover { opacity: 1; background: rgba(90,180,214,0.18) !important; } + .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; background: rgba(76,175,80,0.12) !important; opacity: 1; } .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } `); From 9cf6d8af97d9d9bef6d57d2a1a5605cf1764e1a7 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:55:49 +0200 Subject: [PATCH 43/94] feat: aggiungi terzo pulsante copia hostname corto e refactor spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Terzo pulsante viola copia l'hostname senza dominio (prima parte prima del primo punto). I tre pulsanti sono ora in un wrapper flex con gap:2px e margin-right:4px verso il nome host, eliminando le irregolarità di spacing tra icone inline. bump version to 2.9.5 --- user_script/checkmk_swissknife.user.js | 79 +++++++++++++++++--------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 80ae85f..be536a4 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.4 +// @version 2.9.5 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -819,52 +819,65 @@ if (!hostCells.length) return; injectStyles(doc, 'cmk-sk-inv-btn-styles', ` - .cmk-sk-inv-btn { + .cmk-sk-btn-group { display: inline-flex; align-items: center; - justify-content: center; - width: 16px; - height: 16px; - background: rgba(229,165,0,0.08); - color: #e5a500 !important; - border: 1px solid #e5a500; - border-radius: 3px; - text-decoration: none !important; - margin-right: 2px; + gap: 2px; + margin-right: 4px; vertical-align: middle; - cursor: pointer; - opacity: 0.8; - flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } - .cmk-sk-copy-btn { + .cmk-sk-inv-btn, .cmk-sk-copy-btn, .cmk-sk-copy-short-btn { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; - background: rgba(90,180,214,0.08); - color: #5ab4d6 !important; - border: 1px solid #5ab4d6; border-radius: 3px; - margin-right: 5px; - vertical-align: middle; cursor: pointer; opacity: 0.8; flex-shrink: 0; padding: 0; + text-decoration: none !important; + } + .cmk-sk-inv-btn, .cmk-sk-inv-btn:visited { + background: rgba(229,165,0,0.08); + color: #e5a500 !important; + border: 1px solid #e5a500; + } + .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } + .cmk-sk-copy-btn { + background: rgba(90,180,214,0.08); + color: #5ab4d6 !important; + border: 1px solid #5ab4d6; } .cmk-sk-copy-btn:hover { opacity: 1; background: rgba(90,180,214,0.18) !important; } - .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; background: rgba(76,175,80,0.12) !important; opacity: 1; } - .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } + .cmk-sk-copy-short-btn { + background: rgba(160,120,200,0.08); + color: #a078c8 !important; + border: 1px solid #a078c8; + } + .cmk-sk-copy-short-btn:hover { opacity: 1; background: rgba(160,120,200,0.18) !important; } + .cmk-sk-copy-btn.copied, .cmk-sk-copy-short-btn.copied { + border-color: #4caf50 !important; + color: #4caf50 !important; + background: rgba(76,175,80,0.12) !important; + opacity: 1; + } + .cmk-sk-btn-group svg { display: block; } `); + const CLIP_SVG = ''; + hostCells.forEach(td => { const link = td.querySelector('a[href*="view_name=hoststatus"]'); if (!link) return; const params = new URLSearchParams(link.getAttribute('href').split('?')[1] || ''); const hostname = params.get('host'); if (!hostname) return; + const shortname = hostname.split('.')[0]; + + const group = doc.createElement('span'); + group.className = 'cmk-sk-btn-group'; const btn = doc.createElement('a'); btn.className = 'cmk-sk-inv-btn'; @@ -878,7 +891,7 @@ copyBtn.className = 'cmk-sk-copy-btn'; copyBtn.type = 'button'; copyBtn.title = `Copia hostname: ${hostname}`; - copyBtn.innerHTML = ''; + copyBtn.innerHTML = CLIP_SVG; copyBtn.addEventListener('click', () => { navigator.clipboard.writeText(hostname).then(() => { copyBtn.classList.add('copied'); @@ -886,8 +899,22 @@ }); }); - td.prepend(copyBtn); - td.prepend(btn); + const copyShortBtn = doc.createElement('button'); + copyShortBtn.className = 'cmk-sk-copy-short-btn'; + copyShortBtn.type = 'button'; + copyShortBtn.title = `Copia hostname corto: ${shortname}`; + copyShortBtn.innerHTML = CLIP_SVG; + copyShortBtn.addEventListener('click', () => { + navigator.clipboard.writeText(shortname).then(() => { + copyShortBtn.classList.add('copied'); + setTimeout(() => copyShortBtn.classList.remove('copied'), 900); + }); + }); + + group.appendChild(btn); + group.appendChild(copyBtn); + group.appendChild(copyShortBtn); + td.prepend(group); }); doc.body.dataset.cmkInventoryBtns = '1'; From 4a17668e9e5d1e75279409fd70765c34436bd9a4 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:55:49 +0200 Subject: [PATCH 44/94] feat: aggiungi terzo pulsante copia hostname corto e refactor spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Terzo pulsante viola copia l'hostname senza dominio (prima parte prima del primo punto). I tre pulsanti sono ora in un wrapper flex con gap:2px e margin-right:4px verso il nome host, eliminando le irregolarità di spacing tra icone inline. bump version to 2.9.5 --- user_script/checkmk_swissknife.user.js | 79 +++++++++++++++++--------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index 80ae85f..be536a4 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.4 +// @version 2.9.5 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -819,52 +819,65 @@ if (!hostCells.length) return; injectStyles(doc, 'cmk-sk-inv-btn-styles', ` - .cmk-sk-inv-btn { + .cmk-sk-btn-group { display: inline-flex; align-items: center; - justify-content: center; - width: 16px; - height: 16px; - background: rgba(229,165,0,0.08); - color: #e5a500 !important; - border: 1px solid #e5a500; - border-radius: 3px; - text-decoration: none !important; - margin-right: 2px; + gap: 2px; + margin-right: 4px; vertical-align: middle; - cursor: pointer; - opacity: 0.8; - flex-shrink: 0; } - .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } - .cmk-sk-copy-btn { + .cmk-sk-inv-btn, .cmk-sk-copy-btn, .cmk-sk-copy-short-btn { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; - background: rgba(90,180,214,0.08); - color: #5ab4d6 !important; - border: 1px solid #5ab4d6; border-radius: 3px; - margin-right: 5px; - vertical-align: middle; cursor: pointer; opacity: 0.8; flex-shrink: 0; padding: 0; + text-decoration: none !important; + } + .cmk-sk-inv-btn, .cmk-sk-inv-btn:visited { + background: rgba(229,165,0,0.08); + color: #e5a500 !important; + border: 1px solid #e5a500; + } + .cmk-sk-inv-btn:hover { opacity: 1; background: rgba(229,165,0,0.18) !important; } + .cmk-sk-copy-btn { + background: rgba(90,180,214,0.08); + color: #5ab4d6 !important; + border: 1px solid #5ab4d6; } .cmk-sk-copy-btn:hover { opacity: 1; background: rgba(90,180,214,0.18) !important; } - .cmk-sk-copy-btn.copied { border-color: #4caf50 !important; color: #4caf50 !important; background: rgba(76,175,80,0.12) !important; opacity: 1; } - .cmk-sk-inv-btn svg, .cmk-sk-copy-btn svg { display: block; } + .cmk-sk-copy-short-btn { + background: rgba(160,120,200,0.08); + color: #a078c8 !important; + border: 1px solid #a078c8; + } + .cmk-sk-copy-short-btn:hover { opacity: 1; background: rgba(160,120,200,0.18) !important; } + .cmk-sk-copy-btn.copied, .cmk-sk-copy-short-btn.copied { + border-color: #4caf50 !important; + color: #4caf50 !important; + background: rgba(76,175,80,0.12) !important; + opacity: 1; + } + .cmk-sk-btn-group svg { display: block; } `); + const CLIP_SVG = ''; + hostCells.forEach(td => { const link = td.querySelector('a[href*="view_name=hoststatus"]'); if (!link) return; const params = new URLSearchParams(link.getAttribute('href').split('?')[1] || ''); const hostname = params.get('host'); if (!hostname) return; + const shortname = hostname.split('.')[0]; + + const group = doc.createElement('span'); + group.className = 'cmk-sk-btn-group'; const btn = doc.createElement('a'); btn.className = 'cmk-sk-inv-btn'; @@ -878,7 +891,7 @@ copyBtn.className = 'cmk-sk-copy-btn'; copyBtn.type = 'button'; copyBtn.title = `Copia hostname: ${hostname}`; - copyBtn.innerHTML = ''; + copyBtn.innerHTML = CLIP_SVG; copyBtn.addEventListener('click', () => { navigator.clipboard.writeText(hostname).then(() => { copyBtn.classList.add('copied'); @@ -886,8 +899,22 @@ }); }); - td.prepend(copyBtn); - td.prepend(btn); + const copyShortBtn = doc.createElement('button'); + copyShortBtn.className = 'cmk-sk-copy-short-btn'; + copyShortBtn.type = 'button'; + copyShortBtn.title = `Copia hostname corto: ${shortname}`; + copyShortBtn.innerHTML = CLIP_SVG; + copyShortBtn.addEventListener('click', () => { + navigator.clipboard.writeText(shortname).then(() => { + copyShortBtn.classList.add('copied'); + setTimeout(() => copyShortBtn.classList.remove('copied'), 900); + }); + }); + + group.appendChild(btn); + group.appendChild(copyBtn); + group.appendChild(copyShortBtn); + td.prepend(group); }); doc.body.dataset.cmkInventoryBtns = '1'; From bf02cf8307e6308c14907d0a2a11198fe1dd5fca Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:00:36 +0200 Subject: [PATCH 45/94] v2.9.6 - Fix spaziatura terzo pulsante azioni host in view.py Aggiunto margin: 0 ai pulsanti .cmk-sk-inv-btn/.cmk-sk-copy-btn/.cmk-sk-copy-short-btn per azzerare il margin di default del browser sui button element e ottenere spaziatura uniforme (gap: 2px) tra tutte e tre le icone nel gruppo. --- user_script/checkmk_swissknife.user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index be536a4..a15ce23 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.5 +// @version 2.9.6 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -837,6 +837,7 @@ opacity: 0.8; flex-shrink: 0; padding: 0; + margin: 0; text-decoration: none !important; } .cmk-sk-inv-btn, .cmk-sk-inv-btn:visited { From 58684f0c6616b12d7c9469f2af78f5dca2b3f7b8 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:00:36 +0200 Subject: [PATCH 46/94] v2.9.6 - Fix spaziatura terzo pulsante azioni host in view.py Aggiunto margin: 0 ai pulsanti .cmk-sk-inv-btn/.cmk-sk-copy-btn/.cmk-sk-copy-short-btn per azzerare il margin di default del browser sui button element e ottenere spaziatura uniforme (gap: 2px) tra tutte e tre le icone nel gruppo. --- user_script/checkmk_swissknife.user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_script/checkmk_swissknife.user.js b/user_script/checkmk_swissknife.user.js index be536a4..a15ce23 100644 --- a/user_script/checkmk_swissknife.user.js +++ b/user_script/checkmk_swissknife.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Checkmk SwissKnife // @namespace https://luigidacunto.com/ -// @version 2.9.5 +// @version 2.9.6 // @description Raccolta di miglioramenti all'interfaccia di Checkmk WATO. Ogni fix o enhancement viene aggiunto qui come feature indipendente. // @author Luigi D'Acunto // @homepageURL https://git.luigidacunto.com/tools/checkmk-swissknife @@ -837,6 +837,7 @@ opacity: 0.8; flex-shrink: 0; padding: 0; + margin: 0; text-decoration: none !important; } .cmk-sk-inv-btn, .cmk-sk-inv-btn:visited { From 00843155c27ec1ca20813914a883d3bca93d9bf7 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:10:13 +0200 Subject: [PATCH 47/94] v2.9.7 - Converti pulsante discovery da a