Welcome, visitor! Log in
 

KB Plugins blog

The best Wordpress plugins are free

Announcing: KB Backtick Comments

My latest plugin is a simple piece of code that replicates the `code in backticks` function used in the wp.org support forums . It works in posts and comments, although it’s mainly intended for the comments section (because there are already many, many, many plugins for pasting code into posts).

(If you use it in posts, you must use the code view–it will not work in the visual editor, I assure you. By that, I mean that you need to have the visual editor disabled in your profile. Otherwise, when you go back later to edit the post, it will try to open in the visual editor first, and it will get screwy.)

So now when folks are asking me questions about my plugins, they can just insert an error message or a piece of code right into their comment with no difficulty at all. How lovely.

Appropriately enough, I’ll post version 0.1 of the code right here using my plugin as a demo.

Important: The code pasted below is version 0.1. The current version of the plugin can be downloaded here .

Here goes:

<?php
/*
Plugin Name: KB Backtick Comments
Plugin URI: http://adambrown.info/b/widgets/category/kb-backtick-comments/
Description: Include code in posts or comments by putting it in `backticks`
Author: Adam Brown
Author URI: http://adambrown.info/
Version: 0.1
*/

// OPTIONAL SETTINGS

// To control how this plugin's code blocks look, modify the style declarations at the very end of this file. If all you want to change is the colors, though,
// you can do that here.
define('KB_BACKTICK_BG', '#e3e3ff');	// what background color will code blocks have? (Affects BOTH code blocks AND inline code.)
define('KB_BACKTICK_BORDER', '#88a');	// what border color will code blocks have? (Does not affect inline code--code blocks only.)


/*
	WHAT DOES THIS DO?
	Allows your commenters to paste code in their comments by enclosing it in `backticks`, like in the wp.org support forums.
	Also works in posts, but only if you have the visual editor disabled (in your profile).

	TIPS
	* If you need to include backticks in the code you paste, escape them with a backslash: \`
	* If, for some odd reason, you need to include a \` in what you post, double escape it (two backslashes): \\`

	LICENSE
	THIS IS BETA SOFTWARE. I have only tested it on my platform under limited circumstances. In other words, I wrote this to do something specific on my site. I hope
	it works for you too, but I don't promise anything. You use this entirely at your own risk. You are welcome to modify this code, but please refer people to my site
	(or WP.org if it gets listed there) to download the plugin rather than redistributing it from your own site. I mean really, I gave this to you for free, so the least you 
	can do is let me have a shot at getting some advertising revenue by sending people to my site to get the code.

	DEV NOTES
	This plugin works in three stages.
	1) 	Before a comment/post gets saved, we look for `...` and replace it with `<code>htmlspecialchars(...)</code>` (preserving the backticks, as you see, to make the block
		of code recognizable to later stages of the plugin)
	2)	Before a comment/post gets displayed, we remove the backticks and, if the code is a separate paragraph, wrap it in <pre> tags. (This is a distinct stage
		from the first one because <pre> tags aren't allowed in comments. An earlier version of this would add <pre> to the allowed list of tags, but that was buggy.)
	3)	When a comment/post gets opened for editing, we look for `<code>...</code>`, remove the <code> tags, and apply htmlspecialchars_decode() to the contents.

	SOMETHING WEIRD
	WP will not let you post a comment that contains certain HTML entities, including &#39;. So we strip all entities in stage 1 and reformat them as &#95;&#95;abENT&#95;&#95;###; (where
	### is the entity code), then in stages 2 and 3 we turn that back into &###; for display.
*/


// STAGE 1 FILTERS: BEFORE A COMMENT/POST GETS SAVED
$ab_bslash = '\\'; // makes life easier when you're trying to tell \ from \\. (This is a single backslash.)

