Show selected images on top in Media Manager

I’ve searched numerous StackOverflow and WordPress Awnsers questions, but I couldn’t find any solution that matches my situation or even remotely points me in the right direction.

I have a custom media frame in my plugin where I have pre-selected certain images out of a media library of about 3000 images. However, these pre-selected images aren’t sorted on top of the list by default, which results in you browsing the list for hours to find your selected images. In short I want these to be on top of the list. I have their ID’s.

Read More

I found that in media-models.js where the code goes:

Query = media.model.Query = Attachments.extend({ ...

it handles the querying / sorting of the attachments that are displayed in the media manager.

I’ve tried extending it like this in my main plugin javascript file

wp.media.model.Query = wp.media.model.Attachments.extend({ ...

which gave me no result and numerous other suggestions in question that looked similar to my situation. Now I’m at the point where I’m out of idea’s of how to tackle this.
Could someone point me in the right direction?


EDIT: Well I have been able to influence the queries made by my media frame.

In my main.js in my plugin I do this now:

file_frame = wp.media.frames.file_frame = wp.media({
    id: 'multi_image',
    title: 'Select images',
    button: {
        text: 'Use selected images'
    },
    library: {
        type: 'image', post__in:[3322,3323,3324,3325,3326,3327,3328]
    },
    multiple: true,
}); 

This way I can show the media frame only displaying the image attachments with certain id’s. However I also want to display the rest of the attachments, only with my selected images on top. So I would need to either combine the results of this query by making a new query with post__not_in instead of post__in or by custom ordering the attacments giving the attachments with those id’s priority. I don’t know how to do that. The arguments I can pass to the library seem limited to me.

Related posts

2 comments

  1. After reading up on the Backbone.js documentation I found out it’s quite easy to manually add attachments by ID to the library of the media frame.

    You can just assign a reference to the library on the open event. Then create an instance of wp.media.attachment. Fetch it. And then call the add function on your library reference and passing the attachment with it. I had my attachment id’s in an array and looped through them like so:

    file_frame.on('open',function(){
    
            var lib = file_frame.state().get('library');
            for (var i=0;i<ids.length;i++){
                var attachment = wp.media.attachment(ids[i]);
                attachment.fetch();
                lib.add(attachment);
            }
    });
    

    EDIT: Additionally I encountered a problem where the sorting got messed up sometimes and some of my attachments weren’t being placed on top of the list. So to make sure that they did I overrode the comparator function of the library like this to force compare on ID . It used to do get(key), but I turned it into get(‘id’):

    lib.comparator = function ( a, b, options ) {
                    var key   = this.props.get('orderby'),
                    order = this.props.get('order') || 'DESC',
                    ac    = a.cid,
                    bc    = b.cid;
    
                a = a.get( 'id' );
                b = b.get( 'id' );
    
                if ( 'date' === key || 'modified' === key ) {
                    a = a || new Date();
                    b = b || new Date();
                }
    
                // If `options.ties` is set, don't enforce the `cid` tiebreaker.
                if ( options && options.ties )
                    ac = bc = null;
    
                return ( 'DESC' === order ) ? compare( a, b, ac, bc ) : compare( b, a, bc, ac );
            } 
    

    However it’s going to whine about the compare function being undefined. So I just declared it myself the same it is as in wp.media:

    compare = function( a, b, ac, bc ) {
            if ( _.isEqual( a, b ) )
                return ac === bc ? 0 : (ac > bc ? -1 : 1);
            else
                return a > b ? -1 : 1;
        };
    

    This comparator needs to be set before adding new attachments to the library. Because otherwise the problem will persist.

  2. I’ve needed to do exactly this for a plugin I’m working on and found a slightly different solution based on what Ogier did (which pointed me in the right direction thanks!)

    With the code provided by Ogier I found that the selected images were not appearing on top even with the extra comparitor code, so after a bit of digging around in the core and specifically the code for featured images I found the comparator they use for that and it seems to work fine.

    You’ll find the FeaturedImage controller in wp-includes/js/media-views.js, I took the comparator code from there and ended up with (snippet from my code):

    $('.quote-items').on('click', '.quote-edit-image', function (e) {
        e.preventDefault();
    
        var id = $(this).attr('id');
        var index = $(this).attr('data-index');
    
        var images = quote.get_product_gallery(index);
    
        file_frame = wp.media.frames.file_frame = wp.media({
            title: 'Title',
            button: {
                text: 'Select',
            },
            multiple: false,  // Set to true to allow multiple files to be selected
        });
    
        file_frame.on('open', function () {
            var lib = file_frame.state().get('library');
    
            lib.comparator = function( a, b ) {
                var aInQuery = !! this.mirroring.get( a.cid ),
                    bInQuery = !! this.mirroring.get( b.cid );
    
                if ( ! aInQuery && bInQuery ) {
                    return -1;
                } else if ( aInQuery && ! bInQuery ) {
                    return 1;
                } else {
                    return 0;
                }
            };
    
            images.forEach(function(id){
                attachment = wp.media.attachment(id);
                attachment.fetch();
                lib.add( attachment ? [ attachment ] : [] );
            });
        });
    
        file_frame.on( 'select', function() {
          // We set multiple to false so only get one image from the uploader
          attachment = file_frame.state().get('selection').first().toJSON();
    
          $('#' + id +' img').attr('src', attachment.sizes.thumbnail.url);
        });
    
        file_frame.open();
    });
    

    Note that I edited the comparator slightly to not use a compare function that isn’t defined, you could always define that if you need to.

Comments are closed.