How can I omit content from Octopress/Jekyll's extracted excerpt? - jekyll

I'm using Octopress 2.0 for blogging, which uses the Jekyll static site generator.
To mark an excerpt of a blog post for the front page, you can insert <!--more--> into the post, and the content up to that point will be used as the excerpt..
For some posts, even though I want an excerpt, I want to exclude certain things from it (maybe a table of contents that only makes sense on the post, or some extra notes that aren't useful for an excerpt).
Is there a way in Octopress/Jekyll/Liquid to use the <!--more--> method to generate an excerpt, but also mark some small amount of content to be left out it?
Here is a brief example. Take this post:
---
layout: post
title: "Example Post"
---
This is the first paragraph. It will be included in the excerpt.
[Jump to third paragraph](#para3). This paragraph should **not** be in the excerpt.
This is the second paragraph. It will be included in the excerpt.
<!--more-->
<a name="para3"></a>This is the third paragraph. It won't be in the excerpt.
I want a way to have the generated excerpt for this post be:
This is the first paragraph. It will be included in the excerpt.
This is the second paragraph. It will be included in the excerpt.

Edit : I now understand what you're trying to do.
I assume that you're using the default octopress markdown : rdiscount.
Let's got the filter way :
In Gemfile add gem 'nokogiri'
In your post, the idea is to add a span.secondary to the part that has sometimes to be stripped. content
...
remove_secondary_content_from_excerpt : true
---
This is the first paragraph. It will be included in the excerpt.
[[Jump to third paragraph](#para3). This paragraph should **not**
be in the excerpt.](class:secondary)
This is the second paragraph. It will be included in the excerpt.
<!--more-->
### This is the TEXT title
This is **the text**
In _includes/article.html
...
</header>
{% endunless %}
{% if index %}
<div class="entry-content">
{% if post.remove_secondary_content_from_excerpt == true %}
{% capture secondary_content %}{{ post.excerpt | excerpt_get_secondary_content }}{% endcapture %}
{{ post.excerpt | remove: secondary_content }}
{% else %}
{{ post.excerpt }}
{% endif %}
</div>
{% capture excerpted %}{{ content | has_excerpt }}{% endcapture %}
{% if excerpted == 'true' %}
<footer>
<a rel="full-article" href="{{ root_url }}{{ post.url }}">{{ site.excerpt_link }}</a>
</footer>
{% endif %}
{% else %}
<div class="entry-content">
<!-- example on how to use it in post page -->
{% if page.remove_secondary_content_from_excerpt == true %}
{% capture secondary_content %}{{ page.excerpt | excerpt_get_secondary_content }}{% endcapture %}
{{ page.excerpt | remove: secondary_content }}
{{ secondary_content }}
{{ page.content | markdownify | remove: page.excerpt }}
{% else %}
{{ content }}
{% endif %}
</div>
{% endif %}
In _plugins/octopress_filters.rb
...
module OctopressLiquidFilters
def excerpt_get_secondary_content(input)
require 'nokogiri'
doc = Nokogiri::HTML(input)
# as excerpt can surrounded by one <p> (when no double newline in it)
# or with multiple <p> when a double newline is found
multiparagraph = doc.css("p").length > 1
if multiparagraph
# look for parent <p>
xpathString = "span.secondary/.."
end
# look only for the span element
xpathString = "span.secondary"
else
secondary = doc.css(xpathString)
secondary.to_s
end
...
Install Nokogiri bundle update
I hop your rake generate will make you happy.

Related

Liquid filtering array over a page property

I've got a jekyll site with two pages (page1.html and page2.html), they both use the same layout. This layout print some information about some other pages in a given subdirectory. For example
/_layouts/test.html
{% for p in site.pages %}
{{ p.title }}
{% endfor %}
/page1.html
---
layout: test
---
this page lists the title of my books...
This would print the title of every page in my site, but I want it to print only the title of pages in the subdirectory /books, so I would change the layout page to
{% for p in site.pages | where: 'dir','/books/' %}
{{ p.title }}
{% endfor %}
This works fine, but I would like another page to use the same layout and list the content of my comics (that are inside /comics folder) instead of my books, so I would change the structure of my site in the following way:
/_layouts/test.html
{% for p in site.pages | where: 'dir','/{{ page.directory_to_scan }}/' %}
{{ p.title }}
{% endfor %}
/page1.html
---
layout: test
directory_to_scan: books
---
this page lists the title of my books...
/page2.html
---
layout: test
directory_to_scan: comics
---
this page lists the title of my comics...
However this does not work and no title at all is printed.
You can't mix tags and filters (except for assign). Moreover, there's no need to enclose variables to a filter within double braces:
---
directory_to_scan: '/comics/'
---
And the layout would use:
{% assign my_pages = site.pages | where: 'dir', page.directory_to_scan %}
{% for p in my_pages %}
{{ p.title }}
{% endfor %}
So, I've eventually solved by assembling the string before the loop, using the append filter.
{% assign d = '/' | append: page.directory_to_scan | append: '/' %}
{% for p in site.pages | where: 'dir',d %}
{{ p.title }}
{% endfor %}

How to change format of page title only for blog posts?

I have a Jekyll website with a number of static pages and a number of blog posts.
On the static pages, my page title is "Site-wide Title | Page Title", which is okay. But on blog posts, I would like the page title to be only the post title, without my site-wide title in front.
The <title> tag is defined in my _includes/head.html, and says
{% if page.title %}My website | {{ page.title }}{% else %}{{ site.title }}{% endif %}
How do I set up different formats for pages and posts?
What is site.title? I can't find it in the Jekyll docs
Use this condition in your head.html:
{% if page.layout == 'post' %}
{{ page.title }}
{% else %}
{{ site.title }} | {{ page.title }}
{% endif %}
However, I would recommend to put the 'page.title' in front of the 'site.title'.

Kramdown table of content doesn't display inside HTML block

I was following this question, but it doesn't seem to take effect for me. Any help would be greatly appreciated.
_includes/layout.html
<main>
<div>
<!-- Sidebar -->
<aside markdown="1">
<h4>Table of Contents</h4>
* ToC
{:toc}
</aside>
<!-- END Sidebar -->
<!-- Main content -->
<article>
{{ content }}
</article>
<!-- END Main content -->
</div>
</main>
_config.yml
markdown: kramdown
Result:
Update
_layouts/site.html
<aside markdown="1">
mark**down**
</aside>
It just renders as above. Kramdown is turned on in config.
Solved by using pure liquid ToC by allejo.
% capture tocWorkspace %}
{% comment %}
"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
Usage:
{% include toc_pure_liquid.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %}
Parameters:
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
Optional Parameters:
* sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC
* class (string) : '' - a CSS class assigned to the TOC
* id (string) : '' - an ID to assigned to the TOC
* h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored
* h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored
Output:
An unordered list representing the table of contents of a markdown block. This snippet will only generate the table of contents and will NOT output the markdown given to it
{% endcomment %}
{% capture my_toc %}{% endcapture %}
{% assign minHeader = include.h_min | default: 1 %}
{% assign maxHeader = include.h_max | default: 6 %}
{% assign nodes = include.html | split: '<h' %}
{% for node in nodes %}
{% if node == "" %}
{% continue %}
{% endif %}
{% assign headerLevel = node | replace: '"', '' | slice: 0, 1 %}
{% assign headerLevel = headerLevel | times: 1 %}
{% assign indentAmount = headerLevel | minus: minHeader | add: 1 %}
{% assign _workspace = node | split: '</h' %}
{% unless headerLevel >= minHeader %}
{% continue %}
{% endunless %}
{% if headerLevel > maxHeader %}
{% continue %}
{% endif %}
{% assign _idWorkspace = _workspace[0] | split: '"' %}
{% assign html_id = _idWorkspace[1] %}
{% capture _hAttrToStrip %}{{ headerLevel }} id="{{ html_id }}">{% endcapture %}
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
{% assign space = '' %}
{% for i in (1..indentAmount) %}
{% assign space = space | prepend: ' ' %}
{% endfor %}
{% capture my_toc %}{{ my_toc }}
{{ space }}- [{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}](#{{ html_id }}){% endcapture %}
{% endfor %}
{% if include.class %}
{% capture my_toc %}{:.{{ include.class }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% if include.id %}
{% capture my_toc %}{: #{{ include.id }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% endcapture %}{% assign tocWorkspace = '' %}
{{ my_toc | markdownify }}
In theory it should work that way, (it is not working for me either) but you can force to process the code inside a block with `markdown="1" like this:
<aside markdown="1">
<h4>Table of Contents</h4>
* ToC
{:toc}
</aside>
Make sure you don't indent the code inside the aside tag or it will be parsed as kramdown code.
By default, kramdown parses all block HTML tags and all XML tags as
raw HTML blocks. However, this can be configured with the
parse_block_html. If this is set to true, then syntax parsing in HTML
blocks is globally enabled. It is also possible to enable/disable
syntax parsing on a tag per tag basis using the markdown attribute:
If an HTML tag has an attribute markdown="0", then the tag is parsed as raw HTML block.
If an HTML tag has an attribute markdown="1", then the default mechanism for parsing syntax in this tag is used.
Update
I've checked your repo, you need to rename index.html to index.md so kramdown will parse it and then you can also add the line to _config.yml to parse markdown inside html blocks.
Jekyll only parse .md or .mardown files.
.html files are not proccessed by markdown parser.
If you rename a file from .html to .md, it will be processed as kramdown.
But there you will have problems with indentation.
Mixing html and markdown is not so easy ;-)

Jekyll Loop breaks on second iteration

I'm looping through two products - on the post view page I pull in a secondary post (in the example, a related recipe) which parses just fine on the first product page - on the second product page just {{ post.content }} won't parse. I can hack it with {{ post.content | markdownify }} - but I'd like to know why it's breaking. Here's the relevant code:
{% for post in site.categories.recipe %}
{% if post.products contains page.title and post.featured %}
<div class="row">
<div class="four columns">
<h4>{{ post.title }}</h4>
<ul>
<li>Serves {{ post.serves }}</li>
<li>Prep: {{ post.time }}</li>
<li>Share</li>
</ul>
{{ post.content }}
...
<!-- All tags are closed, the rest just isn't relevant -->
{% endif %}
{% endfor %}
Please find my solution with counter
<pre>
{% assign counter=0 %}
{% for post in site.posts%}
{% if post.category == 'blog' and counter < 2 %}
{% assign counter=counter | plus:1 %}
{{post.content}}
{% endif %}
{% endfor %}
</pre>
The markdownify filter is probably making it work because there might be special characters that aren't encoded in the content you're pulling from. I always forget to make my & into &.
If you're using the default Markdown interpreter Maruku, here's a list of the entities that might be giving you problems and their encoded equivalent. http://maruku.rubyforge.org/entity_test.html and more info on Maruku. http://maruku.rubyforge.org/maruku.html

How to show a preview of a post? (Using Jekyll Bootstrap theme)

This is probably an easy question, but how do I show previews of my posts on the default page? I am using the Jekyll Bootstrap theme Tom.
This also works at least as of 1.0.0, is built in and is simple to use.
<ul>
{% for post in site.posts %}
<li>
{{ post.title }}
<p>{{ post.excerpt }}</p>
</li>
{% endfor %}
</ul>
See here.
Looking through the functions here, I found strip_html and truncatewords.
Here's a sample "posts list" with 75 words of preview text.
<ul >
{% for post in site.posts limit 4 %}
<li><span>{{ post.date | date_to_string }}</span> » {{ post.title }}</li>
{{ post.content | strip_html | truncatewords:75}}<br>
Read more...<br><br>
{% endfor %}
</ul>
I liked the <!--more--> comment approach from WordPress, so I wrote something along those lines:
_plugins/more.rb:
module More
def more(input, type)
if input.include? "<!--more-->"
if type == "excerpt"
input.split("<!--more-->").first
elsif type == "remaining"
input.split("<!--more-->").last
else
input
end
else
input
end
end
end
Liquid::Template.register_filter(More)
Your post would then look like this:
---
layout: post
title: "Your post title"
published: true
---
<p>This is the excerpt.</p>
<!--more-->
<p>This is the remainder of the post.</p>
You can then use it in your templates like so:
Show excerpt (everything above the <!--more--> comment):
<summary>{{ post.content | more: "excerpt" }}</summary>
Show remainder (everything after the <!--more--> comment):
<article>{{ post.content | more: "remaining" }}</article>
Any argument other than excerpt or remaining will simply show the whole post.
This is an old question, still would like to add a workaround for the formatting issue, as raised in #Talon876 's answer here.
At the end of each post adding closing tags like </em>,</strong> or </b> might not be so neat, but it maintains formatting while showing the excerpts.
For example:
<ul >
{% for post in site.posts limit 4 %}
<li><span>{{ post.date | date_to_string }}</span> » {{ post.title }}</li>
{{ post.content | strip_html | truncatewords:75}}
</em></strong> <!-- This is what does the task-->
<br>
Read more...<br><br>
{% endfor %}
</ul>