1

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.

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.

4
  • 1
    Try it with dumping $GLOBALS['wpdb']->last_query and $GLOBALS['wpdb']->last_result in a callback hooked to pre_get_posts. If you find the query that is responsible, you can simply narrow your scenario down until all other queries are excluded. Update your question in between.
    – kaiser
    Commented Aug 26, 2013 at 15:37
  • Is this going to work for AJAX calls aswell? Should i dump it in the AJAX call itself? Or can I just dump it on the admin page and refresh the page? Commented Aug 28, 2013 at 7:54
  • I'd say: "Give it a try!" :) ... dump inside your ajax calls, check your dev tools to inspect the return value.
    – kaiser
    Commented Aug 28, 2013 at 8:07
  • I feel I'm almost there. Could someone please enlighten me? Commented Aug 29, 2013 at 10:02

2 Answers 2

4

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.

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.