No me voy a entusiasmar demasiado con esto, pero estuve usando Nokogiri para otra cosa durante el fin de semana. Es algo adictivo. Pensé en echar un vistazo al código de incrustación mientras Nokogiri estaba fresco en mi mente.
Mi interés en esto es que me gustaría ver que Discourse se use más ampliamente en sitios de noticias y blogs. Si eso sucediera, puedo imaginar que los nuevos propietarios de sitios se frustren con la funcionalidad de incrustación actual. Aquí hay una idea para mejorarla:
Agregue dos nuevos atributos opcionales al modelo EmbeddableHost:
target_selector: el selector CSS externo que contiene el contenido que se va a incrustar
exclude_selectors: una lista de selectores CSS que se excluirán del contenido seleccionado por target_selector.
Se debe agregar un botón “Configurar” a cada fila de Host Incrustable en la página Admin / Incrustación. Hacer clic en ese botón abre una página similar a la página Correos electrónicos / Resumen de vista previa.
La página Configurar Host tendría un formulario con campos para ingresar la configuración target_selector y exclude_selectors del host, y un campo de URL que permitiría probar los valores proporcionados contra una página web específica. La prueba esencialmente solo ejecutaría TopicEmbed.parse_html con los valores target_selector y exclude_selectors proporcionados, y luego mostraría los resultados.
Los cambios en el código parse_html son fáciles de probar. Aquí hay un enfoque posible. Tenga en cuenta que este código es solo una prueba de concepto:
editado en topic_embed.rb (discourse/app/models/topic_embed.rb at main · discourse/discourse · GitHub)
###########################################################################
# `target_selector` y `exclude_selectors` idealmente se encontrarían en el registro `EmbeddableHost` del dominio
# estas configuraciones particulares se usaron para probar contra boingboing.net
target_selector = 'article'
exclude_selectors = ['.article-header, .share-comments-container', '.boing-single-post-rev-content', '.next-post-list-container', '.boing-end-of-article-container-on-single-post-pages']
if defined?(target_selector) && target_selector.present?
read_doc = article_content(html, target_selector, exclude_selectors)
else
# recurrir a Readability si `target_selector` no está configurado para el host
read_doc = Readability::Document.new(html, opts)
end
###########################################################################
Para probar sin crear una nueva clase, aquí hay un método básico article_content agregado a la clase TopicEmbed:
def self.article_content(html, target_selector, exclude_selectors = [])
doc = Nokogiri::HTML(html)
# eliminar comentarios y etiquetas de script
doc.xpath('//comment()').each { |i| i.remove }
doc.css("script, style").each { |i| i.remove }
# obtener el NodeSet para el target_selector
# tal vez recurrir a Readability aquí si el conjunto devuelto está vacío
selected_nodes = doc.css(target_selector)
# excluir nodos
unless exclude_selectors.empty?
selected_nodes.css(*exclude_selectors).each do |node|
node.remove
end
end
# tratar con tamaños de imagen, podría necesitar mejoras
selected_nodes.css('img').each do |img|
img.remove_attribute('width')
img.remove_attribute('height')
end
# solo por si acaso, permitir iframes si su fuente está permitida
# usar `[data-sanitized="true"]` para evitar que los iframes sean eliminados en el paso remove_empty_nodes
allowed_iframe_sources = SiteSetting.allowed_iframes.split('|')
selected_nodes.css('iframe').each do |iframe|
allowed = allowed_iframe_sources.any? do |allowed_source|
iframe['src'].start_with?(allowed_source)
end
if allowed
iframe['data-sanitized'] = 'true'
iframe['width'] = '690'
iframe['height'] = '388'
else
iframe.remove
end
end
# eliminar nodos 'p' y 'div' vacíos
selected_nodes.css('p', 'div').each do |node|
node.remove if node.content.strip.empty? && !node.at_css('iframe[data-sanitized="true"]')
end
# convertir los nodos a una cadena y devolver un objeto con un método `content`
content = selected_nodes.to_s
OpenStruct.new(content: content)
end
Estoy bastante seguro de que solo tomaría un poco de ajuste en varios dominios para que funcione correctamente. Los resultados que he estado obteniendo para BBS son buenos hasta ahora.
El objetivo es crear algo que los propietarios de sitios puedan entender y configurar fácilmente por sí mismos. Con este enfoque, cuanto más específico sea el target_selector, más fácil será configurar el exclude_selectors. Por ejemplo, para un sitio de WordPress, si se seleccionara .entry-content como target_selector, no se requeriría ninguna configuración adicional. Si los propietarios del sitio quisieran obtener más que el HTML básico de .entry-content, podrían averiguar cómo hacerlo en la página Configurar Host.
El único problema real que puedo ver es para los hosts con HTML muy inconsistente. Ese caso podría manejarse manteniendo Ruby Readability como respaldo.