Skip to content

Commit da78007

Browse files
authored
Merge pull request #325 from hmrc/fix-header-and-footer-links-when-generating-relative-links
TRG-668: Support sites deployed on a path other than "/" when generating header and footer links.
2 parents 4ae3ea6 + 7e16828 commit da78007

File tree

8 files changed

+141
-40
lines changed

8 files changed

+141
-40
lines changed

govuk_tech_docs.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
3737

3838
spec.add_dependency "autoprefixer-rails", "~> 10.2"
3939
spec.add_dependency "chronic", "~> 0.10.2"
40+
spec.add_dependency "haml", "< 6.0.0"
4041
spec.add_dependency "middleman", "~> 4.0"
4142
spec.add_dependency "middleman-autoprefixer", "~> 2.10.0"
4243
spec.add_dependency "middleman-compass", ">= 4.0.0"
@@ -47,7 +48,6 @@ Gem::Specification.new do |spec|
4748
spec.add_dependency "nokogiri"
4849
spec.add_dependency "openapi3_parser", "~> 0.9.0"
4950
spec.add_dependency "redcarpet", "~> 3.5.1"
50-
spec.add_dependency "haml", "< 6.0.0"
5151

5252
spec.add_development_dependency "byebug"
5353
spec.add_development_dependency "capybara", "~> 3.32"
Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
1+
require "uri"
12
module GovukTechDocs
23
module PathHelpers
3-
def get_path_to_resource(config, resource, current_page)
4-
if config[:relative_links]
5-
resource_path_segments = resource.path.split("/").reject(&:empty?)[0..-2]
6-
resource_file_name = resource.path.split("/")[-1]
4+
# Some useful notes from https://www.rubydoc.info/github/middleman/middleman/Middleman/Sitemap/Resource#url-instance_method :
5+
# 'resource.path' is "The source path of this resource (relative to the source directory, without template extensions)."
6+
# 'resource.destination_path', which is: "The output path in the build directory for this resource."
7+
# 'resource.url' is based on 'resource.destination_path', but is further tweaked to optionally strip the index file and prefixed with any :http_prefix.
78

8-
path_to_site_root = path_to_site_root config, current_page.path
9-
resource_path = path_to_site_root + resource_path_segments
10-
.push(resource_file_name)
11-
.join("/")
9+
# Calculates the path to the sought resource, taking in to account whether the site has been configured
10+
# to generate relative or absolute links.
11+
# Identifies whether the sought resource is an internal or external target: External targets are returned untouched. Path calculation is performed for internal targets.
12+
# Works for both "Middleman::Sitemap::Resource" resources and plain strings (which may be passed from the site configuration when generating header links).
13+
#
14+
# @param [Object] config
15+
# @param [Object] resource
16+
# @param [Object] current_page
17+
def get_path_to_resource(config, resource, current_page)
18+
if resource.is_a?(Middleman::Sitemap::Resource)
19+
config[:relative_links] ? get_resource_path_relative_to_current_page(config, current_page.path, resource.path) : resource.url
20+
elsif external_url?(resource)
21+
resource
22+
elsif config[:relative_links]
23+
get_resource_path_relative_to_current_page(config, current_page.path, resource)
1224
else
13-
resource_path = resource.url
25+
resource
1426
end
15-
resource_path
1627
end
1728

1829
def path_to_site_root(config, page_path)
@@ -26,5 +37,24 @@ def path_to_site_root(config, page_path)
2637
end
2738
path_to_site_root
2839
end
40+
41+
private
42+
43+
# Calculates the path to the sought resource, relative to the current page.
44+
# @param [Object] config Middleman config.
45+
# @param [String] current_page path of the current page, from the site root.
46+
# @param [String] resource_path_from_site_root path of the sought resource, from the site root.
47+
def get_resource_path_relative_to_current_page(config, current_page, resource_path_from_site_root)
48+
path_segments = resource_path_from_site_root.split("/").reject(&:empty?)[0..-2]
49+
path_file_name = resource_path_from_site_root.split("/")[-1]
50+
51+
path_to_site_root = path_to_site_root config, current_page
52+
path_to_site_root + path_segments.push(path_file_name).join("/")
53+
end
54+
55+
def external_url?(url)
56+
uri = URI.parse(url)
57+
uri.scheme || uri.to_s.split("/")[0]&.include?(".")
58+
end
2959
end
3060
end

