Jekyll truncate number of blog posts conditional on excluding some - arrays

On my Jekyll website, I have an overview page on which I list my last 10 blog posts.
However, I also assign a tag exclude to some of my blog posts and those I don't want to show. This works, but then I don't get the last 10 blog posts, but 10 minus the number of exclude blog posts.
Here is how that looks like:
---
layout: page
title: "Last posts"
permalink: /last/
---
### Last posts
{% for post in site.posts limit:10 %}
{% unless post.category == "exclude"%}
* {{ post.date | date_to_string }} ยป [ {{ post.title }} ]({{ post.url }})
{% endunless %}
{% endfor %}
How can I always show the last 10 non-exclude blog posts?

To show the last 10 non-exclude blog posts:
Create an array with posts that doesn't contain the exclude tag.
{% assign nonexcludeposts = ''|split:''%}
{% for post in site.posts %}
{% unless post.category == "exclude"%}
{% assign nonexcludeposts = nonexcludeposts|push:post%}
{% endunless %}
{% endfor %}
Display 10 most recent posts
<ul>
{% for post in nonexcludeposts limit:10 %}
<li>
{{post.title}}
</li>
{% endfor %}
</ul>

Related

Shopify - Add Number (1,2,3) before Product in Collection

I want the collection page to look like a numbered list (1. product 2. product 3. product ...) with a number in front of each product title.
In the product-grid-item.liquid I found the title and added {{ forloop.index }} in front of it but nothing shows up. When I add {% for product in collection.products %}{% endfor %} around it, it shows "123456789 title". When I add {% for product in collection.products %} before the top div and {% endfor %} after the bottom div, it repeats the whole collection multiple times.
When I use {% cycle '1', '2', '3', '4', '5' %} it shows "1." in front of every product.
What am I doing wrong? Are there other ways? Any help would be appreciated.
Okay, so this bellow code is for default Dawn theme from Shopify.
You need to edit the main-collection-product-grid.liquid and then navigate to code render 'card-product' and pass the foorloop.index as counter
on file card-product.liquid use it before the title
Need to add some logic to calculate correct number, add this code to calulcate it
{%- if paginate.pages > 1 -%}
{% assign page_size = paginate.page_size %}
{% if paginate.current_page > 1 %}
{% assign currPage = paginate.current_page | minus: 1 %}
{% assign loop_item = page_size | times: currPage %}
{% assign loop_item = loop_item | plus: forloop.index %}
{% else %}
{% assign loop_item = forloop.index %}
{% endif %}
{% else %}
{% assign loop_item = forloop.index %}
{% endif %}

How can i retrieve context from for loop in views django and send it to template

I need to do one page with topic and all entries about this topic. And for each entries I need all comments shown up below each entry. Comments should be for exact entry. Cant find it solution and decided ask. Thank you.
models.py
--snip--
class Comment(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
text = models.TextField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
entry = models.ForeignKey(Entry, on_delete=models.CASCADE)
def __str__(self):
return f"{self.text[:50]}..."
views.py
--snip--
def topic(request, topic_id):
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.all()
comments = []
for entry in entries:
c = entry.comment_set.all()
comments.append(c)
context = {'topic': topic, 'entries': entries,
'comments': comments}
return render(request, 'learning_logs/topic.html'
,context)
topic.html
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>{{ topic.title }}</p>
<p>{{ topic.description }}</p><br>
<p>Entries:</p><br>
<ul>
{% for entry in entries %}
<li>{{ entry.text }}</li>
<ul>
<p>Comments:</p>
{% for comment in comments %}
<li>{{ comment.text }}</li>
{% endfor %}<br>
</ul>
{% empty %}
<li>Not entries</li>
{% endfor %}
</ul>
{% endblock content %}
You can do this with just topic. No need to get entries and comments:
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>{{ topic.title }}</p>
<p>{{ topic.description }}</p><br>
<p>Entries:</p><br>
<ul>
{% for entry in topic.entry_set.all %}
<li>{{ entry.text }}</li>
<ul>
<p>Comments:</p>
{% for comment in entry.comment_set.all %}
<li>{{ comment.text }}</li>
{% endfor %}<br>
</ul>
{% empty %}
<li>Not entries</li>
{% endfor %}
</ul>
{% endblock content %}
You will also want to optimise the query using prefetch_related so you don't hit the db per entry per comment:
topic = Topic.objects.prefetch_related('entry_set__comment_set').get(id=topic_id))
context = {'topic': topic}
return render(request, 'learning_logs/topic.html', context)