// looks for code blocks (in backticks), sends to callback
function ab_backtickFilter($content){
	if (false===strpos($content,'`')) // save CPU resources
		return $content;
	global $ab_bslash;

	// check for special problems:
	// on the offchance that somebody's code contains '&#95;&#95;abENT&#95;&#95;' in it, we should replace the underscores with entity #95 (makes posting this file possible, for example):
	$content = str_replace('&#95;&#95;abENT&#95;&#95;', '&#95;&#95;abENT&#95;&#95;#95;&#95;&#95;abENT&#95;&#95;#95;abENT&#95;&#95;abENT&#95;&#95;#95;&#95;&#95;abENT&#95;&#95;#95;', $content);
	// allow for escaped backticks. Remember that posting vars adds slashes, so we check for two slashes, not just one:
	$content = str_replace($ab_bslash.$ab_bslash.'`', '&#95;&#95;abENT&#95;&#95;#96;', $content);
	// but just in case, we'll also look for just one slash:
	$content = str_replace($ab_bslash.'`', '&#95;&#95;abENT&#95;&#95;#96;', $content);

	// do it
	$content = preg_replace_callback( '|`([^`]+)`|Us', 'ab_backtickCallback', $content );
	return $content;
}
function ab_backtickCallback($matches){ // callback
	$r = trim($matches[1]);
	$r = stripslashes($r); // do this before converting any backslashes to entities (see below)
	$r = htmlspecialchars($r);

	// inexplicably, if $r contains &#039; the comment won't post, so we can't use htmlspecialchars(...,ENT_QUOTES). (More broadly, this is why we have that ab_removeEnts() function.)
	// But we also can't just leave the apostrophes untouched, or WP texturizes them into "friendly" apostrophes. So we turn them into &apos; even though &apos;
	// isn't recognized by IE. We'll turn the &apos; back into &#039; with our stage 2 filters.
	$r = str_replace( "'", '&apos;', $r );

	// some other cleanup:
	global $ab_bslash;
	$r = str_replace( $ab_bslash, '&#92;', $r ); // convert backslashes to entities (lest WP have problems)
	$r = str_replace( '.', '&#46;', $r ); // lest WP turn ... into an elipses entity
	$r = str_replace( '/', '&#8260;', $r ); // break links in the code (lest WP underline them) by replacing slashes with fraction entities (which are similar to slashes)

	// wrap in tags:
	$r = '`<code>' . nl2br($r) . '</code>`';

	// remove all entities, lest WP refuse to post the comment (not necessary for posts, but very necessary for comments)
	$r = ab_removeEnts($r);
	return $r;
}
// Our hooks. Do it early, so kses and wp_texturize haven't hit yet--priority of 5
add_filter('content_save_pre', 'ab_backtickFilter', 5);
add_filter('pre_comment_content', 'ab_backtickFilter', 5);


// STAGE 2 FILTERS: BEFORE DISPLAYING A COMMENT/POST:

function ab_backtickStage2($content){
	if (false==strpos($content,'`<code>')) // save CPU resources
		return $content;

	// first we look for blocks of code and apply <pre> tags:
	$content = preg_replace_callback('|<p>`<code>(.*)</code>`</p>|Us', 'ab_backtickCallback2', $content );

	// then we look for inline code and remove the backticks:
	$content = preg_replace('|`<code>(.*)</code>`|Us', '<code class="backtick">$1</code>', $content );

	// fix the entities that we destroyed in stage 1
	$content = ab_restoreEnts($content);

	// fix our apostrophes (see notes in ab_backtickCallback())
	$content = str_replace( '&apos;', "&#039;", $content );

	// fix our slashes (see notes in ab_backtickFilter())
	$content = str_replace( '&#8260;', '/', $content );

	return $content;
}
function ab_backtickCallback2($matches){
	// change paragraph tags to <pre> tags
	$r = '<pre class="backtick"><code>'.$matches[1].'</code></pre>';

	// if this is a multi-line block of code, there might be </p><p> inside it. Let's get rid of those. Also, WP likes to turn newlines into <br />, so let's kill those too.
	$find = array('<p>', '</p>', '<br />');
	$replace = array("\n", '', '');
	$r = str_replace($find, $replace, $r);

	return $r;
}
// Our hooks. Do it late in the game (after WP is done texturizing) with a very high priority:
add_filter('comment_text', 'ab_backtickStage2', 1001);
add_filter('the_content', 'ab_backtickStage2', 1001);

