@shakedko
IF AN EXPERT SAYS IT CAN'T BE DONE GET ANOTHER EXPERT.
- DAVID BEN-GURION

Removing Wordpress and switching to Pelican

Description

I wanted to remove Wordpress for long time but couldn't figure which platform I should use.

The reasons that I wanted to change were:

  • Wordpress is slow
  • It's hard to write a blog post using those Javascript editors (e.g tinymce, ckeditor etc)
  • It's hard to write or find well written Wordpress plugins
  • Almost every few weeks-month Wordpress suggests a security update or it's plugins

And the last and the most important reason is because I actually don't need any complicated system to maintain my blog.

So after looking for different blog platforms I decided to use a static web generator to generate my blog and chose Pelican

Why ?

Pelican seems like a simple static web generator which allows you to add plugins and themes. Together with that you can use Markdown or Restructured-text and enjoy the ability of writing a clear and simple post without any extra HTML tags and inconsistent generated structure.

Another reason which is more related to the idea of static web generator and not necessarily pelican would be the idea of getting rid of database dependency.

How ?

There are few steps in order to get to the point that you are able to publish your work while some of you might need to migrate from Wordpress.

Basic Installation

Basically there is nothing difficult with installing Pelican. Just go to Getting Started - Installing Pelican page and follow the instructions

Export Wordpress

Exporting Wordpress blog posts is easy, Wordpress supplies a Guide to export it's posts and comments.

Import Wordpress

Pelican comes with a "built-in importer" which can work with different blog platforms, such as: Wordpress, Dotclear, Posterous, Tumblr.

The thing is that unfortunately it doesn't work 100% and it requires a post-overview of the imported posts, why? because some tags are not really suppose to be where they are and many WYSIWYGs are not working in a way of generating a minimal and valid HTMLs. This means that you will have to go over your imported posts and make sure they stayed the same as before or fix whatever is broken.

Development mode

Pelican supplies a simple way to adjust your while using a localhost server.

All you need to do is go to your Pelican blog directory and use the following command:

Debug mode:

path/to/pelican-blog/: make devserver [PORT=8000]

Production mode:

path/to/pelican-blog/: make server [PORT=8000]

Plugins

I am using 3 plugins at the moment:

I am also using built-in plugins such as Disqus.

Assets

Assets requires an installation of the Webassets package. Though the default instruction of the Assets plugin says to use

pip install webassets

Webassets recommends using the latest update by installing it's dev mode:

pip install webassets==dev

For me it mattered as I had issues regarding to Python 2.7 and language encoding.

Using the Assets plugin actually makes all Javascript and CSS management easier. Though I am not using Javascript almost at all at the moment, I could use Compass\SASS, CSS compressor etc by just defining it in my base.html template.

Related posts

I had to extend this plugin's behavior as I wanted to be able to point the related posts by myself just by using the post's meta data instead of letting it to find the related tags and attach them together.

I have added some extra logic in order to accomplish that:

:::python
.... # SAME CODE AS BEFORE
for article in generator.articles:
# set priority in case of forced related posts
if hasattr(article,'related_posts'):
    # split slugs
    related_posts = article.related_posts.split(',')
    posts = []
    # get related articles
    for slug in related_posts:
        i = 0
        for a in generator.articles:
            if i >= numentries: # break in case there are max related psots
                break
            if a.slug == slug:
                posts.append(a)
                i += 1
    article.related_posts = posts
else
.... # SAME CODE AS BEFORE

This will look for "related_posts:" meta data and let you add your own related posts by using your post's slug, e.g:

related_posts: slug1, slug2, slug3....slugN

Note: I have added a pull request on Github but at the moment I was writing this post it wasn't approved yet.

Sitemap

There is nothing much to say here as this plugin has a very straight-forward instructions.

New URLs Redirection

While replacing your blog sometimes you will have to redirect your old posts to your new posts. For example, while migrating my blog I decided to add the dates of the post in the URL so I have changed my posts URLs from:

/slug

To

/Year/Month/Day/slug

I chose to use an .htaccess file that contains the old URLs vs the new ones.

The file is in content/extra/.htaccess and is being copied to output/.htaccess. All you need to is to use the following code in your pelicanconf.py:

STATIC_PATHS = [
    'extra/robots.txt',
]
EXTRA_PATH_METADATA = {
    'extra/.htaccess': {'path': '.htaccess'},
}

