diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 7c010de1d..4ad0202d8 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -340,6 +340,19 @@ module ApplicationHelper
end
end
+ def canonical_url(url = nil)
+ if url.present?
+ content_for(:canonical_url) { url }
+ elsif content_for(:canonical_url).present?
+ content_for(:canonical_url)
+ else
+ request_params = request.params.sort.to_h.with_indifferent_access
+ request_params.delete(:page) if request_params[:page].to_i == 1
+ request_params.delete(:limit)
+ url_for(**request_params, host: Danbooru.config.hostname, only_path: false)
+ end
+ end
+
def atom_feed_tag(title, url = {})
content_for(:html_header, auto_discovery_link_tag(:atom, url, title: title))
end
diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb
index bf1a94442..ea2c05e17 100644
--- a/app/views/layouts/default.html.erb
+++ b/app/views/layouts/default.html.erb
@@ -5,6 +5,7 @@
<%= render "meta_links", collection: @current_item %>
+ <%= tag.link rel: "canonical", href: canonical_url %>
<%= csrf_meta_tag %>
<% unless CurrentUser.enable_desktop_mode? %>
diff --git a/app/views/posts/partials/index/_seo_meta_tags.html.erb b/app/views/posts/partials/index/_seo_meta_tags.html.erb
index 8fbfe0a8c..1c58f8d5a 100644
--- a/app/views/posts/partials/index/_seo_meta_tags.html.erb
+++ b/app/views/posts/partials/index/_seo_meta_tags.html.erb
@@ -10,6 +10,10 @@
<% atom_feed_tag "Posts: #{@post_set.tag_string}", posts_url(tags: @post_set.tag_string, format: :atom) %>
<% end %>
+<% if params[:tags].blank? && @post_set.current_page == 1 %>
+ <% canonical_url root_url(host: Danbooru.config.hostname) %>
+<% end %>
+
<% if @post_set.hide_from_crawler? %>
<% end %>
@@ -18,8 +22,6 @@
<% end %>
-<%= tag.meta name: "canonical", content: posts_url(tags: params[:tags], host: Danbooru.config.hostname, protocol: "https") %>
-
<% if @post_set.best_post.present? %>
<%= tag.meta property: "og:image", content: @post_set.best_post.open_graph_image_url %>
<%= tag.meta name: "twitter:image", content: @post_set.best_post.open_graph_image_url %>
diff --git a/app/views/posts/show.html.erb b/app/views/posts/show.html.erb
index 3aa5e7798..9a4112cfe 100644
--- a/app/views/posts/show.html.erb
+++ b/app/views/posts/show.html.erb
@@ -1,7 +1,8 @@
<% page_title @post.presenter.humanized_essential_tag_string %>
<% meta_description "View this #{@post.image_width}x#{@post.image_height} #{number_to_human_size(@post.file_size)} image" %>
-
+<% canonical_url post_url(@post, host: Danbooru.config.hostname) %>
<% atom_feed_tag "Comments for post ##{@post.id}", comments_url(:atom, search: { post_id: @post.id }) %>
+
<%= render "posts/partials/common/secondary_links" %>
<% content_for(:sidebar) do %>
@@ -148,8 +149,6 @@
<%= tag.meta property: "og:image", content: @post.open_graph_image_url %>
<% end %>
- <%= tag.meta name: "canonical", content: post_url(@post, host: Danbooru.config.hostname, protocol: "https") %>
-
<% if @post.twitter_card_supported? %>
diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb
index 41bdc1c88..1e673a532 100644
--- a/test/functional/posts_controller_test.rb
+++ b/test/functional/posts_controller_test.rb
@@ -1,6 +1,10 @@
require "test_helper"
class PostsControllerTest < ActionDispatch::IntegrationTest
+ def assert_canonical_url_equals(expected)
+ assert_equal(expected, response.parsed_body.css("link[rel=canonical]").attribute("href").value)
+ end
+
context "The posts controller" do
setup do
@user = travel_to(1.month.ago) {create(:user)}
@@ -10,11 +14,29 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
context "index action" do
setup do
mock_post_search_rankings(Date.today, [["1girl", 100], ["original", 50]])
+ create_list(:post, 2)
end
- should "render" do
- get posts_path
- assert_response :success
+ context "for an empty search" do
+ should "render the first page" do
+ get root_path
+ assert_response :success
+ assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
+
+ get posts_path
+ assert_response :success
+ assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
+
+ get posts_path(page: 1)
+ assert_response :success
+ assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
+ end
+
+ should "render the second page" do
+ get posts_path(page: 2, limit: 1)
+ assert_response :success
+ assert_canonical_url_equals(posts_url(page: 2, host: Danbooru.config.hostname))
+ end
end
context "with a single tag search" do
@@ -22,6 +44,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
get posts_path, params: { tags: "does_not_exist" }
assert_response :success
assert_select "#show-excerpt-link", count: 0
+ assert_canonical_url_equals(posts_url(tags: "does_not_exist", host: Danbooru.config.hostname))
end
should "render for an artist tag" do
@@ -261,7 +284,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
get posts_path(format: :atom)
assert_response :success
- assert_select "entry", 1
+ assert_select "entry", 3
end
should "render with tags" do
@@ -272,7 +295,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
end
should "hide restricted posts" do
- @post.update(is_banned: true)
+ Post.update_all(is_banned: true)
get posts_path(format: :atom)
assert_response :success
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 892046f7e..6fed3f46b 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -64,6 +64,7 @@ class ActionDispatch::IntegrationTest
extend ControllerHelper
register_encoder :xml, response_parser: ->(body) { Nokogiri.XML(body) }
+ register_encoder :html, response_parser: ->(body) { Nokogiri.HTML5(body) }
def method_authenticated(method_name, url, user, **options)
post session_path, params: { name: user.name, password: user.password }