From 5c40b1bddbdc21b10cf10f29e309eebcba19511f Mon Sep 17 00:00:00 2001 From: Mohammed Ghouse Date: Sat, 12 Sep 2026 03:42:05 +0530 Subject: [PATCH] Fix knowledge base aside renaming all nested articles The aside tree nests child articles inside their parent element, so the descendant selectors used when updating an article title and illustration also matched every child. Renaming or changing the illustration of a parent (in particular the root) visually changed all its descendants until the page was refreshed. Update only the entry that belongs to the current article, as already done in AsideController::findEmptyMenus(). Fixes #25394 --- js/modules/Knowbase/ArticleController.js | 43 +++++++++++++++--------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/js/modules/Knowbase/ArticleController.js b/js/modules/Knowbase/ArticleController.js index 1a3f91f23b1d..7e08dc8af0da 100644 --- a/js/modules/Knowbase/ArticleController.js +++ b/js/modules/Knowbase/ArticleController.js @@ -578,11 +578,17 @@ export class GlpiKnowbaseArticleController } const aside = document.querySelector('[data-main-page-aside="knowbaseitem"]'); - const entries = aside.querySelectorAll( - `[data-glpi-kb-article-id="${CSS.escape(String(this.#item_id))}"] [data-glpi-kb-article-title]` + const articles = aside.querySelectorAll( + `[data-glpi-kb-article-id="${CSS.escape(String(this.#item_id))}"]` ); - for (const entry of entries) { - entry.textContent = title; + for (const article of articles) { + for (const entry of article.querySelectorAll('[data-glpi-kb-article-title]')) { + // Child articles are nested inside this article's element, so only + // update the title that belongs to this article and not its children. + if (entry.closest('[data-glpi-kb-article-id]') === article) { + entry.textContent = title; + } + } } } @@ -597,22 +603,29 @@ export class GlpiKnowbaseArticleController } const aside = document.querySelector('[data-main-page-aside="knowbaseitem"]'); - const containers = aside.querySelectorAll( - `[data-glpi-kb-article-id="${CSS.escape(String(this.#item_id))}"] [data-glpi-kb-article-illustration]` + const articles = aside.querySelectorAll( + `[data-glpi-kb-article-id="${CSS.escape(String(this.#item_id))}"]` ); // The illustration picker preview already holds a freshly rendered node // for the selected illustration. We clone it with a different size. const source = this.#getIllustrationPreviewNode(); - for (const container of containers) { - if (source === null) { - container.replaceChildren(); - } else { - const icon = source.cloneNode(true); - // The aside renders illustrations at size 20 (see aside.html.twig). - icon.setAttribute('width', '20'); - icon.setAttribute('height', '20'); - container.replaceChildren(icon); + for (const article of articles) { + for (const container of article.querySelectorAll('[data-glpi-kb-article-illustration]')) { + // Child articles are nested inside this article's element, so only + // update the illustration that belongs to this article. + if (container.closest('[data-glpi-kb-article-id]') !== article) { + continue; + } + if (source === null) { + container.replaceChildren(); + } else { + const icon = source.cloneNode(true); + // The aside renders illustrations at size 20 (see aside.html.twig). + icon.setAttribute('width', '20'); + icon.setAttribute('height', '20'); + container.replaceChildren(icon); + } } } }