These parameters can help you add other things as well, @see Step 11 - Extra Configurations

Custom Theme

Customizing your theme is a pretty straightforward task.

You just need to define your new theme by using

THEME = './themes/shakedos'

and create the folder as mentioned.

Follow the instruction on Pelican's website to understand the structure and options that you can use.

Deployment

As I am using the MakeFile all I need to do is:

make ssh_upload

and I will be deploying using ssh. It is possible to use other options as: rsync, dropbox, ftp, s3, cf and github pages

Extra Configurations

As an example I will just share my own configuration file with comments included.

Configuration is divided to two files:

  1. pelicanconf.py - works both on development and production mode
  2. publishconf.py - works on production mode and overrides development settings if needed
pelicanconf.py
:::python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
# Basic Information
AUTHOR = u'Shaked Klein Orbach'
SITENAME = u'Shakedos'
SITEURL = 'https://www.shakedos.com'
TIMEZONE = 'Europe/Amsterdam'
DEFAULT_LANG = u'en'
LOCALE = ('en_US')
# Apply comments
DISQUS_SITENAME = 'DISQUS SITENANE'
# Dont load .html files
READERS = {'html': None}
# Paths
OUTPUT_PATH = 'output'
PATH = 'content'
# Feed generation is usually not desired when developing
FEED_DOMAIN = SITEURL
FEED_ALL_RSS = 'feeds/site.xml'
FEED_ALL_ATOM = 'feeds/all.atom.xml'
CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
TRANSLATION_FEED_ATOM = None

# Customized Social widget (was changed regarding to the blog's template)
SOCIAL = (('linkedin', 'http://www.linkedin.com/pub/shaked-klein-orbach/13/60b/941'),
          ('github', 'http://www.github.com/shaked'),
          ('twitter', 'http://www.twitter.com/shakedko'),
          ('facebook', 'http://facebook.com/shakedosblog'),
          ('rss', FEED_ALL_RSS))
# Show pagination over 50 pages
# TODO: endless scrolling
DEFAULT_PAGINATION = 50
# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = True
DELETE_OUTPUT_DIRECTORY = True
# Change slugs and files
ARTICLE_SAVE_AS = '{date:%Y}/{date:%b}/{date:%d}/{slug}.html'
ARTICLE_URL = '{date:%Y}/{date:%b}/{date:%d}/{slug}.html'
# Take advantage of the following defaults
STATIC_PATHS = [
    'images',
    'extra/robots.txt',
    'extra/favicon.ico',
    'extra/.htaccess',
    'examples',
    'files',
]
EXTRA_PATH_METADATA = {
    'extra/robots.txt': {'path': 'robots.txt'},
    'extra/favicon.ico': {'path': 'favicon.ico'},
    'extra/.htaccess': {'path': '.htaccess'},
}
# Plugins
PLUGIN_PATH = './pelican-plugins'
PLUGINS = ['assets', 'related_posts','sitemap']
# Site map
SITEMAP = {
    'format': 'xml',
    'priorities': {
        'articles': 0.5,
        'indexes': 0.5,
        'pages': 0.5
    },
    'changefreqs': {
        'articles': 'monthly',
        'indexes': 'daily',
        'pages': 'monthly'
    }
}
# Assets and themes
WEBASSETS = True
THEME = './themes/shakedos'
ASSET_SOURCE_PATHS = (
    'static',
)
ASSETS_RELATIVE = True
# plugin for 'share this'
SHARE_THIS_PROVIDERS = [
  {'name':'facebook', 'size':'large'},
  {'name':'sharethis', 'size':'large'},
  {'name':'googleplus', 'size':'large'},
  {'name':'twitter', 'size':'large'},
  {'name':'pinterest', 'size':'large'},
  {'name':'email', 'size':'large'},
]
publishconf.py
:::python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
# This file is only used if you use `make publish` or
# explicitly specify it as your config file.
import os
import sys
sys.path.append(os.curdir)
from pelicanconf import *
SITEURL = 'http://blog.shakedos.com'
RELATIVE_URLS = False
FEED_ALL_ATOM = 'feeds/all.atom.xml'
CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
# Following items are often useful when publishing
GOOGLE_ANALYTICS = 'UA-23773041-1'

Note that all variables should be used with capital letters otherwise they won't work

Work In Progress 🚧
Discipline