Skip to content

Commit

Permalink
Merge pull request #3175 from AlchemyCMS/link-blank-noopener
Browse files Browse the repository at this point in the history
Add rel="noopener noreferrer" to external links
  • Loading branch information
tvdeyen authored Feb 6, 2025
2 parents c2949c2 + 1c00620 commit 2c5024c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 4 deletions.
8 changes: 7 additions & 1 deletion app/components/alchemy/ingredients/link_view.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Alchemy
module Ingredients
class LinkView < BaseView
include LinkTarget

attr_reader :link_text

# @param ingredient [Alchemy::Ingredient]
Expand All @@ -12,7 +14,11 @@ def initialize(ingredient, text: nil, html_options: {})
end

def call
link_to(link_text, value, {target: ingredient.link_target.presence}.merge(html_options)).html_safe
target = ingredient.link_target.presence
link_to(link_text, value, {
target: link_target_value(target),
rel: link_rel_value(target)
}.merge(html_options)).html_safe
end
end
end
Expand Down
7 changes: 5 additions & 2 deletions app/components/alchemy/ingredients/picture_view.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module Alchemy
module Ingredients
# Renders a picture ingredient view
class PictureView < BaseView
include LinkTarget

attr_reader :ingredient,
:show_caption,
:disable_link,
Expand Down Expand Up @@ -46,10 +48,11 @@ def call
output = caption ? img_tag + caption : img_tag

if is_linked?
target = ingredient.link_target.presence
output = link_to(output, url_for(ingredient.link), {
title: ingredient.link_title.presence,
target: (ingredient.link_target == "blank") ? "_blank" : nil,
data: {link_target: ingredient.link_target.presence}
rel: link_rel_value(target),
target: link_target_value(target)
})
end

Expand Down
5 changes: 4 additions & 1 deletion app/components/alchemy/ingredients/text_view.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Alchemy
module Ingredients
class TextView < BaseView
include LinkTarget

attr_reader :disable_link

delegate :dom_id, :link, :link_title, :link_target,
Expand All @@ -21,7 +23,8 @@ def call
link_to(value, url_for(link), {
id: dom_id.presence,
title: link_title,
target: link_target
target: link_target_value(link_target),
rel: link_rel_value(link_target)
}.merge(html_options))
end.html_safe
end
Expand Down
18 changes: 18 additions & 0 deletions app/components/concerns/alchemy/ingredients/link_target.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Alchemy
module Ingredients
module LinkTarget
BLANK_VALUE = "_blank"
REL_VALUE = "noopener noreferrer"

def link_rel_value(target)
if link_target_value(target) == BLANK_VALUE
REL_VALUE
end
end

def link_target_value(target)
(target == "blank") ? BLANK_VALUE : target
end
end
end
end
26 changes: 26 additions & 0 deletions spec/components/alchemy/ingredients/picture_view_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,32 @@
expect(page).to have_selector('a[href="/home"] img')
end

context "with link target set to '_blank'" do
before do
ingredient.link_target = "_blank"
end

it "adds rel noopener noreferrer" do
render_view
expect(page).to have_selector(
'a[href="/home"][target="_blank"][rel="noopener noreferrer"] img'
)
end
end

context "with link target set to 'blank'" do
before do
ingredient.link_target = "blank"
end

it "sets target '_blank' and adds rel noopener noreferrer" do
render_view
expect(page).to have_selector(
'a[href="/home"][target="_blank"][rel="noopener noreferrer"] img'
)
end
end

context "but disabled link option" do
before do
options[:disable_link] = true
Expand Down
26 changes: 26 additions & 0 deletions spec/views/alchemy/ingredients/link_view_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,30 @@
expect(rendered).to eq('<a class="foo" href="http://google.com">http://google.com</a>')
end
end

context "with link target set to '_blank'" do
let(:ingredient) do
Alchemy::Ingredients::Link.new(value: "http://google.com", link_target: "_blank")
end

it "adds rel noopener noreferrer" do
render ingredient
expect(rendered).to eq(
'<a target="_blank" rel="noopener noreferrer" href="http://google.com">http://google.com</a>'
)
end
end

context "with link target set to 'blank'" do
let(:ingredient) do
Alchemy::Ingredients::Link.new(value: "http://google.com", link_target: "blank")
end

it "sets target '_blank' and adds rel noopener noreferrer" do
render ingredient
expect(rendered).to eq(
'<a target="_blank" rel="noopener noreferrer" href="http://google.com">http://google.com</a>'
)
end
end
end
18 changes: 18 additions & 0 deletions spec/views/alchemy/ingredients/text_view_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@
expect(rendered).to have_selector('a[title="Foo"][target="_blank"][href="http://google.com"]')
end

context "with link target set to '_blank'" do
it "adds rel noopener noreferrer" do
render ingredient
expect(rendered).to have_selector(
'a[title="Foo"][target="_blank"][href="http://google.com"][rel="noopener noreferrer"]'
)
end
end

context "with link target set to 'blank'" do
it "sets target '_blank' and adds rel noopener noreferrer" do
render ingredient
expect(rendered).to have_selector(
'a[title="Foo"][target="_blank"][href="http://google.com"][rel="noopener noreferrer"]'
)
end
end

context "with html_options given" do
it "renders the linked with these options" do
render ingredient, html_options: {title: "Bar", class: "blue"}
Expand Down

0 comments on commit 2c5024c

Please sign in to comment.