diff --git a/docs/dev-guide/themes.md b/docs/dev-guide/themes.md
index ce6a5a6a..b5ea0fc2 100644
--- a/docs/dev-guide/themes.md
+++ b/docs/dev-guide/themes.md
@@ -354,6 +354,11 @@ All `page` objects contain the following attributes:
show_root_full_path: false
heading_level: 5
+::: properdocs.structure.pages.Page.content_title
+ options:
+ show_root_full_path: false
+ heading_level: 5
+
::: properdocs.structure.pages.Page.content
options:
show_root_full_path: false
@@ -453,7 +458,7 @@ object to alter the behavior. For example, to display a different title
on the homepage:
```django
-{% if not page.is_homepage %}{{ page.title }} - {% endif %}{{ site_name }}
+
{% if not page.is_homepage %}{{ page.title }} - {% endif %}{{ site_name }}
```
::: properdocs.structure.pages.Page.previous_page
diff --git a/properdocs/structure/pages.py b/properdocs/structure/pages.py
index b03bacfb..afd76034 100644
--- a/properdocs/structure/pages.py
+++ b/properdocs/structure/pages.py
@@ -52,9 +52,6 @@ def __init__(self, title: str | None, file: File, config: ProperDocsConfig) -> N
)
# Placeholders to be filled in later in the build process.
- self.markdown = None
- self._title_from_render: str | None = None
- self.content = None
self.toc = [] # type: ignore
self.meta = {}
@@ -71,14 +68,16 @@ def __repr__(self):
url = self.abs_url or self.file.url
return f"{name}(title={title}, url={url!r})"
- markdown: str | None
+ markdown: str | None = None
"""The original Markdown content from the file."""
- content: str | None
+ content: str | None = None
"""The rendered Markdown as HTML, this is the contents of the documentation.
Populated after `.render()`."""
+ _title_from_render: str | None = None
+
toc: TableOfContents
"""An iterable object representing the Table of contents for a page. Each item in
the `toc` is an [`AnchorLink`][properdocs.structure.toc.AnchorLink]."""
@@ -227,15 +226,15 @@ def _set_title(self) -> None:
@weak_property
def title(self) -> str | None: # type: ignore[override]
"""
- Returns the title for the current page.
+ Returns the title for the page, in the context of the nav.
Before calling `read_source()`, this value is empty. It can also be updated by `render()`.
Checks these in order and uses the first that returns a valid title:
- - value provided on init (passed in from config)
+ - value specified in the `nav` config in properdocs.yml (or the value that was passed when creating the `Page` programmatically)
- value of metadata 'title'
- - content of the first H1 in Markdown content
+ - content of the first H1 heading in Markdown content
- convert filename to title
"""
if self.markdown is None:
@@ -260,6 +259,39 @@ def title(self) -> str | None: # type: ignore[override]
title = title.capitalize()
return title
+ @property
+ def content_title(self) -> str:
+ """
+ Returns the title for the page, in the context of the current page's content.
+
+ NEW: **New in ProperDocs 1.7.**
+
+ Similar to `title` but prioritizes the title from the document itself over the title
+ specified in the `nav` config.
+
+ For themes, this should be preferred within the `` tag. To apply the preferred behavior but keep compatibility with older versions, you can use:
+
+ ```jinja
+ {{ page.content_title or page.title }}
+ ```
+
+ Checks these in order and uses the first that returns a valid title:
+
+ - value of metadata 'title'
+ - content of the first H1 heading in Markdown content
+ - value specified in the `nav` config in mkdocs.yml (or the value that was passed when creating the `Page` programmatically)
+ - convert filename to title
+
+ When using this property outside of themes, do not access it before `render()` was called on the content, or it will raise.
+ """
+ if self.content is None:
+ raise RuntimeError("`content` field hasn't been set (via `render`)")
+ if 'title' in self.meta:
+ return self.meta['title']
+ if self._title_from_render:
+ return self._title_from_render
+ return self.title
+
def render(self, config: ProperDocsConfig, files: Files) -> None:
"""Convert the Markdown source file to HTML as per the config."""
if self.markdown is None:
diff --git a/properdocs/tests/structure/page_tests.py b/properdocs/tests/structure/page_tests.py
index 851ad0b0..cd363c9c 100644
--- a/properdocs/tests/structure/page_tests.py
+++ b/properdocs/tests/structure/page_tests.py
@@ -318,8 +318,10 @@ def test_page_title_from_markdown(self):
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
self.assertEqual(pg.title, 'Welcome to ProperDocs')
+ self.assertRaises(RuntimeError, lambda: pg.content_title)
pg.render(cfg, Files([fl]))
self.assertEqual(pg.title, 'Welcome to ProperDocs')
+ self.assertEqual(pg.content_title, 'Welcome to ProperDocs')
def _test_extract_title(self, content, expected, extensions={}):
md = markdown.Markdown(extensions=list(extensions.keys()), extension_configs=extensions)
@@ -437,10 +439,16 @@ def test_page_title_from_meta(self):
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
+ self.assertRaises(RuntimeError, lambda: pg.content_title)
self.assertEqual(pg.title, 'A Page Title')
self.assertEqual(pg.toc, [])
pg.render(cfg, Files([fl]))
self.assertEqual(pg.title, 'A Page Title')
+ self.assertEqual(pg.content_title, 'A Page Title')
+ # Test setting the title directly (e.g. through a plugin):
+ pg.title = 'New title'
+ self.assertEqual(pg.title, 'New title')
+ self.assertEqual(pg.content_title, 'A Page Title')
def test_page_title_from_filename(self):
cfg = load_config(docs_dir=DOCS_DIR)
@@ -463,9 +471,16 @@ def test_page_title_from_filename(self):
self.assertEqual(pg.next_page, None)
self.assertEqual(pg.parent, None)
self.assertEqual(pg.previous_page, None)
+ self.assertRaises(RuntimeError, lambda: pg.content_title)
self.assertEqual(pg.title, 'Page title')
pg.render(cfg, Files([fl]))
self.assertEqual(pg.title, 'Page title')
+ self.assertEqual(pg.content_title, 'Page title')
+ # Test setting the title directly (e.g. through a plugin):
+ pg.title = 'New title'
+ self.assertEqual(pg.title, 'New title')
+ # Content title is missing, title still takes precedence.
+ self.assertEqual(pg.content_title, 'New title')
def test_page_title_from_capitalized_filename(self):
cfg = load_config(docs_dir=DOCS_DIR)