Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refacto(material_integrations): use POO to manage integrations with Material theme framework #335

Merged
merged 5 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions mkdocs_rss_plugin/integrations/theme_material_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#! python3 # noqa: E265

# ############################################################################
# ########## Libraries #############
# ##################################

# standard library
from typing import Optional

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

# package
from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME

# 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


# ############################################################################
# ########## Globals #############
# ################################

logger = get_plugin_logger(MKDOCS_LOGGER_NAME)

# ############################################################################
# ########## Logic ###############
# ################################


class IntegrationMaterialThemeBase:
# attributes
IS_THEME_MATERIAL: bool = False
IS_INSIDERS: bool = False

def __init__(self, mkdocs_config: MkDocsConfig) -> None:
"""Integration instantiation.

Args:
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
"""
# store Mkdocs config as attribute
self.mkdocs_config = mkdocs_config

self.IS_THEME_MATERIAL = self.is_mkdocs_theme_material()
self.IS_INSIDERS = self.is_mkdocs_theme_material_insiders()

def is_mkdocs_theme_material(
self, mkdocs_config: Optional[MkDocsConfig] = None
) -> bool:
"""Check if the theme set in mkdocs.yml is material or not.

Args:
mkdocs_config (Optional[MkDocsConfig]): Mkdocs website configuration object.

Returns:
bool: True if the theme's name is 'material'. False if not.
"""
if mkdocs_config is None and isinstance(self.mkdocs_config, MkDocsConfig):
mkdocs_config: MkDocsConfig = self.mkdocs_config

self.IS_THEME_MATERIAL = mkdocs_config.theme.name == "material"
return self.IS_THEME_MATERIAL

def is_mkdocs_theme_material_insiders(self) -> Optional[bool]:
"""Check if the material theme is community or insiders edition.

Returns:
bool: True if the theme is Insiders edition. False if community. None if
the Material theme is not installed.
"""
if not self.IS_THEME_MATERIAL:
return None

if material_version is not None and "insiders" in material_version:
logger.debug("Material theme edition INSIDERS")
self.IS_INSIDERS = True
return True
else:
logger.debug("Material theme edition COMMUNITY")
self.IS_INSIDERS = False
return False
103 changes: 103 additions & 0 deletions mkdocs_rss_plugin/integrations/theme_material_blog_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#! python3 # noqa: E265

# ############################################################################
# ########## Libraries #############
# ##################################

# standard library
from typing import Optional

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

# package
from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME
from mkdocs_rss_plugin.integrations.theme_material_base import (
IntegrationMaterialThemeBase,
)

# conditional
try:
from material import __version__ as material_version
from material.plugins.blog.plugin import BlogPlugin

except ImportError:
material_version = None


# ############################################################################
# ########## Globals #############
# ################################

logger = get_plugin_logger(MKDOCS_LOGGER_NAME)

# ############################################################################
# ########## Logic ###############
# ################################


class IntegrationMaterialBlog(IntegrationMaterialThemeBase):
# attributes
IS_ENABLED: bool = True
IS_BLOG_PLUGIN_ENABLED: bool = True

def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> None:
"""Integration instantiation.

Args:
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
switch_force (bool, optional): option to force integration disabling. Set
it to False to disable it even if Social Cards are enabled in Mkdocs
configuration. Defaults to True.
"""
# check if the integration can be enabled or not
self.IS_BLOG_PLUGIN_ENABLED = self.is_blog_plugin_enabled_mkdocs(
mkdocs_config=mkdocs_config
)
# if every conditions are True, enable the integration
self.IS_ENABLED = all([self.IS_THEME_MATERIAL, self.IS_BLOG_PLUGIN_ENABLED])

# except if the end-user wants to disable it
if switch_force is False:
self.IS_ENABLED = False
logger.debug(
"Integration with Blog (Material theme) is "
"disabled in plugin's option in Mkdocs configuration."
)

