How to create a custom gallery meta field for posts in WordPress

Hello!

If you read our last post on us releasing a new WordPress Plugin for Portfolio Galleries, you would have seen us touch a bit on creating custom fields / meta boxes for your post type.

One of the bigger challenges when creating the Portfolio Grid plugin was implementing the administrative jQuery to manipulate the WordPress media system in such a way to create custom galleries.

The galleries were needed for the plugin. When you view a single portfolio item, you will be brought to a single post page template that displays a modal gallery of additional portfolio images.

I’ll be walking you through how we manipulated the custom gallery field with jQuery. I will also show the final product which is the query to produce the gallery images as thumbnails with a modal window to expand the images if they are clicked.

Create a meta box for posts in WordPress

This was covered in the previous post, but it would make sense to go over it again in the context of how we manipulate the fields with jQuery. The first thing we want to do is add a meta box area when editing your posts. You can see at around line 6 that we are specifying the post as “post” – if your dealing with a custom post type, simply add the post type slug there.

// Add the Meta Box
function shift8_portfolio_add_custom_meta_box() {
    add_meta_box(
        'custom_meta_box', // $id
        'Shift8 Portfolio Fields', // $title
        'shift8_portfolio_show_custom_meta_box', // $callback
        'post', // $page
        'normal', // $context
        'high'); // $priority
}
add_action('add_meta_boxes', 'shift8_portfolio_add_custom_meta_box');

Create a field array for the gallery

You can see below that there is two fields being created : main image and gallery images. We will be focusing primarily on Gallery images, but for the main image you will see that it is very similar other than the fact that the field will always only store one image reference.

The field array is where you store all your fields whether it be checkboxes, text fields or media.

// Field Array
$prefix = 'shift8_portfolio_';
$custom_meta_fields = array(
    array(
        'label'=> 'Main Image',
        'desc'  => 'This is the main image that is shown in the grid and at the top of the single item page.',
        'id'    => $prefix.'image',
        'type'  => 'media'
    ),
    array(
        'label'=> 'Gallery Images',
        'desc'  => 'This is the gallery images on the single item page.',
        'id'    => $prefix.'gallery',
        'type'  => 'gallery'
    ),
);

Create a callback function for the meta box to display the fields

In the first code snippet, you can see that there is a reference to the callback function. Here we will draw out what the meta box fields will look like. For repeating fields we can iterate through them with a simple foreach field. You can use this logic for any field type, but in this example it will be images for the gallery.

