Liquid template: Get first 5 posts that match criterion - jekyll

I want to display the 5 most recent posts which have a thumbnail on the main page of my jekyll-blog. How to achieve this?
I set the thumnail as an attribut in the header of the post:
---
layout: post
title: Lorem Ipsum
thumb: images/thumb.jpg
---
I tried
{% for post in site.posts | sort:"date" | reverse | limit: 5 %}
{% if post.thumb %}
<img src= ... />
{% endif %}
{% endfor %}
but of course, if one of the five happens not to have a picture, only four will be displayed. Is there a smooth way around this?

Try:
{% assign maxPost = 5 %}
{% assign counter = 0 %}
{% for post in site.posts | sort:"date" | reverse %}
{% if post.thumb %}
<img src= ... />
{% assign counter = counter | plus: 1 %}
{% if counter == maxPost %}
{% break %} {% comment %}exit the for loop{% endcomment %}
{% endif %}
{% endif %}
{% endfor %}

Related

Jekyll liquid syntax for loading _data subfolders for Staticman comments?

I’m trying to set up staticman comments and have gotten all the code implemented to save comments correctly, but the comments are not loading.
My site has multiple types of pages so I have recipe comments save in _data/comments/recipes/recipe-name/entry12233445430320.yml
The problem is in this code:
{% assign urlslug = page.url | remove:'.html' | remove_first:'/' | split: '/' %}
{% assign urlfolder = urlslug[0] %}
{% assign urlfilename = urlslug[1] %}
<p>folder: {{urlfolder}}</p><p>filename: {{urlfilename}}</p>
<h2>{{post_slug}}</h2>
{% if site.data.comments.recipes[urlfilename] %}
<!-- Start static comments -->
<div id="comments" class="js-comments">
<h2 class="page__section-label">
{% if site.data.comments.recipes[urlfilename].size > 1 %}
{{ site.data.comments.recipes[urlfilename] | size }}
{% endif %}
Comments test 1
</h2>
<h2> {{ site.data.comments.recipes[urlfilename] }} </h2>
{% assign comments = site.data.comments.recipes[urlfilename] | sort | where_exp: 'entry', 'comment[1].replying_to_uid == blank' %}
<h3>a: {{ comments }} </h3>
<h3>b: {{ comments[0] }} </h3>
{% for comment in comments %}
<h3>comment test 2</h3>
{% assign index = forloop.index %}
<!-- {% assign replying_to = comment[1].replying_to | to_integer %}
{% assign avatar = comment[1].avatar %} -->
{% assign email = comment[1].email %}
{% assign name = comment[1].name %}
{% assign url = comment[1].url %}
{% assign date = comment[1].date %}
{% assign message = comment[1].message %}
{%- assign uid = comment[1]._id %}
{% include comment.html index=index replying_to=replying_to avatar=avatar email=email name=name url=url date=date message=message %}
{% endfor %}
</div>
<!-- End static comments -->
{% endif %}
I'm successfully accessing the right comment and printing it on the page in raw format, but I can't make it print the comment correctly.
Any help is greatly appreciated.

Liquid: ifelse not working and printing var in the body

I'm trying to create a home page for a simple blog with HTML and CSS. For the blog posts, I've created 2 types of cards: blog-card and blog-card alt, one to the left and one to the right, and they are meanted to be alterneted. So I did the following code:
{% assign iter = 0 %}
{% for post in site.posts %}
{% increment iter %}
{% assign itermodulo = iter | modulo: 2 %}
{% if itermodulo == 0 %}
<div class="blog-card">
...
</div>
{% else %}
<div class="blog-card alt">
...
</div>
{% endif %}
{% endfor %}
But instead of alternating the card format, all the cards are on the left (following blog-card) and the var iter is being printed on the screen. What I have to do? Thanks in advace!
Something like this should work:
{% for post in site.posts %}
{% assign itermodulo = forloop.index | modulo: 2 %}
{% if itermodulo == 0 %}
<div class="blog-card"></div>
{% else %}
<div class="blog-card alt"></div>
{% endif %}
{% endfor %}

Unable to use concat on `nil`