def is_blog_plugin_enabled_mkdocs(
self, mkdocs_config: Optional[MkDocsConfig]
) -> bool:
"""Check if blog plugin is installed and enabled.

Args:
mkdocs_config (Optional[MkDocsConfig]): Mkdocs website configuration object.

Returns:
bool: True if the theme material and the plugin blog is enabled.
"""
if mkdocs_config is None and isinstance(self.mkdocs_config, MkDocsConfig):
mkdocs_config = self.mkdocs_config

if not self.is_mkdocs_theme_material(mkdocs_config=mkdocs_config):
logger.debug("Installed theme is not 'material'. Integration disabled.")
return False

if not mkdocs_config.plugins.get("material/blog"):
logger.debug("Material blog plugin is not listed in configuration.")
self.IS_BLOG_PLUGIN_ENABLED = False
return False

self.blog_plugin_cfg: BlogPlugin | None = mkdocs_config.plugins.get(
"material/blog"
)

if not self.blog_plugin_cfg.config.enabled:
logger.debug("Material blog plugin is installed but disabled.")
self.IS_BLOG_PLUGIN_ENABLED = False
return False

logger.debug("Material blog plugin is enabled in Mkdocs configuration.")
self.IS_BLOG_PLUGIN_ENABLED = True
return True
110 changes: 31 additions & 79 deletions mkdocs_rss_plugin/integrations/theme_material_social_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@

# package
from mkdocs_rss_plugin.constants import MKDOCS_LOGGER_NAME
from mkdocs_rss_plugin.integrations.theme_material_base import (
IntegrationMaterialThemeBase,
)
from mkdocs_rss_plugin.integrations.theme_material_blog_plugin import (
IntegrationMaterialBlog,
)

# conditional
try:
from material import __version__ as material_version
from material.plugins.blog.plugin import BlogPlugin
from pymdownx.slugs import slugify

except ImportError:
Expand All @@ -39,14 +44,11 @@
# ################################


class IntegrationMaterialSocialCards:
class IntegrationMaterialSocialCards(IntegrationMaterialThemeBase):
# attributes
IS_ENABLED: bool = True
IS_BLOG_PLUGIN_ENABLED: bool = True
IS_SOCIAL_PLUGIN_ENABLED: bool = True
IS_SOCIAL_PLUGIN_CARDS_ENABLED: bool = True
IS_THEME_MATERIAL: bool = False
IS_INSIDERS: bool = False
CARDS_MANIFEST: Optional[dict] = None

def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> None:
Expand All @@ -58,7 +60,10 @@ def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> No
it to False to disable it even if Social Cards are enabled in Mkdocs
configuration. Defaults to True.
"""
self.mkdocs_config = mkdocs_config
# support cards for blog posts
self.integration_material_blog = IntegrationMaterialBlog(
mkdocs_config=mkdocs_config
)
# check if the integration can be enabled or not
self.IS_SOCIAL_PLUGIN_CARDS_ENABLED = (
self.is_social_plugin_and_cards_enabled_mkdocs(mkdocs_config=mkdocs_config)
Expand Down Expand Up @@ -91,87 +96,28 @@ def __init__(self, mkdocs_config: MkDocsConfig, switch_force: bool = True) -> No
self.social_cards_cache_dir = self.get_social_cards_cache_dir(
mkdocs_config=mkdocs_config
)
self.IS_BLOG_PLUGIN_ENABLED = self.is_blog_plugin_enabled_mkdocs(
mkdocs_config=mkdocs_config
)

if self.is_mkdocs_theme_material_insiders():
self.load_cache_cards_manifest()

# store some attributes used to compute social card hash
self.site_name = mkdocs_config.site_name
self.site_description = mkdocs_config.site_description or ""

def is_mkdocs_theme_material(self, mkdocs_config: MkDocsConfig) -> bool:
"""Check if the theme set in mkdocs.yml is material or not.

Args:
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.

