diff --git a/grapple/types/interfaces.py b/grapple/types/interfaces.py index 84c41c14..11acdf69 100644 --- a/grapple/types/interfaces.py +++ b/grapple/types/interfaces.py @@ -42,22 +42,40 @@ class PageInterface(graphene.Interface): parent = graphene.Field(get_page_interface) children = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) siblings = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) next_siblings = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) previous_siblings = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) descendants = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) ancestors = QuerySetList( - graphene.NonNull(get_page_interface), enable_search=True, required=True + graphene.NonNull(get_page_interface), + enable_search=True, + required=True, + enable_in_menu=True, ) search_score = graphene.Float() diff --git a/grapple/types/pages.py b/grapple/types/pages.py index 98e35456..f9a7198b 100644 --- a/grapple/types/pages.py +++ b/grapple/types/pages.py @@ -188,6 +188,7 @@ class Mixin: required=False, ), enable_search=True, + enable_in_menu=True, required=True, ) page = graphene.Field( diff --git a/grapple/types/sites.py b/grapple/types/sites.py index 51358fd8..b4ede59f 100644 --- a/grapple/types/sites.py +++ b/grapple/types/sites.py @@ -23,6 +23,7 @@ class SiteObjectType(DjangoObjectType): description=_("Filter by content type. Uses the `app.Model` notation."), ), enable_search=True, + enable_in_menu=True, required=True, ) page = graphene.Field( diff --git a/grapple/types/structures.py b/grapple/types/structures.py index 3262c4b2..9c107ddb 100644 --- a/grapple/types/structures.py +++ b/grapple/types/structures.py @@ -28,11 +28,14 @@ class QuerySetList(graphene.List): This list setts the following arguments on itself: * ``id`` + * ``in_menu`` * ``limit`` * ``offset`` * ``search_query`` * ``order`` + :param enable_in_menu: Enable in_menu filter. + :type enable_in_menu: bool :param enable_limit: Enable limit argument. :type enable_limit: bool :param enable_offset: Enable offset argument. @@ -44,6 +47,7 @@ class QuerySetList(graphene.List): """ def __init__(self, of_type, *args, **kwargs): + enable_in_menu = kwargs.pop("enable_in_menu", False) enable_limit = kwargs.pop("enable_limit", True) enable_offset = kwargs.pop("enable_offset", True) enable_search = kwargs.pop("enable_search", True) @@ -58,6 +62,16 @@ def __init__(self, of_type, *args, **kwargs): f"{of_type} is not a subclass of DjangoObjectType and it " "cannot be used with QuerySetList." ) + + # Enable in_menu for Page models. + if enable_in_menu is True and "in_menu" not in kwargs: + kwargs["in_menu"] = graphene.Argument( + graphene.Boolean, + description=_( + "Filter pages by Page.show_in_menus property. That is, the " + "'show in menus' checkbox is checked in the page editor." + ), + ) # Enable limiting on the queryset. if enable_limit is True and "limit" not in kwargs: kwargs["limit"] = graphene.Argument( @@ -141,6 +155,7 @@ def PaginatedQuerySet(of_type, type_class, **kwargs): This type setts the following arguments on itself: * ``id`` + * ``in_menu`` * ``page`` * ``per_page`` * ``search_query`` @@ -152,6 +167,7 @@ def PaginatedQuerySet(of_type, type_class, **kwargs): :type enable_order: bool """ + enable_in_menu = kwargs.pop("enable_in_menu", False) enable_search = kwargs.pop("enable_search", True) enable_order = kwargs.pop("enable_order", True) required = kwargs.get("required", False) @@ -168,6 +184,16 @@ def PaginatedQuerySet(of_type, type_class, **kwargs): "cannot be used with QuerySetList." ) + # Enable in_menu for Page models. + if enable_in_menu is True and "in_menu" not in kwargs: + kwargs["in_menu"] = graphene.Argument( + graphene.Boolean, + description=_( + "Filter pages by Page.show_in_menus property. That is, the " + "'show in menus' checkbox is checked in the page editor." + ), + ) + # Enable page for Django Paginator. if "page" not in kwargs: kwargs["page"] = graphene.Argument( diff --git a/grapple/utils.py b/grapple/utils.py index e4c9dafa..b901c11f 100644 --- a/grapple/utils.py +++ b/grapple/utils.py @@ -100,6 +100,7 @@ def resolve_queryset( id=None, order=None, collection=None, + in_menu=None, **kwargs, ): """ @@ -125,6 +126,10 @@ def resolve_queryset( qs = qs.all() if id is None else qs.filter(pk=id) + # filter by in_menu + if in_menu is not None: + qs = qs.in_menu() if in_menu else qs.not_in_menu() + order_by_relevance = True if order is not None: qs = qs.order_by(*(x.strip() for x in order.split(","))) diff --git a/tests/test_grapple.py b/tests/test_grapple.py index 1c2445f9..fe23abea 100644 --- a/tests/test_grapple.py +++ b/tests/test_grapple.py @@ -474,7 +474,7 @@ class PagesSearchTest(BaseGrappleTest): @classmethod def setUpTestData(cls): cls.home = HomePage.objects.first() - BlogPageFactory(title="Alpha", parent=cls.home) + BlogPageFactory(title="Alpha", parent=cls.home, show_in_menus=True) BlogPageFactory(title="Alpha Alpha", parent=cls.home) BlogPageFactory(title="Alpha Beta", parent=cls.home) BlogPageFactory(title="Alpha Gamma", parent=cls.home) @@ -568,6 +568,31 @@ def test_explicit_order(self): self.assertEqual(page_data[4]["title"], "Beta Gamma") self.assertEqual(page_data[5]["title"], "Alpha Gamma") + def test_search_in_menus(self): + query = """ + query($searchQuery: String, $inMenu: Boolean) { + pages(searchQuery: $searchQuery, inMenu: $inMenu) { + title + } + } + """ + executed = self.client.execute(query, variables={"inMenu": True}) + page_data = executed["data"].get("pages") + self.assertEqual(len(page_data), 1) + self.assertEqual(page_data[0]["title"], "Alpha") + + def test_search_not_in_menus(self): + query = """ + query($searchQuery: String, $inMenu: Boolean) { + pages(searchQuery: $searchQuery, inMenu: $inMenu, limit: 100) { + title + } + } + """ + executed = self.client.execute(query, variables={"inMenu": False}) + page_data = executed["data"].get("pages") + self.assertEqual(len(page_data), 12) # 11 blog pages + home page + class PageUrlPathTest(BaseGrappleTest): def _query_by_path(self, path, *, in_site=False):