Using plugins to create custom media menus in WordPress 3.3

WordPress 3.3 introduced a new, unified media menu, where the functionality that used to be accessed through several different menus is now all in one. If you are a plugin developer, and you want to create a custom media menu, you will discover WordPress’ media menu is an arcane and fickle beast. The general reason it’s challenging to work with is that the media menu lacks a complete API. I put several hours of code archaeology work into teasing out its functionality, so this post can guide you through using it. There are two approaches.

Approach one: use ThickBox directly

The WordPress media menu is displayed using ThickBox. One approach is to simply invoke ThickBox directly from your own media button, and not rely on WordPress’ filters or hooks beyond that. This is the simplest approach, and is suitable for many purposes, but it prevents you from using WordPress’ filters and hooks for applying stylesheets to it, or using menu tabs. This is the approach used by the Gravity Forms plugin. Here is the relevant parts of the Gravity Forms code, slightly re-arranged, to show how it all fits together:

  1. Add the Gravity Forms button to the media menu (“RGForms” and “GFCommon” are custom Gravity Forms classes)
    add_action('media_buttons_context', array('RGForms', 'add_form_button'));
    
    public static function add_form_button($context){
        $image_btn = GFCommon::get_base_url() . "/images/form-button.png";
        $out = '<a href="#TB_inline?width=480&inlineId=select_gravity_form" class="thickbox" id="add_gform" title="' . __("Add Gravity Form", 'gravityforms') . '"><img src="'.$image_btn.'" alt="' . __("Add Gravity Form", 'gravityform') . '" /></a>';
        return $context . $out;
    }
  2. Add to the editor page footer the javascript and html that will be shown in the thickbox window when the media button is clicked.
    if(in_array(RG_CURRENT_PAGE, array('post.php', 'page.php', 'page-new.php', 'post-new.php'))){
        add_action('admin_footer',  array('RGForms', 'add_mce_popup'));
    }
  3. In the function below I’ve removed the javascript code that reads the form inputs. The call to send_to_editor passes the resulting Gravity Forms shortcode to the editor window. After that is the html code that displays the form. The display: none style is important: the form is hidden at the bottom of the editor page, and then shown in the ThickBox window when the media button is clicked (the media button link refers to the id of this div). I’ve also removed the input form, which has all its CSS styling done inline (with this approach, there’s no straightforward way to call an external stylesheet).
    function add_mce_popup(){
        // ...not shown - javascript that reads the form inputs
    
        window.send_to_editor("[gravityform id=\"" + form_id + "\" name=\"" + form_name + "\"" + title_qs + description_qs + ajax_qs + "]");
        // ...
    
        <div id="select_gravity_form" style="display:none;">
        // ...not shown - html for the form
    }

Approach two: use WordPress’ filters and hooks

Due to a code change in WordPress 3.3, you can no longer use your own media button if you want to create a menu with custom tabs (or, at least, I couldn’t figure out how). With this approach, your menu will be accessible as a new tab in WordPress 3.3’s unified media menu. I’ll use code from my Shashin plugin as an example.

  1. Register the function that will add tabs to the media menu. The function then adds tabs to the media menu array ($tabs contains the pre-defined WordPress menu tabs)
    add_filter('media_upload_tabs', array($this, 'addMediaMenuTabs'));
    
    public function addMediaMenuTabs($tabs) {
        $shashinTabs = array(
            'shashinPhotos' => __('Shashin Photos', 'shashin'),
            'shashinAlbums' => __('Shashin Albums', 'shashin')
        );
        return array_merge($tabs, $shashinTabs);
    }
  2. Use the media_upload_* action to register the functions that will load the contents for the Shashin menus. It’s crucial that the last portion of the action name match the array keys added to the tabs in the previous step.
    add_action('media_upload_shashinPhotos', array($this, 'initPhotoMediaMenu'));
    add_action('media_upload_shashinAlbums', array($this, 'initAlbumMediaMenu'));
  3. For the remaining steps I’ll just show the “shashinPhotos” tab, as the albums one is similar. Calling admin_print_styles with -media-upload-popup lets you pass your own css file to the media menu, for your custom tab. wp_iframe loads your html into menu.
    public function initPhotoMediaMenu() {
        add_action('admin_print_styles-media-upload-popup', array($this, 'displayMediaMenuCss'));
        return wp_iframe(array($this, 'mediaDisplayPhotoMenu'));
    }
  4. Load your custom css
    public function displayMediaMenuCss() {
        $cssUrl = plugins_url('/Display/', __FILE__) .'media.css';
        wp_enqueue_style('shashinMediaMenuStyle', $cssUrl, false, $this->version);
    }
  5. It’s crucial for this function name to start with the word media. WordPress uses this as a cue to load its standard css for the ThickBox window. Without it, the tabs and other aspects of the window will not get the correct layout. media_upload_header loads the menu tabs. After that, put in your javascript and html for what you want to display for this tab. Like the Gravity Forms example above, you need to call WordPress’ send_to_editor javascript function to insert the final result into the editor.
    public function mediaDisplayPhotoMenu() {
        media_upload_header();
    
        // ... not shown - javascript and html for the menu
    }

