def get_root(self): """ Get the root page for this menu """ try: return Page.objects.get(title=self.menu_name, parent=None) except Page.DoesNotExist: raise Page.DoesNotExist("Menu does not exist.\nNo top-level page found with the title '%s'." % self.menu_name)
def show_menu(context, menu_name, min_level, max_level, expand=None): menu_pages = [] needed_pages = [] try: root_page = Page.objects.get(title=menu_name, parent=None) except Page.DoesNotExist: raise Page.DoesNotExist( "Menu does not exist.\nNo top-level page found with the title '%s'." % menu_name) # Page.get_absolute_url() accesses self.parent recursively to build URLs # (assuming relative URLs). # This means that to render any menu item, we need all the ancestors up to # the root. Therefore it is more efficient to pull back this tree, without # min_level applied, and apply it just to decide which items to render. current_page = None if 'fiber_page' in context: current_page = context['fiber_page'] if current_page and current_page.is_child_of(root_page): tree = root_page.get_descendants(include_self=True).filter( level__lte=max_level) if expand == 'all': needed_pages = tree else: if current_page.level + 1 < min_level: # Nothing to do needed_pages = [] else: # We need the 'route' nodes, the 'sibling' nodes and the children route = tree.filter(lft__lt=current_page.lft, rght__gt=current_page.rght) # We show any siblings of anything in the route to the current page. # The logic here is that if the user drills down, menu items # shown previously should not disappear. # The following assumes that accessing .parent is cheap, which # it can be if current_page was loaded correctly. p = current_page sibling_qs = [] while p.parent_id is not None: sibling_qs.append( tree.filter(level=p.level, lft__gt=p.parent.lft, rght__lt=p.parent.rght)) p = p.parent route_siblings = reduce(operator.or_, sibling_qs) children = tree.filter(lft__gt=current_page.lft, rght__lt=current_page.rght) if expand != 'all_descendants': # only want immediate children: children = children.filter(level=current_page.level + 1) needed_pages = route | route_siblings | children else: # Only show menus that start at the first level (min_level == 1) # when the current page is not in the menu tree. if min_level == 1: if not expand: needed_pages = Page.objects.filter( tree_id=root_page.tree_id).filter(level__lte=1) elif expand == 'all': needed_pages = Page.objects.filter( tree_id=root_page.tree_id).filter(level__lte=max_level) else: needed_pages = [] needed_pages = Page.objects.link_parent_objects(needed_pages) # Now we need to do min_level filtering for p in needed_pages: if p.level >= min_level: menu_pages.append(p) # Remove pages that shouldn't be shown in the menu for the current user. user = context['user'] menu_pages = [ p for p in menu_pages if (p.is_public_for_user(user) and p.show_in_menu) ] """ Order menu_pages for use with tree_info template tag. """ menu_pages = sorted(menu_pages, key=lambda menu_page: menu_page.lft) """ Find parent page for this menu """ menu_parent_page = None if menu_pages: menu_parent_page = menu_pages[0].parent elif min_level == 1: menu_parent_page = root_page context['Page'] = Page context['fiber_menu_pages'] = menu_pages context['fiber_menu_parent_page'] = menu_parent_page context['fiber_menu_args'] = { 'menu_name': menu_name, 'min_level': min_level, 'max_level': max_level, 'expand': expand } return context
def show_menu(context, menu_name, min_level, max_level, expand=None): menu_pages = [] try: root_page = Page.objects.get(title=menu_name, parent=None) except Page.DoesNotExist: raise Page.DoesNotExist( "Menu does not exist.\nNo top-level page found with the title '%s'." % menu_name) current_page = None if 'fiber_page' in context and expand != 'all': current_page = context['fiber_page'] if current_page and current_page.get_root() == root_page: """ Get all siblings of the pages in the path from the root_node to the current page, plus the pages one level below the current page, but stay inside min_level and max_level """ for page in current_page.get_ancestors(): if min_level <= page.level: if page.level <= max_level: menu_pages.extend(page.get_siblings(True)) else: break if min_level <= current_page.level <= max_level: menu_pages.extend(current_page.get_siblings(True)) if min_level <= (current_page.level + 1) <= max_level: if not expand: menu_pages.extend(current_page.get_children()) elif expand == 'all_descendants': menu_pages.extend(current_page.get_descendants()) else: """ Only show menus that start at the first level (min_level == 1) when the current page is not in the menu tree. """ if min_level == 1: if not expand: menu_pages.extend( Page.objects.filter(tree_id=root_page.tree_id).filter( level=min_level)) elif expand == 'all': menu_pages.extend( Page.objects.filter(tree_id=root_page.tree_id).filter( level__range=(min_level, max_level))) """ Remove pages that the current user isn't supposed to see. """ visible_pages_for_user = Page.objects.visible_pages_for_user( context['user']).filter(show_in_menu=True) menu_pages = list(set(menu_pages) & set(visible_pages_for_user)) """ Order menu_pages for use with tree_info template tag. """ menu_pages = sorted(menu_pages, key=lambda menu_page: menu_page.lft) """ Find parent page for this menu """ menu_parent_page = None if menu_pages: menu_parent_page = menu_pages[0].parent elif min_level == 1: menu_parent_page = root_page context['Page'] = Page context['fiber_menu_pages'] = menu_pages context['fiber_menu_parent_page'] = menu_parent_page context['fiber_menu_args'] = { 'menu_name': menu_name, 'min_level': min_level, 'max_level': max_level, 'expand': expand } return context