This is an advanced tutorial on how to expand the WordPress media manager interface with new options. In particular, I want to expand the drop-down menu in the media manager menu so that I can do searches based on posts, pages, and custom post types, as well as include their thumbnails into my galleries.
Other media manager tutorials include –
- How to Add the WordPress 3.5 Media Manager Interface – Basic tutorial on how to add the media manager interface into our plugin or theme.
- How to Add the WordPress 3.5 Media Manager Interface – Part 2 – More advanced tutorial on how to create our own media manager object, and how to customize its various parameters.
- How to Add the Media Manager Menu to the Theme Preview Interface – More advanced tutorial on how to add the media manager menu into the WordPress theme customizer interface.
1. Send New Options to Media Manager Interface
Currently, the media manager only allows searches to be performed on images. There are two options in the drop-down menu – Images and Uploaded to this post.
The first thing that I want to do, is determine which post types I want to add to the search drop-down menu. As a start, I want to be able to search for posts, pages, galleries, and shiba_links, where gallery and shiba_link are custom post types. Therefore, I define and send those entries over to the media manager javascript object by using the media_view_settings filter.
add_filter( 'media_view_settings', 'my_media_view_settings', 10, 2); function my_media_view_settings($settings, $post) { $post_types = array('post' => 'Posts', 'page' => 'Pages', 'gallery' => 'Galleries', 'shiba_link' => 'Shiba Links'); // Add in post types foreach ($post_types as $slug => $label) { if ($slug == 'attachment') continue; $settings['postTypes'][$slug] = $label; } return $settings; }
We can later access this array in javascript using wp.media.view.settings.postTypes.
2. Add New Options into Gallery Drop-Down Menu
The entries in the search drop-down menu are controlled by the wp.media.view.AttachmentFilters.Uploaded object. More specifically, the filter attributes are defined in the createFilters function. Therefore, to expand the drop-down menu we want to override that javascript function.
// Override relevant media manager javascript functions add_action( 'admin_print_footer_scripts', 'my_override_filter_object', 51); function my_override_filter_object() { ?> <script type="text/javascript"> // Add custom post type filters l10n = wp.media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n; wp.media.view.AttachmentFilters.Uploaded.prototype.createFilters = function() { var type = this.model.get('type'), types = wp.media.view.settings.mimeTypes, text; if ( types && type ) text = types[ type ]; filters = { all: { text: text || l10n.allMediaItems, props: { uploadedTo: null, orderby: 'date', order: 'DESC' }, priority: 10 }, uploaded: { text: l10n.uploadedToThisPost, props: { uploadedTo: wp.media.view.settings.post.id, orderby: 'menuOrder', order: 'ASC' }, priority: 20 } }; // Add post types only for gallery if (this.options.controller._state.indexOf('gallery') !== -1) { delete(filters.all); filters.image = { text: 'Images', props: { type: 'image', uploadedTo: null, orderby: 'date', order: 'DESC' }, priority: 10 }; _.each( wp.media.view.settings.postTypes || {}, function( text, key ) { filters[ key ] = { text: text, props: { type: key, uploadedTo: null, orderby: 'date', order: 'DESC' } }; }); } this.filters = filters; }; // End create filters </script> <?php }
Line 7 – Override createFilters function.
Lines 8 – 34 – Copied from original createFilters function.
Lines 35 – 36 – Only change the drop-down menu for the gallery screens (gallery and edit-gallery).
Lines 37 – 47 – Remove the default ‘all’ filter and replace it with an image-type filter. This is necessary because the ‘all’ filter does not contain a type, which becomes important when we start adding in more types in addition to images.
Lines 48 – 58 – Add in our post types from step 1 (wp.media.view.settings.postTypes).
3. Link new filters to server
Doing searches based on our new post type options, however, always comes up empty. For the right searches to get performed, we also need to link our new filter options to our server.
In particular, searches performed in the media manager interface gets passed on to our server as an AJAX call – wp_ajax_query-attachments. One way to enable our expanded search options is to override this ajax function.
add_action('wp_ajax_query-attachments', 'my_wp_ajax_query_attachments', 1); function my_wp_ajax_query_attachments() { if ( ! current_user_can( 'upload_files' ) ) wp_send_json_error(); $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array(); $query = array_intersect_key( $query, array_flip( array( 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type', 'post_parent', 'post__in', 'post__not_in', ) ) ); if (isset($query['post_mime_type']) && ($query['post_mime_type'] != "image")) { // post type $query['post_type'] = $query['post_mime_type']; $query['post_status'] = 'publish'; unset($query['post_mime_type']); } else { // image $query['post_type'] = 'attachment'; $query['post_status'] = 'inherit'; if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) ) $query['post_status'] .= ',private'; } $query = apply_filters( 'ajax_query_attachments_args', $query ); $query = new WP_Query( $query ); // $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts ); $posts = array_map( 'my_prepare_items_for_js'), $query->posts ); $posts = array_filter( $posts ); wp_send_json_success( $posts ); }
Lines 3-10 – From original ajax function.
Line 12 – Check for our added post types (i.e. where post_mime_type != image).
Lines 14-16 – Set post_type and post_status appropriately for search. Unset post_mime_type since we are searching based on post types and not based on images.
Lines 18-25 – From original ajax function.
Line 26 – Perform the search using the WP_Query object.
Line 28 – The wp_prepare_attachment_for_js function only prepares attachment objects. Other object types are not processed and then simply removed by the array_filter PHP function on line 30.
To properly return our post type objects, we will need to write our own processing function (my_prepare_items_for_js) that can deal with both attachments and posts.
function my_prepare_items_for_js($item) { switch($item->post_type) { case 'attachment': return wp_prepare_attachment_for_js($item); case 'post': case 'page': case 'gallery': default: return my_prepare_post_for_js($item); } } function my_prepare_post_for_js( $post ) { if ( ! $post = get_post( $post ) ) return; $attachment_id = get_post_thumbnail_id( $post->ID ); $attachment = get_post($attachment_id); $post_link = get_permalink( $post->ID ); $type = $post->post_type; $subtype = 'none'; if ($attachment) { $url = wp_get_attachment_url( $attachment->ID ); } else { // Show default image $url = includes_url('images/crystal/default.png'); } $response = array( 'id' => $post->ID, 'title' => $post->post_title, 'filename' => wp_basename( $post_link ), 'url' => $url, 'link' => $post_link, 'alt' => '', 'author' => $post->post_author, 'description' => $post->post_content, 'caption' => $post->post_excerpt, 'name' => $post->post_name, 'status' => $post->post_status, 'uploadedTo' => $post->post_parent, 'date' => strtotime( $post->post_date_gmt ) * 1000, 'modified' => strtotime( $post->post_modified_gmt ) * 1000, 'menuOrder' => '', // $attachment->menu_order, 'mime' => '', // $attachment->post_mime_type, 'type' => $type, 'subtype' => $subtype, 'icon' => $url, // wp_mime_type_icon( $attachment_id ), 'dateFormatted' => mysql2date( get_option('date_format'), $post->post_date ), 'nonces' => array( 'update' => false, 'delete' => false, ), 'editLink' => false, ); // Don't allow delete or update for posts. So don't create nonces. return apply_filters( 'wp_prepare_post_for_js', $response, $post ); }
Once we add in our own query-attachments function, our post type searches work as they should.
4. Adding our search results to the gallery
Unfortunately, there is one more fly in the ointment. When we click on Create Gallery, only selected images get added. To include our post type objects, we will need to override the mainGalleryToolbar javascript function.
// Add to my_override_filter_object function wp.media.view.MediaFrame.Post.prototype.mainGalleryToolbar = function( view ) { var controller = this; this.selectionStatusToolbar( view ); view.set( 'gallery', { style: 'primary', text: l10n.createNewGallery, priority: 60, requires: { selection: true }, click: function() { var selection = controller.state().get('selection'), edit = controller.state('gallery-edit'); // models = selection.where({ type: 'image' }); // Don't filter based on type edit.set( 'library', selection); /* edit.set( 'library', new wp.media.model.Selection( selection, { props: selection.props.toJSON(), multiple: true }) ); */ this.controller.setState('gallery-edit'); } }); };
Line 18 – Just pass through our selection instead of filtering it based on image type as is done in lines 19-22.
Now, posts, pages, and more can be added to our media manager gallery.
5. Customize our post type rendering
Finally, we can make our post type objects more pretty by overriding the media manager attachment template.
// Override attachment template - i.e. how attachment objects are viewed // in the media manager interface add_action( 'admin_footer', 'my_print_media_templates', 5); function print_media_templates() { ?> <script type="text/html" id="tmpl-attachment"> [Insert template code here] </script> <?php }
Ken William says
Thanks to ShibaShake for sharing it, it was really helpful to me personally. I spent nearly a day implementing this feature for my plugin and now it’s really perfect. Great!
Cyril says
Hi,
Thanks for this!
I have been implementing this but I need more features, for example, I need to display more than on thumbnail per post (we are uing acf and custom get field). We’ve been working on it but we don’t find a way to do it.
We can pay for this modification, if you are interested in, please contact me and I will explain what I need, and you will tell me your price 🙂
Thanks in advance,
Cyril
Cyril says
Hi,
Can you help me with this as a freelance job?
You can contact me on my e-mail address.
Thanks in advance,
Cyril
ShibaShake says
Hello Cyril, I am not doing any freelance jobs at the moment. Too much on my plate. Good luck with your project.
Zain says
Hi Shiba,
Great tutorial (and very cool site)!
I’m just wondering – do you have a tutorial on how you can change the default WordPress Media Manager “Link To | Media File” location so it goes to the *large* version of the image, instead of the *original* size? A lot of site authors are now uploading very large 8MB+ images from their cameras, without realising that they’re slowing down their site. That would be really helpful!
An nice solution for a plugin/theme would be an option to set the default size for the Media File URL, so a user could choose to always use one of the other sizes (medium/large/original/custom). Alternatively, an image size dropdown, rather than the text input field, for the user to choose the right size.
Anyway, keep up the great work – it’s appreciated!
Cheers,
Zain
Rizqy says
Hi, thanks for this cool tutorial. I was spending 2 hours implementing this feature for my plugin and now it works perfectly. 🙂
jey says
HI Its very useful, But is there any way to add a textbox or a new dropdown to the gallery page