Before WordPress 3.3, the Shashin plugin had its own media button, that launched a media menu with two custom tabs. From what I can tell, there is no straightforward way to do this in WordPress 3.3. It used to be possible to have tabs that were independent of the tabs that are part of the standard WordPress media uploader. But now if you want to have custom tabs, you can’t have them as part of a custom menu – you have to add them to the existing media uploader tabs. So for Shashin, I decided to take this approach, as the only alternative would be to add two buttons to the media button bar, and have two separate media menus instead of one menu with two tabs.

22 Comments

  1. Reply
    Toine December 30, 2011

    Hi Mike,
    Currently installed v3.0.9, WP 3.3, Theme: TwentyTen 1.3
    I’ve 2 problems with the position attribute.
    1- when position=”left”, why is my photo in the center of the page?
    My code:
    [shashin type="photo" id="12" size="small" columns="1" order="user" position="left"]Bienvenue dans WordPress. Ceci est votre premier article. Modifiez-le ou supprimez-le, puis lancez-vous !
    My website: http://www.basketsetsacados.com/trip/?p=1
    2- With the same code ([shashin type="photo" id="12" size="small" columns="1" order="user" position="left"]Bienvenue dans WordPress. Ceci est votre premier article. Modifiez-le ou supprimez-le, puis lancez-vous !), I don’t understand why the text isn’t near the photo but bottom the photo?
    Finally, how to delete the sliver border around the photo?
    Thanks and sorry for my bad English…

    • Reply
      Mike January 3, 2012

      Hi Toine – sorry for the delay – I was away over the holidays. Please see an earlier comment thread, describing how to fix this in the twenty eleven theme: http://www.toppa.com/2011/shashin-3-0-2/#comment-58623

      In the case of twenty ten, look in style.css for #content table – the width: 100% is what’s causing the left and right positioning in Shashin to not work. This is also what’s forcing the text to go below the photo.

      • Reply
        Toine January 6, 2012

        You’re great Mike! Without this line, it’s work!
        Last question: how to delete (or hide) the sliver line around the photos?
        Thanks and happy new year!

        • Reply
          Mike January 8, 2012

          Hi Toine,

          That border is being caused by two things in the theme’s stylesheet:

          #content table {
              border: 1px solid #E7E7E7;
          }

          and

          #content tr td {
              border-top: 1px solid #E7E7E7;
          }
  2. Reply
    syl December 31, 2011

    Hi Mike,

    I currently have WP 3.3 installed and just installed Shashin 3.0.9, and I’m having problems adding Picasa albums – I keep getting “Failed to retrieve album feed at [URL]”. I’m using the correct RSS link URLs, and have tried adding both single albums and all of a user’s albums. The albums are all public. The problem seems to be similar to the one Bill was having (based on his comment on your previous post). Any idea how to fix this?

    Thanks!

    • Reply
      Mike January 8, 2012

      Hi syl – sorry for the late reply – I somehow missed your comment earlier. Are you still having this problem? If so, can you give the RSS url you are entering into Shashin? Then I can try it.

  3. Reply
    Lee Evans January 2, 2012

    Happy New Year,

    My customer would like to link directly to one of her albums but with the JQuery loading it seems this is not possible. Is there any workaround?

    Many Thanks
    Lee

    • Reply
      Mike January 8, 2012

      Hi Lee – you can use the “albumphotos” type instead of “album” to view the photos from an album without having to click its thumbnail first. But if you want to click the album thumbnail first, there is no way around loading them without jquery (if javascript is turned off in the user’s browser, the link will go directly to Picasa to view the photos, so it degrades gracefully).

  4. Reply
    Radek Kaminski January 5, 2012

    Hi,

    Just starting with your plugin (and WordPress all together ;-)) I know I can not use private albums with your plugin but most of my albums are published as “Anyone with a link”. I know it’s not public but it’s not private either. Can I use them with your plugin or do I have to change all my albums to public?

  5. Reply
    Mike January 8, 2012

    Hi Radek – currently Shashin does not support this, but it’s on my list of features to add. I will try to include it in the next version.

  6. Reply
    Valentin January 11, 2012

    Hi Mike,

    Shashin is a great tool for us and we appreciate it for our travel blog. In the beginning of the blog, we used the version 2 and we have just upgrade to version 3.

    And now, we have an error when we use the “Sync all” link, following the upgrade instruction :
    Fatal error: Out of memory (allocated 31719424) (tried to allocate 45 bytes) in (blog-repo)/wp-content/plugins/shashin/Admin/ShashinSynchronizer.php on line 97

    We are on a shared host with a memory limited to 32mb. We can’t increase it (1and1 host). We have 8 or 10 albums with 320 photos in one of them.

    We never used this link before so we don’t know if it works with v2 or not. But now we don’t know what to do… Continue the upgrade workflow or stay like that and don’t touch anything ?

    Thanks for your reply,

    Valentin

    • Reply
      Mike January 22, 2012

      Hi Valentin – sorry for the late reply. That line of code is where Shashin decodes the json encoded data from an album. Have you tried syncing albums individually, instead of sync all? Shashin 2 used the RSS feed directly, while Shashin 3 retrieves the json encoded version of the feed. I can parse the json feed with a single PHP command, instead of having to worry about an RSS parsing library. I haven’t done a memory usage comparison of the two, but it sounds like, at least in your case, the json parsing is more memory intensive. If you can successfully sync albums individually, you could turn on the automated syncing on the Shashin settings page. Then it will sync your albums one at a time automatically, over the course of a day, so it’s a “gentler” way to sync all your albums.

  7. Reply
    Kim January 15, 2012

    Hi Mike,

    Thank you for your great plugin shashin, I’m using it on my website.

    However, I don’t know why when I click to photo, a window to view that photo appear without any button such as “close, next, previous …” like you do in your blog.

    But I still can use keyboard to view the next or previous photo.

    May be you want to take a look at my website and give me any idea how to fix this.

    http://www.mamnonabc.vn/hinh-anh/

    Thanks a lot 😉

    • Reply
      Mike January 22, 2012

      Hi Kim – you have a separate Highslide plugin installed. It is probably interfering with the version of Highslide that is included with Shashin.

  8. Reply
    Alex January 19, 2012

    Hi!

    Great tutorial this one.

    I’m having an issue with the send to editor function inside an extra added tab in the insert media thickbox.

    The send_to_editor(‘value’) or window.send_to_editor(‘value’) just doesn’t work inside my new tab. If i put such a button in a meta box it works fine.

    Maybe you have a solution?

    • Reply
      Mike January 22, 2012

      Thanks Alex. Sorry, I don’t have an answer for you. Using the Quicktags API, you don’t need to call send_to_editor() yourself, so I haven’t been working with that function.

      • Reply
        Alex January 23, 2012

        Hi Mike!

        I figured it out and thought i come and share the answer.

        The problem was my javascript was calling the function window.send_to_editor() or just send_to_editor().

        Apparently i needs parent. in front of it. So in my extra tab in the media thickbox is changed the function to: parent.send_to_editor() and then it works just perfect!

  9. Reply
    Mike January 26, 2012

    Thanks Alex!

  10. Reply
    Rangga December 25, 2013

    Hi,
    Thanks for your tutorial.
    I want to ask to you, why the tabs i’ve done is wrapped by iframe?

Leave a Reply to AlexCancel reply