I am trying to build a related post include file for my Jekyll site. The site is based around the concept of members, attractions and parks (each as collections). Each post has a many to many relationships. I am trying to up a combined array of each of the page attributes (members, attractions and parks) loop through the array and find posts with a common number of tags.
It's quite simple but I am getting stuck with one section, not all the posts have members, attractions and parks fields so they are returning nil but the concat filter requires an array. I am trying to default the variable to an [] but it always gets set to nil. Any ideas?
Here's the code:
<ul class="row">
{% assign pageTags = [] %}{% if page.tags.first %}{% assign pageTags = page.tags %}{% endif %}
{% assign pageAttractions = [] %}{% if page.attractions.first %}{% assign pageAttractions = page.attractions %}{% endif %}
{% assign pageMembers = [] %}{% if page.members.first %}{% assign pageMembers = page.members %}{% endif %}
{% assign pageParks = [] %}{% if page.parks.first %}{% assign pageParks = page.parks %}{% endif %}
{% assign pageTagList = pageTags | concat: pageAttractions | concat: pageMembers | concat: pageParks %}
{% for post in site.documents %}
{% assign sameTagCount = 0 %}
{% assign commonTags = '' %}
{% assign postTags = [] %}{% if post.tags %}{% assign postTags = post.tags %}{% endif %}
{% assign postAttractions = [] %}{% if post.attractions %}{% assign postAttractions = post.attractions %}{% endif %}
{% assign postMembers = [] %}{% if post.members %}{% assign postMembers = post.members %}{% endif %}
{% assign postParks = [] %}{% if post.parks %}{% assign postParks = post.parks %}{% endif %}
{% assign postTageList = postTags | concat: postAttractions | concat: postMembers | concat postParks %}
{% if post.hidden == true %}
{% break %}
{% endif %}
{% for tag in postTageList %}
{% if post.url != page.url %}
{% if pageTagList contains tag %}
{% assign sameTagCount = sameTagCount | plus: 1 %}
{% capture tagmarkup %} <span class="label label-default">{{ tag }}</span> {% endcapture %}
{% assign commonTags = commonTags | append: tagmarkup %}
{% endif %}
{% endif %}
{% endfor %}
{% if sameTagCount >= minCommonTags %}
<li class="col-lg-4 col-md-12">
<div class="main-image">
</div>
<h5>{{ post.categories | first }}</h5>
<h3>{{ post.title | replace: 'Review', '' }}</h3>
<p>
{% if post.description %}
{{ post.description }}
{% else %}
{{ post.content | markdownify | strip_html | truncatewords: 20 }}
{% endif %}
</p>
<p>
Read Article →
</p>
</li>
{% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %}
{% if maxRelatedCounter >= maxRelated %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
</ul>
You can see the repo here: https://github.com/dtsn/jungleskipper/blob/feature/members/_includes/related-posts.html
From the Liquid documentation:
You cannot initialize arrays using only Liquid.
You can, however, use the split filter to break a string into an array of substrings.
You should look at compact which removes any nil values from an array.
Here is a link to the doc on shopify.
Example from Liquid documentation
Input:
{% assign site_categories = site.pages | map: "category" %}
{% for category in site_categories %}
- {{ category }}
{% endfor %}
Output:
- business
- celebrities
-
- lifestyle
- sports
-
- technology
With Compact
Input:
{% assign site_categories = site.pages | map: "category" | compact %}
{% for category in site_categories %}
- {{ category }}
{% endfor %}
Output:
- business
- celebrities
- lifestyle
- sports
- technology

Looping through all list of posts not in some category in liquid

I want to loop through posts on a site except ones with the category unlisted. I'm able to do this by nesting an if statement inside the for loop, but this breaks down when I want to also specify a limit – the loop will run for 5 times only regardless of whether the post passes the check.
{% for post in site.posts limit: 5 %}
{% unless post.categories contains 'unlisted' %}
<!-- display post -->
{% endunless %}
{% endfor %}
I need to pass an already filtered list to the for loop, but I'm unable to do this mainly because I can't find a way to combine the where filter with contains and negation:
{% for post in site.posts | WHERE CATEGORIES NOT CONTAINS 'UNLISTED' | limit: 5 %}
<!-- display post -->
{% endfor %}
You can use a counter :
<ul>
{% assign postCounter = 0 %}
{% assign maxPost = 5 %}
{% for post in site.posts %}
{% unless post.categories contains 'unlisted' %}
<li>{{ post.title }}</li>
{% assign postCounter = postCounter | plus: 1 %}
{% if postCounter >= maxPost %}
{% break %}
{% endif %}
{% endunless %}
{% endfor %}
</ul>

How to set up related collection items rather than related posts in Jekyll?

I've successfully implemented related posts in my site by using site.related_posts with the following Liquid templating:
The code bellow returns 4 posts related by tags on every post page, so far so good, but what's the best approach to achieve something like this for Jekyll collections?
{% assign hasSimilar = '' %}
{% for post in site.related_posts %}
{% assign postHasSimilar = false %}
{% for tag in post.tags %}
{% for thisTag in page.tags %}
{% if postHasSimilar == false and hasSimilar.size < 4 and post != page and tag == thisTag %}
{% if hasSimilar.size == 0 %}
{% endif %}
<a href="{{ site.baseurl }}{{ post.url }}" class="entry">
<div class="entry-media">
<img src="{{ post.thumbnail | prepend: site.baseurl }}" alt="">
<p class="post-cat">{% for category in post.categories.last %}{{ category }}{% endfor %}</p>
</div>
<h6 class="entry-title">{{ post.title | truncatewords: 70, '...' }}</h6>
<p class="post-date">{{ post.date | date: "%d/%m/%Y" }}</p>{% if post.series %}(Series: {{ post.series }}){% endif %}
</a>
{% capture hasSimilar %}{{ hasSimilar }}*{% endcapture %}
{% assign postHasSimilar = true %}{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% if hasSimilar.size > 0 %}
{% endif %}
As per Jekyll's documentation, when you create a collection it becomes available via the site Liquid variable, much like site.pages and site.posts, so in order to create a related collection items for my portfolio collection I replicated the Liquid logic above and assigned site.portfolio as posts, see:
{% assign posts = site.portfolio | sort: "tags" %}
So I set tags: [tag1, tag2, tag3] on every "project.md" document's front-matter within my _portfolio collectionas, as I'd normally do with posts and the liquid logic above returns the related portfolio collection items.
Anyway, although I'm not sure if this is the right path to achieve such functionality, it works as intended. I couldn't find any references/ usage for site.related_posts when it come to collections.
I'd really appreciate any thoughts regarding this issue. Thanks in advance.
site.related_posts are the ten most recent posts.
So, your code is only looking for related in ten posts not all posts.
The following code will looks for tags related items for site.posts or any site.collection
{% assign pageTagsNumber = page.tags | size %}
{% assign related = "" | split: "/" %}<!-- create empty array -->
{% assign relatedCount = 0 %}
{% assign maxRelated = 4 %}<!-- maximum number of related items -->
{% assign minTagMatch = 4 %}<!-- minimum number of tag match to be a related -->
{% if minTagMatch > pageTagsNumber %}
{% assign minTagMatch = pageTagsNumber %}
{% endif %}
{% assign matchedComplete = false %}<!-- flag -->
<!--
numberOfTag to match to be in the related list
Will try to match all page tags, then page tags size - 1, -2, until reaching minTagMatch
-->
{% for numberOfTag in (minTagMatch...pageTagsNumber) reversed %}
<!-- Looping over site.posts or any site.mycollection -->
<!-- here you can do {% for item in site.mycollection %} -->
{% for item in site.posts %}
<!-- !!! ITEM SPECIFIC -->
<!-- for a collection change item.title == page.title if needed-->
{% if related contains item or item.title == page.title %}
<!--
Don't scan an item that is already in related
Don't scan an item if we are on his page
-->
{% continue %}<!-- go to next item -->
{% endif %}
{% assign itemTagsNumber = item.tags | size %}
{% if itemTagsNumber < numberOfTag %}
<!-- Not enough tags {{ itemTagsNumber }} -->
{% continue %}<!-- go to next item -->
{% endif %}
{% assign matchingTags = 0 %}<!-- tag match counter -->
{% for tag in page.tags %}
<!-- Comparing page tag "{{ tag }}" to {{ item.tags | join: ", " }} -->
{% if item.tags contains tag %}
<!-- one matching tag increment matchingTags counter-->
{% assign matchingTags = matchingTags | plus: 1 %}
<!-- We have a match and {{ matchingTags }} matching tags total -->
{% if matchingTags >= numberOfTag %}
<!-- we have enough matching tag, this is a related item -->
{% capture html %}
<li><a href="{{ site.baseurl }}{{ item.url }}">
{{ item.title }}
{% assign pageTagsNumber = item.tags | size %}
- has {{ pageTagsNumber }} tags
- match on {{ matchingTags }} tags
</a></li>
{% endcapture %}
{% assign related = related | push: html %}
{% assign relatedCount = related | size %}
{% if relatedCount >= maxRelated %}
<!-- if we have all our related set the matchedComplete flag to true -->
{% assign matchedComplete = true %}
{% endif %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
{% if matchedComplete == true %}
<!-- matchedComplete breaking item loop! -->
{% break %}
{% endif %}
{% endfor %}
{% if matchedComplete == true %}
<!-- matchedComplete breaking numberOfTag loop! -->
{% break %}
{% endif %}
{% endfor %}
{% if relatedCount > 0 %}
<!-- print related items -->
<ul>
{% for item in related %}
{{ item }}
{% endfor %}
</ul>
{% endif %}