How do Sphinx configurations setup "breadcrumbs"? - python-sphinx

How do Sphinx configurations setup "breadcrumbs" in the header? I have a Sphinx project and all I see is the project name; if I jump down into subpages I get lost and I don't see the path to the current page.
Some examples in the wild:
https://docs.python.org/2.7/library/argparse.html
See top line: Documentation >> The Python Standard Library >> 15. Generic Operating System Services
http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html
See the rounded rectangles at the top
Source code for scipy appears to be here:
https://raw.githubusercontent.com/numpy/numpy/v1.11.1/doc/source/reference/index.rst
https://raw.githubusercontent.com/numpy/numpy/v1.11.1/doc/source/reference/arrays.rst
https://raw.githubusercontent.com/numpy/numpy/v1.11.1/doc/source/reference/arrays.ndarray.rst
It looks like the Sphinx "basic" theme has this functionality built-in as the "relbar":
From https://github.com/sphinx-doc/sphinx/blob/master/sphinx/themes/basic/layout.html:
{%- macro relbar() %}
<div class="related" role="navigation" aria-label="related navigation">
<h3>{{ _('Navigation') }}</h3>
<ul>
{%- for rellink in rellinks %}
<li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
<a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
{{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
{%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
{%- endfor %}
{%- block rootrellink %}
<li class="nav-item nav-item-0">{{ shorttitle|e }}{{ reldelim1 }}</li>
{%- endblock %}
{%- for parent in parents %}
<li class="nav-item nav-item-{{ loop.index }}"><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
{%- endfor %}
{%- block relbaritems %} {% endblock %}
</ul>
</div>
{%- endmacro %}
see also http://www.sphinx-doc.org/en/stable/templating.html
But I can't figure out how to enable.
OK, I am really confused here. I got something to appear but not the way I wanted.
I have my index.rst for my project "MyDoc":
Top Level Page Title Is Here
============================
content goes here
.. toctree::
:maxdepth: 2
introduction
(other files)
and then I have introduction.rst
Introduction
============
content goes here
.. toctree::
:maxdepth: 2
test1
and test1.rst
Test1
============
blah blah
For index.html it shows MyDoc in the relbar.
For introduction.html it shows MyDoc in the relbar. (I would have expected MyDoc >> Introduction)
For test1.html it shows MyDoc >> Introduction in the relbar. (I would have expected MyDoc >> Introduction >> Test1)
I guess what I am confused about is why it works this way.

I'm not sure what question you are asking, as it seems like you have changed it at least once. From some of your later comments, however, it sounds like you expect the current document name to show as the final item in the breadcrumb links.
It so happens I was just trying to do exactly that myself. I ended up adding this to my layout.html:
{# Show the current document in the navigation bar. #}
{% block relbaritems %}
<li class="nav-item">{{ title|striptags|e }}</li>
{% endblock %}
...which seems to work well for me. I didn't make it a link, because a link to the current document seems unnecessary, but if you want that, it shouldn't be hard to figure out how to do it from looking at the base layout.html.
I didn't figure out how to hide it for the root document, however. If anyone knows how to do that, please follow up!

Related

How can I sort a collection before rendering it in liquid on Dawn/Shopify?

1. The problem.
I want to sort sold out products at the bottom of a Shopify collection using liquid, in a customized version of the Dawn theme.
I tried filtering the product loop twice using opposing criteria, and it works to some extent. The main issue is that the pagination renders both loops per page, rather than rendering the whole collection and then paginating it.
So in a collection with 100 products, 30 sold out, and 5 pages with 24 products per page, the goal is for the 30 sold out products to take up pages 4 and 5. With my current code, it only pushes the sold out products in the first 24 spots to the bottom of that page, making the rest of the collection appear sold out when the user scrolls to the bottom.
This is what I have tried so far:
<div id="CollectionProductGrid">
{%- paginate collection.products by section.settings.products_per_page -%}
{%- if collection.products.size == 0 -%}
<div class="collection collection--empty page-width" id="main-collection-product-grid" data-id="{{ section.id }}">
<div class="loading-overlay gradient"></div>
<p class="collection-product-count light" role="status">
{%- if collection.products_count == collection.all_products_count -%}
{{ 'sections.collection_template.product_count_simple' | t: count: collection.products_count }}
{%- else -%}
{{ 'sections.collection_template.product_count' | t: product_count: collection.products_count, count: collection.all_products_count }}
{%- endif -%}
</p>
<div class="title-wrapper center">
<h2 class="title title--primary">
{{ 'sections.collection_template.empty' | t }}<br>
{{ 'sections.collection_template.use_fewer_filters_html' | t: link: collection.url, class: "underlined-link link" }}
</h2>
</div>
</div>
{%- else -%}
<div class="collection page-width">
<div class="loading-overlay gradient"></div>
<ul id="main-collection-product-grid" data-id="{{ section.id }}" class="
grid grid--1-col negative-margin product-grid
{% if collection.products_count < 4 %} grid--{{ collection.products_count }}-col-tablet{% else %}
{% if collection.products_count == 4 %} grid--4-col-desktop{% else %} grid--3-col-tablet grid--one-third-max grid--4-col-desktop grid--quarter-max{% endif %}
{% endif %}">
{% comment %}--Loops through available products first--{% endcomment %}
{%- for product in collection.products -%}
{%- if product.available -%}
<li class="grid__item">
{% render 'product-card',
product_card_product: product,
media_size: section.settings.image_ratio,
show_secondary_image: section.settings.show_secondary_image,
add_image_padding: section.settings.add_image_padding,
show_vendor: section.settings.show_vendor,
show_image_outline: section.settings.show_image_outline,
show_rating: section.settings.show_rating,
show_atc: section.settings.show_atc
%}
</li>
{%- endif -%}{%- endfor -%}
{%- for product in collection.products -%}
{%- unless product.available -%}
<li class="grid__item">
{% render 'product-card',
product_card_product: product,
media_size: section.settings.image_ratio,
show_secondary_image: section.settings.show_secondary_image,
add_image_padding: section.settings.add_image_padding,
show_vendor: section.settings.show_vendor,
show_image_outline: section.settings.show_image_outline,
show_rating: section.settings.show_rating,
show_atc: section.settings.show_atc
%}
</li>
{%- endunless -%}{%- endfor -%}
</ul>
{%- if paginate.pages > 1 -%}
{% render 'pagination', paginate: paginate, anchor: '' %}
{%- endif -%}
</div>
{%- endif -%}
{%- endpaginate -%}
</div>
The issue is the pagination, and it seems to be a difficult one to manipulate for an inexperienced coder like myself, so there are still other options I believe might work.
Either 1: Sorting
Creating a custom sorting order, if that is even possible. Since the sorting works with the pagination, this might be one of the easier ways to achieve this I believe, but I don't know how.
2: Using the filters and re-rendering the collection with javascript
This is far from ideal in terms of performance and UX, but might work as a hack since the filters seem to render the collection very quickly in real-time after filtering.
3: Filtering the loop before rendering it, and before pagination.
This is what I believe will be the best solution.
I have looked into capturing the loop in an array, but I am struggling to putting it all together correctly. This post explains how you can do this: http://www.codeshopify.com/blog_posts/building-arrays-with-liquid-in-shopify
And here is some example code I believe might be helpful:
{% capture products_list %}
{% for product in collection.products%}
{{product.title}}|{{product.url}}|{{product.description}}|{{product.featured_image.src | product_img_url: 'medium' }}
{% if forloop.last == false %}::{% endif%}
{% endfor %}
{% endcapture %}
{% assign products_array = products_list | split: '::'%}
What I need help with for the last option, is to figure out what to capture, how to concatinate it properly, and then how to loop through the new and filtered array. Or, a better solution if there is one.

How can i use string interpolation with Jekyll?

I have a Jekyll 3 project that allows language selection. We use the 'jekyll-multiple-languages-plugin' gem for internationalization.
We have a glossary that is supposed to display German terms or English terms according to the selected language. I get the selected language using the variable site.lang provided by the 'jekyll-multiple-languages-plugin' gem.
Right now glossary.html looks like this
<div id="glossary">
{% if site.lang == "de" %}
{% for term in site.data['terms_de'] %}
<!-- German glossary goes here -->
{% endfor %}
{% elsif site.lang == "en" %}
{% for term in site.data['terms_en'] %}
<!-- English glossary goes here -->
{% endfor %}
{% endif %}
</div>
However, i'd love to have something like this
<div id="glossary">
{% for term in site.data["terms_#{site.lang}"] %}
<!-- Glossary goes here -->
{% endfor %}
</div>
But for some reason, the string interpolation "terms_#{site.lang}" doesn't work. I also tried 'terms_'+site.lang
I think the interpolation is not working because, when i put {{ site.lang }} in the page, i see the selected language, but when i write {{ "terms_"+site.lang }} i don't see anything.
Thanks in advance.
You can make use of the capture tag, instead of displaying a value it sets to a variable:
{% capture term_lang %}{{ 'terms_' | append: site.lang }}{% endcapture%}
Then you can use that variable as the index of the array:
site.data[term_lang]
In your example:
{% capture term_lang %}{{ 'terms_' | append: site.lang }}{% endcapture%}
<div id="glossary">
{% for term in site.data[term_lang] %}
<!-- Glossary goes here -->
{% endfor %}
</div>

Selectively rendering Liquid templates?

Is there a possibility to selectively render specific tags in a liquid template and leave the rest for a second render?
I have pages containing snippets(includes) and variables. The snippets are stored in the database and expensive to render. The variables are available only at runtime (via the URL request in the scenario of a landing page). I want to cache the page content with the snippets rendered but with all the rest of the liquid markup untouched.
So, If I have
{% snippet header %}
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
{% snippet footer %}
I would want the cached page content to be:
The header content
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
The footer content
At runtime this would be picked up from the memcached store and rendered:
The header content
Welcome, John
The footer content
Any idea on how to achieve this?
Update: Here's what I have in place already:
(It works, but I am looking for a cleaner, ideally liquid-only-based solution.)
A "vars" tag which produces a variable with the given name:
{% vars first_name %} #=> {{ vars.first_name }}
And, I use modified liquid markup for everything I don't want rendered the first time:
{* if vars.first_name *}
So, currently the initial page looks like this:
{% snippet header %}
{* if vars.first_name *}
Welcome, {% vars first_name %}
{* endif *}
{% snippet footer %}
Which gets rendered once and cached as:
The header content
{* if vars.first_name *}
Welcome, {{ vars.first_name }}
{* endif *}
The footer content
Then at runtime I retrieve the cached version and replace {* with {% etc. to get
The header content
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
The footer content
Which I render with liquid again to get to the desired outcome.
This does the job but is not pure liquid and I was wondering if there is a cleaner solution.
Is there?
{% snippet header %}
{% raw %}{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}{% endraw %}
{% snippet footer %}
This should get you the rendering that you want to cache, and then if you re-render it through Liquid I would think it would process the runtime variable.

Multiple Blogs In Single Jekyll Website

Is there a way I can have a single Jekyll website have more than one blog? I currently want to have two blogs in one site.
I am the author of the page http://www.garron.me/blog/multi-blog-site-jekyll.html
Considering that you need individual archives pages, and latest post per individual blog. Just use something like this:
Create a file archives-blog-1.html and fill it with:
{% for post in site.posts %}
{% if post.categories contains 'blog1' %}
<div class="post">
<h3 class="title">{{ post.title }}</h3>
<p class="meta">Date: {{ post.date }}</p>
</div>
{% endif %}
{% endfor %}
That will give you a list of all post in blog1, you can do the same for blog2. That page can be anyplace you want.
For the latest post, you can use the same code but enclosed between:
{% for post in site.posts limit:5 %}
....
{% endfor %}
That will give you the lastes 5 posts... I am using this
{% for post in site.posts limit:5 %}
<div class="post">
<ul>
<li>{{ post.title | truncate:200 }} <small>{{ post.date }}</small>
{% if post.summary %}
<p class="entry">{{ post.summary }}</p>
{% endif %}
</li>
</ul>
</div>
{% endfor %}
In my index page. http://www.garron.me/index.html ... under the sub-title (From the blogs)
I am not limiting to any category, so posts from all blogs appear there, you can limit with {% if post.categories contains 'blog1' %}
Hope it helps you.
There's a simpler solution than any of the answers so far.
Folder structure:
- blog1/
- _posts/
- blog2/
- _posts/
Then in the index.html for blog1, use site.categories.blog1 instead of site.posts.
See the documentation for "site.categories" and "page.categories" in https://jekyllrb.com/docs/variables/
I used two separate Jekyll installations to run two blogs on the same domain; if your blogs are going to live in separate root dirs (mine are at / and /photos/), then I'd recommend this approach. I also described how I merged both blogs' sitemap.xml files.
Your best bet would be to look into the data files feature. You can put .markdown files in a separate folder in your source and link to them as you post. This does mean that in order to make a post, you'll need to write a data file entry, but you can host as many "blogs" as you'd like, each with their own folder. Posts will automatically have the folder they're in as the url. I use this method for my own personal blog and portfolio.
Either that, or you may want to look into collections: http://jekyllrb.com/docs/collections/
Adding to #ggarron's answer, there's a short version of filtering by categories:
{% assign filtered_posts = site.posts | where_exp: "post", "post.categories contains 'blog1'" %}
The advantage of assigning to a variable is that one can replace all uses of site.posts with filtered_posts and keep consistency. For example, I have a snippet to get the first post of the list:
{% assign first_post = filtered_posts | first %}
This works as expected.

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