4

To Wordpress' visual editor (TinyMCE), I would like to add a button that adds four non-breaking spaces when clicked, like this:

    

I found some plugins that add buttons to the HTML editor, but not to the visual editor (this one for e.g).

or in general, it would be nice to know if there's a plugin or a codable (but simple) alternative to add custom buttons to the visual tinyMCE editor.

7
  • do you want to add tiny-mce button?? Jan 3, 2012 at 15:09
  • @phantom.omaga yes, that's right.
    – its_me
    Jan 3, 2012 at 15:18
  • I hope I'm not out of place asking, but did the comment by @Alexey not work? If so, what was wrong? The reason I ask is I find your question interesting and want to implement something similar, but don't want to waste time trying things willy-nilly if a given answer does not work.
    – akTed
    Jan 12, 2013 at 13:43
  • @AKTed Actually, the last time I tried, I could not get it to work. I wasn't persistent enough then as I had to get things done. Now I am back to get this done. :)
    – its_me
    Jan 12, 2013 at 13:49
  • 1
    You are welcome, although I would not rely on the upvote as you are considering of when each answer were posted. I would instead ask you to review the code itself and see which one functions the best for your question.
    – Christine Cooper
    Jan 17, 2013 at 17:10

2 Answers 2

6
+50

I already demonstrated how you can achieve such thing in this question but I can explain it here again with your request. I have tested this in Wordpress 3.5 and it works elegantly. To avoid this answer being the length of a thesis, I have added comments in all codes to help you understand what each function does and I highly recommend that you read and thoroughly understand them.

Firstly, in your theme folder, add a folder named admin and inside it, create file class.new_tinymce_btn.php with code:

<?php
//wpex_37798_christine_cooper
//class start
class add_new_tinymce_btn {

public $btn_arr;
public $js_file;
/*
 * call the constructor and set class variables
 * From the constructor call the functions via wordpress action/filter
*/
function __construct($seperator, $btn_name,$javascrip_location){
  $this->btn_arr = array("Seperator"=>$seperator,"Name"=>$btn_name);
  $this->js_file = $javascrip_location;
  add_action('init', array(&$this,'add_tinymce_button'));
  add_filter( 'tiny_mce_version', array(&$this,'refresh_mce_version'));

}
/*
 * create the buttons only if the user has editing privs.
 * If so we create the button and add it to the tinymce button array
*/
function add_tinymce_button() {
   if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') )
     return;
   if ( get_user_option('rich_editing') == 'true') {
     //the function that adds the javascript
     add_filter('mce_external_plugins', array(&$this,'add_new_tinymce_plugin'));
     //adds the button to the tinymce button array
     add_filter('mce_buttons', array(&$this,'register_new_button')); 
   }
}
/*
 * add the new button to the tinymce array
*/
function register_new_button($buttons) {
   array_push($buttons, $this->btn_arr["Seperator"],$this->btn_arr["Name"]);
   return $buttons;
}
/*
 * Call the javascript file that loads the 
 * instructions for the new button
*/
function add_new_tinymce_plugin($plugin_array) {
   $plugin_array[$this->btn_arr['Name']] = $this->js_file;
   return $plugin_array;
}
/*
 * This function tricks tinymce in thinking 
 * it needs to refresh the buttons
*/
function refresh_mce_version($ver) {
  $ver += 3;
  return $ver;
}

}//class end
?>

This code will add custom buttons to the visual editor.

Next, in your theme folder, create these folders adminjs/buttons and inside, create this JS file spacebutton.js with code:

(function() {
    tinymce.create('tinymce.plugins.nextpage', {
        init : function(ed, url) {
            ed.addButton('nextpage', {
                title : 'Space Button',
                image : url+'/images/btn_spacebutton.png',
                onclick : function() {                    
                    var prompt_text = "&nbsp;&nbsp;&nbsp;&nbsp;";
                    var caret = "caret_pos_holder";
                    var insert = "<p>" + prompt_text + " &nbsp;&nbsp;&nbsp;&nbsp;</p> <span id="+caret+"></span>";
                     ed.execCommand('mceInsertContent', false, insert);
                     ed.selection.select(ed.dom.select('span#caret_pos_holder')[0]); //select the span
                     ed.dom.remove(ed.dom.select('span#caret_pos_holder')[0]); //remove the span
                }
            });
        },
        createControl : function(n, cm) {
            return null;
        },
    });
    tinymce.PluginManager.add('nextpage', tinymce.plugins.nextpage);
})(); 

You will need to add an image for the button (/images/btn_spacebutton.png). The above code is a rather simple javascript function for what happens when the button is pressed.

Now you will need to load this button class by adding this to your functions file:

//load custom buttons class
require_once (TEMPLATEPATH . '/admin/class.new_tinymce_btn.php');
//create an instance of the class
$t = new add_new_tinymce_btn('|','nextpage',get_bloginfo('template_url').'/adminjs/buttons/spacebutton.js');

That is it. You should find your new custom button in the visual editor. Whenever you want to add more custom buttons, just add a new JS file with the button function and load the button class as demonstrated above.

5

TL;DR, code is at the bottom.

OK, this should work for you, but it is a beta. It works for me, but I haven't done any kind of rigorous testing. One thing, it doesn't output four consecutive &nbsp; entities; it does a stuttered &nbsp; [space] &nbsp; [space], but at least it keeps them as-is when switching back-and-forth between Visual & Text modes. It only works in Visual mode, I haven't taken the time to figure out how to make it work in Text mode.

It comes in two files, name as you see fit. I used the highly imaginative name: 4Spaces. :) The TinyMCE button is on the top-row, far-right, of the editor. It'll show whatever your flavor of browser shows for non-existent images. That's easily changed in 4spaces.js, line 8:

