Skip to content

Commit

Permalink
Add structured data for news articles
Browse files Browse the repository at this point in the history
This implements a minimum of structured data for news articles.

 As per Google:

> Structured data is a standardized format for providing information
about a page and classifying the page content; for example, on a recipe
page, what are the ingredients, the cooking time and temperature, the
calories, and so on.

https://developers.google.com/search/docs/guides/intro-structured-data

We use the JSON-LD (JSON "Linked Data", https://json-ld.org) here. It's
Google's recommended way of implementing this and avoids a lot of the
hassle involved with Microdata, which interleaves the structured data
with HTML.

The output of this is tested with the Structured data testing tool:

https://search.google.com/structured-data/testing-tool

Unfortunately, the "publisher" attribute needs a logo. I've chosen the
Opengraph image for this because it's not likely to change and will
probably stay around for a long time. If we're going to implement more
structured data, we'll have to come up with an alternative (and a
better image).
  • Loading branch information
tijmenb committed Apr 16, 2018
1 parent 9e310ca commit 84bcaa3
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 8 deletions.
16 changes: 8 additions & 8 deletions app/presenters/content_item/updatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ def history
reverse_chronological_change_history
end

def first_public_at
content_item["details"]["first_public_at"]
end

def public_updated_at
content_item["public_updated_at"]
end

private

def change_history
Expand All @@ -39,13 +47,5 @@ def any_updates?
false
end
end

def first_public_at
content_item["details"]["first_public_at"]
end

def public_updated_at
content_item["public_updated_at"]
end
end
end
4 changes: 4 additions & 0 deletions app/presenters/news_article_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ class NewsArticlePresenter < ContentItemPresenter
def image
content_item["details"]["image"]
end

def structured_data
NewsArticleStructured.new(self).structured_data
end
end
64 changes: 64 additions & 0 deletions app/presenters/news_article_structured.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
class NewsArticleStructured
def initialize(presenter)
@presenter = presenter
end

def structured_data
return {} unless enough_structured_data?

# http://schema.org/NewsArticle
{
"@context" => "http://schema.org",
"@type" => "NewsArticle",
"mainEntityOfPage" => {
"@type" => "WebPage",
"@id" => page_url,
},
"headline" => presenter.title,
"datePublished" => presenter.first_public_at,
"dateModified" => presenter.public_updated_at,
"description" => presenter.description,
"publisher" => {
"@type" => "Organization",
"name" => "GOV.UK",
"url" => "https://www.gov.uk",
"logo" => {
"@type" => "ImageObject",
# TODO: change this to a better image, without the URL hard coded.
"url" => "https://assets.publishing.service.gov.uk/static/opengraph-image-a1f7d89ffd0782738b1aeb0da37842d8bd0addbd724b8e58c3edbc7287cc11de.png",
},
},
"image" => [
image["url"],
],
"author" => {
"@type" => "Organization",
"name" => publishing_organisation["title"],
"url" => Plek.current.website_root + publishing_organisation["base_path"],
},
}
end

private

attr_reader :presenter

def enough_structured_data?
# The author (for which we use the publishing org) and image are required
# fields. If the news article doesn't have them, don't use structured data
# at all.
publishing_organisation && image
end

def publishing_organisation
presenter.content_item.dig("links", "primary_publishing_organisation").to_a.first
end

def page_url
Plek.current.website_root + presenter.content_item["base_path"]
end

def image
presenter.image
end
end
4 changes: 4 additions & 0 deletions app/views/content_items/news_article.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<script type="application/ld+json">
<%= @content_item.structured_data.to_json.html_safe %>
</script>

<div class="grid-row">
<div class="column-two-thirds">
<%= render 'govuk_component/title', @content_item.title_and_context %>
Expand Down
16 changes: 16 additions & 0 deletions test/presenters/news_article_presenter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ class PresentedNewsArticleTest < NewsArticlePresenterTestCase
test 'presents the locale' do
assert_equal schema_item['locale'], presented_item.locale
end

test 'has no structured data if no publishing org' do
expected = {}
assert_equal expected, presented_item.structured_data
end

test 'has structured data' do
item = { "links" => { "primary_publishing_organisation" => [{ "title" => "Ministry of Magic", "base_path" => "/government/organisations/magic" }] } }

structured_data = presented_item("news_article", item).structured_data

assert_equal "https://www.test.gov.uk/government/news/christmas-2016-prime-ministers-message", structured_data["mainEntityOfPage"]["@id"]
assert_equal "2016-12-25T00:15:02.000+00:00", structured_data["datePublished"]
assert_equal "Ministry of Magic", structured_data["author"]["name"]
assert_equal "https://www.test.gov.uk/government/organisations/magic", structured_data["author"]["url"]
end
end

class HistoryModePresentedNewsArticle < NewsArticlePresenterTestCase
Expand Down

0 comments on commit 84bcaa3

Please sign in to comment.