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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +273 -9
- data/lib/govspeak.rb +9 -9
- data/lib/govspeak/presenters/attachment_presenter.rb +288 -0
- data/lib/govspeak/presenters/contact_presenter.rb +24 -0
- data/lib/govspeak/presenters/h_card_presenter.rb +71 -0
- data/lib/govspeak/version.rb +1 -1
- data/test/govspeak_link_test.rb +17 -6
- metadata +5 -5
- data/lib/presenters/attachment_presenter.rb +0 -286
- data/lib/presenters/contact_presenter.rb +0 -22
- data/lib/presenters/h_card_presenter.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 177a638111a0d8d422e3201f0e3e265218fb1cb9
|
4
|
+
data.tar.gz: 5be0baf8e84b10bfd28f0f5ed6e1c5e292fe4c1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77ca5d6c13299db9221804e96b01fb15412d0c38d3017219304992d225dab4382dec796015fd2533e62e7ad734d6f0199b216b175e4f45d099cc913b8ed24a6a
|
7
|
+
data.tar.gz: b52ed84b08d05f85c44a2a3bb3fe2d86835f2cf9934e9ead9b7c143b9be9fcbea03b84d8af70ca2245426a9ea1676b54a35a2714d7a3e679ad890d1ee87f46d9
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
##
|
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
|
-
|
256
|
+
For lists where you want to specify a number of items to be highlighted as priority.
|
191
257
|
|
192
|
-
|
258
|
+
$PriorityList:3
|
259
|
+
- Item 1
|
260
|
+
- Item 2
|
261
|
+
- Item 3
|
262
|
+
- Item 4
|
263
|
+
- Item 5
|
193
264
|
|
194
|
-
|
265
|
+
creates a list with priority items flagged with a class
|
195
266
|
|
196
|
-
<
|
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>
|
data/lib/govspeak.rb
CHANGED
@@ -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 '
|
12
|
-
require '
|
13
|
-
require 'presenters/
|
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
|
-
|
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
|
data/lib/govspeak/version.rb
CHANGED
data/test/govspeak_link_test.rb
CHANGED
@@ -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.
|
10
|
-
title: "Example
|
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.
|
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.
|
22
|
-
title: "**Example
|
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.
|
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.
|
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-
|
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
|