diff --git a/Gemfile b/Gemfile index 2c2b7eb089c..c5361add5cd 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem "bootsnap", require: false gem "carrierwave" gem "carrierwave-i18n" gem "chronic" +gem "content_block_tools" gem "dalli" gem "dartsass-rails" gem "diffy" diff --git a/Gemfile.lock b/Gemfile.lock index 400ee884212..5c770aa0b55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -139,6 +139,8 @@ GEM coderay (1.1.3) concurrent-ruby (1.3.4) connection_pool (2.4.1) + content_block_tools (0.3.0) + actionview (>= 6, < 7.2.2) crack (1.0.0) bigdecimal rexml @@ -1028,6 +1030,7 @@ DEPENDENCIES carrierwave-i18n chronic climate_control + content_block_tools cucumber cucumber-rails dalli diff --git a/app/helpers/admin/admin_govspeak_helper.rb b/app/helpers/admin/admin_govspeak_helper.rb index 5448f0add25..157d1777aed 100644 --- a/app/helpers/admin/admin_govspeak_helper.rb +++ b/app/helpers/admin/admin_govspeak_helper.rb @@ -4,7 +4,8 @@ module Admin::AdminGovspeakHelper def govspeak_to_admin_html(govspeak, images = [], attachments = [], alternative_format_contact_email = nil) images = prepare_images(images) attachments = prepare_attachments(attachments, alternative_format_contact_email) - wrapped_in_govspeak_div(bare_govspeak_to_admin_html(govspeak, images, attachments)) + html = ContentBlockManager::FindAndReplaceEmbedCodesService.call bare_govspeak_to_admin_html(govspeak, images, attachments) + wrapped_in_govspeak_div(html) end def govspeak_edition_to_admin_html(edition) diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb index 11840ae093a..e2143dc6f64 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb @@ -8,9 +8,24 @@ class Edition < ApplicationRecord include ValidatesDetails include Workflow + scope :current_versions, lambda { + joins( + "LEFT JOIN content_block_documents document ON document.latest_edition_id = content_block_editions.id", + ).where(state: "published") + } + def update_document_reference_to_latest_edition! document.update!(latest_edition_id: id) end + + def render + ContentBlockTools::ContentBlock.new( + document_type: "content_block_#{block_type}", + content_id: document.content_id, + title:, + details:, + ).render + end end end end diff --git a/lib/engines/content_block_manager/app/services/content_block_manager/find_and_replace_embed_codes_service.rb b/lib/engines/content_block_manager/app/services/content_block_manager/find_and_replace_embed_codes_service.rb new file mode 100644 index 00000000000..62d91a0f014 --- /dev/null +++ b/lib/engines/content_block_manager/app/services/content_block_manager/find_and_replace_embed_codes_service.rb @@ -0,0 +1,35 @@ +module ContentBlockManager + class FindAndReplaceEmbedCodesService + def self.call(html) + new(html).call + end + + def call + embed_content_references.each do |reference| + content_block = content_blocks.find { |c| c.document.content_id == reference.content_id } + next if content_block.nil? + + html.gsub!(reference.embed_code, content_block.render) + end + + html + end + + private + + attr_reader :html + + def initialize(html) + @html = html + end + + def embed_content_references + @embed_content_references ||= ContentBlockTools::ContentBlockReference.find_all_in_document(html) + end + + def content_blocks + @content_blocks ||= ContentBlockManager::ContentBlock::Edition.current_versions + .where(document: { content_id: embed_content_references.map(&:content_id) }) + end + end +end diff --git a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb index f99d41c018a..ed0bb2ad40e 100644 --- a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb @@ -1,122 +1,161 @@ require "test_helper" class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase - setup do - @new_content_id = SecureRandom.uuid - ContentBlockManager::ContentBlock::Edition.any_instance.stubs(:create_random_id).returns(@new_content_id) - - @created_at = Time.zone.local(2000, 12, 31, 23, 59, 59).utc - @updated_at = Time.zone.local(2000, 12, 31, 23, 59, 59).utc - @details = { "some_field" => "some_content" } - @title = "Document title" - @creator = create(:user) - @organisation = create(:organisation) - - @content_block_edition = ContentBlockManager::ContentBlock::Edition.new( - created_at: @created_at, - updated_at: @updated_at, - details: @details, + extend Minitest::Spec::DSL + + let(:new_content_id) { SecureRandom.uuid } + + let(:created_at) { Time.zone.local(2000, 12, 31, 23, 59, 59).utc } + let(:updated_at) { Time.zone.local(2000, 12, 31, 23, 59, 59).utc } + let(:details) { { "some_field" => "some_content" } } + let(:title) { "Document title" } + let(:creator) { create(:user) } + let(:organisation) { create(:organisation) } + + let(:content_block_edition) do + ContentBlockManager::ContentBlock::Edition.new( + created_at:, + updated_at:, + details:, document_attributes: { block_type: "email_address", - title: @title, + title:, }, - creator: @creator, - organisation_id: @organisation.id.to_s, + creator:, + organisation_id: organisation.id.to_s, ) - @content_block_edition.stubs(:schema).returns(build(:content_block_schema)) end - test "content_block_edition exists with required data" do - @content_block_edition.save! - @content_block_edition.reload + before do + ContentBlockManager::ContentBlock::Edition.any_instance.stubs(:create_random_id).returns(new_content_id) + content_block_edition.stubs(:schema).returns(build(:content_block_schema)) + end + + it "exists with required data" do + content_block_edition.save! + content_block_edition.reload - assert_equal @created_at, @content_block_edition.created_at - assert_equal @updated_at, @content_block_edition.updated_at - assert_equal @details, @content_block_edition.details + assert_equal created_at, content_block_edition.created_at + assert_equal updated_at, content_block_edition.updated_at + assert_equal details, content_block_edition.details end - test "it persists the block type to the document" do - @content_block_edition.save! - @content_block_edition.reload - document = @content_block_edition.document + it "persists the block type to the document" do + content_block_edition.save! + content_block_edition.reload + document = content_block_edition.document - assert_equal document.block_type, @content_block_edition.block_type + assert_equal document.block_type, content_block_edition.block_type end - test "it persists the title to the document" do - @content_block_edition.save! - @content_block_edition.reload - document = @content_block_edition.document + it "persists the title to the document" do + content_block_edition.save! + content_block_edition.reload + document = content_block_edition.document - assert_equal document.title, @content_block_edition.title + assert_equal document.title, content_block_edition.title end - test "it creates a document" do - @content_block_edition.save! - @content_block_edition.reload + it "creates a document" do + content_block_edition.save! + content_block_edition.reload - assert_not_nil @content_block_edition.document - assert_equal @content_block_edition.document.content_id, @new_content_id + assert_not_nil content_block_edition.document + assert_equal content_block_edition.document.content_id, new_content_id end - test "it adds a content id if a document is provided" do - @content_block_edition.document = build(:content_block_document, :email_address, content_id: nil) - @content_block_edition.save! - @content_block_edition.reload + it "adds a content id if a document is provided" do + content_block_edition.document = build(:content_block_document, :email_address, content_id: nil) + content_block_edition.save! + content_block_edition.reload - assert_not_nil @content_block_edition.document - assert_equal @content_block_edition.document.content_id, @new_content_id + assert_not_nil content_block_edition.document + assert_equal content_block_edition.document.content_id, new_content_id end - test "it validates the presence of a document block_type" do - @content_block_edition = build( + it "validates the presence of a document block_type" do + content_block_edition = build( :content_block_edition, - created_at: @created_at, - updated_at: @updated_at, - details: @details, + created_at:, + updated_at:, + details:, document_attributes: { block_type: nil, }, - organisation_id: @organisation.id.to_s, + organisation_id: organisation.id.to_s, ) - assert_invalid @content_block_edition - assert @content_block_edition.errors.full_messages.include?("Document block type can't be blank") + assert_invalid content_block_edition + assert content_block_edition.errors.full_messages.include?("Document block type can't be blank") end - test "it validates the presence of a document title" do + it "validates the presence of a document title" do content_block_edition = build( :content_block_edition, - created_at: @created_at, - updated_at: @updated_at, - details: @details, + created_at:, + updated_at:, + details:, document_attributes: { title: nil, }, - organisation_id: @organisation.id.to_s, + organisation_id: organisation.id.to_s, ) assert_invalid content_block_edition assert content_block_edition.errors.full_messages.include?("Title can't be blank") end - test "it adds a creator and first edition author for new records" do - @content_block_edition.save! - @content_block_edition.reload - assert_equal @content_block_edition.creator, @content_block_edition.edition_authors.first.user + it "adds a creator and first edition author for new records" do + content_block_edition.save! + content_block_edition.reload + assert_equal content_block_edition.creator, content_block_edition.edition_authors.first.user + end + + describe "#creator=" do + it "raises an exception if called for a persisted record" do + content_block_edition.save! + assert_raise RuntimeError do + content_block_edition.creator = create(:user) + end + end end - test "#creator= raises an exception if called for a persisted record" do - @content_block_edition.save! - assert_raise RuntimeError do - @content_block_edition.creator = create(:user) + describe "#update_document_reference_to_latest_edition!" do + it "updates the document reference to the latest edition" do + latest_edition = create(:content_block_edition, document: content_block_edition.document) + latest_edition.update_document_reference_to_latest_edition! + + assert_equal latest_edition.document.latest_edition_id, latest_edition.id end end - test "#update_document_reference_to_latest_edition! updates the document reference to the latest edition" do - latest_edition = create(:content_block_edition, document: @content_block_edition.document) - latest_edition.update_document_reference_to_latest_edition! + describe ".current_versions" do + it "returns current published versions" do + document = create(:content_block_document, :email_address) + edition = create(:content_block_edition, :email_address, state: "published", document:) + draft_edition = create(:content_block_edition, :email_address, state: "draft", document:) + document.latest_edition = draft_edition + document.save! + + assert_equal ContentBlockManager::ContentBlock::Edition.current_versions.to_a, [edition] + end + end - assert_equal latest_edition.document.latest_edition_id, latest_edition.id + describe "#render" do + let(:rendered_response) { "RENDERED" } + let(:stub_block) { stub("ContentBlockTools::ContentBlock", render: rendered_response) } + let(:document) { content_block_edition.document } + + it "initializes and renders a content block" do + ContentBlockTools::ContentBlock.expects(:new) + .with( + document_type: "content_block_#{document.block_type}", + content_id: document.content_id, + title:, + details:, + ).returns(stub_block) + + assert_equal content_block_edition.render, rendered_response + end end end diff --git a/lib/engines/content_block_manager/test/unit/app/services/find_and_replace_embed_codes_service_test.rb b/lib/engines/content_block_manager/test/unit/app/services/find_and_replace_embed_codes_service_test.rb new file mode 100644 index 00000000000..7ba41253ed0 --- /dev/null +++ b/lib/engines/content_block_manager/test/unit/app/services/find_and_replace_embed_codes_service_test.rb @@ -0,0 +1,51 @@ +require "test_helper" + +class ContentBlockManager::FindAndReplaceEmbedCodesServiceTest < ActiveSupport::TestCase + extend Minitest::Spec::DSL + + it "finds and replaces embed codes" do + document_1 = create(:content_block_document, :email_address) + edition_1 = create(:content_block_edition, :email_address, state: "published", document: document_1) + document_1.latest_edition = edition_1 + document_1.save! + + document_2 = create(:content_block_document, :email_address) + edition_2 = create(:content_block_edition, :email_address, state: "published", document: document_2) + document_2.latest_edition = edition_2 + document_2.save! + + html = """ +
Hello there
+#{edition_2.document.embed_code}
+#{edition_1.document.embed_code}
+ """ + + expected = """ +Hello there
+#{edition_2.render}
+#{edition_1.render}
+ """ + + result = ContentBlockManager::FindAndReplaceEmbedCodesService.call(html) + + assert_equal result, expected + end + + it "ignores blocks that aren't present in the database" do + edition = build(:content_block_edition, :email_address) + + html = edition.document.embed_code + + result = ContentBlockManager::FindAndReplaceEmbedCodesService.call(html) + assert_equal result, html + end + + it "ignores blocks that don't have a live version" do + edition = create(:content_block_edition, :email_address, state: "draft") + + html = edition.document.embed_code + + result = ContentBlockManager::FindAndReplaceEmbedCodesService.call(html) + assert_equal result, html + end +end diff --git a/test/unit/app/helpers/admin/admin_govspeak_helper_test.rb b/test/unit/app/helpers/admin/admin_govspeak_helper_test.rb index a10cc0f0cd2..d19fa13e428 100644 --- a/test/unit/app/helpers/admin/admin_govspeak_helper_test.rb +++ b/test/unit/app/helpers/admin/admin_govspeak_helper_test.rb @@ -121,4 +121,14 @@ class Admin::AdminGovspeakHelperTest < ActionView::TestCase assert_equivalent_html "