def on_template_context(self, context, template_name, config, **kwargs): """ The template_context event is called immediately after the context is created for the subject template and can be used to alter the context for that specific template only. See https://www.mkdocs.org/dev-guide/plugins/#on_template_context """ if not self.config.get("enabled"): return # Save the page context # We'll use the same context of the last rendered page # And apply it to the print page as well (in on_post_build event) # Note a theme can have multiple templates # Found a bug where in the mkdocs theme, # the "sitemap.xml" static template # has incorrect 'extra_css' and 'extra_js' paths # leading to breaking the print page # at random (when sitemap.xml was rendered last) # we're assuming here all templates have a 404.html template # print(f"\nName: {template_name}\nContext: {context.get('extra_css')}") if template_name == "404.html": self.context = context # Make sure paths are OK if config.get('extra_css'): self.context['extra_css'] = [ get_relative_url(f, self.print_page.file.url) for f in config.get('extra_css') ] if config.get('extra_javascript'): self.context['extra_javascript'] = [ get_relative_url(f, self.print_page.file.url) for f in config.get('extra_javascript') ]
def test_get_relative_url_empty(self): for url in ['', '.', '/.']: for other in ['', '.', '/', '/.']: self.assertEqual(utils.get_relative_url(url, other), '.') self.assertEqual(utils.get_relative_url('/', ''), './') self.assertEqual(utils.get_relative_url('/', '/'), './') self.assertEqual(utils.get_relative_url('/', '.'), './') self.assertEqual(utils.get_relative_url('/', '/.'), './')
def test_get_relative_url(self): expected_results = { ('foo/bar', 'foo'): 'bar', ('foo/bar.txt', 'foo'): 'bar.txt', ('foo', 'foo/bar'): '..', ('foo', 'foo/bar.txt'): '.', ('foo/../../bar', '.'): 'bar', ('foo/../../bar', 'foo'): '../bar', ('foo//./bar/baz', 'foo/bar/baz'): '.', ('a/b/.././../c', '.'): 'c', ('a/b/c/d/ee', 'a/b/c/d/e'): '../ee', ('a/b/c/d/ee', 'a/b/z/d/e'): '../../../c/d/ee', ('foo', 'bar.'): 'foo', ('foo', 'bar./'): '../foo', ('foo', 'foo/bar./'): '..', ('foo', 'foo/bar./.'): '..', ('foo', 'foo/bar././'): '..', ('foo/', 'foo/bar././'): '../', ('foo', 'foo'): '.', ('.foo', '.foo'): '.foo', ('.foo/', '.foo'): '.foo/', ('.foo', '.foo/'): '.', ('.foo/', '.foo/'): './', ('///', ''): './', ('a///', ''): 'a/', ('a///', 'a'): './', ('.', 'here'): '..', ('..', 'here'): '..', ('../..', 'here'): '..', ('../../a', 'here'): '../a', ('..', 'here.txt'): '.', ('a', ''): 'a', ('a', '..'): 'a', ('a', 'b'): '../a', ('a', 'b/..'): '../a', # The dots are considered a file. Documenting a long-standing bug. ('a', 'b/../..'): 'a', ('a/..../b', 'a/../b'): '../a/..../b', ('a/я/b', 'a/я/c'): '../b', ('a/я/b', 'a/яя/c'): '../../я/b', } for (url, other), expected_result in expected_results.items(): # Leading slash intentionally ignored self.assertEqual(utils.get_relative_url(url, other), expected_result) self.assertEqual(utils.get_relative_url('/' + url, other), expected_result) self.assertEqual(utils.get_relative_url(url, '/' + other), expected_result) self.assertEqual(utils.get_relative_url('/' + url, '/' + other), expected_result)
def get_context(nav, files, config, page=None, base_url=''): """ Return the template context for a given page or template. """ if page is not None: base_url = utils.get_relative_url('.', page.url) extra_javascript = utils.create_media_urls(config['extra_javascript'], page, base_url) extra_css = utils.create_media_urls(config['extra_css'], page, base_url) if isinstance(files, Files): files = files.documentation_pages() return { 'nav': nav, 'pages': files, 'base_url': base_url, 'extra_css': extra_css, 'extra_javascript': extra_javascript, 'mkdocs_version': mkdocs.__version__, 'build_date_utc': utils.get_build_datetime(), 'config': config, 'page': page, }
def _build_template(name, template, files, config, nav): """ Return rendered output for given template as a string. """ # Run `pre_template` plugin events. template = config['plugins'].run_event( 'pre_template', template, template_name=name, config=config ) if utils.is_error_template(name): # Force absolute URLs in the nav of error pages and account for the # possability that the docs root might be different than the server root. # See https://github.com/mkdocs/mkdocs/issues/77. # However, if site_url is not set, assume the docs root and server root # are the same. See https://github.com/mkdocs/mkdocs/issues/1598. base_url = urlparse(config['site_url'] or '/').path else: base_url = utils.get_relative_url('.', name) context = get_context(nav, files, config, base_url=base_url) # Run `template_context` plugin events. context = config['plugins'].run_event( 'template_context', context, template_name=name, config=config ) output = template.render(context) # Run `post_template` plugin events. output = config['plugins'].run_event( 'post_template', output, template_name=name, config=config ) return output
def get_context(nav, files, config, page=None, base_url=''): """ Return the template context for a given page or template. """ if page is not None: base_url = utils.get_relative_url('.', page.url) extra_javascript = utils.create_media_urls(config['extra_javascript'], page, base_url) extra_css = utils.create_media_urls(config['extra_css'], page, base_url) # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds. # See https://reproducible-builds.org/specs/source-date-epoch/ timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', timegm(datetime.utcnow().utctimetuple()))) return { 'nav': nav, 'pages': files.documentation_pages(), 'base_url': base_url, 'extra_css': extra_css, 'extra_javascript': extra_javascript, 'mkdocs_version': mkdocs.__version__, 'build_date_utc': datetime.utcfromtimestamp(timestamp), 'config': config, 'page': page, }
def on_page_content(self, html, page, config, files, **kwargs): """ 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. See https://www.mkdocs.org/user-guide/plugins/#on_page_content. """ if not self.config.get("enabled"): return html # Save each page HTML *before* a template is applied inside the page class if page != self.print_page: page.html = html # We need to validate that the first heading on each page is a h1 # This is required for the print page table of contents and enumeration logic if self.config.get("add_table_of_contents") or self.config.get( "enumerate_headings"): if page in self.all_pages_in_nav: match = re.search(r"\<h[0-6]", html) if match: if not match.group() == "<h1": msg = f"The page {page.title} ({page.file.src_path}) does not start with a level 1 heading." msg += "This is required for print page Table of Contents and/or enumeration of headings." raise AssertionError(msg) # Link to the PDF version of the entire site on a page. if self.config.get("path_to_pdf") != "": page.url_to_pdf = get_relative_url(self.config.get("path_to_pdf"), page.file.url) return html
def _build_template(name, template, files, config, nav): """ Return rendered output for given template as a string. """ # Run `pre_template` plugin events. template = config['plugins'].run_event( 'pre_template', template, template_name=name, config=config ) if utils.is_error_template(name): # Force absolute URLs in the nav of error pages and account for the # possability that the docs root might be different than the server root. # See https://github.com/mkdocs/mkdocs/issues/77. # However, if site_url is not set, assume the docs root and server root # are the same. See https://github.com/mkdocs/mkdocs/issues/1598. base_url = utils.urlparse(config['site_url'] or '/').path else: base_url = utils.get_relative_url('.', name) context = get_context(nav, files, config, base_url=base_url) # Run `template_context` plugin events. context = config['plugins'].run_event( 'template_context', context, template_name=name, config=config ) output = template.render(context) # Run `post_template` plugin events. output = config['plugins'].run_event( 'post_template', output, template_name=name, config=config ) return output
def __render_tag_links(self, tag, pages): content = ["## <span class=\"md-tag\">{}</span>".format(tag), ""] for page in pages: url = utils.get_relative_url(page.file.src_path, self.tags_file.src_path) content.append("- [{}]({})".format( page.meta.get("title", page.title), url)) # Return rendered tag links return "\n".join(content)
def on_page_content(self, html, page, config, files, **kwargs): # Save each page HTML *before* a template is applied inside the page class if page != self.print_page: page.html = html if self.config.get("path_to_pdf") != "": page.url_to_pdf = get_relative_url(self.config.get("path_to_pdf"), page.file.url) return html
def __render_tag_links(self, tag, pages): content = [f"## <span class=\"md-tag\">{tag}</span>", ""] for page in pages: url = utils.get_relative_url( page.file.src_path.replace(os.path.sep, "/"), self.tags_file.src_path.replace(os.path.sep, "/")) # Ensure forward slashes, as we have to use the path of the source # file which contains the operating system's path separator. content.append("- [{}]({})".format( page.meta.get("title", page.title), url)) # Return rendered tag links return "\n".join(content)
def on_page_content(self, html, page, config, files, **kwargs): """ 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. See https://www.mkdocs.org/user-guide/plugins/#on_page_content. """ # Save each page HTML *before* a template is applied inside the page class if page != self.print_page: page.html = html # Link to the PDF version of the entire site on a page. if self.config.get("path_to_pdf") != "": page.url_to_pdf = get_relative_url(self.config.get("path_to_pdf"), page.file.url) return html
def url_relative_to(self, other): """ Return url for file relative to other file. """ return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other)
def url(value): return normalize_url(value, page=page, base=get_relative_url('.', page.url))