diff --git a/HISTORY.rst b/HISTORY.rst index 76ae54f..6fda271 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,7 @@ History * Use all_languages_column to admin * Add publish button * Fix issues in migrations. Thanks @skirsdeda +* Fix selecting current menu item according to menu layout 0.6.3 (2015-12-22) ++++++++++++++++++ diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index 8d7920f..6be7aad 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -1,21 +1,34 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from cms.apphook_pool import apphook_pool from cms.menu_bases import CMSAttachMenu +from django.core.urlresolvers import resolve from django.db.models.signals import post_delete, post_save from django.utils.translation import get_language_from_request, ugettext_lazy as _ -from menus.base import NavigationNode +from menus.base import Modifier, NavigationNode from menus.menu_pool import menu_pool from .cms_appconfig import BlogConfig from .models import BlogCategory, Post -from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS +from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS, get_setting class BlogCategoryMenu(CMSAttachMenu): + """ + Main menu class + + Handles all types of blog menu + """ name = _('Blog menu') def get_nodes(self, request): + """ + Generates the nodelist + + :param request: + :return: list of nodes + """ nodes = [] language = get_language_from_request(request, check_path=True) @@ -40,9 +53,12 @@ class BlogCategoryMenu(CMSAttachMenu): node = NavigationNode( category.name, category.get_absolute_url(), - '%s-%s' % (category.__class__.__name__, category.pk), - ('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent - else None) + '{0}-{1}'.format(category.__class__.__name__, category.pk), + ( + '{0}-{1}'.format( + category.__class__.__name__, category.parent.id + ) if category.parent else None + ) ) nodes.append(node) @@ -57,10 +73,10 @@ class BlogCategoryMenu(CMSAttachMenu): if categories_menu: category = post.categories.first() if category: - parent = '%s-%s' % (category.__class__.__name__, category.pk) - post_id = '%s-%s' % (post.__class__.__name__, post.pk), + parent = '{0}-{1}'.format(category.__class__.__name__, category.pk) + post_id = '{0}-{1}'.format(post.__class__.__name__, post.pk), else: - post_id = '%s-%s' % (post.__class__.__name__, post.pk), + post_id = '{0}-{1}'.format(post.__class__.__name__, post.pk), if post_id: node = NavigationNode( post.get_title(), @@ -75,7 +91,54 @@ class BlogCategoryMenu(CMSAttachMenu): menu_pool.register_menu(BlogCategoryMenu) +class BlogNavModifier(Modifier): + """ + This navigation modifier makes sure that when + a particular blog post is viewed, + a corresponding category is selected in menu + """ + def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): + """ + Actual modifier function + :param request: request + :param nodes: complete list of nodes + :param namespace: Menu namespace + :param root_id: eventual root_id + :param post_cut: flag for modifier stage + :param breadcrumb: flag for modifier stage + :return: nodeslist + """ + app = None + config = None + if getattr(request, 'current_page', None) and request.current_page.application_urls: + app = apphook_pool.get_apphook(request.current_page.application_urls) + + if app and app.app_config: + namespace = resolve(request.path).namespace + config = app.get_config(namespace) + if config and config.menu_structure != MENU_TYPE_CATEGORIES: + return nodes + if post_cut: + return nodes + current_post = getattr(request, get_setting('CURRENT_POST_IDENTIFIER'), None) + category = None + if current_post and current_post.__class__ == Post: + category = current_post.categories.first() + if not category: + return nodes + + for node in nodes: + if '{0}-{1}'.format(category.__class__.__name__, category.pk) == node.id: + node.selected = True + return nodes + +menu_pool.register_modifier(BlogNavModifier) + + def clear_menu_cache(**kwargs): + """ + Empty menu cache when saving categories + """ menu_pool.clear(all=True) post_save.connect(clear_menu_cache, sender=BlogCategory) diff --git a/tests/test_menu.py b/tests/test_menu.py index 0f9f046..8247368 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -29,6 +29,7 @@ class MenuTest(BaseTest): self.cats.append(cat) activate('en') + menu_pool.clear(all=True) menu_pool.discover_menus() # All cms menu modifiers should be removed from menu_pool.modifiers # so that they do not interfere with our menu nodes @@ -139,22 +140,55 @@ class MenuTest(BaseTest): (PostDetailView, 'slug', posts[0], posts[0].categories.first()), (CategoryEntriesView, 'category', self.cats[2], self.cats[2]) ) + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE + self.app_config_1.save() for view_cls, kwarg, obj, cat in tests: - request = self.get_page_request(pages[1], self.user, path=obj.get_absolute_url()) with smart_override('en'): with switch_language(obj, 'en'): + request = self.get_page_request( + pages[1], self.user, path=obj.get_absolute_url() + ) + cache.clear() + menu_pool.clear(all=True) view_obj = view_cls() view_obj.request = request view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.app_config = self.app_config_1 view_obj.kwargs = {kwarg: obj.slug} view_obj.get(request) + view_obj.get_context_data() # check if selected menu node points to cat nodes = menu_pool.get_nodes(request) - found = False + found = [] for node in nodes: if node.selected: - self.assertEqual(node.url, obj.get_absolute_url()) - found = True - break - self.assertTrue(found) + found.append(node.get_absolute_url()) + self.assertTrue(obj.get_absolute_url() in found) + + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES + self.app_config_1.save() + for view_cls, kwarg, obj, cat in tests: + with smart_override('en'): + with switch_language(obj, 'en'): + request = self.get_page_request( + pages[1], self.user, path=obj.get_absolute_url() + ) + cache.clear() + menu_pool.clear(all=True) + view_obj = view_cls() + view_obj.request = request + view_obj.namespace, view_obj.config = get_app_instance(request) + view_obj.app_config = self.app_config_1 + view_obj.kwargs = {kwarg: obj.slug} + view_obj.get(request) + view_obj.get_context_data() + # check if selected menu node points to cat + nodes = menu_pool.get_nodes(request) + found = [] + for node in nodes: + if node.selected: + found.append(node.get_absolute_url()) + self.assertTrue(cat.get_absolute_url() in found) + + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE + self.app_config_1.save() diff --git a/tests/test_models.py b/tests/test_models.py index ab8977d..689e2ae 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -91,7 +91,7 @@ class AdminTest(BaseTest): for fieldname in BlogConfigForm.base_fields: self.assertContains(response, 'id="id_config-%s"' % fieldname) self.assertContains(response, '') - self.assertContains(response, '') + self.assertContains(response, 'sample_app') def test_admin_category_views(self): post_admin = admin.site._registry[BlogCategory]