govspeak 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +1 -45
  4. data/lib/govspeak.rb +18 -8
  5. data/lib/govspeak/blockquote_extra_quote_remover.rb +4 -3
  6. data/lib/govspeak/html_sanitizer.rb +1 -1
  7. data/lib/govspeak/post_processor.rb +25 -3
  8. data/lib/govspeak/presenters/attachment_presenter.rb +7 -194
  9. data/lib/govspeak/version.rb +1 -1
  10. data/locales/ar.yml +0 -17
  11. data/locales/be.yml +0 -17
  12. data/locales/bg.yml +0 -19
  13. data/locales/cs.yml +0 -19
  14. data/locales/cy.yml +0 -18
  15. data/locales/de.yml +0 -18
  16. data/locales/el.yml +0 -19
  17. data/locales/en.yml +0 -20
  18. data/locales/es-419.yml +0 -18
  19. data/locales/es.yml +0 -18
  20. data/locales/et.yml +0 -19
  21. data/locales/fa.yml +0 -17
  22. data/locales/fr.yml +0 -18
  23. data/locales/he.yml +0 -18
  24. data/locales/hi.yml +0 -18
  25. data/locales/hu.yml +0 -18
  26. data/locales/hy.yml +0 -18
  27. data/locales/id.yml +0 -17
  28. data/locales/it.yml +0 -18
  29. data/locales/ja.yml +0 -16
  30. data/locales/ko.yml +0 -16
  31. data/locales/lt.yml +0 -17
  32. data/locales/lv.yml +0 -17
  33. data/locales/pl.yml +0 -19
  34. data/locales/ps.yml +0 -17
  35. data/locales/pt.yml +0 -17
  36. data/locales/ro.yml +0 -19
  37. data/locales/ru.yml +0 -16
  38. data/locales/si.yml +0 -17
  39. data/locales/sr.yml +0 -18
  40. data/locales/ta.yml +0 -19
  41. data/locales/th.yml +0 -16
  42. data/locales/tr.yml +0 -17
  43. data/locales/uk.yml +0 -17
  44. data/locales/ur.yml +0 -18
  45. data/locales/uz.yml +0 -17
  46. data/locales/vi.yml +0 -17
  47. data/locales/zh-hk.yml +0 -15
  48. data/locales/zh-tw.yml +0 -15
  49. data/locales/zh.yml +0 -15
  50. data/test/blockquote_extra_quote_remover_test.rb +7 -0
  51. data/test/govspeak_attachment_link_test.rb +25 -0
  52. data/test/govspeak_attachment_test.rb +40 -0
  53. metadata +33 -35
  54. data/lib/templates/attachment.html.erb +0 -57
  55. data/locales/dr.yml +0 -20
  56. data/locales/so.yml +0 -22
  57. data/locales/sq.yml +0 -21
  58. data/test/govspeak_attachments_test.rb +0 -358
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93b2022644e3af2c929faf57f33c9fe70680977e599bde24d59c71ee20402713
4
- data.tar.gz: 7dec8938eedb2183514c54665dd9609f130ac535fbb336739f0f8b6e3d528bc3
3
+ metadata.gz: ec0c2e0694235f631985feeea77f20f07f8ce6ccaf55a8317a83b42583a6bcd2
4
+ data.tar.gz: f98e90a503d00b4dd7c92c9b4dd9e1ff4e3a14494787a344f3722cd788515294
5
5
  SHA512:
6
- metadata.gz: 7b96f54ac8cfdf3e0595cb2de3bc205df86c7efc9869c4e9d06f22623067f4e114d878e9f31c72817d76657b9d759e0b3344ae87724db8394a5a79a727765407
7
- data.tar.gz: 9fc03735199e9fe6016ffe1196e57eac617a744c7aaa3421fc1a57a4ba53190c3dc85c5dda7f76b202aaf29c7c5da51f47ed0fcf71ff43a5b504097bcf8b6ebb
6
+ metadata.gz: b45e92bd8d25ef8a0674de0aba0ac4785589a3af588bf07893b567e09792b13deb15101ba85aef05c45b4429982add8ff52669abfa8b273a9ce29d51591c908b
7
+ data.tar.gz: b193767c7dd265bf61ac851cdc2b0caafd249f63057f5b2008e9dea991ae8d79166fa86169305cf099d3a3ddf1fbe0f1c256f02216d27ae95d2ad2f7a2f17d1e
@@ -1,3 +1,11 @@
1
+ ## 6.1.0
2
+
3
+ * Remove `[embed:attachments:content-id]` this isn't used by any apps and has never worked
4
+ * Add dependency on govuk_publishing_components
5
+ * Add new `AttachementLink:attachment-id` extension and mark as experimental
6
+ * Add new `Attachement:attachment-id` extension and mark as experimental
7
+ * Blockquote quote remover is now more forgiving to spaces before or after quote characters
8
+
1
9
  ## 6.0.0
