Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

What I want to do

Sharing code in posts on WordPress is quite a pain with all the HTML escaping to take care of. So, I plan to enclose code in posts within a custom shortcode called [sourcecode], as shown below, and let it automatically escape the special characters.

[sourcecode]
<?php

    // Some code here.

?>
[/sourcecode]

The code

And this is what the relevant function (in my theme's functions.php) looks like:

/**
 * Functionality to set up custom shortcode correctly.
 *
 * This function is attached to the 'the_content' filter hook.
 */
add_filter( 'the_content', 'run_bbcodes', 7 );
function run_bbcodes( $content ) {
    global $shortcode_tags;

    $orig_shortcode_tags = $shortcode_tags;
    $shortcode_tags = array();

    // New shortcodes below
    add_shortcode( 'sourcecode', 'bbcode_code' );

    $content = do_shortcode( $content );
    $shortcode_tags = $orig_shortcode_tags;

    return $content;
}

/**
 * Add Custom shortcode functions below
 *
 * This function is attached to the 'sourcecode' shortcode hook.
 */
function bbcode_code( $atts, $content = null ) {

    // Ensure contents of a <pre>...</pre> HTML block aren't converted into paragraphs or line-breaks
    $content = clean_pre( $content );

    $content = str_replace(

        // Replace these special characters...
        array( '&', '\0', '<', '>', '\'', '"', '/', '[', ']' ),

        // ...with the HTML entities below, respectively
        array( '&amp;', '&#92;&#48;', '&lt;', '&gt;', '&apos;', '&quot;', '&#47;', '&#91;', '&#93;' ),

        $content
    );

    return '<pre><code>' . trim( $content ) . '</code></pre>';
}

/**
 * Related sourcecode worth reading:
 *
 * https://bitbucket.org/cbavota/syntax-highlighter-plugin/src
 *
 * https://github.com/mdawaffe/Highlight.js-for-WordPress/blob/master/highlight-js.php
 *
 * https://github.com/fracek/highlight-wp/blob/master/highlight.php
 *
 * http://plugins.svn.wordpress.org/syntaxhighlighter/trunk/
 *
 * http://blog.webspace.jp/235
 *
 * http://core.trac.wordpress.org/browser/trunk/src/wp-includes/shortcodes.php
 */

Question(s)

Now that the explanation is out of the way...

  1. Am I missing anything else?

    For example, until I read the source of SyntaxHighlighter Evolved plugin, I didn't know that \0 also needs to be replaced with &#92;&#48; "to work around kses" (which wasn't clear enough for me).

  2. And other than escaping the special characters, is there anything else that I could possibly be missing? Am I doing it the right way?

    (It would be great if anyone could take a look at this file (PHP source code of SyntaxHighlighter Evolved plugin) and see if you can find something that I need to implement. I already did try my best though.)


PS: Why not use the plugin itself? For one, I don't want the syntax highlighting that it does; and I want to know if what it does can be implemented easily.

share|improve this question
    
I see that bungshea's Snippets plugin is using a custom table to store code, the column is 'code longtext not null'. . . I was playing with the snippets idea and did a CPT with only title and a textarea custom field and didn't found much problem in displaying them with htmlentities. . . Anyway, nothing in depth, just sharing a bit of research. –  brasofilo Oct 1 '13 at 12:10
    
@brasofilo I am probably missing your point, but as I see it, the Snippets plugin is not for displaying code on the site (which requires escaping special HTML characters) but for running code that you'd otherwise have to add in your theme's functions.php. Entirely different, isn't it? –  its_me Oct 1 '13 at 12:32
    
I missed the point, re: plugin Snippets... But the experiment I did was to display the snippets, not to run them. –  brasofilo Oct 1 '13 at 12:46

2 Answers 2

up vote 4 down vote accepted
+100

You're asking about X, but I'll answer with Y. IMHO, there are too many exceptions to handle to grant another solution.

SyntaxHighlighter Evolved has the following Pro Tip (strong, uppercase, in the original):

TIP: Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML.

Not only that, it's quite common to have plugins and themes messing around with the_content. Rough (and totally unscientific) measure: 10 occurrences within 50 plugins; 15 occurrences within 50 themes.

Another Pro Tip:

enter image description here

Proposed solution

1) To handle the code, I think it's either a Custom Field/Box in the Edit Post screen or a bridge between the Post and a "private" Custom Post Type that will store the code. I prefer the latter. Steps:

  • paste the embeddable code into a <textarea> rendered with edit_form_after_title
  • use esc_html to display in the backend
  • and '<pre class="pretty">'.htmlentities($code).</pre> in the frontend

2) To handle the syntax highlight, it's up to the reader to choose its preferred method. Google Prettify is literally 2 lines of code and adding a class to the <pre> tag. Enqueue the script only when the shortcode is present.

Sample plugin

Custom post type enter image description here

Post post type
enter image description here

