From 6856a3736de098dc21bb869f43f3e4c516719984 Mon Sep 17 00:00:00 2001 From: adevopg Date: Thu, 16 Jul 2026 13:41:03 +0000 Subject: [PATCH] Foro: no ejecutar wowhead dentro del editor (ensuciaba el post) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El post salía "doble y fatal" porque se ejecutaba wowhead DENTRO del iframe del editor: wowhead mutaba el DOM editable (icono, clase icontinyl y una tabla enorme oculta con el tooltip completo) y TinyMCE serializaba todo eso al publicar, así que la basura se guardaba en el mensaje. Se deja de inyectar wowhead en el editor. Ahora, igual que recruit: - El botón inserta un enlace PLANO con el nombre real como texto (resuelto en el servidor vía /api/forum/wowhead, sin el parpadeo de item=xxx). Nada de ni clases/estilos horneados. - El icono (como fondo, icontinyl) y el color de calidad los añade wowhead en la PÁGINA publicada, donde muta el DOM en vivo sin que se guarde nada. Se revierte el horneado y su CSS (.wh-icon) y el class en img del saneador. Se conservan los colores de calidad q0..q7 en forum.css (los necesita el qN que wowhead pone al vuelo, porque theme.css solo traía q3..q6). Verificado: crear tema funciona y el post se guarda como enlace plano + nombre, sin icontinyl/tabla/img. Co-Authored-By: Claude Opus 4.8 (1M context) --- web-next/app/forum.css | 3 -- web-next/components/ForumEditor.tsx | 73 ++++++++--------------------- web-next/lib/forum-sanitize.ts | 2 +- 3 files changed, 21 insertions(+), 57 deletions(-) diff --git a/web-next/app/forum.css b/web-next/app/forum.css index 3d717d4..72c3d6c 100644 --- a/web-next/app/forum.css +++ b/web-next/app/forum.css @@ -213,9 +213,6 @@ h3.post_field dd { margin: 0; } .post_container a.q5 { color: #ff8000 !important; } .post_container a.q6 { color: #e6cc80 !important; } .post_container a.q7, .q7 { color: #00ccff !important; } - -/* Icono de wowhead horneado junto al enlace (no depende del script). */ -img.wh-icon { display: inline-block; width: 18px; height: 18px; vertical-align: middle; border-radius: 3px; margin: 0 4px 2px 0; } .post_deleted { opacity: .5; } ul.post_controls { display: flex; justify-content: flex-end; align-items: center; gap: 8px; list-style: none; padding: 15px; margin: auto 0 0; } diff --git a/web-next/components/ForumEditor.tsx b/web-next/components/ForumEditor.tsx index a744643..9dd3962 100644 --- a/web-next/components/ForumEditor.tsx +++ b/web-next/components/ForumEditor.tsx @@ -2,7 +2,7 @@ import { Editor } from '@tinymce/tinymce-react' import { useLocale } from 'next-intl' -import { wowheadUrl, wowheadData, wowheadIcon, type WowheadType } from '@/lib/wowhead' +import { wowheadUrl, wowheadData, type WowheadType } from '@/lib/wowhead' /** * Editor TinyMCE del foro (community, self-hosted desde /tinymce — ver @@ -80,38 +80,11 @@ export function ForumEditor({ 'bold italic strikethrough forecolor backcolor emoticons | link wowhead image media | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat', image_advtab: true, setup: (editor) => { - // Vista previa: carga el script de tooltips de wowhead DENTRO del iframe - // del editor para que el enlace insertado se vea con icono, nombre y color - // de calidad, igual que al postear. No afecta a lo que se guarda: los - // formularios envían el HTML del estado de React (lo insertado, limpio), - // no la mutación visual que wowhead hace en el iframe. - const refreshEditorWowhead = () => { - try { - const wp = (editor.getWin() as unknown as { $WowheadPower?: { refreshLinks?: () => void } }) - .$WowheadPower - wp?.refreshLinks?.() - } catch { - /* preview no crítico */ - } - } - editor.on('init', () => { - try { - const doc = editor.getDoc() - const win = editor.getWin() as unknown as { whTooltips?: unknown } - win.whTooltips = { colorLinks: true, iconizeLinks: true, renameLinks: true } - if (!doc.getElementById('wh-tooltips-editor')) { - const s = doc.createElement('script') - s.id = 'wh-tooltips-editor' - s.src = 'https://wow.zamimg.com/js/tooltips.js' - doc.head.appendChild(s) - } - // procesa el contenido inicial (posts en edición) cuando el script cargue - setTimeout(refreshEditorWowhead, 400) - setTimeout(refreshEditorWowhead, 1200) - } catch { - /* preview no crítico */ - } - }) + // OJO: no se ejecuta wowhead DENTRO del editor a propósito. Si se hace, + // wowhead muta el DOM editable (icono, clase icontinyl y una tabla enorme + // oculta con el tooltip) y TinyMCE lo serializa al publicar, ensuciando el + // post. El editor muestra el enlace con su nombre; el icono y el color de + // calidad los pone wowhead en la página publicada (como recruit). editor.ui.registry.addButton('wowhead', { text: 'Wowhead', tooltip: L.tooltip, @@ -152,33 +125,27 @@ export function ForumEditor({ // Resolver nombre + calidad en el servidor para insertar el enlace // YA con su nombre real y su color (clase qN), sin el parpadeo de // «item=xxx». Si falla, se cae al modo data-wh-rename-link. + // Igual que recruit: se inserta un enlace PLANO con el nombre real + // como texto (resuelto en el servidor, sin el parpadeo de item=xxx), + // y wowhead le añade el icono (como fondo, icontinyl) y el color de + // calidad al vuelo. Nada de ni clases horneadas: así no sale + // doble ni descolocado. Si no hay texto y no se resuelve el nombre, + // se cae a data-wh-rename-link para que wowhead lo nombre. let name = custom - let qClass = '' - let iconHtml = '' - try { - const res = await fetch(`/api/forum/wowhead?type=${type}&id=${id}&locale=${locale}`) - const info = (await res.json()) as { - name: string | null - quality: number | null - icon: string | null + if (!name) { + try { + const res = await fetch(`/api/forum/wowhead?type=${type}&id=${id}&locale=${locale}`) + const info = (await res.json()) as { name: string | null } + if (info.name) name = info.name + } catch { + /* wowhead no disponible: fallback abajo */ } - if (!name && info.name) name = info.name - if (typeof info.quality === 'number' && info.quality >= 0) qClass = ` q${info.quality}` - // Icono horneado: así no depende del script (que, al ver la clase - // de calidad, da el enlace por procesado y no añadía icono). - if (info.icon) { - iconHtml = `` - } - } catch { - /* wowhead no disponible: se usa el fallback */ } const rename = name ? '' : ' data-wh-rename-link="true"' const label = esc(name || `${type}=${id}`) editor.insertContent( - `${iconHtml}${label} `, + `${label} `, ) - // icono en el editor (el nombre y el color ya van puestos) - setTimeout(refreshEditorWowhead, 60) }, }) }, diff --git a/web-next/lib/forum-sanitize.ts b/web-next/lib/forum-sanitize.ts index 488cc5d..7a4a035 100644 --- a/web-next/lib/forum-sanitize.ts +++ b/web-next/lib/forum-sanitize.ts @@ -39,7 +39,7 @@ export function cleanPostHtml(html: string): string { // automáticamente (sobrescribe el renameLinks:false global, que existe para no // pisar los nombres en español de la tienda). data-wh-icon-size ajusta el icono. a: ['href', 'title', 'target', 'data-wowhead', 'data-wh-rename-link', 'data-wh-icon-size', 'class'], - img: ['src', 'alt', 'title', 'width', 'height', 'class'], + img: ['src', 'alt', 'title', 'width', 'height'], span: ['style', 'data-wowhead'], div: ['style'], td: ['colspan', 'rowspan'],