Expand the WordPress Media Library Admin Panel

As of WordPress 2.8, it appears that the only way to insert new interface panels into the WordPress Media Library is through the restrict_manage_posts action hook.

Below I describe how I use this hook to expand the Media Library admin panel.

1. Media Library Menu Check

Be careful when you use this hook because it gets called in other admin menus as well, including the Posts menu. To limit my new menus to just the Media Library, I first do the following check in my plugin file or functions.php theme file.

add_action('admin_init', 'init_media_plus');

function init_media_plus() {
	// Only activate plugin for the Media Library page
	if (strpos($_SERVER["REQUEST_URI"], "upload.php") === FALSE)
		return;
	
	add_action('restrict_manage_posts', 'media_library_menu');
	add_action('admin_head', 'media_plus_header', 51);
	add_filter('wp_redirect', 'media_plus_redirect', 10, 2);
}

Line 5 – The Media Library menu is constructed from the wp-admin/upload.php file. Therefore,we first check to see that we are on the upload.php page.

Line 8 – Only add our Media Library admin panels if we are on the Media Library menu page.

Line 9 – Add any Javascript functions here.

Lines 10… – Add any additional action or filter hooks.

2. Define New Media Library Admin Panels

Next, we define the new admin panels we want to add to the Media Library. Below, we add a drop-down menu containing some actions that we want to perform on images in our Media Library.

In addition to the existing Delete action, we now enable the Attach action for all image objects. We also add in a new Remove-Attachment option.

function media_library_menu() {
 	global $wpdb;

    ?>
 	<div class="wrap">   
		<?php screen_icon(); ?>
		<h2>Media Library Plus</h2>

        <form id="media-plus-form" action="" method="get">
            <select name="action" class="select-action">
            <option value="-1" selected="selected"><?php _e('Bulk Actions'); ?></option>
            <option value="delete"><?php _e('Delete'); ?></option>
            <option value="attach"><?php _e('Attach to a post'); ?></option>
            <option value="remove"><?php _e('Detach from a post'); ?></option>

            </select>
        
            <input type="submit" value="<?php esc_attr_e('Apply'); ?>" name="doaction" id="doaction" class="button-secondary action" onClick="getSelectedMedia('posts-filter');" />
            <?php wp_nonce_field('bulk-media'); ?>
            <?php find_posts_div(); ?>
        </form>
    </div> <!-- End div wrap -->
    <?php	
}

Line 19 – This enables our new admin panel to execute actions that are in the native WordPress Media Library file (wp-admin/upload.php).

Line 20find_posts_div is a useful WordPress function that adds a post/page search pop-up menu to our HTML form. It is defined in wp-admin/includes/template.php.

3. Get Selected Media Objects

Ok, so now we have a new Media Library admin panel, but it is in a separate form than our media objects. As a result, it is unaware of which media objects have been selected by the user, and is unable to pass on that information when the form is submitted.

We add the selected media objects into our new Media Library form by using Javascript.

Note -We link this Javascript to our new Media Library admin panel by tying it to the onClick event on our form submit button.

function media_plus_header() {
?>
<script type="text/javascript">
<!--
// get_selected_media(document.posts-filter.list)

function addNewArg(name, value) {
	var newArg = document.createElement("input");
	newArg.type = "hidden";
	newArg.name = name;
	newArg.value = value;
	newArg.id = name;
	return newArg;
}

function getSelectedMedia() {
	var mediaPlusForm=document.getElementById('media-plus-form' );
	var args = document.getElementById('posts-filter').elements;
	
	for (i = 0; i < args.length; i++) {
		if (args[i].name == "media[]" && args[i].checked) {
			mediaPlusForm.appendChild(addNewArg("media[]", args[i].value));
		}
	}

	// Redirect to previous page by adding previous mime_type and detached state
	var hasType = location.href.indexOf('post_mime_type');
	if (hasType >= 0) {
		var sPos = location.href.indexOf('=', mimeType);
		var ePos = location.href.indexOf('&', sPos);
		
		if (ePos >= 0) {
			var mimeStr = location.href.substring(sPos+1, ePos);
		} else {
			var mimeStr = location.href.substring(sPos+1);
		}

		mediaPlusForm.appendChild(addNewArg('post_mime_type', mimeStr));
	}
	
	if (location.href.indexOf('detached') >= 0) {
			mediaPlusForm.appendChild(addNewArg('detached', '1'));
	}
}
//-->
</script>
<?php
}

