Repository: getnikola/nikola

Issue:
Plugin with a path handler generating external links
### Description:
I wanted to write a plugin allowing me register a path handler which would give me a possibility to share a bibliography of useful externals links between posts.

For example:
```rst
__ link://biblio/linkname ---> https://some.random.site.com/path/to/resource
```
Turns out, judging by the Nikola code, path handlers are only able to generate internal links, which makes the whole link handler mechanism useless for the case.

Is there a way I can help with developing the feature? Or is there an alternative approach to the problem?


Discussion:
I think we can modify `Nikola.path` (and possibly `Nikola.link`) to behave differently if the path handler returns a single string instead of a list/tuple. And yes, we welcome contributions from all users, so if you’re willing to do it — please go ahead!
If you are using reStructured text, the suggested answer would be a custom role.

In general, you could use a templated shortcode, it's pretty simple and requires no code.

But yes, it's intriguing if we could provide such a thing.
That was fast! :-)

Anyways, I am looking at the code of `Nikola.path` right now. AFAICS, it should be almost trivial to just check if the return value is a string (using `isinstance(path, str)`) and immediately return it.

Am I missing something here..?

Given this modification I believe I can easily come up with the plugin I need.
@ralsina Yes, sure, a shortcode is an option but using the path handler mechanism just feels right here.
@vkazanov yeah, that should work. Feel free to PR :-)
Great! I'll take a look at it.

Resolving patch:
diff --git a/nikola/nikola.py b/nikola/nikola.py
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -1736,38 +1736,47 @@ def path(self, kind, name, lang=None, is_link=False, **kwargs):
         * slug (name is the slug of a post or page)
         * filename (name is the source filename of a post/page, in DEFAULT_LANG, relative to conf.py)
 
-        The returned value is always a path relative to output, like
-        "categories/whatever.html"
+        The returned value is either a path relative to output, like "categories/whatever.html", or
+        an absolute URL ("https://getnikola.com/"), if path handler returns a string.
 
         If is_link is True, the path is absolute and uses "/" as separator
         (ex: "/archive/index.html").
         If is_link is False, the path is relative to output and uses the
         platform's separator.
         (ex: "archive\index.html")
+        If the registered path handler returns a string instead of path component list - it's
+        considered to be an absolute URL and returned as is.
+
         """
         if lang is None:
             lang = utils.LocaleBorg().current_lang
 
         try:
             path = self.path_handlers[kind](name, lang, **kwargs)
-            if path is None:
-                path = "#"
-            else:
-                path = [os.path.normpath(p) for p in path if p != '.']  # Fix Issue #1028
-            if is_link:
-                link = '/' + ('/'.join(path))
-                index_len = len(self.config['INDEX_FILE'])
-                if self.config['STRIP_INDEXES'] and \
-                        link[-(1 + index_len):] == '/' + self.config['INDEX_FILE']:
-                    return link[:-index_len]
-                else:
-                    return link
-            else:
-                return os.path.join(*path)
         except KeyError:
             utils.LOGGER.warn("Unknown path request of kind: {0}".format(kind))
             return ""
 
+        # If path handler returns a string we consider it to be an absolute URL not requiring any
+        # further processing, i.e 'https://getnikola.com/'. See Issue #2876.
+        if isinstance(path, str):
+            return path
+
+        if path is None:
+            path = "#"
+        else:
+            path = [os.path.normpath(p) for p in path if p != '.']  # Fix Issue #1028
+        if is_link:
+            link = '/' + ('/'.join(path))
+            index_len = len(self.config['INDEX_FILE'])
+            if self.config['STRIP_INDEXES'] and \
+                    link[-(1 + index_len):] == '/' + self.config['INDEX_FILE']:
+                return link[:-index_len]
+            else:
+                return link
+        else:
+            return os.path.join(*path)
+
     def post_path(self, name, lang):
         """Link to the destination of an element in the POSTS/PAGES settings.
 


Test patch: