I have a need to limit items in the media library for users without a specific capability. There are a lot of examples out there for how to limit them to only items that a given user has uploaded, but my need is more complex than that.
I need to limit media items for users without a specific capability to those attachments that are NOT:
- header images (i.e., set in Appearance > Headers)
- page thumbnails (i.e., featured images)
Below is the solution I’ve come up.
/*
* restrict media items available to users without cap=mycap to those that are NOT:
*
* 1. theme headers (i.e., set in Appearance > Headers)
* 2. page thumbnails (i.e., featured images)
*/
add_action ('pre_get_posts', 'restrict_media') ;
function
restrict_media ($query)
{
if (!is_admin () || $query->get ('post_type') != 'attachment' ||
current_user_can ('mycap')) {
return ;
}
// get our theme's headers (i.e., set in Appearance > Headers)
$args = array (
'post_type' => 'attachment',
'post_status' => array ('inherit', 'private'),
'posts_per_page' => -1,
'meta_query' => array (
'relation' => 'AND',
array (
'key' => '_wp_attachment_context',
'value' => 'custom-header',
),
array (
'key' => '_wp_attachment_is_custom_header',
'value' => 'antelopevalley',
),
),
) ;
// remove ourself from pre_get_posts to avoid infinite regression
remove_action ('pre_get_posts', array ($this, 'restrict_media')) ;
$headers = new WP_Query ($args) ;
// get the IDs of the headers
$header_ids = array_map (function ($p) { return ($p->ID) ; }, $headers->posts) ;
// add ourself back to pre_get_posts for subsequent queries
add_action ('pre_get_posts', array ($this, 'restrict_media')) ;
// get posts with thumbnails
$args = array (
'post_type' => 'page',
'post_status' => array ('publish', 'pending', 'draft', 'private'),
'posts_per_page' => -1,
'meta_query' => array (
array (
'key' => '_thumbnail_id',
'compare' => 'EXISTS',
),
),
) ;
$with_thumbnail = new WP_Query ($args) ;
// get the IDs of the thumbnails
$thumbnail_ids = array_map (function ($p) { return (get_post_meta ($p->ID, '_thumbnail_id', true)) ; },
$with_thumbnail->posts) ;
// exclude all theme headers and thumbnails
$query->set ('post__not_in', array_merge ($header_ids, $thumbnail_ids)) ;
return ;
}
The above solution works; however, it seems overly complex to me!
I’m wondering if anyone can suggest a simpler solution.
Note: I’ve tried the post_{where,join} filters before for other needs and have found them too complex to maintain so I didn’t even try to figure out a solution to this problem using them, but I’m more than open to one that uses them
Good code. I think you could simplify by making direct SQL queries instead:
Also, with your method, I think that if you call WP_Query with
suppress_filters => true
(or useget_posts()
instead; it suppresses filters by default) you can avoid having to remove your own filter.