Wagtail Uploaded Document direct URL

I am trying to generate a url to an uploaded document (PDF, DOC...etc) on my search results page. Search is returning the items but there seems to be no url field. There is a file field which seems to have the file name, but I am unable to get a link back to this file. I am using the stock document model. Is there some sort of special tag that needs to be used like with the images?? At my wits end.
Search view
if search_query:
results = []
page_results = Page.objects.live().search(search_query)
if page_results:
results.append({'page': page_results})
doc_results = Document.objects.all().search(search_query)
if doc_results:
results.append({'docs': doc_results})
img_results = Image.objects.all().search(search_query)
if img_results:
results.append({'image': img_results})
search_results = list(chain(page_results, doc_results, img_results))
query = Query.get(search_query)
# Record hit
query.add_hit()
and the template page.
{% for result in search_results %}
{% for k, v in result.items %}
{% if k == 'page' %}
{% for item in v %}
<p>
<h4>{{ item }}</h4>
Type: Article<br>
Author: {{ item.specific.owner.get_full_name }}<br>
Publish Date: {{ item.specific.last_published_at}}
</p>
{% endfor %}
{% elif k == 'docs' %}
{% for item in v %}
<p>
<h4>{{ item.title }}</h4>
Type: Document<br>
Publish Date: {{ item.created_at }}
</p>
{% endfor %}
{% elif k == 'image' %}
{% for item in v %}
<p>
{% image item original as item_img %}
<h4>{{ item.title }}</h4>
Type: Image<br>
Publish Date: {{ item.created_at }}
</p>
{% endfor %}
{% endif%}
{% endfor %}
{% endfor %}

How to specify multiple authors for a single post, then count the posts each author is related to?

