Social Cards Part 2 (#2188)

* Social Cards Part 2

This is a follow up to part 1: https://github.com/thepracticaldev/dev.to/pull/2090.

This PR updates the og:image URLs to use the new social_preview.png urls
added in Part 1.

So that the already generated/cached images do not need to be recreated,
this sets a MIGRATION_DATETIME. Any object that has been updated after
that date will generate a new image

Objects from before that date will not. They will use the new url.

* Fix weird change made by rubocop autocorrect

* Handle organizations + users

* Run CI

* Use time with zone to be explicit about timezone

* Simplify branching in user_social_image_url method

* Fix param alignment + bump migration date
This commit is contained in:
Mike Coutermarsh 2019-05-01 08:07:25 -07:00 committed by Ben Halpern
parent 44b06ea442
commit 4c9959f0ff
10 changed files with 124 additions and 34 deletions

View file

@ -120,25 +120,6 @@ module ApplicationHelper
sign_url: true)
end
def cloud_social_image(article)
cache_key = "article-social-img-#{article}-#{article.updated_at}-#{article.comments_count}"
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
src = GeneratedImage.new(article).social_image
return src if src.start_with? "https://res.cloudinary.com/"
cl_image_path(src,
type: "fetch",
width: "1000",
height: "500",
crop: "imagga_scale",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
end
end
def tag_colors(tag)
Rails.cache.fetch("view-helper-#{tag}/tag_colors", expires_in: 5.hours) do
if (found_tag = Tag.select(%i[bg_color_hex text_color_hex]).find_by(name: tag))

View file

@ -0,0 +1,56 @@
module SocialImageHelper
# After this date we use SocialPreview controller directly rather than passing to URL2PNG.
# Keeping old URLs around since they are already generated.
SOCIAL_PREVIEW_MIGRATION_DATETIME = Time.zone.parse("2019-04-22T00:00:00Z")
def user_social_image_url(user)
return GeneratedImage.new(user).social_image unless use_new_social_url?(user)
if user.is_a?(Organization)
organization_social_preview_url(user, format: :png)
else
user_social_preview_url(user, format: :png)
end
end
def article_social_image_url(article)
return legacy_article_social_image(article) unless use_new_social_url?(article)
if (image = article.social_image || article.main_image || article.video_thumbnail_url)
return cl_image_path(image,
type: "fetch",
width: "1000",
height: "500",
crop: "imagga_scale",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
end
article_social_preview_url(article, format: :png)
end
def legacy_article_social_image(article)
cache_key = "article-social-img-#{article}-#{article.updated_at}-#{article.comments_count}"
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
src = GeneratedImage.new(article).social_image
return src if src.start_with? "https://res.cloudinary.com/"
cl_image_path(src,
type: "fetch",
width: "1000",
height: "500",
crop: "imagga_scale",
quality: "auto",
flags: "progressive",
fetch_format: "auto",
sign_url: true)
end
end
def use_new_social_url?(resource)
resource.updated_at > SOCIAL_PREVIEW_MIGRATION_DATETIME
end
end

View file

@ -583,7 +583,6 @@ class Article < ApplicationRecord
def async_bust
CacheBuster.new.bust_article(self)
HTTParty.get GeneratedImage.new(self).social_image if published
end
handle_asynchronously :async_bust
end

View file

@ -5,7 +5,7 @@ json.description @article.description
json.cover_image cloud_cover_url @article.main_image
json.published_at @article.published_at
json.readable_publish_date @article.readable_publish_date
json.social_image cloud_social_image(@article)
json.social_image article_social_image_url(@article)
json.tag_list @article.cached_tag_list
json.slug @article.slug
json.path @article.path

View file

@ -53,8 +53,8 @@
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:widgets:new-embed-design" content="on">
<% if @article.published %>
<meta property="og:image" content="<%= cloud_social_image(@article) %>" />
<meta name="twitter:image:src" content="<%= cloud_social_image(@article) %>">
<meta property="og:image" content="<%= article_social_image_url(@article) %>" />
<meta name="twitter:image:src" content="<%= article_social_image_url(@article) %>">
<% end %>
<% if !@article.published || (@article.positive_reactions_count < 7 && @article.user.comments_count < 1 && !@article.featured) ||
@article.featured_number.to_i < 1500000000 || @article.score < -10 %>
@ -76,7 +76,7 @@
<% end %>
<article itemscope itemtype="http://schema.org/Article" itemprop="mainEntityOfPage">
<meta itemprop="url" content="https://dev.to<%= @article.path %>">
<meta itemprop="image" content="<%= cloud_social_image(@article) %>">
<meta itemprop="image" content="<%= article_social_image_url(@article) %>">
<div itemprop="publisher" itemscope itemtype="https://schema.org/Organization">
<div itemprop="logo" itemscope itemtype="https://schema.org/ImageObject">
<meta itemprop="url" content="<%= asset_path "android-icon-192x192.png" %>">

View file

@ -20,8 +20,8 @@
<% if @tag_model %>
<meta name="twitter:card" content="summary_large_image">
<meta property="og:image" content="<%= GeneratedImage.new(@tag_model).social_image %>">
<meta name="twitter:image:src" content="<%= GeneratedImage.new(@tag_model).social_image %>">
<meta property="og:image" content="<%= tag_social_preview_url(@tag_model, format: :png) %>">
<meta name="twitter:image:src" content="<%= tag_social_preview_url(@tag_model, format: :png) %>">
<% else %>
<meta property="og:image" content="https://thepracticaldev.s3.amazonaws.com/i/g355ol6qsrg0j2mhngz9.png">
<meta name="twitter:image:src" content="https://thepracticaldev.s3.amazonaws.com/i/g355ol6qsrg0j2mhngz9.png">

View file

@ -28,8 +28,8 @@
<meta name="twitter:title" content="[Discussion] <%= @commentable.title %> — DEV">
<% end %>
<% if @commentable.class.name == "Article" %>
<meta property="og:image" content="<%= cloud_social_image(@commentable) %>" />
<meta name="twitter:image:src" content="<%= cloud_social_image(@commentable) %>">
<meta property="og:image" content="<%= article_social_image_url(@commentable) %>" />
<meta name="twitter:image:src" content="<%= article_social_image_url(@commentable) %>">
<% end %>
<% end %>

View file

@ -5,7 +5,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://dev.to/<%= @user.username %>" />
<meta property="og:title" content="<%= @user.name %> — DEV Profile" />
<meta property="og:image" content="<%= GeneratedImage.new(@user).social_image %>">
<meta property="og:image" content="<%= user_social_image_url(@user) %>">
<meta property="og:description" content="<%= @user.summary %>" />
<meta property="og:site_name" content="The DEV Community" />
@ -14,5 +14,5 @@
<meta name="twitter:creator" content="@<%= @user.twitter_username %>">
<meta name="twitter:title" content="<%= @user.name %> — DEV Profile">
<meta name="twitter:description" content="<%= @user.summary %>">
<meta name="twitter:image:src" content="<%= GeneratedImage.new(@user).social_image %>">
<meta name="twitter:image:src" content="<%= user_social_image_url(@user) %>">
<%= auto_discovery_link_tag(:rss, "https://dev.to/feed/#{@user.username}", title: "The Practical Dev RSS Feed") %>

View file

@ -160,10 +160,10 @@ Rails.application.routes.draw do
post "/pusher/auth" => "pusher#auth"
get "/social_previews/article/:id" => "social_previews#article"
get "/social_previews/user/:id" => "social_previews#user"
get "/social_previews/organization/:id" => "social_previews#organization"
get "/social_previews/tag/:id" => "social_previews#tag"
get "/social_previews/article/:id" => "social_previews#article", as: :article_social_preview
get "/social_previews/user/:id" => "social_previews#user", as: :user_social_preview
get "/social_previews/organization/:id" => "social_previews#organization", as: :organization_social_preview
get "/social_previews/tag/:id" => "social_previews#tag", as: :tag_social_preview
### Subscription vanity url
post "membership-action" => "stripe_subscriptions#create"

View file

@ -0,0 +1,54 @@
require "rails_helper"
describe SocialImageHelper do
let(:user) { create(:user) }
let(:article) { create(:article, main_image: nil) }
describe ".user_social_image_url" do
it "returns social preview path for newer users" do
url = helper.user_social_image_url(user)
expect(url).to eq user_social_preview_url(user, format: :png)
end
it "returns Organization social preview path for Orgs" do
organization = create(:organization)
url = helper.user_social_image_url(organization)
expect(url).to eq organization_social_preview_url(organization, format: :png)
end
it "returns older url2png image if already generated" do
user.updated_at = SocialImageHelper::SOCIAL_PREVIEW_MIGRATION_DATETIME - 1.week
url = helper.user_social_image_url(user)
expect(url).to eq GeneratedImage.new(user).social_image
end
end
describe ".article_social_image_url" do
it "returns social preview path for newer articles" do
url = helper.article_social_image_url(article)
expect(url).to eq article_social_preview_url(article, format: :png)
end
it "returns the main image if set" do
article.main_image = Faker::CryptoCoin.url_logo
url = helper.article_social_image_url(article)
expect(url).to match(/#{article.main_image}/)
end
it "returns older url2png image if already generated" do
article.updated_at = SocialImageHelper::SOCIAL_PREVIEW_MIGRATION_DATETIME - 1.week
url = helper.article_social_image_url(article)
expect(url).to eq GeneratedImage.new(article).social_image
end
end
end