How to Create an Image Selection Plugin for tinyMCE with Ruby on Rails

TinyMCE is a great content editor written in javascript from the folks at Moxiecode systems. We created a plugin for tinyMCE that allows users to select images from a selection of thumbnails instead of having to type a URL into a popup. This is part one of a two part tutorial on creating a custom tinyMCE plugin for image selection. Part one covers the basics of getting the plugin created. Part two will add image upload functionality. At the end of this tutorial you should have a tinyMCE popup window like this:

picture-3.png

Some administrative details before we get started. You will need a couple things before you can follow along:

  1. A working rails project and some images.
  2. TinyMCE installed and working – A tutorial for that is here


We start a plugin that comes with tinyMCE called advimage and modify it by creating a a tab called uploaded images that will contain thumbnail images that can be selected by clicking one of them. After being clicked the window should close and the image should appear in the tinyMCE enabled text box. We want to keep the ability to provide a URL and alt text manually via the general tab. With that out of the way, lets jump into the code!

  1. Navigate to the tiny_mce/plugins directory.
  2. make a copy of the advimage directory, I called mine ts_advimage,
  3. open ts_advimage/image.htm (or whatever you called your directory, from here on it will be ts_advimage)
  4. We need to add prototype.js to the head section because we are going to use AJAX to fetch the images from our controller:
     <script language="javascript" type="text/javascript" src="/javascripts/prototype.js"></script>
  5. look for the div with class=tabs and add code for a new tab:
    <div class="tabs"> <ul>
      <li id="dynamic_select_tab" class="current"><span><a href="javascript:mcTabs.displayTab('dynamic_select_tab','dynamic_select_panel');" onmousedown="return false;">Uploaded Images</a></span></li>
      <!--- existing plugin tab code --->
      <li id="general_tab"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{$lang_advimage_tab_general}</a></span></li>
      <li id="appearance_tab"><span><a href="javascript:mcTabs.displayTab('appearance_tab','appearance_panel');" onmousedown="return false;">{$lang_advimage_tab_appearance}</a></span></li>
      <li id="advanced_tab"><span><a href="javascript:mcTabs.displayTab('advanced_tab','advanced_panel');" onmousedown="return false;">{$lang_advimage_tab_advanced}</a></span></li>
     </ul>
    </div>

    The new tab is called dynamic_select_tab with a label of ‘Uploaded Images’ it will open dynamic_select_panel, and it is the current tab, i.e. the default when the form is rendered. The only thing you should change below the existing plugin tab code line is to remove class=”current” from general_tab.

  6. Create a new panel to house the images we are pulling from the controller.
    <div class="panel_wrapper">  <div id="dynamic_select_panel" class="panel current" style='overflow:auto'>
        <fieldset>
         <legend>Available Images</legend>
         <script type="text/javascript">//<![CDATA[
            new Ajax.Request("/manage_images",
              {asynchronous:true, evalScripts:true, method:'get'});     //]]>
         </script>
         <div id='dynamic_images_list'>
            Loading Images...<br />
            <img src='/images/loading.gif'>
        </div>
       </fieldset>
    </div>

    The ‘/manage_images’ parameter to Ajax.Request will need to be replaced with the path to your controller. It is important to note the style overflow:auto in the above code snippet. This will allow the area with images to grow (i.e. a scroll bar will appear as needed).

  7. Modify your image controller’s index action to respond to javascript. In the index action for the controller you need to make sure the resond_to block has a format.js section has format.js:
        respond_to do |format|
          format.html
          format.js
        end
  8. Create a new file in the controller’s views sub-folder called index.js with the following rjs code:
    page.replace_html :dynamic_images_list, :partial => 'show_image_list', :locals => { :images => @images }
    

    I chose to use a partial show_image_list, you can do the same and call it _show_image_list.rhtml.
    The page.replace_html call will convert the .rhtml from the partial to javascript and replace the dynamic_images_list div we created earlier with our list of images. The code for the partial:

    <% if images.size > 0 %>
    <ul>
      <% images.each do |image| %>
        <li>
          <a href="javascript:void(0)" onclick="<%="ts_insert_image('#{image.val.public_filename()}', '#{image.name}');" %>">
          <%= image_tag( image.val.public_filename(:thumb) ) %></a>
        </li>
      <% end %>
    </ul>
    <% else %>
     No Images Uploaded Yet.
    <% end %>

    The class variable @images contains all of the image models we would like displayed.

    QUICK TIP: If there is a chance that your image name or filename could have single quotes in it, you should escape them.

  9. An onclick event in the partial is used to take action when a user clicks an image: onclick=”<%=”ts_insert_image(‘#{image.val.public_filename()}’, ‘#{image.name}’);” %>”. This javascript function sets fields in the tinyMCE popup. Create this function in functions.js which lives in ts_advimage/jscripts/functions.js:
    function ts_insert_image(url, alt_text){
     var formObj = document.forms[0]; formObj.src.value = url;
     formObj.alt.value = alt_text;
     insertAction();
    }

    This function sets the fields in the general tab and closes the popup inserting the image into the text box.

  10. We are only a couple of steps away from being done, so hang in there. We need to tell tinyMCE about our new plugin so open up ts_advimage/editor_plugin.js. It is one long line (if you want to study the structure of the file look at editor_plugin_src.js). The code below is copied from editor_plugin_src.js so it is easy to understand. You must also make the changes in editor_plugin.js. What I changed is highlighted below:
    tinyMCE.importPluginLanguagePack('ts_advimage');
    var TinyMCE_ThriveSmartAdvancedImagePlugin = {
     getInfo : function() {
     	return {
     		longname : 'ThriveSmart Advanced image',
     		author : 'ThriveSmart LLC',
     		authorurl : 'http://www.thrivesmart.com',
     		infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage',
     		version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
     	};
     },
     getControlHTML : function(cn) {
     	switch (cn) {
     		case "ts_image":
     			return tinyMCE.getButtonHTML(cn, 'lang_image_desc', '{$themeurl}/images/image.gif', 'mceTSAdvImage');
     	}
     	return "";
     },
     execCommand : function(editor_id, element, command, user_interface, value) {
     	switch (command) {
     		case "mceTSAdvImage":
     			var template = new Array();
     			template['file']   = '../../plugins/ts_advimage/image.htm';
     			template['width']  = 480;
     			template['height'] = 480;
     			// Language specific width and height addons
     			template['width']  += tinyMCE.getLang('lang_advimage_delta_width', 0);
     			template['height'] += tinyMCE.getLang('lang_advimage_delta_height', 0);
     			var inst = tinyMCE.getInstanceById(editor_id);
     			var elm = inst.getFocusElement();
     			if (elm != null && tinyMCE.getAttrib(elm, 'class').indexOf('') != -1)
     				return true;
     			tinyMCE.openWindow(template, {editor_id : editor_id, inline : "yes"});
     			return true;
     	}
     	return false;
     },
  11. In the last line of editor_plugin.js and editor_plugin_src.js you need to change
    tinyMCE.addPlugin to tinyMCE.addPlugin(“ts_advimage”, TinyMCE_ThriveSmartAdvancedImagePlugin);
  12. Now we need to add our ts_advimage plugin to the tinyMCE menu bar
    	tinyMCE.init({
      	mode : "textareas",
      	editor_selector : "tiny_mce",
      	theme : "advanced",
      	plugins : "inlinepopups,ts_advimage,advlink,emotions,iespell,zoom,searchreplace,fullscreen,visualchars,youtube",
      	theme_advanced_buttons1 : "undo,redo,removeformat,|,bold,italic,underline,strikethrough,sub,sup,|,outdent,indent,bullist,numlist,hr,backcolor,|,link,unlink,charmap,emotions,ts_image,youtube,iespell,|,search,replace,|,cleanup,code,fullscreen,help",
      	theme_advanced_buttons2 : "",
      	theme_advanced_buttons3 : "",
      	theme_advanced_toolbar_location : "top",
      	theme_advanced_toolbar_align : "left",
      	theme_advanced_path_location : "bottom",
      	gecko_spellcheck : true,
      	extended_valid_elements : "a[name|href|target|title|onclick],img[class|src|style|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],span[class|align|style]",
      	theme_advanced_resizing : true,
      	theme_advanced_resize_horizontal : false
      });

    I’ve included the entire block for clarity, the important portions of the above code are in plugins : ts_advimage and
    theme_advanced_buttons1 : ts_image

Fire up your browser, clear the cache and try it out because now you have an image chooser plugin that works with tinyMCE.

I hope you enjoyed this post. Part two is out as well! will be out soon so stay tuned!

Explore posts in the same categories: User Interface

29 Comments on “How to Create an Image Selection Plugin for tinyMCE with Ruby on Rails”

  1. David Southwell Says:

    Great — building a new website and this is just what I need…

    Thank yuuuuuuuuuuuuu


  2. […] Two: How to Create an Image Selection Plugin for tinyMCE with Ruby on Rails 28Aug07 In the first part of our how-to we created a tinyMCE plugin with a dynamic image selector panel based on the advimage plugin. Now […]

  3. Liuba Says:

    Guys

    A great ‘How To’! Very useful and easy to follow…

    Thank you very much!!!!!!!!!!!

  4. Forgeten Says:

    Awesome howto however you might mention that you need to change the last line of advimage.js

    tinyMCE.addPlugin(“advimage”, TinyMCE_AdvancedImagePlugin);

    to

    tinyMCE.addPlugin(‘ts_advimage’, TinyMCE_AdvancedImagePlugin);


  5. I’ve tried all this but the button isn’t appearing in the toolbar for some reason. When I change to ts_image from image it simply disappears.

    Any ideas?

    Thanks.

  6. Dan Says:

    @Forgeten – Good catch, You are correct, I think you meant editor_plugin_src.js and editor_plugin.js though. I update the tutorial with the change. Thanks.

    @Aaron Pollock – Hmm, it sounds like tinyMCE can not find the new plugin. I just updated the tutorial (new step 11). If that does not work, there is sample code in part two, I would recommend looking at that, it might be easier than the html formatted stuff here.


  7. Dan, i think in editor_plugin_src.js case expression should contain “ts_advimage”:

    getControlHTML : function(cn) {
    switch (cn) {
    case “ts_image”:
    return tinyMCE.getButtonHTML(cn, ‘lang_image_desc’, ‘{$themeurl}/images/image.gif’, ‘mceTSAdvImage’);
    }
    return “”;
    }

    This is why button isn’t appered in Aaron’s case.

  8. Ian Lotinsky Says:

    Works like a charm. Thanks!

    On to Part Two…

  9. Mao Says:

    Thanks a lot, Very good job! Save me a lot of time. Cheers.

  10. Fran Says:

    Okay, this is probably the stupidest question ever, but what if you don’t already have an images controller? I’ve created one using the scaffold_resources generator but what is your images model/database like? Are you assuming use of a plugin to control files (I’m currently using acts_as_attachment for file uploads elsewhere in my site?). It would be really helpful if you briefly mentioned how you’ve set up your images controller and model!

  11. Ian Drysdale Says:

    Excellent work, thank you!

    I’ve started adapting this for version 3 of TinyMCE, however I’m running into some problems. I was wondering if you’ve already done this?

  12. vim_commando Says:

    I am trying to wrap my head around how to get this to work with sub-directories. I would love to have a separate folder for each :controller with subdirectories for each :id. I have almost 400 images in my public/images folder, so loading them all from one directory is an aweful way to handle it.

    The problem I’m seeing is that the call to the manage_images controller is hard-coded into an Ajax request in the TinMCE plugin’s image.htm. So when it is called it cannot access the params[] array to build a url.

    Any thoughts?

  13. Dan Says:

    @Ian, I haven’t tried version 3 of TinyMCE, what kind of problems are you having?

    @vim_commando, attachment_fu can create sub directories for images based on the id of the model (I can’t remember if that is the default or not). You could change the javascript in the image.html file and setup routes such that if you have two controllers controller1 and controller2 you would have URI’s like controller1/manage_images or controller2/manage_images. In the manage images controller you could take action based on the request path.

  14. vim_commando Says:

    I came to realize after-the-fact that this entire howto assumes you are using attachment_fu to handle your image uploads. It should be noted as a prerequisite, as the first reference to it was in the response to my question >.<

  15. alexey Says:

    Great tutorial !!
    I saw missing in paragraph 6..

  16. Ron Says:

    hey… thanks so much for a great tutorial, this worked great. nice to see something like this actually cover enough gotchas to get people up and running too.


  17. […] How to Create an Image Selection Plugin for tinyMCE with Ruby on Rails « Hack’d – Great Stuff for… (tags: plugin javascript editor tinymce rubyonrails) […]

  18. Darren Says:

    shaaaaaaaweeeeet! works fine except with version 3 and up.

  19. venkat Says:

    Hi…
    Just the kind of thing I was looking for. Followed right from the beginning but failed to get the “Uploaded Images” tab even. Also tried some hacks in others comments but nothing helped me.. where could i go wrong?

  20. ThillaiArasu Says:

    nice blog for image upload in tiny mce for rails,your demo project is good .Thanks for it


  21. […] How to Create an Image Selection Plugin for tinyMCE with Ruby on Rails « Hack’d – Great Stuff for… […]


  22. […] you’ll want to be able to add images inline style. Here’s a useful tutorial for […]

  23. aaron Says:

    I’m having an issue getting the icon to show up in the TinyMCE menu. I looked through the comments and made the changes that people suggested and it is not showing up. Maybe I’m missing in the “editor_plugin.js”. Can someone post their “editor_plugin.js”?

    thanks
    aaron

  24. Brian Says:

    I’m trying to embed a form into TinyMCE and it strips out the form tags… any suggestions??? Thanks.

  25. James Says:

    In step 6 you are missing a closing div tag for

  26. James Says:

    Oh whoops, I didn’t realize you are supposed to put the new “dynamic_select_panel” div inside of the existing “panel_wrapper” div.

  27. wouter Says:

    Does anyone know a tutorial for uploading and adding files?

  28. NerdMan Says:

    If I would like to use Tinymce version 3.

    How would it be done?


Leave a reply to NerdMan Cancel reply