11from __future__ import absolute_import , unicode_literals
22
3+ from django .utils .encoding import force_text
34from django .utils .translation import ugettext_lazy as _
5+
46from wagtail .wagtailcore import blocks
7+ from wagtail .wagtailimages .blocks import ImageChooserBlock
58
69from wagtail_personalisation .adapters import get_segment_adapter
710from wagtail_personalisation .models import Segment
811
912
1013def list_segment_choices ():
14+ """Get a list of segment choices visible in the admin site when editing
15+ BasePersonalisedStructBlock and its derived classes."""
16+ yield (- 1 , _ ('Visible to everyone' ))
17+
1118 for segment in Segment .objects .all ():
1219 yield (segment .pk , segment .name )
1320
1421
15- class PersonalisedStructBlock (blocks .StructBlock ):
16- """Struct block that allows personalisation per block."""
17-
22+ class BasePersonalisedStructBlock (blocks .StructBlock ):
23+ """Base class for personalised struct blocks."""
1824 segment = blocks .ChoiceBlock (
1925 choices = list_segment_choices ,
2026 required = False , label = _ ("Personalisation segment" ),
2127 help_text = _ ("Only show this content block for users in this segment" ))
2228
29+ def __init__ (self , * args , ** kwargs ):
30+ """Instantiate personalised struct block.
31+
32+ The arguments are the same as for the blocks.StructBlock constructor and
33+ one addtional one.
34+
35+ Keyword Arguments:
36+ render_fields: List with field names to be rendered or None to use
37+ the default block rendering.
38+ """
39+ render_fields = kwargs .pop ('render_fields' ,
40+ self ._meta_class .render_fields )
41+ super (BasePersonalisedStructBlock , self ).__init__ (* args , ** kwargs )
42+
43+ if isinstance (render_fields , tuple ):
44+ render_fields = list (render_fields )
45+
46+ if render_fields is not None \
47+ and not isinstance (render_fields , list ):
48+ raise ValueError ('"render_fields" has to be a list or None.' )
49+ elif isinstance (render_fields , list ) \
50+ and not set (render_fields ).issubset (self .child_blocks ):
51+ raise ValueError ('"render_fields" has to contain name(s) of the '
52+ 'specified blocks.' )
53+ else :
54+ setattr (self ._meta_class , 'render_fields' , render_fields )
55+
56+
57+ def is_visible (self , value , request ):
58+ """Check whether user should see the block based on their segments.
59+
60+ :param value: The value from the block.
61+ :type value: dict
62+ :returns: True if user should see the block.
63+ :rtype: bool
64+
65+ """
66+ if int (value ['segment' ]) == - 1 :
67+ return True
68+
69+ if value ['segment' ]:
70+ for segment in get_segment_adapter (request ).get_segments ():
71+ if int (segment ['id' ]) == int (value ['segment' ]):
72+ return True
73+
74+ return False
75+
2376 def render (self , value , context = None ):
2477 """Only render this content block for users in this segment.
2578
@@ -31,14 +84,83 @@ def render(self, value, context=None):
3184 :rtype: blocks.StructBlock or empty str
3285
3386 """
34- request = context ['request' ]
35- adapter = get_segment_adapter (request )
36- user_segments = adapter .get_segments ()
87+ if not self .is_visible (value , context ['request' ]):
88+ return ""
89+
90+ if self ._meta_class .render_fields is None :
91+ return super (BasePersonalisedStructBlock , self ).render (
92+ value , context )
93+
94+ if isinstance (self ._meta_class .render_fields , list ):
95+ render_value = ''
96+ for field_name in self ._meta_class .render_fields :
97+ if hasattr (value .bound_blocks [field_name ], 'render_as_block' ):
98+ block_value = value .bound_blocks [field_name ] \
99+ .render_as_block (context = context )
100+ else :
101+ block_value = force_text (value [field_name ])
102+
103+ if block_value != 'None' :
104+ render_value += block_value
105+
106+ return render_value
107+
108+ raise RuntimeError ('"render_fields" is neither "None" or "list" '
109+ 'during rendering.' )
110+
111+ class Meta :
112+ """
113+ Setting render field will define which field gets rendered.
114+ Please use a name of the field. If none, it will render the whole block.
115+ """
116+ render_fields = None
117+
118+
119+ class PersonalisedStructBlock (BasePersonalisedStructBlock ):
120+ """Struct block that allows personalisation per block."""
121+
122+ class Meta :
123+ label = _ ('Personalised Block' )
124+ render_fields = None
125+
126+
127+ class PersonalisedRichTextBlock (BasePersonalisedStructBlock ):
128+ """Rich text block that allows personalisation."""
129+ rich_text = blocks .RichTextBlock (label = _ ('Rich Text' ))
130+
131+ class Meta :
132+ icon = blocks .RichTextBlock ._meta_class .icon
133+ label = _ ('Personalised Rich Text' )
134+ render_fields = ['rich_text' ]
135+
136+
137+ class PersonalisedTextBlock (BasePersonalisedStructBlock ):
138+ """Text block that allows personalisation."""
139+ text = blocks .TextBlock (label = _ ('Mutli-line Text' ))
140+
141+ class Meta :
142+ icon = blocks .TextBlock ._meta_class .icon
143+ label = _ ('Personalised Multi-line Text' )
144+ render_fields = ['text' ]
145+
146+
147+ class PersonalisedCharBlock (BasePersonalisedStructBlock ):
148+ """Char block that allows personalisation."""
149+ char = blocks .CharBlock (label = _ ('Text' ))
150+
151+ class Meta :
152+ icon = blocks .CharBlock ._meta_class .icon
153+ label = _ ('Personalised Single-line Text' )
154+ render_fields = ['char' ]
155+
156+
157+ class PersonalisedImageChooserBlock (BasePersonalisedStructBlock ):
158+ """Image chooser block that allows personalisation."""
159+ image = ImageChooserBlock (label = _ ('Image' ))
160+
161+ class Meta :
162+ icon = ImageChooserBlock ._meta_class .icon
163+ label = _ ('Personalised Image' )
164+ render_fields = ['image' ]
37165
38- if value ['segment' ]:
39- for segment in user_segments :
40- if segment ['id' ] == int (value ['segment' ]):
41- return super (PersonalisedStructBlock , self ).render (
42- value , context )
43166
44- return ""
0 commit comments