govspeak 8.4.1 → 8.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fee9ba67d010cc01d04d929c4995d48be18802a9cc263eb766e4fb5dd64ea04
4
- data.tar.gz: 62206016e1f736157da903662d696438da0406e497d53c6dc0a2574a0cc3555c
3
+ metadata.gz: 5a417c917e3816a6dc8f1dd69adb0264a6b89d679fa0fe40342e853913140885
4
+ data.tar.gz: 666d01ec5fc952dea1a2445aca9a29662b4208b6baf68833610a33487030249a
5
5
  SHA512:
6
- metadata.gz: 3aacba8ee80a9e26f59042d100d74611aef458e90fcdd9b0f5070eb780f25b19a7c28d549ad147afe0af66c802394b134430d5a0d568c2bfd051ef4e2b3582b2
7
- data.tar.gz: 1fd96e90cb895b1e48f5b678ea63258ac092058aadaff415400785be8ed2654b8dda08975e70e9e7beba8ff5d2bd564d90cca41331e3f00b6d0d0803b8e25140
6
+ metadata.gz: b140b11ff35bc4698a2f0a485556c5992abde565fd9fc1bdea76d2cb9f8494956a2f5038cda47e29e14fa18dad85b0e190f403f4362c76b263c56e248f50bbdf
7
+ data.tar.gz: 9f4f534254cf22208c8c71016f8b35ca0dc4908a07026ff034857ec4c33b6a192389e4c110d55c0231f54f5aa3b4177e6384f4a9ae22e25d51de80afc8c57e45
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ### 8.5.1
2
+
3
+ * Rename embed-related code to `content block`
4
+ * Do not attempt to parse embed codes if `content_blocks` option is missing
5
+
6
+ ### 8.5.0
7
+
8
+ * Support embeds in Govspeak
9
+
1
10
  ## 8.4.1
2
11
  * Do not pin version of govuk_publishing_components
3
12
 
data/README.md CHANGED
@@ -608,6 +608,34 @@ will output
608
608
  </div>
