jekyll filename for current file - ruby

The title says it all. Is there a way to access the current filename in Jekyll? Before you jump the gun and try to mark it as duplicate, keep in mind that I'm asking for the current filename, not for the generated filepath which can be access by page.url.
As an example, lets say that there is a file1.html which include file2.html. I want to be be able to get file1.html if I'm inside file1.html and file2.html vice-versa. page.path only returns the filename for the generated file as in I will get file1.html even from within file2.html.
A concrete example:
file2.html:
<div>
{{ page.name }}
</div>
file1.html:
<div>
{% include file2.html %}
</div>
index.html:
{% include file1.html %}
rendered:
<div>
<div>
index.html
</div>
</div>
Instead of index.html, I want it to return file2.html
Thanks

Edit : An include is just a fragment that is inserted into a page. Once every include are made, the page is then processed as a single block of code.
There is no way to get any info from liquid about an include name.b

Related

jinja2 nested for loops two lists

I have the following code that is not rendering the way I would like on the html front end.
{% for image in images %}
{% for title in titles %}
<div class="card" style="width: 18rem;">
<img src="{{image}}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
Go somewhere
</div>
{% endfor %}
{% endfor %}
Essentially, images and titles are both lists of URLS. The images works correctly when on it's own to redner the HTML with all the images.
However, when I try to add titles, which is basically the image URL sliced to give only part of the URL text, in the above format, it messes up the formatting, displaying only one image.
I have tried various different ways but it doesn't work.
I want the code to display all the images in the cards, and in the TITLE field, display the corresponding title, and title in this case is the sliced string (or so I think)
The python (Flask) route, function that scrapes the images URLS and slicing code is shown here:
#app.route('/') #this is what we type into our browser to go to pages. we create these using routes
#app.route('/home')
def home():
images=imagescrape()
titles=(images[58:])
#gettitles=gettitle()
#titles=(gettitles[58:93])
return render_template('home.html',images=images,titles=titles)
def imagescrape():
result_images=[]
#html = urlopen('https://en.wikipedia.org/wiki/Prince_Harry,_Duke_of_Sussex')
html = urlopen('https://en.wikipedia.org/wiki/Rembrandt')
bs = BeautifulSoup(html, 'html.parser')
images = bs.find_all('img', {'src':re.compile('.jpg')})
for image in images:
result_images.append("https:"+image['src']+'\n') #concatenation!
return result_images
UPDATE:
I've got this which NEARLY Works but not quite. It displays an image, then ALL the image urls text, instead of the corresponding one.
{% for image in images %}
<div class="card" style="width: 18rem;">
<img src="{{image}}" class="card-img-top" alt="...">
<div class="card-body">
{% for title in titles %}
<h5 class="card-title">{{title}}</h5>
{% endfor %}
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
Go somewhere
</div>
</div>
{% endfor %}
I think only a small change is needed for it to work but I don't know what.
Combine the two lists of images and titles one by one.
For the iteration, unpack the two variables of the respective tuple.
import re # regular expressions used to match strings
from bs4 import BeautifulSoup # web scraping library
from urllib.request import urlopen # open a url connection
from urllib.parse import unquote # decode special url characters
#app.route('/')
#app.route('/home')
def home():
images=imagescrape()
# Iterate over all sources and extract the title from the URL
titles=(titleextract(src) for src in images)
# zip combines two lists into one.
# It goes through all elements and takes one element from the first
# and one element from the second list, combines them into a tuple
# and adds them to a sequence / generator.
images_titles = zip(images, titles)
return render_template('home.html', image_titles=images_titles)
def imagescrape():
result_images=[]
#html = urlopen('https://en.wikipedia.org/wiki/Prince_Harry,_Duke_of_Sussex')
html = urlopen('https://en.wikipedia.org/wiki/Rembrandt')
bs = BeautifulSoup(html, 'html.parser')
images = bs.find_all('img', {'src':re.compile('.jpg')})
for image in images:
result_images.append("https:"+image['src']+'\n') #concatenation!
return result_images
def titleextract(url):
# Extract the part of the string between the last two "/" characters
# Decode special URL characters and cut off the suffix
# Replace all "_" with spaces
return unquote(url[58:url.rindex("/", 58)-4]).replace('_', ' ')
{% for image, title in images_titles %}
<div class="card" style="width: 18rem;">
<img src="{{image}}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
Go somewhere
</div>
</div>
{% endfor %}

Overriding a context register in a Jekyll Block plugin

