1

I'm creating a front-end based project where users can make posts and add images to them. It's a network of sites that users have access to.

For a better user experience, I want users to be able to see all their images posted on every site on the network, in case they want to reuse them. Uploads are handled by WP media library, and users can only see media posted on the current site.

With the code below, users are restricted to seeing only their own uploads within a subsite.

add_filter( 'ajax_query_attachments_args', 'show_this_user_attachments_only', 10, 1 );
function show_this_user_attachments_only( $query = array() ) {
if ( ! current_user_can( 'manage_options' ) ) {
    $query[ 'author' ]  =   get_current_user_id();
}
return $query;
}

I've tried hooking into pre_get_posts but it doesn't do a thing.

I know it would surely involve switch_to_blog() to achieve this, but I just can't figure out how.

Several hours of searching hasn't yielded any help.


EDIT:
I created a simple form for creating and editing posts and I use the WP Editor as below, the values for the variables are worked out in the said form accordingly:

wp_editor( $content, $editor_id, array(
        'tabindex'      =>  1,
        'textarea_rows' =>  10,
        'editor_class'  =>  'flds',
        'drag_drop_upload'  =>  true
    ) );

And for the media library script, I have added:

wp_enqueue_media();

So, the media library loads the user's images as expected (I believe that's done via Ajax), given the restrictions by the first code.

THE MAIN ISSUE: Getting all the user's images from across the network to load among those displayed inside the media library.

So the problem area is in the media library's output.

Everything else about the form and the posting/editing process is working perfectly well.

5
  • this can't be done from a single query, multiple queries are needed, and you'll have to combine the results after the fact. It's probably easiest to fetch the data via the REST API as then you can grab the data in javascript given a list of sites to fetch from without worrying about the server timing out. This will be slow and expensive. Can you edit/modify your question to include the code that fetches the images? How are you displaying them? There's nothing to modify or change for someone writing an answer
    – Tom J Nowell
    Aug 17, 2023 at 14:40
  • @TomJNowell - Actually, I'm not doing anything extra to fetch the images. It's done by the WP Editor and the Media Library with the wp_enqueue_media(); function as I've added. Aug 17, 2023 at 15:23
  • 1
    oh, in that case this particular way of doing it may not be possible. The fundamental problem being that even if you achieved your goal you'd never be able to pick the attachments because the IDs it saves are unique to each site, they aren't universal. The media with ID 5 on blog B, may be a page with ID 5 on blog C. Instead you should look into network media library plugins, and consolidating your entire media library on to a single blog/site
    – Tom J Nowell
    Aug 18, 2023 at 9:09
  • @TomJNowell - oh wow! That explains it a whole lot. I'll look in that direction now, thanks. Aug 18, 2023 at 12:29
  • Thanks @TomJNowell for pointing me in the right direction. I just found this post that answers every aspect of the problem for me: link Aug 18, 2023 at 13:11

1 Answer 1

0

You could create a cron job that gathers all the data and caches the results in a usermeta value for each user. It'd be slow to generate, but that wouldn't matter because it'd run in the background. The data would then be fast to retrieve on the page where you display the user's images.

The basic code would be something like:

add_action( 'init', 'schedule_cron_jobs' );
add_action( 'cache_user_images', 'cache_user_images' );

function schedule_cron_jobs() : void {
    if ( ! wp_next_scheduled( 'cache_user_images' ) ) {
        wp_schedule_event( time(), 'twicedaily', 'cache_user_images' );
    }
};

function cache_user_images() : void {
    $user_images = array();

    $users = get_users(
        array(
            'number' => -1,
            'fields' => 'ID',
        )
    );

    $sites = get_sites(
        array(
            'number' => false,
            'fields' => 'ids',
        )
    );

    foreach ( $sites as $site_id ) {
        switch_to_blog( $site_id );

        foreach ( $users as $user_id ) {
            $attachments = get_posts(
                array(
                    'post_type'      => 'attachment',
                    'post_status'    => 'inherit',
                    'post_mime_type' => 'image',
                    'author'         => (int) $user_id,
                    'posts_per_page' => -1,
                )
            );

            // Prune the cached data to only what's needed, to save disk space.
            array_walk(
                $attachments,
                function( &$attachment ) use ( $site_id ) {
                    $attachment = array(
                        'site_id'    => $site_id,
                        'post_id'    => $attachment->ID,
                        'post_title' => $attachment->post_title,
                        'url'        => wp_get_attachment_url( $attachment->ID ),
                    );
                }
            );

            if ( $attachments ) {
                $user_images[ $user_id ] = array_merge( $user_images[ $user_id ] ?? array(), $attachments );
            }
        }

        restore_current_blog();
    }

    foreach ( $users as $user_id ) {
        if ( ! empty( $user_images[ $user_id ] ) ) {
            update_user_meta( $user_id, 'user_images', $user_images[ $user_id ] );
        }
    }
}

There's all kinds of optimizations you could make if needed, but it'll depend on number of sites/users, specific app needs, etc. For example:

  • Run the cron every 10 minutes, but have it only work on 100 users during each execution.
  • Restrict to users/sites queries to a subset, if only some users/sites apply.
  • Set count_total and count to false in the user and site queries, respectively, to avoid the extra query to get the total count.

One downside to this approach is that once a user uploads an image, it won't show up in the cache until the next cron execution. To solve that, you'll need to add some more code that will register a save_post callback. That callback would append the attachment being uploaded to the user's cache, so that it's available immediately.

2
  • I tried the code and as a matter of fact, the images are listed as an array in the user meta field of "user_images" when I do a var_dump. But the data doesn't translate into the items listed in the Media Library, which is my main objective. Aug 17, 2023 at 23:27
  • 1
    Ah, I missed that part, and assumed that you'd write a custom front-end template to display them. The article you linked to in reply to Tom seems like a good approach then 👍🏻
    – Ian Dunn
    Aug 18, 2023 at 14:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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