def get_site_url(mkdocs_config: Config) -> str or None: """Extract site URL from MkDocs configuration and enforce the behavior to ensure \ returning a str with length > 0 or None. If exists, it adds an ending slash. :param mkdocs_config: configuration object :type mkdocs_config: Config :return: site url :rtype: str or None """ # this method exists because the following line returns an empty string instead of \ # None (because the key alwayus exists) defined_site_url = mkdocs_config.get("site_url", None) # cases if defined_site_url is None or not len(defined_site_url): # in cas of mkdocs's behavior change site_url = None else: site_url = defined_site_url # handle trailing slash if not site_url.endswith("/"): site_url = site_url + "/" return site_url
def on_page_markdown( self, markdown: str, page: Page, config: config_options.Config, files ) -> str: """The page_markdown event is called after the page's markdown is loaded from file and can be used to alter the Markdown source text. The meta- data has been stripped off and is available as page.meta at this point. https://www.mkdocs.org/user-guide/plugins/#on_page_markdown Args: markdown (str): Markdown source text of page as string page: mkdocs.nav.Page instance config: global configuration object site_navigation: global navigation object Returns: str: Markdown source text of page as string """ # retrieve dates from git log page_dates = self.util.get_file_dates( in_page=page, source_date_creation=self.src_date_created, source_date_update=self.src_date_updated, meta_datetime_format=self.meta_datetime_format, ) # append to list to be filtered later self.pages_to_filter.append( PageInformation( abs_path=Path(page.file.abs_src_path), authors=self.util.get_authors_from_meta(in_page=page), created=page_dates[0], updated=page_dates[1], title=page.title, description=self.util.get_description_or_abstract( in_page=page, chars_count=self.config.get("abstract_chars_count") ), image=self.util.get_image( in_page=page, base_url=config.get("site_url", __uri__) ), url_full=page.canonical_url, src_path=page.file.src_path, ) )
def guess_locale(mkdocs_config: Config) -> str or None: """Extract language code from MkDocs or Theme configuration. :param mkdocs_config: configuration object :type mkdocs_config: Config :return: language code :rtype: str or None """ # MkDocs locale settings - might be added in future mkdocs versions # see: https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/24 if mkdocs_config.get("locale"): return mkdocs_config.get("locale") # Some themes implement a locale or a language setting if "theme" in mkdocs_config and "locale" in mkdocs_config.get("theme"): return mkdocs_config.get("theme")._vars.get("locale") elif "theme" in mkdocs_config and "language" in mkdocs_config.get("theme"): return mkdocs_config.get("theme")._vars.get("language") else: return None
def on_config(self, config: config_options.Config) -> dict: """ Determine which locale to use. The config event is the first event called on build and is run immediately after the user configuration is loaded and validated. Any alterations to the config should be made here. https://www.mkdocs.org/user-guide/plugins/#on_config Args: config (dict): global configuration object Returns: dict: global configuration object """ # Get locale settings - might be added in future mkdocs versions # see: https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/24 mkdocs_locale = config.get("locale", None) # Get locale from plugin configuration plugin_locale = self.config.get("locale", None) # theme locale if "theme" in config and "locale" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("locale") logging.debug( "Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name) ) elif "theme" in config and "language" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("language") logging.debug( "Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name) ) else: theme_locale = None logging.debug( "No locale found in theme configuration (or no custom theme set)" ) # First prio: plugin locale if plugin_locale: locale_set = plugin_locale logging.debug("Using locale from plugin configuration: %s" % locale_set) # Second prio: theme locale elif theme_locale: locale_set = theme_locale logging.debug( "Locale not set in plugin. Fallback to theme configuration: %s" % locale_set ) # Third prio is mkdocs locale (which might be added in the future) elif mkdocs_locale: locale_set = mkdocs_locale logging.debug("Using locale from mkdocs configuration: %s" % locale_set) else: locale_set = "en" logging.debug("No locale set. Fallback to: %s" % locale_set) # set locale also in plugin configuration self.config["locale"] = locale_set return config
def on_config(self, config: config_options.Config) -> dict: """The config event is the first event called on build and is run immediately after the user configuration is loaded and validated. Any alterations to the config should be made here. https://www.mkdocs.org/user-guide/plugins/#on_config :param config: global configuration object :type config: config_options.Config :raises FileExistsError: if the template for the RSS feed is not found :return: plugin configuration object :rtype: dict """ # Skip if disabled if not self.config.get("enabled"): return config # check template dirs if not Path(DEFAULT_TEMPLATE_FILENAME).is_file(): raise FileExistsError(DEFAULT_TEMPLATE_FILENAME) self.tpl_file = Path(DEFAULT_TEMPLATE_FILENAME) self.tpl_folder = DEFAULT_TEMPLATE_FOLDER # start a feed dictionary using global config vars base_feed = { "author": config.get("site_author", None), "buildDate": formatdate(get_build_timestamp()), "copyright": config.get("copyright", None), "description": config.get("site_description", None), "entries": [], "generator": "{} - v{}".format(__title__, __version__), "html_url": self.util.get_site_url(config), "language": self.util.guess_locale(config), "pubDate": formatdate(get_build_timestamp()), "repo_url": config.get("repo_url", config.get("site_url", None)), "title": config.get("site_name", None), "ttl": self.config.get("feed_ttl", None), } # feed image if self.config.get("image"): base_feed["logo_url"] = self.config.get("image") # pattern to match pages included in output self.match_path_pattern = compile(self.config.get("match_path")) # date handling if self.config.get("date_from_meta") is not None: self.src_date_created = self.config.get("date_from_meta").get( "as_creation", False ) self.src_date_updated = self.config.get("date_from_meta").get( "as_update", False ) self.meta_datetime_format = self.config.get("date_from_meta").get( "datetime_format", "%Y-%m-%d %H:%M" ) logger.debug( "[rss-plugin] Dates will be retrieved from page meta (yaml " "frontmatter). The git log will be used as fallback." ) else: logger.debug("[rss-plugin] Dates will be retrieved from git log.") # create 2 final dicts self.feed_created = deepcopy(base_feed) self.feed_updated = deepcopy(base_feed) # final feed url if base_feed.get("html_url"): # concatenate both URLs self.feed_created["rss_url"] = ( base_feed.get("html_url") + OUTPUT_FEED_CREATED ) self.feed_updated["rss_url"] = ( base_feed.get("html_url") + OUTPUT_FEED_UPDATED ) else: logging.warning( "[rss-plugin] The variable `site_url` is not set in the MkDocs " "configuration file whereas a URL is mandatory to publish. " "See: https://validator.w3.org/feed/docs/rss2.html#requiredChannelElements" ) self.feed_created["rss_url"] = self.feed_updated["rss_url"] = None # ending event return config
def on_post_build(self, config: config_options.Config) -> dict: """The post_build event does not alter any variables. \ Use this event to call post-build scripts. \ See: <https://www.mkdocs.org/user-guide/plugins/#on_post_build> :param config: global configuration object :type config: config_options.Config :return: global configuration object :rtype: dict """ # Skip if disabled if not self.config.get("enabled"): return # pretty print or not pretty_print = self.config.get("pretty_print", False) # output filepaths out_feed_created = Path(config.get("site_dir")) / OUTPUT_FEED_CREATED out_feed_updated = Path(config.get("site_dir")) / OUTPUT_FEED_UPDATED # created items self.feed_created.get("entries").extend( self.util.filter_pages( pages=self.pages_to_filter, attribute="created", length=self.config.get("length", 20), ) ) # updated items self.feed_updated.get("entries").extend( self.util.filter_pages( pages=self.pages_to_filter, attribute="updated", length=self.config.get("length", 20), ) ) # write feeds according to the pretty print option if pretty_print: # load Jinja environment and template env = Environment( autoescape=select_autoescape(["html", "xml"]), loader=FileSystemLoader(self.tpl_folder), ) template = env.get_template(self.tpl_file.name) # write feeds to files with out_feed_created.open(mode="w", encoding="UTF8") as fifeed_created: fifeed_created.write(template.render(feed=self.feed_created)) with out_feed_updated.open(mode="w", encoding="UTF8") as fifeed_updated: fifeed_updated.write(template.render(feed=self.feed_updated)) else: # load Jinja environment and template env = Environment( autoescape=select_autoescape(["html", "xml"]), loader=FileSystemLoader(self.tpl_folder), lstrip_blocks=True, trim_blocks=True, ) template = env.get_template(self.tpl_file.name) # write feeds to files stripping out spaces and new lines with out_feed_created.open(mode="w", encoding="UTF8") as fifeed_created: prev_char = "" for char in template.render(feed=self.feed_created): if char == "\n": continue if char == " " and prev_char == " ": prev_char = char continue prev_char = char fifeed_created.write(char) with out_feed_updated.open(mode="w", encoding="UTF8") as fifeed_updated: for char in template.render(feed=self.feed_updated): if char == "\n": prev_char = char continue if char == " " and prev_char == " ": prev_char = char continue prev_char = char fifeed_updated.write(char)
def on_page_content( self, html: str, page: Page, config: config_options.Config, files ) -> str: """The page_content event is called after the Markdown text is rendered to HTML (but before being passed to a template) and can be used to alter the HTML body of the page. https://www.mkdocs.org/user-guide/plugins/#on_page_content :param html: HTML rendered from Markdown source as string :type html: str :param page: mkdocs.nav.Page instance :type page: Page :param config: global configuration object :type config: config_options.Config :param files: global navigation object :type files: [type] :return: HTML rendered from Markdown source as string :rtype: str """ # Skip if disabled if not self.config.get("enabled"): return # skip pages that don't match the config var match_path if not self.match_path_pattern.match(page.file.src_path): return # retrieve dates from git log page_dates = self.util.get_file_dates( in_page=page, source_date_creation=self.src_date_created, source_date_update=self.src_date_updated, meta_datetime_format=self.meta_datetime_format, ) # handle custom URL parameters if self.config.get("url_parameters"): page_url_full = self.util.build_url( base_url=page.canonical_url, path="", args_dict=self.config.get("url_parameters"), ) else: page_url_full = page.canonical_url # handle URL comment path if self.config.get("comments_path"): page_url_comments = self.util.build_url( base_url=page.canonical_url, path=self.config.get("comments_path"), ) else: page_url_comments = None # append to list to be filtered later self.pages_to_filter.append( PageInformation( abs_path=Path(page.file.abs_src_path), authors=self.util.get_authors_from_meta(in_page=page), categories=self.util.get_categories_from_meta( in_page=page, categories_labels=self.config.get("categories") ), created=page_dates[0], description=self.util.get_description_or_abstract( in_page=page, chars_count=self.config.get("abstract_chars_count") ), guid=page.canonical_url, image=self.util.get_image( in_page=page, base_url=config.get("site_url", __uri__) ), title=page.title, updated=page_dates[1], url_comments=page_url_comments, url_full=page_url_full, ) )
def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: """ Determine which locale to use. The config event is the first event called on build and is run immediately after the user configuration is loaded and validated. Any alterations to the config should be made here. https://www.mkdocs.org/user-guide/plugins/#on_config Args: config (dict): global configuration object Returns: dict: global configuration object """ if not self.config.get('enabled'): return config self.util = Util(config=self.config) # Save last commit timestamp for entire site self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( config.get('docs_dir') ) # Get locale settings - might be added in future mkdocs versions # see: https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/24 mkdocs_locale = config.get("locale", None) # Get locale from plugin configuration plugin_locale = self.config.get("locale", None) # theme locale if "theme" in config and "locale" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("locale") logging.debug( "Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name) ) elif "theme" in config and "language" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("language") logging.debug( "Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name) ) else: theme_locale = None logging.debug( "No locale found in theme configuration (or no custom theme set)" ) # First prio: plugin locale if plugin_locale: locale_set = plugin_locale logging.debug("Using locale from plugin configuration: %s" % locale_set) # Second prio: theme locale elif theme_locale: locale_set = theme_locale logging.debug( "Locale not set in plugin. Fallback to theme configuration: %s" % locale_set ) # Third prio is mkdocs locale (which might be added in the future) elif mkdocs_locale: locale_set = mkdocs_locale logging.debug("Using locale from mkdocs configuration: %s" % locale_set) else: locale_set = "en" logging.debug("No locale set. Fallback to: %s" % locale_set) # set locale also in plugin configuration self.config["locale"] = locale_set # Add pointers to support files for timeago.js if self.config.get("type") == "timeago": config["extra_javascript"] = ["js/timeago_mkdocs_material.js"] + config[ "extra_javascript" ] config["extra_javascript"] = ["js/timeago.min.js"] + config[ "extra_javascript" ] config["extra_css"] = ["css/timeago.css"] + config["extra_css"] return config
def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: """ Determine which locale to use. The config event is the first event called on build and is run immediately after the user configuration is loaded and validated. Any alterations to the config should be made here. https://www.mkdocs.org/user-guide/plugins/#on_config Args: config (dict): global configuration object Returns: dict: global configuration object """ if not self.config.get('enabled'): return config assert self.config['type'] in [ "date", "datetime", "iso_date", "iso_datetime", "timeago", "custom" ] self.util = Util(config=self.config) # Save last commit timestamp for entire site self.last_site_revision_timestamp = self.util.get_git_commit_timestamp( config.get('docs_dir')) # Get locale from plugin configuration plugin_locale = self.config.get("locale", None) # theme locale if "theme" in config and "locale" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("locale") logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name)) elif "theme" in config and "language" in config.get("theme"): custom_theme = config.get("theme") theme_locale = custom_theme._vars.get("language") logging.debug("Locale '%s' extracted from the custom theme: '%s'" % (theme_locale, custom_theme.name)) else: theme_locale = None logging.debug( "No locale found in theme configuration (or no custom theme set)" ) # First prio: plugin locale if plugin_locale: locale_set = plugin_locale logging.debug("Using locale from plugin configuration: %s" % locale_set) # Second prio: theme locale elif theme_locale: locale_set = theme_locale logging.debug( "Locale not set in plugin. Fallback to theme configuration: %s" % locale_set) # Lastly, fallback is English else: locale_set = "en" logging.debug("No locale set. Fallback to: %s" % locale_set) # Validate locale locale_set = str(locale_set) assert len( locale_set ) == 2, "locale must be a 2 letter code, see https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" # set locale also in plugin configuration self.config["locale"] = locale_set # Add pointers to support files for timeago.js if self.config.get("type") == "timeago": config["extra_javascript"] = ["js/timeago_mkdocs_material.js" ] + config["extra_javascript"] config["extra_javascript"] = ["js/timeago.min.js" ] + config["extra_javascript"] config["extra_css"] = ["css/timeago.css"] + config["extra_css"] # Compatibility with mkdocs-static-i18n plugins = [*OrderedDict(config["plugins"])] if "i18n" in plugins: if plugins.index("git-revision-date-localized") < plugins.index( "i18n"): msg = "[git-revision-date-localized] should be defined after the i18n plugin in your mkdocs.yml file. " msg += "This is because i18n adds a 'locale' variable to markdown pages that this plugin supports." raise ConfigurationError(msg) return config