// STAGE 3 FILTERS: BEFORE OPENING A COMMENT/POST FOR EDITING:

// we undo everything in the stage 1 filter (and in the opposite order)
function ab_backtickStage3($content){
	if (false===strpos($content,'`')) // save CPU resources
		return $content;
	$content = ab_restoreEnts($content);
	$content = preg_replace_callback( '|`<code>(.*)</code>`|Us', 'ab_backtickCallback3', $content );

	// check for escaped backticks outside the code blocks:
	$content = str_replace('`', '\`', $content );

	return $content;
}
function ab_backtickCallback3($matches){
	$r = $matches[1];

	// WP likes to convert newlines in code blocks into <br />. Let's kill them all (the <pre> tag only needs newlines)
	$r = str_replace( '<br />', '', $r );

	// undo stage 1:
	// fix:			slashes,		backslashes,		apostrophes,		(escaped) backticks,	periods
	$find = array( 	'&#8260;',	'&#92;',	'&apos;',	'`',		'&#46;' );
	$replace = array('/',		'\\',		"'",		'\`',			'.');
	$r = str_replace( $find, $replace, $r );
	$r = htmlspecialchars_decode($r);

	$r = '`'.$r.'`'; // restore backticks

	return $r;
}
add_filter('format_to_edit', 'ab_backtickStage3', 5); // one hook for both comments and posts

// OTHER STUFF

// used to remove and restore the HTML entities, which WP doesn't like to see in comments:
function ab_removeEnts($s){ // reformat all entities so WP won't screw with them
	return preg_replace('|&([0-9a-zA-Z#]{1,5});|', '&#95;&#95;abENT&#95;&#95;$1;', $s);
}
function ab_restoreEnts($s){ // fix all the entities
	return preg_replace('|&#95;&#95;abENT&#95;&#95;([0-9a-zA-Z#]{1,5});|', '&$1;', $s);
}

// Replicate a PHP 5 function for all those PHP 4 types out there:
if (!function_exists("htmlspecialchars_decode")){
    function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT){
        return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
    }
}

