From bb6a5100e5901ad6dd459b6f70afe556ee466dfe Mon Sep 17 00:00:00 2001 From: Bruce Bolt Date: Tue, 23 Apr 2024 08:46:31 +0100 Subject: [PATCH] WIP: Migrate non-editionable worldwide organisations to be editionable Note: we cannot use a data migration as they do not have access to presenters and helpers that are needed to perform the consistency check before commiting the transaction. --- config/environments/development.rb | 2 +- ...ate_worldwide_organisations_to_editions.rb | 417 ++++++++++++++++++ db/schema.rb | 139 +++--- 3 files changed, 493 insertions(+), 65 deletions(-) create mode 100644 db/migrate/20240422104433_migrate_worldwide_organisations_to_editions.rb diff --git a/config/environments/development.rb b/config/environments/development.rb index d1a6aa026adf..8f22c5f180d1 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -65,7 +65,7 @@ config.i18n.raise_on_missing_translations = true # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load + # config.active_record.migration_error = :page_load # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. diff --git a/db/migrate/20240422104433_migrate_worldwide_organisations_to_editions.rb b/db/migrate/20240422104433_migrate_worldwide_organisations_to_editions.rb new file mode 100644 index 000000000000..94356679cf8d --- /dev/null +++ b/db/migrate/20240422104433_migrate_worldwide_organisations_to_editions.rb @@ -0,0 +1,417 @@ +# rubocop:disable Rails/Output + +class MigrateWorldwideOrganisationsToEditions < ActiveRecord::Migration[7.1] + def up + actor = User.find_by(email: "bruce.bolt@digital.cabinet-office.gov.uk") + + ## Disable version history callbacks, as we don't want an audit trail item for the create/updates in this migration + Edition.skip_callback(:create, :after, :record_create) + Edition.skip_callback(:update, :before, :record_update) + + ## Disable Publishing API callbacks, as we don't want anything published on update until the migration has been completed + Contact.skip_callback(:commit, :after, :publish_to_publishing_api) + Contact.skip_callback(:commit, :after, :republish_embassies_index_page_to_publishing_api) + Contact.skip_callback(:commit, :after, :republish_worldwide_office_to_publishing_api) + WorldwideOffice.skip_callback(:commit, :after, :publish_to_publishing_api) + WorldwideOffice.skip_callback(:commit, :after, :republish_embassies_index_page_to_publishing_api) + WorldwideOrganisation.skip_callback(:commit, :after, :publish_to_publishing_api) + WorldwideOrganisation.skip_callback(:commit, :after, :republish_embassies_index_page_to_publishing_api) + WorldwideOrganisation.skip_callback(:commit, :after, :republish_worldwide_offices) + WorldwideOrganisationPage.skip_callback(:commit, :after, :republish_worldwide_organisation_draft) + + ## Disable Whitehall's attempts at changing the slug, e.g. dropping `the-` from the beginning + Edition.skip_callback(:validation, :before, :update_document_slug) + + ## TODO: remove after dev work complete + total = WorldwideOrganisation.all.count + puts "Total organisations to migrate #{total}" + completed = 0 + failed = 0 + + transaction = ActiveRecord::Base.transaction do + WorldwideOrganisation.find_each do |worldwide_organisation| + # WorldwideOrganisation.where(slug: "british-embassy-kuwait").each do |worldwide_organisation| + next if worldwide_organisation.slug == "british-embassy-kyiv" + next if worldwide_organisation.slug == "british-embassy-madrid" + next if worldwide_organisation.slug == "british-high-commission-dhaka" + next if worldwide_organisation.slug == "british-high-commission-trinidad-and-tobago" + ## Output some information which will help with debugging + puts "Worldwide Organisation: #{worldwide_organisation.slug}" + + ## Worldwide Organisation Document + editionable_worldwide_organisation_document = Document.create!( + content_id: worldwide_organisation.content_id, + created_at: worldwide_organisation.created_at, + document_type: "EditionableWorldwideOrganisation", + slug: worldwide_organisation.slug, + ) + + about_us_page = worldwide_organisation.corporate_information_pages.find_by(state: "published", corporate_information_page_type_id: CorporateInformationPageType::AboutUs.id) + + updated_body = if about_us_page + convert_legacy_attachment_syntax(worldwide_organisation.body, about_us_page.attachments) + else + body ## TODO: uk-science-innovation-network-in-latvia is failing on this line + end + + updated_summary = if about_us_page + convert_legacy_attachment_syntax(worldwide_organisation.summary, about_us_page.attachments) + else + summary + end + + ## Worldwide Organisation Edition + editionable_worldwide_organisation_published_edition = EditionableWorldwideOrganisation.create!( + analytics_identifier: worldwide_organisation.analytics_identifier, + body: updated_body || "", ## TODO: skip the validation that disallows an empty body + created_at: worldwide_organisation.created_at, + creator: actor, + document: editionable_worldwide_organisation_document, + lead_organisations: worldwide_organisation.sponsoring_organisations, + locale: "en", + logo_formatted_name: worldwide_organisation.logo_formatted_name, + previously_published: false, + roles: worldwide_organisation.roles, + summary: updated_summary, + title: worldwide_organisation.name, + world_locations: worldwide_organisation.world_locations, ## TODO: need to suppress this validation as some orgs don't have them, e.g. british-embassy-liechtenstein and british-consulate-general-alexandria + ) + + ## About us page attachments + about_us_page.attachments.map do |attachment| + editionable_worldwide_organisation_published_edition.attachments.create!(attachment.attributes.except("id", "attachable_id", "attachable_type")) + end + + ## Translations + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + I18n.with_locale(locale) do + editionable_worldwide_organisation_published_edition.update( + body: worldwide_organisation.body, + summary: worldwide_organisation.summary, + title: worldwide_organisation.name, + ) + end + end + + ## Default news image + if worldwide_organisation.default_news_image.present? + editionable_worldwide_organisation_published_edition.build_default_news_image(worldwide_organisation.default_news_image.attributes.except("id", "featured_imageable_id", "featured_imageable_type")) + + worldwide_organisation.default_news_image.assets.each do |asset| + editionable_worldwide_organisation_published_edition.default_news_image.assets << asset.dup + end + end + ## TODO: need a test for the news image as it isn't included in the presenter + + ## Social Media Links + worldwide_organisation.social_media_accounts.each do |social_media_account| + new_social_media_account = editionable_worldwide_organisation_published_edition.social_media_accounts.create!(social_media_account.attributes.except("id", "socialable_id", "socialable_type")) + + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + I18n.with_locale(locale) do + new_social_media_account.update!(social_media_account.attributes.except("id", "socialable_id", "socialable_type")) + end + end + end + + ## Offices + home_page_offices = [worldwide_organisation.main_office, worldwide_organisation.home_page_offices].compact.flatten + other_offices = worldwide_organisation.offices - home_page_offices + all_offices_in_order = home_page_offices + other_offices + + all_offices_in_order.each do |office| + new_office = editionable_worldwide_organisation_published_edition.offices.new(office.attributes.except("id", "worldwide_organisation_id")) + new_office.contact = Contact.create(office.contact.attributes.except("id", "contactable_id", "contactable_type")) + new_office.save! + + office.services.each do |service| + new_office.services.create(service.attributes.except("id", "worldwide_office_id")) + end + + if worldwide_organisation.office_shown_on_home_page?(office) + editionable_worldwide_organisation_published_edition.add_office_to_home_page!(new_office) + end + + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + next unless office.contact.translations.pluck(:locale).include?(locale.to_s) + + I18n.with_locale(locale) do + new_office.update!(office.attributes.except("id", "edition_id", "worldwide_organisation_id")) + new_office.contact.attributes = office.contact.attributes.except("id", "contactable_id", "contactable_type") + new_office.contact.save!(validate: false) ## Some offices appear to have no country in their translation (perhaps they were added before the validation was implemented), so don't do this validation now + end + end + end + + ## History (from the history tab) + worldwide_organisation.versions.each do |version| + editionable_worldwide_organisation_published_edition.versions.create(version.attributes.except("id", "item_id", "item_type", "state").merge(state: "draft")) + end + + ## Published Corporate Information Pages + published_corporate_information_pages = worldwide_organisation.corporate_information_pages.where(state: "published") + published_corporate_information_pages.find_each do |cip| + ## About Us pages have already been migrated into the main EditionableWorldwideOrganisation edition's body and summary + next if cip.corporate_information_page_type_id == CorporateInformationPageType::AboutUs.id + + ## Replace legacy attachment markdown from the fields that accept govspeak + updated_body = convert_legacy_attachment_syntax(cip.body, cip.attachments) + updated_summary = convert_legacy_attachment_syntax(cip.summary, cip.attachments) + + new_page = editionable_worldwide_organisation_published_edition.pages.create!( + body: updated_body, + content_id: cip.content_id, + corporate_information_page_type: cip.corporate_information_page_type, + created_at: cip.created_at, + edition: editionable_worldwide_organisation_published_edition, + summary: updated_summary, + ) + + cip.attachments.map do |attachment| + new_page.attachments.create!(attachment.attributes.except("id", "attachable_id", "attachable_type")) + end + + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + next unless cip.translations.pluck(:locale).include?(locale.to_s) + + I18n.with_locale(locale) do + new_page.update!( + body: cip.body, + summary: cip.summary, + ) + end + end + + ## Backdating the updated_at timestamp needs to be done last, as updating the translations means the date changes + new_page.update!( + updated_at: cip.public_timestamp || cip.updated_at, + ) + end + + ## Reassociate editions (e.g. world news stories) with the new organisation + EditionWorldwideOrganisation.where(worldwide_organisation:).find_each do |edition_worldwide_organisation| + EditionEditionableWorldwideOrganisation.create( + edition: edition_worldwide_organisation.edition, + document: editionable_worldwide_organisation_document, + ) + end + ## TODO: how to test this -- could possibly check presenters for every document associated with an org; or maybe check the count of editions associated?? + + ## Updating the updated at time needs to be the very last thing, else the `update` calls above will change it + ## We also cannot update a published edition, so need to set this at the very end + editionable_worldwide_organisation_published_edition.update!( + major_change_published_at: worldwide_organisation.updated_at, + state: "published", + updated_at: worldwide_organisation.updated_at, + ) + + possible_updated_at_dates = [editionable_worldwide_organisation_published_edition.updated_at, about_us_page.updated_at] + published_corporate_information_pages.pluck(:updated_at) + + ## Draft Corporate Information Pages (CIPs) + ## TODO: ignore unpublished CIPs which have a new draft created on unpublishing, e.g. https://whitehall-admin.integration.publishing.service.gov.uk/government/admin/worldwide_organisations/british-high-commission-trinidad-and-tobago/corporate_information_pages/687437 + if draft_cips = worldwide_organisation.corporate_information_pages.where(state: Edition::PRE_PUBLICATION_STATES) + puts "HAS DRAFT CIPS" + + editionable_worldwide_organisation_draft_edition = editionable_worldwide_organisation_published_edition.create_draft(actor) + + ## Draft about us CIP + if draft_about_us_page = draft_cips.find_by(corporate_information_page_type_id: CorporateInformationPageType::AboutUs.id) + updated_body = convert_legacy_attachment_syntax(draft_about_us_page.body, draft_about_us_page.attachments) + updated_summary = convert_legacy_attachment_syntax(draft_about_us_page.summary, draft_about_us_page.attachments) + + ## Update the draft edition + editionable_worldwide_organisation_draft_edition.update!( + body: updated_body, + minor_change: true, + summary: updated_summary, + ) + + ## Attachments + draft_about_us_page.attachments.map do |attachment| + new_attachment = editionable_worldwide_organisation_draft_edition.attachments.find_or_create_by!(attachment_data_id: attachment.attachment_data_id) + new_attachment.update!(attachment.attributes.except("id", "attachable_id", "attachable_type")) + end + + ## Translations + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + I18n.with_locale(locale) do + editionable_worldwide_organisation_draft_edition.update( + body: draft_about_us_page.body, + summary: draft_about_us_page.summary, + ) + end + end + + possible_updated_at_dates << draft_about_us_page.updated_at + end + + ## Draft non-about us CIPs + if other_draft_pages = draft_cips.where.not(corporate_information_page_type_id: CorporateInformationPageType::AboutUs.id) + other_draft_pages.each do |draft_page| + updated_body = convert_legacy_attachment_syntax(draft_page.body, draft_page.attachments) + updated_summary = convert_legacy_attachment_syntax(draft_page.summary, draft_page.attachments) + + ## Update the existing page or create a new one + new_page = editionable_worldwide_organisation_draft_edition.pages.find_or_initialize_by(corporate_information_page_type_id: draft_page.corporate_information_page_type_id) + new_page.update!( + body: updated_body, + content_id: draft_page.content_id, + corporate_information_page_type: draft_page.corporate_information_page_type, + created_at: draft_page.created_at, + summary: updated_summary, + ) + new_page.save! + + ## Attachments + draft_page.attachments.map do |attachment| + new_attachment = new_page.attachments.find_or_initialize_by(attachment_data_id: attachment.attachment_data_id) + new_attachment.update!(attachment.attributes.except("id", "attachable_id", "attachable_type")) + new_attachment.save! + end + + ## Translations + worldwide_organisation.non_english_translated_locales.map(&:code).each do |locale| + I18n.with_locale(locale) do + new_page.update( + body: draft_page.body, + summary: draft_page.summary, + ) + end + end + + ## Backdating the updated_at timestamp needs to be done last, as updating the translations means the date changes + new_page.update!( + updated_at: draft_page.public_timestamp || draft_page.updated_at, + ) + + possible_updated_at_dates << draft_page.updated_at + end + end + + ## Override the draft edition's timestamps to reflect the draft CIPs we have used to create it + editionable_worldwide_organisation_draft_edition.update!( + created_at: draft_cips.pluck(:created_at).min, + minor_change: true, ## This allows us to save the record without a change note; the user can enter one when they publish this draft + updated_at: possible_updated_at_dates.max, + ) + else + puts "NO DRAFT CIPS FOR THIS ORGANISATION" + end + + ## Abort if anything in the presenter doesn't match + worldwide_organisation.translated_locales.each do |locale| + check_results = [] + + I18n.with_locale(locale) do + presented_worldwide_organisation = PublishingApi::WorldwideOrganisationPresenter.new(worldwide_organisation, update_type: "minor") + presented_editionable_worldwide_organisation = PublishingApi::EditionableWorldwideOrganisationPresenter.new(editionable_worldwide_organisation_published_edition, update_type: "minor") + + check_results << integrity_check(presented_worldwide_organisation, presented_editionable_worldwide_organisation, "Published Worldwide Organisation for #{locale}") + + worldwide_organisation.offices.each do |office| + presented_worldwide_office = PublishingApi::WorldwideOfficePresenter.new(office, update_type: "minor") + editionable_office = editionable_worldwide_organisation_published_edition.offices.find_by(slug: office.slug) + presented_editionable_worldwide_office = PublishingApi::WorldwideOfficePresenter.new(editionable_office, update_type: "minor") + + check_results << integrity_check(presented_worldwide_office, presented_editionable_worldwide_office, "Worldwide Office for #{office.slug} and #{locale}") + + ## TODO: are these checks also needed for contacts?? + end + + worldwide_organisation.corporate_information_pages.where(state: "published").find_each do |cip| + next if cip.corporate_information_page_type_id == CorporateInformationPageType::AboutUs.id + + presented_cip = PublishingApi::WorldwideCorporateInformationPagePresenter.new(cip, update_type: "minor") + editionable_page = editionable_worldwide_organisation_published_edition.pages.find_by(corporate_information_page_type_id: cip.corporate_information_page_type_id) + presented_page = PublishingApi::WorldwideOrganisationPagePresenter.new(editionable_page, update_type: "minor") + + check_results << integrity_check(presented_cip, presented_page, "Published Worldwide Organisation Page for #{cip.slug} and #{locale}") + end + + if editionable_worldwide_organisation_draft_edition + presented_worldwide_organisation = PublishingApi::WorldwideOrganisationPresenter.new(worldwide_organisation, state: "draft", update_type: "minor") + presented_editionable_worldwide_organisation = PublishingApi::EditionableWorldwideOrganisationPresenter.new(editionable_worldwide_organisation_draft_edition, update_type: "minor") + + check_results << integrity_check(presented_worldwide_organisation, presented_editionable_worldwide_organisation, "Draft Worldwide Organisation for #{locale}") + + worldwide_organisation.corporate_information_pages.where(state: "draft").find_each do |cip| + next if cip.corporate_information_page_type_id == CorporateInformationPageType::AboutUs.id + + presented_cip = PublishingApi::WorldwideCorporateInformationPagePresenter.new(cip, update_type: "minor") + editionable_page = editionable_worldwide_organisation_draft_edition.pages.find_by(corporate_information_page_type_id: cip.corporate_information_page_type_id) + presented_page = PublishingApi::WorldwideOrganisationPagePresenter.new(editionable_page, update_type: "minor") + + check_results << integrity_check(presented_cip, presented_page, "Draft Worldwide Organisation Page for #{cip.slug} and #{locale}") + end + end + end + + if check_results.include?(false) + failed = failed + 1 + else + completed = completed + 1 + end + puts "Completed #{completed} of #{total}" + puts "Failed #{failed} of #{total}" + + ## Raise and rollback the transaction if the integrity check fails + # raise ActiveRecord::Rollback unless check_passed + end + + ## TODO: Delete the non-editionable worldwide organisation and all it's associations + end + + ## Rolling back anyway, as we're not wanting to migrate whilst doing development + raise ActiveRecord::Rollback + end + + raise StandardError unless transaction + end + + def integrity_check(old_presenter, new_presenter, description) + if old_presenter.content_id == new_presenter.content_id && + old_presenter.content.merge == new_presenter.content && + old_presenter.links == new_presenter.links + puts "INTEGRITY CHECK - PASS FOR #{description}" + + true + else + puts "INTEGRITY CHECK - FAIL FOR #{description}" + + puts "Expected Content ID: #{old_presenter.content_id}" + puts "Actual Content ID: #{new_presenter.content_id}" + + puts "Expected Content:" + puts old_presenter.content.sort.to_h.inspect + puts "Actual Content:" + puts new_presenter.content.sort.to_h.inspect + + puts "Expected Links:" + puts old_presenter.links.sort.to_h.inspect + puts "Actual Links:" + puts new_presenter.links.sort.to_h.inspect + + false + end + end + + def convert_legacy_attachment_syntax(govspeak, attachments = []) + return govspeak if govspeak.blank? + + govspeak = govspeak.gsub(/\n{0,2}^!@([0-9]+)\s*/) do + if (attachment = attachments[Regexp.last_match(1).to_i - 1]) + "\n\n[Attachment: #{attachment.filename}]\n\n" + else + "\n\n" + end + end + + govspeak.gsub(/\[InlineAttachment:([0-9]+)\]/) do + if (attachment = attachments[Regexp.last_match(1).to_i - 1]) + "[AttachmentLink: #{attachment.filename}]" + else + "" + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f910258f3a5a..6f73ad520827 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema[7.1].define(version: 2024_04_22_091946) do - create_table "assets", charset: "utf8mb3", force: :cascade do |t| + create_table "assets", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "asset_manager_id", null: false t.string "variant", null: false t.datetime "created_at", null: false @@ -40,7 +40,7 @@ t.index ["attachment_id"], name: "index_attachment_sources_on_attachment_id" end - create_table "attachments", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "attachments", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "title" @@ -95,7 +95,7 @@ t.integer "call_for_evidence_response_form_data_id" end - create_table "call_for_evidence_responses", charset: "utf8mb3", force: :cascade do |t| + create_table "call_for_evidence_responses", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "edition_id" t.text "summary" t.datetime "created_at", precision: nil @@ -142,7 +142,7 @@ t.index ["edition_id"], name: "index_consultation_responses_on_edition_id" end - create_table "contact_number_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "contact_number_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "contact_number_id" t.string "locale" t.datetime "created_at", precision: nil, null: false @@ -153,7 +153,7 @@ t.index ["locale"], name: "index_contact_number_translations_on_locale" end - create_table "contact_numbers", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "contact_numbers", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "contact_id" t.string "label" t.string "number" @@ -162,7 +162,7 @@ t.index ["contact_id"], name: "index_contact_numbers_on_contact_id" end - create_table "contact_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "contact_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "contact_id" t.string "locale" t.datetime "created_at", precision: nil, null: false @@ -179,7 +179,7 @@ t.index ["locale"], name: "index_contact_translations_on_locale" end - create_table "contacts", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "contacts", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.decimal "latitude", precision: 15, scale: 10 t.decimal "longitude", precision: 15, scale: 10 t.integer "contactable_id" @@ -197,7 +197,7 @@ t.index ["version"], name: "index_data_migration_records_on_version", unique: true end - create_table "document_collection_group_memberships", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "document_collection_group_memberships", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "document_id" t.integer "document_collection_group_id" t.integer "ordering" @@ -209,7 +209,7 @@ t.index ["non_whitehall_link_id"], name: "index_document_collection_non_whitehall_link" end - create_table "document_collection_groups", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "document_collection_groups", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "document_collection_id" t.string "heading" t.text "body" @@ -228,7 +228,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "documents", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "documents", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "slug" @@ -259,7 +259,7 @@ t.index ["dependable_id", "dependable_type", "edition_id"], name: "index_edition_dependencies_on_dependable_and_edition", unique: true end - create_table "edition_editionable_worldwide_organisations", charset: "utf8mb3", force: :cascade do |t| + create_table "edition_editionable_worldwide_organisations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "edition_id" t.integer "document_id" t.datetime "created_at", null: false @@ -268,7 +268,7 @@ t.index ["edition_id"], name: "index_edition_editionable_worldwide_organisations_on_edition_id" end - create_table "edition_lead_images", charset: "utf8mb3", force: :cascade do |t| + create_table "edition_lead_images", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "edition_id" t.integer "image_id" t.datetime "created_at", null: false @@ -276,7 +276,7 @@ t.index ["edition_id"], name: "index_lead_image_on_edition_id", unique: true end - create_table "edition_organisations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "edition_organisations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.integer "organisation_id" t.datetime "created_at", precision: nil @@ -296,7 +296,7 @@ t.index ["policy_content_id"], name: "index_edition_policies_on_policy_content_id" end - create_table "edition_relations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "edition_relations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id", null: false t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -312,7 +312,7 @@ t.index ["role_appointment_id"], name: "index_edition_role_appointments_on_role_appointment_id" end - create_table "edition_roles", id: false, charset: "utf8mb3", force: :cascade do |t| + create_table "edition_roles", id: false, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "edition_id", null: false t.bigint "role_id", null: false t.datetime "created_at", null: false @@ -326,7 +326,7 @@ t.index ["edition_id"], name: "index_edition_statistical_data_sets_on_edition_id" end - create_table "edition_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "edition_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.string "locale" t.string "title" @@ -338,7 +338,7 @@ t.index ["locale"], name: "index_edition_translations_on_locale" end - create_table "edition_world_locations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "edition_world_locations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.integer "world_location_id" t.datetime "created_at", precision: nil @@ -348,7 +348,7 @@ t.index ["world_location_id"], name: "index_edition_world_locations_on_world_location_id" end - create_table "edition_worldwide_organisations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "edition_worldwide_organisations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.integer "worldwide_organisation_id" t.datetime "created_at", precision: nil @@ -357,7 +357,7 @@ t.index ["worldwide_organisation_id"], name: "index_edition_worldwide_orgs_on_worldwide_organisation_id" end - create_table "editions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "editions", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.integer "lock_version", default: 0 @@ -425,7 +425,7 @@ t.index ["type"], name: "index_editions_on_type" end - create_table "editorial_remarks", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "editorial_remarks", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.text "body" t.integer "edition_id" t.integer "author_id" @@ -435,7 +435,7 @@ t.index ["edition_id"], name: "index_editorial_remarks_on_edition_id" end - create_table "fact_check_requests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "fact_check_requests", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.string "key" t.datetime "created_at", precision: nil @@ -454,7 +454,7 @@ t.text "personal_details" end - create_table "feature_lists", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "feature_lists", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "featurable_id" t.string "featurable_type" t.string "locale" @@ -463,7 +463,7 @@ t.index ["featurable_id", "featurable_type", "locale"], name: "featurable_lists_unique_locale_per_featurable", unique: true end - create_table "featured_image_data", charset: "utf8mb3", force: :cascade do |t| + create_table "featured_image_data", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "carrierwave_image" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -489,7 +489,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "features", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "features", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "document_id" t.integer "feature_list_id" t.string "alt_text" @@ -505,7 +505,7 @@ t.index ["ordering"], name: "index_features_on_ordering" end - create_table "flipflop_features", charset: "utf8mb3", force: :cascade do |t| + create_table "flipflop_features", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "key", null: false t.boolean "enabled", default: false, null: false t.datetime "created_at", null: false @@ -569,7 +569,7 @@ t.index ["slug"], name: "index_groups_on_slug" end - create_table "historical_account_roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "historical_account_roles", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "role_id" t.integer "historical_account_id" t.datetime "created_at", precision: nil @@ -578,7 +578,7 @@ t.index ["role_id"], name: "index_historical_account_roles_on_role_id" end - create_table "historical_accounts", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "historical_accounts", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "person_id" t.text "summary" t.text "body" @@ -593,7 +593,7 @@ t.index ["person_id"], name: "index_historical_accounts_on_person_id" end - create_table "home_page_list_items", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "home_page_list_items", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "home_page_list_id", null: false t.integer "item_id", null: false t.string "item_type", null: false @@ -605,7 +605,7 @@ t.index ["item_id", "item_type"], name: "index_home_page_list_items_on_item_id_and_item_type" end - create_table "home_page_lists", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "home_page_lists", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "owner_id", null: false t.string "owner_type", null: false t.string "name" @@ -614,13 +614,13 @@ t.index ["owner_id", "owner_type", "name"], name: "index_home_page_lists_on_owner_id_and_owner_type_and_name", unique: true end - create_table "image_data", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "image_data", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "carrierwave_image" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil end - create_table "images", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "images", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "image_data_id" t.integer "edition_id" t.string "alt_text" @@ -658,7 +658,7 @@ t.index ["link_reportable_type", "link_reportable_id"], name: "index_link_checker_api_reportable" end - create_table "nation_inapplicabilities", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "nation_inapplicabilities", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "nation_id" t.integer "edition_id" t.datetime "created_at", precision: nil @@ -690,7 +690,7 @@ t.index ["slug"], name: "index_operational_fields_on_slug" end - create_table "organisation_roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "organisation_roles", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "organisation_id" t.integer "role_id" t.datetime "created_at", precision: nil @@ -706,7 +706,7 @@ t.index ["superseded_organisation_id"], name: "index_organisation_supersedings_on_superseded_organisation_id" end - create_table "organisation_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "organisation_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "organisation_id" t.string "locale" t.string "name" @@ -728,7 +728,7 @@ t.index ["parent_organisation_id"], name: "index_organisational_relationships_on_parent_organisation_id" end - create_table "organisations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "organisations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "slug", null: false @@ -762,7 +762,7 @@ t.index ["slug"], name: "index_organisations_on_slug", unique: true end - create_table "people", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "people", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "title" t.string "forename" t.string "surname" @@ -775,7 +775,7 @@ t.index ["slug"], name: "index_people_on_slug", unique: true end - create_table "person_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "person_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "person_id" t.string "locale" t.text "biography" @@ -796,7 +796,7 @@ t.index ["policy_group_id"], name: "index_policy_group_dependencies_on_policy_group_id" end - create_table "policy_groups", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "policy_groups", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "email" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -808,7 +808,7 @@ t.index ["slug"], name: "index_policy_groups_on_slug" end - create_table "promotional_feature_items", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "promotional_feature_items", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "promotional_feature_id" t.text "summary" t.string "image" @@ -822,7 +822,7 @@ t.index ["promotional_feature_id"], name: "index_promotional_feature_items_on_promotional_feature_id" end - create_table "promotional_feature_links", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "promotional_feature_links", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "promotional_feature_item_id" t.string "url" t.string "text" @@ -831,7 +831,7 @@ t.index ["promotional_feature_item_id"], name: "index_promotional_feature_links_on_promotional_feature_item_id" end - create_table "promotional_features", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "promotional_features", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "organisation_id" t.string "title" t.datetime "created_at", precision: nil @@ -840,7 +840,7 @@ t.index ["organisation_id"], name: "index_promotional_features_on_organisation_id" end - create_table "recent_edition_openings", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "recent_edition_openings", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id", null: false t.integer "editor_id", null: false t.datetime "created_at", precision: nil, null: false @@ -856,6 +856,17 @@ t.index ["edition_id"], name: "index_related_mainstreams_on_edition_id" end + create_table "responses", id: :integer, charset: "utf8mb3", force: :cascade do |t| + t.integer "edition_id" + t.text "summary" + t.datetime "created_at", precision: nil + t.datetime "updated_at", precision: nil + t.date "published_on" + t.string "type" + t.index ["edition_id", "type"], name: "index_responses_on_edition_id_and_type" + t.index ["edition_id"], name: "index_responses_on_edition_id" + end + create_table "review_reminders", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "document_id" t.integer "creator_id" @@ -869,7 +880,7 @@ t.index ["review_at", "reminder_sent_at"], name: "index_review_reminders_on_review_at_and_reminder_sent_at" end - create_table "role_appointments", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "role_appointments", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "role_id" t.integer "person_id" t.datetime "created_at", precision: nil @@ -883,7 +894,7 @@ t.index ["role_id"], name: "index_role_appointments_on_role_id" end - create_table "role_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "role_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "role_id" t.string "locale" t.string "name" @@ -895,7 +906,7 @@ t.index ["role_id"], name: "index_role_translations_on_role_id" end - create_table "roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "roles", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "type", null: false @@ -924,7 +935,7 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "social_media_account_translations", charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "social_media_account_translations", charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.text "url" t.text "title" t.string "locale", null: false @@ -935,7 +946,7 @@ t.index ["social_media_account_id"], name: "index_on_social_media_account" end - create_table "social_media_accounts", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "social_media_accounts", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "socialable_id" t.integer "social_media_service_id" t.datetime "created_at", precision: nil @@ -945,7 +956,7 @@ t.index ["socialable_id"], name: "index_social_media_accounts_on_organisation_id" end - create_table "social_media_services", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "social_media_services", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -1017,7 +1028,7 @@ t.index ["topic_id"], name: "index_statistics_announcements_on_topic_id" end - create_table "take_part_pages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "take_part_pages", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "title", null: false t.string "slug", null: false t.string "summary", null: false @@ -1031,7 +1042,7 @@ t.index ["slug"], name: "index_take_part_pages_on_slug", unique: true end - create_table "topical_event_about_pages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "topical_event_about_pages", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "topical_event_id" t.string "name" t.text "summary" @@ -1063,7 +1074,7 @@ t.index ["topical_event_id"], name: "index_topical_event_feat_on_topical_event_id" end - create_table "topical_event_memberships", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "topical_event_memberships", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "topical_event_id" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -1073,7 +1084,7 @@ t.index ["topical_event_id"], name: "index_topical_event_memberships_on_topical_event_id" end - create_table "topical_event_organisations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "topical_event_organisations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "organisation_id", null: false t.integer "topical_event_id", null: false t.datetime "created_at", precision: nil @@ -1086,7 +1097,7 @@ t.index ["topical_event_id"], name: "index_topical_event_org_on_topical_event_id" end - create_table "topical_events", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "topical_events", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -1101,7 +1112,7 @@ t.index ["slug"], name: "index_topical_events_on_slug" end - create_table "unpublishings", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "unpublishings", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "edition_id" t.integer "unpublishing_reason_id" t.text "explanation" @@ -1117,13 +1128,13 @@ t.index ["unpublishing_reason_id"], name: "index_unpublishings_on_unpublishing_reason_id" end - create_table "user_world_locations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "user_world_locations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "user_id" t.integer "world_location_id" t.index ["user_id", "world_location_id"], name: "index_user_world_locations_on_user_id_and_world_location_id", unique: true end - create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -1138,7 +1149,7 @@ t.index ["organisation_slug"], name: "index_users_on_organisation_slug" end - create_table "versions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "versions", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.string "item_type", null: false t.integer "item_id", null: false t.string "event", null: false @@ -1165,7 +1176,7 @@ t.datetime "updated_at", null: false end - create_table "world_location_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "world_location_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "world_location_id" t.string "locale" t.string "name" @@ -1175,7 +1186,7 @@ t.index ["world_location_id"], name: "index_world_location_translations_on_world_location_id" end - create_table "world_locations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "world_locations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "slug" @@ -1196,7 +1207,7 @@ t.datetime "updated_at", precision: nil end - create_table "worldwide_offices", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "worldwide_offices", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "worldwide_organisation_id" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil @@ -1210,7 +1221,7 @@ t.index ["worldwide_organisation_id"], name: "index_worldwide_offices_on_worldwide_organisation_id" end - create_table "worldwide_organisation_page_translations", charset: "utf8mb3", force: :cascade do |t| + create_table "worldwide_organisation_page_translations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "worldwide_organisation_page_id", null: false t.string "locale", null: false t.datetime "created_at", null: false @@ -1222,7 +1233,7 @@ t.index ["worldwide_organisation_page_id"], name: "index_bbd0fc4436b2d97c8b36796e9089468751fc0f2e" end - create_table "worldwide_organisation_pages", charset: "utf8mb3", force: :cascade do |t| + create_table "worldwide_organisation_pages", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "corporate_information_page_type_id", null: false t.integer "edition_id", null: false t.datetime "created_at", null: false @@ -1231,7 +1242,7 @@ t.index ["edition_id"], name: "index_worldwide_organisation_pages_on_edition_id" end - create_table "worldwide_organisation_roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "worldwide_organisation_roles", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "worldwide_organisation_id" t.integer "role_id" t.datetime "created_at", precision: nil @@ -1240,7 +1251,7 @@ t.index ["worldwide_organisation_id"], name: "index_worldwide_org_roles_on_worldwide_organisation_id" end - create_table "worldwide_organisation_translations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t| + create_table "worldwide_organisation_translations", id: :integer, charset: "utf8mb3", collation: "utf8_unicode_ci", force: :cascade do |t| t.integer "worldwide_organisation_id" t.string "locale" t.string "name"