// The Callback
function shift8_portfolio_show_custom_meta_box($object) {
        global $custom_meta_fields, $post;
        // Use nonce for verification
        echo '';

        // Begin the field table and loop
        echo '';
        foreach ($custom_meta_fields as $field) {
                // get value of this field if it exists for this post
                $meta = get_post_meta($post->ID, $field['id'], true);
                // begin a table row with
                echo '';
        } // end foreach
        echo '
'; switch($field['type']) { case 'media': $close_button = null; if ($meta) { $close_button = ''; } echo '
' . $close_button . '
'; break; case 'gallery': $meta_html = null; if ($meta) { $meta_html .= ''; } echo ' ' . $meta_html . ' '; break; } //end switch echo '
'; // end table }

There’s a few things to look at here. First, lets break down the important parts of what is being created :

Add Gallery

This button will trigger the wordpress media popup. This will be discussed in further detail later, but we are triggering the system and manipulating it from the default behavior slightly in order to be able to add images to the gallery.

Delete gallery images

Each image that is added has a little “X” in the top right, reflected by the code below :

 $meta_html .= '
  • ';

    You can see the span class “shift8_portfolio_gallery_close”. This is going to trigger a jQuery action if clicked and will be explained further.

    Trigger the WordPress Media popup with jQuery

    The first thing we want to do is enqueue an admin-only JS file so that we can execute jQuery in the administration area :

    // Register admin scripts for custom fields
    function shift8_portfolio_load_wp_admin_style() {
            wp_enqueue_media();
            wp_enqueue_script('media-upload');
            wp_enqueue_style( 'shift8_portfolio_admin_css', plugin_dir_url(dirname(__FILE__)) . 'css/shift8_portfolio_admin.css' );
            wp_enqueue_script( 'shift8_portfolio_admin_script', plugin_dir_url(dirname(__FILE__)) . 'js/shift8_portfolio_admin.js' );
    }
    add_action( 'admin_enqueue_scripts', 'shift8_portfolio_load_wp_admin_style' );
    

    You will see that we are also using the wp_enqueue_media and enqueuing the media-upload script. This is built into WordPress but needs to be loaded if you are going to use the media popup system.

    The most important thing is the shift8_portfolio_admin.js file.

    var meta_gallery_frame;
            // Runs when the image button is clicked.
            jQuery('#shift8_portfolio_gallery_button').click(function(e){
    
                    //Attachment.sizes.thumbnail.url/ Prevents the default action from occuring.
                    e.preventDefault();
    
                    // If the frame already exists, re-open it.
                    if ( meta_gallery_frame ) {
                            meta_gallery_frame.open();
                            return;
                    }
    
                    // Sets up the media library frame
                    meta_gallery_frame = wp.media.frames.meta_gallery_frame = wp.media({
                            title: shift8_portfolio_gallery.title,
                            button: { text:  shift8_portfolio_gallery.button },
                            library: { type: 'image' },
                            multiple: true
                    });
    
                    // Create Featured Gallery state. This is essentially the Gallery state, but selection behavior is altered.
                    meta_gallery_frame.states.add([
                            new wp.media.controller.Library({
                                    id:         'shift8-portfolio-gallery',
                                    title:      'Select Images for Gallery',
                                    priority:   20,
                                    toolbar:    'main-gallery',
                                    filterable: 'uploaded',
                                    library:    wp.media.query( meta_gallery_frame.options.library ),
                                    multiple:   meta_gallery_frame.options.multiple ? 'reset' : false,
                                    editable:   true,
                                    allowLocalEdits: true,
                                    displaySettings: true,
                                    displayUserSettings: true
                            }),
                    ]);
    
                    meta_gallery_frame.on('open', function() {
                            var selection = meta_gallery_frame.state().get('selection');
                            var library = meta_gallery_frame.state('gallery-edit').get('library');
                            var ids = jQuery('#shift8_portfolio_gallery').val();
                            if (ids) {
                                    idsArray = ids.split(',');
                                    idsArray.forEach(function(id) {
                                            attachment = wp.media.attachment(id);
                                            attachment.fetch();
                                            selection.add( attachment ? [ attachment ] : [] );
                                    });
                         }
                    });
    
                    meta_gallery_frame.on('ready', function() {
                            jQuery( '.media-modal' ).addClass( 'no-sidebar' );
                    });
    
                    // When an image is selected, run a callback.
                    //meta_gallery_frame.on('update', function() {
                    meta_gallery_frame.on('select', function() {
                            var imageIDArray = [];
                            var imageHTML = '';
                            var metadataString = '';
                            images = meta_gallery_frame.state().get('selection');
                            imageHTML += '';
                            metadataString = imageIDArray.join(",");
                            if (metadataString) {
                                    jQuery("#shift8_portfolio_gallery").val(metadataString);
                                    jQuery("#shift8_portfolio_gallery_src").html(imageHTML);
                                    setTimeout(function(){
                                            ajaxUpdateTempMetaData();
                                    },0);
                            }
                    });
    
                    // Finally, open the modal
                    meta_gallery_frame.open();
    
            });
    

    There’s quite a lot going on in the above jQuery. All of the above code is basically an elaborate function that is triggered once you click the “Add gallery” button.

    We are using the wp.media function (loaded by the enqueue functions earlier) to create a custom media popup. You can see that the meta_gallery variable is holding all the options for the wp.media instance.

    How the gallery system works is we are simply taking the WordPress media ID number and assigning it to an array that we will pass to the meta box field that we created earlier. The array will simply be a list of comma separated numbers with each number representing a media item. In PHP we can take these media ID numbers and generate the image URI to load the thumbnail in the admin area or the full image in the page template.

    Delete gallery items by clicking them in WordPress

    The last piece to this puzzle is being able to remove individual gallery items after adding them. We created a class “shift8_portfolio_gallery_close” earlier – now we need to trigger a jQuery function when its clicked. Clicking the close button will allow me to parse the media ID number with jQuery in order to remove it from the gallery :

            jQuery(document.body).on('click', '.shift8_portfolio_gallery_close', function(event){
    
                    event.preventDefault();
    
                    if (confirm('Are you sure you want to remove this image?')) {
    
                            var removedImage = jQuery(this).children('img').attr('id');
                            var oldGallery = jQuery("#shift8_portfolio_gallery").val();
                            var newGallery = oldGallery.replace(','+removedImage,'').replace(removedImage+',','').replace(removedImage,'');
                            jQuery(this).parents().eq(1).remove();
                            jQuery("#shift8_portfolio_gallery").val(newGallery);
                    }
    
            });
    

    We are parsing the ID number, then creating an oldGallery and newGallery variable so that we can remove the ID number from the array. The new array, minus the ID number we just deleted, is passed back to the gallery field so that when the post is saved, it will update with the new ID number array.

    Hopefully this helps others out there to create custom gallery fields in WordPress! You can download the plugin from WordPress or check out the github project.