Skip to content

Commit

Permalink
Merge pull request #108 from LabD/feature/segment-template-block
Browse files Browse the repository at this point in the history
Adds an ifsegment template block
  • Loading branch information
blurrah committed May 31, 2017
2 parents 9790a44 + de2c7f9 commit dcb8867
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
30 changes: 27 additions & 3 deletions docs/usage_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ personalised content. To do this, you can go one of two directions.

2. Create StreamField blocks only visible to your segment.

3. Create a template block only visible to your segment.


Method 1: Create a copy
^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -59,13 +61,35 @@ You'll notice a new "Variants" dropdown button has appeared. Click the button
and select the segment you'd like to create personalized content for.

Once you've selected the segment, a copy of the page will be created with a
title that includes the segment. Don't worry, you'r visitors won't be able to
title that includes the segment. Don't worry, your visitors won't be able to
see this title.

You can change everything on this page you'd like. Visitors that are included in
your segment, will automatically see the new page you've created for them.


Method 2: Create a block
^^^^^^^^^^^^^^^^^^^^^^^^
Method 2: Create a StreamField block
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


Method 3: Create a template block
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can add a template block that only shows its contents to users of a
specific segment. This is done using the "segment" block.

When editing templates make sure to load the ``wagtail_personalisation_tags``
tags library in the template::

{% load wagtail_personalisation_tags %}

After that you can add a template block with the name of the segment you want
the content to show up for::

{% segment name="My Segment" %}
<p>Only users within "My Segment" see this!</p>
{% endsegment %}

The template block currently only supports one segment at a time. If you want
to target multiple segments you will have to make multiple blocks with the
same content.
7 changes: 5 additions & 2 deletions src/wagtail_personalisation/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ def get_segment(self, segment_id):
:rtype: wagtail_personalisation.models.Segment or None
"""
return next(item for item in self.request.session['segments']
if item.id == segment_id)
try:
return next(item for item in self.request.session['segments']
if item['id'] == segment_id)
except StopIteration:
return None

def add(self, segment):
"""Add a segment to the request session.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django import template
from django.template import TemplateSyntaxError

from django.utils.safestring import mark_safe

from wagtail_personalisation.models import Segment
from wagtail_personalisation.utils import parse_tag

register = template.Library()


def do_segment(parser, token):
"""Block that only shows content if user is in chosen segment."""
# Parse the tag
tag_name, _, kwargs = parse_tag(token, parser)

# If no segment is provided this block will raise an error
if set(kwargs.keys()) != {'name'}:
usage = '{% {tag_name} name="segmentname" %} ... {% end{tag_name} %}'.format(tag_name=tag_name)
raise TemplateSyntaxError("Usage: %s" % usage)

nodelist = parser.parse(('endsegment',))
parser.delete_first_token()

return SegmentNode(nodelist, name=kwargs['name'])


register.tag('segment', do_segment)


class SegmentNode(template.Node):
"""Node that only returns contents if user is in the segment.
This node checks if the chosen segment exists and if the
user has been segmented in the chosen segment.
If not it will return nothing
"""
def __init__(self, nodelist, name):
self.nodelist = nodelist
self.name = name

def render(self, context):
# Check if segment exists
name = self.name.resolve(context)
segment = Segment.objects.filter(name=name).first()
if not segment:
return ""

# Check if user has segment
user_segment = context['request'].segment_adapter.get_segment(segment_id=segment.pk)
if not user_segment:
return ""

content = self.nodelist.render(context)
content = mark_safe(content)

return content
35 changes: 35 additions & 0 deletions src/wagtail_personalisation/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import time

from django.template.base import FilterExpression, kwarg_re
from django.utils import timezone


Expand Down Expand Up @@ -58,3 +59,37 @@ def count_active_days(enable_date, disable_date):
return delta.days

return 0


def parse_tag(token, parser):
"""Parses template tag for name, arguments and keyword arguments.
:param token: Template token containing all the tag contents
:type token: django.template.base.Token
:param parser: Template parser
:type parser: django.template.base.Parser
:return: Tuple with tag name, arguments and keyword arguments
:rtype: tuple
"""
# Split the tag content into words, respecting quoted strings.
bits = token.split_contents()

# Pull out the tag name.
tag_name = bits.pop(0)

# Parse the rest of the args, and build FilterExpressions from them so that
# we can evaluate them later.
args = []
kwargs = {}
for bit in bits:
# Is this a kwarg or an arg?
match = kwarg_re.match(bit)
kwarg_format = match and match.group(1)
if kwarg_format:
key, value = match.groups()
kwargs[key] = FilterExpression(value, parser)
else:
args.append(FilterExpression(bit, parser))

return (tag_name, args, kwargs)

0 comments on commit dcb8867

Please sign in to comment.