Is it possible to reuse wp.media.editor Modal for dialogs other than media

To expand: I’d like to utilize the same Modal code/appearance ( as used in wp.media.Modal, wp.media.FocusManager ) to open a modal of my own custom dialog, not the Media Editor. In the past, I’ve used thickbox for this sort of thing, but wp.media.Modal appears to be the way of the future for modals — Not to mention it looks cool.

I’ve poked at the JS source a bit and have come to a couple possible solutions:

Read More
  1. “Borrow” the code media-views.js and use it in my plugin.
  2. “Extend” wp.media.Modal ( it is a Backbone View, after all ).
  3. Create a custom implementation, jQueryUI, etc.
  4. Just give up and use thickbox.

Borrowing seems slightly less dangerous than using wp.media.Model.extend({}), but wasteful. I’m not a big fan of jQueryUI’s modals, but it would get the job done. At the same time, I could do a custom implementation of modals ( or base it off another lib ).

Feels like I’m missing something obvious: Has anyone else pulled this off or is the new media library modal code “too new” to allow for reuse?

Related posts

Leave a Reply

1 comment

  1. Late answer & edit. Disclaimer: The following is no copy & paste-togo code.

    Rough Sketch

    As I never tried misusing the media modal for anything else, here’s a short overview, sketched by breaking out a part from a project I’m currently on. It’s not a ready to go example, but it should bring you close enough. Just read the comments carefully and implement the following PHP in your objects.

    PHP

    In our constructor we register our scripts, add meta boxes that hold info and a media button, filter in additional MIME Types (for e.g. ZIP) and care about saving the additional data:

    public function __construct()
    {
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
    
        foreach( $this->post_types as $post_type )
            add_action( "add_meta_boxes_{$post_type}", array( $this, 'add_meta_box' ) );
    
        add_filter( 'media_view_settings', array( $this, 'filter_media_view_settings' ), 10, 2 );
    
        add_action( 'wp_insert_post_data', array( $this, 'wp_insert_post_data' ), 10, 2 );
    }
    

    Make sure that you abort if you don’t need that script on a particular page. This saves memory, request time and helps keeping your installation clean.

    public function enqueue_scripts( $page )
    {
        if (
            ! in_array( $page, array( 'post.php', 'post-new.php' ) )
            # Assuming that there's a class property array that holds post types we want to add to
            # OR ! in_array( get_current_screen()->post_type, array_keys( $this->post_types ) )
        )
            return;
    
        wp_enqueue_media();
        wp_enqueue_script(
            'wpse_media_modal',
            plugins_url( 'assets/js/media-modal.js', dirname( __FILE__ ) ),
            array(
                # 'jquery',
                'media-views'
            ),
            null,
            true
        );
        wp_localize_script(
            'wpse_media_modal',
            'wpse_obj',
            $this->get_media_props()
        );
    }
    

    Then we add the meta box. Inside the function, we can rely on the $post objects post_type property, which will be set for new posts as well. As we already registered the callbacks in the constructor to the appropriate contextual hooks, we can simply take whatever post type comes along.

    public function add_meta_box( $post )
    {
        add_meta_box(
            'wprd_upload',
            __( 'Upload', 'our_textdomain' ),
            array( $this, 'render_content' ),
            $post->post_type,
            'advanced',
            'default',
            array()
        );
    }
    

    Additional MIME Types

    Simply throw in an array that either overrides or adds to the default MIME types of the Media Modal. You can as well add or override other settings. Just var_dump( $settings ); to see what the callback provides. Also make sure we don’t intercept on the wrong post type.

    public function filter_media_view_settings( $settings, $post )
    {
        if ( ! in_array( $post->post_type, array_keys( $this->post_types ) ) )
            return $settings;
    
        $settings['mimeTypes'] += array( 'application/zip' );
    
        return $settings;
    }
    

    Render the content

    public function render_content()
    {
        $props = array(
            'modalTitle'      => __( 'Select ZIP Archives', 'our_textdomain' ),
    
            // The following data is what we will access later
            // SomeIDfromLocalizedScriptOBJ
            'buttonID'        => 'open-media-lib',
            'buttonClass'     => 'open-media-button',
            'buttonText'      => __( 'Add ZIP', 'our_textdomain' ),
            'buttonDataText'  => __( 'Select', 'our_textdomain' ),
            'buttonDataTitle' => __( 'Select Whatever', 'our_textdomain' ),
    
            'mimeTypes'       => array(
                $zip => __( 'ZIP Archive', 'our_textdomain' ),
            ),
        );
    
        wp_nonce_field( plugin_basename( __FILE__ ), $this->nonce_name );
        ?>
        <input type="button"
               class="button <?php echo $props['buttonClass']; ?>"
               id="<?php echo $props['buttonID']; ?>"
               value="<?php echo $props['buttonText']; ?>"
               data-title="<?php echo $props['buttonDataTitle']; ?>"
               data-button-text="<?php echo $props['buttonDataText']; ?>" />
    }
    

    Save the data

    Finally we make sure our data gets saved properly and will be checked. Use all the esc_*() functions, typecasting, nonces and what not.

    public function wp_insert_post_data( $data, $post_array )
    {
        if (
            ! in_array( $post_array['post_type'], array_keys( $this->post_types ) )
            # OR ( defined( 'DOING_AUTOSAVE' ) AND DOING_AUTOSAVE )
            OR ! isset( $_POST[ $this->nonce_name ] )
            OR ! wp_verify_nonce( $_POST[ $this->nonce_name ], plugin_basename( __FILE__ ) )
        )
            return $data;
    
        $post_array['zip'] = array_map( 'array_filter', $post_array['zip'] );
    
        $id = $post_array['ID'];
        update_post_meta(
            $id,
            'zip',
            $post_array['zip'],
            get_post_meta( $id, 'zip' )
        );
    
        return $data;
    }
    

    Final note, before heading over to the JS example: The code is broken out of a current project. So it will – as already mentioned – not work by default! It’s only a guide and nothing else.

    Javascript

    The javascript itself is pretty straight forward. Not. But as you can see, I’m injecting both the jQuery as the custom localized script object into the function. From there on, you’ll have to add whatever logic you might need. The basic surroundings for different states and callbacks are provided and console.log()s are present.

    var ds = ds || {};
    
    ( function( $, obj ) {
        var media;
    
        ds.media = media = {};
    
        _.extend( media, {
            view: {},
            controller: {}
        } );
    
        media.buttonID    = '#' + obj.buttonID,
    
        _.extend( media, {
            frame: function() {
                if ( this._frame )
                    return this._frame;
    
                var states = [
                    new wp.media.controller.Library(),
                    new wp.media.controller.Library( {
                        id:                 'image',
                        title:              'Images',
                        priority:           20,
                        searchable:         false,
                        library:            wp.media.query( { type: 'image' } ),
                        multiple:           true
                    } ),
                    /*new wp.media.controller.Library( {
                        id:                 'video',
                        title:              'Video',
                        priority:           40,
                        library:            wp.media.query( { type: 'video' } ),
                        multiple:           false,
                        contentUserSetting: false // Show the Upload Files tab.
                    } ),*/
                    new wp.media.controller.Library( {
                        id:                 obj.SomeIDfromLocalizedScriptOBJ,
                        title:              obj.SomeTitlefromLocalizedScriptOBJ,
                        priority:           30,
                        searchable:         true,
                        // filterable:         'uploaded',
                        library:            wp.media.query( { type: obj.SomeMIMETypesfromLocalizedScriptOBJ } ),
                        multiple:           true
                        // contentUserSetting: true
                    } ),
                ];
    
                this._frame = wp.media( {
                    // className: 'media-frame no-sidebar',
                    states: states
                    // frame: 'post'
                } );
    
                this._frame.on( 'open', this.open );
    
                this._frame.on( 'ready', this.ready );
    
                this._frame.on( 'close', this.close );
    
                this._frame.on( 'menu:render:default', this.menuRender );
    
                this._frame.state( 'library' ).on( 'select', this.select );
                this._frame.state( 'image' ).on( 'select', this.select );
                this._frame.state( obj.ZIPTabID ).on( 'select', this.select );
    
                return this._frame;
            },
    
            open: function() {
                console.log( 'Frame opened' );
            },
    
            ready: function() {
                console.log( 'Frame ready' );
            },
    
            close: function() {
                console.log( 'Frame closed' );
            },
    
            menuRender: function( view ) {
                /* view.unset( 'library-separator' );
                view.unset( 'embed' );
                view.unset( 'gallery' ); */
            },
    
            select: function() {
                var settings = wp.media.view.settings,
                    selection = this.get( 'selection' );
    
                selection.map( media.showAttachmentDetails );
            },
    
            showAttachmentDetails: function( attachment ) {
                // This function normally is used to display attachments
                // Handle removal of rows
                media.removeAttachmentRow( /* some var */ );
            },
    
            removeAttachmentRow: function( row ) {
                // Remove stuff callback
            },
    
            init: function() {
                // Open media frame
                $( media.buttonID ).on( 'click.media_frame_open', function( e ) {
                    e.preventDefault();
    
                    media.frame().open();
                } );
            }
        } );
    
        $( media.init );
    } )( jQuery, wpse_obj );
    

    Tutorials

    Dominik Schilling – the author of the WP 3.5 media manager – has written a set of demos for media modals. You can view them on GitHub.