2
10
 
3
11
  * BREAKING CHANGE: Input is sanitized by default, to use unsafe HTML initialize with a sanitize option of false
data/README.md CHANGED
@@ -352,53 +352,9 @@ Embedded content allows authors to reference a supporting item of a document by
352
352
  referencing an id. The details of this content is passed to the publishing
353
353
  application to govspeak at the time of rendering.
354
354
 
355
- ### Attachments
356
-
357
- To create an attachment callout
358
-
359
- [embed:attachment:2b4d92f3-f8cd-4284-aaaa-25b3a640d26c]
360
-
361
- with options provided
362
-
363
- {
364
- attachments: [
365
- {
366
- id: 123,
367
- content_id: "2b4d92f3-f8cd-4284-aaaa-25b3a640d26c",
368
- title: "Attachment Title",
369
- url: "http://example.com/test.pdf",
370
- order_url: "http://example.com/order",
371
- price: 12.3,
372
- isbn: "isbn-123",
373
- attachment?: true,
374
- }
375
- ]
376
- }
377
-
378
- will output an attachment box
379
-
380
- ```html
381
- <section class="attachment embedded">
382
- <div class="attachment-thumb">
383
- <a href="http://example.com/test.pdf" aria-hidden="true" class="embedded"><img src="/images/pub-cover.png" alt="Pub cover"></a>
384
- </div>
385
- <div class="attachment-details">
386
- <h2 class="title">
387
- <a href="http://example.com/test.pdf" aria-describedby="attachment-123-accessibility-help">Attachment Title</a>
388
- </h2>
389
- <p class="metadata">
390
- <span class="references">Ref: ISBN <span class="isbn">isbn-123</span></span>
391
- </p>
392
- <p>
393
- <a href="http://example.com/order" class="order_url" title="Order a copy of the publication">Order a copy</a>(<span class="price">£12.30</span>)
394
- </p>
395
- </div>
396
- </section>
397
- ```
398
-
399
355
  ### Inline Attachment
400
356
 
401
- Attachments can be linked to inline as well
357
+ Attachments can be linked to inline
402
358
 
403
359
  Details referenced in [embed:attachments:inline:34f6dda0-21b1-4e78-8120-3ff4dcea522d]
404
360
 
@@ -5,6 +5,7 @@ require 'htmlentities'
5
5
  require 'kramdown'
6
6
  require 'kramdown/parser/kramdown_with_automatic_external_links'
7
7
  require 'rinku'
8
+ require 'govuk_publishing_components'
8
9
  require 'govspeak/header_extractor'
9
10
  require 'govspeak/structured_header_extractor'
10
11
  require 'govspeak/html_validator'
@@ -217,18 +218,12 @@ module Govspeak
217
218
  render_image(ImagePresenter.new(image))
218
219
  end
219
220
 
220
- extension('attachment', /\[embed:attachments:(?!inline:|image:)\s*(.*?)\s*\]/) do |content_id|
221
- # not treating this as a self closing tag seems to avoid some oddities
222
- # such as an extra new line being inserted when explicitly closed or
223
- # swallowing subsequent elements when not closed
224
- %{<govspeak-embed-attachment content-id="#{content_id}"></govspeak-embed-attachment>}
225
- end
226
-
227
- extension('attachment inline', /\[embed:attachments:inline:\s*(.*?)\s*\]/) do |content_id|
221
+ extension('embed attachment inline', /\[embed:attachments:inline:\s*(.*?)\s*\]/) do |content_id|
228
222
  attachment = attachments.detect { |a| a[:content_id] == content_id }
229
223
  next "" unless attachment
230
224
 
231
225
  attachment = AttachmentPresenter.new(attachment)
226
+
232
227
  span_id = attachment.id ? %{ id="attachment_#{attachment.id}"} : ""
233
228
  # new lines inside our title cause problems with govspeak rendering as this is expected to be on one line.
234
229
  title = (attachment.title || "").tr("\n", " ")