609
609
  ```
610
610
 
611
+ ### Content blocks
612
+
613
+ Authors can embed different types of [supported content](https://github.com/alphagov/govspeak/blob/main/lib/govspeak/content_block.rb#L3) created by the Content Block Manager
614
+
615
+ ```
616
+ {{embed:content_block_email_address:d308f561-e5ee-45b5-90b2-3ac36a23fad9}}
617
+ ```
618
+
619
+ with options provided
620
+
621
+ ```
622
+ {
623
+ content_blocks: [
624
+ {
625
+ content_id: "d308f561-e5ee-45b5-90b2-3ac36a23fad9",
626
+ title: "Government Digital Service",
627
+ details: { email_address: "test@example.com" },
628
+ }
629
+ ]
630
+ }
631
+ ```
632
+
633
+ will output
634
+
635
+ ```html
636
+ <span class="embed_content_block_email_address" id="embed_d308f561-e5ee-45b5-90b2-3ac36a23fad9">test@example.com</span>
637
+ ```
638
+
611
639
  ### Button
612
640
 
613
641
  An accessible way to add button links into content, that can also allow cross domain tracking with [Google Analytics](https://support.google.com/analytics/answer/7372977?hl=en)
@@ -0,0 +1,15 @@
1
+ module Govspeak
2
+ class ContentBlock
3
+ SUPPORTED_DOCUMENT_TYPES = %w[contact content_block_email_address].freeze
4
+ UUID_REGEX = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
5
+ EMBED_REGEX = /({{embed:(#{SUPPORTED_DOCUMENT_TYPES.join('|')}):#{UUID_REGEX}}})/
6
+
7
+ attr_reader :document_type, :content_id, :embed_code
8
+
9
+ def initialize(document_type:, content_id:, embed_code:)
10
+ @document_type = document_type
11
+ @content_id = content_id
12
+ @embed_code = embed_code
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Govspeak
2
+ class ContentBlockExtractor
3
+ def initialize(document)
4
+ @document = document
5
+ end
6
+
7
+ def content_references
8
+ @content_references ||= @document.scan(ContentBlock::EMBED_REGEX).map { |match|
9
+ ContentBlock.new(document_type: match[1], content_id: match[2], embed_code: match[0])
10
+ }.uniq
11
+ end
12
+
13
+ def content_ids
14
+ @content_ids ||= content_references.map(&:content_id)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ require "action_view"
2
+ require "htmlentities"
3
+
4
+ module Govspeak
5
+ class ContentBlockPresenter
6
+ include ActionView::Helpers::TagHelper
7
+
8
+ attr_reader :embed
9
+
10
+ def initialize(embed)
11
+ @embed = ActiveSupport::HashWithIndifferentAccess.new(embed)
12
+ end
13
+
14
+ def content_id
15
+ embed[:content_id]
16
+ end
17
+
18
+ def document_type
19
+ embed[:document_type]
20
+ end
21
+
22
+ def render
23
+ body = if document_type == "content_block_email_address"
24
+ embed.dig(:details, :email_address)
25
+ else
26
+ embed[:title]
27
+ end
28
+
29
+ content_tag(:span, body, class: "embed embed-#{document_type}", id: "embed_#{content_id}")
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "8.4.1".freeze
2
+ VERSION = "8.5.1".freeze
3
3
  end
data/lib/govspeak.rb CHANGED
@@ -14,11 +14,14 @@ require "govspeak/structured_header_extractor"
14
14
  require "govspeak/html_validator"
15
15
  require "govspeak/html_sanitizer"
16
16
  require "govspeak/blockquote_extra_quote_remover"
17
+ require "govspeak/content_block_extractor"
18
+ require "govspeak/content_block"
17
19
  require "govspeak/post_processor"
18
20
  require "govspeak/link_extractor"
19
21
  require "govspeak/template_renderer"
20
22
  require "govspeak/presenters/attachment_presenter"
21
23
  require "govspeak/presenters/contact_presenter"
24
+ require "govspeak/presenters/content_block_presenter"
22
25
  require "govspeak/presenters/h_card_presenter"
23
26
  require "govspeak/presenters/image_presenter"
24
27
  require "govspeak/presenters/attachment_image_presenter"
@@ -37,7 +40,7 @@ module Govspeak
37
40
  @extensions = []
38
41
 
39
42
  attr_accessor :images
40
- attr_reader :attachments, :contacts, :links, :locale
43
+ attr_reader :attachments, :contacts, :links, :locale, :content_blocks
41
44
 
42
45
  def self.to_html(source, options = {})
43
46
  new(source, options).to_html
@@ -57,6 +60,7 @@ module Govspeak
57
60
  @attachments = Array.wrap(options.delete(:attachments))
58
61
  @links = Array.wrap(options.delete(:links))
59
62
  @contacts = Array.wrap(options.delete(:contacts))
63
+ @content_blocks = Array.wrap(options.delete(:content_blocks))
60
64
  @locale = options.fetch(:locale, "en")
61
65
  @options = { input: PARSER_CLASS_NAME,
62
66
  sanitize: true,
@@ -255,6 +259,15 @@ module Govspeak
255
259
  render_image(AttachmentImagePresenter.new(attachment))
256
260
  end
257
261
 
262
+ extension("content blocks", Govspeak::ContentBlock::EMBED_REGEX) do |embed_code, _document_type, content_id|
263
+ next embed_code if content_blocks.empty?
264
+
265
+ embed = content_blocks.detect { |e| e[:content_id] == content_id }
266
+ next "" unless embed
267
+
268
+ ContentBlockPresenter.new(embed).render
269
+ end
270
+
258
271
  # As of version 1.12.0 of Kramdown the block elements (div & figcaption)
259
272
  # inside this html block will have it's < > converted into HTML Entities
260
273
  # when ever this code is used inside block level elements.
@@ -0,0 +1,59 @@
1
+ require "test_helper"
2
+
3
+ class ContentBlockExtractorTest < Minitest::Test
4
+ extend Minitest::Spec::DSL
5
+
6
+ describe "ContentBlockExtractor" do
7
+ subject { Govspeak::ContentBlockExtractor.new(document) }
8
+
9
+ describe "when there is no embedded content" do
10
+ let(:document) { "foo" }
11
+
12
+ describe "#content_references" do
13
+ it "returns an empty array" do
14
+ assert_equal [], subject.content_references
15
+ end
16
+ end
17
+
18
+ describe "#content_ids" do
19
+ it "returns an empty array" do
20
+ assert_equal [], subject.content_ids
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "when there is embedded content" do
26
+ let(:contact_uuid) { SecureRandom.uuid }
27
+ let(:content_block_email_address_uuid) { SecureRandom.uuid }
28
+
29
+ let(:document) do
30
+ """
31
+ {{embed:contact:#{contact_uuid}}}
32
+ {{embed:content_block_email_address:#{content_block_email_address_uuid}}}
33
+ """
34
+ end
35
+
36
+ describe "#content_references" do
37
+ it "returns all references" do
38
+ result = subject.content_references
39
+
40
+ assert_equal 2, result.count
41
+
42
+ assert_equal "contact", result[0].document_type
43
+ assert_equal contact_uuid, result[0].content_id
44
+ assert_equal "{{embed:contact:#{contact_uuid}}}", result[0].embed_code
45
+
46
+ assert_equal "content_block_email_address", result[1].document_type
47
+ assert_equal content_block_email_address_uuid, result[1].content_id
48
+ assert_equal "{{embed:content_block_email_address:#{content_block_email_address_uuid}}}", result[1].embed_code
49
+ end
50
+ end
51
+
52
+ describe "#content_ids" do
53
+ it "returns all uuids as an array" do
54
+ assert_equal [contact_uuid, content_block_email_address_uuid], subject.content_ids
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,98 @@
1
+ require "test_helper"
2
+
3
+ class GovspeakContentBlocksTest < Minitest::Test
4
+ extend Minitest::Spec::DSL
5
+
6
+ def compress_html(html)
7
+ html.gsub(/[\n\r]+\s*/, "")
8
+ end
9
+
10
+ let(:content_id) { SecureRandom.uuid }
11
+
12
+ it "renders an email address when present in options[:embeds]" do
13
+ content_block = {
14
+ content_id:,
15
+ document_type: "content_block_email_address",
16
+ title: "foo",
17
+ details: {
18
+ email_address: "foo@example.com",
19
+ },
20
+ }
21
+ govspeak = "{{embed:content_block_email_address:#{content_id}}}"
22
+
23
+ rendered = Govspeak::Document.new(govspeak, content_blocks: [content_block]).to_html
24
+
25
+ expected = "<p><span class=\"embed embed-content_block_email_address\" id=\"embed_#{content_id}\">#{content_block[:details][:email_address]}</span></p>"
26
+
27
+ assert_equal compress_html(expected), compress_html(rendered)
28
+ end
29
+
30
+ it "renders the title when the document type is a contact" do
31
+ content_block = {
32
+ content_id:,
33
+ document_type: "contact",
34
+ title: "foo",
35
+ }
36
+ govspeak = "{{embed:contact:#{content_id}}}"
37
+
38
+ rendered = Govspeak::Document.new(govspeak, content_blocks: [content_block]).to_html
39
+
40
+ expected = "<p><span class=\"embed embed-contact\" id=\"embed_#{content_id}\">#{content_block[:title]}</span></p>"
41
+
42
+ assert_equal compress_html(expected), compress_html(rendered)
43
+ end
44
+
45
+ it "removes embed code if a content block cannot be found" do
46
+ content_block = {
47
+ content_id: SecureRandom.uuid,
48
+ document_type: "contact",
49
+ title: "foo",
50
+ }
51
+
52
+ govspeak = "{{embed:contact:#{content_id}}}"
53
+
54
+ rendered = Govspeak::Document.new(govspeak, content_blocks: [content_block]).to_html
55
+
56
+ assert_equal compress_html(""), compress_html(rendered)
57
+ end
58
+
59
+ it "retains an embed code if content_blocks are not specified" do
60
+ govspeak = "{{embed:contact:#{content_id}}}"
61
+
62
+ rendered = Govspeak::Document.new(govspeak).to_html
63
+
64
+ assert_equal compress_html("<p>#{govspeak}</p>"), compress_html(rendered)
65
+ end
66
+
67
+ it "supports multiple embeds" do
68
+ content_blocks = [
69
+ {
70
+ content_id: SecureRandom.uuid,
71
+ document_type: "contact",
72
+ title: "foo",
73
+ },
74
+ {
75
+ content_id: SecureRandom.uuid,
76
+ document_type: "content_block_email_address",
77
+ title: "foo",
78
+ details: {
79
+ email_address: "foo@example.com",
80
+ },
81
+ },
82
+ ]
83
+
84
+ govspeak = %(Here is a contact: {{embed:contact:#{content_blocks[0][:content_id]}}}
85
+
86
+ Here is an email address: {{embed:content_block_email_address:#{content_blocks[1][:content_id]}}}
87
+ )
88
+
89
+ rendered = Govspeak::Document.new(govspeak, content_blocks:).to_html
90
+
91
+ expected = """
92
+ <p>Here is a contact: <span class=\"embed embed-contact\" id=\"embed_#{content_blocks[0][:content_id]}\">#{content_blocks[0][:title]}</span></p>
93
+ <p>Here is an email address: <span class=\"embed embed-content_block_email_address\" id=\"embed_#{content_blocks[1][:content_id]}\">#{content_blocks[1][:details][:email_address]}</span></p>
94
+ """
95
+
96
+ assert_equal compress_html(expected), compress_html(rendered)
97
+ end
98
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govspeak
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.4.1
4
+ version: 8.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-10 00:00:00.000000000 Z
11
+ date: 2024-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -250,6 +250,8 @@ files:
250
250
  - config/address_formats.yml
251
251
  - lib/govspeak.rb
252
252
  - lib/govspeak/blockquote_extra_quote_remover.rb
253
+ - lib/govspeak/content_block.rb
254
+ - lib/govspeak/content_block_extractor.rb
253
255
  - lib/govspeak/header_extractor.rb
254
256
  - lib/govspeak/html_sanitizer.rb
255
257
  - lib/govspeak/html_validator.rb
@@ -258,6 +260,7 @@ files:
258
260
  - lib/govspeak/presenters/attachment_image_presenter.rb
259
261
  - lib/govspeak/presenters/attachment_presenter.rb
260
262
  - lib/govspeak/presenters/contact_presenter.rb
263
+ - lib/govspeak/presenters/content_block_presenter.rb
261
264
  - lib/govspeak/presenters/h_card_presenter.rb
262
265
  - lib/govspeak/presenters/image_presenter.rb
263
266
  - lib/govspeak/structured_header_extractor.rb
@@ -306,12 +309,14 @@ files:
306
309
  - locales/zh-tw.yml
307
310
  - locales/zh.yml
308
311
  - test/blockquote_extra_quote_remover_test.rb
312
+ - test/content_block_extractor_test.rb
309
313
  - test/govspeak_attachment_link_test.rb
310
314
  - test/govspeak_attachment_test.rb
311
315
  - test/govspeak_attachments_image_test.rb
312
316
  - test/govspeak_attachments_inline_test.rb
313
317
  - test/govspeak_button_test.rb
314
318
  - test/govspeak_contacts_test.rb
319
+ - test/govspeak_content_blocks_test.rb
315
320
  - test/govspeak_extract_contact_content_ids_test.rb
316
321
  - test/govspeak_footnote_test.rb
317
322
  - test/govspeak_images_bang_test.rb
@@ -344,18 +349,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
349
  - !ruby/object:Gem::Version
345
350
  version: '0'
346
351
  requirements: []
347
- rubygems_version: 3.5.21
352
+ rubygems_version: 3.5.22
348
353
  signing_key:
349
354
  specification_version: 4
350
355
  summary: Markup language for single domain
351
356
  test_files:
352
357
  - test/blockquote_extra_quote_remover_test.rb
358
+ - test/content_block_extractor_test.rb
353
359
  - test/govspeak_attachment_link_test.rb
354
360
  - test/govspeak_attachment_test.rb
355
361
  - test/govspeak_attachments_image_test.rb
356
362
  - test/govspeak_attachments_inline_test.rb
357
363
  - test/govspeak_button_test.rb
358
364
  - test/govspeak_contacts_test.rb
365
+ - test/govspeak_content_blocks_test.rb
359
366
  - test/govspeak_extract_contact_content_ids_test.rb
360
367
  - test/govspeak_footnote_test.rb
361
368
  - test/govspeak_images_bang_test.rb