Skip to content
Permalink
Browse files
New option FEED_PREVIEWIMAGE includes post previewimage meta (Issue #…
…2095)

Checks to see if the previewimage is found in the content before
including it. Avoids duplicate imagery. <figure><img></figure> is
recommended by Flipboard and supported by Apple News and Feedly.

* Deprecated RSS_PLAIN in favor of FEED_PLAIN for Atom and RSS
* Deprecated RSS_TEASERS in favor of FEED_TEASER for Atom and RSS
* Hi mom
  • Loading branch information
da2x committed Sep 18, 2015
1 parent dac0a62 commit 900131f04f875cff21c45e15347f57036cbe887e
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 38 deletions.
@@ -4,9 +4,14 @@ New in Master
Features
--------

* New option ``FEED_PREVIEWIMAGE`` includes the ``post.meta.previewimage``
image in Atom and RSS feeds. (Issue #2095)

Bugfixes
--------

* Deprecated ``RSS_TEASERS`` and ``RSS_PLAIN`` in favor of ``FEED_TEASERS``
and ``FEED_PLAIN`` for both RSS and Atom feeds. (Issue #2095)
* /robots.txt was never being built (Issue #2098)

New in v7.7.1
@@ -425,9 +425,6 @@ HIDDEN_AUTHORS = ['Guest']
# output / TRANSLATION[lang] / RSS_PATH / rss.xml
# RSS_PATH = ""

# Number of posts in RSS feeds
# FEED_LENGTH = 10

# Slug the Tag URL. Easier for users to type, special characters are
# often removed or replaced as well.
# SLUG_TAG_PATH = True
@@ -677,7 +674,7 @@ IMAGE_FOLDERS = {'images': 'images'}

# 'Read more...' for the index page, if INDEX_TEASERS is True (translatable)
INDEX_READ_MORE_LINK = ${INDEX_READ_MORE_LINK}
# 'Read more...' for the RSS_FEED, if RSS_TEASERS is True (translatable)
# 'Read more...' for the RSS_FEED, if FEED_TEASERS is True (translatable)
RSS_READ_MORE_LINK = ${RSS_READ_MORE_LINK}

# Append a URL query to the RSS_READ_MORE_LINK in Atom and RSS feeds. Advanced
@@ -892,22 +889,31 @@ MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra']
# them. Generate Atom for tags by setting TAG_PAGES_ARE_INDEXES to True.
# Atom feeds are built based on INDEX_DISPLAY_POST_COUNT and not FEED_LENGTH
# Switch between plain-text summaries and full HTML content using the
# RSS_TEASER option. RSS_LINKS_APPEND_QUERY is also respected. Atom feeds
# FEED_TEASER option. RSS_LINKS_APPEND_QUERY is also respected. Atom feeds
# are generated even for old indexes and have pagination link relations
# between each other. Old Atom feeds with no changes are marked as archived.
# GENERATE_ATOM = False

# Only inlclude teasers in Atom and RSS feeds. Disabling include the full content.
# FEED_TEASERS = True

# Strip HTML from Atom annd RSS feed summaries and content.
# FEED_PLAIN = True

# Number of posts in Atom and RSS feeds.
# FEED_LENGTH = 10

# Inclue preview image as a <figure><img></figure> at the top of the entry.
# Requires FEED_PLAIN = False. If the preview image is found in the content,
# it will not be included again. Image will be included as-is, aim to optmize
# the image source for Feedly, Apple News, Flipboard, and other popular clients.
# FEED_PREVIEWIMAGE = False

# RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None,
# the base.tmpl will use the feed Nikola generates. However, you may want to
# change it for a FeedBurner feed or something else.
# RSS_LINK = None

# Show teasers (instead of full posts) in feeds? Defaults to True.
# RSS_TEASERS = True

# Strip HTML in the RSS feed? Default to False
# RSS_PLAIN = False

# A search form to search this site, for the sidebar. You can use a Google
# custom search (https://www.google.com/cse/)
# Or a DuckDuckGo search: https://duckduckgo.com/search_box.html
@@ -455,11 +455,12 @@ def __init__(self, **config):
'REDIRECTIONS': [],
'ROBOTS_EXCLUSIONS': [],
'GENERATE_ATOM': False,
'FEED_TEASERS': True,
'FEED_PLAIN': True,
'FEED_PREVIEWIMAGE': False,
'GENERATE_RSS': True,
'RSS_LINK': None,
'RSS_PATH': '',
'RSS_PLAIN': False,
'RSS_TEASERS': True,
'SASS_COMPILER': 'sass',
'SASS_OPTIONS': [],
'SEARCH_FORM': '',
@@ -608,6 +609,22 @@ def __init__(self, **config):
for i1, i2, i3 in self.config['PAGES']:
self.config['post_pages'].append([i1, i2, i3, False])

# RSS_TEASERS has been replaced with FEED_TEASERS
# TODO: remove on v8
if 'RSS_TEASERS' in config:
utils.LOGGER.warn('The RSS_TEASERS option is deprecated, use FEED_TEASERS instead.')
if 'FEED_TEASERS' in config:
utils.LOGGER.warn('FEED_TEASERS conflicts with RSS_TEASERS, ignoring RSS_TEASERS.')
self.config['FEED_TEASERS'] = config['RSS_TEASERS']

# RSS_PLAIN has been replaced with FEED_PLAIN
# TODO: remove on v8
if 'RSS_TEASERS' in config:
utils.LOGGER.warn('The RSS_TEASERS option is deprecated, use FEED_TEASERS instead.')
if 'FEED_TEASERS' in config:
utils.LOGGER.warn('FEED_TEASERS conflicts with RSS_TEASERS, ignoring RSS_TEASERS.')
self.config['FEED_TEASERS'] = config['RSS_TEASERS']

# DEFAULT_TRANSLATIONS_PATTERN was changed from "p.e.l" to "p.l.e"
# TODO: remove on v8
if 'TRANSLATIONS_PATTERN' not in self.config:
@@ -1284,6 +1301,8 @@ def generic_rss_renderer(self, lang, title, link, description, timeline, output_
if feed_url is not None and data:
# Massage the post's HTML (unless plain)
if not rss_plain:
if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in data:
data = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], data)
# FIXME: this is duplicated with code in Post.text()
try:
doc = lxml.html.document_fromstring(data)
@@ -1835,8 +1854,6 @@ def atom_link(link_rel, link_type, link_href):
nslist = {}
if context["is_feed_stale"] or "feedpagenum" in context and (not context["feedpagenum"] == context["feedpagecount"] - 1 and not context["feedpagenum"] == 0):
nslist["fh"] = "http://purl.org/syndication/history/1.0"
if not self.config["RSS_TEASERS"]:
nslist["xh"] = "http://www.w3.org/1999/xhtml"
feed_xsl_link = self.abs_link("/assets/xml/atom.xsl")
feed_root = lxml.etree.Element("feed", nsmap=nslist)
feed_root.addprevious(lxml.etree.ProcessingInstruction(
@@ -1886,26 +1903,41 @@ def atom_link(link_rel, link_type, link_href):
feedRelUri=context["feedlink"],
feedFormat="atom")

for post in posts:
data = post.text(lang, teaser_only=self.config["RSS_TEASERS"], strip_html=self.config["RSS_TEASERS"],
rss_read_more_link=True, rss_links_append_query=feed_append_query)
if not self.config["RSS_TEASERS"]:
def atom_post_text(post, text):
if not self.config["FEED_PLAIN"]:
if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in text:
text = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], text)

# FIXME: this is duplicated with code in Post.text() and generic_rss_renderer
try:
doc = lxml.html.document_fromstring(data)
doc = lxml.html.document_fromstring(text)
doc.rewrite_links(lambda dst: self.url_replacer(post.permalink(lang), dst, lang, 'absolute'))
try:
body = doc.body
data = (body.text or '') + ''.join(
text = (body.text or '') + ''.join(
[lxml.html.tostring(child, encoding='unicode')
for child in body.iterchildren()])
except IndexError: # No body there, it happens sometimes
data = ''
text = ''
except lxml.etree.ParserError as e:
if str(e) == "Document is empty":
data = ""
text = ""
else: # let other errors raise
raise(e)
return text

for post in posts:
summary = atom_post_text(post, post.text(lang, teaser_only=True,
strip_html=self.config["FEED_PLAIN"],
rss_read_more_link=True,
rss_links_append_query=feed_append_query))
content = None
if not self.config["FEED_TEASERS"]:
content = atom_post_text(post, post.text(lang, teaser_only=self.config["FEED_TEASERS"],
strip_html=self.config["FEED_PLAIN"],
rss_read_more_link=True,
rss_links_append_query=feed_append_query))


entry_root = lxml.etree.SubElement(feed_root, "entry")
entry_title = lxml.etree.SubElement(entry_root, "title")
@@ -1922,14 +1954,19 @@ def atom_link(link_rel, link_type, link_href):
entry_root.append(atom_link("alternate", "text/html",
post.permalink(lang, absolute=True,
query=feed_append_query)))
if self.config["RSS_TEASERS"]:
entry_summary = lxml.etree.SubElement(entry_root, "summary")
entry_summary.text = data
entry_summary = lxml.etree.SubElement(entry_root, "summary")
if not self.config["FEED_PLAIN"]:
entry_summary.set("type", "html")
else:
entry_summary.set("type", "text")
entry_summary.text = summary
if content:
entry_content = lxml.etree.SubElement(entry_root, "content")
entry_content.set("type", "xhtml")
entry_content_nsdiv = lxml.etree.SubElement(entry_content, "{http://www.w3.org/1999/xhtml}div")
entry_content_nsdiv.text = data
if not self.config["FEED_PLAIN"]:
entry_content.set("type", "html")
else:
entry_content.set("type", "text")
entry_content.text = content
for category in post.tags_for_language(lang):
entry_category = lxml.etree.SubElement(entry_root, "category")
entry_category.set("term", utils.slugify(category))
@@ -1981,7 +2018,6 @@ def generic_index_renderer(self, lang, posts, indexes_title, template_name, cont
kw['demote_headers'] = self.config['DEMOTE_HEADERS']
kw['generate_atom'] = self.config["GENERATE_ATOM"]
kw['feed_link_append_query'] = self.config["RSS_LINKS_APPEND_QUERY"]
kw['feed_teasers'] = self.config["RSS_TEASERS"]
kw['currentfeed'] = None

# Split in smaller lists
@@ -2072,6 +2108,9 @@ def generic_index_renderer(self, lang, posts, indexes_title, template_name, cont
context["currentfeedlink"] = kw["currentfeed"]
context["feedpagenum"] = i
context["feedpagecount"] = num_pages
kw['feed_teasers'] = self.config['FEED_TEASERS']
kw['feed_plain'] = self.config['FEED_PLAIN']
kw['feed_previewimage'] = self.config['FEED_PREVIEWIMAGE']
atom_task = {
"basename": basename,
"name": atom_output_name,
@@ -2088,6 +2127,7 @@ def generic_index_renderer(self, lang, posts, indexes_title, template_name, cont
}
yield utils.apply_filters(atom_task, kw['filters'])


if kw["indexes_pages_main"] and kw['indexes_prety_page_url'](lang):
# create redirection
output_name = os.path.join(kw['output_folder'], page_path(0, utils.get_displayed_page_number(0, num_pages, self), num_pages, True))
@@ -68,8 +68,8 @@ def gen_tasks(self):
'author_path': self.site.config['AUTHOR_PATH'],
"author_pages_are_indexes": self.site.config['AUTHOR_PAGES_ARE_INDEXES'],
"generate_rss": self.site.config['GENERATE_RSS'],
"rss_teasers": self.site.config["RSS_TEASERS"],
"rss_plain": self.site.config["RSS_PLAIN"],
"feed_teasers": self.site.config["FEED_TEASERS"],
"feed_plain": self.site.config["FEED_PLAIN"],
"rss_link_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"feed_length": self.site.config['FEED_LENGTH'],
@@ -58,10 +58,11 @@ def gen_tasks(self):
"base_url": self.site.config["BASE_URL"],
"blog_description": self.site.config["BLOG_DESCRIPTION"],
"output_folder": self.site.config["OUTPUT_FOLDER"],
"rss_teasers": self.site.config["RSS_TEASERS"],
"rss_plain": self.site.config["RSS_PLAIN"],
"feed_teasers": self.site.config["FEED_TEASERS"],
"feed_plain": self.site.config["FEED_PLAIN"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"feed_length": self.site.config['FEED_LENGTH'],
"feed_previewimage": self.site.config["FEED_PREVIEWIMAGE"],
"tzinfo": self.site.tzinfo,
"rss_read_more_link": self.site.config["RSS_READ_MORE_LINK"],
"rss_links_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"],
@@ -96,7 +97,7 @@ def gen_tasks(self):
'actions': [(utils.generic_rss_renderer,
(lang, kw["blog_title"](lang), kw["site_url"],
kw["blog_description"](lang), posts, output_name,
kw["rss_teasers"], kw["rss_plain"], kw['feed_length'], feed_url,
kw["feed_teasers"], kw["feed_plain"], kw['feed_length'], feed_url,
None, kw["rss_links_append_query"]))],

'task_dep': ['render_posts'],
@@ -74,8 +74,8 @@ def gen_tasks(self):
'category_prefix': self.site.config['CATEGORY_PREFIX'],
"category_pages_are_indexes": self.site.config['CATEGORY_PAGES_ARE_INDEXES'],
"generate_rss": self.site.config['GENERATE_RSS'],
"rss_teasers": self.site.config["RSS_TEASERS"],
"rss_plain": self.site.config["RSS_PLAIN"],
"feed_teasers": self.site.config["FEED_TEASERS"],
"feed_plain": self.site.config["FEED_PLAIN"],
"rss_link_append_query": self.site.config["RSS_LINKS_APPEND_QUERY"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"feed_length": self.site.config['FEED_LENGTH'],
@@ -379,7 +379,7 @@ def tag_rss(self, tag, lang, posts, kw, is_category):
'actions': [(utils.generic_rss_renderer,
(lang, "{0} ({1})".format(kw["blog_title"](lang), self._get_title(tag, is_category)),
kw["site_url"], None, post_list,
output_name, kw["rss_teasers"], kw["rss_plain"], kw['feed_length'],
output_name, kw["feed_teasers"], kw["feed_plain"], kw['feed_length'],
feed_url, None, kw["rss_link_append_query"]))],
'clean': True,
'uptodate': [utils.config_changed(kw, 'nikola.plugins.task.tags:rss')] + deps_uptodate,

0 comments on commit 900131f

Please sign in to comment.