Foro: no ejecutar wowhead dentro del editor (ensuciaba el post)
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 <img> 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 <img> 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) <noreply@anthropic.com>
This commit is contained in:
@@ -213,9 +213,6 @@ h3.post_field dd { margin: 0; }
|
|||||||
.post_container a.q5 { color: #ff8000 !important; }
|
.post_container a.q5 { color: #ff8000 !important; }
|
||||||
.post_container a.q6 { color: #e6cc80 !important; }
|
.post_container a.q6 { color: #e6cc80 !important; }
|
||||||
.post_container a.q7, .q7 { color: #00ccff !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; }
|
.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; }
|
ul.post_controls { display: flex; justify-content: flex-end; align-items: center; gap: 8px; list-style: none; padding: 15px; margin: auto 0 0; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { Editor } from '@tinymce/tinymce-react'
|
import { Editor } from '@tinymce/tinymce-react'
|
||||||
import { useLocale } from 'next-intl'
|
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
|
* 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',
|
'bold italic strikethrough forecolor backcolor emoticons | link wowhead image media | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat',
|
||||||
image_advtab: true,
|
image_advtab: true,
|
||||||
setup: (editor) => {
|
setup: (editor) => {
|
||||||
// Vista previa: carga el script de tooltips de wowhead DENTRO del iframe
|
// OJO: no se ejecuta wowhead DENTRO del editor a propósito. Si se hace,
|
||||||
// del editor para que el enlace insertado se vea con icono, nombre y color
|
// wowhead muta el DOM editable (icono, clase icontinyl y una tabla enorme
|
||||||
// de calidad, igual que al postear. No afecta a lo que se guarda: los
|
// oculta con el tooltip) y TinyMCE lo serializa al publicar, ensuciando el
|
||||||
// formularios envían el HTML del estado de React (lo insertado, limpio),
|
// post. El editor muestra el enlace con su nombre; el icono y el color de
|
||||||
// no la mutación visual que wowhead hace en el iframe.
|
// calidad los pone wowhead en la página publicada (como recruit).
|
||||||
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 */
|
|
||||||
}
|
|
||||||
})
|
|
||||||
editor.ui.registry.addButton('wowhead', {
|
editor.ui.registry.addButton('wowhead', {
|
||||||
text: 'Wowhead',
|
text: 'Wowhead',
|
||||||
tooltip: L.tooltip,
|
tooltip: L.tooltip,
|
||||||
@@ -152,33 +125,27 @@ export function ForumEditor({
|
|||||||
// Resolver nombre + calidad en el servidor para insertar el enlace
|
// Resolver nombre + calidad en el servidor para insertar el enlace
|
||||||
// YA con su nombre real y su color (clase qN), sin el parpadeo de
|
// 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.
|
// «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 <img> 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 name = custom
|
||||||
let qClass = ''
|
if (!name) {
|
||||||
let iconHtml = ''
|
try {
|
||||||
try {
|
const res = await fetch(`/api/forum/wowhead?type=${type}&id=${id}&locale=${locale}`)
|
||||||
const res = await fetch(`/api/forum/wowhead?type=${type}&id=${id}&locale=${locale}`)
|
const info = (await res.json()) as { name: string | null }
|
||||||
const info = (await res.json()) as {
|
if (info.name) name = info.name
|
||||||
name: string | null
|
} catch {
|
||||||
quality: number | null
|
/* wowhead no disponible: fallback abajo */
|
||||||
icon: string | null
|
|
||||||
}
|
}
|
||||||
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 = `<img class="wh-icon" src="${esc(wowheadIcon(info.icon, 'small'))}" width="18" height="18" alt="" />`
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
/* wowhead no disponible: se usa el fallback */
|
|
||||||
}
|
}
|
||||||
const rename = name ? '' : ' data-wh-rename-link="true"'
|
const rename = name ? '' : ' data-wh-rename-link="true"'
|
||||||
const label = esc(name || `${type}=${id}`)
|
const label = esc(name || `${type}=${id}`)
|
||||||
editor.insertContent(
|
editor.insertContent(
|
||||||
`${iconHtml}<a href="${href}" data-wowhead="${esc(wh)}" class="wowhead-link${qClass}"${rename} target="_blank" rel="noopener noreferrer">${label}</a> `,
|
`<a href="${href}" data-wowhead="${esc(wh)}"${rename} target="_blank" rel="noopener noreferrer">${label}</a> `,
|
||||||
)
|
)
|
||||||
// icono en el editor (el nombre y el color ya van puestos)
|
|
||||||
setTimeout(refreshEditorWowhead, 60)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function cleanPostHtml(html: string): string {
|
|||||||
// automáticamente (sobrescribe el renameLinks:false global, que existe para no
|
// 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.
|
// 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'],
|
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'],
|
span: ['style', 'data-wowhead'],
|
||||||
div: ['style'],
|
div: ['style'],
|
||||||
td: ['colspan', 'rowspan'],
|
td: ['colspan', 'rowspan'],
|
||||||
|
|||||||
Reference in New Issue
Block a user