週末に別の目的で Nokogiri を使用していましたが、あまり夢中にならないようにします。それは一種の依存性があります。Nokogiri がまだ記憶に残っているうちに、埋め込みコードを見てみることにしました。
これに興味があるのは、ニュースサイトやブログサイトで Discourse がより広く使用されるのを見たいからです。もしそうなった場合、新しいサイトの所有者が現在の埋め込み機能に不満を感じるようになる可能性があります。改善のためのアイデアをいくつか紹介します。
EmbeddableHost モデルに 2 つの新しいオプション属性を追加します。
target_selector: 埋め込むコンテンツを含む外側の CSS セレクター
exclude_selectors: target_selector によって選択されたコンテンツから除外する CSS セレクターのリスト。
管理画面の「埋め込み」ページの各埋め込みホスト行に「設定」ボタンを追加する必要があります。このボタンをクリックすると、メール / プレビュー要約ページに似たページが開きます。
ホスト設定ページには、ホストの target_selector および exclude_selectors 設定を入力するためのフィールドと、指定された値を特定の Web ページに対してテストできる URL フィールドを備えたフォームがあります。テストは基本的に、指定された target_selector および exclude_selectors 値で TopicEmbed.parse_html を実行し、結果を表示するだけです。
parse_html コードの変更は簡単にテストできます。以下に考えられるアプローチを示します。このコードは概念実証にすぎないことに注意してください。
topic_embed.rb に編集済み (discourse/app/models/topic_embed.rb at main · discourse/discourse · GitHub)
###########################################################################
# `target_selector` と `exclude_selectors` は、理想的にはドメインの `EmbeddableHost` レコードから見つけられるべきです
# これらの特定の の設定は 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
# ホストに `target_selector` が設定されていない場合は Readability にフォールバックします
read_doc = Readability::Document.new(html, opts)
end
###########################################################################
新しいクラスを作成せずにテストするには、TopicEmbed クラスに追加された基本的な article_content メソッドを次に示します。
def self.article_content(html, target_selector, exclude_selectors = [])
doc = Nokogiri::HTML(html)
# コメントとスクリプトタグを削除します
doc.xpath('//comment()').each { |i| i.remove }
doc.css("script, style").each { |i| i.remove }
# target_selector の NodeSet を取得します
# 返されたセットが空の場合は、ここで Readability を使用してフォールバックする必要があるかもしれません
selected_nodes = doc.css(target_selector)
# ノードを除外します
unless exclude_selectors.empty?
selected_nodes.css(*exclude_selectors).each do |node|
node.remove
end
end
# 画像サイズを処理します。改善が必要かもしれません
selected_nodes.css('img').each do |img|
img.remove_attribute('width')
img.remove_attribute('height')
end
# とりあえず、ソースが許可されている場合は iframe を許可します
# remove_empty_nodes ステップで iframe が削除されるのを防ぐために `[data-sanitized="true"]` を使用します
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
# 空の 'p' および 'div' ノードを削除します
selected_nodes.css('p', 'div').each do |node|
node.remove if node.content.strip.empty? && !node.at_css('iframe[data-sanitized="true"]')
end
# ノードを文字列に変換し、`content` メソッドを持つオブジェクトを返します
content = selected_nodes.to_s
OpenStruct.new(content: content)
end
複数のドメインに対して少し調整するだけで、うまくいくと確信しています。これまでの BBS の結果は良好です。
目標は、サイト所有者が簡単に理解して自分で設定できるものを作成することです。このアプローチでは、target_selector が具体的であるほど、exclude_selectors を設定しやすくなります。たとえば、WordPress サイトの場合、.entry-content を target_selector として選択すれば、それ以上の設定は不要になります。サイト所有者が .entry-content HTML の基本以上のものを取得したい場合は、設定ホストページでその方法を見つけることができます。
唯一の実際の問題は、HTML が非常に一貫性のないホストの場合です。その場合は、Ruby Readability をフォールバックとして維持することで対処できます。