Темам встраивания нужна небольшая помощь

Сегодня я вспомнил об этом после нажатия кнопки «Показать полный пост» для Introducing Discourse AI. Полный пост, отображаемый на Discourse, лишён всех изображений и многих заголовков. Добавляя путаницы, отображаются подписи к изображениям, но без самих изображений.

Возможно, проблему на Meta для её (Ghost?) блога можно исправить, изменив настройку сайта «Разрешённые селекторы встраивания» (allowed embed selectors): Configuring allowed embed selectors. По своему опыту знаю, что настройка этого параметра может быть непростым процессом. Если вы решите его изменить, внимательно следите за результатами.

Discourse обладает большим потенциалом для функционирования как система комментариев для внешних постов, но для качественной реализации этого необходимо, чтобы при нажатии кнопки «Показать полный пост» надёжно загружались все элементы внешнего поста. Мне кажется, проблема в том, что Ruby Readability gem, используемый для парсинга внешних постов, не предназначен для той задачи, которую Discourse пытается с его помощью решить. Кроме того, он не поддерживается активно: GitHub - cantino/ruby-readability: Port of arc90's readability project to Ruby · GitHub.

3 лайка

Да, на данный момент мы либо перейдём на что-то другое, что немного улучшит ситуацию, либо просто изменим стратегию встраивания: заменим Показать полный пост на Читать полный пост — простую ссылку на оригинальный пост. В конце концов, возможно, бессмысленно бороться со всеми возможными проблемами встраивания на каждом сайте.

4 лайка

@sam только что это исправил, посмотри.

3 лайка

Мы готовимся запустить наш блог на Ghost и использовать интеграцию Ghost и Discourse. Очень рады этому изменению!

4 лайка

Изображения теперь подгружаются. Я не очень хорош в играх типа «найди отличия», но всё же замечаю некоторые расхождения:

  • Отсутствует заголовок Semantic Related Topics
  • Отсутствует заголовок Community Sentiment
  • В разделе Modules Providers отсутствует маркированный список
  • Отсутствует заголовок Installing Discourse AI on your community

В идеале, призыв «Зарегистрируйтесь для получения нашей рассылки» не должен включаться во встроенный пост.

Возможность легко цитировать встроенный пост кажется важной. Подумав об этом сейчас, я не уверен, какое поведение ожидается при нажатии кнопок «развернуть/свернуть» и «перейти к сообщению» для цитат внутри встроенного поста.

Это сложная проблема. В идеале всё должно быть так же просто, как очистка HTML, содержащегося в элементе article или main поста, но я подозреваю, что даже с таким подходом возникнут проблемы. Например, потребуется специальная обработка, чтобы предотвратить дублирование элемента h1 блога, если header находится внутри article.

1 лайк

Похоже, это происходит даже в readability.js, это режим чтения Firefox:

<h2 id="installing-discourse-ai-on-your-community">
      <strong>Installing Discourse AI on your community</strong>
</h2>

Посмотрим, есть ли простой способ исправить это…

Не уверен насчет этого… но если нам действительно очень этого хочется, мы можем добавить .discourse-newsletter-signup в blocked_embed_selectors

4 лайка

Да, readablity.js основан на том же коде, что и GitHub - cantino/ruby-readability: Port of arc90's readability project to Ruby · GitHub, поэтому, вероятно, для удаления этих элементов используется та же логика. Хотя readablity.js в целом справляется лучше, чем Ruby Readability.

Призыв к действию (CTA) в письме запутывает, потому что поле ввода email удаляется из встроенного поста. Технически я не уверен, что CTA должен находиться внутри article.

1 лайк

Напоминаю об этом, так как я согласен с @simon, что этот вопрос следует пересмотреть в будущем.

Значительная часть запросов в службу поддержки плагина WP Discourse связана с проблемами парсинга читаемости.

Я думаю, это точно отражает моё первоначальное впечатление.

Тем не менее, в данный момент у меня нет лучшего решения, кроме этого.

Однако я готов внести вклад в поиск лучшего решения, чем текущее положение дел, так как это снизит нагрузку на службу поддержки WP Discourse.

1 лайк

Они действительно рассматривают проблемы, но медленно их исправляют…

Настроить так, чтобы MiniRacer оборачивал readability, не слишком сложно… Я сделал прототип этого.

Возможно, мы могли бы перейти к этой реализации, но мы уже разошлись в подходах, поэтому в итоге пришлось бы отказаться от некоторых функций.

Это непростая проблема для решения.

2 лайка

Да, справедливо, однако мне кажется, что это превратится в бесконечную игру «кто-кто». Всегда найдётся кто-то, кто скажет:

Мой пост на сайте выглядит как X, а когда я нажимаю «Показать полный пост», он выглядит как Y, и я хочу, чтобы они были идентичны.

Думаю, мой более глубокий вопрос заключается в том, есть ли реальная польза от этого функционала, который никогда не будет идеальным, по сравнению с

Создавая кнопку «Показать полный пост», мы формируем у пользователей ожидания точности, которую Discourse не может полностью обеспечить. Моя главная забота — управление ожиданиями.

