Skip to content

Commit df31da7

Browse files
authored
Merge pull request #1026 from sphinx-contrib/adding-html-directive
Add the html confluence directive
2 parents 53c45a9 + f4eecc2 commit df31da7

File tree

10 files changed

+161
-0
lines changed

10 files changed

+161
-0
lines changed

doc/configuration.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,6 +2081,9 @@ Advanced processing configuration
20812081
20822082
See also |confluence_prev_next_buttons_location|_.
20832083

2084+
.. |confluence_permit_raw_html| replace:: ``confluence_permit_raw_html``
2085+
.. _confluence_permit_raw_html:
2086+
20842087
.. confval:: confluence_permit_raw_html
20852088

20862089
.. versionadded:: 2.2
@@ -2117,6 +2120,8 @@ Advanced processing configuration
21172120
or Confluence may reject the publication of Confluence document (i.e.
21182121
failing to upload a page).
21192122

2123+
See also :lref:`confluence_html` directive.
2124+
21202125
.. |confluence_remove_title| replace:: ``confluence_remove_title``
21212126
.. _confluence_remove_title:
21222127

doc/directives.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,31 @@ Common
172172
173173
See also :doc:`guide-collapse`.
174174

175+
.. _confluence_html:
176+
177+
.. rst:directive:: confluence_html
178+
179+
.. versionadded:: 2.7
180+
181+
.. warning::
182+
183+
The `HTML Macro`_ is disabled by default on Confluence instances.
184+
Using this directive is only useful for users that have instances
185+
where a system administrator has enabled their use.
186+
187+
The ``confluence_html`` directive allows a user to define a Confluence
188+
`HTML Macro`_ to render HTML content on a page. For example:
189+
190+
.. code-block:: rst
191+
192+
.. confluence_html::
193+
194+
<h1>Header</h1>
195+
196+
This is an <strong>example</strong>.
197+
198+
See also :lref:`confluence_permit_raw_html`.
199+
175200
.. _confluence_metadata:
176201

177202
.. rst:directive:: confluence_metadata
@@ -699,6 +724,7 @@ See also :ref:`smart link roles <smart-link-roles>`.
699724
.. _Excerpt Include Macro: https://confluence.atlassian.com/doc/excerpt-include-macro-148067.html
700725
.. _Excerpt Macro: https://confluence.atlassian.com/doc/excerpt-macro-148062.html
701726
.. _Expand Macro: https://confluence.atlassian.com/doc/expand-macro-223222352.html
727+
.. _HTML Macro: https://confluence.atlassian.com/doc/html-macro-38273085.html
702728
.. _Sphinx's toctree directive: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#table-of-contents
703729
.. _Table of Contents Macro: https://support.atlassian.com/confluence-cloud/docs/insert-the-table-of-contents-macro/
704730
.. _directives: https://www.sphinx-doc.org/en/stable/usage/restructuredtext/directives.html

sphinxcontrib/confluencebuilder/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from sphinxcontrib.confluencebuilder.directives import ConfluenceExcerptDirective
1111
from sphinxcontrib.confluencebuilder.directives import ConfluenceExcerptIncludeDirective
1212
from sphinxcontrib.confluencebuilder.directives import ConfluenceExpandDirective
13+
from sphinxcontrib.confluencebuilder.directives import ConfluenceHtmlDirective
1314
from sphinxcontrib.confluencebuilder.directives import ConfluenceLatexDirective
1415
from sphinxcontrib.confluencebuilder.directives import ConfluenceLinkDirective
1516
from sphinxcontrib.confluencebuilder.directives import ConfluenceMetadataDirective
@@ -222,6 +223,8 @@ def setup(app):
222223
# (configuration - advanced processing)
223224
# Filename suffix for generated files.
224225
cm.add_conf('confluence_file_suffix', 'confluence')
226+
# Macro configuration for Confluence-managed HTML content.
227+
cm.add_conf('confluence_html_macro', 'confluence')
225228
# Configuration for named JIRA Servers
226229
cm.add_conf('confluence_jira_servers', 'confluence')
227230
# Translation of a raw language to code block macro language.
@@ -349,6 +352,7 @@ def confluence_builder_inited(app):
349352
app.add_directive('confluence_excerpt_include',
350353
ConfluenceExcerptIncludeDirective)
351354
app.add_directive('confluence_expand', ConfluenceExpandDirective)
355+
app.add_directive('confluence_html', ConfluenceHtmlDirective)
352356
app.add_directive('confluence_latex', ConfluenceLatexDirective)
353357
app.add_directive('confluence_link', ConfluenceLinkDirective)
354358
app.add_directive('confluence_metadata', ConfluenceMetadataDirective)

sphinxcontrib/confluencebuilder/config/checks.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ def validate_configuration(builder):
316316

317317
# ##################################################################
318318

319+
# confluence_html_macro
320+
validator.conf('confluence_html_macro') \
321+
.string()
322+
323+
# ##################################################################
324+
319325
# confluence_ignore_titlefix_on_index
320326
validator.conf('confluence_ignore_titlefix_on_index') \
321327
.bool()

sphinxcontrib/confluencebuilder/directives.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from sphinxcontrib.confluencebuilder.nodes import confluence_expand
99
from sphinxcontrib.confluencebuilder.nodes import confluence_excerpt
1010
from sphinxcontrib.confluencebuilder.nodes import confluence_excerpt_include
11+
from sphinxcontrib.confluencebuilder.nodes import confluence_html
1112
from sphinxcontrib.confluencebuilder.nodes import confluence_latex_block
1213
from sphinxcontrib.confluencebuilder.nodes import confluence_link_card
1314
from sphinxcontrib.confluencebuilder.nodes import confluence_metadata
@@ -165,6 +166,19 @@ def run(self):
165166
return [node]
166167

