Hijack the WordPress Media Gallery

I have a friend that wants to update a banner on his WordPress blog homepage without changing the template files. I tell him I'd be glad to help him out and start working on a plugin that provides a custom administration page to allow for this. As I am a firm believer in the power of a lazy coder to get things done faster and better, my first thought was: "Why don't I just see if I can hijack the 'Select Image From Gallery' page to do this?" After all, it just pops up into an Iframe page and tells post editer to insert an image. Hijacking that process for my own ends shouldn't take me but a few minutes...and a lot less time than trying to build my own media selector. Like nearly all the content on my site, if you are reading this, I wasn't able to find my answer elsewhere.

First, we will examine how the media library iframe is launched from the post editor and how we can duplicate that in our plugin. So lets crack open the page source and see what we can find:
<a href="media-upload.php?post_id=-1249588072&type=image&TB_iframe=true" id="add_image" class="thickbox" title='Add an Image' onclick="return false;"><img src='images/media-button-image.gif' alt='Add an Image' /></a>
Looks like a standard hyperlink, with some extra information in the tag. What first jumps out at me is the onclick="return false;". By having an onclick return false in a hyperlink, you ensure that a browser with javascript will never open the link when it is clicked. Sounds pretty silly until you remember that WordPress is a big user of JQuery. From there, we know to look at the class of the hyperlink, class="thickbox". This is a reference to the Thickbox 3.1 library found at /wp-includes/js/thickbox/thickbox.js. This is a library to load an iframe inside an overlay with the background greyed out. Further, it uses the href of the anchor tag to know where to pull the iframe content from.

A quick check of the source on our custom page shows that Thickbox is being loaded in the footer, so we do not have to call it directly from our custom wp-admin page. I can just call up the media library with some html code: <a href="media-upload.php?type=image&TB_iframe=true" class="thickbox" title="Select A Banner Image From Gallery" onclick="return false;" class="thickbox"> <img id="banner_image" src="<?php echo(htmlentities($banner_image)); ?>" width="450" height="25" /> </a> <input type="hidden" id="banner_image_input" name="banner_image" value="<?php echo(htmlentities($banner_image)); ?>" />
The snippet displays an image with the source of $banner_image that can be clicked on to open the Media Gallery iframe. I have also added a hidden form input to store the resulting information passed back from the media library. Once done, I now have an image on my admin page that I can click on to call up the media library.

