17

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.

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?

3
  • 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, 2013 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, 2013 at 23:23
  • yes correct, it works on some OS's.
    – Wyck
    Mar 30, 2013 at 2:44

3 Answers 3

11
+100

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-

2
  • 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, 2013 at 19:57
  • @IanDunn This question is several years old, but I'm finding that I need the same solution. Did you maintain a solution over the years that's tested and mature? Or find plugin or other solution that met your needs? If you do have current code or a solution, could you post it as an updated answer? Thanks!
    – user658182
    Jun 24, 2017 at 3:22
1

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.

1
  • 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, 2013 at 17:28
0

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)

3
  • 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, 2013 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, 2013 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, 2013 at 18:43

Your Answer

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

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