Django CMS menu, how to set a class to parents only? - css

My menu needs some work. I need to have a class on the parent menu items, but with my recursion, it is not working.
This is (part of) my menu:
Home
Teachers (id teachers)
Contact
Info
Projects
myproject
yourproject
I start with "teachers" like this:
{% show_menu_below_id "teqchers" 0 1 0 1 "teachers_menu.html" %}
And this is my teachers_menu.html:
{% load menu_tags %}
{% for child in children %}
<li class="{% if child.selected %}selected parent_{{forloop.counter}}{% endif %} {% if child.sibling %}parent_{{forloop.counter}} {% endif %}">
{{ child.get_menu_title }}
{% if child.children %}
<div class="submenu">
<ul>
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
</ul>
</div>
{% endif %}
</li>
{% endfor %}
With this in place, my menu is working for a bit.
When i click Projects, all is well and the 2 projects are in view. But when i click a project, i expect the page to show, but it does not, it does rebuild my menu, and adds the needed class to the child elements:
{% if child.selected %}selected parent_{{forloop.counter}}{% endif %}
Obvious because it is a child now i guess, but how to prevent this? I only need that class for the first menu items.

Instead of doing this:
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
I now added another template like this:
{% show_menu from_level to_level extra_inactive extra_active "teachers_submenu.html" "" "" child %}
And in that template I now have:
{% load menu_tags %}
{% for child in children %}
<li class="{% if child.selected %}selected{% endif %}">
{{ child.get_menu_title }}
</li>
{% endfor %}
So the extra template takes care of the submenu. With some styling it now works.

Related

How do I get Child of a Child Page Timber/Twig WordPress

How do I get the Child of a Child Page to display on a .twig template?
Parent Page
— First Sibling
— — Second Sibling
— — — Third Sibling
I am getting the children of the First Sibling with
{% for child in post.children %}
<li class="pb-3">{{ child.title }}</li>
{% endfor %}
How do I go one further deeper? Timber/Twig WordPress.
You could do something like this a Child post is a post and because of that it has children if given
{% for child in post.children %}
<li class="pb-3">{{ child.title }}</li>
{% if child.children|length > 0 %}
{% for child in child.children %}
<li class="pb-3">{{ child.title }}</li>
{% endfor %}
{% endif %}
{% endfor %}
If you are using Timber/Twig, and you're using ACF, you can choose a Post Object of Page, select the title of the page that you are wanting to add as the Post Object, and to grab the child pages you can do something like this:
{% for child in Post(list.page).children %}
<li class="pb-3">{{ child.title }}</li>
{% endfor %}

How to change "if" condition related to a page

I would like to include tab_system.twig file in two differents files : archive.twig and blog_list.twig to have the same html in those two files.
{% include "tab_system.twig" %}
In this tab_system.twig file I have a condition to add the active class :
{% if XXX %}active{% endif %}
But this condition must be different depending on which page the user is.
For blog_list.twig :
{% if loop.first %}active{% endif %}
For archive.twig :
{% if item.term_id == term_page.term_id %}active{% endif %}
I wrote this without success :
{% set addclass = '' %}
{% if is_blog_list %}
{% set addclass = '{% if loop.first %}active{% endif %}' %}
{% elseif is_archive %}
{% set addclass = '{% if item.term_id == term_page.term_id %}active{% endif %}' %}
{% endif %}
In tab_system.twig I have a tab system with menu from a part and content to the other part. I wrote a js loop to display the corresponding content. I need to add active class on the first link and first content tab in blog_list.twig file and to add active class to the link and content tab depending on which category the user is.
<div class="tab-menu-list">
{% for item in all_posts %}
<a href="#" class="tab-menu {{ addclass }}"</a>
{% endfor %}
</div>
<div class="tab-content-list">
{% for item in all_posts %}
<div href="#" class="tab-content {{ addclass }}"</div>
{% endfor %}
</div>
is_archive and is_blog_list are variables defined elsewhere. They work
How can I create a condition ? Thank you in advance.
As I'm assuming you are looping the records, I'd say don't overcomplicate things,
single.twig
{% for i in 1..5 %}
{% include 'article.twig' with { 'active' : loop.first, } %}
{% endfor %}
template_blog.twig
{% for item in items %}
{% include 'article.twig' with { 'active' : item.term_id == term_page.term_id, } %}
{% endfor %}
article.twig
<div class="articles__list tab-content{{ active ? ' active'}}">
foo
</div>
demo - just change the main template
If you really wanted to keep that for inside the article template, which I think is not a great idea, you could still use the variables is_archive and is_blog_list inside article, e.g.
article.twig
{% for item in items %}
{% if is_archive %}
{% set active = loop.first %}
{% elseif is_blog_list %}
{% set active = item.term_id == term_page.term_id %}
{% endif %}
<div class="articles__list tab-content{{ active ? ' active'}}">
{% endfor %}

