import logging from django.conf.urls import include, url from django.db import transaction from django.db.models import F from django.http import Http404 from django.shortcuts import redirect, render from django.template.defaultfilters import pluralize from django.urls import reverse from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from wagtail.admin import messages from wagtail.admin.site_summary import PagesSummaryItem, SummaryItem try: from wagtail.admin.views.pages.utils import get_valid_next_url_from_request except ModuleNotFoundError: from wagtail.admin.views.pages import get_valid_next_url_from_request # noqa from wagtail.admin.widgets import Button, ButtonWithDropdownFromHook from wagtail.core import hooks from wagtail.core.models import Page from wagtail_personalisation import admin_urls, models, utils from wagtail_personalisation.adapters import get_segment_adapter from wagtail_personalisation.models import PersonalisablePageMetadata logger = logging.getLogger(__name__) @hooks.register("register_admin_urls") def register_admin_urls(): """Adds the administration urls for the personalisation apps.""" return [ url( r"^personalisation/", include(admin_urls, namespace="wagtail_personalisation"), ) ] @hooks.register("before_serve_page") def set_visit_count(page, request, serve_args, serve_kwargs): """Tests the provided rules to see if the request still belongs to a segment. :param page: The page being served :type page: wagtail.core.models.Page :param request: The http request :type request: django.http.HttpRequest """ adapter = get_segment_adapter(request) adapter.add_page_visit(page) @hooks.register("before_serve_page") def segment_user(page, request, serve_args, serve_kwargs): """Apply a segment to a visitor before serving the page. :param page: The page being served :type page: wagtail.core.models.Page :param request: The http request :type request: django.http.HttpRequest """ adapter = get_segment_adapter(request) adapter.refresh() forced_segment = request.GET.get("segment", None) if request.user.is_superuser and forced_segment is not None: segment = models.Segment.objects.filter(pk=forced_segment).first() if segment: adapter.set_segments([segment]) class UserbarSegmentedLinkItem: def __init__(self, segment): self.segment = segment def render(self, request): return f"""
""" @hooks.register("construct_wagtail_userbar") def add_segment_link_items(request, items): for item in models.Segment.objects.enabled(): items.append(UserbarSegmentedLinkItem(item)) return items @hooks.register("before_serve_page") def serve_variant(page, request, serve_args, serve_kwargs): """Apply a segment to a visitor before serving the page. :param page: The page being served :type page: wagtail.core.models.Page :param request: The http request :type request: django.http.HttpRequest :returns: A variant if one is available for the visitor's segment, otherwise the original page :rtype: wagtail.core.models.Page """ user_segments = [] if not isinstance(page, models.PersonalisablePageMixin): return adapter = get_segment_adapter(request) user_segments = adapter.get_segments() metadata = page.personalisation_metadata # If page is not canonical, don't serve it. if not metadata.is_canonical: raise Http404 if user_segments: # TODO: This is never more then one page? (fix query count) metadata = metadata.metadata_for_segments(user_segments) if metadata: variant = metadata.first().variant.specific return variant.serve(request, *serve_args, **serve_kwargs) @hooks.register("construct_explorer_page_queryset") def dont_show_variant(parent_page, pages, request): return utils.exclude_variants(pages) @hooks.register("register_page_listing_buttons") def page_listing_variant_buttons(page, page_perms, is_parent=False, *args): """Adds page listing buttons to personalisable pages. Shows variants for the page (if any) and a 'Create a new variant' button. """ if not isinstance(page, models.PersonalisablePageMixin): return metadata = page.personalisation_metadata if metadata.is_canonical: yield ButtonWithDropdownFromHook( _("Variants"), hook_name="register_page_listing_variant_buttons", page=page, page_perms=page_perms, is_parent=is_parent, attrs={"target": "_blank", "title": _("Create or edit a variant")}, priority=100, ) @hooks.register("register_page_listing_variant_buttons") def page_listing_more_buttons(page, page_perms, is_parent=False, *args): """Adds a 'more' button to personalisable pages allowing users to quickly create a new variant for the selected segment. """ if not isinstance(page, models.PersonalisablePageMixin): return metadata = page.personalisation_metadata for vm in metadata.variants_metadata: yield Button( "%s variant" % (vm.segment.name), reverse("wagtailadmin_pages:edit", args=[vm.variant_id]), attrs={"title": _("Edit this variant")}, classes=("icon", "icon-fa-pencil"), priority=0, ) for segment in metadata.get_unused_segments(): yield Button( "%s variant" % (segment.name), reverse("segment:copy_page", args=[page.pk, segment.pk]), attrs={"title": _("Create this variant")}, classes=("icon", "icon-fa-plus"), priority=100, ) yield Button( _("Create a new segment"), reverse("wagtail_personalisation_segment_modeladmin_create"), attrs={"title": _("Create a new segment")}, classes=("icon", "icon-fa-snowflake-o"), priority=200, ) class CorrectedPagesSummaryItem(PagesSummaryItem): def get_context(self): # Perform the same check as Wagtail to get the correct count. # Only correct the count when a root page is available to the user. # The `PagesSummaryItem` will return a page count of 0 otherwise. # https://github.com/wagtail/wagtail/blob/5c9ff23e229acabad406c42c4e13cbaea32e6c15/wagtail/admin/site_summary.py#L38 context = super().get_context() root_page = context.get("root_page", None) if root_page: pages = utils.exclude_variants( Page.objects.descendant_of(root_page, inclusive=True) ) page_count = pages.count() if root_page.is_root(): page_count -= 1 context["total_pages"] = page_count return context @hooks.register("construct_homepage_summary_items") def add_corrected_pages_summary_panel(request, items): """Replaces the Pages summary panel to hide variants.""" for index, item in enumerate(items): if item.__class__ is PagesSummaryItem: items[index] = CorrectedPagesSummaryItem(request) class SegmentSummaryPanel(SummaryItem): """The segment summary panel showing the total amount of segments on the site and allowing quick access to the Segment dashboard. """ order = 2000 def render(self): segment_count = models.Segment.objects.count() target_url = reverse("wagtail_personalisation_segment_modeladmin_index") title = _("Segments") return mark_safe( """