Skip to content

Commit 24c86aa

Browse files
committedSep 20, 2015
Merge pull request #2100 from getnikola/feed-previewimage
New option FEED_PREVIEWIMAGE includes post previewimage meta
2 parents a390487 + b417c82 commit 24c86aa

File tree

10 files changed

+137
-68
lines changed

10 files changed

+137
-68
lines changed
 

Diff for: ‎CHANGES.txt

+7
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ New in master
44
Features
55
--------
66

7+
* New option ``FEED_PREVIEWIMAGE`` includes the ``post.meta.previewimage``
8+
image in Atom and RSS feeds. (Issue #2095)
9+
710
Bugfixes
811
--------
912

1013
* Fix reST post list date formatting error (Issue #2104)
14+
* Deprecated ``RSS_TEASERS``, ``RSS_PLAIN``, ``RSS_READ_MORE_LINK``, and
15+
``RSS_LINKS_APPEND_QUERY`` in favor of ``FEED_TEASERS``, ``FEED_PLAIN``,
16+
``FEED_READ_MORE_LINK``, and ``FEED_LINKS_APPEND_QUERY`` for both Atom
17+
and RSS feeds. (Issue #2095)
1118
* /robots.txt was never being built (Issue #2098)
1219

1320
New in v7.7.1

Diff for: ‎docs/manual.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,8 @@ In Markdown (or basically, the resulting HTML of any format):
515515
By default all your RSS feeds will be shortened (they'll contain only teasers)
516516
whereas your index page will still show complete posts. You can change
517517
this behaviour with your ``conf.py``: ``INDEX_TEASERS`` defines whether index
518-
page should display the whole contents or only teasers. ``RSS_TEASERS``
519-
works the same way for your RSS feeds.
518+
page should display the whole contents or only teasers. ``FEED_TEASERS``
519+
works the same way for your Atom and RSS feeds.
520520

521521
By default, teasers will include a "read more" link at the end. If you want to
522522
change that text, you can use a custom teaser:

Diff for: ‎nikola/conf.py.in

+21-14
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,6 @@ HIDDEN_AUTHORS = ['Guest']
425425
# output / TRANSLATION[lang] / RSS_PATH / rss.xml
426426
# RSS_PATH = ""
427427

428-
# Number of posts in RSS feeds
429-
# FEED_LENGTH = 10
430-
431428
# Slug the Tag URL. Easier for users to type, special characters are
432429
# often removed or replaced as well.
433430
# SLUG_TAG_PATH = True
@@ -677,18 +674,18 @@ IMAGE_FOLDERS = {'images': 'images'}
677674

678675
# 'Read more...' for the index page, if INDEX_TEASERS is True (translatable)
679676
INDEX_READ_MORE_LINK = ${INDEX_READ_MORE_LINK}
680-
# 'Read more...' for the RSS_FEED, if RSS_TEASERS is True (translatable)
681-
RSS_READ_MORE_LINK = ${RSS_READ_MORE_LINK}
677+
# 'Read more...' for the RSS_FEED, if FEED_TEASERS is True (translatable)
678+
FEED_READ_MORE_LINK = ${FEED_READ_MORE_LINK}
682679

683-
# Append a URL query to the RSS_READ_MORE_LINK in Atom and RSS feeds. Advanced
680+
# Append a URL query to the FEED_READ_MORE_LINK in Atom and RSS feeds. Advanced
684681
# option used for traffic source tracking.
685682
# Minimum example for use with Piwik: "pk_campaign=feed"
686683
# The following tags exist and are replaced for you:
687684
# {feedRelUri} A relative link to the feed.
688685
# {feedFormat} The name of the syndication format.
689686
# Example using replacement for use with Google Analytics:
690687
# "utm_source={feedRelUri}&utm_medium=nikola_feed&utm_campaign={feedFormat}_feed"
691-
RSS_LINKS_APPEND_QUERY = False
688+
FEED_LINKS_APPEND_QUERY = False
692689

693690
# A HTML fragment describing the license, for the sidebar.
694691
# (translatable)
@@ -892,22 +889,32 @@ MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra']
892889
# them. Generate Atom for tags by setting TAG_PAGES_ARE_INDEXES to True.
893890
# Atom feeds are built based on INDEX_DISPLAY_POST_COUNT and not FEED_LENGTH
894891
# Switch between plain-text summaries and full HTML content using the
895-
# RSS_TEASER option. RSS_LINKS_APPEND_QUERY is also respected. Atom feeds
892+
# FEED_TEASER option. FEED_LINKS_APPEND_QUERY is also respected. Atom feeds
896893
# are generated even for old indexes and have pagination link relations
897894
# between each other. Old Atom feeds with no changes are marked as archived.
898895
# GENERATE_ATOM = False
899896

897+
# Only inlclude teasers in Atom and RSS feeds. Disabling include the full
898+
# content. Defaults to True.
899+
# FEED_TEASERS = True
900+
901+
# Strip HTML from Atom annd RSS feed summaries and content. Defaults to False.
902+
# FEED_PLAIN = False
903+
904+
# Number of posts in Atom and RSS feeds.
905+
# FEED_LENGTH = 10
906+
907+
# Inclue preview image as a <figure><img></figure> at the top of the entry.
908+
# Requires FEED_PLAIN = False. If the preview image is found in the content,
909+
# it will not be included again. Image will be included as-is, aim to optmize
910+
# the image source for Feedly, Apple News, Flipboard, and other popular clients.
911+
# FEED_PREVIEWIMAGE = True
912+
900913
# RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None,
901914
# the base.tmpl will use the feed Nikola generates. However, you may want to
902915
# change it for a FeedBurner feed or something else.
903916
# RSS_LINK = None
904917

905-
# Show teasers (instead of full posts) in feeds? Defaults to True.
906-
# RSS_TEASERS = True
907-
908-
# Strip HTML in the RSS feed? Default to False
909-
# RSS_PLAIN = False
910-
911918
# A search form to search this site, for the sidebar. You can use a Google
912919
# custom search (https://www.google.com/cse/)
913920
# Or a DuckDuckGo search: https://duckduckgo.com/search_box.html

Diff for: ‎nikola/nikola.py

+81-27
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080

8181
# Default "Read more..." link
8282
DEFAULT_INDEX_READ_MORE_LINK = '<p class="more"><a href="{link}">{read_more}…</a></p>'
83-
DEFAULT_RSS_READ_MORE_LINK = '<p><a href="{link}">{read_more}…</a> ({min_remaining_read})</p>'
83+
DEFAULT_FEED_READ_MORE_LINK = '<p><a href="{link}">{read_more}…</a> ({min_remaining_read})</p>'
8484

8585
# Default pattern for translation files' names
8686
DEFAULT_TRANSLATIONS_PATTERN = '{path}.{lang}.{ext}'
@@ -450,16 +450,17 @@ def __init__(self, **config):
450450
'PRETTY_URLS': False,
451451
'FUTURE_IS_NOW': False,
452452
'INDEX_READ_MORE_LINK': DEFAULT_INDEX_READ_MORE_LINK,
453-
'RSS_READ_MORE_LINK': DEFAULT_RSS_READ_MORE_LINK,
454-
'RSS_LINKS_APPEND_QUERY': False,
455453
'REDIRECTIONS': [],
456454
'ROBOTS_EXCLUSIONS': [],
457455
'GENERATE_ATOM': False,
456+
'FEED_TEASERS': True,
457+
'FEED_PLAIN': False,
458+
'FEED_PREVIEWIMAGE': True,
459+
'FEED_READ_MORE_LINK': DEFAULT_FEED_READ_MORE_LINK,
460+
'FEED_LINKS_APPEND_QUERY': False,
458461
'GENERATE_RSS': True,
459462
'RSS_LINK': None,
460463
'RSS_PATH': '',
461-
'RSS_PLAIN': False,
462-
'RSS_TEASERS': True,
463464
'SASS_COMPILER': 'sass',
464465
'SASS_OPTIONS': [],
465466
'SEARCH_FORM': '',
@@ -543,7 +544,7 @@ def __init__(self, **config):
543544
'EXTRA_HEAD_DATA',
544545
'NAVIGATION_LINKS',
545546
'INDEX_READ_MORE_LINK',
546-
'RSS_READ_MORE_LINK',
547+
'FEED_READ_MORE_LINK',
547548
'INDEXES_TITLE',
548549
'POSTS_SECTION_COLORS',
549550
'POSTS_SECTION_DESCRIPTIONS',
@@ -608,6 +609,38 @@ def __init__(self, **config):
608609
for i1, i2, i3 in self.config['PAGES']:
609610
self.config['post_pages'].append([i1, i2, i3, False])
610611

612+
# RSS_TEASERS has been replaced with FEED_TEASERS
613+
# TODO: remove on v8
614+
if 'RSS_TEASERS' in config:
615+
utils.LOGGER.warn('The RSS_TEASERS option is deprecated, use FEED_TEASERS instead.')
616+
if 'FEED_TEASERS' in config:
617+
utils.LOGGER.warn('FEED_TEASERS conflicts with RSS_TEASERS, ignoring RSS_TEASERS.')
618+
self.config['FEED_TEASERS'] = config['RSS_TEASERS']
619+
620+
# RSS_PLAIN has been replaced with FEED_PLAIN
621+
# TODO: remove on v8
622+
if 'RSS_PLAIN' in config:
623+
utils.LOGGER.warn('The RSS_PLAIN option is deprecated, use FEED_PLAIN instead.')
624+
if 'FEED_PLAIN' in config:
625+
utils.LOGGER.warn('FEED_PLIN conflicts with RSS_PLAIN, ignoring RSS_PLAIN.')
626+
self.config['FEED_PLAIN'] = config['RSS_PLAIN']
627+
628+
# RSS_LINKS_APPEND_QUERY has been replaced with FEED_LINKS_APPEND_QUERY
629+
# TODO: remove on v8
630+
if 'RSS_LINKS_APPEND_QUERY' in config:
631+
utils.LOGGER.warn('The RSS_LINKS_APPEND_QUERY option is deprecated, use FEED_LINKS_APPEND_QUERY instead.')
632+
if 'FEED_TEASERS' in config:
633+
utils.LOGGER.warn('FEED_LINKS_APPEND_QUERY conflicts with RSS_LINKS_APPEND_QUERY, ignoring RSS_LINKS_APPEND_QUERY.')
634+
self.config['FEED_LINKS_APPEND_QUERY'] = utils.TranslatableSetting('FEED_LINKS_APPEND_QUERY', config['RSS_LINKS_APPEND_QUERY'], self.config['TRANSLATIONS'])
635+
636+
# RSS_READ_MORE_LINK has been replaced with FEED_READ_MORE_LINK
637+
# TODO: remove on v8
638+
if 'RSS_READ_MORE_LINK' in config:
639+
utils.LOGGER.warn('The RSS_READ_MORE_LINK option is deprecated, use FEED_READ_MORE_LINK instead.')
640+
if 'FEED_READ_MORE_LINK' in config:
641+
utils.LOGGER.warn('FEED_READ_MORE_LINK conflicts with RSS_READ_MORE_LINK, ignoring RSS_READ_MORE_LINK')
642+
self.config['FEED_READ_MORE_LINK'] = utils.TranslatableSetting('FEED_READ_MORE_LINK', config['RSS_READ_MORE_LINK'], self.config['TRANSLATIONS'])
643+
611644
# DEFAULT_TRANSLATIONS_PATTERN was changed from "p.e.l" to "p.l.e"
612645
# TODO: remove on v8
613646
if 'TRANSLATIONS_PATTERN' not in self.config:
@@ -1280,10 +1313,12 @@ def generic_rss_renderer(self, lang, title, link, description, timeline, output_
12801313

12811314
for post in timeline[:feed_length]:
12821315
data = post.text(lang, teaser_only=rss_teasers, strip_html=rss_plain,
1283-
rss_read_more_link=True, rss_links_append_query=feed_append_query)
1316+
feed_read_more_link=True, feed_links_append_query=feed_append_query)
12841317
if feed_url is not None and data:
12851318
# Massage the post's HTML (unless plain)
12861319
if not rss_plain:
1320+
if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in data:
1321+
data = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], data)
12871322
# FIXME: this is duplicated with code in Post.text()
12881323
try:
12891324
doc = lxml.html.document_fromstring(data)
@@ -1835,8 +1870,6 @@ def atom_link(link_rel, link_type, link_href):
18351870
nslist = {}
18361871
if context["is_feed_stale"] or "feedpagenum" in context and (not context["feedpagenum"] == context["feedpagecount"] - 1 and not context["feedpagenum"] == 0):
18371872
nslist["fh"] = "http://purl.org/syndication/history/1.0"
1838-
if not self.config["RSS_TEASERS"]:
1839-
nslist["xh"] = "http://www.w3.org/1999/xhtml"
18401873
feed_xsl_link = self.abs_link("/assets/xml/atom.xsl")
18411874
feed_root = lxml.etree.Element("feed", nsmap=nslist)
18421875
feed_root.addprevious(lxml.etree.ProcessingInstruction(
@@ -1881,31 +1914,45 @@ def atom_link(link_rel, link_type, link_href):
18811914
feed_generator.text = "Nikola"
18821915

18831916
feed_append_query = None
1884-
if self.config["RSS_LINKS_APPEND_QUERY"]:
1885-
feed_append_query = self.config["RSS_LINKS_APPEND_QUERY"].format(
1917+
if self.config["FEED_LINKS_APPEND_QUERY"]:
1918+
feed_append_query = self.config["FEED_LINKS_APPEND_QUERY"].format(
18861919
feedRelUri=context["feedlink"],
18871920
feedFormat="atom")
18881921

1889-
for post in posts:
1890-
data = post.text(lang, teaser_only=self.config["RSS_TEASERS"], strip_html=self.config["RSS_TEASERS"],
1891-
rss_read_more_link=True, rss_links_append_query=feed_append_query)
1892-
if not self.config["RSS_TEASERS"]:
1922+
def atom_post_text(post, text):
1923+
if not self.config["FEED_PLAIN"]:
1924+
if self.config["FEED_PREVIEWIMAGE"] and 'previewimage' in post.meta[lang] and post.meta[lang]['previewimage'] not in text:
1925+
text = "<figure><img src=\"{}\"></figure> {}".format(post.meta[lang]['previewimage'], text)
1926+
18931927
# FIXME: this is duplicated with code in Post.text() and generic_rss_renderer
18941928
try:
1895-
doc = lxml.html.document_fromstring(data)
1929+
doc = lxml.html.document_fromstring(text)
18961930
doc.rewrite_links(lambda dst: self.url_replacer(post.permalink(lang), dst, lang, 'absolute'))
18971931
try:
18981932
body = doc.body
1899-
data = (body.text or '') + ''.join(
1933+
text = (body.text or '') + ''.join(
19001934
[lxml.html.tostring(child, encoding='unicode')
19011935
for child in body.iterchildren()])
19021936
except IndexError: # No body there, it happens sometimes
1903-
data = ''
1937+
text = ''
19041938
except lxml.etree.ParserError as e:
19051939
if str(e) == "Document is empty":
1906-
data = ""
1940+
text = ""
19071941
else: # let other errors raise
19081942
raise(e)
1943+
return text
1944+
1945+
for post in posts:
1946+
summary = atom_post_text(post, post.text(lang, teaser_only=True,
1947+
strip_html=self.config["FEED_PLAIN"],
1948+
feed_read_more_link=True,
1949+
feed_links_append_query=feed_append_query))
1950+
content = None
1951+
if not self.config["FEED_TEASERS"]:
1952+
content = atom_post_text(post, post.text(lang, teaser_only=self.config["FEED_TEASERS"],
1953+
strip_html=self.config["FEED_PLAIN"],
1954+
feed_read_more_link=True,
1955+
feed_links_append_query=feed_append_query))
19091956

19101957
entry_root = lxml.etree.SubElement(feed_root, "entry")
19111958
entry_title = lxml.etree.SubElement(entry_root, "title")
@@ -1922,14 +1969,19 @@ def atom_link(link_rel, link_type, link_href):
19221969
entry_root.append(atom_link("alternate", "text/html",
19231970
post.permalink(lang, absolute=True,
19241971
query=feed_append_query)))
1925-
if self.config["RSS_TEASERS"]:
1926-
entry_summary = lxml.etree.SubElement(entry_root, "summary")
1927-
entry_summary.text = data
1972+
entry_summary = lxml.etree.SubElement(entry_root, "summary")
1973+
if not self.config["FEED_PLAIN"]:
1974+
entry_summary.set("type", "html")
19281975
else:
1976+
entry_summary.set("type", "text")
1977+
entry_summary.text = summary
1978+
if content:
19291979
entry_content = lxml.etree.SubElement(entry_root, "content")
1930-
entry_content.set("type", "xhtml")
1931-
entry_content_nsdiv = lxml.etree.SubElement(entry_content, "{http://www.w3.org/1999/xhtml}div")
1932-
entry_content_nsdiv.text = data
1980+
if not self.config["FEED_PLAIN"]:
1981+
entry_content.set("type", "html")
1982+
else:
1983+
entry_content.set("type", "text")
1984+
entry_content.text = content
19331985
for category in post.tags_for_language(lang):
19341986
entry_category = lxml.etree.SubElement(entry_root, "category")
19351987
entry_category.set("term", utils.slugify(category))
@@ -1980,8 +2032,7 @@ def generic_index_renderer(self, lang, posts, indexes_title, template_name, cont
19802032
kw['indexes_prety_page_url'] = self.config["INDEXES_PRETTY_PAGE_URL"]
19812033
kw['demote_headers'] = self.config['DEMOTE_HEADERS']
19822034
kw['generate_atom'] = self.config["GENERATE_ATOM"]
1983-
kw['feed_link_append_query'] = self.config["RSS_LINKS_APPEND_QUERY"]
1984-
kw['feed_teasers'] = self.config["RSS_TEASERS"]
2035+
kw['feed_link_append_query'] = self.config["FEED_LINKS_APPEND_QUERY"]
19852036
kw['currentfeed'] = None
19862037

19872038
# Split in smaller lists
@@ -2072,6 +2123,9 @@ def generic_index_renderer(self, lang, posts, indexes_title, template_name, cont
20722123
context["currentfeedlink"] = kw["currentfeed"]
20732124
context["feedpagenum"] = i
20742125
context["feedpagecount"] = num_pages
2126+
kw['feed_teasers'] = self.config['FEED_TEASERS']
2127+
kw['feed_plain'] = self.config['FEED_PLAIN']
2128+
kw['feed_previewimage'] = self.config['FEED_PREVIEWIMAGE']
20752129
atom_task = {
20762130
"basename": basename,
20772131
"name": atom_output_name,

Diff for: ‎nikola/plugins/command/check.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ def analyze(self, fname, find_sources=False, check_remote=False):
227227
d = lxml.etree.parse(filename)
228228
link_elements = lxml.html.fromstring('<html/>')
229229
for elm in d.findall('*//{http://www.w3.org/2005/Atom}link'):
230-
feed_link = elm.attrib['href'].split('?')[0].strip() # strip RSS_LINKS_APPEND_QUERY
230+
feed_link = elm.attrib['href'].split('?')[0].strip() # strip FEED_LINKS_APPEND_QUERY
231231
link_elements.append(lxml.etree.Element('a', href=feed_link))
232232
link_elements = list(link_elements.iterlinks())
233233
elif filename.endswith('sitemap.xml') or filename.endswith('sitemapindex.xml'):

Diff for: ‎nikola/plugins/command/init.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import tarfile
4242

4343
import nikola
44-
from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN, DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_RSS_READ_MORE_LINK, LEGAL_VALUES, urlsplit, urlunsplit
44+
from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN, DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_FEED_READ_MORE_LINK, LEGAL_VALUES, urlsplit, urlunsplit
4545
from nikola.plugin_categories import Command
4646
from nikola.utils import ask, ask_yesno, get_logger, makedirs, STDERR_HANDLER, load_messages
4747
from nikola.packages.tzlocal import get_localzone
@@ -71,7 +71,7 @@
7171
'CATEGORY_OUTPUT_FLAT_HIERARCHY': False,
7272
'TRANSLATIONS_PATTERN': DEFAULT_TRANSLATIONS_PATTERN,
7373
'INDEX_READ_MORE_LINK': DEFAULT_INDEX_READ_MORE_LINK,
74-
'RSS_READ_MORE_LINK': DEFAULT_RSS_READ_MORE_LINK,
74+
'FEED_READ_MORE_LINK': DEFAULT_FEED_READ_MORE_LINK,
7575
'POSTS': """(
7676
("posts/*.rst", "posts", "post.tmpl"),
7777
("posts/*.txt", "posts", "post.tmpl"),
@@ -210,10 +210,10 @@ def prepare_config(config):
210210
"""Parse sample config with JSON."""
211211
p = config.copy()
212212
p.update({k: json.dumps(v, ensure_ascii=False) for k, v in p.items()
213-
if k not in ('POSTS', 'PAGES', 'COMPILERS', 'TRANSLATIONS', 'NAVIGATION_LINKS', '_SUPPORTED_LANGUAGES', '_SUPPORTED_COMMENT_SYSTEMS', 'INDEX_READ_MORE_LINK', 'RSS_READ_MORE_LINK')})
213+
if k not in ('POSTS', 'PAGES', 'COMPILERS', 'TRANSLATIONS', 'NAVIGATION_LINKS', '_SUPPORTED_LANGUAGES', '_SUPPORTED_COMMENT_SYSTEMS', 'INDEX_READ_MORE_LINK', 'FEED_READ_MORE_LINK')})
214214
# READ_MORE_LINKs require some special treatment.
215215
p['INDEX_READ_MORE_LINK'] = "'" + p['INDEX_READ_MORE_LINK'].replace("'", "\\'") + "'"
216-
p['RSS_READ_MORE_LINK'] = "'" + p['RSS_READ_MORE_LINK'].replace("'", "\\'") + "'"
216+
p['FEED_READ_MORE_LINK'] = "'" + p['FEED_READ_MORE_LINK'].replace("'", "\\'") + "'"
217217
# fix booleans and None
218218
p.update({k: str(v) for k, v in config.items() if isinstance(v, bool) or v is None})
219219
return p

0 commit comments

Comments
 (0)
Please sign in to comment.