Merge pull request #185 from Retrospring/feature/opengraph

Add OpenGraph metadata on user profiles and answers
This commit is contained in:
Karina Kwiatek 2021-08-10 13:48:02 +02:00 committed by GitHub
commit 1cf2733b69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 4 deletions

View file

@ -1,4 +1,6 @@
module ApplicationHelper
include ApplicationHelper::GraphMethods
def nav_entry(body, path, options = {})
options = {
badge: nil,

View file

@ -0,0 +1,59 @@
module ApplicationHelper::GraphMethods
# Creates <meta> tags for OpenGraph properties from a hash
# @param values [Hash]
def opengraph_meta_tags(values)
values.map { |name, content| tag(:meta, property: name, content: content) }.join("\n").html_safe
end
# Creates <meta> tags from a hash
# @param values [Hash]
def meta_tags(values)
values.map { |name, content| tag(:meta, name: name, content: content) }.join("\n").html_safe
end
# @param user [User]
def user_opengraph(user)
opengraph_meta_tags({
'og:title': user.safe_name,
'og:type': 'profile',
'og:image': full_profile_picture_url(user),
'og:url': show_user_profile_url(user.screen_name),
'og:description': user.bio,
'og:site_name': APP_CONFIG['site_name'],
'profile:username': user.screen_name
})
end
# @param user [User]
def user_twitter_card(user)
meta_tags({
'twitter:card': 'summary',
'twitter:site': '@retrospring',
'twitter:title': user.motivation_header.presence || "Ask me anything!",
'twitter:description': "Ask #{user.safe_name} anything on Retrospring",
'twitter:image': full_profile_picture_url(user)
})
end
# @param answer [Answer]
def answer_opengraph(answer)
opengraph_meta_tags({
'og:title': "#{answer.user.safe_name} answered: #{answer.question.content}",
'og:type': 'article',
'og:image': full_profile_picture_url(answer.user),
'og:url': show_user_answer_url(answer.user.screen_name, answer.id),
'og:description': answer.content,
'og:site_name': APP_CONFIG['site_name']
})
end
def full_profile_picture_url(user)
# @type [String]
profile_picture_url = user.profile_picture.url(:large)
if profile_picture_url.start_with? '/'
"#{root_url}#{profile_picture_url[1..-1]}"
else
profile_picture_url
end
end
end

View file

@ -253,6 +253,10 @@ class User < ApplicationRecord
!self.export_processing
end
def safe_name
self.display_name.presence || self.screen_name
end
# %w[admin moderator].each do |m|
# define_method(m) { raise "not allowed: #{m}" }
# define_method(m+??) { raise "not allowed: #{m}?"}

View file

@ -1,3 +1,4 @@
- provide(:title, answer_title(@answer))
- provide(:og, answer_opengraph(@answer))
.container.container--main
= render 'answerbox', a: @answer, display_all: @display_all

View file

@ -1,5 +1,5 @@
!!! 5
%html{ lang: 'en' }
%html{ lang: 'en', prefix: 'og: https://ogp.me/ns#' }
%head
%meta{ charset: 'utf-8' }
%meta{ 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }
@ -17,6 +17,8 @@
- if user_signed_in? && current_user.mod?
= javascript_pack_tag 'legacy-moderation', data: { 'turbolinks-track': 'reload' }
= csrf_meta_tags
= yield(:og)
= yield(:meta)
%body
- if user_signed_in?
= render 'navigation/main'

View file

@ -10,5 +10,8 @@
%button.btn.btn-light#load-more-btn{ type: :button, data: { last_id: @answers_last_id } }
= t 'views.actions.load'
- provide(:title, user_title(@user))
- parent_layout 'user/profile'
:ruby
provide(:title, user_title(@user))
provide(:og, user_opengraph(@user))
provide(:meta, user_twitter_card(@user))
parent_layout 'user/profile'

View file

@ -3,6 +3,91 @@
require "rails_helper"
describe ApplicationHelper, :type => :helper do
describe "#bootstrap_color" do
it 'should map error and alert to danger' do
expect(bootstrap_color("error")).to eq("danger")
expect(bootstrap_color("alert")).to eq("danger")
end
it 'should map notice to info' do
expect(bootstrap_color("notice")).to eq("info")
end
it 'should return any uncovered value' do
expect(bootstrap_color("success")).to eq("success")
end
end
describe "#user_opengraph" do
context "sample user" do
let(:user) { FactoryBot.create(:user,
display_name: 'Cunes',
bio: 'A bunch of raccoons in a trenchcoat.',
screen_name: 'raccoons') }
subject { user_opengraph(user) }
it 'should generate a matching OpenGraph structure for a user' do
allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring')
expect(subject).to eq(<<-EOS.chomp)
<meta property="og:title" content="Cunes" />
<meta property="og:type" content="profile" />
<meta property="og:image" content="http://test.host/images/large/no_avatar.png" />
<meta property="og:url" content="http://test.host/raccoons" />
<meta property="og:description" content="A bunch of raccoons in a trenchcoat." />
<meta property="og:site_name" content="pineapplespring" />
<meta property="profile:username" content="raccoons" />
EOS
end
end
end
describe "#user_twitter_card" do
context "sample user" do
let(:user) { FactoryBot.create(:user,
display_name: '',
bio: 'A bunch of raccoons in a trenchcoat.',
screen_name: 'raccoons') }
subject { user_twitter_card(user) }
it 'should generate a matching OpenGraph structure for a user' do
expect(subject).to eq(<<-EOS.chomp)
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@retrospring" />
<meta name="twitter:title" content="Ask me anything!" />
<meta name="twitter:description" content="Ask raccoons anything on Retrospring" />
<meta name="twitter:image" content="http://test.host/images/large/no_avatar.png" />
EOS
end
end
end
describe "#answer_opengraph" do
context "sample user and answer" do
let!(:user) { FactoryBot.create(:user,
display_name: '',
bio: 'A bunch of raccoons in a trenchcoat.',
screen_name: 'raccoons') }
let(:answer) { FactoryBot.create(:answer,
user_id: user.id,) }
subject { answer_opengraph(answer) }
it 'should generate a matching OpenGraph structure for a user' do
allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring')
expect(subject).to eq(<<-EOS.chomp)
<meta property="og:title" content="raccoons answered: #{answer.question.content}" />
<meta property="og:type" content="article" />
<meta property="og:image" content="http://test.host/images/large/no_avatar.png" />
<meta property="og:url" content="http://test.host/raccoons/a/#{answer.id}" />
<meta property="og:description" content="#{answer.content}" />
<meta property="og:site_name" content="pineapplespring" />
EOS
end
end
end
describe "#rails_admin_path_for_resource" do
context "user resource" do
let(:resource) { FactoryBot.create(:user) }
@ -13,4 +98,4 @@ describe ApplicationHelper, :type => :helper do
end
end
end
end
end