Skip to content

Commit

Permalink
Feature: use author name and email from .authors.yml set up in Mate…
Browse files Browse the repository at this point in the history
…rial Blog (#340)

Related to: #250
  • Loading branch information
Guts authored Dec 2, 2024
2 parents 9e5f2b3 + c15778a commit e1364a0
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 23 deletions.
60 changes: 60 additions & 0 deletions docs/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,66 @@ title: Integrations
icon: octicons/plug-16
---

## Blog plugin (from Material theme)

Since version 1.17, the plugin integrates with the [Blog plugin (shipped with Material theme)](https://squidfunk.github.io/mkdocs-material/plugins/blog/) (see also [the tutorial about blog + RSS plugins](https://squidfunk.github.io/mkdocs-material/tutorials/blogs/engage/)).

In some cases, the RSS plugin needs to work with the Material Blog:

- for blog posts, the structure of the path to social cards is depending on blog configuration
- retrieve the author's name from the `.authors.yml` file
- optionnaly retrieve the author's email from the `.authors.yml` file

If you don't want this integration, you can disable it with the option: `use_material_blog=false`.

> See [related section in settings](./configuration.md#use_material_blog).
### Example of blog authors with email

```yaml title="docs/blog/.authors.yml"
authors:
alexvoss:
name: Alex Voss
description: Weltenwanderer
avatar: https://github.com/alexvoss.png
guts:
avatar: https://cdn.geotribu.fr/img/internal/contributeurs/jmou.jfif
description: GIS Watchman
name: Julien Moura
url: https://github.com/guts/
email: [email protected]
```
This given Markdown post:
```markdown title="blog/posts/demo.md"
---
authors:
- alexvoss
- guts
date: 2024-12-02
categories:
- tutorial
---

# Demonstration blog post

[...]
```

Will be rendered as:

```xml title="/build/site/feed_rss_created.xml"
[...]
<item>
<title>Demonstration blog post</title>
<author>Alex Voss</author>
<author>Julien Moura ([email protected])</author>
[...]
```

----

## Social Cards plugin (from Material theme)

Since version 1.10, the plugin integrates with the [Social Cards plugin (shipped with Material theme)](https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/) (see also [the full plugin documentation here](https://squidfunk.github.io/mkdocs-material/plugins/social/)).
Expand Down
2 changes: 0 additions & 2 deletions mkdocs_rss_plugin/integrations/theme_material_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
# conditional
try:
from material import __version__ as material_version
from material.plugins.blog.plugin import BlogPlugin
from pymdownx.slugs import slugify

except ImportError:
material_version = None
Expand Down
45 changes: 45 additions & 0 deletions mkdocs_rss_plugin/integrations/theme_material_blog_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
# ##################################

# standard library
from functools import lru_cache
from pathlib import Path
from typing import Optional

# 3rd party
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.plugins import get_plugin_logger
from mkdocs.structure.pages import Page

# package
from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME
Expand Down Expand Up @@ -101,3 +104,45 @@ def is_blog_plugin_enabled_mkdocs(
logger.debug("Material blog plugin is enabled in Mkdocs configuration.")
self.IS_BLOG_PLUGIN_ENABLED = True
return True

@lru_cache
def author_name_from_id(self, author_id: str) -> str:
"""Return author name from author_id used in Material blog plugin (.authors.yml).
Args:
author_id (str): author key in .authors.yml
Returns:
str: author name or passed author_id if not found within .authors.yml
"""
if (
self.blog_plugin_cfg.config.authors
and isinstance(self.blog_plugin_cfg, BlogPlugin)
and hasattr(self.blog_plugin_cfg, "authors")
and isinstance(self.blog_plugin_cfg.authors, dict)
):
if author_id in self.blog_plugin_cfg.authors:
author_metadata = self.blog_plugin_cfg.authors.get(author_id)
if "email" in self.blog_plugin_cfg.authors.get(author_id):
return f"{author_metadata.get('name')} ({author_metadata.get('email')})"
else:
return author_metadata.get("name")
else:
logger.error(
f"Author ID '{author_id}' is not part of known authors: "
f"{self.blog_plugin_cfg.authors}. Returning author_id."
)
return author_id

def is_page_a_blog_post(self, mkdocs_page: Page) -> bool:
"""Identifies if the given page is part of Material Blog.
Args:
mkdocs_page (Page): page to identify
Returns:
bool: True if the given page is a Material Blog post.
"""
return Path(mkdocs_page.file.src_uri).is_relative_to(
self.blog_plugin_cfg.config.blog_dir
)
22 changes: 9 additions & 13 deletions mkdocs_rss_plugin/integrations/theme_material_social_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
# conditional
try:
from material import __version__ as material_version
from pymdownx.slugs import slugify

except ImportError:
material_version = None
Expand Down Expand Up @@ -261,10 +260,9 @@ def get_social_card_build_path_for_page(
mkdocs_site_dir = self.mkdocs_site_build_dir

# if page is a blog post
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
if (
self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED
and self.integration_material_blog.is_page_a_blog_post(mkdocs_page)
):
expected_built_card_path = Path(
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
Expand Down Expand Up @@ -306,10 +304,9 @@ def get_social_card_cache_path_for_page(self, mkdocs_page: Page) -> Optional[Pat
if self.IS_INSIDERS:

# if page is a blog post
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
if (
self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED
and self.integration_material_blog.is_page_a_blog_post(mkdocs_page)
):
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
f"assets/images/social/{Path(mkdocs_page.file.dest_uri).parent}.png"
Expand Down Expand Up @@ -376,10 +373,9 @@ def get_social_card_url_for_page(
mkdocs_site_url = self.mkdocs_site_url

# if page is a blog post
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
if (
self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED
and self.integration_material_blog.is_page_a_blog_post(mkdocs_page)
):
page_social_card = (
f"{mkdocs_site_url}assets/images/social/"
Expand Down
11 changes: 10 additions & 1 deletion mkdocs_rss_plugin/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,16 @@ def get_authors_from_meta(self, in_page: Page) -> Optional[tuple[str]]:
if isinstance(in_page.meta.get("authors"), str):
return (in_page.meta.get("authors"),)
elif isinstance(in_page.meta.get("authors"), (list, tuple)):
return tuple(in_page.meta.get("authors"))
if (
self.material_blog.IS_ENABLED
and self.material_blog.is_page_a_blog_post(in_page)
):
return [
self.material_blog.author_name_from_id(author_id)
for author_id in in_page.meta.get("authors")
]
else:
return tuple(in_page.meta.get("authors"))
else:
logging.warning(
"Type of authors value in page.meta (%s) is not valid. "
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/docs/blog/.authors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ authors:
name: Julien Moura
slug: julien-moura
url: https://geotribu.fr/team/julien-moura/
email: [email protected]
2 changes: 1 addition & 1 deletion tests/fixtures/docs/blog/posts/firstpost.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
authors:
- alexvoss
- guts
date: 2023-10-11
categories:
- meta
Expand Down Expand Up @@ -29,4 +30,3 @@ pharetra, pellentesque risus in, consectetur urna. Nulla id enim facilisis
arcu tincidunt pulvinar. Vestibulum laoreet risus scelerisque porta congue.
In velit purus, dictum quis neque nec, molestie viverra risus. Nam pellentesque
tellus id elit ultricies, vel finibus erat cursus.

11 changes: 5 additions & 6 deletions tests/fixtures/mkdocs_items_material_blog_enabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ site_description: Test RSS with blog plugin also enabled
site_url: https://guts.github.io/mkdocs-rss-plugin

plugins:
- blog:
blog_dir: blog
authors_profiles: true
- rss:
use_material_blog: true
- blog:
blog_dir: blog
authors_profiles: true
- rss

theme:
name: material
name: material
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
site_name: Test RSS Plugin
site_description: Test RSS with blog plugin also enabled
site_url: https://guts.github.io/mkdocs-rss-plugin

plugins:
- blog:
blog_dir: blog
authors_profiles: true
- rss:
use_material_blog: false

theme:
name: material
72 changes: 72 additions & 0 deletions tests/test_integrations_material_blog.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
# ##################################

# Standard library
import tempfile
import unittest
from logging import DEBUG, getLogger
from pathlib import Path
from traceback import format_exception

# 3rd party
import feedparser
from mkdocs.config import load_config

# package
Expand All @@ -41,6 +44,24 @@ class TestRssPluginIntegrationsMaterialBlog(BaseTest):
"""Test integration of Material Blog plugin with RSS plugin."""

# -- TESTS ---------------------------------------------------------
def test_plugin_config_social_cards_enabled_but_integration_disabled(self):
# default reference
cfg_mkdocs = load_config(
str(
Path(
"tests/fixtures/mkdocs_items_material_blog_enabled_but_integration_disabled.yml"
).resolve()
)
)

integration_social_cards = IntegrationMaterialBlog(
mkdocs_config=cfg_mkdocs,
switch_force=cfg_mkdocs.plugins.get("rss").config.use_material_blog,
)
self.assertTrue(integration_social_cards.IS_THEME_MATERIAL)
self.assertTrue(integration_social_cards.IS_BLOG_PLUGIN_ENABLED)
self.assertFalse(integration_social_cards.IS_ENABLED)

def test_plugin_config_blog_enabled(self):
# default reference
cfg_mkdocs = load_config(
Expand All @@ -52,6 +73,57 @@ def test_plugin_config_blog_enabled(self):
self.assertTrue(integration_social_cards.IS_BLOG_PLUGIN_ENABLED)
self.assertTrue(integration_social_cards.IS_ENABLED)

def test_simple_build(self):
with tempfile.TemporaryDirectory() as tmpdirname:
cli_result = self.build_docs_setup(
testproject_path="docs",
mkdocs_yml_filepath=Path(
"tests/fixtures/mkdocs_items_material_blog_enabled.yml"
),
output_path=tmpdirname,
strict=False,
)

if cli_result.exception is not None:
e = cli_result.exception
logger.debug(format_exception(type(e), e, e.__traceback__))

self.assertEqual(cli_result.exit_code, 0)
self.assertIsNone(cli_result.exception)

# created items
feed_parsed = feedparser.parse(Path(tmpdirname) / "feed_rss_created.xml")
self.assertEqual(feed_parsed.bozo, 0)

# updated items
feed_parsed = feedparser.parse(Path(tmpdirname) / "feed_rss_updated.xml")
self.assertEqual(feed_parsed.bozo, 0)

with tempfile.TemporaryDirectory() as tmpdirname:
cli_result = self.build_docs_setup(
testproject_path="docs",
mkdocs_yml_filepath=Path(
"tests/fixtures/mkdocs_items_material_blog_enabled_but_integration_disabled.yml"
),
output_path=tmpdirname,
strict=False,
)

if cli_result.exception is not None:
e = cli_result.exception
logger.debug(format_exception(type(e), e, e.__traceback__))

self.assertEqual(cli_result.exit_code, 0)
self.assertIsNone(cli_result.exception)

# created items
feed_parsed = feedparser.parse(Path(tmpdirname) / "feed_rss_created.xml")
self.assertEqual(feed_parsed.bozo, 0)

# updated items
feed_parsed = feedparser.parse(Path(tmpdirname) / "feed_rss_updated.xml")
self.assertEqual(feed_parsed.bozo, 0)


# ##############################################################################
# ##### Stand alone program ########
Expand Down

0 comments on commit e1364a0

Please sign in to comment.