Skip to content

Instantly share code, notes, and snippets.

@cnk
Last active June 6, 2024 15:59
Show Gist options
  • Save cnk/46b2755a210255af107f03da0b6b98dd to your computer and use it in GitHub Desktop.
Save cnk/46b2755a210255af107f03da0b6b98dd to your computer and use it in GitHub Desktop.
class RedirectPage(Page):
destination = models.CharField(
'Destination URL',
max_length=512,
help_text="If you want to redirect to an arbitrary URL, input it here. If redirecting off-site, the URL must "
"start with https://. If you want to redirect to a page on your site, use the Page field, instead.",
blank=True,
)
page = models.ForeignKey(
'wagtailcore.Page', # noqa
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='If you want to redirect to another Page on your site, choose it here.'
)
document = models.ForeignKey(
get_document_model(),
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='If you want to redirect to a Document on your site, choose it here.'
)
###################
# EDIT FORM CONFIG
###################
content_panels = Page.content_panels + [
MultiFieldPanel(
heading='Targets',
children=[
FieldPanel('destination'),
FieldPanel('page'),
FieldPanel('document'),
]
)
]
page_description = 'When users click this page in the menu, they are redirected to a custom URL of your choice.'
class Meta:
verbose_name = 'Redirect Page'
verbose_name_plural = 'Redirect Pages'
def clean(self):
"""
Prevent RedirectPages from being saved unless they've got exactly one of either destination, page, or document.
"""
# int(bool(thing)) converts truthy values to 1 and falsey values to 0.
target_count = int(bool(self.destination)) + int(bool(self.page)) + int(bool(self.document))
if target_count != 1:
raise ValidationError('RedirectPages must have a single target: either destination, page, or document.')
@property
def redirect_target_url(self):
# For historical reasons, we prioritize Document over Page, and both over Destination.
# This cannot have any meaningful effect on newly-saved RedirectPages, as we no longer allow users to set more
# than one of them. But existing RedirectPages with multiple values must continue to function as they always
# have, until they get saved again and are forced to set only one value.
if self.document:
return self.document.url
if self.page:
return self.page.specific.relative_url() # noqa
return self.destination
def serve(self, request, *args, **kwargs): # noqa
"""
Rather than a normal page, RedirectPages serve a redirect to the destination URL, Page, or Document.
NOTE: Most of the time, visitors will not activate this method, because the menu code serves the
redirect_target_url to them directly.
"""
response = redirect(self.redirect_target_url, permanent=True)
return self.add_cache_control_headers(response)
@property
def preview_modes(self):
"""
Disable previewing of RedirectPages, since they don't have anything to preview.
"""
return []
def get_url(self, request=None, current_site=None):
"""
We override this method to make our menu render the destination URL for this RedirectPage, instead of the
Page's own URL.
"""
return self.redirect_target_url
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment