Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to get breadcrumbs from menus #1

Merged
merged 4 commits into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% load navbuilder_tags %}
{% for crumb in navbuilder_breadcrumbs %}
<a class="Crumb" href="{{ crumb.link.get_absolute_url }}" title="{{ crumb.link.title|default:crumb.title }}" target="{{ crumb.target|default:"" }}" data-slug="{{ crumb.link.slug|default:crumb.slug }}">{{ crumb.link.title|default:crumb.title }}</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indented :)

{% if not forloop.last %} > {% endif %}
{% endfor %}
48 changes: 47 additions & 1 deletion navbuilder/templatetags/navbuilder_tags.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import template
from django.contrib.contenttypes.models import ContentType

from navbuilder.models import Menu
from navbuilder.models import Menu, MenuItem

register = template.Library()

Expand All @@ -22,3 +23,48 @@ def render_menu(context, slug):
def render_menuitem(context, obj):
context["object"] = obj
return context


@register.inclusion_tag(
"navbuilder/inclusion_tags/breadcrumbs.html", takes_context=True
)
def navbuilder_breadcrumbs(context, slug):
"""
Render the breadcrumbs, based on the current object. Prefer using the
structure of the menu designated by slug, but use any menu available.
Typical use case for this would be if the main menu has an about/terms
page, but it's mirrored in the footer menu in a much flatter layout. We
prefer the main menu structure. This also allows us to construct
breadcrumbs for items that don't show up in page menus at all.
"""
context["navbuilder_breadcrumbs"] = []
if "object" not in context:
return context

def get_menuitems(item):
if item.parent:
struct = get_menuitems(item.parent)
struct.append(item)
return struct
return [item]

content_type = ContentType.objects.get_for_model(context["object"])
crumb_sets = []
for item in MenuItem.objects.filter(
link_content_type__pk=content_type.id,
link_object_id = context["object"].id):

crumb_sets.append(get_menuitems(item))

for crumb_set in crumb_sets:
menu = crumb_set[0].menu
if menu and menu.slug == slug:
context["navbuilder_breadcrumbs"] = crumb_set

if not context["navbuilder_breadcrumbs"]:
if crumb_sets:
context["navbuilder_breadcrumbs"] = crumb_sets[0]
else:
context["navbuilder_breadcrumbs"] = []

return context
36 changes: 36 additions & 0 deletions navbuilder/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,39 @@ def load_fixtures(kls):
kls.sub_menuitem = models.MenuItem.objects.create(
**kls.sub_menuitem_data
)


def load_crumb_fixtures(kls):
kls.menu_data_2 = {
"title": "Menu 2",
"slug": "menu-2"
}
kls.menu_2 = models.Menu.objects.create(**kls.menu_data_2)

kls.link_data_2 = {
"title": "Link 2",
"slug": "link-2",
"url": "/link/2/"
}
kls.link_2 = Link.objects.create(**kls.link_data_2)

kls.menuitem_data_2 = {
"title": "Menu Item 2",
"slug": "menu-item-2",
"position": 2,
"menu": kls.menu_2,
"link": kls.link
}
kls.menuitem_2 = models.MenuItem.objects.create(**kls.menuitem_data_2)

kls.sub_menuitem_data_2 = {
"title": "Sub Menu Item 2",
"slug": "sub-menu-item-2",
"position": 2,
"parent": kls.menuitem_2,
"target": "blank",
"link": None
}
kls.sub_menuitem_2 = models.MenuItem.objects.create(
**kls.sub_menuitem_data_2
)
75 changes: 75 additions & 0 deletions navbuilder/tests/test_breadcrumbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client

from navbuilder import models
from navbuilder.tests.test_base import load_fixtures, load_crumb_fixtures

from django.template import Context, Template

crumb_template_1 = Template(
"{% load navbuilder_tags %}"
"{% navbuilder_breadcrumbs 'menu-1' %}"
)

crumb_template_2 = Template(
"{% load navbuilder_tags %}"
"{% navbuilder_breadcrumbs 'menu-2' %}"
)

crumb_template_3 = Template(
"{% load navbuilder_tags %}"
"{% navbuilder_breadcrumbs 'menu-3' %}"
)


class BreadcrumbsTestCase(TestCase):
def setUp(self):
self.client = Client()
load_fixtures(self)
load_crumb_fixtures(self)
# Reorganise the items
self.menuitem.link = None
self.sub_menuitem_2.link = self.link


def test_single_level(self):
# The link object maps to a single level in menu 2
out = crumb_template_2.render(Context({"object": self.link}))
self.assertHTMLEqual(out, """
<a class="Crumb" href="/link/1/" title="Link 1"
target="" data-slug="link-1">
Link 1
</a>
"""
)

def test_multilevel(self):
# The link object maps to the submenu in menu 1
out = crumb_template_1.render(Context({"object": self.link}))
self.assertHTMLEqual(out, """
<a class="Crumb" href="/link/1/"
title="Link 1" target="" data-slug="link-1">
Link 1
</a>
>
<a class="Crumb" data-slug="link-1" href="/link/1/"
target="blank" title="Link 1">
Link 1
</a>
"""
)

def test_menu_slug_not_found(self):
# If we cannot identify the menu it comes from, take the first one.
out = crumb_template_3.render(Context({"object": self.link}))
self.assertIn("Link 1", out)

def test_no_matching_menuitem(self):
# If the object does not show up in any menu, render nothing
self.menuitem_2.link = None
out = crumb_template_3.render(Context({"object": self.link_2}))
self.assertHTMLEqual("", out)

def tearDown(self):
pass