summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@gitlab.com>2019-07-16 11:53:47 +0000
committerBob Van Landuyt <bob@gitlab.com>2019-07-16 11:53:47 +0000
commit1e99c1b0a7b4e80be5a0be40aebb7f4cad0077de (patch)
treedced6bc8df79afa503160cabc518c9b35e9fb4ea
parent4a63ae48465ad1ba2403158077acd02d63a9b4ff (diff)
parent0cba81c0ecc71f411153b1f2a2952c07125217f9 (diff)
downloadgitlab-ce-1e99c1b0a7b4e80be5a0be40aebb7f4cad0077de.tar.gz
Merge branch 'issue-64070-asciidoctor-section-anchors' into 'master'
Enable section anchors Closes #64070 See merge request gitlab-org/gitlab-ce!30666
-rw-r--r--changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml5
-rw-r--r--lib/banzai/filter/ascii_doc_sanitization_filter.rb30
-rw-r--r--lib/gitlab/asciidoc.rb1
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb69
4 files changed, 103 insertions, 2 deletions
diff --git a/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml b/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml
new file mode 100644
index 00000000000..51c1537a159
--- /dev/null
+++ b/changelogs/unreleased/64070-asciidoctor-enable-section-anchors.yml
@@ -0,0 +1,5 @@
+---
+title: "Enable section anchors in Asciidoctor"
+merge_request: 30666
+author: Guillaume Grossetie
+type: added \ No newline at end of file
diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
index a78bb60103c..d8d63075752 100644
--- a/lib/banzai/filter/ascii_doc_sanitization_filter.rb
+++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
@@ -6,6 +6,9 @@ module Banzai
#
# Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
+ # Section anchor link pattern
+ SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
+
# Classes used by Asciidoctor to style components
ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze
CALLOUT_CLASSES = ['conum'].freeze
@@ -19,14 +22,17 @@ module Banzai
td: ['icon'].freeze,
i: ADMONITION_CLASSES + CALLOUT_CLASSES + CHECKLIST_CLASSES,
ul: LIST_CLASSES,
- ol: LIST_CLASSES
+ ol: LIST_CLASSES,
+ a: ['anchor'].freeze
}.freeze
+ ALLOWED_HEADERS = %w(h2 h3 h4 h5 h6).freeze
+
def customize_whitelist(whitelist)
# Allow marks
whitelist[:elements].push('mark')
- # Allow any classes in `span`, `i`, `div`, `td`, `ul` and `ol` elements
+ # Allow any classes in `span`, `i`, `div`, `td`, `ul`, `ol` and `a` elements
# but then remove any unknown classes
whitelist[:attributes]['span'] = %w(class)
whitelist[:attributes]['div'].push('class')
@@ -34,12 +40,32 @@ module Banzai
whitelist[:attributes]['i'] = %w(class)
whitelist[:attributes]['ul'] = %w(class)
whitelist[:attributes]['ol'] = %w(class)
+ whitelist[:attributes]['a'].push('class')
whitelist[:transformers].push(self.class.remove_element_classes)
+ # Allow `id` in heading elements for section anchors
+ ALLOWED_HEADERS.each do |header|
+ whitelist[:attributes][header] = %w(id)
+ end
+ whitelist[:transformers].push(self.class.remove_non_heading_ids)
+
whitelist
end
class << self
+ def remove_non_heading_ids
+ lambda do |env|
+ node = env[:node]
+
+ return unless ALLOWED_HEADERS.any?(node.name)
+ return unless node.has_attribute?('id')
+
+ return if node['id'] =~ SECTION_LINK_REF_PATTERN
+
+ node.remove_attribute('id')
+ end
+ end
+
def remove_element_classes
lambda do |env|
node = env[:node]
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 00c87cce7b6..da65caa6c9c 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -13,6 +13,7 @@ module Gitlab
MAX_INCLUDE_DEPTH = 5
DEFAULT_ADOC_ATTRS = {
'showtitle' => true,
+ 'sectanchors' => true,
'idprefix' => 'user-content-',
'idseparator' => '-',
'env' => 'gitlab',
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index ff002acbd35..5293732ead1 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -96,6 +96,75 @@ module Gitlab
end
end
+ context 'with passthrough' do
+ it 'removes non heading ids' do
+ input = <<~ADOC
+ ++++
+ <h2 id="foo">Title</h2>
+ ++++
+ ADOC
+
+ output = <<~HTML
+ <h2>Title</h2>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
+
+ context 'with section anchors' do
+ it 'preserves ids and links' do
+ input = <<~ADOC
+ = Title
+
+ == First section
+
+ This is the first section.
+
+ == Second section
+
+ This is the second section.
+
+ == Thunder ⚡ !
+
+ This is the third section.
+ ADOC
+
+ output = <<~HTML
+ <h1>Title</h1>
+ <div>
+ <h2 id="user-content-first-section">
+ <a class="anchor" href="#user-content-first-section"></a>First section</h2>
+ <div>
+ <div>
+ <p>This is the first section.</p>
+ </div>
+ </div>
+ </div>
+ <div>
+ <h2 id="user-content-second-section">
+ <a class="anchor" href="#user-content-second-section"></a>Second section</h2>
+ <div>
+ <div>
+ <p>This is the second section.</p>
+ </div>
+ </div>
+ </div>
+ <div>
+ <h2 id="user-content-thunder">
+ <a class="anchor" href="#user-content-thunder"></a>Thunder ⚡ !</h2>
+ <div>
+ <div>
+ <p>This is the third section.</p>
+ </div>
+ </div>
+ </div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
+
context 'with checklist' do
it 'preserves classes' do
input = <<~ADOC