Using ButterCMS in a Jupyter notebook with VueJS

ButterCMS

Goal

The goal of this notebook is to get myself familiar with ButterCMS, a headless CMS that can be easily integrated with any platform in any language. In the past I've experienced with Joomla, Drupal and WordPress (and Blogger long time ago), where I have lost invested a lot of time in simply getting the CMS in place, extending the CMS with more functionality and migrating data between servers. Since I am investigating different ways of using microservices, becoming less platform dependent and overall learning new tools, I use this notebook to show a simple example of ButterCMS in Jupyter.

About

Add a blog or CMS to your site in minutes Drop-in our Headless CMS and get back to more interesting problems.

Prerequisites

In [1]:
!pip install buttercms-python
Requirement already satisfied: buttercms-python in /home/jitsejan/miniconda3/lib/python3.6/site-packages (1.1)
Requirement already satisfied: requests in /home/jitsejan/miniconda3/lib/python3.6/site-packages (from buttercms-python) (2.18.4)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /home/jitsejan/miniconda3/lib/python3.6/site-packages (from requests->buttercms-python) (3.0.4)
Requirement already satisfied: idna<2.7,>=2.5 in /home/jitsejan/miniconda3/lib/python3.6/site-packages (from requests->buttercms-python) (2.6)
Requirement already satisfied: urllib3<1.23,>=1.21.1 in /home/jitsejan/miniconda3/lib/python3.6/site-packages (from requests->buttercms-python) (1.22)
Requirement already satisfied: certifi>=2017.4.17 in /home/jitsejan/miniconda3/lib/python3.6/site-packages (from requests->buttercms-python) (2018.4.16)
You are using pip version 10.0.0, however version 10.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
In [2]:
import os
os.environ['buttercmskey'] = '1b13611e144e11bbce5b484f4064e39638b223a1'

Python implementation

In [3]:
from butter_cms import ButterCMS
client = ButterCMS(os.getenv('buttercmskey'))
In [4]:
client
Out[4]:
<butter_cms.ButterCMS at 0x7f3edc72f630>
In [5]:
import json
print(json.dumps(client.posts.all({'page_size': 10, 'page': 1}), indent=4))
{
    "data": [
        {
            "url": "jupyter-notebook",
            "created": "2018-06-14T11:53:54.533383Z",
            "published": "2018-06-14T11:50:00Z",
            "author": {
                "first_name": "Jitse-Jan",
                "last_name": "van Waterschoot",
                "email": "[email protected]",
                "slug": "jitse-jan-van-waterschoot",
                "bio": "",
                "title": "",
                "linkedin_url": "",
                "facebook_url": "",
                "instagram_url": "",
                "pinterest_url": "",
                "twitter_handle": "",
                "profile_image": ""
            },
            "categories": [
                {
                    "name": "development",
                    "slug": "development"
                }
            ],
            "tags": [
                {
                    "name": "",
                    "slug": ""
                },
                {
                    "name": "buttercms",
                    "slug": "buttercms"
                },
                {
                    "name": "python",
                    "slug": "python"
                },
                {
                    "name": "vuejs",
                    "slug": "vuejs"
                }
            ],
            "featured_image": "https://cdn.buttercms.com/eBXUxLoCR4CKQMUnhjur",
            "slug": "jupyter-notebook",
            "title": "Jupyter notebook",
            "body": "<p>I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.&nbsp;</p>",
            "summary": "I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.",
            "seo_title": "Jupyter notebook",
            "meta_description": "I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.",
            "status": "published"
        },
        {
            "url": "example-post",
            "created": "2018-06-14T11:47:08.121702Z",
            "published": "2018-06-14T11:47:08.121391Z",
            "author": {
                "first_name": "Jitse-Jan",
                "last_name": "van Waterschoot",
                "email": "[email protected]",
                "slug": "jitse-jan-van-waterschoot",
                "bio": "",
                "title": "",
                "linkedin_url": "",
                "facebook_url": "",
                "instagram_url": "",
                "pinterest_url": "",
                "twitter_handle": "",
                "profile_image": ""
            },
            "categories": [
                {
                    "name": "Example Category",
                    "slug": "example-category"
                }
            ],
            "tags": [
                {
                    "name": "Example Tag",
                    "slug": "example-tag"
                }
            ],
            "featured_image": "https://d2devwt40at1e2.cloudfront.net/api/file/tdt3s1OHRO6wfQOpmAHw",
            "slug": "example-post",
            "title": "Example Post",
            "body": "<p>Welcome to ButterCMS! This an example blog post written using Butter.</p>\n<h3>What's happening here?</h3>\n<p>If you're viewing this post from your website or command line, you've successfully made a request to&nbsp;the <a href=\"https://buttercms.com/docs/api\">Butter API</a>. If you haven't already, make sure you have our <a href=\"https://buttercms.com/docs/\">development guides</a> pulled up for step-by-step instructions on setting up Butter.</p>\n<h3>How does&nbsp;editing work?</h3>\n<p>Butter's WYSIWYG editor supports standard text formatting including headings, links, quotes, code, text alignment, and more. You can upload, crop, and resize images which are automatically hosted and delivered through a CDN (see below). You can also edit HTML directly when needed.</p>\n<figure class=\"image\"><img src=\"https://d2wzhk7xhrnk1x.cloudfront.net/rgPM9aHoSSKnjk44TQlD_butter-blog-post.jpg\" alt=\"Delivered to you via CDN\" />\n<figcaption>Delivered to you via CDN</figcaption>\n</figure>\n<h3>Can I use Butter as a full CMS for&nbsp;things other than a&nbsp;blog?</h3>\n<p>Yes. Butter can be used as a full CMS for managing dynamic content and creating pages across your entire website or app. Check out our <a href=\"https://buttercms.com/docs/\">development guides</a> for step-by-step tutorials on setting this up.</p>\n",
            "summary": "This is an example blog post. Pretty neat huh?",
            "seo_title": "Example Post SEO Optimized Title",
            "meta_description": "This is our example blog posts SEO optimized meta description.",
            "status": "published"
        }
    ],
    "meta": {
        "next_page": null,
        "count": 2,
        "previous_page": null
    }
}
In [6]:
posts = client.posts.all({'page_size': 10, 'page': 1})
In [7]:
posts['data'][0]
Out[7]:
{'url': 'jupyter-notebook',
 'created': '2018-06-14T11:53:54.533383Z',
 'published': '2018-06-14T11:50:00Z',
 'author': {'first_name': 'Jitse-Jan',
  'last_name': 'van Waterschoot',
  'email': '[email protected]',
  'slug': 'jitse-jan-van-waterschoot',
  'bio': '',
  'title': '',
  'linkedin_url': '',
  'facebook_url': '',
  'instagram_url': '',
  'pinterest_url': '',
  'twitter_handle': '',
  'profile_image': ''},
 'categories': [{'name': 'development', 'slug': 'development'}],
 'tags': [{'name': '', 'slug': ''},
  {'name': 'buttercms', 'slug': 'buttercms'},
  {'name': 'python', 'slug': 'python'},
  {'name': 'vuejs', 'slug': 'vuejs'}],
 'featured_image': 'https://cdn.buttercms.com/eBXUxLoCR4CKQMUnhjur',
 'slug': 'jupyter-notebook',
 'title': 'Jupyter notebook',
 'body': '<p>I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.&nbsp;</p>',
 'summary': 'I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.',
 'seo_title': 'Jupyter notebook',
 'meta_description': 'I am working on a Jupyter notebook to show how to work with ButterCMS, VuejS and Python and how it all smoothly works together.',
 'status': 'published'}
