Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set og:description meta tag to diary entry description #5056

Merged
merged 9 commits into from
Aug 14, 2024
7 changes: 5 additions & 2 deletions app/controllers/diary_entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ def show
@entry = entries.find_by(:id => params[:id])
if @entry
@title = t ".title", :user => params[:display_name], :title => @entry.title
@og_image = @entry.body.image
@og_image_alt = @entry.body.image_alt
@opengraph_properties = {
"og:image" => @entry.body.image,
"og:image:alt" => @entry.body.image_alt,
"og:description" => @entry.body.description
}
@comments = can?(:unhide, DiaryComment) ? @entry.comments : @entry.visible_comments
else
@title = t "diary_entries.no_such_entry.title", :id => params[:id]
Expand Down
18 changes: 9 additions & 9 deletions app/helpers/open_graph_helper.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
module OpenGraphHelper
require "addressable/uri"

def opengraph_tags(title = nil, og_image = nil, og_image_alt = nil)
def opengraph_tags(title, properties)
tags = {
"og:site_name" => t("layouts.project_name.title"),
"og:title" => title || t("layouts.project_name.title"),
"og:type" => "website",
"og:url" => url_for(:only_path => false),
"og:description" => t("layouts.intro_text")
"og:description" => properties["og:description"] || t("layouts.intro_text")
}.merge(
opengraph_image_properties(og_image, og_image_alt)
opengraph_image_properties(properties)
)

safe_join(tags.map do |property, content|
Expand All @@ -19,13 +19,13 @@ def opengraph_tags(title = nil, og_image = nil, og_image_alt = nil)

private

def opengraph_image_properties(og_image, og_image_alt)
def opengraph_image_properties(properties)
begin
if og_image
properties = {}
properties["og:image"] = Addressable::URI.join(root_url, og_image).normalize
properties["og:image:alt"] = og_image_alt if og_image_alt
return properties
if properties["og:image"]
image_properties = {}
image_properties["og:image"] = Addressable::URI.join(root_url, properties["og:image"]).normalize
image_properties["og:image:alt"] = properties["og:image:alt"] if properties["og:image:alt"]
return image_properties
end
rescue Addressable::URI::InvalidURIError
# return default image
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/_meta.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<% end -%>
<%= tag.link :rel => "search", :type => "application/opensearchdescription+xml", :title => "OpenStreetMap Search", :href => asset_path("osm.xml") %>
<%= tag.meta :name => "description", :content => "OpenStreetMap is the free wiki world map." %>
<%= opengraph_tags(@title, @og_image, @og_image_alt) %>
<%= opengraph_tags(@title, @opengraph_properties || {}) %>
<% if flash[:matomo_goal] -%>
<%= tag.meta :name => "matomo-goal", :content => flash[:matomo_goal] %>
<% end -%>
47 changes: 47 additions & 0 deletions lib/rich_text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module RichText
"Business Description:", "Additional Keywords:"
].freeze

MAX_DESCRIPTION_LENGTH = 500

def self.new(format, text)
case format
when "html" then HTML.new(text || "")
Expand Down Expand Up @@ -57,6 +59,10 @@ def image_alt
nil
end

def description
nil
end

protected

def simple_format(text)
Expand Down Expand Up @@ -105,6 +111,12 @@ def image_alt
@image_element.attr["alt"] if @image_element
end

def description
return @description if defined? @description

@description = first_truncated_text_content(document.root)
end

private

def document
Expand All @@ -120,9 +132,44 @@ def first_image_element(element)
end
end

def first_truncated_text_content(element)
if paragraph?(element)
truncated_text_content(element)
else
element.children.find do |child|
text = first_truncated_text_content(child)
break text unless text.nil?
end
end
end

def truncated_text_content(element)
text = ""

append_text = lambda do |child|
if child.type == :text
text << child.value
else
child.children.each do |c|
append_text.call(c)
break if text.length > MAX_DESCRIPTION_LENGTH
end
end
end
append_text.call(element)