lib/source/layouts/_footer.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<ul class="govuk-footer__inline-list">
77
<% config[:tech_docs][:footer_links].each do |title, path| %>
88
<li class="govuk-footer__inline-list-item">
9-
<a class="govuk-footer__link" href="<%= url_for path %>"><%= title %></a>
9+
<a class="govuk-footer__link" href="<%= get_path_to_resource config, path, current_page %>"><%= title %></a>
1010
</li>
1111
<% end %>
1212
</ul>

lib/source/layouts/_header.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="govuk-header__container govuk-header__container--full-width">
33
<div class="govuk-header__logo">
44
<% if config[:tech_docs][:service_link] %>
5-
<a href="<%= url_for config[:tech_docs][:service_link] %>" class="govuk-header__link govuk-header__link--homepage">
5+
<a href="<%= get_path_to_resource config, config[:tech_docs][:service_link], current_page %>" class="govuk-header__link govuk-header__link--homepage">
66
<% else %>
77
<span class="govuk-header__link govuk-header__link--homepage">
88
<% end %>
@@ -46,7 +46,7 @@
4646
<ul id="navigation" class="govuk-header__navigation-list">
4747
<% config[:tech_docs][:header_links].each do |title, path| %>
4848
<li class="govuk-header__navigation-item<% if active_page(path) %> govuk-header__navigation-item--active<% end %>">
49-
<a class="govuk-header__link" href="<%= url_for path %>"><%= title %></a>
49+
<a class="govuk-header__link" href="<%= get_path_to_resource config, path, current_page %>"><%= title %></a>
5050
</li>
5151
<% end %>
5252
</ul>

spec/govuk_tech_docs/doubles.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def create_resource_double(**kwargs)
2+
resource = instance_double(Middleman::Sitemap::Resource, **kwargs)
3+
allow(resource).to receive(:is_a?).with(Middleman::Sitemap::Resource).and_return(true)
4+
resource
5+
end