In [8]:
title = posts['data'][0]['title']
title
Out[8]:
'Jupyter notebook'

Javascript implementation

In [9]:
%%javascript
require.config({
    paths: {
        buttercms: "https://cdnjs.buttercms.com/buttercms-1.1.1.min",
    },
    shim: {
        buttercms: {
            exports: "ButterCMS"
        },
    }
});
In [10]:
%%javascript
require(['buttercms'], function(ButterCMS) {
    var butter = Butter('1b13611e144e11bbce5b484f4064e39638b223a1');
    butter.post.list({page: 1, page_size: 10}).then(function(response) {
        console.log(response['data']['data'][0]['title'])
        console.log(response['data']['data'][0]['body'])
    })
});

VueJS implementation

Finally an attempt to integrate VueJS with Jupyter using data from the ButterCMS. Following this guide we are able to add the blog posts to the body of this notebook.

In [11]:
%%javascript
require.config({
    paths: {
        buttercms: "https://cdnjs.buttercms.com/buttercms-1.1.1.min",
        vue: "https://cdn.jsdelivr.net/npm/vue/dist/vue"
    },
    shim: {
        buttercms: {
            exports: "ButterCMS"
        },
        vue: {
            exports: "Vue"
        }
    }
});
In [12]:
%%html
<script type="text/x-template" id="blog-home-template">
    <div id="blog-home">
        <h1>{{ page_title }}</h1>
        <!-- Create `v-for` and apply a `key` for Vue. Here we are using a combination of the slug and index. -->
        <div v-for="(post,index) in posts" :key="post.slug + '_' + index">
            <article class="media">
                <figure>
                  <!-- Bind results using a `:` -->
                  <!-- Use a `v-if`/`else` if their is a `featured_image` -->
                  <img v-if="post.featured_image" :src="post.featured_image" alt="">
                  <img v-else src="http://via.placeholder.com/250x250" alt="">
                </figure>
                <h2>{{ post.title }}</h2>
                <p>{{ post.summary }}</p>
            </article>
        </div>
    </div>
</script>
In [13]:
%%html
<div id="vue-app">
  <blog-home :posts="blogPosts"/>
</div>
In [14]:
%%javascript
require(['buttercms', 'vue'], function(ButterCMS, Vue) {
    console.log(Vue.version);
    var butter = Butter('1b13611e144e11bbce5b484f4064e39638b223a1');
    
    var BlogHome = Vue.component('blog-home', {
      template: '#blog-home-template',
      props: {
        data: Array,
      },
      data: function () {
        return {
            page_title: 'Blog',
            posts: []
          }
      },
      methods: {
          getPosts() {
            butter.post.list({
              page: 1,
              page_size: 10
            }).then((res) => {
              this.posts = res.data.data
            })
          }
      },
      created() {
          this.getPosts()
      }
    })

    var vueApp = new Vue({
      el: '#vue-app',
      components: {BlogHome: BlogHome},
      data: {
          blogPosts: []
      }
    })

});
In [15]:
%%html
<style>
#blog-home article{
    width: 50%;
    float: left;
}
#blog-home article figure img{
    height: 50px;
}
</style>

Result

To put everything together I have created a JSfiddle and embedded the result below.

In [16]:
%%html
<iframe width="100%" height="500" src="//jsfiddle.net/z4hapsLt/13/embedded/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>