We add this Javascript into our page header by tying it to the admin-head hook in step-1.

Line 18 – We retrieve the selected media objects by using Javascript to query the native Media Library selection form (posts-filter form).

Lines 20-24 – For each selected media object, we add it as a hidden input field in our own Media Library panel (media-plus-form).

Lines 26 … – The subsequent lines add state information about the current Media Library page. In particular, we add information on the current media object list. For example, whether we are on the image object, video object, audio object, or unattached view. This allows us to redirect the user back to his previous state once we finish our image attachment operations.

4. WordPress wp_redirect Filter

We use the wp_redirect filter to parse the state information defined in our Javascript function, and direct users back to their previous Media Library page.

// Change redirect set in upload.php
function media_plus_redirect($location, $status) {
	if ( isset($_GET['found_post_id']) && isset($_GET['media']) ) {
		if (!isset($_GET['detached']))
			$location = remove_query_arg('detached', $location);
	}
	return $location;
}

5. Add a New Function for Dealing with the Remove Attachment Action

Finally, the current WordPress Media Library does not allow for the removal of attachments from media objects. To enable this new option, we add a new function to our plugin or theme.

function detach_media_action() {
	// $_GET['doaction'] should be 'Apply'
	global $wpdb;
	
	if ( !strcmp($_GET['action'], 'remove') && isset($_GET['media']) ) {
		check_admin_referer('bulk-media');
	
		$attach = array();
		foreach( (array) $_GET['media'] as $att_id ) {
			$att_id = (int) $att_id;
	
			if ( !current_user_can('edit_post', $att_id) )
				continue;
	
			$attach[] = $att_id;
		}
	
		if ( ! empty($attach) ) {
			$attach = implode(',', $attach);
			$attached = $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_parent = %d WHERE post_type = 'attachment' AND ID IN ($attach)", '') );
		}
	
		if ( isset($attached) ) {
			$location = 'upload.php';
			if ( $referer = wp_get_referer() ) {
				if ( false !== strpos($referer, 'upload.php') )
					$location = $referer;
			}
	
			$location = add_query_arg( array( 'attached' => $attached ) , $location );
			// redirect after header here can't use wp_redirect($location);
			?>
			  <script type="text/javascript">
              <!--
              window.location= <?php echo "'" . $location . "'"; ?>;
              //-->
              </script>
			<?php
			exit;
		}
		
	}
}

This function is very similar to the attach action defined in upload.php. Instead of querying the user for an attachment post, we just clear the post_parent attribute of our media objects in the WordPress database (Line-20).

The other change we made is to use Javascript to redirect our page instead of the wp_redirect command. wp_redirect only works before the page headers are defined. Since the restrict_manage_posts only gets called after page headers have been defined, calling wp_redirect will result in an error.

Finally, just add the detach_media_action function to the start of your restrict_manage_posts hook function and you are done!

Media Library Plus Plugin

You can also see all of the above code in action by downloading the Shiba Media Library Plugin.

Related Articles

Comments

  1. Aaron says

    Hi, I stumbled here on search for a solution to my problem and I’m wondering if you can give a bit of direction, or maybe confirm your plugin is the solution I’ve been looking for all along.

    I’m hoping to customize the Attachment Display Settings Link To drop-down menu to add a custom option. Where my posts/pages have pdfs for download I’m trying to get them all to include a download icon and the file size next to the anchor text. So that the site’s admin (who has no coding capability) will be able to simply select their pdf from the Media Library, choose my custom setting in the Link To menu, and the link would appear in the content with the css and the metadata required.

    Is that feasible? Does your plugin do it better? Any input or direction would be a tremendous help.

    Thanks!

    • says

      Do you mean you want to expand the new Media Manager (WP 3.5) pop-up menu? Just to be clear, this article is about expanding the Media Library screen (i.e. the page we go to when we click on the Media link from our dashboard). It is not about the media manager pop-up menu.

      However, media manager menu expansions is something that I want to do as well, but I haven’t had the time to look into it yet. Based on what I have seen, my guess is that it will be a non-trivial process.

  2. says

    Hey this was a really nice tut. I’m trying to add some functions to the attachment edit part of the media library, like an extra text input field under “description”. Would you know how to go about adding that?

    Thanks, Shawn

Leave a Reply

Your email address will not be published.

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>