Fixed crash of blog plugin when pagination is disabled

This commit is contained in:
squidfunk 2023-11-23 11:53:43 +01:00
parent 0fd2a3940e
commit ae056297bf
No known key found for this signature in database
GPG Key ID: 5ED40BC4F9C436DF
4 changed files with 62 additions and 98 deletions

View File

@ -27,6 +27,8 @@ import yaml
from babel.dates import format_date from babel.dates import format_date
from datetime import datetime from datetime import datetime
from jinja2 import pass_context
from jinja2.runtime import Context
from mkdocs.config.defaults import MkDocsConfig from mkdocs.config.defaults import MkDocsConfig
from mkdocs.exceptions import PluginError from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin, event_priority from mkdocs.plugins import BasePlugin, event_priority
@ -35,6 +37,7 @@ from mkdocs.structure.files import File, Files, InclusionLevel
from mkdocs.structure.nav import Navigation, Section from mkdocs.structure.nav import Navigation, Section
from mkdocs.structure.pages import Page from mkdocs.structure.pages import Page
from mkdocs.utils import copy_file, get_relative_url from mkdocs.utils import copy_file, get_relative_url
from mkdocs.utils.templates import url_filter
from paginate import Page as Pagination from paginate import Page as Pagination
from shutil import rmtree from shutil import rmtree
from tempfile import mkdtemp from tempfile import mkdtemp
@ -44,7 +47,6 @@ from .author import Authors
from .config import BlogConfig from .config import BlogConfig
from .readtime import readtime from .readtime import readtime
from .structure import Archive, Category, Excerpt, Post, View from .structure import Archive, Category, Excerpt, Post, View
from .templates import url_filter
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Classes # Classes
@ -148,7 +150,6 @@ class BlogPlugin(BasePlugin[BlogConfig]):
if self.config.pagination: if self.config.pagination:
for view in self._resolve_views(self.blog): for view in self._resolve_views(self.blog):
for page in self._generate_pages(view, config, files): for page in self._generate_pages(view, config, files):
page.file.inclusion = InclusionLevel.EXCLUDED
view.pages.append(page) view.pages.append(page)
# Ensure that entrypoint is always included in navigation # Ensure that entrypoint is always included in navigation
@ -180,6 +181,7 @@ class BlogPlugin(BasePlugin[BlogConfig]):
# Revert temporary exclusion of views from navigation # Revert temporary exclusion of views from navigation
for view in self._resolve_views(self.blog): for view in self._resolve_views(self.blog):
view.file.inclusion = self.blog.file.inclusion
for page in view.pages: for page in view.pages:
page.file.inclusion = self.blog.file.inclusion page.file.inclusion = self.blog.file.inclusion
@ -298,9 +300,25 @@ class BlogPlugin(BasePlugin[BlogConfig]):
def date_filter(date: datetime): def date_filter(date: datetime):
return self._format_date_for_post(date, config) return self._format_date_for_post(date, config)
# Patch URL template filter to add support for paginated views, i.e.,
# that paginated views never link to themselves but to the main view
@pass_context
def url_filter_with_pagination(context: Context, url: str | None):
page = context["page"]
# If the current page is a view, check if the URL links to the page
# itself, and replace it with the URL of the main view
if isinstance(page, View):
view = self._resolve_original(page)
if page.url == url:
url = view.url
# Forward to original template filter
return url_filter(context, url)
# Register custom template filters # Register custom template filters
env.filters["date"] = date_filter env.filters["date"] = date_filter
env.filters["url"] = url_filter env.filters["url"] = url_filter_with_pagination
# Prepare view for rendering (run latest) - views are rendered last, as we # Prepare view for rendering (run latest) - views are rendered last, as we
# need to mutate the navigation to account for pagination. The main problem # need to mutate the navigation to account for pagination. The main problem
@ -527,7 +545,7 @@ class BlogPlugin(BasePlugin[BlogConfig]):
# Resolve original page or view (e.g. for paginated views) # Resolve original page or view (e.g. for paginated views)
def _resolve_original(self, page: Page): def _resolve_original(self, page: Page):
if isinstance(page, View): if isinstance(page, View) and page.pages:
return page.pages[0] return page.pages[0]
else: else:
return page return page
@ -550,8 +568,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Create file in temporary directory # Create file in temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
self._save_to_file(file.abs_src_path, f"# {name}") self._save_to_file(file.abs_src_path, f"# {name}")
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view - we don't explicitly set the title of # Create and yield view - we don't explicitly set the title of
# the view, so authors can override them in the page's content # the view, so authors can override them in the page's content
@ -585,8 +605,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Create file in temporary directory # Create file in temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
self._save_to_file(file.abs_src_path, f"# {name}") self._save_to_file(file.abs_src_path, f"# {name}")
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view - we don't explicitly set the title of # Create and yield view - we don't explicitly set the title of
# the view, so authors can override them in the page's content # the view, so authors can override them in the page's content
@ -615,8 +637,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Copy file to temporary directory # Copy file to temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
copy_file(view.file.abs_src_path, file.abs_src_path) copy_file(view.file.abs_src_path, file.abs_src_path)
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view # Create and yield view
if not isinstance(file.page, View): if not isinstance(file.page, View):

View File

@ -1,42 +0,0 @@
# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from jinja2 import pass_context
from jinja2.runtime import Context
from material.plugins.blog.structure import View
from mkdocs.utils.templates import url_filter as _url_filter
# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
# Filter for normalizing URLs with support for paginated views
@pass_context
def url_filter(context: Context, url: str):
page = context["page"]
# If the current page is a view, check if the URL links to the page
# itself, and replace it with the URL of the main view
if isinstance(page, View):
if page.url == url:
url = page.pages[0].url
# Forward to original template filter
return _url_filter(context, url)

