govspeak 6.5.1 → 6.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +6 -5
- data/Rakefile +6 -3
- data/lib/govspeak.rb +56 -54
- data/lib/govspeak/html_sanitizer.rb +7 -4
- data/lib/govspeak/kramdown_overrides.rb +2 -2
- data/lib/govspeak/link_extractor.rb +3 -3
- data/lib/govspeak/post_processor.rb +25 -5
- data/lib/govspeak/presenters/attachment_presenter.rb +26 -26
- data/lib/govspeak/presenters/image_presenter.rb +2 -2
- data/lib/govspeak/structured_header_extractor.rb +2 -2
- data/lib/govspeak/version.rb +1 -1
- data/lib/kramdown/parser/govuk.rb +5 -6
- data/test/blockquote_extra_quote_remover_test.rb +25 -27
- data/test/govspeak_attachment_link_test.rb +0 -2
- data/test/govspeak_attachment_test.rb +0 -2
- data/test/govspeak_attachments_image_test.rb +2 -4
- data/test/govspeak_attachments_inline_test.rb +4 -6
- data/test/govspeak_button_test.rb +25 -27
- data/test/govspeak_contacts_test.rb +11 -13
- data/test/govspeak_extract_contact_content_ids_test.rb +0 -2
- data/test/govspeak_images_bang_test.rb +30 -32
- data/test/govspeak_images_test.rb +36 -38
- data/test/govspeak_link_extractor_test.rb +1 -1
- data/test/govspeak_link_test.rb +0 -2
- data/test/govspeak_structured_headers_test.rb +5 -4
- data/test/govspeak_table_with_headers_test.rb +67 -20
- data/test/govspeak_test.rb +80 -83
- data/test/govspeak_test_helper.rb +1 -1
- data/test/html_sanitizer_test.rb +9 -1
- data/test/presenters/h_card_presenter_test.rb +0 -2
- data/test/test_helper.rb +6 -2
- metadata +41 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd25c3749cd867192b6243a3d607d83b4b53def14cfb81f9cfaec0f9194052fe
|
4
|
+
data.tar.gz: 9ba6512c5ff9c8a410ea424ea880baef90d0a90e1cc8b660755d3428148bb899
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adc39b260589d92b0ba86e2ee04a513677fece35b50e338ba7716a927b4706c7e2782e13b50a4f5158429621dd2248b40d4dda7804bc176e60d8e73ee3f348d2
|
7
|
+
data.tar.gz: 46a366da4e41699f7bf182967a71e1cf3cc92f06f477b655eadd939b8ca25dfd1088591dd8d1b83f563d9ebcc49b797726c59ce2c72d9ce6b63a8dc3992f7513
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,25 @@
|
|
1
|
+
## 6.5.6
|
2
|
+
|
3
|
+
* Update Kramdown version to 2.3.0 or greater
|
4
|
+
|
5
|
+
## 6.5.5
|
6
|
+
|
7
|
+
* Prevent links in table headers being stripped (PR#187)
|
8
|
+
|
9
|
+
## 6.5.4
|
10
|
+
|
11
|
+
* Require Sanitize to be at least 5.2.1 due to https://nvd.nist.gov/vuln/detail/CVE-2020-4054
|
12
|
+
|
13
|
+
## 6.5.3
|
14
|
+
|
15
|
+
* Use button component for buttons (PR#176)
|
16
|
+
|
17
|
+
## 6.5.2
|
18
|
+
|
19
|
+
* Allow `data` attributes on `div` tags (PR#173)
|
20
|
+
|
1
21
|
## 6.5.1
|
22
|
+
|
2
23
|
* Change unicode testing characters after external gem change
|
3
24
|
* Move from govuk-lint to rubocop-govuk
|
4
25
|
* Allow version 6 of actionview
|
data/README.md
CHANGED
@@ -619,8 +619,7 @@ will output
|
|
619
619
|
|
620
620
|
An accessible way to add button links into content, that can also allow cross domain tracking with [Google Analytics](https://support.google.com/analytics/answer/7372977?hl=en)
|
621
621
|
|
622
|
-
This button component
|
623
|
-
Note: Ideally we'd use the original component directly but this currently isnt possible
|
622
|
+
This button component uses the component from the [components gem](https://components.publishing.service.gov.uk/component-guide/button) for use in govspeak.
|
624
623
|
|
625
624
|
You must use the [link](https://daringfireball.net/projects/markdown/syntax#link) syntax within the button tags.
|
626
625
|
|
@@ -635,7 +634,7 @@ To get the most basic button.
|
|
635
634
|
which outputs
|
636
635
|
|
637
636
|
```html
|
638
|
-
<a role="button" class="button" href="https://gov.uk/random">
|
637
|
+
<a role="button" class="gem-c-button govuk-button" href="https://gov.uk/random">
|
639
638
|
Continue
|
640
639
|
</a>
|
641
640
|
```
|
@@ -649,8 +648,9 @@ To turn a button into a ['Start now' button](https://www.gov.uk/service-manual/d
|
|
649
648
|
which outputs
|
650
649
|
|
651
650
|
```html
|
652
|
-
<a role="button" class="button button-start" href="https://gov.uk/random">
|
651
|
+
<a role="button" class="gem-c-button govuk-button govuk-button--start" href="https://gov.uk/random">
|
653
652
|
Start Now
|
653
|
+
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg>
|
654
654
|
</a>
|
655
655
|
```
|
656
656
|
|
@@ -667,12 +667,13 @@ which outputs
|
|
667
667
|
```html
|
668
668
|
<a
|
669
669
|
role="button"
|
670
|
-
class="button button-start"
|
670
|
+
class="gem-c-button govuk-button govuk-button--start"
|
671
671
|
href="https://example.com/external-service/start-now"
|
672
672
|
data-module="cross-domain-tracking"
|
673
673
|
data-tracking-code="UA-XXXXXX-Y"
|
674
674
|
data-tracking-name="govspeakButtonTracker"
|
675
675
|
>
|
676
676
|
Start Now
|
677
|
+
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg>
|
677
678
|
</a>
|
678
679
|
```
|
data/Rakefile
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require "rake"
|
2
2
|
require "rake/testtask"
|
3
|
+
require "rubocop/rake_task"
|
3
4
|
require "bundler"
|
4
5
|
|
5
6
|
Bundler::GemHelper.install_tasks
|
6
7
|
|
8
|
+
RuboCop::RakeTask.new
|
9
|
+
|
7
10
|
desc "Run basic tests"
|
8
|
-
Rake::TestTask.new("test")
|
11
|
+
Rake::TestTask.new("test") do |t|
|
9
12
|
t.libs << "test"
|
10
13
|
t.pattern = "test/*_test.rb"
|
11
14
|
t.verbose = true
|
12
15
|
t.warning = true
|
13
|
-
|
16
|
+
end
|
14
17
|
|
15
|
-
task default: [
|
18
|
+
task default: %i[test rubocop]
|
data/lib/govspeak.rb
CHANGED
@@ -57,7 +57,9 @@ module Govspeak
|
|
57
57
|
@links = Array.wrap(options.delete(:links))
|
58
58
|
@contacts = Array.wrap(options.delete(:contacts))
|
59
59
|
@locale = options.fetch(:locale, "en")
|
60
|
-
@options = { input: PARSER_CLASS_NAME,
|
60
|
+
@options = { input: PARSER_CLASS_NAME,
|
61
|
+
sanitize: true,
|
62
|
+
syntax_highlighter: nil }.merge(options)
|
61
63
|
@options[:entity_output] = :symbolic
|
62
64
|
end
|
63
65
|
|
@@ -108,9 +110,9 @@ module Govspeak
|
|
108
110
|
source = Govspeak::BlockquoteExtraQuoteRemover.remove(source)
|
109
111
|
source = remove_forbidden_characters(source)
|
110
112
|
self.class.extensions.each do |_, regexp, block|
|
111
|
-
source.gsub!(regexp)
|
113
|
+
source.gsub!(regexp) do
|
112
114
|
instance_exec(*Regexp.last_match.captures, &block)
|
113
|
-
|
115
|
+
end
|
114
116
|
end
|
115
117
|
source
|
116
118
|
end
|
@@ -127,20 +129,20 @@ module Govspeak
|
|
127
129
|
end
|
128
130
|
|
129
131
|
def self.surrounded_by(open, close = nil)
|
130
|
-
open = Regexp
|
132
|
+
open = Regexp.escape(open)
|
131
133
|
if close
|
132
|
-
close = Regexp
|
133
|
-
%r
|
134
|
+
close = Regexp.escape(close)
|
135
|
+
%r{(?:\r|\n|^)#{open}(.*?)#{close} *(\r|\n|$)?}m
|
134
136
|
else
|
135
|
-
%r
|
137
|
+
%r{(?:\r|\n|^)#{open}(.*?)#{open}? *(\r|\n|$)}m
|
136
138
|
end
|
137
139
|
end
|
138
140
|
|
139
141
|
def self.wrap_with_div(class_name, character, parser = Kramdown::Document)
|
140
|
-
extension(class_name, surrounded_by(character))
|
142
|
+
extension(class_name, surrounded_by(character)) do |body|
|
141
143
|
content = parser ? parser.new("#{body.strip}\n").to_html : body.strip
|
142
|
-
%
|
143
|
-
|
144
|
+
%(\n<div class="#{class_name}">\n#{content}</div>\n)
|
145
|
+
end
|
144
146
|
end
|
145
147
|
|
146
148
|
def insert_strong_inside_p(body, parser = Govspeak::Document)
|
@@ -160,11 +162,11 @@ module Govspeak
|
|
160
162
|
\s* # any whitespace between opening bracket and link
|
161
163
|
{\/button} # match ending bracket
|
162
164
|
(?:\r|\n|$) # non-capturing match to make sure end of line and linebreak
|
163
|
-
}x)
|
164
|
-
button_classes = "button"
|
165
|
-
button_classes << " button-start" if attributes =~ /start/
|
165
|
+
}x) do |attributes, text, href|
|
166
|
+
button_classes = "govuk-button"
|
166
167
|
/cross-domain-tracking:(?<cross_domain_tracking>.[^\s*]+)/ =~ attributes
|
167
168
|
data_attribute = ""
|
169
|
+
data_attribute << " data-start='true'" if attributes =~ /start/
|
168
170
|
if cross_domain_tracking
|
169
171
|
data_attribute << " data-module='cross-domain-tracking'"
|
170
172
|
data_attribute << " data-tracking-code='#{cross_domain_tracking.strip}'"
|
@@ -173,36 +175,36 @@ module Govspeak
|
|
173
175
|
text = text.strip
|
174
176
|
href = href.strip
|
175
177
|
|
176
|
-
%
|
177
|
-
|
178
|
+
%(\n<a role="button" class="#{button_classes}" href="#{href}" #{data_attribute}>#{text}</a>\n)
|
179
|
+
end
|
178
180
|
|
179
|
-
extension("highlight-answer")
|
180
|
-
%
|
181
|
-
#{Govspeak::Document.new(body.strip).to_html}</div>\n
|
182
|
-
|
181
|
+
extension("highlight-answer") do |body|
|
182
|
+
%(\n\n<div class="highlight-answer">
|
183
|
+
#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
184
|
+
end
|
183
185
|
|
184
|
-
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m)
|
185
|
-
%
|
186
|
-
#{Govspeak::Document.new(body.strip).to_html}</aside>\n
|
187
|
-
|
186
|
+
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m) do |body|
|
187
|
+
%(\n\n<aside class="stat-headline">
|
188
|
+
#{Govspeak::Document.new(body.strip).to_html}</aside>\n)
|
189
|
+
end
|
188
190
|
|
189
191
|
# FIXME: these surrounded_by arguments look dodgy
|
190
|
-
extension("external", surrounded_by("x[", ")x"))
|
192
|
+
extension("external", surrounded_by("x[", ")x")) do |body|
|
191
193
|
Kramdown::Document.new("[#{body.strip}){:rel='external'}").to_html
|
192
|
-
|
194
|
+
end
|
193
195
|
|
194
|
-
extension("informational", surrounded_by("^"))
|
195
|
-
%
|
196
|
-
#{Govspeak::Document.new(body.strip).to_html}</div>\n
|
197
|
-
|
196
|
+
extension("informational", surrounded_by("^")) do |body|
|
197
|
+
%(\n\n<div role="note" aria-label="Information" class="application-notice info-notice">
|
198
|
+
#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
199
|
+
end
|
198
200
|
|
199
|
-
extension("important", surrounded_by("@"))
|
200
|
-
%
|
201
|
-
|
201
|
+
extension("important", surrounded_by("@")) do |body|
|
202
|
+
%(\n\n<div role="note" aria-label="Important" class="advisory">#{insert_strong_inside_p(body)}</div>\n)
|
203
|
+
end
|
202
204
|
|
203
|
-
extension("helpful", surrounded_by("%"))
|
204
|
-
%
|
205
|
-
|
205
|
+
extension("helpful", surrounded_by("%")) do |body|
|
206
|
+
%(\n\n<div role="note" aria-label="Warning" class="application-notice help-notice">\n#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
207
|
+
end
|
206
208
|
|
207
209
|
extension("barchart", /{barchart(.*?)}/) do |captures|
|
208
210
|
stacked = ".mc-stacked" if captures.include? "stacked"
|
@@ -210,13 +212,13 @@ module Govspeak
|
|
210
212
|
negative = ".mc-negative" if captures.include? "negative"
|
211
213
|
|
212
214
|
[
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
215
|
+
"{:",
|
216
|
+
".js-barchart-table",
|
217
|
+
stacked,
|
218
|
+
compact,
|
219
|
+
negative,
|
220
|
+
".mc-auto-outdent",
|
221
|
+
"}",
|
220
222
|
].join(" ")
|
221
223
|
end
|
222
224
|
|
@@ -234,12 +236,12 @@ module Govspeak
|
|
234
236
|
|
235
237
|
attachment = AttachmentPresenter.new(attachment)
|
236
238
|
|
237
|
-
span_id = attachment.id ? %
|
239
|
+
span_id = attachment.id ? %( id="attachment_#{attachment.id}") : ""
|
238
240
|
# new lines inside our title cause problems with govspeak rendering as this is expected to be on one line.
|
239
241
|
title = (attachment.title || "").tr("\n", " ")
|
240
242
|
link = attachment.link(title, attachment.url)
|
241
243
|
attributes = attachment.attachment_attributes.empty? ? "" : " (#{attachment.attachment_attributes})"
|
242
|
-
%
|
244
|
+
%(<span#{span_id} class="attachment-inline">#{link}#{attributes}</span>)
|
243
245
|
end
|
244
246
|
|
245
247
|
# DEPRECATED: use 'Image:image-id' instead
|
@@ -261,10 +263,10 @@ module Govspeak
|
|
261
263
|
#
|
262
264
|
# This issue is not considered a bug by kramdown: https://github.com/gettalong/kramdown/issues/191
|
263
265
|
def render_image(image)
|
264
|
-
id_attr = image.id ? %
|
266
|
+
id_attr = image.id ? %( id="attachment_#{image.id}") : ""
|
265
267
|
lines = []
|
266
|
-
lines << %
|
267
|
-
lines << %
|
268
|
+
lines << %(<figure#{id_attr} class="image embedded">)
|
269
|
+
lines << %(<div class="img"><img src="#{encode(image.url)}" alt="#{encode(image.alt_text)}"></div>)
|
268
270
|
lines << image.figcaption_html if image.figcaption?
|
269
271
|
lines << "</figure>"
|
270
272
|
lines.join
|
@@ -279,9 +281,9 @@ module Govspeak
|
|
279
281
|
wrap_with_div("example", "$E", Govspeak::Document)
|
280
282
|
wrap_with_div("call-to-action", "$CTA", Govspeak::Document)
|
281
283
|
|
282
|
-
extension("address", surrounded_by("$A"))
|
283
|
-
%
|
284
|
-
|
284
|
+
extension("address", surrounded_by("$A")) do |body|
|
285
|
+
%(\n<div class="address"><div class="adr org fn"><p>\n#{body.sub("\n", '').gsub("\n", '<br />')}\n</p></div></div>\n)
|
286
|
+
end
|
285
287
|
|
286
288
|
extension("legislative list", /#{NEW_PARAGRAPH_LOOKBEHIND}\$LegislativeList\s*$(.*?)\$EndLegislativeList/m) do |body|
|
287
289
|
Govspeak::KramdownOverrides.with_kramdown_ordered_lists_disabled do
|
@@ -295,9 +297,9 @@ module Govspeak
|
|
295
297
|
|
296
298
|
extension("numbered list", /^[ \t]*((s\d+\.\s.*(?:\n|$))+)/) do |body|
|
297
299
|
body.gsub!(/s(\d+)\.\s(.*)(?:\n|$)/) do
|
298
|
-
"<li>#{Govspeak::Document.new(
|
300
|
+
"<li>#{Govspeak::Document.new(Regexp.last_match(2).strip).to_html}</li>\n"
|
299
301
|
end
|
300
|
-
%
|
302
|
+
%(<ol class="steps">\n#{body}</ol>)
|
301
303
|
end
|
302
304
|
|
303
305
|
def self.devolved_options
|
@@ -362,13 +364,13 @@ module Govspeak
|
|
362
364
|
extension("Attachment", /#{NEW_PARAGRAPH_LOOKBEHIND}\[Attachment:\s*(.*?)\s*\]/) do |attachment_id|
|
363
365
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
364
366
|
|
365
|
-
%
|
367
|
+
%(<govspeak-embed-attachment id="#{attachment_id}"></govspeak-embed-attachment>)
|
366
368
|
end
|
367
369
|
|
368
370
|
extension("AttachmentLink", /\[AttachmentLink:\s*(.*?)\s*\]/) do |attachment_id|
|
369
371
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
370
372
|
|
371
|
-
%
|
373
|
+
%(<govspeak-embed-attachment-link id="#{attachment_id}"></govspeak-embed-attachment-link>)
|
372
374
|
end
|
373
375
|
|
374
376
|
private
|
@@ -51,12 +51,15 @@ class Govspeak::HtmlSanitizer
|
|
51
51
|
def sanitize_config
|
52
52
|
Sanitize::Config.merge(
|
53
53
|
Sanitize::Config::RELAXED,
|
54
|
-
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link],
|
54
|
+
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path],
|
55
55
|
attributes: {
|
56
56
|
:all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
|
57
|
-
"a"
|
58
|
-
"
|
59
|
-
"
|
57
|
+
"a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data],
|
58
|
+
"svg" => Sanitize::Config::RELAXED[:attributes][:all] + %w[xmlns width height viewbox focusable],
|
59
|
+
"path" => Sanitize::Config::RELAXED[:attributes][:all] + %w[fill d],
|
60
|
+
"div" => [:data],
|
61
|
+
"th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
|
62
|
+
"td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
|
60
63
|
"govspeak-embed-attachment" => %w[content-id],
|
61
64
|
},
|
62
65
|
)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Govspeak
|
2
2
|
module KramdownOverrides
|
3
|
-
#
|
3
|
+
# This depends on two internal parts of Kramdown.
|
4
4
|
# 1. Parser registry (kramdown/parser/kramdown.rb#define_parser)
|
5
5
|
# 2. Kramdown list regexes (kramdown/parser/kramdown/list.rb)
|
6
|
-
#
|
6
|
+
# Updating the Kramdown gem therefore also means updating this file to to
|
7
7
|
# match Kramdown's internals.
|
8
8
|
|
9
9
|
def self.with_kramdown_ordered_lists_disabled
|
@@ -14,9 +14,9 @@ module Govspeak
|
|
14
14
|
attr_reader :document, :website_root
|
15
15
|
|
16
16
|
def extract_links
|
17
|
-
document_anchors
|
18
|
-
map { |link| extract_href_from_link(link) }
|
19
|
-
reject(&:blank?)
|
17
|
+
document_anchors
|
18
|
+
.map { |link| extract_href_from_link(link) }
|
19
|
+
.reject(&:blank?)
|
20
20
|
end
|
21
21
|
|
22
22
|
def extract_href_from_link(link)
|
@@ -92,11 +92,31 @@ module Govspeak
|
|
92
92
|
end
|
93
93
|
|
94
94
|
document.css(":not(thead) tr td:first-child").map do |el|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
95
|
+
next unless el.content.match?(/^#($|\s.*$)/)
|
96
|
+
|
97
|
+
# Replace '# ' and '#', but not '#Word'.
|
98
|
+
# This runs on the first child of the element to preserve any links
|
99
|
+
el.children.first.content = el.children.first.content.gsub(/^#($|\s)/, "")
|
100
|
+
el.name = "th" if el.content.present? # This also prevents a `th` with nothing inside it; a `td` is preferable.
|
101
|
+
el[:scope] = "row" if el.content.present? # `scope` shouldn't be used if there's nothing in the table heading.
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
extension("use gem component for buttons") do |document|
|
106
|
+
document.css(".govuk-button").map do |el|
|
107
|
+
button_html = GovukPublishingComponents.render(
|
108
|
+
"govuk_publishing_components/components/button",
|
109
|
+
text: el.content,
|
110
|
+
href: el["href"],
|
111
|
+
start: el["data-start"],
|
112
|
+
data_attributes: {
|
113
|
+
module: el["data-module"],
|
114
|
+
"tracking-code": el["data-tracking-code"],
|
115
|
+
"tracking-name": el["data-tracking-name"],
|
116
|
+
},
|
117
|
+
).squish.gsub("> <", "><").gsub!(/\s+/, " ")
|
118
|
+
|
119
|
+
el.swap(button_html)
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
@@ -55,46 +55,46 @@ module Govspeak
|
|
55
55
|
|
56
56
|
def humanized_content_type(file_extension)
|
57
57
|
file_extension_vs_humanized_content_type = {
|
58
|
-
"chm"
|
59
|
-
"csv"
|
58
|
+
"chm" => file_abbr_tag("CHM", "Microsoft Compiled HTML Help"),
|
59
|
+
"csv" => file_abbr_tag("CSV", "Comma-separated Values"),
|
60
60
|
"diff" => file_abbr_tag("DIFF", "Plain text differences"),
|
61
|
-
"doc"
|
61
|
+
"doc" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
62
62
|
"docx" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
63
|
-
"dot"
|
64
|
-
"dxf"
|
65
|
-
"eps"
|
66
|
-
"gif"
|
67
|
-
"gml"
|
63
|
+
"dot" => file_abbr_tag("DOT", "MS Word Document Template"),
|
64
|
+
"dxf" => file_abbr_tag("DXF", "AutoCAD Drawing Exchange Format"),
|
65
|
+
"eps" => file_abbr_tag("EPS", "Encapsulated PostScript"),
|
66
|
+
"gif" => file_abbr_tag("GIF", "Graphics Interchange Format"),
|
67
|
+
"gml" => file_abbr_tag("GML", "Geography Markup Language"),
|
68
68
|
"html" => file_abbr_tag("HTML", "Hypertext Markup Language"),
|
69
69
|
"ics" => file_abbr_tag("ICS", "iCalendar file"),
|
70
|
-
"jpg"
|
71
|
-
"odp"
|
72
|
-
"ods"
|
73
|
-
"odt"
|
74
|
-
"pdf"
|
75
|
-
"png"
|
76
|
-
"ppt"
|
70
|
+
"jpg" => "JPEG",
|
71
|
+
"odp" => file_abbr_tag("ODP", "OpenDocument Presentation"),
|
72
|
+
"ods" => file_abbr_tag("ODS", "OpenDocument Spreadsheet"),
|
73
|
+
"odt" => file_abbr_tag("ODT", "OpenDocument Text document"),
|
74
|
+
"pdf" => file_abbr_tag("PDF", "Portable Document Format"),
|
75
|
+
"png" => file_abbr_tag("PNG", "Portable Network Graphic"),
|
76
|
+
"ppt" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
77
77
|
"pptx" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
78
|
-
"ps"
|
79
|
-
"rdf"
|
80
|
-
"rtf"
|
81
|
-
"sch"
|
82
|
-
"txt"
|
78
|
+
"ps" => file_abbr_tag("PS", "PostScript"),
|
79
|
+
"rdf" => file_abbr_tag("RDF", "Resource Description Framework"),
|
80
|
+
"rtf" => file_abbr_tag("RTF", "Rich Text Format"),
|
81
|
+
"sch" => file_abbr_tag("SCH", "XML based Schematic"),
|
82
|
+
"txt" => "Plain text",
|
83
83
|
"wsdl" => file_abbr_tag("WSDL", "Web Services Description Language"),
|
84
|
-
"xls"
|
84
|
+
"xls" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
85
85
|
"xlsm" => file_abbr_tag("XLSM", "MS Excel Macro-Enabled Workbook"),
|
86
86
|
"xlsx" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
87
|
-
"xlt"
|
88
|
-
"xsd"
|
87
|
+
"xlt" => file_abbr_tag("XLT", "MS Excel Spreadsheet Template"),
|
88
|
+
"xsd" => file_abbr_tag("XSD", "XML Schema"),
|
89
89
|
"xslt" => file_abbr_tag("XSLT", "Extensible Stylesheet Language Transformation"),
|
90
|
-
"zip"
|
90
|
+
"zip" => file_abbr_tag("ZIP", "Zip archive"),
|
91
91
|
}
|
92
92
|
file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, "")
|
93
93
|
end
|
94
94
|
|
95
95
|
def link(body, url, options = {})
|
96
|
-
options_str = options.map { |k, v| %
|
97
|
-
%
|
96
|
+
options_str = options.map { |k, v| %(#{encode(k)}="#{encode(v)}") }.join(" ")
|
97
|
+
%(<a href="#{encode(url)}" #{options_str}>#{body}</a>)
|
98
98
|
end
|
99
99
|
|
100
100
|
private
|