Skip to content

Commit bffc8ba

Browse files
authored
feature(integration): support social cards for blog plugin posts (#326)
This PR improves the Material Social Cards plugin integration by adding support for social cards generated for Material Blog plugin posts (https://squidfunk.github.io/mkdocs-material/plugins/blog/).
2 parents 4179aaf + 8bb75c5 commit bffc8ba

File tree

9 files changed

+207
-13
lines changed

9 files changed

+207
-13
lines changed

mkdocs_rss_plugin/integrations/theme_material_social_plugin.py

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121
# conditional
2222
try:
2323
from material import __version__ as material_version
24+
from material.plugins.blog.plugin import BlogPlugin
25+
from pymdownx.slugs import slugify
26+
2427
except ImportError:
2528
material_version = None
2629

30+
2731
# ############################################################################
2832
# ########## Globals #############
2933
# ################################
@@ -38,6 +42,7 @@
3842
class IntegrationMaterialSocialCards:
3943
# attributes
4044
IS_ENABLED: bool = True
45+
IS_BLOG_PLUGIN_ENABLED: bool = True
4146
IS_SOCIAL_PLUGIN_ENABLED: bool = True
4247
IS_SOCIAL_PLUGIN_CARDS_ENABLED: bool = True
4348
IS_THEME_MATERIAL: bool = False
@@ -53,6 +58,7 @@ def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> No
5358
it to False to disable it even if Social Cards are enabled in Mkdocs
5459
configuration. Defaults to True.
5560
"""
61+
self.mkdocs_config = mkdocs_config
5662
# check if the integration can be enabled or not
5763
self.IS_SOCIAL_PLUGIN_CARDS_ENABLED = (
5864
self.is_social_plugin_and_cards_enabled_mkdocs(mkdocs_config=mkdocs_config)
@@ -85,6 +91,9 @@ def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> No
8591
self.social_cards_cache_dir = self.get_social_cards_cache_dir(
8692
mkdocs_config=mkdocs_config
8793
)
94+
self.IS_BLOG_PLUGIN_ENABLED = self.is_blog_plugin_enabled_mkdocs(
95+
mkdocs_config=mkdocs_config
96+
)
8897
if self.is_mkdocs_theme_material_insiders():
8998
self.load_cache_cards_manifest()
9099

@@ -123,6 +132,37 @@ def is_mkdocs_theme_material_insiders(self) -> Optional[bool]:
123132
self.IS_INSIDERS = False
124133
return False
125134

135+
def is_blog_plugin_enabled_mkdocs(self, mkdocs_config: MkDocsConfig) -> bool:
136+
"""Check if blog plugin is installed and enabled.
137+
138+
Args:
139+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
140+
141+
Returns:
142+
bool: True if the theme material and the plugin blog is enabled.
143+
"""
144+
if not self.is_mkdocs_theme_material(mkdocs_config=mkdocs_config):
145+
logger.debug("Installed theme is not 'material'. Integration disabled.")
146+
return False
147+
148+
if not mkdocs_config.plugins.get("material/blog"):
149+
logger.debug("Material blog plugin is not listed in configuration.")
150+
self.IS_BLOG_PLUGIN_ENABLED = False
151+
return False
152+
153+
self.blog_plugin_cfg: BlogPlugin | None = mkdocs_config.plugins.get(
154+
"material/blog"
155+
)
156+
157+
if not self.blog_plugin_cfg.config.enabled:
158+
logger.debug("Material blog plugin is installed but disabled.")
159+
self.IS_BLOG_PLUGIN_ENABLED = False
160+
return False
161+
162+
logger.debug("Material blog plugin is enabled in Mkdocs configuration.")
163+
self.IS_BLOG_PLUGIN_ENABLED = True
164+
return True
165+
126166
def is_social_plugin_enabled_mkdocs(self, mkdocs_config: MkDocsConfig) -> bool:
127167
"""Check if social plugin is installed and enabled.
128168
@@ -274,18 +314,29 @@ def get_social_card_build_path_for_page(
274314
if mkdocs_site_dir is None and self.mkdocs_site_build_dir:
275315
mkdocs_site_dir = self.mkdocs_site_build_dir
276316

277-
expected_built_card_path = Path(
278-
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
279-
f"{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
280-
)
317+
# if page is a blog post
318+
if self.IS_BLOG_PLUGIN_ENABLED and Path(
319+
mkdocs_page.file.src_uri
320+
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
321+
expected_built_card_path = Path(
322+
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
323+
f"{Path(mkdocs_page.file.dest_uri).parent}.png"
324+
)
325+
else:
326+
expected_built_card_path = Path(
327+
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
328+
f"{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
329+
)
281330

282331
if expected_built_card_path.is_file():
283332
logger.debug(
284-
f"Social card file found in cache folder: {expected_built_card_path}"
333+
f"Social card file found in build folder: {expected_built_card_path}"
285334
)
286335
return expected_built_card_path
287336
else:
288-
logger.debug(f"Not found: {expected_built_card_path}")
337+
logger.debug(
338+
f"Social card not found in build folder: {expected_built_card_path}"
339+
)
289340
return None
290341

291342
def get_social_card_cache_path_for_page(self, mkdocs_page: Page) -> Optional[Path]:
@@ -305,16 +356,28 @@ def get_social_card_cache_path_for_page(self, mkdocs_page: Page) -> Optional[Pat
305356
Path: path to the image in local cache folder if it exists
306357
"""
307358
if self.IS_INSIDERS:
308-
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
309-
f"assets/images/social/{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
310-
)
359+
360+
# if page is a blog post
361+
if self.IS_BLOG_PLUGIN_ENABLED and Path(
362+
mkdocs_page.file.src_uri
363+
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
364+
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
365+
f"assets/images/social/{Path(mkdocs_page.file.dest_uri).parent}.png"
366+
)
367+
else:
368+
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
369+
f"assets/images/social/{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
370+
)
371+
311372
if expected_cached_card_path.is_file():
312373
logger.debug(
313374
f"Social card file found in cache folder: {expected_cached_card_path}"
314375
)
315376
return expected_cached_card_path
316377
else:
317-
logger.debug(f"Not found: {expected_cached_card_path}")
378+
logger.debug(
379+
f"Social card not found in cache folder: {expected_cached_card_path}"
380+
)
318381

319382
else:
320383
if "description" in mkdocs_page.meta:
@@ -362,4 +425,18 @@ def get_social_card_url_for_page(
362425
if mkdocs_site_url is None and self.mkdocs_site_url:
363426
mkdocs_site_url = self.mkdocs_site_url
364427

365-
return f"{mkdocs_site_url}assets/images/social/{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
428+
# if page is a blog post
429+
if self.IS_BLOG_PLUGIN_ENABLED and Path(
430+
mkdocs_page.file.src_uri
431+
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
432+
page_social_card = (
433+
f"{mkdocs_site_url}assets/images/social/"
434+
f"{Path(mkdocs_page.file.dest_uri).parent}.png"
435+
)
436+
else:
437+
page_social_card = (
438+
f"{mkdocs_site_url}assets/images/social/"
439+
f"{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
440+
)
441+
442+
return page_social_card

tests/fixtures/docs/blog/.authors.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
authors:
2+
squidfunk:
3+
name: Martin Donath
4+
description: Creator
5+
avatar: https://github.com/squidfunk.png
6+
alexvoss:
7+
name: Alex Voss
8+
description: Weltenwanderer
9+
avatar: https://github.com/alexvoss.png
10+
guts:
11+
avatar: https://cdn.geotribu.fr/img/internal/contributeurs/jmou.jfif
12+
description: GIS Watchman
13+
name: Julien Moura
14+
slug: julien-moura
15+
url: https://geotribu.fr/team/julien-moura/

tests/fixtures/docs/blog/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Blog
2+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
authors:
3+
- alexvoss
4+
date: 2023-10-11
5+
categories:
6+
- meta
7+
---
8+
9+
# My first blog post
10+
11+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec
12+
maximus ex. Sed consequat, nulla quis malesuada dapibus, elit metus vehicula
13+
erat, ut egestas tellus eros at risus. In hac habitasse platea dictumst.
14+
Phasellus id lacus pulvinar erat consequat pretium. Morbi malesuada arcu mauris
15+
Nam vel justo sem. Nam placerat purus non varius luctus. Integer pretium leo in
16+
sem rhoncus, quis gravida orci mollis. Proin id aliquam est. Vivamus in nunc ac
17+
metus tristique pellentesque. Suspendisse viverra urna in accumsan aliquet.
18+
19+
<!-- more -->
20+
21+
Donec volutpat, elit ac volutpat laoreet, turpis dolor semper nibh, et dictum
22+
massa ex pulvinar elit. Curabitur commodo sit amet dolor sed mattis. Etiam
23+
tempor odio eu nisi gravida cursus. Maecenas ante enim, fermentum sit amet
24+
molestie nec, mollis ac libero. Vivamus sagittis suscipit eros ut luctus.
25+
26+
Nunc vehicula sagittis condimentum. Cras facilisis bibendum lorem et feugiat.
27+
In auctor accumsan ligula, at consectetur erat commodo quis. Morbi ac nunc
28+
pharetra, pellentesque risus in, consectetur urna. Nulla id enim facilisis
29+
arcu tincidunt pulvinar. Vestibulum laoreet risus scelerisque porta congue.
30+
In velit purus, dictum quis neque nec, molestie viverra risus. Nam pellentesque
31+
tellus id elit ultricies, vel finibus erat cursus.
32+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
authors:
3+
- squidfunk
4+
date: 2023-10-12
5+
categories:
6+
- hello
7+
---
8+
9+
# A second post
10+
11+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nec
12+
maximus ex. Sed consequat, nulla quis malesuada dapibus, elit metus vehicula
13+
erat, ut egestas tellus eros at risus. In hac habitasse platea dictumst.
14+
Phasellus id lacus pulvinar erat consequat pretium. Morbi malesuada arcu mauris
15+
Nam vel justo sem. Nam placerat purus non varius luctus. Integer pretium leo in
16+
sem rhoncus, quis gravida orci mollis. Proin id aliquam est. Vivamus in nunc ac
17+
metus tristique pellentesque. Suspendisse viverra urna in accumsan aliquet.
18+
19+
<!-- more -->
20+
21+
Donec volutpat, elit ac volutpat laoreet, turpis dolor semper nibh, et dictum
22+
massa ex pulvinar elit. Curabitur commodo sit amet dolor sed mattis. Etiam
23+
tempor odio eu nisi gravida cursus. Maecenas ante enim, fermentum sit amet
24+
molestie nec, mollis ac libero. Vivamus sagittis suscipit eros ut luctus.
25+
26+
Nunc vehicula sagittis condimentum. Cras facilisis bibendum lorem et feugiat.
27+
In auctor accumsan ligula, at consectetur erat commodo quis. Morbi ac nunc
28+
pharetra, pellentesque risus in, consectetur urna. Nulla id enim facilisis
29+
arcu tincidunt pulvinar. Vestibulum laoreet risus scelerisque porta congue.
30+
In velit purus, dictum quis neque nec, molestie viverra risus. Nam pellentesque
31+
tellus id elit ultricies, vel finibus erat cursus.
32+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
site_name: Test RSS Plugin
2+
site_description: Test social cards support in RSS with blog plugin also enabled
3+
site_url: https://guts.github.io/mkdocs-rss-plugin
4+
5+
plugins:
6+
- blog:
7+
blog_dir: blog
8+
- rss
9+
- social:
10+
enabled: true
11+
cards: true
12+
13+
theme:
14+
name: material

tests/test_build.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,13 @@ def test_simple_build_item_length_unlimited(self):
398398
if feed_item.title not in (
399399
"Page without meta with short text",
400400
"Blog sample",
401+
"Blog",
401402
):
402-
self.assertGreater(len(feed_item.description), 150, feed_item.title)
403+
self.assertGreater(
404+
len(feed_item.description),
405+
150,
406+
f"Failed item title: {feed_item.title}",
407+
)
403408

404409
def test_simple_build_item_delimiter(self):
405410
with tempfile.TemporaryDirectory() as tmpdirname:

tests/test_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class TestConfig(BaseTest):
4141
@classmethod
4242
def setUpClass(cls):
4343
"""Executed when module is loaded before any test."""
44-
cls.config_files = sorted(Path("tests/fixtures/").glob("**/*.yml"))
44+
cls.config_files = sorted(Path("tests/fixtures/").glob("**/mkdocs_*.yml"))
4545
cls.feed_image = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Feed-icon.svg/128px-Feed-icon.svg.png"
4646

4747
def setUp(self):

tests/test_integrations_material_social_cards.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,23 @@ def test_plugin_config_social_plugin_enabled_but_cards_disabled(self):
117117
self.assertFalse(integration_social_cards.IS_SOCIAL_PLUGIN_CARDS_ENABLED)
118118
self.assertFalse(integration_social_cards.IS_ENABLED)
119119

120+
def test_plugin_config_social_cards_enabled_with_blog_plugin(self):
121+
# default reference
122+
cfg_mkdocs = load_config(
123+
str(
124+
Path("tests/fixtures/mkdocs_item_image_social_cards_blog.yml").resolve()
125+
)
126+
)
127+
128+
integration_social_cards = IntegrationMaterialSocialCards(
129+
mkdocs_config=cfg_mkdocs
130+
)
131+
self.assertTrue(integration_social_cards.IS_THEME_MATERIAL)
132+
self.assertTrue(integration_social_cards.IS_SOCIAL_PLUGIN_ENABLED)
133+
self.assertTrue(integration_social_cards.IS_SOCIAL_PLUGIN_CARDS_ENABLED)
134+
self.assertTrue(integration_social_cards.IS_BLOG_PLUGIN_ENABLED)
135+
self.assertTrue(integration_social_cards.IS_ENABLED)
136+
120137
def test_simple_build(self):
121138
with tempfile.TemporaryDirectory() as tmpdirname:
122139
cli_result = self.build_docs_setup(

0 commit comments

Comments
 (0)