7

Compare commits

...

3 Commits

Author SHA1 Message Date
1d18a82453 Fix flake8 errors 2018-07-19 15:40:06 +02:00
63bbbd70ea Add example to the sandbox 2018-07-19 15:38:10 +02:00
5e343e78a4 Add basic personalisation blocks 2018-07-19 15:36:48 +02:00
6 changed files with 220 additions and 14 deletions

View File

@ -12,7 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('wagtailcore', '0033_remove_golive_expiry_help_text'),
('wagtailcore', '0001_initial'),
('wagtail_personalisation', '0011_personalisablepagemetadata'),
]

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-05-31 16:15
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import wagtail.wagtailcore.blocks
import wagtail.wagtailcore.fields
import wagtail.wagtailimages.blocks
import wagtail_personalisation.blocks
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0001_initial'),
('home', '0003_homepage_text_content'),
]
operations = [
migrations.CreateModel(
name='PersonalisedFieldsPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
('body', wagtail.wagtailcore.fields.StreamField((('personalised_block', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('heading', wagtail.wagtailcore.blocks.CharBlock()), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock())))), ('personalised_block_template', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('heading', wagtail.wagtailcore.blocks.CharBlock()), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock())), label='Block with template', template='blocks/personalised_block_template.html')), ('personalised_rich_text_block', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('rich_text', wagtail.wagtailcore.blocks.RichTextBlock(label='Rich Text'))))), ('personalised_image', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock(label='Image'))))), ('personalised_char', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('char', wagtail.wagtailcore.blocks.CharBlock(label='Text'))))), ('personalised_text', wagtail.wagtailcore.blocks.StructBlock((('segment', wagtail.wagtailcore.blocks.ChoiceBlock(choices=wagtail_personalisation.blocks.list_segment_choices, help_text='Only show this content block for users in this segment', label='Personalisation segment', required=False)), ('text', wagtail.wagtailcore.blocks.TextBlock(label='Mutli-line Text')))))))),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

View File

@ -5,8 +5,11 @@ from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page
from wagtail_personalisation.blocks import (
PersonalisedCharBlock, PersonalisedImageChooserBlock,
PersonalisedRichTextBlock, PersonalisedStructBlock,
PersonalisedTextBlock)
from wagtail_personalisation.models import PersonalisablePageMixin
from wagtail_personalisation.blocks import PersonalisedStructBlock
class HomePage(PersonalisablePageMixin, Page):
@ -21,3 +24,24 @@ class HomePage(PersonalisablePageMixin, Page):
RichTextFieldPanel('intro'),
StreamFieldPanel('body'),
]
class PersonalisedFieldsPage(Page):
body = StreamField([
('personalised_block', PersonalisedStructBlock([
('heading', blocks.CharBlock()),
('paragraph', blocks.RichTextBlock())
], render_fields=['heading', 'paragraph'])),
('personalised_block_template', PersonalisedStructBlock([
('heading', blocks.CharBlock()),
('paragraph', blocks.RichTextBlock())
], template='blocks/personalised_block_template.html', label=_('Block with template'))),
('personalised_rich_text_block', PersonalisedRichTextBlock()),
('personalised_image', PersonalisedImageChooserBlock()),
('personalised_char', PersonalisedCharBlock()),
('personalised_text', PersonalisedTextBlock()),
])
content_panels = Page.content_panels + [
StreamFieldPanel('body')
]

View File

@ -0,0 +1,9 @@
{% load wagtailcore_tags %}
<div class="personalisation-block-template" style="background-color: cornsilk;">
<p>This is a block with <strong>template</strong>.</p>
<h2>Heading: {{ value.heading }}</h2>
<div>
{{ value.paragraph|richtext }}
</div>
</div>

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-homepage{% endblock %}
{% block content %}
<h1>{{ page.title }}</h1>
{% for block in page.body %}
<section class="section-{{ block.block_type }}">
<p><em>Section for {{ block.block_type }}.</em></p>
{% include_block block %}
</section>
<hr>
{% endfor %}
{% endblock %}

View File

