How do I list direct children of home page? - wagtail

I have the following in my base.html before the content block.
{% for page in homepage.get_children.specific %}
<li class="nav-item">
<a class="nav-link" href="#">{{ page }}</a>
</li>
{% endfor %}
Of course, this displays nothing. How do I set things up so that every page displays the children of the home page and links to them?

This fails because homepage is not defined. request.site.root_page will give you the page object you're looking for:
{% load wagtailcore_tags %}
{% for page in request.site.root_page.get_children.specific %}
<li class="nav-item">
<a class="nav-link" href="{% pageurl page %}">{{ page.title }}</a>
</li>
{% endfor %}
Here we're using Wagtail's {% pageurl %} tag to output the link URLs.

Related

How to use parent loop counter in the nested for-loop to access to a specific row in a json like data in django templates (.html file)

at the following code the interior for loop needs to access to a specific row of entries.
In the other words the nested loop should be {% for entry in entries.(topic.id) %}
the entries is a JSON like array as the following:
entries = [
{'a', 'b', 'c'},
{'d'},
{'e', 'f', 'g', 'h', 'i'},
{'j','k'}
.
.
.
]
{% for topic in topics %}
<li>
<h5>
{{topic.id}} - {{topic}}
<ul>
<small>
{% with i=topic.id %}
{{i}}
{% for entry in entries.i %}
<li>{{forloop.counter}} . {{entry}}</li>
{% empty %}
<li>No entries available!</li>
{% endfor %}
{% endwith %}
</small>
</ul>
</h5>
</li>
{% empty %}
<li>
<h4 style="color: tomato;">There is no available topic(s)</h4>
</li>
{% endfor %}
A custom template tag fixed my issue.
Refer to:
https://docs.djangoproject.com/en/3.2/howto/custom-template-tags/
from django import template
register = template.Library()
#register.filter
def topicID(List, i):
return List[int(i)]
And by apply this filter to the nested for-loop my issue has been solved, my code is now as following:
{% for topic in topics %}
<li>
<h5>
{{forloop.counter}} - {{topic}}
<ul>
<small>
{% for entry in entries|topicID:forloop.counter0%}
<li>{{forloop.counter}} . {{entry}}</li>
{% empty %}
<li>No entries available!</li>
{% endfor %}
</small>
</ul>
</h5>
</li>
{% empty %}
<li>
<h4 style="color: tomato;">There is no available topic(s)</h4>
</li>
{% endfor %}

wagtail 'your first site': how do I link to original of gallery image?

