I’ve inserted a WP 3.5 media uploader into a widget by running this JavaScript when a button is clicked on:
var frame = wp.media( {
title : 'Widget Uploader',
multiple : false,
library : { type : 'image' },
button : { text : 'Select Image' }
} );
frame.on( 'close', function() {
var attachments = frame.state().get( 'selection' ).toJSON();
imageWidget.render( widget_id, widget_id_string, attachments[0] );
} );
frame.open();
return false;
That gives me a modal that has the “Upload Files” and “Media Library” tabs, but I also want it to have the “Insert From URL” tab that you get when you click on the “Add Media” button while editing a post/page.
I’ve spent a couple hours digging around the web, reading through the source code and and watching Daryl Koopersmith’s presentation on the uploader’s backend, but haven’t been able to figure it out.
Can someone point me in the right direction? Is there a parameter I can pass to wp.media() to include it, or should I use one of the built-in views/models that includes it?
I’ve been digging through the source code for a similar reason; I’d like to add the “Attachment Display Settings” to the default “select” frame. As far as I can tell, this can’t be done by passing parameters to wp.media(), as we would all like. wp.media currently has the two frames (“post” and “select”), and the views that accompany them are preset.
The approach I’m looking at now is to extend media.view.mediaFrame to create a new frame (based on the “select” frame) that includes the parts of the view I need. If I get this working I’ll post the code.
EDIT:
Ian, I got the feature I wanted working and took some time to figure out yours. wp.media() is not quite as modular as it could be. It only accepts the values ‘select’ and ‘post’ for frame, with ‘select’ being the default, so you can’t create a new frame object. Instead, you need to extend one of the two frame objects (all this is in /wp-includes/js/media-views.js), which is also kind of clunky. Adding part of the UI is a several-step process. You could start with Select and add on, but for yours I chose to start with the code in the Post frame and take away the gallery stuff. Here’s my code, working but not heavily tested. Probably some room for streamlining, too.
wp.media.view.MediaFrame.Select = wp.media.view.MediaFrame.Select.extend({
initialize: function() {
wp.media.view.MediaFrame.prototype.initialize.apply( this, arguments );
_.defaults( this.options, {
multiple: true,
editing: false,
state: 'insert'
});
this.createSelection();
this.createStates();
this.bindHandlers();
this.createIframeStates();
},
createStates: function() {
var options = this.options;
// Add the default states.
this.states.add([
// Main states.
new wp.media.controller.Library({
id: 'insert',
title: 'Insert Media',
priority: 20,
toolbar: 'main-insert',
filterable: 'image',
library: wp.media.query( options.library ),
multiple: options.multiple ? 'reset' : false,
editable: true,
// If the user isn't allowed to edit fields,
// can they still edit it locally?
allowLocalEdits: true,
// Show the attachment display settings.
displaySettings: true,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: true
}),
// Embed states.
new wp.media.controller.Embed(),
]);
if ( wp.media.view.settings.post.featuredImageId ) {
this.states.add( new wp.media.controller.FeaturedImage() );
}
},
bindHandlers: function() {
// from Select
this.on( 'router:create:browse', this.createRouter, this );
this.on( 'router:render:browse', this.browseRouter, this );
this.on( 'content:create:browse', this.browseContent, this );
this.on( 'content:render:upload', this.uploadContent, this );
this.on( 'toolbar:create:select', this.createSelectToolbar, this );
//
this.on( 'menu:create:gallery', this.createMenu, this );
this.on( 'toolbar:create:main-insert', this.createToolbar, this );
this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
var handlers = {
menu: {
'default': 'mainMenu'
},
content: {
'embed': 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each( handlers, function( regionHandlers, region ) {
_.each( regionHandlers, function( callback, handler ) {
this.on( region + ':render:' + handler, this[ callback ], this );
}, this );
}, this );
},
// Menus
mainMenu: function( view ) {
view.set({
'library-separator': new wp.media.View({
className: 'separator',
priority: 100
})
});
},
// Content
embedContent: function() {
var view = new wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
this.content.set( view );
view.url.focus();
},
editSelectionContent: function() {
var state = this.state(),
selection = state.get('selection'),
view;
view = new wp.media.view.AttachmentsBrowser({
controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: wp.media.view.Attachment.EditSelection
}).render();
view.toolbar.set( 'backToLibrary', {
text: 'Return to Library',
priority: -100,
click: function() {
this.controller.content.mode('browse');
}
});
// Browse our library of attachments.
this.content.set( view );
},
// Toolbars
selectionStatusToolbar: function( view ) {
var editable = this.state().get('editable');
view.set( 'selection', new wp.media.view.Selection({
controller: this,
collection: this.state().get('selection'),
priority: -40,
// If the selection is editable, pass the callback to
// switch the content mode.
editable: editable && function() {
this.controller.content.mode('edit-selection');
}
}).render() );
},
mainInsertToolbar: function( view ) {
var controller = this;
this.selectionStatusToolbar( view );
view.set( 'insert', {
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function() {
var state = controller.state(),
selection = state.get('selection');
controller.close();
state.trigger( 'insert', selection ).reset();
}
});
},
featuredImageToolbar: function( toolbar ) {
this.createSelectToolbar( toolbar, {
text: 'Set Featured Image',
state: this.options.state || 'upload'
});
},
mainEmbedToolbar: function( toolbar ) {
toolbar.view = new wp.media.view.Toolbar.Embed({
controller: this,
text: 'Insert Image'
});
}
});
This combines the code from wp.media.view.MediaFrame.Post with that from media.view.MediaFrame.Select, adjusting for the fact that this is executed outside of the original scope. The values for text are the various buttons, and you can reference your own localization object if you want. The ‘filterable’ value in the Library constructor (in createStates()) determines which media types will be supported.
Once you have extended the Select object with this approach, just instantiate it the same way you currently are and add your custom handler for when an image is selected. The Insert from Url might fire a different event than when picking from uploaded media. It would probably be better to instantiate your frame first, actually, and then extend it, so that any other media frames on the page would be unaffected, but I haven’t tried this.
Hope that helps-