Skip to content

Commit 1c17192

Browse files
committed
Merge branch 'feature-custom-feed-title' of https://github.com/toomanybrians/mkdocs-rss-plugin into feature-custom-feed-title
2 parents 6c33175 + 20b63b1 commit 1c17192

File tree

5 files changed

+206
-43
lines changed

5 files changed

+206
-43
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
1818
-->
1919

20+
## 1.13.1 - 2024-06-15
21+
22+
### Bugs fixes 🐛
23+
24+
* Fix remote image length warnings using requests instead of urllib (standard lib) by @Guts in <https://github.com/Guts/mkdocs-rss-plugin/pull/289>
25+
26+
### Features and enhancements 🎉
27+
28+
* refacto(cleanup): remove python 3.9 related code and deps (timezone) by @Guts in <https://github.com/Guts/mkdocs-rss-plugin/pull/288>
29+
* improve(logs): enhance some logs and type hints by @Guts in <https://github.com/Guts/mkdocs-rss-plugin/pull/293>
30+
* Feature: improve social cards integration by @Guts in <https://github.com/Guts/mkdocs-rss-plugin/pull/294>
31+
2032
## 1.13.0 - 2024-06-10
2133

2234
### Bugs fixes 🐛

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Or it could be displayed as a RSS or Feedly follow button:
6767
6868
For JSON Feed, you can use the icon:
6969
70-
[![JSON Feed icon](https://jsonfeed.org/graphics/icon.png){: width=130 loading=lazy }](https://guts.github.io/mkdocs-rss-plugin/feed_json_created.json)
70+
[![JSON Feed icon](https://raw.githubusercontent.com/manton/JSONFeed/master/graphics/icon.png){: width=130 loading=lazy }](https://guts.github.io/mkdocs-rss-plugin/feed_json_created.json)
7171
{: align=middle }
7272
7373
!!! tip

mkdocs_rss_plugin/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
__title_clean__ = "".join(e for e in __title__ if e.isalnum())
4141
__uri__ = "https://github.com/Guts/mkdocs-rss-plugin/"
4242

43-
__version__ = "1.13.0"
43+
__version__ = "1.13.1"
4444
__version_info__ = tuple(
4545
[
4646
int(num) if num.isdigit() else num

mkdocs_rss_plugin/integrations/theme_material_social_plugin.py

Lines changed: 175 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@
55
# ##################################
66

77
# standard library
8+
import json
9+
from hashlib import md5
810
from pathlib import Path
911

1012
# 3rd party
11-
from mkdocs.config.config_options import Config
13+
from mkdocs.config.defaults import MkDocsConfig
1214
from mkdocs.plugins import get_plugin_logger
1315
from mkdocs.structure.pages import Page
1416

1517
# package
1618
from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME
1719

20+
# conditional
21+
try:
22+
from material import __version__ as material_version
23+
except ImportError:
24+
material_version = None
25+
1826
# ############################################################################
1927
# ########## Globals #############
2028
# ################################
@@ -32,12 +40,14 @@ class IntegrationMaterialSocialCards:
3240
IS_SOCIAL_PLUGIN_ENABLED: bool = True
3341
IS_SOCIAL_PLUGIN_CARDS_ENABLED: bool = True
3442
IS_THEME_MATERIAL: bool = False
43+
IS_INSIDERS: bool = False
44+
CARDS_MANIFEST: dict | None = None
3545

36-
def __init__(self, mkdocs_config: Config, switch_force: bool = True) -> None:
46+
def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> None:
3747
"""Integration instantiation.
3848
3949
Args:
40-
mkdocs_config (Config): Mkdocs website configuration object.
50+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
4151
switch_force (bool, optional): option to force integration disabling. Set
4252
it to False to disable it even if Social Cards are enabled in Mkdocs
4353
configuration. Defaults to True.
@@ -68,55 +78,85 @@ def __init__(self, mkdocs_config: Config, switch_force: bool = True) -> None:
6878
if self.IS_ENABLED:
6979
self.mkdocs_site_url = mkdocs_config.site_url
7080
self.mkdocs_site_build_dir = mkdocs_config.site_dir
71-
self.social_cards_assets_dir = self.get_social_cards_dir(
81+
self.social_cards_assets_dir = self.get_social_cards_build_dir(
82+
mkdocs_config=mkdocs_config
83+
)
84+
self.social_cards_cache_dir = self.get_social_cards_cache_dir(
7285
mkdocs_config=mkdocs_config
7386
)
87+
if self.is_mkdocs_theme_material_insiders():
88+
self.load_cache_cards_manifest()
89+
90+
# store some attributes used to compute social card hash
91+
self.site_name = mkdocs_config.site_name
92+
self.site_description = mkdocs_config.site_description or ""
7493

75-
def is_theme_material(self, mkdocs_config: Config) -> bool:
94+
def is_mkdocs_theme_material(self, mkdocs_config: MkDocsConfig) -> bool:
7695
"""Check if the theme set in mkdocs.yml is material or not.
7796
7897
Args:
79-
mkdocs_config (Config): Mkdocs website configuration object.
98+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
8099
81100
Returns:
82101
bool: True if the theme's name is 'material'. False if not.
83102
"""
84103
self.IS_THEME_MATERIAL = mkdocs_config.theme.name == "material"
85104
return self.IS_THEME_MATERIAL
86105

87-
def is_social_plugin_enabled_mkdocs(self, mkdocs_config: Config) -> bool:
106+
def is_mkdocs_theme_material_insiders(self) -> bool | None:
107+
"""Check if the material theme is community or insiders edition.
108+
109+
Returns:
110+
bool: True if the theme is Insiders edition. False if community. None if
111+
the Material theme is not installed.
112+
"""
113+
if not self.IS_THEME_MATERIAL:
114+
return None
115+
116+
if material_version is not None and "insiders" in material_version:
117+
logger.debug("Material theme edition INSIDERS")
118+
self.IS_INSIDERS = True
119+
return True
120+
else:
121+
logger.debug("Material theme edition COMMUNITY")
122+
self.IS_INSIDERS = False
123+
return False
124+
125+
def is_social_plugin_enabled_mkdocs(self, mkdocs_config: MkDocsConfig) -> bool:
88126
"""Check if social plugin is installed and enabled.
89127
90128
Args:
91-
mkdocs_config (Config): Mkdocs website configuration object.
129+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
92130
93131
Returns:
94132
bool: True if the theme material and the plugin social cards is enabled.
95133
"""
96-
if not self.is_theme_material(mkdocs_config=mkdocs_config):
134+
if not self.is_mkdocs_theme_material(mkdocs_config=mkdocs_config):
97135
logger.debug("Installed theme is not 'material'. Integration disabled.")
98136
return False
99137

100138
if not mkdocs_config.plugins.get("material/social"):
101-
logger.debug("Social plugin not listed in configuration.")
139+
logger.debug("Material Social plugin not listed in configuration.")
102140
return False
103141

104142
social_plugin_cfg = mkdocs_config.plugins.get("material/social")
105143

106144
if not social_plugin_cfg.config.enabled:
107-
logger.debug("Social plugin is installed but disabled.")
145+
logger.debug("Material Social plugin is installed but disabled.")
108146
self.IS_SOCIAL_PLUGIN_ENABLED = False
109147
return False
110148

111-
logger.debug("Social plugin is enabled in Mkdocs configuration.")
149+
logger.debug("Material Social plugin is enabled in Mkdocs configuration.")
112150
self.IS_SOCIAL_PLUGIN_CARDS_ENABLED = True
113151
return True
114152

115-
def is_social_plugin_and_cards_enabled_mkdocs(self, mkdocs_config: Config) -> bool:
153+
def is_social_plugin_and_cards_enabled_mkdocs(
154+
self, mkdocs_config: MkDocsConfig
155+
) -> bool:
116156
"""Check if social cards plugin is enabled.
117157
118158
Args:
119-
mkdocs_config (Config): Mkdocs website configuration object.
159+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
120160
121161
Returns:
122162
bool: True if the theme material and the plugin social cards is enabled.
@@ -127,19 +167,21 @@ def is_social_plugin_and_cards_enabled_mkdocs(self, mkdocs_config: Config) -> bo
127167
social_plugin_cfg = mkdocs_config.plugins.get("material/social")
128168

129169
if not social_plugin_cfg.config.cards:
130-
logger.debug("Social plugin is installed, present but cards are disabled.")
170+
logger.debug(
171+
"Material Social plugin is installed, present but cards are disabled."
172+
)
131173
self.IS_SOCIAL_PLUGIN_CARDS_ENABLED = False
132174
return False
133175

134-
logger.debug("Social cards are enabled in Mkdocs configuration.")
176+
logger.debug("Material Social cards are enabled in Mkdocs configuration.")
135177
self.IS_SOCIAL_PLUGIN_CARDS_ENABLED = True
136178
return True
137179

138180
def is_social_plugin_enabled_page(
139181
self, mkdocs_page: Page, fallback_value: bool = True
140182
) -> bool:
141183
"""Check if the social plugin is enabled or disabled for a specific page. Plugin
142-
has to enabled in Mkdocs configuration before.
184+
has to be enabled in Mkdocs configuration before.
143185
144186
Args:
145187
mkdocs_page (Page): Mkdocs page object.
@@ -153,46 +195,154 @@ def is_social_plugin_enabled_page(
153195
"cards", fallback_value
154196
)
155197

156-
def get_social_cards_dir(self, mkdocs_config: Config) -> str:
198+
def load_cache_cards_manifest(self) -> dict | None:
199+
"""Load social cards manifest if the file exists.
200+
201+
Returns:
202+
dict | None: manifest as dict or None if the file does not exist
203+
"""
204+
cache_cards_manifest = Path(self.social_cards_cache_dir).joinpath(
205+
"manifest.json"
206+
)
207+
if not cache_cards_manifest.is_file():
208+
logger.debug(
209+
"Material Social Cards cache manifest file not found: "
210+
f"{cache_cards_manifest}"
211+
)
212+
return None
213+
214+
with cache_cards_manifest.open(mode="r", encoding="UTF-8") as manifest:
215+
self.CARDS_MANIFEST = json.load(manifest)
216+
logger.debug(
217+
f"Material Social Cards cache manifest loaded from {cache_cards_manifest}"
218+
)
219+
220+
return self.CARDS_MANIFEST
221+
222+
def get_social_cards_build_dir(self, mkdocs_config: MkDocsConfig) -> Path:
157223
"""Get Social Cards folder within Mkdocs site_dir.
158224
See: https://squidfunk.github.io/mkdocs-material/plugins/social/#config.cards_dir
159225
160226
Args:
161-
mkdocs_config (Config): Mkdocs website configuration object.
227+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
162228
163229
Returns:
164230
str: True if the theme material and the plugin social cards is enabled.
165231
"""
166232
social_plugin_cfg = mkdocs_config.plugins.get("material/social")
167233

168234
logger.debug(
169-
"Social cards folder in Mkdocs build directory: "
235+
"Material Social cards folder in Mkdocs build directory: "
170236
f"{social_plugin_cfg.config.cards_dir}."
171237
)
172238

173-
return social_plugin_cfg.config.cards_dir
239+
return Path(social_plugin_cfg.config.cards_dir).resolve()
240+
241+
def get_social_cards_cache_dir(self, mkdocs_config: MkDocsConfig) -> Path:
242+
"""Get Social Cards folder within Mkdocs site_dir.
243+
See: https://squidfunk.github.io/mkdocs-material/plugins/social/#config.cards_dir
244+
245+
Args:
246+
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
247+
248+
Returns:
249+
str: True if the theme material and the plugin social cards is enabled.
250+
"""
251+
social_plugin_cfg = mkdocs_config.plugins.get("material/social")
252+
self.social_cards_cache_dir = Path(social_plugin_cfg.config.cache_dir).resolve()
253+
254+
logger.debug(
255+
"Material Social cards cache folder: " f"{self.social_cards_cache_dir}."
256+
)
257+
258+
return self.social_cards_cache_dir
174259

175260
def get_social_card_build_path_for_page(
176261
self, mkdocs_page: Page, mkdocs_site_dir: str | None = None
177-
) -> Path:
178-
"""Get social card URL for a specific page in documentation.
262+
) -> Path | None:
263+
"""Get social card path in Mkdocs build dir for a specific page.
179264
180265
Args:
181266
mkdocs_page (Page): Mkdocs page object.
182267
mkdocs_site_dir (Optional[str], optional): Mkdocs build site dir. If None, the
183268
'class.mkdocs_site_build_dir' is used. is Defaults to None.
184269
185270
Returns:
186-
str: URL to the image once published
271+
Path: path to the image once published
187272
"""
188273
if mkdocs_site_dir is None and self.mkdocs_site_build_dir:
189274
mkdocs_site_dir = self.mkdocs_site_build_dir
190275

191-
return Path(
276+
expected_built_card_path = Path(
192277
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
193278
f"{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
194279
)
195280

281+
if expected_built_card_path.is_file():
282+
logger.debug(
283+
f"Social card file found in cache folder: {expected_built_card_path}"
284+
)
285+
return expected_built_card_path
286+
else:
287+
logger.debug(f"Not found: {expected_built_card_path}")
288+
return None
289+
290+
def get_social_card_cache_path_for_page(self, mkdocs_page: Page) -> Path | None:
291+
"""Get social card path in social plugin cache folder for a specific page.
292+
293+
Note:
294+
As we write this code (June 2024), the cache mechanism in Insiders edition
295+
has stores images directly with the corresponding Page's path and name and
296+
keep a correspondance matrix with hashes in a manifest.json;
297+
the cache mechanism in Community edition uses the hash as file names without
298+
any exposed matching criteria.
299+
300+
Args:
301+
mkdocs_page (Page): Mkdocs page object.
302+
303+
Returns:
304+
Path: path to the image in local cache folder if it exists
305+
"""
306+
if self.IS_INSIDERS:
307+
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
308+
f"assets/images/social/{Path(mkdocs_page.file.src_uri).with_suffix('.png')}"
309+
)
310+
if expected_cached_card_path.is_file():
311+
logger.debug(
312+
f"Social card file found in cache folder: {expected_cached_card_path}"
313+
)
314+
return expected_cached_card_path
315+
else:
316+
logger.debug(f"Not found: {expected_cached_card_path}")
317+
318+
else:
319+
if "description" in mkdocs_page.meta:
320+
description = mkdocs_page.meta["description"]
321+
else:
322+
description = self.site_description
323+
324+
page_hash = md5(
325+
"".join(
326+
[
327+
self.site_name,
328+
str(mkdocs_page.meta.get("title", mkdocs_page.title)),
329+
description,
330+
]
331+
).encode("utf-8")
332+
)
333+
expected_cached_card_path = self.social_cards_cache_dir.joinpath(
334+
f"{page_hash.hexdigest()}.png"
335+
)
336+
337+
if expected_cached_card_path.is_file():
338+
logger.debug(
339+
f"Social card file found in cache folder: {expected_cached_card_path}"
340+
)
341+
return expected_cached_card_path
342+
else:
343+
logger.debug(f"Not found: {expected_cached_card_path}")
344+
return None
345+
196346
def get_social_card_url_for_page(
197347
self,
198348
mkdocs_page: Page,

0 commit comments

Comments
 (0)