167168

169+
class ConfluenceHtmlDirective(Directive):
170+
has_content = True
171+
172+
def run(self):
173+
self.assert_has_content()
174+
text = '\n'.join(self.content)
175+
176+
node = confluence_html(rawsource=text)
177+
178+
self.state.nested_parse(self.content, self.content_offset, node)
179+
return [node]
180+
181+
168182
class ConfluenceLatexDirective(Directive):
169183
has_content = True
170184
option_spec = {

sphinxcontrib/confluencebuilder/nodes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ class confluence_header(nodes.Decorative, nodes.Element):
102102
a document.
103103
"""
104104

105+
class confluence_html(nodes.TextElement):
106+
"""
107+
confluence html node
108+
109+
A Confluence builder defined HTML node, used to help manage HTML content
110+
designed for an HTML macro.
111+
"""
105112

106113
class confluence_metadata(nodes.Invisible, nodes.Special, ConfluenceParams):
107114
"""

sphinxcontrib/confluencebuilder/storage/translator.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,21 @@ def depart_confluence_header(self, node):
25132513
**{'style':
25142514
'clear: both; padding-top: 10px; margin-bottom: 30px'}))
25152515

2516+
def visit_confluence_html(self, node):
2517+
html_macro = 'html'
2518+
if self.builder.config.confluence_html_macro:
2519+
html_macro = self.builder.config.confluence_html_macro
2520+
2521+
html_content = node.rawsource
2522+
2523+
self.body.append(self.start_ac_macro(node, html_macro))
2524+
self.body.append(self.start_ac_plain_text_body_macro(node))
2525+
self.body.append(self.escape_cdata(html_content))
2526+
self.body.append(self.end_ac_plain_text_body_macro(node))
2527+
self.body.append(self.end_ac_macro(node))
2528+
2529+
raise nodes.SkipNode
2530+
25162531
def visit_confluence_newline(self, node):
25172532
self.body.append(self.start_tag(
25182533
node, 'br', suffix=self.nl, empty=True))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
:orphan:
2+
3+
html macro
4+
----------
5+
6+
.. confluence_html::
7+
8+
<strong>strong text</strong>

tests/unit-tests/test_config_checks.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,17 @@ def test_config_check_header_file(self):
434434
self.config['confluence_header_file'] = relbase + 'sample-header.tpl'
435435
self._try_config()
436436

437+
def test_config_check_confluence_html_macro(self):
438+
self.config['confluence_html_macro'] = ''
439+
self._try_config()
440+
441+
self.config['confluence_html_macro'] = 'dummy'
442+
self._try_config()
443+
444+
self.config['confluence_html_macro'] = 1
445+
with self.assertRaises(ConfluenceConfigError):
446+
self._try_config()
447+
437448
def test_config_check_jira_servers(self):
438449
self.config['confluence_jira_servers'] = {}
439450
self._try_config()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
# Copyright Sphinx Confluence Builder Contributors (AUTHORS)
3+
4+
from bs4 import BeautifulSoup
5+
from bs4 import CData
6+
from tests.lib.parse import parse
7+
from tests.lib.testcase import ConfluenceTestCase
8+
from tests.lib.testcase import setup_builder
9+
10+
11+
class TestConfluenceHtml(ConfluenceTestCase):
12+
@classmethod
13+
def setUpClass(cls):
14+
super().setUpClass()
15+
16+
cls.dataset = cls.datasets / 'html'
17+
18+
@setup_builder('confluence')
19+
def test_storage_confluence_html_custom_macro(self):
20+
config = dict(self.config)
21+
config['confluence_html_macro'] = 'custom-html'
22+
out_dir = self.build(self.dataset, config=config)
23+
24+
with parse('index', out_dir) as data:
25+
html_macro = data.find('ac:structured-macro',
26+
{'ac:name': 'custom-html'})
27+
self.assertIsNotNone(html_macro)
28+
29+
plain_body = html_macro.find('ac:plain-text-body')
30+
self.assertIsNotNone(plain_body)
31+
32+
html_macro_cdata = next(plain_body.children, None)
33+
self.assertIsNotNone(html_macro_cdata)
34+
self.assertTrue(isinstance(html_macro_cdata, CData))
35+
36+
html_macro_data = BeautifulSoup(html_macro_cdata, 'html.parser')
37+
38+
strong_element = html_macro_data.find('strong')
39+
self.assertIsNotNone(strong_element)
40+
text_contents = strong_element.text.strip()
41+
self.assertIsNotNone(text_contents)
42+
self.assertTrue('strong text' in text_contents)
43+
44+
@setup_builder('confluence')
45+
def test_storage_confluence_html_default(self):
46+
out_dir = self.build(self.dataset)
47+
48+
with parse('index', out_dir) as data:
49+
html_macro = data.find('ac:structured-macro', {'ac:name': 'html'})
50+
self.assertIsNotNone(html_macro)
51+
52+
plain_body = html_macro.find('ac:plain-text-body')
53+
self.assertIsNotNone(plain_body)
54+
55+
html_macro_cdata = next(plain_body.children, None)
56+
self.assertIsNotNone(html_macro_cdata)
57+
self.assertTrue(isinstance(html_macro_cdata, CData))
58+
59+
html_macro_data = BeautifulSoup(html_macro_cdata, 'html.parser')
60+
61+
strong_element = html_macro_data.find('strong')
62+
self.assertIsNotNone(strong_element)
63+
text_contents = strong_element.text.strip()
64+
self.assertIsNotNone(text_contents)
65+
self.assertTrue('strong text' in text_contents)

0 commit comments

Comments
 (0)