govspeak 6.5.2 → 6.5.7
Sign up to get free protection for your applications and to get access to all the features.
- 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 +6 -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 +4 -5
- 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 +0 -1
- data/test/presenters/h_card_presenter_test.rb +0 -2
- data/test/test_helper.rb +6 -2
- metadata +40 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6cd5f76c1c1a80f74d86785f10b591bdfc1f75f2e6b2dcf874e8b39ab0e213c
|
4
|
+
data.tar.gz: 4be63d972c6fd0acd9a09120930760230e45ffd59522689177602d977b9443ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ef225809fc1434259a89d44f5d5c48f9afed99dabe5b81306347a19fe50df08ecbc1c906fa2ee704a9f63b3d9448d88ddeaa1ef110a49c99ba6441da299a0c5
|
7
|
+
data.tar.gz: ce7ec875cb1b33b353469846ba9c5226fbffc13551ef74cb92508e5ca7f76f1519b1c6a9b288d9978b52c16cc9fa9cb7c7500c5f3fedbc35c9c846e0faa029e2
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,29 @@
|
|
1
|
+
## 6.5.7
|
2
|
+
|
3
|
+
* Update GOV.UK Publishing components to version to 23.0.0 or greater
|
4
|
+
|
5
|
+
## 6.5.6
|
6
|
+
|
7
|
+
* Update Kramdown version to 2.3.0 or greater
|
8
|
+
|
9
|
+
## 6.5.5
|
10
|
+
|
11
|
+
* Prevent links in table headers being stripped (PR#187)
|
12
|
+
|
13
|
+
## 6.5.4
|
14
|
+
|
15
|
+
* Require Sanitize to be at least 5.2.1 due to https://nvd.nist.gov/vuln/detail/CVE-2020-4054
|
16
|
+
|
17
|
+
## 6.5.3
|
18
|
+
|
19
|
+
* Use button component for buttons (PR#176)
|
20
|
+
|
1
21
|
## 6.5.2
|
2
22
|
|
3
23
|
* Allow `data` attributes on `div` tags (PR#173)
|
4
24
|
|
5
25
|
## 6.5.1
|
26
|
+
|
6
27
|
* Change unicode testing characters after external gem change
|
7
28
|
* Move from govuk-lint to rubocop-govuk
|
8
29
|
* 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,13 +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"
|
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],
|
58
60
|
"div" => [:data],
|
59
|
-
"th"
|
60
|
-
"td"
|
61
|
+
"th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
|
62
|
+
"td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
|
61
63
|
"govspeak-embed-attachment" => %w[content-id],
|
62
64
|
},
|
63
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
|