Multiple post pagination on same page with Timber

I recently implemented a blog that takes content from two post types and displays them in a tabbed navigation. The problem I encounter is that I can't seem to be able to create pagination links for each post type without one overriding the other.
<div id="view1">
{% block content %} {% for post in posts %} {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %} {% endfor %} {% endblock %}
<div class="tool-pagination">
<ul class="pages">
<li>
{% if pagination.prev %}
Prev
{% endif %}
</li>
{% for page in pagination.pages %}
<li>
{% if page.link %}
{{page.title}}
{% else %}
<span class="{{page.class}}">{{page.title}}</span>
{% endif %}
</li>
{% endfor %}
<li>Next
</li>
</ul>
{% if pagination.next %} {% endif %}
</div>
</div>
<!-- Workbench Tab -->
<div id="view2">
{% block workbench %} {% for post in workbench %} {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %} {% endfor %} {% endblock %}
<div class="tool-pagination">
<ul class="pages">
<li>
{% if pagination.prev %}
Prev
{% endif %}
</li>
{% for page in pagination.pages %}
<li>
{% if page.link %}
{{page.title}}
{% else %}
<span class="{{page.class}}">{{page.title}}</span>
{% endif %}
</li>
{% endfor %}
<li>Next
</li>
</ul>
{% if pagination.next %} {% endif %}
</div>
</div>
My index.php file looks like this:
$context['pagination'] = Timber::get_pagination();
I tried following the instructions on the timber site pagination but all I've managed to do is choose which category is paginated not both
Thanks in advance!!
Ivan
Multiple pagination wasn't something I really anticipated for. But here's how I think it could work:
$context['posts'] = Timber::get_posts();
$context['posts_pagination'] = Timber::get_pagination();
$query = array('post_type' => 'workbench');
$context['workbench'] = Timber::get_posts($query);
query_posts($query); //this forces WP to rerun query stuff
$context['workbench_pagination'] = Timber::get_pagination();
This is not tested, but based on what you got, this is the closest stab I can take

Recursive parsing of hierarchical Twig layout with parents, children, grandchildren

In my SF2 project I have an entity (Category) which I am representing in a hierarchical format with a parent at the top, followed by children, grandchildren etc.
The Category entity has a getChildren method, which works and returns Category entity objects.
I'm trying to work out a way to make this layout more dynamic, rather than having to explicitly set children and grandchildren variables within the template.
Is there a better way to do this?
<ul class="sortable">
{% for cat in cats %}
{% set children = cat.getChildren %}
<li id="menuItem_{{ cat.id }}">
<div data-id="{{ cat.id }}">
<span>{{ cat.name }}</span>
</div>
{% for child in children %}
{% set grandchildren = child.getChildren %}
<ul>
<li id="menuItem_{{ child.id }}">
<div data-id="{{ child.id }}">
{{ child.name }}
</div>
{% for grandchild in grandchildren %}
<ul>
<li id="menuItem_{{ grandchild.id }}">
<div data-id="{{ grandchild.id }}">
{{ grandchild.name }}
</div>
</li>
</ul>
{% endfor %}
</li>
</ul>
{% endfor %}
</li>
{% endfor %}
</ul>
so what you are trying to achieve is recursive parsing of a tree in twig right?
If so, have a look at macros
.
{% import _self as macros %}
{% macro showChild(object) %}
{% import _self as macros %}
<ul>
{% for child in object.children %}
{{ macros.showChild(child) }}
{% endfor %}
<li id="menuItem_{{ object.id }}">
<div data-id="{{ object.id }}">
{{ object.name }}
</div>
</li>
</ul>
{% endmacro %}
<ul class="sortable">
{% for cat in cats %}
{{ macros.showChild(cat) }}
{% endfor %}
</ul>
that's all :)
let me know if you need help
EDIT 1:
If you want to use the macro in another file, remove the "import _self" line and just import it with an alias in another file:
index.html.twig:
{% import 'macro_file_name.html.twig' as macros %}
then you can use the same notation to call it

Django CMS Multi-Level Dropdown Menu

