from django.urls import reverse from django.utils.functional import cached_property # Retain backwards compatibility for imports from wagtail.admin.utils import ( # noqa: F401 get_latest_str, get_valid_next_url_from_request, ) from wagtail.permissions import page_permission_policy def get_breadcrumbs_items_for_page( page, user, url_name="wagtailadmin_explore", root_url_name="wagtailadmin_explore_root", include_self=True, querystring_value="", ): # find the closest common ancestor of the pages that this user has direct explore permission # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb cca = page_permission_policy.explorable_root_instance(user) if not cca: return [] pages = ( page.get_ancestors(inclusive=include_self) .descendant_of(cca, inclusive=True) .specific(defer=True) ) items = [] for page in pages: if page.is_root() and root_url_name: url = reverse(root_url_name) else: url = reverse(url_name, args=(page.id,)) items.append({"url": url + querystring_value, "label": get_latest_str(page)}) return items class GenericPageBreadcrumbsMixin: """ A mixin that allows a view for pages that extends a generic view to combine the page explorer breadcrumbs with the generic view's breadcrumbs. This is done by generating the explorer breadcrumbs items for the page as a normalised breadcrumbs items list, and then concatenating that with the last item of the generic view's generated breadcrumbs items. """ _show_breadcrumbs = True breadcrumbs_items_to_take = 1 @cached_property def breadcrumbs_items(self): return get_breadcrumbs_items_for_page(self.object, self.request.user) def get_breadcrumbs_items(self): # The generic view tends to generate breadcrumbs with items such as # IndexView > EditView > CurrentView, # but we don't want that because we want the preceding items to be links # to the explore view of the page's ancestors for consistency with how # page breadcrumbs have always worked. So we only take the last N items, # which in most cases is the final item that links to the current view. # However, this can be customised in the case of generic views that are # nested inside another generic view. return self.breadcrumbs_items + [ super().get_breadcrumbs_items()[-self.breadcrumbs_items_to_take] ]