// OPTIONAL. 
// You'll want this in your style, or something like it, so that code blocks look good. I've made it easy to change the settings via an option at the top of
// this file, or you can just edit this function directly.
function ab_backtickStyles(){
	// inline code looks like this:	<p>blah blah blah <code class="backtick">code code code</code> blah blah blah</p>
	// blocks of code like like this:	<pre class="backtick"><code>code code code code</code></pre>
	echo '
		<style type="text/css"><!--
			.backtick{background:'.KB_BACKTICK_BG.';color:#000;}
			pre.backtick{overflow:auto;max-height:30em;padding:1em;background:'.KB_BACKTICK_BG.';border:'.KB_BACKTICK_BORDER.' 1px solid;}			
		// -->
		</style>
	';
}
add_action('wp_head', 'ab_backtickStyles');

?>

Important note: This is very, very pre-beta. I’ll be testing it as people use it here on my site. It might mess things up in the code. If you notice that it does mess something up, please let me know, although I don’t know whether I’ll have time to fix it. (Patches are much more appreciated than bug reports, although bug reports are also helpful.)

5 Comments

  1. Posted November 29, 2007 at 1:51 pm | Permalink

    A quick demo. Put code in backticks in your comments. You can do it inline or as a block. Either way, it gets wrapped in <code> tags, but if it’s a block, it also gets <pre> tags.

    Example of <?php // look, I'm inline ?> inline code.

    <?php // I'm an example of a code block. ?>

    Easy as that. The code can be in any language, and it can be very long, as this example shows (my stylesheet):

    /*main2.css overrides*/
    #content{padding-right:0em;}
    .siteNotice{margin-right:2em;}
    /*blog name, desc*/
    h2 a{color:#000;}
    h3#blogdesc{color:#00a;text-align:center;margin:-1.2em 0 2em;padding-top:0;}
    /*table#layout*/
    #layout{width:100%;border-collapse:collapse;}
    #layout td{vertical-align:top;}
    #layout td#sidebartd{width:14em;}
    /*side*/
    #sidebar{border-left:#a00 2px solid;padding:2em 1em 5em;margin:0 0 0.5em 1em;color:#777;background:#eeb;}
    #sidebar h3{text-align:center;padding:0;margin:0 0 0.5em;}
    #sidebar ul#sidebarMain{margin:0;padding:0;list-style:none;font-size:90%;}
    #sidebar ul{margin:0;padding:0 0 0 1em;}
    #sidebar li.widget{margin:0 0 3em;}
    #sidebar a{color:#33f;}
    #sidebar a:hover,#sidebar a:active{color:#3a3;}
    /*alerts*/
    div#blogrefresh{background:#fdb;border:1px solid #db9;color:#000;margin:0 0 2em 0;padding:0.3em 1em}
    /*posts*/
    .post{margin:0 0 4em;}
    .post h2{text-align:left;font-size:150%;margin:0 0 1em;padding:0;}
    .post-date{text-align:center;padding:0 2em 0 0;vertical-align:top;}
    .post-date-day{font-size:2em;position:relative;color:#586;}
    .post-date-month{font-size:1.2em;color:#586;}
    .post-date-year{font-size:1em;color:#586;}
    .post-header h2 a{color:#000;}
    .post-footer{text-align:right;border-top:dotted 1px #bba;font-size:75%;margin:1em 0 0;color:#887;}
    .post-footer a{color:#88f;}
    /*comments*/
    .comments h4{color:#865;border-top:double 3px;margin:3em 0 1em;}
    .comments h4#comments{margin-bottom:3em;}
    .comments h4 a{color:#44f;}
    .comment{border:2px solid #bbd;margin:0 0 2em 1em;padding:0.5em 1em}
    .gravatar{width:60px;height:60px;float:left;margin:-1em 1em 0.3em 0;border:solid 1px #33a;border-right:solid 2px #007;border-bottom:solid 2px #007;}
    .comNum{float:right;color:#ccc;font-size:200%;margin-left:1em;}
    /*.comOdd{background:#ffefde;}
    .comEven{background:#effade;}*/
    .myComment{border:2px solid #007;background:#fff;}
    .comBy{font-weight:bold;}
    .comBy a{color:#007;}
    .comBy span{color:#777;}
    .comWhen{font-size:75%;color:#777;}
    /*commentform*/
    .comGuide{border:solid 2px #007;padding:0.3em;background:#e3e3ff;}
    .comGuide ul{font-size:85%;}
    .ltgt{color:#aaa;}
    .comments textarea{width:100%;height:9em;}
    .noMoreComments{margin-top:3em;font-style:italic;}
  2. Posted November 29, 2007 at 4:18 pm | Permalink

    Very neat! Useful for posting code in different tutorials :)

  3. Posted January 24, 2008 at 1:53 am | Permalink

    Hi,

    I’m using this plugin for my website, but it looks like something is giong wrong when there is some text before or after the code…

    Something like this doen’t work on my website :

    text
    code in backticks
    text

    If I let a white line after the text, it’s ok… Like this is working:

    text

    code in backticks

    text

    Do you think that you can fix easylly this problem

  4. Posted January 28, 2008 at 9:42 am | Permalink

    Oddly enough, you can see that it worked both ways in your comment. Maybe it’s only a problem in posts, not comments?

    I’m swamped right now with my real work, but I’ll take a look at this next time I update the plugin. Don’t know how soon that will be.

  5. Posted April 6, 2008 at 1:22 pm | Permalink

    Nice plugin. If you are curious, I struggled with the same single quote problem in a plugin of mine, and discovered why WordPress chokes on single quotes. It is because the “blacklist” check doesn’t allow any ASCII entities below 128, except for the ampersand, marking them as spam. (I went through my blog database and discovered hundreds of comments that I had never seen because of that.)

    This code happens in wp-includes/comment.php:

    if ( 38 == $char )
    continue; // Unless it's &
    if ( $char < 128)
    return true;

    Anyway, your solution to the single quote problem is great, and I “borrowed” it for my plugin.