File: plugin.py

package info (click to toggle)
mkdocs-redirects 1.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 136 kB
  • sloc: python: 168; makefile: 23; sh: 17
file content (131 lines) | stat: -rw-r--r-- 4,741 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
Copyright 2019-2022 DataRobot, Inc. and its affiliates.
All rights reserved.
"""
import logging
import os
import posixpath

from mkdocs import utils
from mkdocs.config import config_options
from mkdocs.plugins import BasePlugin
from mkdocs.structure.files import File

log = logging.getLogger("mkdocs.plugin.redirects")


HTML_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Redirecting...</title>
    <link rel="canonical" href="{url}">
    <script>var anchor=window.location.hash.substr(1);location.href="{url}"+(anchor?"#"+anchor:"")</script>
    <meta http-equiv="refresh" content="0; url={url}">
</head>
<body>
You're being redirected to a <a href="{url}">new destination</a>.
</body>
</html>
"""


def write_html(site_dir, old_path, new_path):
    """Write an HTML file in the site_dir with a meta redirect to the new page"""
    # Determine all relevant paths
    old_path_abs = os.path.join(site_dir, old_path)
    old_dir = os.path.dirname(old_path)
    old_dir_abs = os.path.dirname(old_path_abs)

    # Create parent directories if they don't exist
    if not os.path.exists(old_dir_abs):
        log.debug("Creating directory '%s'", old_dir)
        os.makedirs(old_dir_abs)

    # Write the HTML redirect file in place of the old file
    log.debug("Creating redirect: '%s' -> '%s'", old_path, new_path)
    content = HTML_TEMPLATE.format(url=new_path)
    with open(old_path_abs, "w", encoding="utf-8") as f:
        f.write(content)


def get_relative_html_path(old_page, new_page, use_directory_urls):
    """Return the relative path from the old html path to the new html path"""
    old_path = get_html_path(old_page, use_directory_urls)
    new_path, new_hash_fragment = _split_hash_fragment(new_page)

    relative_path = posixpath.relpath(new_path, start=posixpath.dirname(old_path))
    if use_directory_urls:
        relative_path = relative_path + "/"

    return relative_path + new_hash_fragment


def get_html_path(path, use_directory_urls):
    """Return the HTML file path for a given markdown file"""
    f = File(path, "", "", use_directory_urls)
    return f.dest_path.replace(os.sep, "/")


class RedirectPlugin(BasePlugin):
    # Any options that this plugin supplies should go here.
    config_scheme = (
        ("redirect_maps", config_options.Type(dict, default={})),  # note the trailing comma
    )

    # Build a list of redirects on file generation
    def on_files(self, files, config, **kwargs):
        self.redirects = self.config.get("redirect_maps", {})

        # Validate user-provided redirect "old files"
        for page_old in self.redirects:
            if not utils.is_markdown_file(page_old):
                log.warning("redirects plugin: '%s' is not a valid markdown file!", page_old)

        # Build a dict of known document pages to validate against later
        self.doc_pages = {}
        for page in files.documentation_pages():  # object type: mkdocs.structure.files.File
            self.doc_pages[page.src_path.replace(os.sep, "/")] = page

    # Create HTML files for redirects after site dir has been built
    def on_post_build(self, config, **kwargs):
        # Determine if 'use_directory_urls' is set
        use_directory_urls = config.get("use_directory_urls")

        # Walk through the redirect map and write their HTML files
        for page_old, page_new in self.redirects.items():
            # Need to remove hash fragment from new page to verify existence
            page_new_without_hash, hash = _split_hash_fragment(str(page_new))

            # External redirect targets are easy, just use it as the target path
            if page_new.lower().startswith(("http://", "https://")):
                dest_path = page_new

            elif page_new_without_hash in self.doc_pages:
                file = self.doc_pages[page_new_without_hash]
                dest_path = get_relative_html_path(page_old, file.url + hash, use_directory_urls)

            # If the redirect target isn't external or a valid internal page, throw an error
            # Note: we use 'warn' here specifically; mkdocs treats warnings specially when in strict mode
            else:
                log.warning("Redirect target '%s' does not exist!", page_new)
                continue

            # DO IT!
            write_html(
                config["site_dir"],
                get_html_path(page_old, use_directory_urls),
                dest_path,
            )


def _split_hash_fragment(path):
    """
    Returns (path, hash-fragment)

    "path/to/file#hash" => ("/path/to/file", "#hash")
    "path/to/file"      => ("/path/to/file", "")
    """
    path, hash, after = path.partition("#")
    return path, hash + after