0

I have realised that a pre_get_post filter in my functions.php is spoiling the default behaviour of other modules running (ajax) in the frontend while user is logged in.

What I want to achieve is to filter media library content in the dashboard to prevent non admin content to appear in the media library. So I have setup a filter pre_get_post in a way that when non admin users are accessing the media library, content will be filtered. This runs perfectly.

The problem occurs because this filter is applied to the dashboard media library loading module (namely admin-ajax.php) and for unknown reasons it looks like the filter is being applied also when the user is navigating in the frontend, (while logged in), and Ajax navigation in the frontend will suffer from this.

I wonder if is there a solution to stop this, ensuring that filter will really be applied only when the user is in the dashboard. I have attempted to stuff as much as possible conditions to stop running the code when user is not in the dashboard but I couldn't get it to work.

Here's the offending code in functions.php:

    $url_split = explode("/",$_SERVER['REQUEST_URI']); // get current URI to ensure the filter is only applied when navigating in the dashboard

    $user = wp_get_current_user();   
    if ( is_admin() && !in_array( 'administrator', (array) $user->roles ) && strtolower($url_split[1]) == "wp-admin"){
        add_filter('pre_get_posts', 'posts_for_current_role');
    }

    // ********** post & media lists filtered by user role & category ****************
function posts_for_current_role($query) {
global $students_allowed_cat;
global $teachers_allowed_cat;
global $pagenow;


        $user = wp_get_current_user();

        if( 'edit.php' == $pagenow   ){
            if ($query->get('cat') == '0' || IsNullOrEmptyString($query->get('cat')) || !count($_GET) ){
                if ( in_array( 'teacher', (array) $user->roles ) ) {
                    //The user has the "teacher" role
                    $filtered_cat = $teachers_allowed_cat;
                }
                if ( in_array( 'student', (array) $user->roles ) ) {
                    //The user has the "student" role
                    $filtered_cat = $students_allowed_cat;
                }
                $query->set('cat',  $filtered_cat);
            }
        }
        if (in_array( $pagenow, array( 'upload.php', 'admin-ajax.php')  ) ){
                // hide admin media for non-admin users
                $query->set('author',  '-1'); // **** OFFENDING!!!
                // log to a file what page is applying the filter
                $file = 'c:\temp\log.txt';
                $runningpage = $pagenow."\n";
                file_put_contents($file, $runningpage , FILE_APPEND | LOCK_EX);
                // unbelievable but the log file will show that the code is being run from admin-ajax.php   
            }
        return ($query);     

}
  • You don't seem to define $pagenow – Andy Macaulay-Brook Aug 7 '16 at 14:47
  • I did, and also I have logged what page is running the code and the code runs from admin-ajax.php – Riccardo Aug 7 '16 at 14:57
  • In your function posts_for_current_role, it looks as though $pagenow is undefined so your offending line never executes. Or am I not seeing something? – Andy Macaulay-Brook Aug 7 '16 at 15:07
  • Sorry but to make things easier I didn't post all the function, but the global $pagenow is included. Should I post the whole function? – Riccardo Aug 7 '16 at 15:10
  • 1
    Any Ajax call to WP will likely call admin-ajax.php as that's the way you're meant make Ajax calls. So your last condition will be true for many front end Ajax calls and then you're setting author -1 for all of those queries and excluding everything that user 1 has written. – Andy Macaulay-Brook Aug 7 '16 at 16:02
0

Any ajax call to WP will likely call admin-ajax.php as that's the way you're meant to make ajax calls in WP. So your last condition will be true for many front end ajax calls and then you're setting author -1 for all of those queries and excluding everything that User #1, generally the default Admin user, has written.

It's probably better to use the wp_ajax_query_attachments hook instead which only fires in the context you want and so gives you a much more specific target for your query adjustment:

if ( is_admin() && !in_array( 'administrator', (array) $user->roles ) ) { 
    add_filter( 'ajax_query_attachments_args', 'exclude_admin_media', 1, 1 );
} 

function exclude_admin_media( $query ) { 

    $query['author'] = '-1'; 
    // exclude admin media 

    return $query; 
}
  • Thanks again Andy, but from an academical point of view, how could the admin-ajax.php make it through the test strtolower($url_split[1]) == "wp-admin"), while in the front end? – Riccardo Aug 7 '16 at 20:09
  • Because that's where the file is: /wp-admin/admin-ajax.php. admin-ajax.php does set a constant DOING_AJAX to true, so you can use that to exclude ajax calls in your logic. As an aside, you can't generally rely on testing the file path like that and ought to get admin paths using admin_url(). – Andy Macaulay-Brook Aug 7 '16 at 20:16
  • Tell me more: if the code is running in the front-end the first part of the URI is not"wp-admin"... – Riccardo Aug 7 '16 at 20:19
  • While it might seem counter-intuitive, the preferred way of making ajax requests in the front end does call on the file /wp-admin/admin-ajax.php , as documented in codex.wordpress.org/AJAX_in_Plugins as well as most books on plugin development. – Andy Macaulay-Brook Aug 7 '16 at 20:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.