Skip to content

Commit

Permalink
Introduce Jinja2 filters for converting links in additional fields.
Browse files Browse the repository at this point in the history
Until now, the link replacing worked only on article and page
contents or summaries. With this patch, if one needes to replace
links in custom fields, there are two new Jinja2 filters that can do
that. For fields that are referenced in the `FORMATTED_FIELDS` setting,
one can use the `expand_links` Jinja2 filter in the template, passing
the field name as a parameter:

    {{ article|expand_links('legal') }}

If the custom field consists of just one link (for example a link to
article cover image for a social meta tag), one can use the
`expand_link` Jinja2 filter:

    {{ article|expand_link('cover') }}

With the above being in a template and `FORMATTED_FIELDS` setting
containing the `'legal'` field, a RST article making use of both fields
could look like this:

    An article
    ##########

    📅 2017-06-22
    :legal: This article is released under `CC0 {filename}/license.rst`.
    :cover: {filename}/img/article-cover.jpg
  • Loading branch information
mosra committed Jun 22, 2017
1 parent 87270c8 commit c506578
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 63 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Next release
============

* New signal: ``feed_generated``
* Introduced ``expand_link`` and ``expand_links`` Jinja2 filters to allow URL
replacement in user-defined metadata fields.

3.7.1 (2017-01-10)
==================
Expand Down
23 changes: 23 additions & 0 deletions docs/content.rst
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,29 @@ and ``article2.md``::
[a link relative to the current file]({filename}category/article1.rst)
[a link relative to the content root]({filename}/category/article1.rst)

The link replacing works by default on article and page contents as well as
summaries. If you need to replace links in custom formatted fields that are
referenced in the ``FORMATTED_FIELDS`` setting, use the ``expand_links``
Jinja2 filter in your template, passing the field name as a parameter::

{{ article|expand_links('legal') }}

If your custom field consists of just one link (for example a link to article
cover image for a social meta tag), use the ``expand_link`` Jinja2 filter::

{{ article|expand_link('cover') }}

With the above being in a template and ``FORMATTED_FIELDS`` setting containing
the ``'legal'`` field, a RST article making use of both fields could look like
this::

An article
##########

:date: 2017-06-22
:legal: This article is released under `CC0 {filename}/license.rst`.
:cover: {filename}/img/article-cover.jpg

Linking to static files
-----------------------

Expand Down
126 changes: 63 additions & 63 deletions pelican/contents.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,68 @@ def get_url_setting(self, key):
key = key if self.in_default_lang else 'lang_%s' % key
return self._expand_settings(key)

def _link_replacer(self, siteurl, m):
what = m.group('what')
value = urlparse(m.group('value'))
path = value.path
origin = m.group('path')

# XXX Put this in a different location.
if what in {'filename', 'attach'}:
if path.startswith('/'):
path = path[1:]
else:
# relative to the source path of this content
path = self.get_relative_source_path(
os.path.join(self.relative_dir, path)
)

if path not in self._context['filenames']:
unquoted_path = path.replace('%20', ' ')

if unquoted_path in self._context['filenames']:
path = unquoted_path

linked_content = self._context['filenames'].get(path)
if linked_content:
if what == 'attach':
if isinstance(linked_content, Static):
linked_content.attach_to(self)
else:
logger.warning(
"%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.",
self.get_relative_source_path())
origin = '/'.join((siteurl, linked_content.url))
origin = origin.replace('\\', '/') # for Windows paths.
else:
logger.warning(
"Unable to find '%s', skipping url replacement.",
value.geturl(), extra={
'limit_msg': ("Other resources were not found "
"and their urls not replaced")})
elif what == 'category':
origin = '/'.join((siteurl, Category(path, self.settings).url))
elif what == 'tag':
origin = '/'.join((siteurl, Tag(path, self.settings).url))
elif what == 'index':
origin = '/'.join((siteurl, self.settings['INDEX_SAVE_AS']))
elif what == 'author':
origin = '/'.join((siteurl, Author(path, self.settings).url))
else:
logger.warning(
"Replacement Indicator '%s' not recognized, "
"skipping replacement",
what)

# keep all other parts, such as query, fragment, etc.
parts = list(value)
parts[2] = origin
origin = urlunparse(parts)

return ''.join((m.group('markup'), m.group('quote'), origin,
m.group('quote')))

def _update_content(self, content, siteurl):
"""Update the content attribute.
Expand All @@ -227,69 +289,7 @@ def _update_content(self, content, siteurl):
\2""".format(instrasite_link_regex)
hrefs = re.compile(regex, re.X)

def replacer(m):
what = m.group('what')
value = urlparse(m.group('value'))
path = value.path
origin = m.group('path')

# XXX Put this in a different location.
if what in {'filename', 'attach'}:
if path.startswith('/'):
path = path[1:]
else:
# relative to the source path of this content
path = self.get_relative_source_path(
os.path.join(self.relative_dir, path)
)

if path not in self._context['filenames']:
unquoted_path = path.replace('%20', ' ')

if unquoted_path in self._context['filenames']:
path = unquoted_path

linked_content = self._context['filenames'].get(path)
if linked_content:
if what == 'attach':
if isinstance(linked_content, Static):
linked_content.attach_to(self)
else:
logger.warning(
"%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.",
self.get_relative_source_path())
origin = '/'.join((siteurl, linked_content.url))
origin = origin.replace('\\', '/') # for Windows paths.
else:
logger.warning(
"Unable to find '%s', skipping url replacement.",
value.geturl(), extra={
'limit_msg': ("Other resources were not found "
"and their urls not replaced")})
elif what == 'category':
origin = '/'.join((siteurl, Category(path, self.settings).url))
elif what == 'tag':
origin = '/'.join((siteurl, Tag(path, self.settings).url))
elif what == 'index':
origin = '/'.join((siteurl, self.settings['INDEX_SAVE_AS']))
elif what == 'author':
origin = '/'.join((siteurl, Author(path, self.settings).url))
else:
logger.warning(
"Replacement Indicator '%s' not recognized, "
"skipping replacement",
what)

# keep all other parts, such as query, fragment, etc.
parts = list(value)
parts[2] = origin
origin = urlunparse(parts)

return ''.join((m.group('markup'), m.group('quote'), origin,
m.group('quote')))

return hrefs.sub(replacer, content)
return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content)

def get_siteurl(self):
return self._context.get('localsiteurl', '')
Expand Down
19 changes: 19 additions & 0 deletions pelican/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import fnmatch
import logging
import os
import re
from codecs import open
from collections import defaultdict
from functools import partial
Expand Down Expand Up @@ -74,6 +75,24 @@ def __init__(self, context, settings, path, theme, output_path,
# provide utils.strftime as a jinja filter
self.env.filters.update({'strftime': DateFormatter()})

# provide link conversion as jinja filters
def _expand_link(content, attr):
link_regex = r"""^
(?P<markup>)(?P<quote>)
(?P<path>{0}(?P<value>.*))
$""".format(settings['INTRASITE_LINK_REGEX'])
links = re.compile(link_regex, re.X)
return links.sub(
lambda m: content._link_replacer(content.get_siteurl(), m),
getattr(content, attr))

def _expand_links(content, attr):
return content._replace_content(getattr(content, attr),
content.get_siteurl())

self.env.filters.update({'expand_link': _expand_link})
self.env.filters.update({'expand_links': _expand_links})

# get custom Jinja filters from user settings
custom_filters = self.settings['JINJA_FILTERS']
self.env.filters.update(custom_filters)
Expand Down

0 comments on commit c506578

Please sign in to comment.