image   : url + '/' + 'YOUR_IMAGE_HERE.png'

Change `YOUR_IMAGE_HERE.png' to your image file, relative to the two files, or use an absolute URI like so:

image   : '/path/to/image/YOUR_IMAGE_HERE.png'

or

image   : 'http://example.com/path/to/image/YOUR_IMAGE_HERE.png'

I've commented and/or left some existing comments throughout the PHP, the JavaScript comments are sparse. You'll see in the PHP header section where the PHP code came from and kind of where the JavaScript originated.

Two notable credits, both listed in the PHP header: this WordPress.SE Answer that provided the code that stops TinyMCE (or WordPress, not sure which) from stripping out the spaces, and the link provided in the prior answer by @Alexey, which, while not critical, helped me stumble onto the JS solution.

I couldn't make the code in that link work, but eventually came back to it and found the nugget that brought it all together (with some tweaking, to be sure).

I think that about sums everything up. Here's the code:

4spaces.php

<?php
/* Plugin Name: 4Spaces
 * Version: 0.1.0
 * Author: AK Ted
 * Author URI: http://akted.com
 * License: GPLv2 or later
 *
 *
 * PHP code adapted & refined from the following two sources:
 * WordPress Codex - http://codex.wordpress.org/TinyMCE_Custom_Buttons#Loading_a_TinyMCE__Plugin
 *
 * WordPress Answers (Stack Exchange) - https://wordpress.stackexchange.com/questions/54398/how-can-i-stop-tinymce-from-converting-my-html-entities-to-characters#answer-54480
 *
 *
 * The JavaScript arose from a lot of trial-and-error, with code [beat into submission...er, I mean] inspired by both of the following sources:
 * tinymce wiki - http://www.tinymce.com/wiki.php/Creating_a_plugin
 * &
 * brett terpstra - http://brettterpstra.com/2010/04/17/adding-a-tinymce-button/
 *
 */

new akt_4spaces();

class akt_4spaces {
    function __construct() {
        add_action('admin_init', array($this, 'init'));
    } // function __construct

    // callback for init
    // sets all the hooks only if user has capability & rich_editing is true 
    function init() {
        // Don't bother doing this stuff if the current user lacks permissions
        if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') ) {
            return;
        }

       // Add only in Rich Editor mode
       if ( get_user_option('rich_editing') == 'true') {
            add_filter('mce_buttons', array($this, 'add_button'));
            add_filter('mce_external_plugins', array($this, 'add_tinymce_plugin'));
            add_filter('tiny_mce_before_init', array($this, 'preserve_entities'));
       }
    } // function init


    // callback for mce_buttons filter
    // adds button to TinyMCE
    function add_button($buttons) {
        array_push($buttons, 'separator', 'four_spaces');
        return $buttons;
    } // function add_button

    // callback for mce_external_plugins filter
    // attaches the JavaScript file to TinyMCE
    function add_tinymce_plugin($plugin_array) {
        $plugin_array['four_spaces'] = plugins_url('/', __FILE__) . '4spaces.js';
        return $plugin_array;
    } // function add_tinymce_plugin


    // callback for tiny_mce_before_init
    // stops TinyMCE (WordPress?) from automatically converting &nbsp; entities
    function preserve_entities( $initArray ) {
        // The odd entries are the entity *number*, the even entries are the entity *name*. If the entity has no name,
        // use the number, prefixed with a hash (for example, the service mark is "8480,#8480").
        $initArray['entities'] = '160,nbsp,' . $initArray['entities'];
        return $initArray;
    } // function preserve_entities

} // class akt_4spaces

4spaces.js

(function() {

    tinymce.create('tinymce.plugins.four_spaces', {
        init : function(ed, url) {
            // Register four_spaces button
            ed.addButton('four_spaces', {
                title   : '4Spaces',
                image   : url + '/' + 'YOUR_IMAGE_HERE.png',
                onclick : function() {
                    ed.execCommand(
                        "mceInsertContent",
                        false,
                        "&nbsp;&nbsp;&nbsp;&nbsp;"
                    );
                }
            });
        }
    });

    // Register plugin
    tinymce.PluginManager.add('four_spaces', tinymce.plugins.four_spaces);

})();

Edit: I forgot to mention, for those who don't know, put both of those files into a directory under /wp-content/plugins/ (default path). It should look something like /wp-content/plugins/4spaces/ or whatever name you decide to call it, then activate it in Admin.

P.S. - I'm relatively new to OOP, so welcome any and all criticisms, advice, etc., from anyone viewing this answer.

4
  • 1
    As it runs only in admin, instead of the init hook, admin_init. Maybe simply if ( get_user_option('rich_editing') ) {/*etc*/}. For the image, wp_localize_script, so you feed the image to JS through PHP. As final touch, a wiki image, so the icon works out of the box. +1 Nice write-up :)
    – brasofilo
    Jan 16, 2013 at 1:45
  • Thanks, @brasofilo! I'd heard of wp_localize_script but hadn't yet tried understanding anything about it. Your link was awesome! Very simply explained. And I've fixed the init. I can't believe I missed that one!
    – akTed
    Jan 16, 2013 at 1:59
  • And re: 'rich_editing', that's a good idea, but I might leave it in for reference. It makes it obvious that it should only work if you have enough juice, i.e., an editor or administrator. But for a production plugin I would definitely remove it. Thanks for the critique! :)
    – akTed
    Jan 16, 2013 at 2:03
  • The localize trick is really cool and easy, I believe this is the origin of the trickery. Don't worry, with massive daily doses of code, I think one can miss many things :P
    – brasofilo
    Jan 16, 2013 at 2:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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