How to use Markdown in WordPress and preserve spaces in code blocks

It’s easy to add Markdown support to WordPress. However, it does have a nefarious quirk. In fact, even if you can input Markdown text from the Text panel, and it gets rendered just fine in the blog, the harsh truth is that, as soon as you inadvertently switch to the Visual panel, all the white space in your code blocks gets wiped out, losing all the indentation you had put in.

This looks like a typical reason for writing a plugin to fix that.

Use a shell plugin

Given that I often need small WordPress adjustments like this one, in the past I developed one ring to rule them all: Custom Stuff.

<?php
/*
Plugin Name: Custom Stuff
Plugin URI: http://andowebsit.es/
Description: Custom stuff for my blog.
Author: Andrea Ercolino
Author URI: http://andowebsit.es/about
Version: 1.0
*/

function custom_stuff_header() {
    require 'header.php';
}
add_action( 'wp_head',  'custom_stuff_header', 10, 0 );


function custom_stuff_footer() {
    require 'footer.php';
}
add_action( 'wp_footer',  'custom_stuff_footer', 10, 0 );

 (file wordpress/wp-content/plugins/custom-stuff/custom-stuff.php)

which is just a simple PHP script that basically declares itself as a WordPress plugin and all it does is to require other PHP scripts where the real action takes place.

Design a usable solution

What we need is a way to make WordPress aware that a post is written in Markdown thus no Visual editor will ever be allowed for it.

My solution is based on the user_can_richedit hook and the Shortcode API. It works like this:

  1. At the start of a Markdown text, you add the shortcode.
  2. In editing mode, the hook handler determines whether or not a post begins with that shortcode. If it does, a false is returned, thus effectively forbidding the Vsual editor.
  3. In reading mode, that shortcode is just replaced by an empty string.

Code

<?php

function custom_stuff_no_richedit_if_content_asks( $default ) {
    global $post;
    $content = $post ? $post->post_content : '';
    $start = '';
    if ( substr($content, 0, strlen($start)) === $start ) {
        return false;
    }
    return $default;
}
add_filter( 'user_can_richedit', 'custom_stuff_no_richedit_if_content_asks', 10, 1 );

function custom_stuff_nothing() {
    return '';
}
add_shortcode( 'no_richedit', 'custom_stuff_nothing', 10, 0 );

 (file wordpress/wp-content/plugins/custom-stuff/no-richedit.php)

<?php
/*
Plugin Name: Custom Stuff
Plugin URI: http://andowebsit.es/
Description: Custom stuff for my blog.
Author: Andrea Ercolino
Author URI: http://andowebsit.es/about
Version: 1.1
*/

function custom_stuff_header() {
    require 'header.php';
}
add_action( 'wp_head',  'custom_stuff_header', 10, 0 );


function custom_stuff_footer() {
    require 'footer.php';
}
add_action( 'wp_footer',  'custom_stuff_footer', 10, 0 );

require 'no-richedit.php';

 (file wordpress/wp-content/plugins/custom-stuff/custom-stuff.php)

Example

This is how WordPress looks like when the shortcode is used. Notice that there is no Visual editor panel

This is how WordPress looks like when the shortcode is not used.

Only one thing to remember

There is only one thing to remember then. When adding a post, before introducing any relevant Markdown, switch to the Text panel, add the [no-richedit] shortcode and save a draft. Then the Visual editor won’t appear anymore for that post. (until you remove the shortcode and save again)

How to include a web page thumbnail in a WordPress post

In my article about My take on a Vending Machine Simulation, I wanted to include a link to my Gist, together with a nice thumbnail. Having installed Nzymes on my blog, that wasn’t too difficult.

I thought about using an injection like this:

´{[ =<URL of the file>= | 1.thumb(1) ]}´

so that I could easily reuse it just by changing the URL.

Here are the contents of the execution enzyme.

thumb

list( $url, $size, $target ) = array_pad( $arguments, 3, null );
if (! $size) $size = 'x';
if (! $target) $target = '_blank';

$src = "http://free.pagepeeker.com/v2/thumbs.php?size=$size&url=" . urlencode($url);
$img = "<img src='$src' />";
$result = "<a href='$url' target='$target'>$img</a>";
return $result;

The idea is to rely on the PagePeeker free service and return a properly built A IMG element.

How to include code from GitHub in a WordPress post

In my article about How to setup a Multi Page Application in AngularJS, I wanted to include code from GitHub. Having installed Nzymes on my blog, that wasn’t too difficult.

I thought about using an injection like this:

´{[ =<URL of the file>= | 1.get-url(1) | =javascript= | 1.hl(2) ]}´

so that I could easily reuse it just by changing the URL.

Here are the contents of the execution enzymes.

get-url

list($url) = $arguments;
$pieces = preg_split('@/@', $url, -1, PREG_SPLIT_NO_EMPTY);
$protocol = array_shift($pieces);
$file = implode('/', $pieces);

$upload_dir = wp_upload_dir();
$filename = "{$upload_dir['basedir']}/$file";
if (! file_exists($filename)) {
  $dirname = dirname($filename);
  if (! file_exists($dirname)) {
    wp_mkdir_p($dirname);
  }
  $data = file_get_contents($url);
  file_put_contents($filename, $data);
}
$data = file_get_contents($filename);
return $data;

The idea is to download the remote file once and store it on the server, in a directory structure like the url (except the protocol). Thus, at any later time, the local copy is returned.

hl

list( $code, $language, $numbers ) = array_pad( $arguments, 3, null );

$pre_class = '';
$numbers   = trim( $numbers );
if ( $numbers ) {
    $pre_class = ' class="' . $numbers . '"';
}

$code     = str_replace( '{' . '[', '{' . '-[', $code );  // escape Enzymes 3 injections just in case
$code     = htmlspecialchars( $code );

$code_class = '';
$language   = trim( $language );
if ( $language ) {
    $code_class = ' class="' . $language . ($language == 'ruby' ? ' hljs' : '') . '"';
    $template = '<pre%s><code%s>%s</code></pre>';
    $args = array($template, $pre_class, $code_class, $code);
} else {
    $template = '<pre%s>%s</pre>';
    $args = array($template, $pre_class, $code);
}

$result   = call_user_func_array( 'sprintf', $args );
return $result;

The idea is to wrap the escaped code into a PRE CODE element, prepared for Chili to later highlight it in the browser. Fun fact: above hl highlights itself:

´{[ 1.hl | =php= | 1.hl(2) ]}´.