@@ -354,6 +349,21 @@ module Govspeak
354
349
  render_image(ImagePresenter.new(image))
355
350
  end
356
351
 
352
+ # This is an alternative syntax for embedding attachments using a readable id (expected
353
+ # to be a unique variation of a filename). This syntax is being used by
354
+ # Content Publisher and should be considered experimental as it is likely
355
+ # to be iterated in the short term.
356
+ extension('Attachment', /#{NEW_PARAGRAPH_LOOKBEHIND}\[Attachment:\s*(.*?)\s*\]/) do |attachment_id|
357
+ %{<govspeak-embed-attachment id="#{attachment_id}"></govspeak-embed-attachment>}
358
+ end
359
+
360
+ # This is an alternative syntax for embedding attachments as links. This
361
+ # syntax is being used by Content Publisher and should be considered
362
+ # experimental
363
+ extension('AttachmentLink', /\[AttachmentLink:\s*(.*?)\s*\]/) do |attachment_id|
364
+ %{<govspeak-embed-attachment-link id="#{attachment_id}"></govspeak-embed-attachment-link>}
365
+ end
366
+
357
367
  private
358
368
 
359
369
  def kramdown_doc
@@ -1,7 +1,7 @@
1
1
  module Govspeak
2
2
  module BlockquoteExtraQuoteRemover
3
- QUOTE = '"\u201C\u201D\u201E\u201F\u2033\u2036'.freeze
4
- LINE_BREAK = '\r\n?|\n'.freeze
3
+ QUOTES = '["\u201C\u201D\u201E\u201F\u2033\u2036]+'.freeze
4
+ WHITESPACE = '[^\S\r\n]*'.freeze
5
5
 
6
6
  # used to remove quotes from a markdown blockquote, as these will be inserted
7
7
  # as part of the rendering
@@ -14,7 +14,8 @@ module Govspeak
14
14
  def self.remove(source)
15
15
  return if source.nil?
16
16
 
17
- source.gsub(/^>[ \t]*[#{QUOTE}]*([^ \t\n].+?)[#{QUOTE}]*[ \t]*(#{LINE_BREAK}?)$/, '> \1\2')
17
+ source.gsub(/^>#{WHITESPACE}#{QUOTES}#{WHITESPACE}(.+?)$/, '> \1') # prefixed with a quote
18
+ .gsub(/^>(.+?)#{WHITESPACE}#{QUOTES}#{WHITESPACE}(\r?)$/, '>\1\2') # suffixed with a quote
18
19
  end
19
20
  end
20
21
  end
@@ -60,7 +60,7 @@ class Govspeak::HtmlSanitizer
60
60
  def sanitize_config
61
61
  Sanitize::Config.merge(
62
62
  Sanitize::Config::RELAXED,
63
- elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment],
63
+ elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link],
64
64
  attributes: {
65
65
  :all => Sanitize::Config::RELAXED[:attributes][:all] + ["role", "aria-label"],
66
66
  "a" => Sanitize::Config::RELAXED[:attributes]["a"] + button_sanitize_config,
@@ -51,14 +51,36 @@ module Govspeak
51
51
 
52
52
  extension("embed attachment HTML") do |document|
53
53
  document.css("govspeak-embed-attachment").map do |el|
54
- attachment = govspeak_document.attachments.detect { |a| a[:content_id] == el["content-id"] }
54
+ attachment = govspeak_document.attachments.detect { |a| a[:id] == el["id"] }
55
+
56
+ unless attachment
57
+ el.remove
58
+ next
59
+ end
60
+
61
+ attachment_html = GovukPublishingComponents.render(
62
+ "govuk_publishing_components/components/attachment",
63
+ attachment: attachment,
64
+ locale: govspeak_document.locale
65
+ )
66
+ el.swap(attachment_html)
67
+ end
68
+ end
69
+
70
+ extension("embed attachment link HTML") do |document|
71
+ document.css("govspeak-embed-attachment-link").map do |el|
72
+ attachment = govspeak_document.attachments.detect { |a| a[:id] == el["id"] }
73
+
55
74
  unless attachment
56
75
  el.remove
57
76
  next
58
77
  end
59
78
 
60
- renderer = TemplateRenderer.new('attachment.html.erb', govspeak_document.locale)
61
- attachment_html = renderer.render(attachment: AttachmentPresenter.new(attachment))
79
+ attachment_html = GovukPublishingComponents.render(
80
+ "govuk_publishing_components/components/attachment_link",
81
+ attachment: attachment,
82
+ locale: govspeak_document.locale
83
+ )
62
84
  el.swap(attachment_html)
63
85
  end
64
86
  end
@@ -1,15 +1,11 @@
1
1
  require "action_view"
2
- require "money"
3
2
  require "htmlentities"
4
3
 
5
- Money.locale_backend = :currency
6
-
7
4
  module Govspeak
8
5
  class AttachmentPresenter
9
6
  attr_reader :attachment
10
7
  include ActionView::Helpers::TagHelper
11
8
  include ActionView::Helpers::NumberHelper
12
- include ActionView::Helpers::AssetTagHelper
13
9
  include ActionView::Helpers::TextHelper
14
10
 
15
11
  def initialize(attachment)
@@ -20,158 +16,19 @@ module Govspeak
20
16
  attachment[:id]
21
17
  end
22
18
 
23
- def order_url
24
- attachment[:order_url]
25
- end
26
-
27
- def opendocument?
28
- attachment[:opendocument?]
29
- end
30
-
31
19
  def url
32
20
  attachment[:url]
33
21
  end
34
22
 
35
- def external?
36
- attachment[:external?]
37
- end
38
-
39
- def price
40
- return unless attachment[:price]
41
-
42
- Money.from_amount(attachment[:price], 'GBP').format
43
- end
44
-
45
- def accessible?
46
- attachment[:accessible?]
47
- end
48
-
49
- def thumbnail_link
50
- return if hide_thumbnail?
51
- return if previewable?
52
-
53
- link(attachment_thumbnail, url, "aria-hidden" => "true", "class" => attachment_class)
54
- end
55
-
56
- def help_block_toggle_id
57
- "attachment-#{id}-accessibility-request"
58
- end
59
-
60
- def section_class
61
- attachment[:external?] ? "hosted-externally" : "embedded"
62
- end
63
-
64
- def mail_to(email_address, name, options = {})
65
- query_string = options.slice(:subject, :body).map { |k, v| "#{urlencode(k)}=#{urlencode(v)}" }.join("&")
66
- "<a href='mailto:#{encode(email_address)}?#{encode(query_string)}'>#{name}</a>"
67
- end
68
-
69
- def alternative_format_order_link
70
- attachment_info = []
71
- attachment_info << " Title: #{title}"
72
- attachment_info << " Original format: #{file_extension}" if file_extension.present?
73
- attachment_info << " ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
74
- attachment_info << " Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
75
- attachment_info << " Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
76
- if attachment[:hoc_paper_number].present?
77
- attachment_info << " House of Commons paper number: #{attachment[:hoc_paper_number]}"
78
- attachment_info << " Parliamentary session: #{attachment[:parliamentary_session]}"
79
- end
80
-
81
- options = {
82
- subject: "Request for '#{title}' in an alternative format",
83
- body: body_for_mail(attachment_info)
84
- }
85
-
86
- mail_to(alternative_format_contact_email, alternative_format_contact_email, options)
87
- end
88
-
89
- def body_for_mail(attachment_info)
90
- <<~TEXT
91
- Details of document required:
92
-
93
- #{attachment_info.join("\n")}
94
-
95
- Please tell us:
96
-
97
- 1. What makes this format unsuitable for you?
98
- 2. What format you would prefer?
99
- TEXT
100
- end
101
-
102
- def alternative_format_contact_email
103
- "govuk-feedback@digital.cabinet-office.gov.uk"
104
- end
105
-
106
- # FIXME: usage of image_tag will cause these to render at /images/ which seems
107
- # very host dependent. I assume this will need links to static urls.
108
- def attachment_thumbnail
109
- if file_extension == "pdf" && attachment[:thumbnail_url]
110
- image_tag(attachment[:thumbnail_url])
111
- elsif file_extension == "html"
112
- image_tag('pub-cover-html.png')
113
- elsif %w{doc docx odt}.include?(file_extension)
114
- image_tag('pub-cover-doc.png')
115
- elsif %w{xls xlsx ods csv}.include?(file_extension)
116
- image_tag('pub-cover-spreadsheet.png')
117
- else
118
- image_tag('pub-cover.png')
119
- end
120
- end
121
-
122
- def reference
123
- ref = []
124
- if attachment[:isbn].present?
125
- ref << "ISBN " + content_tag(:span, attachment[:isbn], class: "isbn")
126
- end
127
-
128
- if attachment[:unique_reference].present?
129
- ref << content_tag(:span, attachment[:unique_reference], class: "unique_reference")
130
- end
131
-
132
- if attachment[:command_paper_number].present?
133
- ref << content_tag(:span, attachment[:command_paper_number], class: "command_paper_number")
134
- end
135
-
136
- if attachment[:hoc_paper_number].present?
137
- ref << content_tag(:span, "HC #{attachment[:hoc_paper_number]}", class: 'house_of_commons_paper_number') + ' ' +
138
- content_tag(:span, attachment[:parliamentary_session], class: 'parliamentary_session')
139
- end
140
-
141
- ref.join(', ').html_safe
142
- end
143
-
144
- # FIXME this has english in it so will cause problems if the locale is not en
145
- def references_for_title
146
- references = []
147
- references << "ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
148
- references << "Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
149
- references << "Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
150
- references << "HC: #{attachment[:hoc_paper_number]} #{attachment[:parliamentary_session]}" if attachment[:hoc_paper_number].present?
151
- prefix = references.size == 1 ? "and its reference" : "and its references"
152
- references.any? ? ", #{prefix} (" + references.join(", ") + ")" : ""
153
- end
154
-
155
- def references?
156
- !attachment[:isbn].to_s.empty? || !attachment[:unique_reference].to_s.empty? || !attachment[:command_paper_number].to_s.empty? || !attachment[:hoc_paper_number].to_s.empty?
157
- end
158
-
159
- def attachment_class
160
- attachment[:external?] ? "hosted-externally" : "embedded"
161
- end
162
-
163
- def unnumbered_paper?
164
- attachment[:unnumbered_command_paper?] || attachment[:unnumbered_hoc_paper?]
165
- end
166
-
167
- def unnumbered_command_paper?
168
- attachment[:unnumbered_command_paper?]
23
+ def title
24
+ attachment[:title]
169
25
  end
170
26
 
171
- def download_link
172
- options = {}
173
- options[:title] = number_to_human_size(attachment[:file_size]) if attachment[:file_size].present?
174
- link("<strong>Download #{file_extension.upcase}</strong>", attachment[:url], options)
27
+ def file_extension
28
+ # Note: this is a separate parameter rather than being calculated from the
29
+ # filename because at the time of writing not all apps were using the effects
30
+ # of this field.
31
+ attachment[:file_extension]
175
32
  end
176
33
 
177
34
  def attachment_attributes
@@ -188,10 +45,6 @@ module Govspeak
188
45
  attributes.join(', ').html_safe
189
46
  end
190
47
 
191
- def preview_url
192
- url + '/preview'
193
- end
194
-
195
48
  MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE = "MS Word Document".freeze
196
49
  MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE = "MS Excel Spreadsheet".freeze
197
50
  MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE = "MS Powerpoint Presentation".freeze
@@ -239,42 +92,6 @@ module Govspeak
239
92
  file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, '')
240
93
  end
241
94
 
242
- def previewable?
243
- file_extension == "csv"
244
- end
245
-
246
- def title
247
- attachment[:title]
248
- end
249
-
250
- def file_extension
251
- # Note: this is a separate parameter rather than being calculated from the
252
- # filename because at the time of writing not all apps were using the effects
253
- # of this field.
254
- attachment[:file_extension]
255
- end
256
-
257
- def hide_thumbnail?
258
- defined?(hide_thumbnail) && hide_thumbnail
259
- end
260
-
261
- def attachment_details
262
- return if previewable?
263
-
264
- link(title, url, title_link_options)
265
- end
266
-
267
- def title_link_options
268
- title_link_options = {}
269
- title_link_options["rel"] = "external" if attachment[:external?]
270
- title_link_options["aria-describedby"] = help_block_id unless attachment[:accessible?]
271
- title_link_options
272
- end
273
-
274
- def help_block_id
275
- "attachment-#{id}-accessibility-help"
276
- end
277
-
278
95
  def link(body, url, options = {})
279
96
  options_str = options.map { |k, v| %{#{encode(k)}="#{encode(v)}"} }.join(" ")
280
97
  %{<a href="#{encode(url)}" #{options_str}>#{body}</a>}
@@ -285,9 +102,5 @@ module Govspeak
285
102
  def encode(text)
286
103
  HTMLEntities.new.encode(text)
287
104
  end
288
-
289
- def urlencode(text)
290
- ERB::Util.url_encode(text)
291
- end
292
105
  end
293
106
  end
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "6.0.0".freeze
2
+ VERSION = "6.1.0".freeze
3
3
  end