What I'm trying to do:
I'm trying to create a list for the posts each author has written. This is a problem since each post should be able to specify multiple authors.
Let's say we have 3 posts in total and 3 authors in total.
Edit 1: as suggested in the answers this is best done by listing the authors in a front matter list instead of a CSV string. So,
Like this
Post:
---
title: post
authors:
- foo
- bar
---
instead of like this:
Post:
---
title: post
authors: [foo, bar]
---
Problem setup:
(edited, according to Edit 1)
Post 1:
---
title: post1
authors:
- foo
- bar
---
Post 2:
---
title: post2
authors:
- foo
- bar
---
Post 3:
---
title: post3
authors:
- john doe
- bar
---
Out:
bar (3)
john doe (1)
foo (2)
A click on the author should then result in all posts getting displayed.
Alternatively an array can be displayed like this, but it's not helping the case, just an equivalent style.
What I tried
I did the same with categories and tags and this algorithm worked like a charm. However, replacing site.categories with site.authors is somehow not supported.
returns:
Liquid Exception: 0 is not a symbol nor a string in authors.html
I suppose this is due to the nature of categories and tags being arrays by default.
I think it would help to be able to set the front matter tag authors as an array somehow. I suppose this is done in _config.yml, but I busted by head in with it.
As of now I got as far as coming up with a way to target individual authors in an array, but I'm far from being able to list them individually and counting them up. I suppose I'm limited due to the nature of authors not being an array by default, otherwise implementations like this one would work with custom front matter variables like authors, but they don't.
What I meant (when I said "As of now"):
{% for post in site.posts %}
<li>{{ post.authors }} ({{ post.authors[0] }})</li>
{% endfor %}
Out:
foobar (foo)
foobar (foo)
john doebar (john doe)
Afterall, I think I'm missing something here. I'm probably not the first one who tried this but the only documentation I found was from people who attempted what I'm trying but didn't really get there.
This user for example created a way to count users, but when used with site.authors
it returns the array size == 0-if clause:
No author
It seems like a simple thing, but for some reason isn't. At least for me.
Edit 2:
based on Kieths answer I came closer to getting there, but I have issues with creating an emphty array. According to this issue in Jekyll this seems to be a problem in general. However a workaround seems to be to assign a variable and split it with an emphty tag.
Currently I struggle with adding authors to the array so I can assess it's size.
{% assign authors = site.posts | map: 'authors' | uniq %}
{% assign authorCount = '' | split: '' %}
<div class="softwaretitle">
{% for author in authors %}
<a>{{ author }}</a>
{% for post in site.posts %}
{% for post_author in post.authors %}
{% if post_author == author %}
{% assign authorCount = authorCount | push author %}
<a>({{ page.authorCount | size }})</a>
{% endif %}
{% endfor %Edit 2:}
{% endfor %}
{% endfor %}
</div>
Out:
Error: Liquid error (.../_includes/authors.html line 14): wrong number of arguments (given 1, expected 2) included
Error: Run jekyll build --trace for more information.
Line 14:
{% assign authorCount = authorCount | push author %}
Edit 3:
At last, the final result (without link to the post list, but thats details)
<!-- Get a list of unique authors -->
{% assign authors = site.posts | map: 'authors' | uniq | sort%}
{% assign author_post_count = 0 %}
{% for author in authors %}
{% assign author_post_count = 0 %}
<div class="">
<li><a>{{ author }}
{% for post in site.posts %}
{% for post_author in post.authors %}
{% if post_author == author %}
{% assign author_post_count = author_post_count | | plus: 1 %}
{% endif %}
{% endfor %}
{% endfor %}
<span>&nbsp ({{ author_post_count }})</span></a></li>
</div>
{% endfor %}
Out:
bar (3)
john doe (1)
foo (2)
Updated answer:
To get a list of authors (without duplicates) and including the total number of posts the author has contributed to, together with a list of posts' titles and a link to the posts.
{% assign authors = site.posts | map: 'authors' | uniq %}
{% for author in authors %}
{% assign author_post_count = 0 %}
{% for post in site.posts %}
{% if post.authors %}
{% for post_author in post.authors %}
{% if post_author == author %}
{% assign author_post_count = author_post_count | plus: 1 %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
<h2>{{ author }} - {{ author_post_count }}</h2>
{% for post in site.posts %}
{% if post.authors %}
{% for post_author in post.authors %}
{% if post_author == author %}
{% assign author_url_query = author | replace: ' ', '-' %}
<a href="{{ post.url }}" title="A posts {{ author }}">
{{ post.title }}
</a>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
If instead, you want to have a page per author that includes a list of posts written by them (automatically generated), you will need to extend Jekyll through a custom plugin. This is very possible if you have experience with the Ruby programming language. This is a very close example: https://github.com/avillafiorita/jekyll-datapage_gen and you can simply remove the _config data requirements and hardcode the directory names and permalinks :)
Problem :
on a post : print author(s) + number of posts written and link to the authors page
on an author's page : print author's datas + list written posts
Solution
We already have posts that are described like this :
---
title: post2
authors: [foo, bar]
# or with the alternate YAML array syntax
authors:
- foo
- bar
---
For authors, we can user a specific collection that will automatically generate author's page.
In _config.yml :
collections:
authors:
output: true
defaults:
- scope:
type: authors
values:
layout: authors
An author's page can be described like this :
_authors/foo.md
---
uid : foo
firstname: John
lastname: Foo
---
Some bio here ...
Posts List (index or any page):
{% assign posts = site.posts %}
{% comment %}
or {% assign posts = paginator.posts %} if you use pagination
{% endcomment %}
<ul>
{% for post in posts %}
<li>
{{ p.title }}
<br>{% include authors.html post=post %}
</li>
{% endfor %}
</ul>
We will also use our authors include in _layouts/post.html
...
<h1 class="post-title" itemprop="name headline">{{ page.title | escape }}</h1>
<p>{% include authors.html post=page %}</p>
...
Now the magic : _includes/authors.html
{% assign post = include.post %}
{% comment %}## if we have a least one author in post authors array {% endcomment %}
{% if post.authors and post.authors != empty %}
{% comment %} ## We will build a string for each author,
store it in authorsArray
then reassemble it at the end {% endcomment %}
{% assign authorsArray = "" | split: "" %}
{% for author in post.authors %}
{% comment %}## Count posts for current author {% endcomment %}
{% assign authorsPostsCount = site.posts | where_exp: "post", "post.authors contains author" | size %}
{% comment %}## Get authors data based on uid matching in the collection {% endcomment %}
{% assign authorsDatas = site.authors | where: "uid", author | first %}
{% if authorsDatas %}
{% capture authorString %}
{{ authorsDatas.firstname }} {{ authorsDatas.lastname }} ({{ authorsPostsCount }})
{% endcapture %}
{% else %}
{% comment %}## No entry for this author in the collection
## or author spelling is wrong {% endcomment %}
{% capture authorString %}
{{ author | capitalize }} ({{ authorsPostsCount }})
{% endcapture %}
{% endif %}
{% comment %}## Push result in authorsArray {% endcomment %}
{% assign authorsArray = authorsArray | push: authorString %}
{% endfor %}
{% comment %}## Create a sentence with array elements {% endcomment %}
by {{ authorsArray | array_to_sentence_string }}
{% endif %}
_layouts/author.html
---
layout: default
---
<h1>{{ page.firstname }} - {{ page. lastname }}</h1>
{% assign authorsPosts = site.posts | where_exp: "post", "post.authors contains page.uid" %}
<ul>
{% for p in authorsPosts %}
<li>{{ p.title }}</li>
{% endfor %}
</ul>

looping over jekyll collections

I have the following code
<div>
{% for note in site.regnotes %}
{% if note.regulationno == page.regulationno %}
<p>
{{ note.regulationno }} - {{ note.url }}
</p>
{% endif %}
{% endfor %}
</div>
This code loops over the regnotes collection in a jekyll site, checks if the current note regulationno is the same as the page regulationno and if so displays the regulationno and url - that is the url of the current page. How do I change this code to include the url of the previous page, the current page and the next page. I'm looking for three urls - previous, current and next? - The "page.previous.url" variable within jekyll does not appear to work in collections.
This is what it might look like in other code
for i=1 to number of items in the regnotes collection
if current note == page note
print page[i].url //current page url
print page[i-1].url //previous page url
print page[i+1].url //next page url
end if
end for
I suppose what I'm trying todo is reference the items in the collection by their array index. just can't seem to get the syntax correct.
Since you are a programmer, you just need to know that you need to use forloop.index0 to know where you are in the for loop (https://docs.shopify.com/themes/liquid-documentation/objects/for-loops#index0).
The code will be something like:
<div>
{% for note in site.regnotes %}
{% assign current_index = forloop.index0 }}
{% assign next_index = current_index | plus: 1 %}
{% assign prev_index = current_index | minus: 1 %}
{% if note.regulationno == page.regulationno %}
<p>
{{ note.regulationno }} - {{ note.url }}
</p>
{% if site.regnotes[prev_index] %}prev{% endif %}
{% if site.regnotes[next_index] %}next{% endif %}
{% endif %}
{% endfor %}
</div>

Resources