make showing empty pages opt-in
This commit is contained in:
parent
1af66f3b85
commit
c259856d85
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
:empty_page:
|
:show_content:
|
||||||
|
|
||||||
=================================
|
=================================
|
||||||
Contributing to the documentation
|
Contributing to the documentation
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
from . import pygments_override
|
from docutils import nodes
|
||||||
from . import translator
|
|
||||||
|
|
||||||
import sphinx.builders.html
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.environment.adapters import toctree
|
from sphinx.environment.adapters import toctree
|
||||||
from docutils import nodes
|
|
||||||
|
from . import pygments_override, translator
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.set_translator('html', translator.BootstrapTranslator)
|
app.set_translator('html', translator.BootstrapTranslator)
|
||||||
|
|
||||||
# VFE TODO check if default meta initialization is necessary.
|
app.connect('html-page-context', set_missing_meta)
|
||||||
# If not, remove update_meta method
|
|
||||||
app.connect('html-page-context', update_meta)
|
|
||||||
|
|
||||||
app.add_js_file('js/utils.js') # Keep in first position
|
app.add_js_file('js/utils.js') # Keep in first position
|
||||||
app.add_js_file('js/layout.js')
|
app.add_js_file('js/layout.js')
|
||||||
@ -20,14 +16,12 @@ def setup(app):
|
|||||||
app.add_js_file('js/page_toc.js')
|
app.add_js_file('js/page_toc.js')
|
||||||
|
|
||||||
|
|
||||||
def update_meta(app, pagename, templatename, context, doctree):
|
def set_missing_meta(app, pagename, templatename, context, doctree):
|
||||||
meta = context.get('meta')
|
if context.get('meta') is None: # Pages without title (used with `include::`) have no meta
|
||||||
if meta is None:
|
context['meta'] = {}
|
||||||
meta = context['meta'] = {}
|
|
||||||
|
|
||||||
# TODO VFE detailed explanation of the patch logic and use.
|
|
||||||
|
|
||||||
class Monkey(object):
|
class Monkey(object):
|
||||||
|
""" Replace patched method of an object by a new method receiving the old one in argument. """
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
def __call__(self, fn):
|
def __call__(self, fn):
|
||||||
@ -37,40 +31,57 @@ class Monkey(object):
|
|||||||
|
|
||||||
@Monkey(toctree.TocTree)
|
@Monkey(toctree.TocTree)
|
||||||
def resolve(old_resolve, tree, docname, *args, **kwargs):
|
def resolve(old_resolve, tree, docname, *args, **kwargs):
|
||||||
|
|
||||||
|
def _clear_empty_pages_reference(_node) -> None:
|
||||||
|
""" Disable references to 'empty' toctree pages.
|
||||||
|
|
||||||
|
Inspect node's children to determine whether the page is a toc and, if so, clear its
|
||||||
|
reference URL. (<a href="#"/>)
|
||||||
|
If the page has the `show_content` metadata, don't clear the reference.
|
||||||
|
|
||||||
|
Internal structure of nodes:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a/></p>
|
||||||
|
<ul>
|
||||||
|
...
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li/>
|
||||||
|
<ul/>
|
||||||
|
"""
|
||||||
|
if isinstance(_node, nodes.reference): # The node is a reference (<a/>)
|
||||||
|
_subnode_docname = _get_docname(_node)
|
||||||
|
if _subnode_docname and any(
|
||||||
|
isinstance(_subnode, nodes.bullet_list)
|
||||||
|
for _subnode in _node.parent.parent.children
|
||||||
|
): # The node references a toc
|
||||||
|
if 'show_content' not in tree.env.metadata[_subnode_docname]:
|
||||||
|
_node['refuri'] = '#' # The page must not be accessible
|
||||||
|
elif isinstance(_node, (addnodes.compact_paragraph, nodes.bullet_list, nodes.list_item)):
|
||||||
|
for _subnode in _node.children:
|
||||||
|
_clear_empty_pages_reference(_subnode)
|
||||||
|
|
||||||
|
def _get_docname(_node):
|
||||||
|
"""
|
||||||
|
docname = a/b/c/the_page_being_rendered
|
||||||
|
_ref = ../../contributing/documentation
|
||||||
|
_path_parts = ['..', '..', 'contributing', 'documentation']
|
||||||
|
_res = ['a', 'contributing', 'documentation']
|
||||||
|
_docname = a/contributing/documentation
|
||||||
|
"""
|
||||||
|
_ref = _node['refuri'].replace('.html', '')
|
||||||
|
_parent_directory_occurrences = _ref.count('..')
|
||||||
|
if not _parent_directory_occurrences: # The ref is already the docname
|
||||||
|
_docname = _ref
|
||||||
|
else:
|
||||||
|
_path_parts = _ref.split('/')
|
||||||
|
_res = docname.split('/')[:-(_parent_directory_occurrences+1)] \
|
||||||
|
+ _path_parts[_parent_directory_occurrences:]
|
||||||
|
_docname = '/'.join(_res)
|
||||||
|
return _docname
|
||||||
|
|
||||||
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
|
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
|
||||||
if resolved_toc:
|
if resolved_toc: # `resolve` returns None if the depth of the TOC to resolve is too high
|
||||||
_toctree_add_empty_class(tree, resolved_toc, docname)
|
_clear_empty_pages_reference(resolved_toc)
|
||||||
return resolved_toc
|
return resolved_toc
|
||||||
|
|
||||||
def _toctree_add_empty_class(tree, node, docname) -> None:
|
|
||||||
for subnode in node.children:
|
|
||||||
if isinstance(subnode, (
|
|
||||||
addnodes.compact_paragraph,
|
|
||||||
nodes.list_item,
|
|
||||||
nodes.bullet_list
|
|
||||||
)):
|
|
||||||
# for <p>, <li> and <ul> just recurse
|
|
||||||
_toctree_add_empty_class(tree, subnode, docname)
|
|
||||||
elif isinstance(subnode, nodes.reference):
|
|
||||||
toc_ref = get_reference(subnode, docname)
|
|
||||||
if toc_ref and 'empty_page' in tree.env.metadata[toc_ref]:
|
|
||||||
subnode['classes'].append('o_empty_page')
|
|
||||||
subnode['refuri'] = '#' # The link must not be clickable
|
|
||||||
|
|
||||||
def get_reference(node, docname):
|
|
||||||
ref = node['refuri'].replace('.html', '') # applications.html
|
|
||||||
if ref.find('..') < 0:
|
|
||||||
# direct reference
|
|
||||||
return ref
|
|
||||||
splitted_refuri = ref.split('/')
|
|
||||||
count = 0 # Number of ../ in refuri
|
|
||||||
for split in splitted_refuri:
|
|
||||||
if split == "..":
|
|
||||||
count += 1
|
|
||||||
# ref = ../../../contributing/documentation
|
|
||||||
# docname = services/legal/terms/enterprise
|
|
||||||
# res = contributing/documentation
|
|
||||||
res = docname.split('/')[:-(count+1)] + splitted_refuri[count:]
|
|
||||||
return "/".join(
|
|
||||||
res
|
|
||||||
)
|
|
||||||
|
@ -64,11 +64,11 @@
|
|||||||
|
|
||||||
{% set main_classes = [] %}
|
{% set main_classes = [] %}
|
||||||
{% if pagename == master_doc %} {# The current page is the homepage #}
|
{% if pagename == master_doc %} {# The current page is the homepage #}
|
||||||
{% set main_classes = main_classes + ['index'] %}
|
{% set main_classes = main_classes + ['index'] %} {# TODO ANVFE should be 'o_index' #}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'code-column' in meta %} {# The page contains a 'memento' (side dynamic block) #}
|
{% if 'code-column' in meta %} {# The page contains a 'memento' (side dynamic block) #}
|
||||||
{% set main_classes = main_classes + ['has_code_col'] %}
|
{% set main_classes = main_classes + ['has_code_col'] %} {# TODO ANVFE see #}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'classes' in meta %} {# The page source defines custom classes #}
|
{% if 'classes' in meta %} {# The page source defines custom classes #}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<div class="o_logo_wrapper">
|
<div class="o_logo_wrapper">
|
||||||
<a href="{{ pathto(master_doc) }}" class="o_logo">
|
<a href="{{ pathto(master_doc) }}" class="o_logo">
|
||||||
{# TODO EDI/design make one unique image for Odoo docs s.t. when clicking on docs you are also redirected #}
|
|
||||||
<img src="{{ pathto('_static/img/logos/odoo_logo.svg', 1) }}"
|
<img src="{{ pathto('_static/img/logos/odoo_logo.svg', 1) }}"
|
||||||
height="30" alt="Odoo"/>
|
height="30" alt="Odoo"/>
|
||||||
<span class="text-dark fw_extralight">docs</span>
|
<span class="text-dark fw_extralight">docs</span>
|
||||||
|
Loading…
Reference in New Issue
Block a user