The following is a skeleton and only outlines the logic. And here the links for some goodies:
Working skeleton at Gist Full plugin at GitHub

<?php
/**
 * Plugin Name: Skeleton for the plugin Snippets Shortcode
 * Plugin URI: https://gist.github.com/brasofilo/6804951
 */

/**
 * Based on Plugin Class Demo
 * https://gist.github.com/toscho/3804204
 */
add_action(
    'plugins_loaded',
    array ( B5F_Snippets_Shortcode::get_instance(), 'plugin_setup' )
);

/**
 * Main class
 *
 * Fires all classes
 * Handles save_post action for other classes
 */
class B5F_Snippets_Shortcode {
    protected static $instance = NULL;
    public $post_type = 'snippet';    
    public function plugin_setup() {
        new B5F_SS_Cpt();
        new B5F_SS_Posts_Pages_Metabox();
        new B5F_SS_Shortcode();
        add_action( 'save_post', array( $this, '_save_post' ), 10, 2 );
    }
} 

/**
 * Register Snippet post type and render Textarea Field after the Title
 *
 * CPT is hierarchical 
 * Custom Metabox for CPT is left empty, can be used as Language Selector
 * Uses save_post for CPT 
 */
class B5F_SS_Cpt {
    public function __construct() {
        add_action( 'init', array( $this, '_cpt' ) );
        add_action( 'edit_form_after_title', array( $this, 'input_text_area' ) );
    }
}

/**
 * Metabox to select the Snippet and to display the correspondent Shortcode
 *
 * Displayed in Posts and Pages post types
 * Add jQuery to listen to the Snippets Dropdown changes and update the sample Text Field
 * Uses save_post for other Post Types 
 */
class B5F_SS_Posts_Pages_Metabox {
    public function __construct() {
        add_action( 'add_meta_boxes', array( $this, '_meta_boxes' ) );
    }
}

/**
 * Snippets Shortcode
 *
 * First gets the Snippet Post ID, then its associated Meta Data, 
 * finally filters the string with http://php.net/manual/en/function.htmlentities.php 
 *
 * Uses Google Code Prettify
 * https://code.google.com/p/google-code-prettify/
 */
class B5F_SS_Shortcode {
    public function __construct() {
        add_shortcode( 'snippet', array( $this, '_shortcode' ) );
    }
}
share|improve this answer
    