I'm trying to get more control over my post excerpts in Jekyll. Using the default excerpt code doesn't seem to work well in my case, as it (a) needs to be the first paragraph, and (b) is still rendered on the page. For most of my blog posts, I want either custom text, which isn't meant to be part of the actual blog post, or some text later on in the blog post. I was hoping to do this with a block, that can optionally take a parameter to render the content (although I'll add in that specific feature later). I've currently got this proof-of-concept plugin:
require "jekyll"
module Jekyll
class RenderExcerptBlock < Liquid::Block
def render(context)
page = context.registers[:page]
content = super
page["excerpt"] = content.lines[0..50]
content
end
end
end
Liquid::Template.register_tag "excerpt", Jekyll::RenderExcerptBlock
Which would theoretically let me do this:
{% excerpt %}
A snippet of this post
{% endexcerpt %}
However, this gives the error when running jekyll build:
Liquid Exception: Key excerpt cannot be set in the drop. in /Users/nick.chambers/blog/_posts/2020-01-21-unix-shell-scripting-with-bash.md
ERROR: YOUR SITE COULD NOT BE BUILT:
------------------------------------
Key excerpt cannot be set in the drop.
Is it possible to override this register's value in some way? Alternatively, is there a more Jekyll way to achieve this?
The Jekyll way to have a custom excerpt for your post (or any document in a collection), is to simply add it to the front matter:
---
title: Test Doc
excerpt: This is a custom excerpt
---
The opening paragraph of this document.
Another paragraph in this document.
Then reference it in your layout to render it:
<div class="post-excerpt">
{{ page.excerpt }}
</div>
or
<div>
{% for post in site.posts %}
<h2>{{ post.title }}</h2>
<div>{{ post.excerpt }}</div>
{% endfor %}
</div>

Jekyll tags that have spaces or multiple words

I'm using tags in Jekyll for my blog posts. The following is an example of tags I declare in the front matter in the Markdown file:
---
tags: [jekyll, tags, user experience]
---
The problem I have is that the "user experience" is rendered with a space, which breaks the link for the tag. I'd like to know if it's possible to have tags that have spaces or multiple words.
This is what my code looks like:
Markup with Ruby:
{% if page.tags.size > 0 %}
<div class="post-tags">
<ul>
{% for tag in page.tags %}
<li>{{ tag }}{% if forloop.last == false %},{% endif %}</li>
{% endfor %}
</ul>
</div>
{% endif %}
Does anyone have any ideas on how I can do that? Thanks!
You can use url_encode filter : {{ tag | url_encode }}.
Note that url_encode will turn a space into a + sign instead of a percent-encoded character. cf. Liquid doc
I had the same problem when I created the tag pages in my blog.
My solution was simply to replace the blanks by dashes:
---
tags: [jekyll, tags, user-experience]
---
Example front-matter from one of my posts:
tags:
- backup
- bitbucket-backup
- roboshell-backup
- source-control
The finished HTML looks like this:
<p><small>tags:
<span class="blog_post_categories">
backup,
bitbucket-backup,
roboshell-backup,
source-control
</span>
</small></p>
I'm sure that there is a more elegant solution which displays the blanks actually as blanks, but for me, dashes were good enough.
I discovered a fix. When you have spaces in the tags, e.g., "user experience", Jekyll isn't concatenating the words as part of the rendered link, leaving the space between both words: your.site/tags/user experience
I needed the two words joined by a + because my Tags page rendered a link to that section as #user+experience. So I added replace like this:
{{ tag | replace: " ","+" }}
This still feels like a bug in Jekyll, but this process works. Replace the space with whatever syntax you need.

Trying to display multiple records from a single database in symfony 2

In the project I'm working on, I need to display 5 of the latest news articles on the website. In the Controller, I have written the following code:
$news = $repository->createQueryBuilder('p')
->Where('p.contenttype = :type')
->setParameter('type', 'newsarticle')
->orderBy('p.lastedit', 'ASC')
->getQuery();
$latestnews = $news->getResult();
This doesn't work for some reason, as I get the error message:
Item "url" for "Array" does not exist in "ShoutMainBundle:Default:page.html.twig" at line 34
However, when I change the getResult(); to getSingleResult(); it works, but will only display the one record (which is what I expect when I use that code).
This is where I come unstuck and confused about what I'm supposed to be doing. I have googled "how to display multiple records in symfony" and I haven't found the answer. (If the answer has been out there, I apologise in advance for this). In normal PHP, I would expect to do a foreach loop (something similar anyway) to get the results which I need. But I also have a feeling that to achieve what I want I need to do something in Twig. But what I need to do I don't know.
Any help with this would be very much appreciated.
Thanks
Edit:
Here is the template code that is used to display this:
<section id="latestnews">
<h2>Latest News</h2>
<ul>
<li><a href="..{{ news.url }}" title="Read {{ news.title }}" />{{ news.title }}</a></li>
</ul>
</section>
Your code tries to read from the variable news, and assumes that this variable has fields url and title. If your controller returns an array, you have to tread news as an array and iterate over it.
<section id="latestnews">
<h2>Latest News</h2>
<ul>
{% for news in latestnews %}
<li><a href="..{{ news.url }}" title="Read {{ news.title }}" />{{ news.title }}</a></li>
{% endfor %}
</ul>
</section>
It looks like in your template, you're looking for an object that's not found. It's looking for the url in an array object, but it doesn't exist. I think you need to put in a check, to see if that exists in the array, and then echo if it does. So something like if(news.url) echo news.url;
It may not be that exact syntax obviously, I'm not all that familiar with twig, but something similar to that.
You need to loop through the "news" results array in Twig.
{% for n in news %}
<li><a href="..{{ n.url }}" title="Read {{ n.title }}" />{{ n.title }}</a></li>
{% endfor %}