@ -1,25 +1,85 @@
from __future__ import absolute_import, unicode_literals
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from wagtail.core import blocks
from wagtail.images.blocks import ImageChooserBlock
from wagtail_personalisation.adapters import get_segment_adapter
from wagtail_personalisation.models import Segment
def list_segment_choices():
"""Get a list of segment choices visible in the admin site when editing
BasePersonalisedStructBlock and its derived classes."""
yield (-1, _('Visible to everyone'))
for pk, name in Segment.objects.values_list('pk', 'name'):
yield pk, name
class PersonalisedStructBlock(blocks.StructBlock):
"""Struct block that allows personalisation per block."""
class BasePersonalisedStructBlock(blocks.StructBlock):
"""Base class for personalised struct blocks."""
segment = blocks.ChoiceBlock(
choices=list_segment_choices,
required=False, label=_("Personalisation segment"),
help_text=_("Only show this content block for users in this segment"))
def __init__(self, *args, **kwargs):
"""Instantiate personalised struct block.
The arguments are the same as for the blocks.StructBlock constructor and
one addtional one.
Keyword Arguments:
render_fields: List with field names to be rendered or None to use
the default block rendering. Please set to None if using block
with template since then it's the template that takes care
of what fields are rendered.
"""
render_fields = kwargs.pop('render_fields',
self._meta_class.render_fields)
super(BasePersonalisedStructBlock, self).__init__(*args, **kwargs)
# Check "render_fields" are either a list or None.
if isinstance(render_fields, tuple):
render_fields = list(render_fields)
if render_fields is not None \
and not isinstance(render_fields, list):
raise ValueError('"render_fields" has to be a list or None.')
elif isinstance(render_fields, list) \
and not set(render_fields).issubset(self.child_blocks):
raise ValueError('"render_fields" has to contain name(s) of the '
'specified blocks.')
else:
setattr(self.meta, 'render_fields', render_fields)
# Template can be used only when "render_fields" is set to None.
if self.meta.render_fields is not None \
and getattr(self.meta, 'template', None):
raise ValueError('"render_fields" has to be set to None when using '
'template.')
def is_visible(self, value, request):
"""Check whether user should see the block based on their segments.
:param value: The value from the block.
:type value: dict
:returns: True if user should see the block.
:rtype: bool
"""
if int(value['segment']) == -1:
return True
if value['segment']:
for segment in get_segment_adapter(request).get_segments():
if segment.id == int(value['segment']):
return True
return False
def render(self, value, context=None):
"""Only render this content block for users in this segment.
@ -31,14 +91,80 @@ class PersonalisedStructBlock(blocks.StructBlock):
:rtype: blocks.StructBlock or empty str
"""
request = context['request']
adapter = get_segment_adapter(request)
user_segments = adapter.get_segments()
if not self.is_visible(value, context['request']):
return ""
if value['segment']:
for segment in user_segments:
if segment.id == int(value['segment']):
return super(PersonalisedStructBlock, self).render(
value, context)
if self.meta.render_fields is None:
return super(BasePersonalisedStructBlock, self).render(
value, context)
return ""
if isinstance(self.meta.render_fields, list):
render_value = ''
for field_name in self.meta.render_fields:
if hasattr(value.bound_blocks[field_name], 'render_as_block'):
block_value = value.bound_blocks[field_name].render_as_block(context=context)
else:
block_value = force_text(value[field_name])
if block_value != 'None':
render_value += block_value
return render_value
raise RuntimeError('"render_fields" is neither "None" or "list" '
'during rendering.')
class Meta:
"""
Setting render field will define which field gets rendered.
Please use a name of the field. If none, it will render the whole block.
"""
render_fields = None
class PersonalisedStructBlock(BasePersonalisedStructBlock):
"""Struct block that allows personalisation per block."""
class Meta:
label = _('Personalised Block')
render_fields = None
class PersonalisedRichTextBlock(BasePersonalisedStructBlock):
"""Rich text block that allows personalisation."""
rich_text = blocks.RichTextBlock(label=_('Rich Text'))
class Meta:
icon = blocks.RichTextBlock._meta_class.icon
label = _('Personalised Rich Text')
render_fields = ['rich_text']
class PersonalisedTextBlock(BasePersonalisedStructBlock):
"""Text block that allows personalisation."""
text = blocks.TextBlock(label=_('Mutli-line Text'))
class Meta:
icon = blocks.TextBlock._meta_class.icon
label = _('Personalised Multi-line Text')
render_fields = ['text']
class PersonalisedCharBlock(BasePersonalisedStructBlock):
"""Char block that allows personalisation."""
char = blocks.CharBlock(label=_('Text'))
class Meta:
icon = blocks.CharBlock._meta_class.icon
label = _('Personalised Single-line Text')
render_fields = ['char']
class PersonalisedImageChooserBlock(BasePersonalisedStructBlock):
"""Image chooser block that allows personalisation."""
image = ImageChooserBlock(label=_('Image'))
class Meta:
icon = ImageChooserBlock._meta_class.icon
label = _('Personalised Image')
render_fields = ['image']