govspeak 5.0.0 → 5.0.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
  SHA1:
3
- metadata.gz: 726774ba404422dd313e1afb12bbc3d1c2e036fd
4
- data.tar.gz: 4f7e5f0f7b4c9e67c33120050f3021048c13dec7
3
+ metadata.gz: 177a638111a0d8d422e3201f0e3e265218fb1cb9
4
+ data.tar.gz: 5be0baf8e84b10bfd28f0f5ed6e1c5e292fe4c1e
5
5
  SHA512:
6
- metadata.gz: a144b4353edd665f01109bd1eae1ff569f22ea6133bf564024e973d7df730e9f318d9eb76e76df5917e130b10dbf9897dcbcd75ccfdb816ba291b16e3e9349a0
7
- data.tar.gz: 8370760f5a9929f8c33b001d99fc5409900182d815bbae0059c33360d47ef7ccd560ae4b206bdec7940747b73ce9e544edffd10751a7107070b8a9aa49006e8f
6
+ metadata.gz: 77ca5d6c13299db9221804e96b01fb15412d0c38d3017219304992d225dab4382dec796015fd2533e62e7ad734d6f0199b216b175e4f45d099cc913b8ed24a6a
7
+ data.tar.gz: b52ed84b08d05f85c44a2a3bb3fe2d86835f2cf9934e9ead9b7c143b9be9fcbea03b84d8af70ca2245426a9ea1676b54a35a2714d7a3e679ad890d1ee87f46d9
@@ -1,3 +1,7 @@
1
+ ## 5.0.1
2
+ * Move presenters into the Govspeak namespace [#93](https://github.com/alphagov/govspeak/pull/93)
3
+ * Embedded links now will automatically be marked with `rel="external"` [#96](https://github.com/alphagov/govspeak/pull/96)
4
+
1
5
  ## 5.0.0
2
6
  * Update Kramdown version to 1.12.0
3
7
  * Add pry-byebug to development dependencies
data/README.md CHANGED
@@ -43,7 +43,7 @@ In addition to the [standard Markdown syntax](http://daringfireball.net/projects
43
43
  creates a callout with an info (i) icon.
44
44
 
45
45
  <div role="note" aria-label="Information" class="application-notice info-notice">
46
- <p>This is an information callout</p>
46
+ <p>This is an information callout</p>
47
47
  </div>
48
48
 
49
49
  ### Warning callouts
@@ -53,7 +53,7 @@ creates a callout with an info (i) icon.
53
53
  creates a callout with a warning or alert (!) icon
54
54
 
55
55
  <div role="note" aria-label="Help" class="application-notice help-notice">
56
- <p>This is a warning callout</p>
56
+ <p>This is a warning callout</p>
57
57
  </div>
58
58
 
59
59
  ### Example callout
@@ -158,6 +158,74 @@ creates a file download box
158
158
  <p><a href="http://example.com/" title="Example form" rel="external">An example form download link.</a></p>
159
159
  </div>
160
160
 
161
+ ## Place
162
+
163
+ $P
164
+ This is a place
165
+ $P
166
+
167
+ creates a place box
168
+
169
+ <div class="place">
170
+ <p>This is a place</p>
171
+ </div>
172
+
173
+ ## Information
174
+
175
+ $I
176
+ This is information
177
+ $I
178
+
179
+ creates an information box
180
+
181
+ <div class="information">
182
+ <p>This is information</p>
183
+ </div>
184
+
185
+ ## Additional Information
186
+
187
+ $AI
188
+ This is additional information
189
+ $AI
190
+
191
+ creates an additional information box
192
+
193
+ <div class="additional-information">
194
+ <p>This is additional information</p>
195
+ </div>
196
+
197
+ ## Call to Action
198
+
199
+ $CTA
200
+ This is a call to action
201
+ $CTA
202
+
203
+ creates an additional information box
204
+
205
+ <div class="call-to-action">
206
+ <p>This is a call to action</p>
207
+ </div>
208
+
209
+ ## Summary
210
+
211
+ $!
212
+ This is a summary
213
+ $!
214
+
215
+ creates a summary box
216
+
217
+ <div class="summary">
218
+ <p>This is a summary</p>
219
+ </div>
220
+
221
+ ## External Link
222
+
223
+ x[External Report](https://example.com/report)x
224
+
225
+ creates a link specified as external
226
+
227
+ <a href="https://example.com/report" rel="external">External Report</a>
228
+
161
229
  ## Steps
162
230
 
163
231
  Steps can be created similar to an ordered list:
@@ -183,17 +251,26 @@ For lists where you want to specify the numbering and have multiple indent level
183
251
  $EndLegislativeList
184
252
  (to indent, add 2 spaces)
185
253
 
186
- ## Abbreviations
187
-
188
- Abbreviations can be defined at the end of the document, and any occurrences elswhere in the document will wrapped in an `<abbr>` tag. They are parsed in the order in which they are defined, so `PCSOs` should be defined before `PCSO`, for example.
254
+ ## Priority Lists
189
255
 
190
- Special rules apply if you’re exporting a vehicle outside the EU.
256
+ For lists where you want to specify a number of items to be highlighted as priority.
191
257
 
192
- *[EU]:European Union
258
+ $PriorityList:3
259
+ - Item 1
260
+ - Item 2
261
+ - Item 3
262
+ - Item 4
263
+ - Item 5
193
264
 
194
- becomes
265
+ creates a list with priority items flagged with a class
195
266
 
196
- <p>Special rules apply if you’re exporting a vehicle outside the <abbr title="European Union">EU</abbr>.</p>
267
+ <ul>
268
+ <li class="primary-item">Item 1</li>
269
+ <li class="primary-item">Item 2</li>
270
+ <li class="primary-item">Item 3</li>
271
+ <li>Item 4</li>
272
+ <li>Item 5</li>
273
+ </ul>
197
274
 
198
275
  ## Devolved content
199
276
 
@@ -203,3 +280,190 @@ becomes
203
280
  :wales:content goes here:wales:
204
281
  :northern-ireland:content goes here:northern-ireland:
205
282
  :england-wales:content goes here:england-wales:
283
+
284
+ will create a box for the specified locality
285
+
286
+ <div class="devolved-content england">
287
+ <p class="devolved-header">This section applies to England</p>
288
+ <div class="devolved-body">
289
+ <p>content goes here</p>
290
+ </div>
291
+ </div>
292
+
293
+ ## Barcharts
294
+
295
+ For when you want a table to be progressively enhanced by Javascript to be
296
+ rendered as a bar chart.
297
+
298
+ |col|
299
+ |---|
300
+ |val|
301
+ {barchart}
302
+
303
+ will be rendered as
304
+ <table class="js-barchart-table mc-auto-outdent">
305
+ <thead>
306
+ <tr>
307
+ <th>col</th>
308
+ </tr>
309
+ </thead>
310
+ <tbody>
311
+ <tr>
312
+ <td>val</td>
313
+ </tr>
314
+ </tbody>
315
+ </table>
316
+
317
+ ## Embedded Content
318
+
319
+ Embedded content allows authors to reference a supporting item of a document by
320
+ referencing an id. The details of this content is passed to the publishing
321
+ application to govspeak at the time of rendering.
322
+
323
+ ### Attachments
324
+
325
+ To create an attachment callout
326
+
327
+ [embed:attachment:2b4d92f3-f8cd-4284-aaaa-25b3a640d26c]
328
+
329
+ with options provided
330
+
331
+ {
332
+ attachments: [
333
+ {
334
+ id: 123,
335
+ content_id: "2b4d92f3-f8cd-4284-aaaa-25b3a640d26c",
336
+ title: "Attachment Title",
337
+ url: "http://example.com/test.pdf",
338
+ order_url: "http://example.com/order",
339
+ price: 12.3,
340
+ isbn: "isbn-123",
341
+ attachment?: true,
342
+ }
343
+ ]
344
+ }
345
+
346
+ will output an attachment box
347
+
348
+ <section class="attachment embedded">
349
+ <div class="attachment-thumb">
350
+ <a href="http://example.com/test.pdf" aria-hidden="true" class="embedded"><img src="/images/pub-cover.png" alt="Pub cover"></a>
351
+ </div>
352
+ <div class="attachment-details">
353
+ <h2 class="title">
354
+ <a href="http://example.com/test.pdf" aria-describedby="attachment-123-accessibility-help">Attachment Title</a>
355
+ </h2>
356
+ <p class="metadata">
357
+ <span class="references">Ref: ISBN <span class="isbn">isbn-123</span></span>
358
+ </p>
359
+ <p>
360
+ <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>)
361
+ </p>
362
+ </div>
363
+ </section>
364
+
365
+ ### Inline Attachment
366
+
367
+ Attachments can be linked to inline as well
368
+
369
+ Details referenced in [embed:attachments:inline:34f6dda0-21b1-4e78-8120-3ff4dcea522d]
370
+
371
+ with options provided
372
+
373
+ {
374
+ attachments: [
375
+ {
376
+ content_id: "34f6dda0-21b1-4e78-8120-3ff4dcea522d",
377
+ title: "My Thorough Study",
378
+ url: "http://example.com/my-thorough-study.pdf",
379
+ }
380
+ ]
381
+ }
382
+
383
+ will output an attachment within a block of text
384
+
385
+ <p>Details referenced in <span class="attachment-inline"><a href="http://example.com/my-thorough-study.pdf">My Thorough Study</a></span>
386
+
387
+ ### Image Attachments
388
+
389
+ Attachments can be used to embed an image within the document
390
+
391
+ [embed:attachments:image:45ee0eea-bc53-4f14-81eb-9e75d33c4d5e]
392
+
393
+ with options provided
394
+
395
+ {
396
+ attachments: [
397
+ {
398
+ content_id: "45ee0eea-bc53-4f14-81eb-9e75d33c4d5e",
399
+ title: "A lovely landscape",
400
+ url: "http://example.com/lovely-landscape.jpg",
401
+ }
402
+ ]
403
+ }
404
+
405
+ will output a image section
406
+
407
+ <figure class="image embedded"><div class="img"><img src="http://example.com/lovely-landscape.jpg" alt="A Lovely Landscape"></div></figure>
408
+
409
+ ### Link
410
+
411
+ Links to different documents can be embedded so they change when the documents
412
+ they reference change.
413
+
414
+ A link to [embed:link:c636b433-1e5c-46d4-96b0-b5a168fac26c]
415
+
416
+ with options provided
417
+
418
+ {
419
+ links: [
420
+ {
421
+ url: "http://example.com",
422
+ title: "An excellent website",
423
+ }
424
+ ]
425
+ }
426
+
427
+ will output
428
+
429
+ <p>A link to <a href="http://example.com">An excellent website</a></p>
430
+
431
+ ### Contact
432
+
433
+ [Contact:df62690f-34a0-4840-a7fa-4ef5acc18666]
434
+
435
+ with options provided
436
+
437
+ {
438
+ contacts: [
439
+ {
440
+ id: 123,
441
+ content_id: "df62690f-34a0-4840-a7fa-4ef5acc18666",
442
+ title: "Government Digital Service",
443
+ email: "people@digital.cabinet-office.gov.uk",
444
+ contact_numbers: [
445
+ { label: "helpdesk", number: "+4412345 67890"}
446
+ ]
447
+ }
448
+ ]
449
+ }
450
+
451
+ will output
452
+
453
+ <div id="contact_123" class="contact">
454
+ <div class="content">
455
+ <h3>Government Digital Service</h3>
456
+ <div class="vcard contact-inner">
457
+ <div class="email-url-number">
458
+ <p class="email">
459
+ <span class="type">Email</span>
460
+ <a href="mailto:people@digital.cabinet-office.gov.uk" class="email">people@digital.cabinet-office.gov.uk</a>
461
+ </p>
462
+ <p class="tel">
463
+ <span class="type">helpdesk</span>
464
+ +4412345 67890
465
+ </p>
466
+ </div>
467
+ </div>
468
+ </div>
469
+ </div>
@@ -1,6 +1,9 @@
1
- require 'kramdown'
2
1
  require 'active_support/core_ext/hash'
3
2
  require 'active_support/core_ext/array'
3
+ require 'erb'
4
+ require 'htmlentities'
5
+ require 'kramdown'
6
+ require 'kramdown/parser/kramdown_with_automatic_external_links'
4
7
  require 'govspeak/header_extractor'
5
8
  require 'govspeak/structured_header_extractor'
6
9
  require 'govspeak/html_validator'
@@ -8,12 +11,9 @@ require 'govspeak/html_sanitizer'
8
11
  require 'govspeak/kramdown_overrides'
9
12
  require 'govspeak/blockquote_extra_quote_remover'
10
13
  require 'govspeak/post_processor'
11
- require 'kramdown/parser/kramdown_with_automatic_external_links'
12
- require 'htmlentities'
13
- require 'presenters/attachment_presenter'
14
- require 'presenters/contact_presenter'
15
- require 'presenters/h_card_presenter'
16
- require 'erb'
14
+ require 'govspeak/presenters/attachment_presenter'
15
+ require 'govspeak/presenters/contact_presenter'
16
+ require 'govspeak/presenters/h_card_presenter'
17
17
 
18
18
  module Govspeak
19
19
 
@@ -32,7 +32,7 @@ module Govspeak
32
32
  end
33
33
 
34
34
  def initialize(source, options = {})
35
- options.deep_symbolize_keys!
35
+ options = options.dup.deep_symbolize_keys
36
36
  @source = source ? source.dup : ""
37
37
  @images = options.delete(:images) || []
38
38
  @attachments = Array.wrap(options.delete(:attachments))
@@ -303,7 +303,7 @@ module Govspeak
303
303
  link = links.detect { |l| l[:content_id].match(content_id) }
304
304
  next "" unless link
305
305
  if link[:url]
306
- %Q{<a href="#{encode(link[:url])}">#{link[:title]}</a>}
306
+ "[#{link[:title]}](#{link[:url]})"
307
307
  else
308
308
  link[:title]
309
309
  end
@@ -0,0 +1,288 @@
1
+ require "action_view"
2
+ require "money"
3
+ require "htmlentities"
4
+
5
+ module Govspeak
6
+ class AttachmentPresenter
7
+ attr_reader :attachment
8
+ include ActionView::Helpers::TagHelper
9
+ include ActionView::Helpers::NumberHelper
10
+ include ActionView::Helpers::AssetTagHelper
11
+ include ActionView::Helpers::TextHelper
12
+
13
+ def initialize(attachment)
14
+ @attachment = attachment
15
+ end
16
+
17
+ def id
18
+ attachment[:id]
19
+ end
20
+
21
+ def order_url
22
+ attachment[:order_url]
23
+ end
24
+
25
+ def opendocument?
26
+ attachment[:opendocument?]
27
+ end
28
+
29
+ def url
30
+ attachment[:url]
31
+ end
32
+
33
+ def external?
34
+ attachment[:external?]
35
+ end
36
+
37
+ def price
38
+ return unless attachment[:price]
39
+ Money.from_amount(attachment[:price], 'GBP').format
40
+ end
41
+
42
+ def accessible?
43
+ attachment[:accessible?]
44
+ end
45
+
46
+ def thumbnail_link
47
+ return if hide_thumbnail?
48
+ return if previewable?
49
+ link(attachment_thumbnail, url, "aria-hidden" => "true", "class" => attachment_class)
50
+ end
51
+
52
+ def help_block_toggle_id
53
+ "attachment-#{id}-accessibility-request"
54
+ end
55
+
56
+ def section_class
57
+ attachment[:external?] ? "hosted-externally" : "embedded"
58
+ end
59
+
60
+ def mail_to(email_address, name, options = {})
61
+ query_string = options.slice(:subject, :body).map { |k, v| "#{urlencode(k)}=#{urlencode(v)}" }.join("&")
62
+ "<a href='mailto:#{encode(email_address)}?#{encode(query_string)}'>#{name}</a>"
63
+ end
64
+
65
+ def alternative_format_order_link
66
+ attachment_info = []
67
+ attachment_info << " Title: #{title}"
68
+ attachment_info << " Original format: #{file_extension}" if file_extension.present?
69
+ attachment_info << " ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
70
+ attachment_info << " Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
71
+ attachment_info << " Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
72
+ if attachment[:hoc_paper_number].present?
73
+ attachment_info << " House of Commons paper number: #{attachment[:hoc_paper_number]}"
74
+ attachment_info << " Parliamentary session: #{attachment[:parliamentary_session]}"
75
+ end
76
+
77
+ options = {
78
+ subject: "Request for '#{title}' in an alternative format",
79
+ body: body_for_mail(attachment_info)
80
+ }
81
+
82
+ mail_to(alternative_format_contact_email, alternative_format_contact_email, options)
83
+ end
84
+
85
+ def body_for_mail(attachment_info)
86
+ <<-END
87
+ Details of document required:
88
+
89
+ #{attachment_info.join("\n")}
90
+
91
+ Please tell us:
92
+
93
+ 1. What makes this format unsuitable for you?
94
+ 2. What format you would prefer?
95
+ END
96
+ end
97
+
98
+ def alternative_format_contact_email
99
+ "govuk-feedback@digital.cabinet-office.gov.uk"
100
+ end
101
+
102
+ # FIXME: usage of image_tag will cause these to render at /images/ which seems
103
+ # very host dependent. I assume this will need links to static urls.
104
+ def attachment_thumbnail
105
+ if file_extension == "pdf" && attachment[:thumbnail_url]
106
+ image_tag(attachment[:thumbnail_url])
107
+ elsif file_extension == "html"
108
+ image_tag('pub-cover-html.png')
109
+ elsif %w{doc docx odt}.include?(file_extension)
110
+ image_tag('pub-cover-doc.png')
111
+ elsif %w{xls xlsx ods csv}.include?(file_extension)
112
+ image_tag('pub-cover-spreadsheet.png')
113
+ else
114
+ image_tag('pub-cover.png')
115
+ end
116
+ end
117
+
118
+ def reference
119
+ ref = []
120
+ if attachment[:isbn].present?
121
+ ref << "ISBN " + content_tag(:span, attachment[:isbn], class: "isbn")
122
+ end
123
+
124
+ if attachment[:unique_reference].present?
125
+ ref << content_tag(:span, attachment[:unique_reference], class: "unique_reference")
126
+ end
127
+
128
+ if attachment[:command_paper_number].present?
129
+ ref << content_tag(:span, attachment[:command_paper_number], class: "command_paper_number")
130
+ end
131
+
132
+ if attachment[:hoc_paper_number].present?
133
+ ref << content_tag(:span, "HC #{attachment[:hoc_paper_number]}", class: 'house_of_commons_paper_number') + ' ' +
134
+ content_tag(:span, attachment[:parliamentary_session], class: 'parliamentary_session')
135
+ end
136
+
137
+ ref.join(', ').html_safe
138
+ end
139
+
140
+ # FIXME this has english in it so will cause problems if the locale is not en
141
+ def references_for_title
142
+ references = []
143
+ references << "ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
144
+ references << "Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
145
+ references << "Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
146
+ references << "HC: #{attachment[:hoc_paper_number]} #{attachment[:parliamentary_session]}" if attachment[:hoc_paper_number].present?
147
+ prefix = references.size == 1 ? "and its reference" : "and its references"
148
+ references.any? ? ", #{prefix} (" + references.join(", ") + ")" : ""
149
+ end
150
+
151
+ def references?
152
+ !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?
153
+ end
154
+
155
+ def attachment_class
156
+ attachment[:external?] ? "hosted-externally" : "embedded"
157
+ end
158
+
159
+ def unnumbered_paper?
160
+ attachment[:unnumbered_command_paper?] || attachment[:unnumbered_hoc_paper?]
161
+ end
162
+
163
+ def unnumbered_command_paper?
164
+ attachment[:unnumbered_command_paper?]
165
+ end
166
+
167
+ def download_link
168
+ options = {}
169
+ options[:title] = number_to_human_size(attachment[:file_size]) if attachment[:file_size].present?
170
+ link("<strong>Download #{file_extension.upcase}</strong>", attachment[:url], options)
171
+ end
172
+
173
+ def attachment_attributes
174
+ attributes = []
175
+ if file_extension == "html"
176
+ attributes << content_tag(:span, 'HTML', class: 'type')
177
+ elsif attachment[:external?]
178
+ attributes << content_tag(:span, url, class: 'url')
179
+ else
180
+ attributes << content_tag(:span, humanized_content_type(file_extension), class: 'type') if file_extension
181
+ attributes << content_tag(:span, number_to_human_size(attachment[:file_size]), class: 'file-size') if attachment[:file_size]
182
+ attributes << content_tag(:span, pluralize(attachment[:number_of_pages], "page"), class: 'page-length') if attachment[:number_of_pages]
183
+ end
184
+ attributes.join(', ').html_safe
185
+ end
186
+
187
+ def preview_url
188
+ url + '/preview'
189
+ end
190
+
191
+ MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE = "MS Word Document".freeze
192
+ MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE = "MS Excel Spreadsheet".freeze
193
+ MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE = "MS Powerpoint Presentation".freeze
194
+
195
+ def file_abbr_tag(abbr, title)
196
+ content_tag(:abbr, abbr, title: title)
197
+ end
198
+
199
+ def humanized_content_type(file_extension)
200
+ file_extension_vs_humanized_content_type = {
201
+ "chm" => file_abbr_tag('CHM', 'Microsoft Compiled HTML Help'),
202
+ "csv" => file_abbr_tag('CSV', 'Comma-separated Values'),
203
+ "diff" => file_abbr_tag('DIFF', 'Plain text differences'),
204
+ "doc" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
205
+ "docx" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
206
+ "dot" => file_abbr_tag('DOT', 'MS Word Document Template'),
207
+ "dxf" => file_abbr_tag('DXF', 'AutoCAD Drawing Exchange Format'),
208
+ "eps" => file_abbr_tag('EPS', 'Encapsulated PostScript'),
209
+ "gif" => file_abbr_tag('GIF', 'Graphics Interchange Format'),
210
+ "gml" => file_abbr_tag('GML', 'Geography Markup Language'),
211
+ "html" => file_abbr_tag('HTML', 'Hypertext Markup Language'),
212
+ "ics" => file_abbr_tag('ICS', 'iCalendar file'),
213
+ "jpg" => "JPEG",
214
+ "odp" => file_abbr_tag('ODP', 'OpenDocument Presentation'),
215
+ "ods" => file_abbr_tag('ODS', 'OpenDocument Spreadsheet'),
216
+ "odt" => file_abbr_tag('ODT', 'OpenDocument Text document'),
217
+ "pdf" => file_abbr_tag('PDF', 'Portable Document Format'),
218
+ "png" => file_abbr_tag('PNG', 'Portable Network Graphic'),
219
+ "ppt" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
220
+ "pptx" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
221
+ "ps" => file_abbr_tag('PS', 'PostScript'),
222
+ "rdf" => file_abbr_tag('RDF', 'Resource Description Framework'),
223
+ "rtf" => file_abbr_tag('RTF', 'Rich Text Format'),
224
+ "sch" => file_abbr_tag('SCH', 'XML based Schematic'),
225
+ "txt" => "Plain text",
226
+ "wsdl" => file_abbr_tag('WSDL', 'Web Services Description Language'),
227
+ "xls" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
228
+ "xlsm" => file_abbr_tag('XLSM', 'MS Excel Macro-Enabled Workbook'),
229
+ "xlsx" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
230
+ "xlt" => file_abbr_tag('XLT', 'MS Excel Spreadsheet Template'),
231
+ "xsd" => file_abbr_tag('XSD', 'XML Schema'),
232
+ "xslt" => file_abbr_tag('XSLT', 'Extensible Stylesheet Language Transformation'),
233
+ "zip" => file_abbr_tag('ZIP', 'Zip archive'),
234
+ }
235
+ file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, '')
236
+ end
237
+
238
+ def previewable?
239
+ file_extension == "csv"
240
+ end
241
+
242
+ def title
243
+ attachment[:title]
244
+ end
245
+
246
+ def file_extension
247
+ # Note: this is a separate parameter rather than being calculated from the
248
+ # filename because at the time of writing not all apps were using the effects
249
+ # of this field.
250
+ attachment[:file_extension]
251
+ end
252
+
253
+ def hide_thumbnail?
254
+ defined?(hide_thumbnail) && hide_thumbnail
255
+ end
256
+
257
+ def attachment_details
258
+ return if previewable?
259
+ link(title, url, title_link_options)
260
+ end
261
+
262
+ def title_link_options
263
+ title_link_options = {}
264
+ title_link_options["rel"] = "external" if attachment[:external?]
265
+ title_link_options["aria-describedby"] = help_block_id unless attachment[:accessible?]
266
+ title_link_options
267
+ end
268
+
269
+ def help_block_id
270
+ "attachment-#{id}-accessibility-help"
271
+ end
272
+
273
+ def link(body, url, options = {})
274
+ options_str = options.map { |k, v| %{#{encode(k)}="#{encode(v)}"} }.join(" ")
275
+ %{<a href="#{encode(url)}" #{options_str}>#{body}</a>}
276
+ end
277
+
278
+ private
279
+
280
+ def encode(text)
281
+ HTMLEntities.new.encode(text)
282
+ end
283
+
284
+ def urlencode(text)
285
+ ERB::Util.url_encode(text)
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_support/core_ext/array'
2
+ require 'ostruct'
3
+
4
+ module Govspeak
5
+ class ContactPresenter
6
+ attr_reader :contact
7
+ delegate :id, :content_id, :title, :recipient, :street_address, :postal_code,
8
+ :locality, :region, :country_code, :country_name, :email, :contact_form_url,
9
+ :comments, :worldwide_organisation_path, to: :contact
10
+
11
+ def initialize(contact)
12
+ @contact = OpenStruct.new(contact)
13
+ end
14
+
15
+ def contact_numbers
16
+ Array.wrap(contact[:contact_numbers])
17
+ end
18
+
19
+ def has_postal_address?
20
+ recipient.present? || street_address.present? || locality.present? ||
21
+ region.present? || postal_code.present?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ module Govspeak
2
+ class HCardPresenter
3
+ def self.from_contact(contact)
4
+ new(contact_properties(contact), contact.country_code)
5
+ end
6
+
7
+ def self.contact_properties(contact)
8
+ { 'fn' => contact.recipient,
9
+ 'street-address' => contact.street_address,
10
+ 'postal-code' => contact.postal_code,
11
+ 'locality' => contact.locality,
12
+ 'region' => contact.region,
13
+ 'country-name' => country_name(contact) }
14
+ end
15
+
16
+ def self.country_name(contact)
17
+ contact.country_name unless contact.country_code == 'GB'
18
+ end
19
+
20
+ def self.property_keys
21
+ ['fn', 'street-address', 'postal-code', 'locality', 'region', 'country-name']
22
+ end
23
+
24
+ def self.address_formats
25
+ @address_formats ||= YAML.load_file('config/address_formats.yml')
26
+ end
27
+
28
+ attr_reader :properties, :country_code
29
+
30
+ def initialize(properties, country_code)
31
+ @properties = properties
32
+ @country_code = country_code
33
+ end
34
+
35
+ def render
36
+ "<p class=\"adr\">\n#{interpolate_address_template}\n</p>\n".html_safe
37
+ end
38
+
39
+ def interpolate_address_property(property_name)
40
+ value = properties[property_name]
41
+
42
+ if value.present?
43
+ "<span class=\"#{property_name}\">#{ERB::Util.html_escape(value)}</span>"
44
+ else
45
+ ""
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def interpolate_address_template
52
+ address = address_template
53
+
54
+ self.class.property_keys.each do |key|
55
+ address.gsub!(/\{\{#{key}\}\}/, interpolate_address_property(key))
56
+ end
57
+
58
+ address.gsub(/^\n/, '') # get rid of blank lines
59
+ .strip # get rid of any trailing whitespace
60
+ .gsub(/\n/, "<br />\n") # add break tags where appropriate
61
+ end
62
+
63
+ def address_template
64
+ (self.class.address_formats[country_code.to_s.downcase] || default_format_string).dup
65
+ end
66
+
67
+ def default_format_string
68
+ self.class.address_formats['gb']
69
+ end
70
+ end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "5.0.0"
2
+ VERSION = "5.0.1"
3
3
  end
@@ -6,27 +6,38 @@ class GovspeakLinkTest < Minitest::Test
6
6
  test "embedded link with link provided" do
7
7
  link = {
8
8
  content_id: "5572fee5-f38f-4641-8ffa-64fed9230ad4",
9
- url: "https://www.example.com/",
10
- title: "Example website"
9
+ url: "https://www.gov.uk/example",
10
+ title: "Example page",
11
11
  }
12
12
  govspeak = "[embed:link:5572fee5-f38f-4641-8ffa-64fed9230ad4]"
13
13
  rendered = Govspeak::Document.new(govspeak, links: link).to_html
14
- expected = %r{<a href="https://www.example.com/">Example website</a>}
14
+ expected = %r{<a href="https://www.gov.uk/example">Example page</a>}
15
15
  assert_match(expected, rendered)
16
16
  end
17
17
 
18
18
  test "embedded link with markdown title" do
19
19
  link = {
20
20
  content_id: "5572fee5-f38f-4641-8ffa-64fed9230ad4",
21
- url: "https://www.example.com/",
22
- title: "**Example website**"
21
+ url: "https://www.gov.uk/example",
22
+ title: "**Example page**",
23
23
  }
24
24
  govspeak = "[embed:link:5572fee5-f38f-4641-8ffa-64fed9230ad4]"
25
25
  rendered = Govspeak::Document.new(govspeak, links: link).to_html
26
- expected = %r{<a href="https://www.example.com/"><strong>Example website</strong></a>}
26
+ expected = %r{<a href="https://www.gov.uk/example"><strong>Example page</strong></a>}
27
27
  assert_match(expected, rendered)
28
28
  end
29
29
 
30
+ test "embedded link with external url" do
31
+ link = {
32
+ content_id: "5572fee5-f38f-4641-8ffa-64fed9230ad4",
33
+ url: "https://www.example.com/",
34
+ title: "Example website",
35
+ }
36
+ govspeak = "[embed:link:5572fee5-f38f-4641-8ffa-64fed9230ad4]"
37
+ rendered = Govspeak::Document.new(govspeak, links: link).to_html
38
+ expected = %r{<a rel="external" href="https://www.example.com/">Example website</a>}
39
+ assert_match(expected, rendered)
40
+ end
30
41
  test "embedded link with url not provided" do
31
42
  link = {
32
43
  content_id: "e510f1c1-4862-4333-889c-8d3acd443fbf",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govspeak
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Griffiths
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-09-30 00:00:00.000000000 Z
12
+ date: 2016-10-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: kramdown
@@ -251,12 +251,12 @@ files:
251
251
  - lib/govspeak/html_validator.rb
252
252
  - lib/govspeak/kramdown_overrides.rb
253
253
  - lib/govspeak/post_processor.rb
254
+ - lib/govspeak/presenters/attachment_presenter.rb
255
+ - lib/govspeak/presenters/contact_presenter.rb
256
+ - lib/govspeak/presenters/h_card_presenter.rb
254
257
  - lib/govspeak/structured_header_extractor.rb
255
258
  - lib/govspeak/version.rb
256
259
  - lib/kramdown/parser/kramdown_with_automatic_external_links.rb
257
- - lib/presenters/attachment_presenter.rb
258
- - lib/presenters/contact_presenter.rb
259
- - lib/presenters/h_card_presenter.rb
260
260
  - lib/templates/attachment.html.erb
261
261
  - lib/templates/contact.html.erb
262
262
  - lib/templates/inline_attachment.html.erb
@@ -1,286 +0,0 @@
1
- require "action_view"
2
- require "money"
3
- require "htmlentities"
4
-
5
- class AttachmentPresenter
6
- attr_reader :attachment
7
- include ActionView::Helpers::TagHelper
8
- include ActionView::Helpers::NumberHelper
9
- include ActionView::Helpers::AssetTagHelper
10
- include ActionView::Helpers::TextHelper
11
-
12
- def initialize(attachment)
13
- @attachment = attachment
14
- end
15
-
16
- def id
17
- attachment[:id]
18
- end
19
-
20
- def order_url
21
- attachment[:order_url]
22
- end
23
-
24
- def opendocument?
25
- attachment[:opendocument?]
26
- end
27
-
28
- def url
29
- attachment[:url]
30
- end
31
-
32
- def external?
33
- attachment[:external?]
34
- end
35
-
36
- def price
37
- return unless attachment[:price]
38
- Money.from_amount(attachment[:price], 'GBP').format
39
- end
40
-
41
- def accessible?
42
- attachment[:accessible?]
43
- end
44
-
45
- def thumbnail_link
46
- return if hide_thumbnail?
47
- return if previewable?
48
- link(attachment_thumbnail, url, "aria-hidden" => "true", "class" => attachment_class)
49
- end
50
-
51
- def help_block_toggle_id
52
- "attachment-#{id}-accessibility-request"
53
- end
54
-
55
- def section_class
56
- attachment[:external?] ? "hosted-externally" : "embedded"
57
- end
58
-
59
- def mail_to(email_address, name, options = {})
60
- query_string = options.slice(:subject, :body).map { |k, v| "#{urlencode(k)}=#{urlencode(v)}" }.join("&")
61
- "<a href='mailto:#{encode(email_address)}?#{encode(query_string)}'>#{name}</a>"
62
- end
63
-
64
- def alternative_format_order_link
65
- attachment_info = []
66
- attachment_info << " Title: #{title}"
67
- attachment_info << " Original format: #{file_extension}" if file_extension.present?
68
- attachment_info << " ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
69
- attachment_info << " Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
70
- attachment_info << " Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
71
- if attachment[:hoc_paper_number].present?
72
- attachment_info << " House of Commons paper number: #{attachment[:hoc_paper_number]}"
73
- attachment_info << " Parliamentary session: #{attachment[:parliamentary_session]}"
74
- end
75
-
76
- options = {
77
- subject: "Request for '#{title}' in an alternative format",
78
- body: body_for_mail(attachment_info)
79
- }
80
-
81
- mail_to(alternative_format_contact_email, alternative_format_contact_email, options)
82
- end
83
-
84
- def body_for_mail(attachment_info)
85
- <<-END
86
- Details of document required:
87
-
88
- #{attachment_info.join("\n")}
89
-
90
- Please tell us:
91
-
92
- 1. What makes this format unsuitable for you?
93
- 2. What format you would prefer?
94
- END
95
- end
96
-
97
- def alternative_format_contact_email
98
- "govuk-feedback@digital.cabinet-office.gov.uk"
99
- end
100
-
101
- # FIXME: usage of image_tag will cause these to render at /images/ which seems
102
- # very host dependent. I assume this will need links to static urls.
103
- def attachment_thumbnail
104
- if file_extension == "pdf" && attachment[:thumbnail_url]
105
- image_tag(attachment[:thumbnail_url])
106
- elsif file_extension == "html"
107
- image_tag('pub-cover-html.png')
108
- elsif %w{doc docx odt}.include?(file_extension)
109
- image_tag('pub-cover-doc.png')
110
- elsif %w{xls xlsx ods csv}.include?(file_extension)
111
- image_tag('pub-cover-spreadsheet.png')
112
- else
113
- image_tag('pub-cover.png')
114
- end
115
- end
116
-
117
- def reference
118
- ref = []
119
- if attachment[:isbn].present?
120
- ref << "ISBN " + content_tag(:span, attachment[:isbn], class: "isbn")
121
- end
122
-
123
- if attachment[:unique_reference].present?
124
- ref << content_tag(:span, attachment[:unique_reference], class: "unique_reference")
125
- end
126
-
127
- if attachment[:command_paper_number].present?
128
- ref << content_tag(:span, attachment[:command_paper_number], class: "command_paper_number")
129
- end
130
-
131
- if attachment[:hoc_paper_number].present?
132
- ref << content_tag(:span, "HC #{attachment[:hoc_paper_number]}", class: 'house_of_commons_paper_number') + ' ' +
133
- content_tag(:span, attachment[:parliamentary_session], class: 'parliamentary_session')
134
- end
135
-
136
- ref.join(', ').html_safe
137
- end
138
-
139
- # FIXME this has english in it so will cause problems if the locale is not en
140
- def references_for_title
141
- references = []
142
- references << "ISBN: #{attachment[:isbn]}" if attachment[:isbn].present?
143
- references << "Unique reference: #{attachment[:unique_reference]}" if attachment[:unique_reference].present?
144
- references << "Command paper number: #{attachment[:command_paper_number]}" if attachment[:command_paper_number].present?
145
- references << "HC: #{attachment[:hoc_paper_number]} #{attachment[:parliamentary_session]}" if attachment[:hoc_paper_number].present?
146
- prefix = references.size == 1 ? "and its reference" : "and its references"
147
- references.any? ? ", #{prefix} (" + references.join(", ") + ")" : ""
148
- end
149
-
150
- def references?
151
- !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?
152
- end
153
-
154
- def attachment_class
155
- attachment[:external?] ? "hosted-externally" : "embedded"
156
- end
157
-
158
- def unnumbered_paper?
159
- attachment[:unnumbered_command_paper?] || attachment[:unnumbered_hoc_paper?]
160
- end
161
-
162
- def unnumbered_command_paper?
163
- attachment[:unnumbered_command_paper?]
164
- end
165
-
166
- def download_link
167
- options = {}
168
- options[:title] = number_to_human_size(attachment[:file_size]) if attachment[:file_size].present?
169
- link("<strong>Download #{file_extension.upcase}</strong>", attachment[:url], options)
170
- end
171
-
172
- def attachment_attributes
173
- attributes = []
174
- if file_extension == "html"
175
- attributes << content_tag(:span, 'HTML', class: 'type')
176
- elsif attachment[:external?]
177
- attributes << content_tag(:span, url, class: 'url')
178
- else
179
- attributes << content_tag(:span, humanized_content_type(file_extension), class: 'type') if file_extension
180
- attributes << content_tag(:span, number_to_human_size(attachment[:file_size]), class: 'file-size') if attachment[:file_size]
181
- attributes << content_tag(:span, pluralize(attachment[:number_of_pages], "page"), class: 'page-length') if attachment[:number_of_pages]
182
- end
183
- attributes.join(', ').html_safe
184
- end
185
-
186
- def preview_url
187
- url + '/preview'
188
- end
189
-
190
- MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE = "MS Word Document"
191
- MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE = "MS Excel Spreadsheet"
192
- MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE = "MS Powerpoint Presentation"
193
-
194
- def file_abbr_tag(abbr, title)
195
- content_tag(:abbr, abbr, title: title)
196
- end
197
-
198
- def humanized_content_type(file_extension)
199
- file_extension_vs_humanized_content_type = {
200
- "chm" => file_abbr_tag('CHM', 'Microsoft Compiled HTML Help'),
201
- "csv" => file_abbr_tag('CSV', 'Comma-separated Values'),
202
- "diff" => file_abbr_tag('DIFF', 'Plain text differences'),
203
- "doc" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
204
- "docx" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
205
- "dot" => file_abbr_tag('DOT', 'MS Word Document Template'),
206
- "dxf" => file_abbr_tag('DXF', 'AutoCAD Drawing Exchange Format'),
207
- "eps" => file_abbr_tag('EPS', 'Encapsulated PostScript'),
208
- "gif" => file_abbr_tag('GIF', 'Graphics Interchange Format'),
209
- "gml" => file_abbr_tag('GML', 'Geography Markup Language'),
210
- "html" => file_abbr_tag('HTML', 'Hypertext Markup Language'),
211
- "ics" => file_abbr_tag('ICS', 'iCalendar file'),
212
- "jpg" => "JPEG",
213
- "odp" => file_abbr_tag('ODP', 'OpenDocument Presentation'),
214
- "ods" => file_abbr_tag('ODS', 'OpenDocument Spreadsheet'),
215
- "odt" => file_abbr_tag('ODT', 'OpenDocument Text document'),
216
- "pdf" => file_abbr_tag('PDF', 'Portable Document Format'),
217
- "png" => file_abbr_tag('PNG', 'Portable Network Graphic'),
218
- "ppt" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
219
- "pptx" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
220
- "ps" => file_abbr_tag('PS', 'PostScript'),
221
- "rdf" => file_abbr_tag('RDF', 'Resource Description Framework'),
222
- "rtf" => file_abbr_tag('RTF', 'Rich Text Format'),
223
- "sch" => file_abbr_tag('SCH', 'XML based Schematic'),
224
- "txt" => "Plain text",
225
- "wsdl" => file_abbr_tag('WSDL', 'Web Services Description Language'),
226
- "xls" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
227
- "xlsm" => file_abbr_tag('XLSM', 'MS Excel Macro-Enabled Workbook'),
228
- "xlsx" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
229
- "xlt" => file_abbr_tag('XLT', 'MS Excel Spreadsheet Template'),
230
- "xsd" => file_abbr_tag('XSD', 'XML Schema'),
231
- "xslt" => file_abbr_tag('XSLT', 'Extensible Stylesheet Language Transformation'),
232
- "zip" => file_abbr_tag('ZIP', 'Zip archive'),
233
- }
234
- file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, '')
235
- end
236
-
237
- def previewable?
238
- file_extension == "csv"
239
- end
240
-
241
- def title
242
- attachment[:title]
243
- end
244
-
245
- def file_extension
246
- # Note: this is a separate parameter rather than being calculated from the
247
- # filename because at the time of writing not all apps were using the effects
248
- # of this field.
249
- attachment[:file_extension]
250
- end
251
-
252
- def hide_thumbnail?
253
- defined?(hide_thumbnail) && hide_thumbnail
254
- end
255
-
256
- def attachment_details
257
- return if previewable?
258
- link(title, url, title_link_options)
259
- end
260
-
261
- def title_link_options
262
- title_link_options = {}
263
- title_link_options["rel"] = "external" if attachment[:external?]
264
- title_link_options["aria-describedby"] = help_block_id unless attachment[:accessible?]
265
- title_link_options
266
- end
267
-
268
- def help_block_id
269
- "attachment-#{id}-accessibility-help"
270
- end
271
-
272
- def link(body, url, options = {})
273
- options_str = options.map { |k, v| %{#{encode(k)}="#{encode(v)}"} }.join(" ")
274
- %{<a href="#{encode(url)}" #{options_str}>#{body}</a>}
275
- end
276
-
277
- private
278
-
279
- def encode(text)
280
- HTMLEntities.new.encode(text)
281
- end
282
-
283
- def urlencode(text)
284
- ERB::Util.url_encode(text)
285
- end
286
- end
@@ -1,22 +0,0 @@
1
- require 'active_support/core_ext/array'
2
- require 'ostruct'
3
-
4
- class ContactPresenter
5
- attr_reader :contact
6
- delegate :id, :content_id, :title, :recipient, :street_address, :postal_code,
7
- :locality, :region, :country_code, :country_name, :email, :contact_form_url,
8
- :comments, :worldwide_organisation_path, to: :contact
9
-
10
- def initialize(contact)
11
- @contact = OpenStruct.new(contact)
12
- end
13
-
14
- def contact_numbers
15
- Array.wrap(contact[:contact_numbers])
16
- end
17
-
18
- def has_postal_address?
19
- recipient.present? || street_address.present? || locality.present? ||
20
- region.present? || postal_code.present?
21
- end
22
- end
@@ -1,69 +0,0 @@
1
- class HCardPresenter
2
- def self.from_contact(contact)
3
- new(contact_properties(contact), contact.country_code)
4
- end
5
-
6
- def self.contact_properties(contact)
7
- { 'fn' => contact.recipient,
8
- 'street-address' => contact.street_address,
9
- 'postal-code' => contact.postal_code,
10
- 'locality' => contact.locality,
11
- 'region' => contact.region,
12
- 'country-name' => country_name(contact) }
13
- end
14
-
15
- def self.country_name(contact)
16
- contact.country_name unless contact.country_code == 'GB'
17
- end
18
-
19
- def self.property_keys
20
- ['fn', 'street-address', 'postal-code', 'locality', 'region', 'country-name']
21
- end
22
-
23
- def self.address_formats
24
- @address_formats ||= YAML.load_file('config/address_formats.yml')
25
- end
26
-
27
- attr_reader :properties, :country_code
28
-
29
- def initialize(properties, country_code)
30
- @properties = properties
31
- @country_code = country_code
32
- end
33
-
34
- def render
35
- "<p class=\"adr\">\n#{interpolate_address_template}\n</p>\n".html_safe
36
- end
37
-
38
- def interpolate_address_property(property_name)
39
- value = properties[property_name]
40
-
41
- if value.present?
42
- "<span class=\"#{property_name}\">#{ERB::Util.html_escape(value)}</span>"
43
- else
44
- ""
45
- end
46
- end
47
-
48
- private
49
-
50
- def interpolate_address_template
51
- address = address_template
52
-
53
- self.class.property_keys.each do |key|
54
- address.gsub!(/\{\{#{key}\}\}/, interpolate_address_property(key))
55
- end
56
-
57
- address.gsub(/^\n/, '') # get rid of blank lines
58
- .strip # get rid of any trailing whitespace
59
- .gsub(/\n/, "<br />\n") # add break tags where appropriate
60
- end
61
-
62
- def address_template
63
- (self.class.address_formats[country_code.to_s.downcase] || default_format_string).dup
64
- end
65
-
66
- def default_format_string
67
- self.class.address_formats['gb']
68
- end
69
- end