spec/govuk_tech_docs/pages_spec.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
require "govuk_tech_docs/doubles"
2+
13
RSpec.describe GovukTechDocs::Pages do
24
describe "#to_json" do
35
it "returns the pages as JSON when using absolute links" do
46
current_page = double(path: "/api/pages.json")
57
sitemap = double(resources: [
6-
double(url: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
7-
double(url: "/b.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
8+
create_resource_double(url: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
9+
create_resource_double(url: "/b.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
810
])
911

1012
json = described_class.new(sitemap, {}, current_page).to_json
@@ -18,8 +20,8 @@
1820
it "returns the pages as JSON when using relative links" do
1921
current_page = double(path: "/api/pages.json")
2022
sitemap = double(resources: [
21-
double(url: "/a.html", path: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
22-
double(url: "/b/c.html", path: "/b/c.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
23+
create_resource_double(url: "/a.html", path: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
24+
create_resource_double(url: "/b/c.html", path: "/b/c.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
2325
])
2426

2527
json = described_class.new(sitemap, { relative_links: true }, current_page).to_json

spec/govuk_tech_docs/path_helpers_spec.rb

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,123 @@
1+
require "govuk_tech_docs/doubles"
2+
13
RSpec.describe GovukTechDocs::PathHelpers do
24
include GovukTechDocs::PathHelpers
35

46
describe "#get_path_to_resource" do
5-
it "calculates the path to a resource when using absolute links" do
7+
context "calculate paths for internal resources" do
68
resource_url = "/documentation/introduction/index.html"
79

8-
config = {}
9-
resource = double("resource", url: resource_url)
10+
it "when the site is configured to generate absolute links" do
11+
config = {}
12+
13+
resource = create_resource_double(url: resource_url)
14+
resource_path = get_path_to_resource(config, resource, nil)
15+
16+
expect(resource_path).to eql(resource_url)
17+
end
18+
19+
it "when the site is configured to generate relative links" do
20+
config = {
21+
relative_links: true,
22+
}
1023

11-
resource_path = get_path_to_resource(config, resource, nil)
12-
expect(resource_path).to eql(resource_url)
24+
current_page_path = "/documentation/introduction/section-one/index.html"
25+
current_page = double("current_page", path: current_page_path)
26+
resource = create_resource_double(url: resource_url, path: resource_url)
27+
resource_path = get_path_to_resource(config, resource, current_page)
28+
29+
expect(resource_path).to eql("../../../documentation/introduction/index.html")
30+
end
1331
end
1432

15-
it "calculates the path to a resource when using relative links" do
33+
context "calculate paths for internal page paths (which are represented as strings)" do
1634
current_page_path = "/documentation/introduction/section-one/index.html"
17-
url = "/documentation/introduction/index.html"
1835

19-
config = {
20-
relative_links: true,
21-
}
22-
resource = double("resource", url: url, path: url)
23-
current_page = double("current_page", path: current_page_path)
36+
it "when the site is configured to generate absolute links" do
37+
config = {}
38+
39+
url = "/documentation/introduction/index.html"
40+
current_page = double("current_page", path: current_page_path)
41+
resource_path = get_path_to_resource(config, url, current_page)
42+
43+
expect(resource_path).to eql(url)
44+
end
45+
46+
it "when the site is configured to generate relative links" do
47+
config = {
48+
relative_links: true,
49+
}
50+
51+
url = "/documentation/introduction/index.html"
52+
current_page = double("current_page", path: current_page_path)
53+
resource_path = get_path_to_resource(config, url, current_page)
54+
55+
expect(resource_path).to eql("../../../documentation/introduction/index.html")
56+
end
57+
end
58+
59+
context "calculate URLs for external URLs" do
60+
external_urls = [
61+
"https://www.example.com/some-page.html", # With scheme included.
62+
"www.example.com/some-page.html", # With scheme omitted.
63+
]
64+
external_urls.each do |external_url|
65+
current_page_path = "/documentation/introduction/section-one/index.html"
66+
67+
it "when the site is configured to generate absolute links" do
68+
config = {}
2469

25-
resource_path = get_path_to_resource(config, resource, current_page)
26-
expect(resource_path).to eql("../../../documentation/introduction/index.html")
70+
current_page = double("current_page", path: current_page_path)
71+
resource_path = get_path_to_resource(config, external_url, current_page)
72+
73+
expect(resource_path).to eql(external_url)
74+
end
75+
76+
it "when the site is configured to generate relative links" do
77+
config = {
78+
relative_links: true,
79+
}
80+
81+
current_page = double("current_page", path: current_page_path)
82+
resource_path = get_path_to_resource(config, external_url, current_page)
83+
84+
expect(resource_path).to eql(external_url)
85+
end
86+
end
2787
end
2888
end
2989

3090
describe "#path_to_site_root" do
31-
it "calculates the path from a page to the site root when using absolute links" do
32-
page_path = "/documentation/introduction/index.html"
33-
91+
it "calculates the path from a page to the site root when the site is configured to generate absolute links" do
3492
config = {
3593
http_prefix: "/", # This is Middleman's default setting.
3694
}
3795

96+
page_path = "/documentation/introduction/index.html"
3897
path_to_site_root = path_to_site_root(config, page_path)
98+
3999
expect(path_to_site_root).to eql("/")
40100
end
41101

42102
it "calculates the path from a page to the site root when a middleman http prefix" do
43-
page_path = "/bananas/documentation/introduction/index.html"
44-
45103
config = {
46104
http_prefix: "/bananas",
47105
}
48106

107+
page_path = "/bananas/documentation/introduction/index.html"
49108
path_to_site_root = path_to_site_root(config, page_path)
109+
50110
expect(path_to_site_root).to eql("/bananas/")
51111
end
52112

53-
it "calculates the path from a page to the site root when using relative links" do
54-
page_path = "/documentation/introduction/index.html"
55-
113+
it "calculates the path from a page to the site root when the site is configured to generate relative links" do
56114
config = {
57115
relative_links: true,
58116
}
59117

118+
page_path = "/documentation/introduction/index.html"
60119
path_to_site_root = path_to_site_root(config, page_path)
120+
61121
expect(path_to_site_root).to eql("../../")
62122
end
63123
end

spec/table_of_contents/helpers_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ def render(_layout)
129129
def add_children(children)
130130
@children.concat children
131131
end
132+
133+
def is_a?(klass)
134+
klass.to_s == "Middleman::Sitemap::Resource"
135+
end
132136
end,
133137
)
134138
end

0 commit comments

Comments
 (0)