From 761ca89cc9a082bac82b084d30684529b5431c7b Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:24:54 +0200 Subject: [PATCH 01/92] 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 c390780054400a26abc64536519a40a061784ec5 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:24:54 +0200 Subject: [PATCH 02/92] 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. --- 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 516950514059b4b0fa9e18585a50a5a82d965bfd Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:36:57 +0200 Subject: [PATCH 03/92] 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 1a9db50817c493b6b67334bb09c60a8bcb5343e2 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:36:57 +0200 Subject: [PATCH 04/92] 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. --- 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 f642f805d7cc9785a1b3cd40f24111c3360ff118 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:43:33 +0200 Subject: [PATCH 05/92] 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 a0a0f20df333e44d4e8680cc663c9e1953f807f3 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:43:33 +0200 Subject: [PATCH 06/92] 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. --- 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 cf30ac2024119f33082c79ac2f119d226e7da1ed Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:51:35 +0200 Subject: [PATCH 07/92] 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 42ca74e8cc52a9e067079f1e3043a40b84e587a0 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:51:35 +0200 Subject: [PATCH 08/92] 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. --- 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 1e0e9a4b1688bfc3fb12a98bb51b8d373c429fba Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:56:47 +0200 Subject: [PATCH 09/92] 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 01a74caef5011578b5cbafbbb877363340798e23 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Tue, 16 Jun 2026 17:56:47 +0200 Subject: [PATCH 10/92] 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 7a39b5e3f891620bee32c951b09f200af29302d6 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:00 +0200 Subject: [PATCH 11/92] 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 df39279c41203a10eb1e63d6dba76b510e5e95c3 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:00 +0200 Subject: [PATCH 12/92] 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 0b5bfb78b207d4c73bae927438723a316793eada Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:40 +0200 Subject: [PATCH 13/92] 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 18a1fa0e497e73cd25739869b5fedf4171d2d0d0 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:01:40 +0200 Subject: [PATCH 14/92] 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 40878df517133463b4c835746cffa452cbda3abf Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:11:22 +0200 Subject: [PATCH 15/92] 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 fa9ea660834ff11fa7e1a864133f1573cdc2605f Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:11:22 +0200 Subject: [PATCH 16/92] 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 8093f2f1c83b56db085b83bcbc7b32ae8eda23ca Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:17:31 +0200 Subject: [PATCH 17/92] 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 d4d994e7ccaa9ecab20452755080b16a39f3aed2 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:17:31 +0200 Subject: [PATCH 18/92] 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 d76fe201d81c2824163e1688640e7a1dca2b5369 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:38:33 +0200 Subject: [PATCH 19/92] 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 d00483cc2fb4ab0183fd0af026fdc5a6512683cd Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 15:38:33 +0200 Subject: [PATCH 20/92] 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 b99d5a85831783fb7fbbd967e6646b1124e2af4c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:12:11 +0200 Subject: [PATCH 21/92] 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 e2f0de0451a5958a2ecc2d97cb2d6cc8ce3d2896 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:12:11 +0200 Subject: [PATCH 22/92] 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 44e68acf22d9d2936aa354c4eaaa4c2f8d39d37b Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:29:06 +0200 Subject: [PATCH 23/92] 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 5c7046f8edafd13ebdeb83aaa4a8f74cf1f0c550 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:29:06 +0200 Subject: [PATCH 24/92] 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 7915571ff4df164aeb79d07b0cf7833288ae9041 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:32:45 +0200 Subject: [PATCH 25/92] 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 9b9d39613c15ec081faf5643c861357f2984c9a5 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:32:45 +0200 Subject: [PATCH 26/92] 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 8d40ccc511e365a05f3b5c5f40d64da9b9dcbe7e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:39:30 +0200 Subject: [PATCH 27/92] 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 9d9cc3f041b6a3b29448043b0aed75f1c11b439e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:39:30 +0200 Subject: [PATCH 28/92] 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 503284419d4b3b3341dd79d394aaef4721f8b98e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:50:01 +0200 Subject: [PATCH 29/92] 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 b3f2ff08e30dfe4ec0701a0928845b59a7d1aad8 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:50:01 +0200 Subject: [PATCH 30/92] 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 4a92b46f228ababdc02351c446f8022806a0df70 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:52:54 +0200 Subject: [PATCH 31/92] 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 291d96e29be925ec7a6d30ba90ad07be098427f4 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:52:54 +0200 Subject: [PATCH 32/92] 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 4a17668e9e5d1e75279409fd70765c34436bd9a4 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:55:49 +0200 Subject: [PATCH 33/92] 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 2cb29441cdc7556be021af9b0419b279f55ef63e Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 17:55:49 +0200 Subject: [PATCH 34/92] 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 58684f0c6616b12d7c9469f2af78f5dca2b3f7b8 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:00:36 +0200 Subject: [PATCH 35/92] 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 48f3bc49d4c392ccd6646745c9235a9d5a9eb140 Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:00:36 +0200 Subject: [PATCH 36/92] 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 22c0c3f38cb1afde86a12a96c88ce0cca797ff0c Mon Sep 17 00:00:00 2001 From: Luigi D'Acunto Date: Wed, 17 Jun 2026 18:10:13 +0200 Subject: [PATCH 37/92] v2.9.7 - Converti pulsante discovery da a