We're half-way there now! All we have to do next is figure out how the media library sends an image to the post editor. This part isn't as easy. Thanks to thickbox, the media library opens up in an inline-iframe. This means that we can't just view the page source and find our answer. Instead, the only clue we have to hunt for is the "Insert into Post" button. A quick file search turns that up in /wp-admin/includes/media.php. From there, we discover the onclick of the button, onclick="addExtImage.insert();", which is thankfully also in the media.php file. At the end of the function, we see exactly what we are looking for: var win = window.dialogArguments || opener || parent || top; win.send_to_editor(html);
The media library calls the function send_to_editor in the scope of the window that opened the media library--our admin page! We can add our own function into the admin page and collect the html that the media library sends us: function send_to_editor(html) { alert(html); tb_remove(); }
Once done, we can open the media library from our admin page and select an image. When we hit the "Insert into Post" button, we get an alert with the following content:
<img src="http://127.0.0.1/devel/wp-content/uploads/2009/09/banner.jpg" alt="banner" title="banner" class="alignnone size-full wp-image-494" />
All we have to do now, is strip out everything but the image source and updates the form. First, the function eliminates all of the text returned except the image source. It then updates the banner image and the hidden input value that I am using in my form: function send_to_editor(html) { var source = html.match(/src=\".*\" alt/); source = source[0].replace(/^src=\"/, "").replace(/" alt$/, ""); $("#banner_image").attr('src', source); $('#banner_image_input").attr('value', source); tb_remove(); }
I fire up the admin page a final time, and everything works! I now have a custom admin page that allows my friend to update a banner on his wordpress site using the media gallery.
Attachments: hijack
This entry was posted in Coding Blog and tagged , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

21 Comments

  1. Posted December 2, 2009 at 7:30 pm | Permalink

    very interesting solution. i wanted to accomplish the same and also came to the point where I was able to tell that the send_to_editor function sends the content, but i am not able to utilize this knowledge. where did you put your own send_to_editor function to pull this of? and how did you manage to not get a javascript error when defining the function a second time?

    Or did you just overwrite the wordpress function? (this is unfortunatley not an option for me, this needs to work as a plugin)

    anyways thanks for the interesting article =)

  2. Posted December 2, 2009 at 8:08 pm | Permalink

    The send_to_editor function is defined in only on pages that use the post/page editor. Unless you are implementing this on that page, the function does not exist.

    If you are intending to make use of this through a custom admin page, you just need to define the function. The examples I used above were built for usage in this manner.

    If you are intending to make use of this on the post/page editor, you would need to wrap the existing send_to_editor in an anonymous function:
    window.original_send_to_editor = window.send_to_editor;
    window.custom_editor = true;
    window.send_to_editor = function(html){
    if (custom_editor) {
    //perform your custom handling
    } else {
    window.original_send_to_editor(html);
    }
    };

    I currently use the media gallery through custom admin pages that I have implemented in a plugin.

  3. Posted December 2, 2009 at 8:58 pm | Permalink

    Thanks a lot, that worked out great :)

  4. Posted December 6, 2009 at 2:35 am | Permalink

    I had a hard time following this tutorial. Do you have an example of the code implemented that i can examine to make sure i am doing it correctly?

    Thanks,
    Brennan

  5. Posted December 7, 2009 at 3:19 am | Permalink

    I have added an example plugin as an attachment to this post. I hope it better explains how to do this.

    Link is here: http://www.braindonor.net/wp-content/uploads/2009/08/hijack.zip

  6. Posted December 7, 2009 at 6:28 am | Permalink

    Okay now it makes sense, it worked perfect. Now what about adding it to the post/pages editor. Can i use it on a custom input that i have placed inside that page? What would it look like?

  7. Posted December 7, 2009 at 3:47 pm | Permalink

    I haven’t build any custom inputs for the post editor yet. You should be able to do the hijacking so long as you wrap the current send_to_editor so that it can tell if it is being called from the wyswyg or from your custom input.

  8. Posted December 11, 2009 at 2:47 am | Permalink

    Okay yeah i got it to work. What i did was create a separate .js file for the javascript function. And used the wp_enqueue_script function to call it as such

    function my_init_method() {
    wp_enqueue_script("change_editor", get_bloginfo('template_directory')."/js/change_editor.js", array(), "", true);
    }
    add_action('init', my_init_method);

    Otherwise the original send_to_editor file would be called instead of mine.

  9. Andrew
    Posted January 19, 2010 at 11:13 pm | Permalink

    Hi John,

    I’ve tried this with my own plugin, but I’m seeing odd behaviour where the popup frame to select an image from the media library doesn’t close after clicking ‘Insert into post’.

    The image is successfully passed to my send_to_editor function. Stepping through the code with a debugger didn’t yield any clues.

    How did you solve this issue in your example?

    Cheers,
    Andrew

  10. Posted January 20, 2010 at 3:16 am | Permalink

    At the end of your send_to_editor function, call:
    tb_remove();
    It was already in the example attachment I provided in the post, but not in the body of the post. I’ve updated my post so that it is included.

    Sorry for the confusion.

  11. Andrew
    Posted January 20, 2010 at 6:06 am | Permalink

    Perfect! Thanks John!

  12. Jazib
    Posted January 29, 2010 at 9:47 pm | Permalink

    Hey I can’t make it work with custom meta values on post/page editor, can you explain what do you mean by “wrap the existing send_to_editor in an anonymous function”

  13. Posted February 24, 2010 at 10:29 am | Permalink

    This is great.

    How do I work this into the front end though?
    I’m trying to create a portfolio image gallery on the homepage of the blog.

    Got the plugin all set up and customized, but no idea how to get the images on the front page (in my theme).

  14. Posted February 24, 2010 at 3:52 pm | Permalink

    Adam,

    The goal of writing to the plugin was to demonstrate how to use the built-in image gallery as a selection tool. I select an image, and it is passed along to a WordPress option, custom field, etc. The core assumption that allows this technique to work is that you are already working on something in the wp-admin, or back-end side of WordPress. In my case, it was how to create a page to update banner ads without reinventing the wheel–a task that would only be performed inside wp-admin.

    Because this technique requires the wp-admin functionality, it cannot be used within a theme without bootstrapping the entire admin interface. I’m not even sure that this is possible. If it is, I certainly would advise against it.

    If you are looking for the ability to display a gallery, I highly recommend NextGEN Gallery. If you are looking for the ability to have users upload the images to the gallery, I recommend that look into having them log into the wp-admin side of things and upload the content. If you are looking for image submissions from public users, I recommend that you use a form that collects the image and stores it to a safe directory location.

  15. Posted March 15, 2010 at 4:52 pm | Permalink

    Hey John,

    I installed the plugin with NO issues, THANKS so much. BUT… when I choose an image from the media gallery, and Save Changes, I get the “You do not have sufficient permissions to access this page.” error… any ideas?

  16. Posted March 15, 2010 at 7:11 pm | Permalink

    That error message originates from the menu.php page inside wp-admin. I believe it is happening because the user that is trying to update the page is not an administrator. You’ll want to check the documentation for adding an administration menus: http://codex.wordpress.org/Adding_Administration_Menus. Currently, I have the plugin set to user level 8–which is an older, legacy user level. Since I snipped this plugin out of older code, it remained. You’ll likely want to play around with the capability required by the menu page to get it working. All of that is in hijack.php file.

  17. Posted April 8, 2010 at 7:38 pm | Permalink

    Thanks John for your great article.

    I’m currently working on a theme using WP 2.9.2. Out of the box, your script doesn’t work properly here. I’d to change the following things:

    1) In my custom admin-option-page the required thickbox css and js file wouldn’t be load by default. To add the required fields to the wordpress list of loaded files, just call add_thickbox(); in your init-function of your admin-page or before wp_head();.

    2) Same goes for my custom js file: Put the line wp_enqueue_script('_custom_js', '/js/custom.js'); in your init-function.

    3) I’d the problem, that the value attribute of my input field wasn’t updated. If you use the built-in jquery support from wordpress, you have to “wrap” every jquery call since WP 2.8+. Instead of using $();, you have to write jQuery();. This took me some time to figure out.

    So you have to change the function send_to_editor to the following:


    function send_to_editor(html) {
    var source = html.match(/src=\".*\" alt/);
    source = source[0].replace(/^src=\"/, "").replace(/" alt$/, "");
    jQuery("#banner_image").attr('src', source);
    jQuery('#banner_image_input").attr('value', source);
    tb_remove();
    }

    However, one problem remains:

    The thickbox window isn’t loaded at full size. I’ve tried to load the built-in js script “media-upload” with wp_enqueue_script('media-upload'); which seems to work, but this breaks the whole script.

    If anybody has a solution, please post ;)

  18. Posted April 17, 2010 at 3:12 pm | Permalink

    The media-upload script needs to be called before wordpress starts sending content to the browser. By the time it starts parsing banner.php, it is already too late. You’ll see how I addressed part of this in the ‘hijack_thickbox_include’ action in hijack.php. I placed your wp_enqueue_script call in there, and everything appears to have worked:
    add_action('admin_init', 'hijack_thickbox_include');
    function hijack_thickbox_include() {
    add_thickbox();
    wp_enqueue_script('media-upload');
    }

    That should fix your problems with the media upload. There is likely a better method for including it, but I’ll have to do more digging to find it.

  19. Posted July 9, 2010 at 9:15 pm | Permalink

    This is great, thanks so much for posting.

    I was having problems using the wp_enqueue_script(‘media-upload’); – I was getting the properly sized editor but my custom send_to_editor() function wasn’t being triggered. I found that if I listed the dependencies on my own custom JS code, that solved the problem.

    Like so:

    function admin_add_scripts() {
    add_thickbox();
    wp_enqueue_script(‘media-upload’);
    // My js file, with an array of dependencies
    wp_enqueue_script( ‘admin.js’, get_stylesheet_directory_uri() . “/scripts/admin.js”, array(‘jquery’, ‘media-upload’) );
    }
    add_action(‘admin_init’, ‘admin_add_scripts’);

    Simon

  20. Wizzie
    Posted April 14, 2011 at 5:18 pm | Permalink

    Any way that the image uploader auto add a custom image title? For example to add “click to enlarge” in title field automatically?

    Thx alot!

  21. Posted April 18, 2011 at 8:30 am | Permalink

    Hi John,

    I am too a developer using your code example, I am having a little trouble. In my custom theme options page, I have several fields with links to open the media library, now there are several fields which will take input from the media library, can you tell me how do I achieve this?

    Thanks again for your help.

    Ali

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>