View File

@ -27,6 +27,8 @@ import yaml
from babel.dates import format_date from babel.dates import format_date
from datetime import datetime from datetime import datetime
from jinja2 import pass_context
from jinja2.runtime import Context
from mkdocs.config.defaults import MkDocsConfig from mkdocs.config.defaults import MkDocsConfig
from mkdocs.exceptions import PluginError from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin, event_priority from mkdocs.plugins import BasePlugin, event_priority
@ -35,6 +37,7 @@ from mkdocs.structure.files import File, Files, InclusionLevel
from mkdocs.structure.nav import Navigation, Section from mkdocs.structure.nav import Navigation, Section
from mkdocs.structure.pages import Page from mkdocs.structure.pages import Page
from mkdocs.utils import copy_file, get_relative_url from mkdocs.utils import copy_file, get_relative_url
from mkdocs.utils.templates import url_filter
from paginate import Page as Pagination from paginate import Page as Pagination
from shutil import rmtree from shutil import rmtree
from tempfile import mkdtemp from tempfile import mkdtemp
@ -44,7 +47,6 @@ from .author import Authors
from .config import BlogConfig from .config import BlogConfig
from .readtime import readtime from .readtime import readtime
from .structure import Archive, Category, Excerpt, Post, View from .structure import Archive, Category, Excerpt, Post, View
from .templates import url_filter
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Classes # Classes
@ -148,7 +150,6 @@ class BlogPlugin(BasePlugin[BlogConfig]):
if self.config.pagination: if self.config.pagination:
for view in self._resolve_views(self.blog): for view in self._resolve_views(self.blog):
for page in self._generate_pages(view, config, files): for page in self._generate_pages(view, config, files):
page.file.inclusion = InclusionLevel.EXCLUDED
view.pages.append(page) view.pages.append(page)
# Ensure that entrypoint is always included in navigation # Ensure that entrypoint is always included in navigation
@ -180,6 +181,7 @@ class BlogPlugin(BasePlugin[BlogConfig]):
# Revert temporary exclusion of views from navigation # Revert temporary exclusion of views from navigation
for view in self._resolve_views(self.blog): for view in self._resolve_views(self.blog):
view.file.inclusion = self.blog.file.inclusion
for page in view.pages: for page in view.pages:
page.file.inclusion = self.blog.file.inclusion page.file.inclusion = self.blog.file.inclusion
@ -298,9 +300,25 @@ class BlogPlugin(BasePlugin[BlogConfig]):
def date_filter(date: datetime): def date_filter(date: datetime):
return self._format_date_for_post(date, config) return self._format_date_for_post(date, config)
# Patch URL template filter to add support for paginated views, i.e.,
# that paginated views never link to themselves but to the main view
@pass_context
def url_filter_with_pagination(context: Context, url: str | None):
page = context["page"]
# If the current page is a view, check if the URL links to the page
# itself, and replace it with the URL of the main view
if isinstance(page, View):
view = self._resolve_original(page)
if page.url == url:
url = view.url
# Forward to original template filter
return url_filter(context, url)
# Register custom template filters # Register custom template filters
env.filters["date"] = date_filter env.filters["date"] = date_filter
env.filters["url"] = url_filter env.filters["url"] = url_filter_with_pagination
# Prepare view for rendering (run latest) - views are rendered last, as we # Prepare view for rendering (run latest) - views are rendered last, as we
# need to mutate the navigation to account for pagination. The main problem # need to mutate the navigation to account for pagination. The main problem
@ -527,7 +545,7 @@ class BlogPlugin(BasePlugin[BlogConfig]):
# Resolve original page or view (e.g. for paginated views) # Resolve original page or view (e.g. for paginated views)
def _resolve_original(self, page: Page): def _resolve_original(self, page: Page):
if isinstance(page, View): if isinstance(page, View) and page.pages:
return page.pages[0] return page.pages[0]
else: else:
return page return page
@ -550,8 +568,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Create file in temporary directory # Create file in temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
self._save_to_file(file.abs_src_path, f"# {name}") self._save_to_file(file.abs_src_path, f"# {name}")
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view - we don't explicitly set the title of # Create and yield view - we don't explicitly set the title of
# the view, so authors can override them in the page's content # the view, so authors can override them in the page's content
@ -585,8 +605,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Create file in temporary directory # Create file in temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
self._save_to_file(file.abs_src_path, f"# {name}") self._save_to_file(file.abs_src_path, f"# {name}")
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view - we don't explicitly set the title of # Create and yield view - we don't explicitly set the title of
# the view, so authors can override them in the page's content # the view, so authors can override them in the page's content
@ -615,8 +637,10 @@ class BlogPlugin(BasePlugin[BlogConfig]):
file = self._path_to_file(path, config) file = self._path_to_file(path, config)
files.append(file) files.append(file)
# Copy file to temporary directory # Copy file to temporary directory and temporarily remove
# from navigation, as we'll add it at a specific location
copy_file(view.file.abs_src_path, file.abs_src_path) copy_file(view.file.abs_src_path, file.abs_src_path)
file.inclusion = InclusionLevel.EXCLUDED
# Create and yield view # Create and yield view
if not isinstance(file.page, View): if not isinstance(file.page, View):

View File

@ -1,42 +0,0 @@
# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from jinja2 import pass_context
from jinja2.runtime import Context
from material.plugins.blog.structure import View
from mkdocs.utils.templates import url_filter as _url_filter
# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
# Filter for normalizing URLs with support for paginated views
@pass_context
def url_filter(context: Context, url: str):
page = context["page"]
# If the current page is a view, check if the URL links to the page
# itself, and replace it with the URL of the main view
if isinstance(page, View):
if page.url == url:
url = page.pages[0].url
# Forward to original template filter
return _url_filter(context, url)