Returns:
bool: True if the theme's name is 'material'. False if not.
"""
self.IS_THEME_MATERIAL = mkdocs_config.theme.name == "material"
return self.IS_THEME_MATERIAL

def is_mkdocs_theme_material_insiders(self) -> Optional[bool]:
"""Check if the material theme is community or insiders edition.

Returns:
bool: True if the theme is Insiders edition. False if community. None if
the Material theme is not installed.
"""
if not self.IS_THEME_MATERIAL:
return None

if material_version is not None and "insiders" in material_version:
logger.debug("Material theme edition INSIDERS")
self.IS_INSIDERS = True
return True
else:
logger.debug("Material theme edition COMMUNITY")
self.IS_INSIDERS = False
return False

def is_blog_plugin_enabled_mkdocs(self, mkdocs_config: MkDocsConfig) -> bool:
"""Check if blog plugin is installed and enabled.

Args:
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.

Returns:
bool: True if the theme material and the plugin blog is enabled.
"""
if not self.is_mkdocs_theme_material(mkdocs_config=mkdocs_config):
logger.debug("Installed theme is not 'material'. Integration disabled.")
return False

if not mkdocs_config.plugins.get("material/blog"):
logger.debug("Material blog plugin is not listed in configuration.")
self.IS_BLOG_PLUGIN_ENABLED = False
return False

self.blog_plugin_cfg: BlogPlugin | None = mkdocs_config.plugins.get(
"material/blog"
)

if not self.blog_plugin_cfg.config.enabled:
logger.debug("Material blog plugin is installed but disabled.")
self.IS_BLOG_PLUGIN_ENABLED = False
return False

logger.debug("Material blog plugin is enabled in Mkdocs configuration.")
self.IS_BLOG_PLUGIN_ENABLED = True
return True

def is_social_plugin_enabled_mkdocs(self, mkdocs_config: MkDocsConfig) -> bool:
def is_social_plugin_enabled_mkdocs(
self, mkdocs_config: Optional[MkDocsConfig] = None
) -> bool:
"""Check if social plugin is installed and enabled.

Args:
mkdocs_config (MkDocsConfig): Mkdocs website configuration object.
mkdocs_config (Optional[MkDocsConfig]): Mkdocs website configuration object.

Returns:
bool: True if the theme material and the plugin social cards is enabled.
"""
if mkdocs_config is None and isinstance(self.mkdocs_config, MkDocsConfig):
mkdocs_config = self.mkdocs_config

if not self.is_mkdocs_theme_material(mkdocs_config=mkdocs_config):
logger.debug("Installed theme is not 'material'. Integration disabled.")
return False
Expand Down Expand Up @@ -315,9 +261,11 @@ def get_social_card_build_path_for_page(
mkdocs_site_dir = self.mkdocs_site_build_dir

# if page is a blog post
if self.IS_BLOG_PLUGIN_ENABLED and Path(
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
):
expected_built_card_path = Path(
f"{mkdocs_site_dir}/{self.social_cards_assets_dir}/"
f"{Path(mkdocs_page.file.dest_uri).parent}.png"
Expand Down Expand Up @@ -358,9 +306,11 @@ 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.IS_BLOG_PLUGIN_ENABLED and Path(
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
):
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 @@ -426,9 +376,11 @@ def get_social_card_url_for_page(
mkdocs_site_url = self.mkdocs_site_url

# if page is a blog post
if self.IS_BLOG_PLUGIN_ENABLED and Path(
if self.integration_material_blog.IS_BLOG_PLUGIN_ENABLED and Path(
mkdocs_page.file.src_uri
).is_relative_to(self.blog_plugin_cfg.config.blog_dir):
).is_relative_to(
self.integration_material_blog.blog_plugin_cfg.config.blog_dir
):
page_social_card = (
f"{mkdocs_site_url}assets/images/social/"
f"{Path(mkdocs_page.file.dest_uri).parent}.png"
Expand Down
11 changes: 11 additions & 0 deletions tests/fixtures/mkdocs_items_material_blog_enabled.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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
- rss

theme:
name: material
Loading