Discourse does not produce large, more engaging Twitter cards

Top: How Discourse produces a not very much engaging so-called “mini card.”

Bottom: Correct, large Twitter card.

Solution:

Please change…

<meta name="twitter:card" content="summary" />

to

<meta name="twitter:card" content="summary_large_image">

in core.

Reference:

3 Likes

Hi @Terrapop

I don’t think that’s a solution. Sometimes images are bad. And bad images with summary_large_image are really bad.

May be an option so the user can change summary to summary_large_image.

3 Likes

In my case, I would opt for that as we make sure that images added to the topics we Tweet on our account have large enough images. AFAIK Twitter automatically falls back to the small version, if you don’t provide a large enough image even when summary_large_image is provided.

Even better would be if images sizes are vetted by core and depending on the minimum sizes required use summary or summary_large_image (min 300x157) automatically.

If that is not possible at all, I would certainly be happy to at least have the option to use summary_large_image instead of summary.

2 Likes

Is there any way to overwrite the behavior of def crawlable_meta_data with a simple plugin?

In particular, we would like to change:

elsif opts[:image].present?
      result << tag(:meta, name: 'twitter:card', content: "summary")
      result << tag(:meta, name: "twitter:image", content: opts[:image])

to

elsif opts[:image].present?
      result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
      result << tag(:meta, name: "twitter:image", content: opts[:image])

as we’re pretty depending on Twitter and big cards for engagement.

I’m quite new to Ruby, so any nudge into the right direction would be appreciated.

Update: Here is the plugin.rb for those who want to patch core because of the small images on Twitter:

after_initialize do
 reloadable_patch do |plugin|
  ApplicationHelper.module_eval do

    def crawlable_meta_data(opts = nil)
        opts ||= {}
        opts[:url] ||= "#{Discourse.base_url_no_prefix}#{request.fullpath}"

        if opts[:image].blank?
          twitter_summary_large_image_url = SiteSetting.site_twitter_summary_large_image_url

          if twitter_summary_large_image_url.present?
            opts[:twitter_summary_large_image] = twitter_summary_large_image_url
          end

          opts[:image] = SiteSetting.site_opengraph_image_url
        end

        # With and height available?

        # Use the correct scheme for opengraph/twitter image
        opts[:image] = get_absolute_image_url(opts[:image]) if opts[:image].present?
        opts[:twitter_summary_large_image] =
          get_absolute_image_url(opts[:twitter_summary_large_image]) if opts[:twitter_summary_large_image].present?

        # Add opengraph & twitter tags
        result = []
        result << tag(:meta, property: 'og:site_name', content: SiteSetting.title)

        if opts[:twitter_summary_large_image].present?
          result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
          result << tag(:meta, name: "twitter:image", content: opts[:twitter_summary_large_image])
        elsif opts[:image].present?
          result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
          result << tag(:meta, name: "twitter:image", content: opts[:image])
        else
          result << tag(:meta, name: 'twitter:card', content: "summary")
        end
        result << tag(:meta, property: "og:image", content: opts[:image]) if opts[:image].present?

        [:url, :title, :description].each do |property|
          if opts[property].present?
            content = (property == :url ? opts[property] : gsub_emoji_to_unicode(opts[property]))
            result << tag(:meta, { property: "og:#{property}", content: content }, nil, true)
            result << tag(:meta, { name: "twitter:#{property}", content: content }, nil, true)
          end
        end

        if opts[:read_time] && opts[:read_time] > 0 && opts[:like_count] && opts[:like_count] > 0
          result << tag(:meta, name: 'twitter:label1', value: I18n.t("reading_time"))
          result << tag(:meta, name: 'twitter:data1', value: "#{opts[:read_time]} mins 🕑")
          result << tag(:meta, name: 'twitter:label2', value: I18n.t("likes"))
          result << tag(:meta, name: 'twitter:data2', value: "#{opts[:like_count]} ❤")
        end

        if opts[:published_time]
          result << tag(:meta, property: 'article:published_time', content: opts[:published_time])
        end

        if opts[:ignore_canonical]
          result << tag(:meta, property: 'og:ignore_canonical', content: true)
        end

        result.join("\n")
      end

  end
 end
end

But it would be better if there would be an option for this going forward and if core would assess min Twitter images sizes to either do summary_large_image or summary (for very small images) if a admin opts for a new setting: “enable large Twitter summary images”.

6 Likes

I would like to know if it’s possible to change the current behavior of always showing summary_large_image to summary — I came across this post but it appears that it is asking for the reverse — so I guess that someone must have implemented it… however is it possible to do the reverse? Is there an option? I tried searching for Admin settings and can’t find it.

1 Like

For anyone else wanting this (to always use summary_large_image) - I’ve taken @Terrapop s code and made it into a plugin you can include:

- https://github.com/CubeCoders/discourse-twitter-large-card

What I’d really like to do is something similar to the Github card you’ll see attached to this post, a way to automatically generate dynamic cards for a given topic. Right now the card will be the first decent-sized image in the post. So if you create a post that starts with an image, it’ll end up using that.