Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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] );
} );

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.

enter image description here

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?

share|improve this question
Maybe a clue into a hacky solution, but when you click "select files" you can past an url into the file explorer that opens, instead of a file location. –  Wyck Mar 29 '13 at 21:55
Are you talking about the operating system's Open File modal? That doesn't work for me in Fedora, so it may be OS-dependent. This is for a distributed plugin too, so the UI needs to be intuitive. –  Ian Dunn Mar 29 '13 at 23:23
yes correct, it works on some OS's. –  Wyck Mar 30 '13 at 2:44
add comment

3 Answers

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.


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'


            createStates: function() {
                var options = this.options;

                // Add the default states.
                    // 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 ) {
                    '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()

                this.content.set( view );

            editSelectionContent: function() {
                var state = this.state(),
                    selection = state.get('selection'),

                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

                view.toolbar.set( 'backToLibrary', {
                    text:     'Return to Library',
                    priority: -100,

                    click: function() {

                // 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() {
                }).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');

                        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-

share|improve this answer
Thanks Brendan, that's the same conclusion that I came to. I tried extending the Post frame, but couldn't get it working quickly and had to take a different approach. I'd love to see the code if you get it working, though. –  Ian Dunn Apr 2 '13 at 19:57
add comment

From examinig the source code it seems the generic media modal does not support "Insert from URL". The one way I was able to get that tab was to specify the "post" frame type:

var frame = wp.media( {
                            title       : 'Widget Uploader',
                            multiple    : false,
                            library     : { type : 'image' },
                            button      : { text : 'Select Image' },
                            frame      : 'post'
                        } );

The downside is the specified modal's title is ignored.

share|improve this answer
That does partially work, but the button says "Insert into Post" and doesn't actually submit anything, probably because it's expecting to be on a post, instead of inside a widget. It also adds the Create Gallery tab, which I don't want, because those can't be inserted into the widget. –  Ian Dunn Mar 28 '13 at 17:28
add comment

The point is that that tab returns an external URL to be directly inserted in the post, while the widget is supposed to return a media ID. Basically the external image needs to be transloaded onto the server.

See how/whether the plugin Grab & Save does what you want. (via)

share|improve this answer
Wether the plugin does it or not, I'd be interested in seeing how it does it, can you ellaborate? –  Tom J Nowell Mar 27 '13 at 17:09
Doesn't the media library handle downloading the external image to the local server for you? Even if it doesn't, the main question is: How do you get the tab to even show up in the first place? –  Ian Dunn Mar 27 '13 at 17:40
I checked, and the media library doesn't download/attach images inserted from URLs; it just links directly to them. That's not really relevant to the question, though, I'm just concerned with how to add the Insert From URL tab to the modal. –  Ian Dunn Mar 27 '13 at 18:43
add comment

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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