|
18 | 18 | # |
19 | 19 | import os |
20 | 20 | import itertools |
| 21 | +import json |
| 22 | +from pathlib import Path |
21 | 23 | from docutils.parsers.rst import Directive |
22 | 24 |
|
| 25 | +from fnmatch import fnmatch |
| 26 | +from string import Template |
| 27 | + |
23 | 28 | # The suffix(es) of source filenames. |
24 | 29 | # You can specify multiple suffix as a list of string: |
25 | 30 | # |
|
66 | 71 |
|
67 | 72 | # Add any Sphinx extension module names here, as strings. They can be |
68 | 73 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
69 | | -extensions = ['sphinx.ext.intersphinx', 'sphinx_tabs.tabs', 'sphinx_reredirects'] |
| 74 | +extensions = ['sphinx.ext.intersphinx', 'sphinx_tabs.tabs'] |
70 | 75 |
|
71 | 76 | # Intersphinx mapping |
72 | 77 |
|
@@ -404,8 +409,61 @@ def _missing_reference(app, env, node, contnode): |
404 | 409 | return _missing_reference |
405 | 410 |
|
406 | 411 |
|
| 412 | +def apply_redirects(app, exception): |
| 413 | + redirects = app.config['redirects'] |
| 414 | + |
| 415 | + if not redirects: |
| 416 | + return |
| 417 | + |
| 418 | + from sphinx.builders.html import StandaloneHTMLBuilder |
| 419 | + from sphinxcontrib.serializinghtml import JSONHTMLBuilder |
| 420 | + |
| 421 | + if isinstance(app.builder, JSONHTMLBuilder): |
| 422 | + # If the builder is the JSONHTMLBuilder, then to support redirects from |
| 423 | + # the https://github.com/ros-infrastructure/rosindex site, we need to |
| 424 | + # add a 'canonical_url' portion to the JSON. When that is in place, the |
| 425 | + # site generator will automatically use that as a redirect. |
| 426 | + |
| 427 | + for source, target in redirects.items(): |
| 428 | + # examine if it matches to some doc |
| 429 | + for doc in app.env.found_docs: |
| 430 | + if fnmatch(doc, source): |
| 431 | + file_path = Path(app.outdir).joinpath(doc).with_suffix('.fjson') |
| 432 | + with open(file_path, 'r') as infp: |
| 433 | + data = json.load(infp) |
| 434 | + data['canonical_url'] = target |
| 435 | + with open(file_path, 'w') as outfp: |
| 436 | + json.dump(data, outfp) |
| 437 | + elif isinstance(app.builder, StandaloneHTMLBuilder): |
| 438 | + # If the builder is the StandaloneHTMLBuilder, we just assume that the |
| 439 | + # user meant to generate an HTML file and do that here. |
| 440 | + |
| 441 | + # The below code is a lightly-modified version of the code at |
| 442 | + # https://gitlab.com/documatt/sphinx-reredirects . It is licensed as BSD. |
| 443 | + |
| 444 | + # HTML used as redirect file content |
| 445 | + redirect_template = '<html><head><meta http-equiv="refresh" content="0; url=${to_uri}"></head></html>' |
| 446 | + # For each entry |
| 447 | + for source, target in redirects.items(): |
| 448 | + # examine if it matches to some doc |
| 449 | + for doc in app.env.found_docs: |
| 450 | + if fnmatch(doc, source): |
| 451 | + # if so, apply $source placeholder |
| 452 | + new_target = Template(target).substitute({'source': doc}) |
| 453 | + # create redirect file |
| 454 | + redirect_file_path = Path(app.outdir).joinpath(doc).with_suffix('.html') |
| 455 | + content = Template(redirect_template).substitute({'to_uri': new_target}) |
| 456 | + redirect_file_path.write_text(content) |
| 457 | + else: |
| 458 | + print('Only JSON and HTML builders support redirects') |
| 459 | + |
| 460 | + |
407 | 461 | def setup(app): |
408 | 462 | RedirectFrom.register(app) |
| 463 | + |
| 464 | + app.connect('build-finished', apply_redirects) |
| 465 | + app.add_config_value('redirects', {}, 'env') |
| 466 | + |
409 | 467 | app.connect('missing-reference', make_router( |
410 | 468 | 'Installation', 'Installation/Eloquent' |
411 | 469 | )) |
0 commit comments