Полагаю, вы призываете к удалению функции встраивания. Я не уверен, что поддерживаю это. Считайте, что сайты, встраивающие очень неструктурированный контент, должны использовать простую форму «ссылка на оригинал». Однако сайты, встраивающие более структурированный контент, могут использовать режим чтения, хотя он и несовершенен.

1 лайк

Не обязательно. Я лишь говорю, что нужно лучше управлять ожиданиями.

99% владельцев веб-сайтов не знают, достаточно ли семантически верен их HTML для простого парсинга библиотекой вроде Readability, или даже не подозревают, что именно это определяет работу функции. Пользователи по умолчанию предполагают, что проблема «в Discourse» (или чаще в плагине WP Discourse), когда между постом на их сайте и контентом, появляющимся после нажатия кнопки «Показать полный пост», нет 100% соответствия.

Думаю, было бы полезно сделать опцию с кнопкой «Читать полный пост» (CTA) легко включаемой, а возможно, и включенной по умолчанию.

2 лайка

То, что я имел в виду, заключается в следующем: Ruby Readability — это «инструмент для извлечения основного читаемого содержимого веб-страницы». В случае с сайтом, публикующим посты в Discourse, можно с уверенностью предположить, что основное читаемое содержимое страницы известно и может быть определено с помощью внешнего CSS-селектора. Например, article, .entry-content, .post и т. д.

Инструмент, который я представляю, просто позволил бы сайтам определять внешний селектор для содержимого своих постов, а затем очищать HTML, находящийся внутри этого селектора. Более продвинутая версия позволила бы сайтам определять внутренние селекторы, которые они хотели бы исключить из Discourse.

На моём сайте WordPress есть пост с полностью стандартной разметкой. Я хотел бы опубликовать в Discourse всё, что находится внутри div с классом .entry-content. Почти всё работает, но я не могу понять, как настроить параметр allowed embed selector в Discourse, чтобы подтянуть списки из поста. Именно с такими проблемами сталкиваются сайты. Без возможности выполнить Rails.cache.clear настроить это действительно сложно.

Публикация поста в виде onebox — разумное решение для этой ситуации.

Редактирование: опция debug полезна для понимания того, что происходит: GitHub - cantino/ruby-readability: Port of arc90's readability project to Ruby · GitHub. В случае с исключёнными списками в моём посте WordPress:

Conditionally cleaned ul#. with weight 0 and content score 0 because it has too many links for its weight (0).

Однако это абсолютно легитимный список.

Очень часто запрашиваемой функцией для расширенных встраиваний является возможность отображения видео YouTube в расширенном содержимом. Запрет на это жёстко прописан в gem: ruby-readability/lib/readability.rb at master · cantino/ruby-readability · GitHub. Не уверен, стоит ли создавать PR, чтобы иметь возможность переопределять этот список с помощью опции.

2 лайка

Я не буду сильно увлекаться этим, но в выходные я использовал Nokogiri для чего-то другого. Это немного затягивает. Решил взглянуть на код встраивания, пока Nokogiri ещё свеж в памяти.

Мой интерес к этому заключается в том, что я хотел бы видеть более широкое использование Discourse новостными и блог-сайтами. Если бы это произошло, я могу представить, что новые владельцы сайтов будут разочарованы текущей функциональностью встраивания. Вот одна идея по её улучшению:

Добавить два новых необязательных атрибута к модели EmbeddableHost:

  • target_selector: внешний CSS-селектор, содержащий контент, который должен быть встроен.
  • exclude_selectors: список CSS-селекторов, которые должны быть исключены из контента, выбранного с помощью target_selector.

На странице «Администрирование / Встраивание» к каждой строке хоста встраивания должна быть добавлена кнопка «Настроить». При нажатии на неё открывается страница, похожая на страницу «Электронная почта / Предварительный просмотр сводки».

Страница «Настройка хоста» будет содержать форму с полями для ввода настроек target_selector и exclude_selectors хоста, а также поле URL, позволяющее проверить переданные значения на конкретной веб-странице. Тест по сути будет просто запускать TopicEmbed.parse_html с переданными значениями target_selector и exclude_selectors, а затем отображать результаты.


Изменения в коде 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
      # возврат к Readability, если `target_selector` не установлен для хоста
      read_doc = Readability::Document.new(html, opts)
    end
    ###########################################################################

Для тестирования без создания нового класса, вот базовый метод article_content, добавленный в класс TopicEmbed:

  def self.article_content(html, target_selector, exclude_selectors = [])
    doc = Nokogiri::HTML(html)
    # удаление комментариев и тегов script
    doc.xpath('//comment()').each { |i| i.remove }
    doc.css("script, style").each { |i| i.remove }

    # получение NodeSet для target_selector
    # возможно, здесь стоит сделать возврат к 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

    # просто так, разрешаем iframes, если их источник разрешён
    # используем `[data-sanitized="true"]`, чтобы предотвратить удаление iframes на шаге 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

    # удаление пустых узлов '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, если в качестве target_selector выбрано .entry-content, дальнейшая настройка не потребуется. Если владельцы сайтов захотят получить больше, чем базовый HTML .entry-content, они смогут разобраться, как это сделать на странице «Настройка хоста».

Единственная реальная проблема, которую я вижу, — это хосты с очень несогласованным HTML. Этот случай можно решить, оставив Ruby Readability в качестве запасного варианта.