An easy way to support tags in a jekyll blog

I am using the standard jekyll installation to maintain a blog, everything is going fine. Except I would really like to tag my posts.
I can tag a post using the YAML front matter, but how do I generate pages for each tag that can will list all posts for a tag?
Here is a solution with alphabetically sorted tags on a single page.
It uses Liquid only, which means that it works on GitHub Pages:
{% capture tags %}
{% for tag in site.tags %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<h3 id="{{ tag }}">{{ tag }}</h3>
<ul>
{% for post in site.tags[tag] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
You can see it in action here.
EDIT:
There's also a way to generate a separate page for each tag without plugins (which will work on GitHub Pages).
I have a more detailed explanation on my blog:
Separate pages per tag/category with Jekyll (without plugins)
First, you need a new layout file:
/_layouts/tagpage.html:
---
layout: default
---
<h1>{{ page.tag }}</h1>
<ul>
{% for post in site.tags[page.tag] %}
<li>
{{ post.date | date: "%B %d, %Y" }}: {{ post.title }}
</li>
{% endfor %}
</ul>
With this layout file, you can add a new tag page by adding a new file with just two lines of YAML front-matter.
Here's an example for the jekyll tag:
/tags/jekyll/index.html:
---
layout: tagpage
tag: jekyll
---
The only disadvantage of this approach: each time you use a new tag for the first time, you have to remember to create a new two-line file for it.
To generate the root index file (i.e. the list of tags that links to /tags/jekyll/index.html etc.), you can use a similar solution like the one on top of this answer where I generate a single page with alphebetically sorted tags:
{% capture tags %}
{% for tag in site.tags %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
{{ tag }}<br>
{% endfor %}
This will generate a list of links like this:
<ul>
<li>.net</li>
<li>authentication</li>
<li>backup</li>
</ul>
Note that this solution uses a blank to split tags, so it doesn't work when your tags contain blanks and Yevgeniy Brikman's comment applies here as well.
This gist will generate a page per category for you: https://gist.github.com/524748
It uses a Jekyll Generator plugin, plus a Page subclass.
Have a look at sites using jekyll. There are a few custom forks which have implemented tagging functionality, hopefully also in the way you want :-)
I had the same question, and stumbled upon this: http://gist.github.com/143571.
It's a rake task which generates a tag list. I modified it slightly, and my version is at:
http://github.com/mattfoster/mattfoster.github.com/blob/master/Rakefile.
Whilst this doesn't give you a page per tag, you can use anchors, which is half way there!
I use the great Jekyll Tagging plugin that automatically generates a tags cloud and tag pages. Easy to install and use.
Here is a page for the "photo" tag on my blog (in french), and you can see the tags cloud in the bottom.
Based on Christian's answer above I made a bash script that does what he described.
https://github.com/ObjectiveTruth/objectivetruth.github.io/blob/master/rebuild_tags.sh
Be sure to have the accompanying 14 line vim script in the /non_website_resources/ directory
AND
Make the /_layouts/tagpage.html shown in Christian's answer above but rename it to /_layouts/tag_pages.html
File structure should be like this:
.jekyll_website_root
├── _posts
├── _layout
│ ├── tag_pages.html
├── rebuild_tags.sh
Run from the root directory ./rebuild_tags.sh
If you get permission denied error be sure to run chmod 777 rebuild_tags.sh
If you look at scripts comments its fairly simple:
Uses sed to find all the tags in every .md file in _post directory
Uses sed to massage the data to proper format
Takes all the unique tags and makes a directory and a index.html for each
This way, if you have any new tags, just run the script to rebuild the pages before pushing to github
A nice simple non-plugin way to do tags
EDIT
Removed dependency on other files. Just need the one script!
I do these with CSS. First lists an element and use the tag name as its id.
<span id="{{ site.posts | map: 'tags' | uniq | join: '"></span><span id="' }}"></span>
And then lists all the post and use its tags as a value for the "tags" custom attribute.
{% for post in site.posts %}
<article class="post" tags="{% for tag in post.tags %}{{tag}}{% if forloop.last == false %}{{" "}}{% endif %}{% endfor %}">
<h3>{{post.title}}</h3>
</article>
{% endfor %}
And then in CSS, hide all the posts by default, and only show posts with tags matches the url id/ hash
.post {
display: none;
}
{% for tag in site.tags %}#{{tag[0]}}:target ~ [tags~={{tag[0]}}]{% if forloop.last == false %}, {% endif %}{% endfor %} {
display: block;
}
/*
The compiled version will look like this
#tagname:target ~ [tags~="tagname"], #tagname2:target ~ [tags~="tagname2"] {
display: block;
}
*/
I made an article about this here.

Resources