return nil if text.blank?

text.truncate(MAX_DESCRIPTION_LENGTH)
end

def image?(element)
element.type == :img || (element.type == :html_element && element.value == "img")
end

def paragraph?(element)
element.type == :p || (element.type == :html_element && element.value == "p")
end
end

class Text < Base
Expand Down
22 changes: 22 additions & 0 deletions test/controllers/diary_entries_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,28 @@ def test_show_og_image_without_alt
assert_dom "head meta[property='og:image:alt']", :count => 0
end

def test_show_no_og_description
user = create(:user)
diary_entry = create(:diary_entry, :user => user, :body => "![nope](https://example.com/nope.jpg)")

get diary_entry_path(user, diary_entry)
assert_response :success
assert_dom "head meta[property='og:description']" do
assert_dom "> @content", I18n.t("layouts.intro_text")
end
end

def test_show_og_description
user = create(:user)
diary_entry = create(:diary_entry, :user => user, :body => "# Hello\n\n![hello](https://example.com/hello.jpg)\n\nFirst paragraph.\n\nSecond paragraph.")

get diary_entry_path(user, diary_entry)
assert_response :success
assert_dom "head meta[property='og:description']" do
assert_dom "> @content", "First paragraph."
end
end

def test_hide
user = create(:user)
diary_entry = create(:diary_entry, :user => user)
Expand Down
52 changes: 50 additions & 2 deletions test/lib/rich_text_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -250,16 +250,18 @@ def test_text_spam_score
assert_equal 141, r.spam_score.round
end

def test_text_no_image
def test_text_no_opengraph_properties
r = RichText.new("text", "foo https://example.com/ bar")
assert_nil r.image
assert_nil r.image_alt
assert_nil r.description
end

def test_html_no_image
def test_html_no_opengraph_properties
r = RichText.new("html", "foo <a href='https://example.com/'>bar</a> baz")
assert_nil r.image
assert_nil r.image_alt
assert_nil r.description
end

def test_markdown_no_image
Expand Down Expand Up @@ -328,6 +330,52 @@ def test_markdown_skip_html_image_without_src
assert_equal "have src", r.image_alt
end

def test_markdown_no_description
r = RichText.new("markdown", "#Nope")
assert_nil r.description
end

def test_markdown_description
r = RichText.new("markdown", "This is an article about something.")
assert_equal "This is an article about something.", r.description
end

def test_markdown_description_after_heading
r = RichText.new("markdown", "#Heading\n\nHere starts the text.")
assert_equal "Here starts the text.", r.description
end

def test_markdown_description_after_image
r = RichText.new("markdown", "![bar](https://example.com/image.jpg)\n\nThis is below the image.")
assert_equal "This is below the image.", r.description
end

def test_markdown_description_only_first_paragraph
r = RichText.new("markdown", "This thing.\n\nMaybe also that thing.")
assert_equal "This thing.", r.description
end

def test_markdown_description_elements
r = RichText.new("markdown", "*Something* **important** [here](https://example.com/).")
assert_equal "Something important here.", r.description
end

def test_markdown_html_description
r = RichText.new("markdown", "<p>Can use HTML tags.</p>")
assert_equal "Can use HTML tags.", r.description
end

def test_markdown_description_max_length
r = RichText.new("markdown", "x" * RichText::MAX_DESCRIPTION_LENGTH)
assert_equal "x" * RichText::MAX_DESCRIPTION_LENGTH, r.description

r = RichText.new("markdown", "y" * (RichText::MAX_DESCRIPTION_LENGTH + 1))
assert_equal "#{'y' * (RichText::MAX_DESCRIPTION_LENGTH - 3)}...", r.description

r = RichText.new("markdown", "*zzzzzzzzz*z" * ((RichText::MAX_DESCRIPTION_LENGTH + 1) / 10.0).ceil)
assert_equal "#{'z' * (RichText::MAX_DESCRIPTION_LENGTH - 3)}...", r.description
end

private

def assert_html(richtext, &block)
Expand Down