govspeak 8.4.1 → 8.5.0

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: 23ac40c67eb46b8265549568542bc48bb0cb96fe47c8b217a1e6135789ab2134
4
+ data.tar.gz: 26d7a270ab0163d056c0fefff53548727019128456deab21409ee3b3ae6e01eb
5
5
  SHA512:
6
- metadata.gz: 3aacba8ee80a9e26f59042d100d74611aef458e90fcdd9b0f5070eb780f25b19a7c28d549ad147afe0af66c802394b134430d5a0d568c2bfd051ef4e2b3582b2
7
- data.tar.gz: 1fd96e90cb895b1e48f5b678ea63258ac092058aadaff415400785be8ed2654b8dda08975e70e9e7beba8ff5d2bd564d90cca41331e3f00b6d0d0803b8e25140
6
+ metadata.gz: e637261f8a573479a054e8cdcba539ed296c4c9a1d70c263ff4caea059faf80992806332922259da0d52983013a4c302a11bed5421b12534f6d1de6b105d9620
7
+ data.tar.gz: fa04e11f539f372793d6420f9dcdc81964d244530f3d9e397f0587ca22fac9e66e41b85d79cd224c88f187946bddeb04f9afdfe223328240c353b060bb45fba2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 8.5.0
2
+
3
+ * Support embeds in Govspeak
4
+
1
5
  ## 8.4.1
2
6
  * Do not pin version of govuk_publishing_components
3
7
 
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/embedded_content.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
+ embeds: [
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,17 @@
1
+ module Govspeak
2
+ class EmbedExtractor
3
+ def initialize(document)
4
+ @document = document
5
+ end
6
+
7
+ def content_references
8
+ @content_references ||= @document.scan(EmbeddedContent::EMBED_REGEX).map { |match|
9
+ EmbeddedContent.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,15 @@
1
+ module Govspeak
2
+ class EmbeddedContent
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,32 @@
1
+ require "action_view"
2
+ require "htmlentities"
3
+
4
+ module Govspeak
5
+ class EmbedPresenter
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.0".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/embed_extractor"
18
+ require "govspeak/embedded_content"
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/embed_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, :embeds
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
+ @embeds = Array.wrap(options.delete(:embeds))
60
64
  @locale = options.fetch(:locale, "en")
61
65
  @options = { input: PARSER_CLASS_NAME,
62
66
  sanitize: true,
@@ -255,6 +259,13 @@ module Govspeak
255
259
  render_image(AttachmentImagePresenter.new(attachment))
256
260
  end
257
261
 
262
+ extension("embeds", Govspeak::EmbeddedContent::EMBED_REGEX) do |_embed_code, _document_type, content_id|
263
+ embed = embeds.detect { |e| e[:content_id] == content_id }
264
+ next "" unless embed
265
+
266
+ EmbedPresenter.new(embed).render
267
+ end
268
+
258
269
  # As of version 1.12.0 of Kramdown the block elements (div & figcaption)
259
270
  # inside this html block will have it's < > converted into HTML Entities
260
271
  # when ever this code is used inside block level elements.
@@ -0,0 +1,59 @@
1
+ require "test_helper"
2
+
3
+ class EmbedExtractorTest < Minitest::Test
4
+ extend Minitest::Spec::DSL
5
+
6
+ describe "EmbedExtractor" do
7
+ subject { Govspeak::EmbedExtractor.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,84 @@
1
+ require "test_helper"
2
+
3
+ class GovspeakEmbedsTest < 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
+ embed = {
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, embeds: [embed]).to_html
24
+
25
+ expected = "<p><span class=\"embed embed-content_block_email_address\" id=\"embed_#{content_id}\">#{embed[: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
+ embed = {
32
+ content_id:,
33
+ document_type: "contact",
34
+ title: "foo",
35
+ }
36
+ govspeak = "{{embed:contact:#{content_id}}}"
37
+
38
+ rendered = Govspeak::Document.new(govspeak, embeds: [embed]).to_html
39
+
40
+ expected = "<p><span class=\"embed embed-contact\" id=\"embed_#{content_id}\">#{embed[:title]}</span></p>"
41
+
42
+ assert_equal compress_html(expected), compress_html(rendered)
43
+ end
44
+
45
+ it "ignores missing embeds" do
46
+ govspeak = "{{embed:contact:#{content_id}}}"
47
+
48
+ rendered = Govspeak::Document.new(govspeak, embeds: []).to_html
49
+
50
+ assert_equal compress_html(""), compress_html(rendered)
51
+ end
52
+
53
+ it "supports multiple embeds" do
54
+ embeds = [
55
+ {
56
+ content_id: SecureRandom.uuid,
57
+ document_type: "contact",
58
+ title: "foo",
59
+ },
60
+ {
61
+ content_id: SecureRandom.uuid,
62
+ document_type: "content_block_email_address",
63
+ title: "foo",
64
+ details: {
65
+ email_address: "foo@example.com",
66
+ },
67
+ },
68
+ ]
69
+
70
+ govspeak = %(Here is a contact: {{embed:contact:#{embeds[0][:content_id]}}}
71
+
72
+ Here is an email address: {{embed:content_block_email_address:#{embeds[1][:content_id]}}}
73
+ )
74
+
75
+ rendered = Govspeak::Document.new(govspeak, embeds:).to_html
76
+
77
+ expected = """
78
+ <p>Here is a contact: <span class=\"embed embed-contact\" id=\"embed_#{embeds[0][:content_id]}\">#{embeds[0][:title]}</span></p>
79
+ <p>Here is an email address: <span class=\"embed embed-content_block_email_address\" id=\"embed_#{embeds[1][:content_id]}\">#{embeds[1][:details][:email_address]}</span></p>
80
+ """
81
+
82
+ assert_equal compress_html(expected), compress_html(rendered)
83
+ end
84
+ 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.0
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-21 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/embed_extractor.rb
254
+ - lib/govspeak/embedded_content.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/embed_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/embed_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_embeds_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/embed_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_embeds_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