Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion backend/api/cms/page/queries/cms_page.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from pretix import user_has_admission_ticket
from api.cms.utils import get_site_by_host
from api.context import Context
from cms.components.page.models import GenericPage as GenericPageModel

import strawberry
Expand All @@ -8,6 +10,7 @@

@strawberry.field
def cms_page(
info: strawberry.Info[Context],
hostname: str,
slug: str,
language: str,
Expand All @@ -22,6 +25,24 @@ def cms_page(
if not page:
return None

password_restriction = (
page.get_view_restrictions().filter(restriction_type="password").first()
)
can_see_page = None

if password_restriction and password_restriction.password == "ticket":
from conferences.models import Conference

# hack so we can go live with this feature for now :)
conference = Conference.objects.get(code="pycon2025")

user = info.context.request.user
can_see_page = user.is_authenticated and user_has_admission_ticket(
email=user.email,
event_organizer=conference.pretix_organizer_id,
event_slug=conference.pretix_event_id,
)

translated_page = (
page.get_translations(inclusive=True)
.filter(locale__language_code=language, live=True)
Expand All @@ -31,4 +52,4 @@ def cms_page(
if not translated_page:
return None

return GenericPage.from_model(translated_page)
return GenericPage.from_model(translated_page, can_see_page=can_see_page)
21 changes: 19 additions & 2 deletions backend/api/cms/page/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,28 @@ class GenericPage:
body: list[get_block_union()] # type: ignore

@classmethod
def from_model(cls, obj: GenericPageModel) -> Self:
def from_model(
cls, obj: GenericPageModel, *, can_see_page: bool | None = None
) -> Self:
match can_see_page:
case None:
# They can see it
# and there are no restrictions
# so show the whole page
body = obj.body
case True:
# They can see the whole page
# so skip the first block that is used to tell users to authenticate or similar
body = obj.body[1:]
case False:
# Only show the first block
# that is used to tell users to authenticate or similar
body = [obj.body[0]]

return cls(
id=obj.id,
title=obj.seo_title or obj.title,
search_description=obj.search_description,
slug=obj.slug,
body=[get_block(block.block_type).from_block(block) for block in obj.body],
body=[get_block(block.block_type).from_block(block) for block in body],
)
135 changes: 135 additions & 0 deletions backend/api/cms/tests/page/queries/test_cms_page.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from decimal import Decimal
from conferences.tests.factories import ConferenceFactory
import pytest
from api.cms.tests.factories import GenericPageFactory, SiteFactory
from wagtail.models import PageViewRestriction

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -68,6 +70,139 @@ def test_page(graphql_client, locale):
}


def test_page_with_ticket_restriction_and_ticket_returns_content(
graphql_client, locale, user, mock_has_ticket
):
graphql_client.force_login(user)
conference = ConferenceFactory(code="pycon2025")
mock_has_ticket(conference)

parent = GenericPageFactory()
page = GenericPageFactory(
slug="bubble-tea",
locale=locale("en"),
parent=parent,
title="Bubble",
body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts",
body__1__map__longitude=Decimal(3.14),
body__2__homepage_hero__city="florence",
body__3__homepage_hero__city=None,
)
page.save_revision().publish()
PageViewRestriction.objects.create(
page=page, restriction_type="password", password="ticket"
)
SiteFactory(hostname="pycon", port=80, root_page=parent)
page.copy_for_translation(locale=locale("it"))
query = """
query Page ($hostname: String!, $language: String!, $slug: String!) {
cmsPage(hostname: $hostname, language: $language, slug: $slug){
...on GenericPage {
title
slug
body {
...on TextSection {
title
}
...on CMSMap {
latitude
longitude
}
... on HomepageHero {
city
}
}
}
}
}
"""

response = graphql_client.query(
query, variables={"hostname": "pycon", "slug": "bubble-tea", "language": "en"}
)

assert response["data"] == {
"cmsPage": {
"title": "Bubble",
"slug": "bubble-tea",
"body": [
{
"latitude": "43.766199999999997771737980656325817108154296875", # noqa: E501
"longitude": "3.140000000000000124344978758017532527446746826171875", # noqa: E501
},
{
"city": "FLORENCE",
},
{
"city": None,
},
],
}
}


def test_page_with_ticket_restriction_without_ticket_returns_first_block(
graphql_client, locale, user, mock_has_ticket
):
graphql_client.force_login(user)
conference = ConferenceFactory(code="pycon2025")
mock_has_ticket(conference, False)

parent = GenericPageFactory()
page = GenericPageFactory(
slug="bubble-tea",
locale=locale("en"),
parent=parent,
title="Bubble",
body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts",
body__1__map__longitude=Decimal(3.14),
body__2__homepage_hero__city="florence",
body__3__homepage_hero__city=None,
)
page.save_revision().publish()
PageViewRestriction.objects.create(
page=page, restriction_type="password", password="ticket"
)
SiteFactory(hostname="pycon", port=80, root_page=parent)
page.copy_for_translation(locale=locale("it"))
query = """
query Page ($hostname: String!, $language: String!, $slug: String!) {
cmsPage(hostname: $hostname, language: $language, slug: $slug){
...on GenericPage {
title
slug
body {
...on TextSection {
title
}
...on CMSMap {
latitude
longitude
}
... on HomepageHero {
city
}
}
}
}
}
"""

response = graphql_client.query(
query, variables={"hostname": "pycon", "slug": "bubble-tea", "language": "en"}
)

assert response["data"] == {
"cmsPage": {
"title": "Bubble",
"slug": "bubble-tea",
"body": [
{"title": "I've Got a Lovely Bunch of " "Coconuts"},
],
}
}


def test_page_returns_live_revision(graphql_client, locale):
parent = GenericPageFactory()
page = GenericPageFactory(
Expand Down
4 changes: 2 additions & 2 deletions backend/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ def locale():

@pytest.fixture
def mock_has_ticket(requests_mock, settings):
def wrapper(conference):
def wrapper(conference, has_ticket=True):
requests_mock.post(
f"{settings.PRETIX_API}organizers/{conference.pretix_organizer_id}/events/{conference.pretix_event_id}/tickets/attendee-has-ticket/",
json={"user_has_admission_ticket": True},
json={"user_has_admission_ticket": has_ticket},
)

return wrapper