Im kinda new to Django CMS and im trying my best to avoid asking, but this one drives me crazy.
I made a Wiki app with a Topic, and Category model. I hooked it to a Site on my CMS and added it to my Menu. Now i would like to be able to show all Top-Level categories, their Child Categories & Topics, and the Child categories of these, and so on, on my menu.
Menu/Navigation should look like this:
Wiki
Category1
Category1.1
Topic
Category1.2
Topic
Category2
Topic
Category3
...
Right now i can only show the Top categories:
Wiki
Category1
Category2
Category3
I Already created a menu.py to get a Custom SubMenu on my Wiki (the one you see above):
menu.py
class WikiSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.filter(parent_id__isnull=True)
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.id,
)
nodes.append(node)
return nodes
menu_pool.register_menu(WikiSubMenu)
My Category Model:
class Category(models.Model):
''' Category model. '''
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
parent = models.ForeignKey(
'self',
null=True,
blank=True,
related_name='children'
)
sort = models.IntegerField(default=0)
class Meta:
ordering = ['sort', 'name']
def __unicode__(self):
return self.name
#models.permalink
def get_absolute_url(self):
return ('topics:categories_category_detail', (), {'slug': self.slug})
def get_all_children(self):
return Category.objects.filter(parent=self)
Now, is it possible to create a Sub-SubMenu, for all Categories with Childs, and their Childs, and their Childs, and so on?
Thanks for help & sorry for bad english
-- EDIT : --
I just found that:
docs.django-cms.org/en/3.0.6/extending_cms/app_integration.html#integration-modifiers
(Removed direct link to add 2 new Links, sorry for that)
I think that is what im looking for, i was kinda blind that i didn't found it. I'll try it out and Post the Answer if it worked out.
-- EDIT (AGAIN): --
The modifier didn't worked for me, but i got a whole piece further,
i read the Docs again, and found that i can give the NavigationNodes an optional attr dictonary, which i filled with all Categories with parent=c, on that way i had the data i needed, then i found that real nice bootstrap dropdown menu, that does exacly what i wanted. So my code until now looks like that:
menu.py
class TopicsSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.filter(parent_id__isnull=True)
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.pk,
attr=dict(
subcategories=Category.objects.filter(parent=c),),
)
nodes.append(node)
return nodes
And my Template:
menu.html
{% for child in children %}
<li>
{% if child.children %}
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{ child.get_menu_title }}
<span class="caret">
</span>
</a>
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
{% for child in child.children %}
{% if child.attr.subcategories.count %}
<li class="dropdown-submenu">
<a tabindex="-1" href="#">{{ child.get_menu_title }}</a>
<ul class="dropdown-menu">
{% for subcategory in child.attr.subcategories %}
<li>
<a tabindex="-1" href="{{ subcategory.get_absolute_url }}">{{ subcategory }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ child.get_menu_title }}</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<a href="{{ child.get_absolute_url }}">
<span>
{{ child.get_menu_title }}
</span>
</a>
{% endif %}
</li>
{% if class and forloop.last and not forloop.parentloop %}
{% endif %}
{% endfor %}
My next step will be to write the whole "for" loops from the template in a Method, make it recursive with a while loop or something and post the result as Answer.
I hope i can help someone with that stuff :)
WOHO! I finally did it!
base.html
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
{% show_menu 0 100 100 100 "menu.html" %}
</ul>
</div>
menu.html:
{% for child in children %}
<li class="child{% if child.selected %} selected{% endif %}{% if child.ancestor %} ancestor{% endif %}{% if child.sibling %} sibling{% endif %}{% if child.descendant %} descendant{% endif %}{% if child.children %} dropdown{% endif %}">
<a {% if child.children %}class="dropdown-toggle" data-toggle="dropdown"{% endif %} href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">
<span>{{ child.get_menu_title }}</span>{% if child.children|length %}<span class="caret"></span>{% endif %}
</a>
{% if child.children %}
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
{% show_menu from_level to_level extra_inactive extra_active "dropdownmenu.html" "" "" child %}
</ul>
{% endif %}
</li>
{% if class and forloop.last and not forloop.parentloop %}{% endif %}
{% endfor %}
and my dropdownmenu.html:
(The recursive stuff starts here)
{% load i18n menu_tags cache mptt_tags %}
{% for child in children %}
<li {% if child.children %}class="dropdown-submenu"{% else %} {% endif %}>
<a tabindex="-1" href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a>
{% if child.children %}
<ul class="dropdown-menu">
{% show_menu from_level to_level extra_inactive extra_active "dropdownmenu.html" "" "" child %}
</ul>
{% endif %}
</li>
{% endfor %}
and the most important, menu.py:
class TopicsSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.all()
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.pk
)
if c.parent:
node.parent_id = c.parent_id
nodes.append(node)
topics = Topic.objects.all().exclude(category__isnull=True)
for t in topics:
node = NavigationNode(
mark_safe(t.title),
t.get_absolute_url(),
t.pk,
t.category.all()[0].id,
parent_namespace="TopicsSubMenu"
)
nodes.append(node)
return nodes
menu_pool.register_menu(TopicsSubMenu)
And thats it!

Resources