Skip to content

Commit fb20c65

Browse files
eYinkalauraghiorghisor-tw
authored andcommitted
Allow offsite links to have many parents
In order to enable standard editions - which are editionable - to be associated with offsite links, we are introducing a join table. This will allow an offsite link to be associated with multiple editions. This should not introduce any behavioural changes to organisations, topical events or world location news.
1 parent 5cfda0d commit fb20c65

21 files changed

Lines changed: 115 additions & 28 deletions

app/controllers/admin/offsite_links_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def new
77
end
88

99
def create
10-
@offsite_link = OffsiteLink.new(offsite_link_params.merge(parent: @parent))
10+
@offsite_link = @parent.offsite_links.create(offsite_link_params)
1111

1212
if @offsite_link.save
1313
flash[:notice] = "An offsite link has been created for #{@parent.name}"

app/models/offsite_link.rb

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,31 @@ def self.display_type(link_type)
5656

5757
date_attributes(:date)
5858

59-
belongs_to :parent, polymorphic: true
59+
has_many :offsite_link_parents
60+
has_many :organisations, through: :offsite_link_parents, source: :parent, source_type: "Organisation"
61+
has_many :topical_events, through: :offsite_link_parents, source: :parent, source_type: "TopicalEvent"
62+
has_many :world_location_news, through: :offsite_link_parents, source: :parent, source_type: "WorldLocationNews"
63+
6064
has_many :features, inverse_of: :offsite_link, dependent: :destroy
6165

62-
after_commit :republish_parent_to_publishing_api
66+
after_commit :republish_parents_to_publishing_api
6367

6468
validates :title, :summary, :link_type, :url, presence: true, length: { maximum: 255 }
6569
validate :check_url_is_allowed
6670
validates :link_type, presence: true, inclusion: { in: LinkTypes.all }
6771

72+
# We only expect one type of parent per offsite link (either organisations, or topical events, or world location news).
73+
# Once topical events become editionable, the parents will return all editions associated with the offsite link.
74+
def parents
75+
organisations + topical_events + world_location_news
76+
end
77+
78+
# For non-editionable parents, we are returning the last - and only - parent.
79+
# Once topical events become editionable, this will return the last associated edition.
80+
def parent
81+
parents.last
82+
end
83+
6884
def check_url_is_allowed
6985
if (uri = Addressable::URI.parse(url))
7086
host = uri.host
@@ -111,7 +127,7 @@ def url_is_permitted?(host)
111127
PERMITTED_HOSTS.any? { |permitted_host| host =~ /(?:^|\.)#{permitted_host}$/ }
112128
end
113129

114-
def republish_parent_to_publishing_api
130+
def republish_parents_to_publishing_api
115131
Whitehall::PublishingApi.republish_async(parent)
116132
end
117133
end

app/models/offsite_link_parent.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class OffsiteLinkParent < ApplicationRecord
2+
belongs_to :parent, polymorphic: true
3+
belongs_to :offsite_link
4+
end

app/models/organisation.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ class Organisation < ApplicationRecord
109109
has_and_belongs_to_many :superseding_organisations, class_name: "Organisation", foreign_key: :superseded_organisation_id, join_table: :organisation_supersedings, association_foreign_key: :superseding_organisation_id
110110
has_and_belongs_to_many :superseded_organisations, class_name: "Organisation", foreign_key: :superseding_organisation_id, join_table: :organisation_supersedings, association_foreign_key: :superseded_organisation_id
111111

112-
has_many :offsite_links, as: :parent
112+
has_many :offsite_link_parents, as: :parent
113+
has_many :offsite_links, through: :offsite_link_parents
113114

114115
def featured_documents_display_limit
115116
return 7 if slug == "prime-ministers-office-10-downing-street"

app/models/topical_event.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class TopicalEvent < ApplicationRecord
2929

3030
MAX_FEATURED_DOCUMENTS = 6
3131
has_many :features, inverse_of: :topical_event, dependent: :destroy
32-
has_many :offsite_links, as: :parent
32+
has_many :offsite_link_parents, as: :parent
33+
has_many :offsite_links, through: :offsite_link_parents
3334
has_many :social_media_accounts, as: :socialable, dependent: :destroy
3435

3536
has_many :topical_event_organisations, -> { extending UserOrderableExtension }

app/models/world_location_news.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ class WorldLocationNews < ApplicationRecord
1616
delegate :slug, :slug=, to: :world_location
1717

1818
has_many :featured_links, -> { order(:created_at) }, as: :linkable, dependent: :destroy
19+
has_many :offsite_link_parents, as: :parent
20+
has_many :offsite_links, through: :offsite_link_parents
1921
accepts_nested_attributes_for :featured_links, reject_if: ->(attributes) { attributes["url"].blank? }, allow_destroy: true
20-
has_many :offsite_links, as: :parent
2122
accepts_nested_attributes_for :offsite_links
2223

2324
translates :title, :mission_statement
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class AddOffsiteLinkParents < ActiveRecord::Migration[8.0]
2+
def change
3+
create_table :offsite_link_parents do |t|
4+
t.references :offsite_link, null: false
5+
t.references :parent, polymorphic: true, null: false
6+
t.timestamps
7+
end
8+
9+
add_index :offsite_link_parents,
10+
[:parent_type, :parent_id, :offsite_link_id],
11+
unique: true
12+
end
13+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class AddDataMigrationForOffsiteLinkParents < ActiveRecord::Migration[8.0]
2+
def up
3+
safety_assured do
4+
execute <<-SQL
5+
INSERT INTO offsite_link_parents (offsite_link_id, parent_id, parent_type, created_at, updated_at)
6+
SELECT id, parent_id, parent_type, NOW(), NOW()
7+
FROM offsite_links AS ol
8+
WHERE NOT EXISTS (
9+
SELECT 1
10+
FROM offsite_link_parents AS olp
11+
WHERE olp.offsite_link_id = ol.id
12+
);
13+
SQL
14+
end
15+
end
16+
end

db/schema.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2026_01_07_195134) do
13+
ActiveRecord::Schema[8.0].define(version: 2026_01_21_122256) do
1414
create_table "assets", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
1515
t.string "asset_manager_id", null: false
1616
t.string "variant", null: false
@@ -653,6 +653,17 @@
653653
t.index ["nation_id"], name: "index_nation_inapplicabilities_on_nation_id"
654654
end
655655

