Relativize absolute links to the instance on export

This commit is contained in:
Stanko K.R.
2026-02-24 08:22:01 +01:00
parent 4eadce8ffe
commit 2f9d4533c5
2 changed files with 80 additions and 3 deletions
@@ -20,7 +20,7 @@ class Account::DataTransfer::ActionText::RichTextRecordSet < Account::DataTransf
end
def export_record(rich_text)
data = rich_text.as_json.merge("body" => convert_sgids_to_gids(rich_text.body))
data = rich_text.as_json.merge("body" => transform_body_for_export(rich_text.body))
zip.add_file "data/action_text_rich_texts/#{rich_text.id}.json", data.to_json
end
@@ -54,9 +54,14 @@ class Account::DataTransfer::ActionText::RichTextRecordSet < Account::DataTransf
check_associations_dont_exist(data)
end
def convert_sgids_to_gids(content)
def transform_body_for_export(content)
return nil if content.blank?
html = convert_sgids_to_gids(content)
relativize_urls(html)
end
def convert_sgids_to_gids(content)
content.send(:attachment_nodes).each do |node|
sgid = SignedGlobalID.parse(node["sgid"], for: ::ActionText::Attachable::LOCATOR_NAME)
@@ -75,6 +80,25 @@ class Account::DataTransfer::ActionText::RichTextRecordSet < Account::DataTransf
content.fragment.source.to_html
end
def relativize_urls(html)
host = Rails.application.routes.default_url_options[:host]
return html unless host
fragment = Nokogiri::HTML.fragment(html)
fragment.css("a[href]").each do |link|
uri = URI.parse(link["href"]) rescue nil
if uri.respond_to?(:host) && uri.host == host
link["href"] = uri.path
link["href"] += "?#{uri.query}" if uri.query
link["href"] += "##{uri.fragment}" if uri.fragment
end
end
fragment.to_html
end
def transform_body_for_import(body)
return body if body.blank?
@@ -112,7 +136,7 @@ class Account::DataTransfer::ActionText::RichTextRecordSet < Account::DataTransf
if match
path = match.post_match.presence || "/"
valid_path = Rails.application.routes.recognize_path(path) rescue nil
link["href"] = "#{account.slug}#{remaining_path}" if valid_path
link["href"] = "#{account.slug}#{path}" if valid_path
end
end
@@ -117,4 +117,57 @@ class Account::DataTransfer::ActionText::RichTextRecordSetTest < ActiveSupport::
assert_equal html, result
end
test "relativize_urls strips instance host from absolute URLs" do
with_default_url_host("fizzy.example.com") do
record_set = Account::DataTransfer::ActionText::RichTextRecordSet.new(accounts(:"37s"))
html = %(<p>See <a href="https://fizzy.example.com/123/cards/42">card</a></p>)
result = record_set.send(:relativize_urls, html)
assert_includes result, %(/123/cards/42)
assert_not_includes result, "fizzy.example.com"
end
end
test "relativize_urls preserves query and fragment" do
with_default_url_host("fizzy.example.com") do
record_set = Account::DataTransfer::ActionText::RichTextRecordSet.new(accounts(:"37s"))
html = %(<p><a href="https://fizzy.example.com/123/cards/42?tab=comments#comment_1">link</a></p>)
result = record_set.send(:relativize_urls, html)
assert_includes result, "/123/cards/42?tab=comments#comment_1"
assert_not_includes result, "fizzy.example.com"
end
end
test "relativize_urls leaves external URLs alone" do
with_default_url_host("fizzy.example.com") do
record_set = Account::DataTransfer::ActionText::RichTextRecordSet.new(accounts(:"37s"))
html = %(<p><a href="https://github.com/some/repo">link</a></p>)
result = record_set.send(:relativize_urls, html)
assert_includes result, "https://github.com/some/repo"
end
end
test "relativize_urls is a no-op when host is not configured" do
record_set = Account::DataTransfer::ActionText::RichTextRecordSet.new(accounts(:"37s"))
html = %(<p><a href="https://fizzy.example.com/123/cards/42">link</a></p>)
result = record_set.send(:relativize_urls, html)
assert_includes result, "https://fizzy.example.com/123/cards/42"
end
private
def with_default_url_host(host)
original = Rails.application.routes.default_url_options[:host]
Rails.application.routes.default_url_options[:host] = host
yield
ensure
Rails.application.routes.default_url_options[:host] = original
end
end