So basically the idea is to: 1. create a custom post type (Snippets) and post code snippets as new posts under the custom post type 2. Create a custom shortcode function that pulls content from a post (under the custom post type - Snippets) and displays it as preformatted text, when supplied with the post ID (parameter). 3. Use the shortcode to share code in your normal posts. Am I correct? (That's a very good idea. Sorry I missed it from your comment.) –  its_me Oct 3 '13 at 13:36
1  
Yes, exactly. Did you see the plugin in action? It's fully functional. Later on, I'll try to make it work with CodeMirror (like seen in bainternet's Admin Page Class). And also add a custom taxonomy to map the snippets language. Yup, I liked the idea too and am totally adopting it in my blog ;) –  brasofilo Oct 3 '13 at 16:18
    
@its_me, definitely, Google Prettify is a nice toy, but CodeMirror is a full fledged swiss-army tool. –  brasofilo Oct 6 '13 at 15:15
    
Sure! I wasn't claiming otherwise. :) –  its_me Oct 7 '13 at 2:25
    
I should have addressed the previous comment to @all, and put some exclamations and smilies ;) I have to clean up the code and will publish the plugin in GitHub, CM rules! –  brasofilo Oct 7 '13 at 4:33

Just in case Rodolfo's answer isn't clear, here's what (I think) he's suggesting — an excellent idea, by the way:

  1. Create a private custom post type called Snippets, in whose posts we'll be storing our code snippets.

    We'll use the supports argument in register_post_type to our advantage. We don't want the Content Editor on the edit screen for the custom post type as we'll be storing our content in a custom meta box[1] (essentially a custom field), so we'll hide it; and other unnecessary fields too as this'll be a private custom post type.

    Here's an example to give you an idea as to how it'd be done:

    /*
     * Register Custom Post Types
     * DON'T FORGET TO FLUSH PERMALINKS
     *
     * http://justintadlock.com/archives/2013/09/13/register-post-type-cheat-sheet
     * https://gist.github.com/justintadlock/6552000
     * http://core.trac.wordpress.org/browser/trunk/src/wp-includes/post.php
     */
    add_action( 'init', 'itsme_register_post_types' );
    function itsme_register_post_types() {
    
        // Post Type: Snippets
        register_post_type(
    
            // Keeping it unique so that it never conflicts with a plugin or theme
            'itsme_snippet',
    
            array(
                'description'         => 'Code snippets for sharing in posts.',
                'public'              => false, // NOTE!
                'publicly_queryable'  => false, // NOTE!
                'exclude_from_search' => true, // NOTE!
                'show_in_nav_menus'   => false,
                'show_ui'             => true,
                'show_in_menu'        => true,
                'show_in_admin_bar'   => false,
                'menu_position'       => 5, // Your choice.
                'menu_icon'           => null,
                'can_export'          => false,
                'delete_with_user'    => false,
                'hierarchical'        => false,
                'taxonomies'          => array(),
                'has_archive'         => false, // NOTE!
                'query_var'           => false, // NOTE!
                'capability_type'     => 'post',
                'rewrite' => array(
                    'slug'            => 'snippets',
                    'with_front'      => false,
                    'pages'           => false,
                    'feeds'           => false, // NOTE!
                ),
    
                // IMPORTANT! At least make sure that the 'editor' (Content Editor) is hidden - i.e. remove it from the array.
                'supports'             => array( 'title', 'revisions', ),
    
                'labels' => array(
                    'name'               => __( 'Snippets' ),
                    'singular_name'      => __( 'Snippet' ),
                    'menu_name'          => __( 'Snippets' ),
                    'name_admin_bar'     => __( 'Snippets' ),
                    'add_new'            => __( 'Add New' ),
                    'add_new_item'       => __( 'Add New Snippet' ),
                    'edit_item'          => __( 'Edit Snippet' ),
                    'new_item'           => __( 'New Snippet' ),
                    'view_item'          => __( 'View Snippet' ),
                    'search_items'       => __( 'Search Snippets' ),
                    'not_found'          => __( 'No Snippets found' ),
                    'not_found_in_trash' => __( 'No Snippets found in trash' ),
                    'all_items'          => __( 'All Snippets' ),
                    'parent_item'        => __( 'Parent Snippet' ),
                    'parent_item_colon'  => __( 'Parent Snippet:' ),
                    'archive_title'      => __( 'Snippets' ),
                )
            )
        );
    
    }
    

    Related Links: (Because I haven't given any explanation within the code.)

  2. Create a 'textarea' type meta box (essentially a custom field) which is shown only on the edit screen for the custom post type Snippets.

    If you don't know how to code one, you can easily create one using a plugin like Advanced Custom Fields. Here's a screenshot just to give you an idea:

    Create a custom meta box using Advanced Custom Fields plugin

  3. Create a custom shortcode that takes in the id of the snippet post and displays content (i.e. code snippet) between <pre> tags; with all HTML special characters properly escaped, of course.

    Here's an example to give you a good idea:

    /*
     * Custom Shortcode: Snippet
     * USAGE: [snippet id="188"], where id = Snippet (CPT) post's ID
     *
     * http://wpquestions.com/question/showChrono?id=8901
     */
    add_shortcode( 'snippet', 'bbcode_snippet' );
    function bbcode_snippet( $atts ) {
        extract( shortcode_atts( array(
            'id' => null,
        ), $atts ) );
    
        $post = get_post( $id );
        $content = $post->post_content;
        $content = trim( htmlspecialchars( clean_pre( $content ) ) );
    
        return '<pre><code>' . $content . '</code></pre>';
    }
    

    Related Links:

  4. To ease things a bit, create a custom meta box that displays the shortcode to use (e.g. [snippet id="120"]) on the edit screen of the custom post type (Snippets). So once you publish the snippet, you can copy the shortcode for embedding the code in your posts.

    add_action( 'add_meta_boxes', 'itsme_custom_meta_boxes' );
    function itsme_custom_meta_boxes() {
        add_meta_box(
    
            // HTML 'id' attribute of the edit screen section (AKA meta box)
            'itsme-snippet-shortcode-box',
    
            // Title of the meta box, visible to user
            esc_html__( 'Snippet Shortcode' ),
    
            // Callback function
            'itsme_snippet_shortcode_box',
    
            // Custom post type on whose edit screen to show meta box
            'aahank_vault',
    
            // The part of edit screen where the meta box should be shown
            'side',
    
            // Priority within the context where the meta box should show
            'high'
    
        );
    }
    
    // Function to display the meta box
    function itsme_snippet_shortcode_box() {
    
        global $post;
        $snippet_id = $post->ID;
        echo '<textarea rows="1" readonly="readonly" placeholder="Publish post first!" onclick="this.focus();this.select();">[snippet id="' . $snippet_id . '"]</textarea>';
    
    }
    

That's pretty much all we need. The syntax highlighting and stuff are extra so I am not getting into any further details.

Footnotes:

  1. Plugins and themes, like Rodolfo said, can easily mess up with the_content. Since we want to leave our code snippets untouched (unless WE want to), we are using a custom field to store our data, i.e. code snippets.

    Also, WordPress, by default, has a lot of filters on the_content, making it quite complex to do what we want. Using a custom field instead makes it a whole lot easier and straightforward.

Ideas:

  1. You can use a custom taxonomy called language or something to organize posts of the custom post type, and even use it to syntax highlight code.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.