Adding a Twitter Widget for Wagtail’s new StreamField

Wagtail‘s latest feature is called the ‘StreamField’, currently only available in the 1.0 Beta 1 version, but clearly a bit of a Django CMS game changer.

It allows the editor to assemble a flow of page content of varying types (‘Blocks’ defined by the developer) in any order / combination they wish, kind of a Sir Trevor / Tumblr style affair.  It comes with a good selection of default block types, but it’s easy to repurpose these to create things like a Twitter Widget block.

In this guide I’m going to add a StreamField to an existing page class, which allows the user to add headers, paragraphs, images and a Twitter Widget to the page in any iteration they choose. The Twitter Widget I’m talking about is the one you can generate from your twitter settings by going to https://twitter.com/settings/widgets when logged in.

The user will be able to specify their twitter username, widget ID and a limit to how many latest tweets to display. The same principle could be applied for also adding Soundcloud, Instagram or whatever widgets, especially useful if you want a social media rich page, which has your content mixed in with feed content from the social media sites you use.

ss1

I like things laid out in easy clear steps, I’m a bit slow I guess 😦 so here goes… (Bare in mind the code may change, but this works for the current incarnation)

First off, add this class to your models.py

At the top I had to add (modify the edit_handlers Panels as you need, but you will obviously need the StreamFieldPanel for this)

from wagtail.wagtailcore.fields import StreamField
from wagtail.wagtailcore import blocks
from wagtail.wagtailimages.blocks import ImageChooserBlock
from wagtail.wagtailadmin.edit_handlers import FieldPanel, FieldRowPanel,MultiFieldPanel, \
 InlinePanel, PageChooserPanel, StreamFieldPanel

then

class TwitterBlock(blocks.StructBlock):
    twitter_box_username = blocks.CharBlock(required=True)
    twitter_box_widget_id = blocks.CharBlock(required=True)
    twitter_box_tweet_limit = blocks.CharBlock(required=True,max_length=2)

    class Meta:
        template = 'yourapp/blocks/twitter.html'
        icon = 'cogs'
        label = 'Twitter Widget'

We’re just making our own class based on the StructBlock (one of the built in types) and setting up the fields we need for our widget. The other thing I like is you can specify a template for your widget at this point, and choose an Icon, I’ve gone with a Cog, but there are plenty of others. The Label is useful to tell the users what it is once it’s part of the interface.

Next go to your page class where you want the StreamField to appear, and add the code below with other existing fields.

class ArticlePage(Page):

....
content = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('twitter', TwitterBlock()),
    ],null=True,blank=True)

....

This sets up our content field which can have a Header, Paragraph,Image or twitter widget.

 Then, so it appears in the admin, add it to the correct panel


....
ArticlePage.content_panels = [
    FieldPanel('title', classname="full title"),
    StreamFieldPanel('content'),

.....

At this point you’ll need to create and apply migrations for the new field. (If you’re replacing a body field with a Streamfield, you might want to think about adding a new field as we’ve done here rather than changing the existing body field. This will allow your editors to migrate the main page content from the old rich text field into the new StreamField one)


./manage.py makemigrations
python manage.py migrate

The final step is to obviously take care of the templating part!

You’ll need to add a new folder called ‘blocks’ in the template folder of your app, in the location we referenced earlier when defining the block, then create twitter.html as below.


<div class="twitter-widget">

{% if self.twitter_box_username %}

    <a class="twitter-timeline" href="https://twitter.com/{{ self.twitter_box_username }}" data-widget-id="{{ self.twitter_box_widget_id }}" data-tweet-limit="{{ self.twitter_box_tweet_limit }}">Tweets by {{ self.twitter_box_username}}</a>
    <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

{% endif %}

</div>

Then of course the main template for your page that has the new StreamField content will need to be changed. Add this code below whereever you want the content StreamField to appear. There a few ways of outputting the blocks, I’ve chosen one from the Wagtail examples where I get a bit more control, for more examples go to http://docs.wagtail.io/en/v1.0b1/pages/streamfield.html#template-rendering


 <article>
 {% for block in self.content %}
     {% if block.block_type == 'heading' %}
         <h1>{{ block.value }}</h1>
     {% elif block.block_type == 'image' %}
         {% image block.value width-400 %}
     {% else %}
        <section class="block-{{ block.block_type }}">
            {{ block }}
        </section>
     {% endif %}
 {% endfor %}
 </article>

Hopefully this might help someone, let me know of any mistakes.