Repository: getpelican/pelican

Issue:
{tag} syntax doesn't generate correct links when used in pages
When using the `{tag}tagname` syntax in an article, it results in the correct link to `/tag/tagname.html`.

When using it in a page, however, it translates to `/pages/tag/tagname.html`, which is a nonexistent location.

Ref: http://stackoverflow.com/q/26621639/695132



Discussion:
Thank you for reporting this @szhorvat. It's likely that this will also happen with `{category}cateoryname` syntax. The problem is that that the replacer returns a path `tag/tagname.html` relative to `SITEURL` as defined in `TAG_URL`. Perhaps a solution would be to make it explicitly relative to siteurl [as with filenames a few lines above](https://github.com/getpelican/pelican/blob/master/pelican/contents.py#L238):

``` Python
# generators.py:247
elif what == 'category':
    origin = '/'.join((siteurl, Category(path, self.settings).url))
elif what == 'tag':
    origin = '/'.join((siteurl, Tag(path, self.settings).url))
```

You can try if this quick fix helps you. If yes, it would be easy to merge it.

I'll try it on the weekend and will let you know.  I'm not in the position to tell how this would interact with the `RELATIVE_URLS` setting, would there be problems?

I actually think this would fix `RELATIVE_URLS` behavior, I doubt it works now. You can try that too.

It does seem to work in a few simple tests.  Wouldn't Windows need the `\` path separator (like a few lines above for filenames)?

no, only `/` can be used in URLs.

Encountered the same bug, except using `{category}foo` and then looking at the various category pages (got an extra category/category link). I can create a PR with the proposed fix above if that is helpful.

@ehashman go ahead :) But please add a few test for the test suite.


Resolving patch:
diff --git a/pelican/contents.py b/pelican/contents.py
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -257,9 +257,9 @@ def replacer(m):
                             'limit_msg': ("Other resources were not found "
                                           "and their urls not replaced")})
             elif what == 'category':
-                origin = Category(path, self.settings).url
+                origin = '/'.join((siteurl, Category(path, self.settings).url))
             elif what == 'tag':
-                origin = Tag(path, self.settings).url
+                origin = '/'.join((siteurl, Tag(path, self.settings).url))
 
             # keep all other parts, such as query, fragment, etc.
             parts = list(value)


Test patch:
diff --git a/pelican/tests/TestPages/page_with_category_and_tag_links.md b/pelican/tests/TestPages/page_with_category_and_tag_links.md
new file mode 100644
--- /dev/null
+++ b/pelican/tests/TestPages/page_with_category_and_tag_links.md
@@ -0,0 +1,7 @@
+Title: Page with a bunch of links
+
+My links:
+
+[Link 1]({tag}マック)
+
+[Link 2]({category}Yeah)
diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py
--- a/pelican/tests/test_contents.py
+++ b/pelican/tests/test_contents.py
@@ -212,17 +212,20 @@ def test_get_content(self):
                            '<a href="|tag|tagname">link</a>')
         page = Page(**args)
         content = page.get_content('http://notmyidea.org')
-        self.assertEqual(content, ('A simple test, with a '
-                                   '<a href="tag/tagname.html">link</a>'))
+        self.assertEqual(
+            content,
+            ('A simple test, with a '
+             '<a href="http://notmyidea.org/tag/tagname.html">link</a>'))
 
         # Category
         args['content'] = ('A simple test, with a '
                            '<a href="|category|category">link</a>')
         page = Page(**args)
         content = page.get_content('http://notmyidea.org')
-        self.assertEqual(content,
-                         ('A simple test, with a '
-                          '<a href="category/category.html">link</a>'))
+        self.assertEqual(
+            content,
+            ('A simple test, with a '
+             '<a href="http://notmyidea.org/category/category.html">link</a>'))
 
     def test_intrasite_link(self):
         # type does not take unicode in PY2 and bytes in PY3, which in
@@ -543,6 +546,31 @@ def test_attach_link_syntax(self):
         self.assertEqual(self.static.save_as, expected_save_as)
         self.assertEqual(self.static.url, path_to_url(expected_save_as))
 
+    def test_tag_link_syntax(self):
+        "{tag} link syntax triggers url replacement."
+
+        html = '<a href="{tag}foo">link</a>'
+        page = Page(
+            content=html,
+            metadata={'title': 'fakepage'}, settings=self.settings,
+            source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
+            context=self.context)
+        content = page.get_content('')
+
+        self.assertNotEqual(content, html)
+
+    def test_category_link_syntax(self):
+        "{category} link syntax triggers url replacement."
+
+        html = '<a href="{category}foo">link</a>'
+        page = Page(content=html,
+            metadata={'title': 'fakepage'}, settings=self.settings,
+            source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
+            context=self.context)
+        content = page.get_content('')
+
+        self.assertNotEqual(content, html)
+
 
 class TestURLWrapper(unittest.TestCase):
     def test_comparisons(self):
diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py
--- a/pelican/tests/test_generators.py
+++ b/pelican/tests/test_generators.py
@@ -428,6 +428,7 @@ def test_generate_context(self):
             ['This is a markdown test page', 'published', 'page'],
             ['This is a test page with a preset template', 'published',
              'custom'],
+            ['Page with a bunch of links', 'published', 'page'],
             ['A Page (Test) for sorting', 'published', 'page'],
         ]
         hidden_pages_expected = [
@@ -517,6 +518,7 @@ def test_generate_sorted(self):
             ['This is a test page', 'published', 'page'],
             ['This is a markdown test page', 'published', 'page'],
             ['A Page (Test) for sorting', 'published', 'page'],
+            ['Page with a bunch of links', 'published', 'page'],
             ['This is a test page with a preset template', 'published',
              'custom'],
         ]
@@ -530,6 +532,7 @@ def test_generate_sorted(self):
         # sort by title
         pages_expected_sorted_by_title = [
             ['A Page (Test) for sorting', 'published', 'page'],
+            ['Page with a bunch of links', 'published', 'page'],
             ['This is a markdown test page', 'published', 'page'],
             ['This is a test page', 'published', 'page'],
             ['This is a test page with a preset template', 'published',
@@ -543,6 +546,26 @@ def test_generate_sorted(self):
         pages = self.distill_pages(generator.pages)
         self.assertEqual(pages_expected_sorted_by_title, pages)
 
+    def test_tag_and_category_links_on_generated_pages(self):
+        """
+        Test to ensure links of the form {tag}tagname and {category}catname
+        are generated correctly on pages
+        """
+        settings = get_settings(filenames={})
+        settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
+        settings['CACHE_PATH'] = self.temp_cache
+        settings['DEFAULT_DATE'] = (1970, 1, 1)
+
+        generator = PagesGenerator(
+            context=settings.copy(), settings=settings,
+            path=CUR_DIR, theme=settings['THEME'], output_path=None)
+        generator.generate_context()
+        pages_by_title = {p.title: p.content for p in generator.pages}
+
+        test_content = pages_by_title['Page with a bunch of links']
+        self.assertIn('<a href="/category/yeah.html">', test_content)
+        self.assertIn('<a href="/tag/matsuku.html">', test_content)
+
 
 class TestTemplatePagesGenerator(unittest.TestCase):