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

Better support for cross-language magic links #3812

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
* `follower <https://github.com/follower>`_
* `George Leslie-Waksman <https://github.com/gwax>`_
* `Grzegorz Śliwiński <https://github.com/fizyk>`_
* `gudzpoz <https://codeberg.org/gudzpoz/>`_
* `Guillermo O. Freschi <https://github.com/Tordek>`_
* `h4ckninja <https://github.com/h4ckninja>`_
* `Hardening <https://github.com/hardening>`_
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ New in master
Features
--------

* Support using ``link://kind/name?lang=some_lang`` to link to pages in specific languages
(Issue #3811)

Bugfixes
--------

* Fix ``link://filename/`` magic links to handle multilingual pages (Issue #3811)
* Ignore errors in parsing SVG files for shrinking them, copy original file to output instead
(Issue #3785)
* Restore `annotation_helper.tmpl` with dummy content - fix themes still mentioning it
Expand Down
17 changes: 5 additions & 12 deletions docs/path_handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
.. DO NOT EDIT, this file is auto-generated by scripts/document_path_handlers.py

Nikola supports special links with the syntax ``link://kind/name``. In
templates you can also use ``_link(kind, name)``. You can add query strings
templates you can also use ``_link(kind, name)``. You can add query strings
(``?key=value``) for extra arguments, or pass keyword arguments to ``_link`` in
templates (support and behavior depends on path handlers themselves). Fragments
(``#anchor``) will be appended to the transformed link.
templates (support and behavior depends on path handlers themselves). By default,
you can only link to current language's contents; in order to link to a different
language, use ``link://kind/name?lang=other_lang`` or ``_link(kind, name, lang)``.
Fragments (``#anchor``) will be appended to the transformed link.

Here are the descriptions for all the supported kinds.

Expand Down Expand Up @@ -83,7 +85,6 @@ filename
Example:

link://filename/manual.txt => /docs/handbook.html


gallery
Link to an image gallery's path.
Expand All @@ -94,7 +95,6 @@ gallery
link://gallery/london => /galleries/trips/london/index.html

link://gallery/trips/london => /galleries/trips/london/index.html


gallery_global
Link to the global gallery path, which contains all the images in galleries.
Expand All @@ -106,7 +106,6 @@ gallery_global
link://gallery_global/trips/london => /galleries/trips/london/index.html

(a ``gallery`` link could lead to eg. /en/galleries/trips/london/index.html)


gallery_rss
Link to an image gallery's RSS feed.
Expand All @@ -117,7 +116,6 @@ gallery_rss
link://gallery_rss/london => /galleries/trips/london/rss.xml

link://gallery_rss/trips/london => /galleries/trips/london/rss.xml


index
Link to a numbered index.
Expand Down Expand Up @@ -150,7 +148,6 @@ listing
link://listing/hello.py => /listings/tutorial/hello.py.html

link://listing/tutorial/hello.py => /listings/tutorial/hello.py.html


listing_source
Return a link to the source code for a listing.
Expand All @@ -162,15 +159,13 @@ listing_source
link://listing_source/hello.py => /listings/tutorial/hello.py

link://listing_source/tutorial/hello.py => /listings/tutorial/hello.py


post_path
Link to the destination of an element in the POSTS/PAGES settings.

Example:

link://post_path/posts => /blog


root
Link to the current language's root.
Expand All @@ -180,7 +175,6 @@ root
link://root_path => /

link://root_path => /translations/spanish/


rss
A link to the RSS feed path.
Expand All @@ -195,7 +189,6 @@ slug
Example:

link://slug/yellow-camaro => /posts/cars/awful/yellow-camaro/index.html


tag
A link to a tag's page. Takes page number as optional keyword argument.
Expand Down
22 changes: 16 additions & 6 deletions nikola/nikola.py
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,10 @@ def url_replacer(self, src, dst, lang=None, url_type=None):
else:
link_kwargs = {}

if 'lang' in link_kwargs:
lang = link_kwargs['lang']
del link_kwargs['lang']

# unquote from issue #2934
dst = self.link(dst_url.netloc, unquote(dst_url.path.lstrip('/')), lang, **link_kwargs)
if dst_url.fragment:
Expand Down Expand Up @@ -1960,20 +1964,26 @@ def slug_path(self, name, lang):
utils.LOGGER.warning('Ambiguous path request for slug: {0}'.format(name))
return [_f for _f in results[0].permalink(lang).split('/')]

def filename_path(self, name, lang):
def filename_path(self, name, _lang):
"""Link to post or page by source filename.

Example:

link://filename/manual.txt => /docs/handbook.html
"""
results = [p for p in self.timeline if p.source_path == name]
if not results:
utils.LOGGER.warning("Cannot resolve path request for filename: {0}".format(name))
else:
name = os.path.normpath(name)
results = [p for p in self.timeline if os.path.normpath(p.source_path) == name]
if results:
if len(results) > 1:
utils.LOGGER.error("Ambiguous path request for filename: {0}".format(name))
return [_f for _f in results[0].permalink(lang).split('/') if _f]
else:
# Probably multilingual post, we have to iterate through the posts more costly
results = self.timeline
for p in results:
for lang in p.translated_to:
if os.path.normpath(p.translated_source_path(lang)) == name:
return [_f for _f in p.permalink(lang).split('/') if _f]
utils.LOGGER.warning("Cannot resolve path request for filename: {0}".format(name))

def register_path_handler(self, kind, f):
"""Register a path handler."""
Expand Down
8 changes: 5 additions & 3 deletions scripts/document_path_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
.. DO NOT EDIT, this file is auto-generated by scripts/document_path_handlers.py

Nikola supports special links with the syntax ``link://kind/name``. In
templates you can also use ``_link(kind, name)``. You can add query strings
templates you can also use ``_link(kind, name)``. You can add query strings
(``?key=value``) for extra arguments, or pass keyword arguments to ``_link`` in
templates (support and behavior depends on path handlers themselves). Fragments
(``#anchor``) will be appended to the transformed link.
templates (support and behavior depends on path handlers themselves). By default,
you can only link to current language's contents; in order to link to a different
language, use ``link://kind/name?lang=other_lang`` or ``_link(kind, name, lang)``.
Fragments (``#anchor``) will be appended to the transformed link.

Here are the descriptions for all the supported kinds.

Expand Down
12 changes: 12 additions & 0 deletions tests/data/translated_titles/pages/1.pl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,15 @@
.. slug: 1

Bar

`en <link://slug/1?lang=en>`_

`pl <link://slug/1>`_

`pl 2 <link://slug/2>`_

`file en <link://filename/pages/1.txt>`_

`file pl <link://filename/pages/1.pl.txt>`_

`file pl 2 <link://filename/pages/2.pl.txt>`_
12 changes: 12 additions & 0 deletions tests/data/translated_titles/pages/1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@
.. date: 2001/01/01 00:00:00

Foo

`en <link://slug/1>`_

`pl <link://slug/1?lang=pl>`_

`pl 2 <link://slug/2?lang=pl>`_

`file en <link://filename/pages/1.txt>`_

`file pl <link://filename/pages/1.pl.txt>`_

`file pl 2 <link://filename/pages/2.pl.txt>`_
14 changes: 14 additions & 0 deletions tests/data/translated_titles/pages/2.pl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. title: Two
.. slug: 2

`en <link://slug/1?lang=en>`_

`pl <link://slug/1>`_

`pl 2 <link://slug/2>`_

`file en <link://filename/pages/1.txt>`_

`file pl <link://filename/pages/1.pl.txt>`_

`file pl 2 <link://filename/pages/2.pl.txt>`_
31 changes: 31 additions & 0 deletions tests/integration/test_translated_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,37 @@ def test_translated_titles(build, output_dir, other_locale):
assert doc.find("//title").text == "Bar | Demo Site"


def test_translated_cross_links(build, output_dir, other_locale):
"""Check that cross-language links are correct."""
files = [
os.path.join(output_dir, "pages", "1", "index.html"),
os.path.join(output_dir, other_locale, "pages", "1", "index.html"),
os.path.join(output_dir, other_locale, "pages", "2", "index.html"),
]
expected = {
"en": "pages/1",
"pl": "pl/pages/1",
"pl 2": "pl/pages/2",
"file en": "pages/1",
"file pl": "pl/pages/1",
"file pl 2": "pl/pages/2",
}
for f in files:
assert os.path.isfile(f)
with io.open(f, "r", encoding="utf8") as inf:
doc = lxml.html.parse(inf)
link_count = 0
for link in doc.findall("//article//p//a"):
text = link.text
href = link.get("href")
if text in expected:
link_count += 1
dest = os.path.join(os.path.dirname(f), href)
rel = os.path.relpath(dest, output_dir)
assert rel == os.path.normpath(expected[text])
assert link_count == len(expected)


@pytest.fixture(scope="module")
def build(target_dir, test_dir):
"""Build the site."""
Expand Down
Loading