In the getting started tutorial for wagtail there is the following code in a template:
{% for item in page.gallery_images.all %}
<div style="float: left; margin: 10px">
{% image item.image fill-320x240 %}
<p>{{ item.caption }}</p>
</div>
{% endfor %}
Please excuse me if I'm missing something obvious but I can't find out how to do this in the docs. How would I link to the original file? eg:
{% image item.image fill-320x240 %}
You'll want to use the {{ img.url }} property in your template.
{% for item in page.gallery_images.all %}
<div style="float: left; margin: 10px">
{% image item.image fill-320x240 as img %}
<p>{{ item.caption }}</p>
{% comment %}
Assign your image `as img` (doesn't need to be `img`, you can
use any alias you'd like
{% endcomment %}
<img href="{{ img.url }}" alt="{{ img.alt }}" />
This is the link to your image
</div>
{% endfor %}
By assigning your image an alias, such as as img (in the code above) you can use {{ img.property_name }} in your template.

Non-clickable menu items in Wagtail

I'd like to have some main menu items which are non-clickable but their children are in drop-down menu and clickable.
I imagine an approach may be as a special type of model which is:
parent to a few pages
inaccessible itself
Or may be more elegant way to do it I don't suspect?
As you mentioned, you are using the example code from here, this is more of a Bootstrap question but here is a potential solution.
Towards the end of the <nav> element you have your container that contains the main menu items.
<div class="container">
<div class="collapse navbar-collapse" id="navbar-collapse-3">
<ul class="nav navbar-nav navbar-left">
{% for menuitem in menuitems %}
<li class="{% if menuitem.active %}active{% endif %}">
{% if menuitem.show_dropdown %}
<a href="{{ menuitem.url }}">{{ menuitem.title }}
<span class="hidden-lg hidden-md hidden-sm visible-xs-inline">
<span class="glyphicon glyphicon-chevron-right"></span>
</span>
</a>
{% top_menu_children parent=menuitem %}
{% else %}
{{ menuitem.title }}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
To make the menu items not clickable, adjust the href attribute for the menu item. Instead of being {{ menuitem.url }} make it #. This will ensure the menu dropdown still works but there is no URL to take people to the 'parent' page.
<a href="#">{{ menuitem.title }}
<span class="hidden-lg hidden-md hidden-sm visible-xs-inline">
<span class="glyphicon glyphicon-chevron-right"></span>
</span>
</a>
See also: Boostrap 3 Navbar Docs

How to assign values to an array using HubSpot's HubL

I'm currently trying to figure out how to allow a content manager to change the order in which elements appear within recent post link lists. Essentially, the concept involves them picking the index in a sorting array that will hold that element's type as a string, which will later be iterated over with output logic determining where each piece gets placed. Unfortunately, one of the drawbacks of using HubL is the lack of debugging or error reporting so I can't see where my syntax is failing. I was wondering if anyone in InternetLand has had any similar experience with this.
Here's the HubL:
<div class="theme-recent-posts">
{% if widget.title_name %}
<div class="theme-recent-posts-title">
<{{ widget.title_tag }}><span>{{ widget.title_name }}</span></{{ widget.title_tag }}>
</div>
{% endif %}
{% set posts = blog_recent_posts(widget.select_blog, widget.max_links) %}
{% for post in posts %}
{% set item_objects = [] %}
{% if widget.show_images == "true" %}
{% set item_objects[widget.sort_images] = "image" %}
{% endif %}
{% if widget.show_titles == "true" %}
{% set item_objects[widget.sort_titles] = "title" %}
{% endif %}
{% if widget.show_dates == "true" %}
{% set item_objects[widget.sort_dates] = "date" %}
{% endif %}
{% if widget.show_authors == "true" %}
{% set item_objects[widget.sort_authors] = "author" %}
{% endif %}
<div class="theme-recent-posts-item">
<a href="{{ post.absolute_url }}">
{% for object in item_objects %}
{% if object == "image" %}
<span class="theme-recent-posts-item-image"><img src="{{ post.featured_image }}" alt="{{ post.name }}" /></span>
{% endif %}
{% if object == "title" %}
<span class="theme-recent-posts-item-title">{{ post.name }}</span>
{% endif %}
{% if object == "date" %}
<span class="theme-recent-posts-item-date">{{ post.created }}</span>
{% endif %}
{% if object == "author" %}
<span class="theme-recent-posts-item-author">{{ post.author_name }}</span>
{% endif %}
{% endfor %}
</a>
</div>
{% endfor %}
</div>
Output currently results in:
<div class="theme-recent-posts">
<div class="theme-recent-posts-title">
<h2><span>Recent Posts</span></h2>
</div>
<div class="theme-recent-posts-item">
</div>
<div class="theme-recent-posts-item">
</div>
<div class="theme-recent-posts-item">
</div>
</div>
Assume the default values wired up in the widget panel:
widget.show_images = true
widget.show_images = true
widget.show_images = true
widget.show_images = true
widget.sort_images = 0
widget.sort_titles = 1
widget.sort_dates = 2
widget.sort_authors = 3
The output should essentially be this:
<div class="theme-recent-posts">
<div class="theme-recent-posts-title">
<h2><span>Recent Posts</span></h2>
</div>
<div class="theme-recent-posts-item">
<a href="//website/path/post-name-3">
<span class="theme-recent-posts-item-image"><img src="path/to/image-3.ext" alt="Post Name 3" /></span>
<span class="theme-recent-posts-item-title">Post Name 3</span>
<span class="theme-recent-posts-item-date">1234567890</span>
<span class="theme-recent-posts-item-author">FirstName LastName</span>
</a>
</div>
<div class="theme-recent-posts-item">
<a href="//website/path/post-name-2">
<span class="theme-recent-posts-item-image"><img src="path/to/image-2.ext" alt="Post Name 2" /></span>
<span class="theme-recent-posts-item-title">Post Name 2</span>
<span class="theme-recent-posts-item-date">1234567890</span>
<span class="theme-recent-posts-item-author">FirstName LastName</span>
</a>
</div>
<div class="theme-recent-posts-item">
<a href="//website/path/post-name-1">
<span class="theme-recent-posts-item-image"><img src="path/to/image-1.ext" alt="Post Name 1" /></span>
<span class="theme-recent-posts-item-title">Post Name 1</span>
<span class="theme-recent-posts-item-date">1234567890</span>
<span class="theme-recent-posts-item-author">FirstName LastName</span>
</a>
</div>
</div>
I'm going to tag Jinja in this post, because HubL is based on Jinja, though it's not a direct translation.
If someone with enough reputation is reading this, I would greatly appreciate the addition of the HubL language tag, as it currently does not exist to select (even though HubSpot was).
Update
Twig is also a close cousin to HubL, and so I looked up how to update elements in an array there, and came up with this answer:
Setting element of array from Twig
{% set item_objects = item_objects|merge({widget.sort_images:"image"}) %}
Implementation did not work as expected. Output remains unchanged.
While this is not an answer to the question I asked, it is an alternate solution, so I provide it as a work around until a real solution is determined.
<div class="theme-recent-posts">
{% if widget.title_name %}
<div class="theme-recent-posts-title">
<{{ widget.title_tag }}><span>{{ widget.title_name }}</span></{{ widget.title_tag }}>
</div>
{% endif %}
{% set object_order = [0, 1, 2, 3] %}
{% set posts = blog_recent_posts(widget.select_blog, widget.max_links) %}
{% for post in posts %}
<div class="theme-recent-posts-item">
<a href="{{ post.absolute_url }}">
{% for order in object_order %}
{% if widget.show_images == "true" and widget.sort_images == order %}
<span class="theme-recent-posts-item-image"><img src="{{ post.featured_image }}" alt="{{ post.name }}" /></span>
{% endif %}
{% if widget.show_titles == "true" and widget.sort_titles == order %}
<span class="theme-recent-posts-item-title">{{ post.name }}</span>
{% endif %}
{% if widget.show_dates == "true" and widget.sort_dates == order %}
<span class="theme-recent-posts-item-date">{{ post.created }}</span>
{% endif %}
{% if widget.show_authors == "true" and widget.sort_authors == order %}
<span class="theme-recent-posts-item-author">{{ post.author_name }}</span>
{% endif %}
{% endfor %}
</a>
</div>
{% endfor %}
</div>
This gives me the flexibility I was looking for and the code is actually lightened quite a bit from the original proposed method. The idea is to use the object_order list as a mask for the for order in object_order loop. This gets the job done, and while I know it's hacky, it has a touch of elegance to it.

How to render a tree in Twig

I would like to render a tree with an undetermined depth (children of children of children, etc.). I need to loop through the array recursively; how can I do this in Twig?
I played around with domi27's idea and came up with this. I made a nested array as my tree, ['link']['sublinks'] is null or another array of more of the same.
Templates
The sub-template file to recurse with:
<!--includes/menu-links.html-->
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{% include "includes/menu-links.html" with {'links': link.sublinks} %}
</ul>
{% endif %}
</li>
{% endfor %}
Then in the main template, call this (kind of redundant 'with' stuff there):
<ul class="main-menu">
{% include "includes/menu-links.html" with {'links':links} only %}
</ul>
Macros
A similar effect can be achieved with macros:
<!--macros/menu-macros.html-->
{% macro menu_links(links) %}
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{{ _self.menu_links(link.sublinks) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
In the main template, do this:
{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
{{ macros.menu_links(links) }}
</ul>
Twig 2.0 - 2.11
If you want to use a macro in the same template, you should use something like this to stay compatible with Twig 2.x:
{% macro menu_links(links) %}
{% import _self as macros %}
{% for link in links %}
<li>
{{ link.name }}
{% if link.sublinks %}
<ul>
{{ macros.menu_links(link.sublinks) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
{% import _self as macros %}
<ul class="main-menu">
{{ macros.menu_links(links) }}
</ul>
This extends random-coder's answer and incorporates dr.scre's hint to the Twig documentation about macros to now use _self, but import locally.
Twig >= 2.11
As of Twig 2.11, you can omit the {% import _self as macros %}, as inlined macros are imported automatically under the _self namespace (see Twig announcement: Automatic macro import):
{# {% import _self as macros %} - Can be removed #}
<ul class="main-menu">
{{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>
If you're running PHP 5.4 or higher, there is a wonderful new solution (as of May 2016) to this problem by Alain Tiemblo: https://github.com/ninsuo/jordan-tree.
It's a "tree" tag that serves this exact purpose. Markup would look like this:
{% tree link in links %}
{% if treeloop.first %}<ul>{% endif %}
<li>
{{ link.name }}
{% subtree link.sublinks %}
</li>
{% if treeloop.last %}</ul>{% endif %}
{% endtree %}
First I thought this may be solved in a straightforward way, but it isn't that easy.
You need to create logic, maybe with a PHP class method, when to include a Twig subtemplate and when not.
<!-- tpl.html.twig -->
<ul>
{% for key, item in menu %}
{# Pseudo Twig code #}
{% if item|hassubitem %}
{% include "subitem.html.tpl" %}
{% else %}
<li>{{ item }}</li>
{% endif %}
{% endfor %}
</ul>
So you could use the special Twig loop variable, which is available inside a Twig for loop. But I'm not sure about the scope of this loop variable.
This and other information are available on Twigs "for" Docu!
Took flu's answer and modified it a little:
{# Macro #}
{% macro tree(items) %}
{% import _self as m %}
{% if items %}
<ul>
{% for i in items %}
<li>
{{ i.title }}
{{ m.tree(i.items) }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
{# Usage #}
{% import 'macros.twig' as m %}
{{ m.tree(items) }}
The answers here lead my to my solution.
I have a category entity with a self-referencing many-to-one association (parent to children).
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
private $children;
In my Twig template I am rendering the tree view like this:
<ul>
{% for category in categories %}
{% if category.parent == null %}
<li>
{{ category.name }}
{% if category.children|length > 0 %}
<ul>
{% for category in category.children %}
<li>
{{ category.name }}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>

Resources