mirror of
https://github.com/mkdocs/mkdocs.git
synced 2026-03-27 09:58:31 +07:00
Enable autoescape in themes Jinja templates
This commit is contained in:
@@ -9,6 +9,7 @@ from urllib.parse import urljoin, urlsplit
|
||||
|
||||
import jinja2
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from markupsafe import Markup
|
||||
|
||||
import mkdocs
|
||||
from mkdocs import utils
|
||||
@@ -212,11 +213,15 @@ def _build_page(
|
||||
# Run `page_context` plugin events.
|
||||
context = config.plugins.on_page_context(context, page=page, config=config, nav=nav)
|
||||
|
||||
page.content = Markup(page.content or '')
|
||||
if excluded:
|
||||
page.content = (
|
||||
'<div class="mkdocs-draft-marker" title="This page will not be included into the built site.">'
|
||||
'DRAFT'
|
||||
'</div>' + (page.content or '')
|
||||
Markup(
|
||||
'<div class="mkdocs-draft-marker" title="This page will not be included into the built site.">'
|
||||
'DRAFT'
|
||||
'</div>'
|
||||
)
|
||||
+ page.content
|
||||
)
|
||||
|
||||
# Render the template.
|
||||
|
||||
@@ -32,6 +32,7 @@ from urllib.parse import urlsplit, urlunsplit
|
||||
import markdown
|
||||
import pathspec
|
||||
import pathspec.gitignore
|
||||
from markupsafe import Markup
|
||||
|
||||
from mkdocs import plugins, theme, utils
|
||||
from mkdocs.config.base import (
|
||||
@@ -536,6 +537,15 @@ class URL(OptionallyRequired[str]):
|
||||
raise ValidationError("The URL isn't valid, it should include the http:// (scheme)")
|
||||
|
||||
|
||||
class HTMLString(BaseConfigOption[Markup]):
|
||||
"""A string of HTML that should not be further escaped when pasting it into other HTML content."""
|
||||
|
||||
def run_validation(self, value: object) -> Markup:
|
||||
if not isinstance(value, str):
|
||||
raise ValidationError(f"Expected a string but received: {type(value)}")
|
||||
return Markup(value)
|
||||
|
||||
|
||||
class Optional(Generic[T], BaseConfigOption[Union[T, None]]):
|
||||
"""
|
||||
Wraps a field and makes a None value possible for it when no value is set.
|
||||
|
||||
@@ -79,7 +79,7 @@ class MkDocsConfig(base.Config):
|
||||
site_dir = c.SiteDir(default='site')
|
||||
"""The directory where the site will be built to"""
|
||||
|
||||
copyright = c.Optional(c.Type(str))
|
||||
copyright = c.Optional(c.HTMLString())
|
||||
"""A copyright notice to add to the footer of documentation."""
|
||||
|
||||
google_analytics = c.Deprecated(
|
||||
|
||||
@@ -4,6 +4,8 @@ import abc
|
||||
from typing import TYPE_CHECKING, Iterable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from markupsafe import Markup
|
||||
|
||||
from mkdocs.structure.nav import Section
|
||||
|
||||
|
||||
@@ -21,7 +23,7 @@ class StructureItem(metaclass=abc.ABCMeta):
|
||||
def is_top_level(self) -> bool:
|
||||
return self.parent is None
|
||||
|
||||
title: str | None
|
||||
title: Markup | None
|
||||
is_section: bool = False
|
||||
is_page: bool = False
|
||||
is_link: bool = False
|
||||
|
||||
@@ -4,6 +4,8 @@ import logging
|
||||
from typing import TYPE_CHECKING, Iterator, TypeVar
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from mkdocs.exceptions import BuildError
|
||||
from mkdocs.structure import StructureItem
|
||||
from mkdocs.structure.files import file_sort_key
|
||||
@@ -46,17 +48,17 @@ class Navigation:
|
||||
|
||||
|
||||
class Section(StructureItem):
|
||||
def __init__(self, title: str, children: list[StructureItem]) -> None:
|
||||
self.title = title
|
||||
def __init__(self, title: Markup, children: list[StructureItem]) -> None:
|
||||
self.title = Markup(title)
|
||||
self.children = children
|
||||
|
||||
self.active = False
|
||||
|
||||
def __repr__(self):
|
||||
name = self.__class__.__name__
|
||||
return f"{name}(title={self.title!r})"
|
||||
return f"{name}(title={str(self.title)!r})"
|
||||
|
||||
title: str
|
||||
title: Markup
|
||||
"""The title of the section."""
|
||||
|
||||
children: list[StructureItem]
|
||||
@@ -95,16 +97,16 @@ class Section(StructureItem):
|
||||
|
||||
|
||||
class Link(StructureItem):
|
||||
def __init__(self, title: str, url: str):
|
||||
self.title = title
|
||||
def __init__(self, title: Markup, url: str):
|
||||
self.title = Markup(title)
|
||||
self.url = url
|
||||
|
||||
def __repr__(self):
|
||||
name = self.__class__.__name__
|
||||
title = f"{self.title!r}" if self.title is not None else '[blank]'
|
||||
title = f"{str(self.title)!r}" if self.title is not None else '[blank]'
|
||||
return f"{name}(title={title}, url={self.url!r})"
|
||||
|
||||
title: str
|
||||
title: Markup
|
||||
"""The title of the link. This would generally be used as the label of the link."""
|
||||
|
||||
url: str
|
||||
|
||||
@@ -15,6 +15,7 @@ import markdown.htmlparser # type: ignore
|
||||
import markdown.postprocessors
|
||||
import markdown.treeprocessors
|
||||
from markdown.util import AMP_SUBSTITUTE
|
||||
from markupsafe import Markup
|
||||
|
||||
from mkdocs import utils
|
||||
from mkdocs.structure import StructureItem
|
||||
@@ -33,11 +34,11 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Page(StructureItem):
|
||||
def __init__(self, title: str | None, file: File, config: MkDocsConfig) -> None:
|
||||
def __init__(self, title: Markup | None, file: File, config: MkDocsConfig) -> None:
|
||||
file.page = self
|
||||
self.file = file
|
||||
if title is not None:
|
||||
self.title = title
|
||||
self.title = Markup(title)
|
||||
|
||||
# Navigation attributes
|
||||
self.children = None
|
||||
@@ -68,7 +69,7 @@ class Page(StructureItem):
|
||||
|
||||
def __repr__(self):
|
||||
name = self.__class__.__name__
|
||||
title = f"{self.title!r}" if self.title is not None else '[blank]'
|
||||
title = f"{str(self.title)!r}" if self.title is not None else '[blank]'
|
||||
url = self.abs_url or self.file.url
|
||||
return f"{name}(title={title}, url={url!r})"
|
||||
|
||||
@@ -281,7 +282,7 @@ class Page(StructureItem):
|
||||
extract_title_ext = _ExtractTitleTreeprocessor()
|
||||
extract_title_ext._register(md)
|
||||
|
||||
self.content = md.convert(self.markdown)
|
||||
self.content = Markup(md.convert(self.markdown))
|
||||
self.toc = get_toc(getattr(md, 'toc_tokens', []))
|
||||
self._title_from_render = extract_title_ext.title
|
||||
self.present_anchor_ids = (
|
||||
|
||||
@@ -5,15 +5,18 @@ For the sake of simplicity we use the Python-Markdown `toc` extension to
|
||||
generate a list of dicts for each toc item, and then store it as AnchorLinks to
|
||||
maintain compatibility with older versions of MkDocs.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Iterator, TypedDict
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
|
||||
class _TocToken(TypedDict):
|
||||
level: int
|
||||
id: str
|
||||
name: str
|
||||
name: Markup
|
||||
children: list[_TocToken]
|
||||
|
||||
|
||||
@@ -28,11 +31,11 @@ def get_toc(toc_tokens: list[_TocToken]) -> TableOfContents:
|
||||
class AnchorLink:
|
||||
"""A single entry in the table of contents."""
|
||||
|
||||
def __init__(self, title: str, id: str, level: int) -> None:
|
||||
self.title, self.id, self.level = title, id, level
|
||||
def __init__(self, title: Markup, id: str, level: int) -> None:
|
||||
self.title, self.id, self.level = Markup(title), id, level
|
||||
self.children = []
|
||||
|
||||
title: str
|
||||
title: Markup
|
||||
"""The text of the item, as HTML."""
|
||||
|
||||
@property
|
||||
|
||||
@@ -7,6 +7,7 @@ import unittest
|
||||
from unittest import mock
|
||||
|
||||
import markdown
|
||||
from markupsafe import Markup
|
||||
|
||||
from mkdocs.config.defaults import MkDocsConfig
|
||||
from mkdocs.structure.files import File, Files
|
||||
@@ -749,7 +750,7 @@ class RelativePathExtensionTests(unittest.TestCase):
|
||||
) -> str:
|
||||
cfg = load_config(docs_dir=DOCS_DIR, **kwargs)
|
||||
fs = [File(f, cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) for f in files]
|
||||
pg = Page('Foo', fs[0], cfg)
|
||||
pg = Page(Markup('Foo'), fs[0], cfg)
|
||||
|
||||
with mock.patch('mkdocs.structure.files.open', mock.mock_open(read_data=content)):
|
||||
pg.read_source(cfg)
|
||||
|
||||
@@ -156,7 +156,7 @@ class Theme(MutableMapping[str, Any]):
|
||||
"""Return a Jinja environment for the theme."""
|
||||
loader = jinja2.FileSystemLoader(self.dirs)
|
||||
# No autoreload because editing a template in the middle of a build is not useful.
|
||||
env = jinja2.Environment(loader=loader, auto_reload=False)
|
||||
env = jinja2.Environment(loader=loader, auto_reload=False, autoescape=True)
|
||||
env.filters['url'] = templates.url_filter
|
||||
env.filters['script_tag'] = templates.script_tag_filter
|
||||
localization.install_translations(env, self.locale, self.dirs)
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
{%- if config.copyright %}
|
||||
<p>{{ config.copyright }}</p>
|
||||
{%- endif %}
|
||||
<p>{% trans mkdocs_link='<a href="https://www.mkdocs.org/">MkDocs</a>' %}Documentation built with {{ mkdocs_link }}.{% endtrans %}</p>
|
||||
<p>{% trans mkdocs_link='<a href="https://www.mkdocs.org/">MkDocs</a>'|safe %}Documentation built with {{ mkdocs_link }}.{% endtrans %}</p>
|
||||
{%- endblock %}
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
||||
{% trans mkdocs_link='<a href="https://www.mkdocs.org/">MkDocs</a>', sphinx_link='<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>', rtd_link='<a href="https://readthedocs.org">Read the Docs</a>' %}Built with %(mkdocs_link)s using a %(sphinx_link)s provided by %(rtd_link)s.{% endtrans %}
|
||||
{% trans mkdocs_link='<a href="https://www.mkdocs.org/">MkDocs</a>'|safe, sphinx_link='<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>'|safe, rtd_link='<a href="https://readthedocs.org">Read the Docs</a>'|safe %}Built with %(mkdocs_link)s using a %(sphinx_link)s provided by %(rtd_link)s.{% endtrans %}
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user