diff --git a/AUTHORS.md b/AUTHORS.md index c3bac53..34b833f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -7,4 +7,5 @@ Val Kneeman under the name [django-menuware](https://github.com/un33k/django-men * Milton Lenis - miltonln04@gmail.com ## Contributors: -* Jonathan Weth - dev@jonathanweth.de \ No newline at end of file +* Jonathan Weth - dev@jonathanweth.de +* Dominik George - nik@naturalnet.de diff --git a/docs/authors.rst b/docs/authors.rst index c2c84fd..7120f21 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -9,3 +9,5 @@ Contributors ============ `Jonathan Weth `__ - dev@jonathanweth.de + +`Dominik George `__ - nik@naturalnet.de diff --git a/docs/menugeneration.rst b/docs/menugeneration.rst index 32c8ee9..69cee42 100644 --- a/docs/menugeneration.rst +++ b/docs/menugeneration.rst @@ -11,6 +11,7 @@ Django Menu Generator uses python dictionaries to represent the menu items, usua "url": URL spec, "root": True | False, "related_urls": [ list of related URLs ], + "related_views": [ list of related views ], "validators": [ list of validators ], "submenu": Dictionary like this } @@ -25,6 +26,8 @@ Where each key is as follows: - ``related_urls``: If one of this URLs is part of the path on the currently opened page, the menu item will be marked as selected (format of URLs like described at :doc:`urls`) + - ``related_views``: If the currently opened page resolves to one of these views, the menu item will be marked as selected. + - ``root``: A flag to indicate this item is the root of a path, with this you can correctly mark nested menus as selected. - ``validators``: See :doc:`validators` @@ -89,4 +92,4 @@ With this feature you can have a project structure like this:: │ ... -You can have a mix of the two approaches if you wish \ No newline at end of file +You can have a mix of the two approaches if you wish diff --git a/menu_generator/menu.py b/menu_generator/menu.py index 276d76e..114f669 100755 --- a/menu_generator/menu.py +++ b/menu_generator/menu.py @@ -5,9 +5,9 @@ from .utils import get_callable, parse_url if django.VERSION >= (1, 10): # pragma: no cover - from django.urls import reverse, NoReverseMatch + from django.urls import resolve, reverse, NoReverseMatch else: - from django.core.urlresolvers import reverse, NoReverseMatch + from django.core.urlresolvers import resolve, reverse, NoReverseMatch class MenuBase(object): @@ -83,12 +83,20 @@ def _get_related_urls(self, item_dict): related_urls = item_dict.get('related_urls', []) return [parse_url(url) for url in related_urls] + def _get_related_views(self, item_dict): + """ + Given a menu item dictionary, it returns the relateds viewss or an empty list. + """ + related_views = item_dict.get('related_views', []) + return related_views + def _is_selected(self, item_dict): """ Given a menu item dictionary, it returns true if `url` is on path, unless the item is marked as a root, in which case returns true if `url` is part of path. If related URLS are given, it also returns true if one of the related URLS is part of path. + If related views are given, it also returns true if the path maps to one of these views. """ url = self._get_url(item_dict) if self._is_root(item_dict) and url in self.path: @@ -100,6 +108,9 @@ def _is_selected(self, item_dict): for related_url in self._get_related_urls(item_dict): if related_url in self.path: return True + # Resolve URL and check if it relates to a related views + if resolve(self.path).func in self._get_related_views(item_dict): + return True return False def _is_root(self, item_dict): diff --git a/menu_generator/tests/test_menu.py b/menu_generator/tests/test_menu.py index c25a89c..6b46fd7 100755 --- a/menu_generator/tests/test_menu.py +++ b/menu_generator/tests/test_menu.py @@ -2,6 +2,7 @@ from django.http import HttpRequest from django.test import TestCase +from .urls import testview from .utils import TestUser, is_main_site, is_paid_user from ..menu import MenuBase from ..templatetags.menu_generator import get_menu @@ -306,3 +307,48 @@ def test_generate_menu_selected_related_urls_submenu(self): self.assertEqual(nav[0]["selected"], True) self.assertEqual(nav[0]["submenu"][0]["selected"], True) self.assertEqual(nav[0]["submenu"][1]["selected"], False) + + def test_generate_menu_selected_related_views_simple(self): + self.request.user = TestUser(authenticated=True) + self.request.path = "/known-view/" + self.menu.save_user_state(self.request) + list_dict = [ + { + "name": "parent1", + "url": "/user/account/", + "related_views": [testview], + } + ] + nav = self.menu.generate_menu(list_dict) + + self.assertEqual(len(nav), 1) + self.assertEqual(nav[0]["selected"], True) + + def test_generate_menu_selected_related_views_submenu(self): + self.request.user = TestUser(authenticated=True) + self.request.path = "/known-view/" + self.menu.save_user_state(self.request) + list_dict = [ + { + "name": "parent1", + "url": "/user/account/", + "submenu": [ + { + "name": "child1", + "url": '/user/account/profile/', + "related_views": [testview] + }, + { + "name": "child2", + "url": 'named_url', + "related_views": [] + }, + ], + } + ] + nav = self.menu.generate_menu(list_dict) + + self.assertEqual(len(nav), 1) + self.assertEqual(nav[0]["selected"], True) + self.assertEqual(nav[0]["submenu"][0]["selected"], True) + self.assertEqual(nav[0]["submenu"][1]["selected"], False) diff --git a/menu_generator/tests/urls.py b/menu_generator/tests/urls.py index fb9c4f1..1ddb11b 100755 --- a/menu_generator/tests/urls.py +++ b/menu_generator/tests/urls.py @@ -1,7 +1,11 @@ from django.conf.urls import url +def testview(request): + return 'foo' + urlpatterns = [ - url('', lambda: 'foo'), - url('named-url', lambda: 'foo', name='named_url'), - url('named-with-params/(?P\d+)/', lambda: 'foo', name='named_with_params') + url(r'named-url', lambda: 'foo', name='named_url'), + url(r'named-with-params/(?P\d+)/', lambda: 'foo', name='named_with_params'), + url(r'known-view', testview, name='known_view'), + url(r'', lambda: 'foo'), ]