656+
create_table "offsite_link_parents", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
657+
t.bigint "offsite_link_id", null: false
658+
t.string "parent_type", null: false
659+
t.bigint "parent_id", null: false
660+
t.datetime "created_at", null: false
661+
t.datetime "updated_at", null: false
662+
t.index ["offsite_link_id"], name: "index_offsite_link_parents_on_offsite_link_id"
663+
t.index ["parent_type", "parent_id", "offsite_link_id"], name: "idx_on_parent_type_parent_id_offsite_link_id_c4a8862347", unique: true
664+
t.index ["parent_type", "parent_id"], name: "index_offsite_link_parents_on_parent"
665+
end
666+
656667
create_table "offsite_links", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
657668
t.string "title"
658669
t.string "summary"

features/step_definitions/topical_event_featurings_steps.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Given(/^the topical event has an offsite link with the title "([^"]*)"$/) do |title|
2-
create(:offsite_link, parent_type: "TopicalEvent", parent: @topical_event, title:)
2+
create(:offsite_link, topical_events: [@topical_event], title:)
33
end
44

55
When(/^I visit the topical event featuring index page$/) do
@@ -13,8 +13,8 @@
1313

1414
And(/^two featurings exist for "([^"]*)"$/) do |name|
1515
topical_event = TopicalEvent.find_by(name:)
16-
offsite_link1 = create(:offsite_link, parent_type: "TopicalEvent", parent: topical_event, title: "Featured link 1")
17-
offsite_link2 = create(:offsite_link, parent_type: "TopicalEvent", parent: topical_event, title: "Featured link 2")
16+
offsite_link1 = create(:offsite_link, topical_events: [topical_event], title: "Featured link 1")
17+
offsite_link2 = create(:offsite_link, topical_events: [topical_event], title: "Featured link 2")
1818
create(:offsite_topical_event_featuring, topical_event:, offsite_link: offsite_link1)
1919
create(:offsite_topical_event_featuring, topical_event:, offsite_link: offsite_link2)
2020
end

0 commit comments

Comments
 (0)