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 from wagtail.admin.views.pages import get_valid_next_url_from_request 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): """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): """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("""