Adding custom image fields and other fields at the same time

I basically want to have a custom CMS page that has pairs of images and labels for those images defined within it. I intend to use these pairs of items for populating content on a custom page of my new WordPress theme.

I have managed to make a new settings page within the CMS and populate any number of text boxes within it all thanks to Handling Plugins Options in WordPress 2.8 with register_setting().

Read More

I just now need to add fields for each text box that let a user open up the media browser and then select an existing image uploaded to WordPress or upload a new one for selection.

I haven’t been able to find any simple clean examples of this online. Even the questions related to this on Stack Overflow I found don’t seem to have a clear elegant example of this working.

Just for the record; I initially planned to do this with a plugin (Developers Custom Fields) which I already have installed, thinking it would be very easy with this plugin to add custom image fields because it is pretty simple adding text boxes with it. However I seem to be wrong and no clear examples exist for specifying this kind of field.

If you know of any examples that illustrate adding custom image fields (irrelevant of adding other fields at the same time), either using native WordPress API code or integration with the Developers Custom Fields plugin.

Related posts

Leave a Reply

3 comments

  1. You don’t exactly mention it, but it seems that you are dealing with Repeatable Fields.

    Two plugins of interest: Advanced Custom Fields and Custom Content Type Manager.

    Today, I’ve seen this article in one of WordPress communities at G+:
    Using the WordPress 3.5 Media Uploader within plugins.

    This solution was originally published here in a SO Question that has been deleted since. It is rewritten, revised and tested:

    <?php
    /**
     * Plugin Name: (SO) Repeatable fields demo
     * Plugin URI:  http://stackoverflow.com/a/14452453/1287812
     * Description: How to make repeatable fields in a meta box
     * Author:      brasofilo
     * License:     GPLv3
     */
    
    add_action( 'admin_init', 'add_post_gallery_so_14445904' );
    add_action( 'admin_head-post.php', 'print_scripts_so_14445904' );
    add_action( 'admin_head-post-new.php', 'print_scripts_so_14445904' );
    add_action( 'save_post', 'update_post_gallery_so_14445904', 10, 2 );
    
    /**
     * Add custom Meta Box to Posts post type
     */
    function add_post_gallery_so_14445904() 
    {
        add_meta_box(
            'post_gallery',
            'Custom Uploader',
            'post_gallery_options_so_14445904',
            'post',
            'normal',
            'core'
        );
    }
    
    /**
     * Print the Meta Box content
     */
    function post_gallery_options_so_14445904() 
    {
        global $post;
        $gallery_data = get_post_meta( $post->ID, 'gallery_data', true );
    
        // Use nonce for verification
        wp_nonce_field( plugin_basename( __FILE__ ), 'noncename_so_14445904' );
    ?>
    
    <div id="dynamic_form">
    
        <div id="field_wrap">
        <?php 
        if ( isset( $gallery_data['image_url'] ) ) 
        {
            for( $i = 0; $i < count( $gallery_data['image_url'] ); $i++ ) 
            {
            ?>
    
            <div class="field_row">
    
              <div class="field_left">
                <div class="form_field">
                  <label>Image URL</label>
                  <input type="text"
                         class="meta_image_url"
                         name="gallery[image_url][]"
                         value="<?php esc_html_e( $gallery_data['image_url'][$i] ); ?>"
                  />
                </div>
                <div class="form_field">
                  <label>Description</label>
                  <input type="text"
                         class="meta_image_desc"
                         name="gallery[image_desc][]"
                         value="<?php esc_html_e( $gallery_data['image_desc'][$i] ); ?>"
                  />
                </div>
              </div>
    
              <div class="field_right image_wrap">
                <img src="<?php esc_html_e( $gallery_data['image_url'][$i] ); ?>" height="48" width="48" />
              </div>
    
              <div class="field_right">
                <input class="button" type="button" value="Choose File" onclick="add_image(this)" /><br />
                <input class="button" type="button" value="Remove" onclick="remove_field(this)" />
              </div>
    
              <div class="clear" /></div> 
            </div>
            <?php
            } // endif
        } // endforeach
        ?>
        </div>
    
        <div style="display:none" id="master-row">
        <div class="field_row">
            <div class="field_left">
                <div class="form_field">
                    <label>Image URL</label>
                    <input class="meta_image_url" value="" type="text" name="gallery[image_url][]" />
                </div>
                <div class="form_field">
                    <label>Image Link</label>
                    <input class="meta_image_desc" value="" type="text" name="gallery[image_desc][]" />
                </div>
            </div>
            <div class="field_right image_wrap">
            </div> 
            <div class="field_right"> 
                <input type="button" class="button" value="Choose File" onclick="add_image(this)" />
                <br />
                <input class="button" type="button" value="Remove" onclick="remove_field(this)" /> 
            </div>
            <div class="clear"></div>
        </div>
        </div>
    
        <div id="add_field_row">
          <input class="button" type="button" value="Add Field" onclick="add_field_row();" />
        </div>
    
    </div>
    
      <?php
    }
    
    /**
     * Print styles and scripts
     */
    function print_scripts_so_14445904()
    {
        // Check for correct post_type
        global $post;
        if( 'post' != $post->post_type )
            return;
        ?>  
        <style type="text/css">
          .field_left {
            float:left;
          }
    
          .field_right {
            float:left;
            margin-left:10px;
          }
    
          .clear {
            clear:both;
          }
    
          #dynamic_form {
            width:580px;
          }
    
          #dynamic_form input[type=text] {
            width:300px;
          }
    
          #dynamic_form .field_row {
            border:1px solid #999;
            margin-bottom:10px;
            padding:10px;
          }
    
          #dynamic_form label {
            padding:0 6px;
          }
        </style>
    
        <script type="text/javascript">
            function add_image(obj) {
                var parent=jQuery(obj).parent().parent('div.field_row');
                var inputField = jQuery(parent).find("input.meta_image_url");
    
                tb_show('', 'media-upload.php?TB_iframe=true');
    
                window.send_to_editor = function(html) {
                    var url = jQuery(html).find('img').attr('src');
                    inputField.val(url);
                    jQuery(parent)
                    .find("div.image_wrap")
                    .html('<img src="'+url+'" height="48" width="48" />');
    
                    // inputField.closest('p').prev('.awdMetaImage').html('<img height=120 width=120 src="'+url+'"/><p>URL: '+ url + '</p>'); 
    
                    tb_remove();
                };
    
                return false;  
            }
    
            function remove_field(obj) {
                var parent=jQuery(obj).parent().parent();
                //console.log(parent)
                parent.remove();
            }
    
            function add_field_row() {
                var row = jQuery('#master-row').html();
                jQuery(row).appendTo('#field_wrap');
            }
        </script>
        <?php
    }
    
    /**
     * Save post action, process fields
     */
    function update_post_gallery_so_14445904( $post_id, $post_object ) 
    {
        // Doing revision, exit earlier **can be removed**
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )  
            return;
    
        // Doing revision, exit earlier
        if ( 'revision' == $post_object->post_type )
            return;
    
        // Verify authenticity
        if ( !wp_verify_nonce( $_POST['noncename_so_14445904'], plugin_basename( __FILE__ ) ) )
            return;
    
        // Correct post type
        if ( 'post' != $_POST['post_type'] ) 
            return;
    
        if ( $_POST['gallery'] ) 
        {
            // Build array for saving post meta
            $gallery_data = array();
            for ($i = 0; $i < count( $_POST['gallery']['image_url'] ); $i++ ) 
            {
                if ( '' != $_POST['gallery']['image_url'][ $i ] ) 
                {
                    $gallery_data['image_url'][]  = $_POST['gallery']['image_url'][ $i ];
                    $gallery_data['image_desc'][] = $_POST['gallery']['image_desc'][ $i ];
                }
            }
    
            if ( $gallery_data ) 
                update_post_meta( $post_id, 'gallery_data', $gallery_data );
            else 
                delete_post_meta( $post_id, 'gallery_data' );
        } 
        // Nothing received, all fields are empty, delete option
        else 
        {
            delete_post_meta( $post_id, 'gallery_data' );
        }
    }
    

    Result:
    repeatable uploader

  2. I used @brasofilo‘s answer above, however I really missed having the image ID saved as well, so I could use wp_get_attachment_imagefor example.

    So here’s my revised code, which also saves the image ID. I also use two arrays to define on which page templates and respectively on which post types the meta box appears. Here’s the code:

    <?php
    /**
     * Plugin Name: Repeatable image fields
     * Plugin URI:  https://stackoverflow.com/a/36456957/4800442
     * Description: Repeatable image fields in a meta box.
     * Author:      brasofilo
     * License:     GPLv3
     */
    
    add_action( 'admin_init', 'add_post_gallery_so_14445904' );
    add_action( 'add_meta_boxes_page', 'add_page_gallery_so_14445904' );
    add_action( 'admin_head-post.php', 'print_scripts_so_14445904' );
    add_action( 'admin_head-post-new.php', 'print_scripts_so_14445904' );
    add_action( 'save_post', 'update_post_gallery_so_14445904', 10, 2 );
    
    // Make it work only in selected templates
    $rep_fields_templates = array('page-aboutus.php');
    $rep_fields_posts     = array('service',
                                  'style',
                                  'brand');
    
    /**
     * Add custom Meta Box
     */
    
    // Add meta box to custom posts 
    function add_post_gallery_so_14445904() 
    {
        global $rep_fields_posts;       
        add_meta_box(
            'post_gallery',
            'Slideshow Gallery',
            'post_gallery_options_so_14445904',
            $rep_fields_posts,
            'normal',
            'core'
        );
    }
    
    // Add meta box to custom page templates
    function add_page_gallery_so_14445904() 
    {       
        global $post, $rep_fields_templates;
        if ( in_array( get_post_meta( $post->ID, '_wp_page_template', true ), $rep_fields_templates ) ) {
            add_meta_box(
                'post_gallery',
                'Slideshow Gallery',
                'post_gallery_options_so_14445904',
                'page',
                'normal',
                'core'
            );
        }
    }
    
    /**
     * Print the Meta Box content
     */
    function post_gallery_options_so_14445904() 
    {
        global $post;
        $gallery_data = get_post_meta( $post->ID, 'gallery_data', true );
    
        // Use nonce for verification
        wp_nonce_field( plugin_basename( __FILE__ ), 'noncename_so_14445904' );
        ?>
    
        <div id="dynamic_form">
    
            <div id="field_wrap">
            <?php 
            if ( isset( $gallery_data['image_url'] ) ) 
            {
                for( $i = 0; $i < count( $gallery_data['image_url'] ); $i++ ) 
                {
                ?>
    
                <div class="field_row">
    
                  <div class="field_left">
                    <div class="form_field">
                      <!--<label>Image URL</label>-->
                      <input type="hidden"
                             class="meta_image_url"
                             name="gallery[image_url][]"
                             value="<?php esc_html_e( $gallery_data['image_url'][$i] ); ?>"
                      />
                      <input type="hidden"
                             class="meta_image_id"
                             name="gallery[image_id][]"
                             value="<?php esc_html_e( $gallery_data['image_id'][$i] ); ?>"
                      />
                    </div>
                    <div class="form_field" style="margin-bottom: 20px">
                      <label>Description</label>
                      <textarea
                             class="meta_image_desc"
                             name="gallery[image_desc][]"
                             rows="3"
                             style="width: 100%"><?php esc_html_e( $gallery_data['image_desc'][$i] ); ?></textarea>
                    </div>
                    <input class="button" type="button" value="Choose File" onclick="add_image(this)" />&nbsp;&nbsp;&nbsp;
                    <input class="button" type="button" value="Remove" onclick="remove_field(this)" />
                  </div>
    
                  <div class="field_right image_wrap">
                    <img src="<?php esc_html_e( $gallery_data['image_url'][$i] ); ?>" />
                  </div>
                  <div class="clear" /></div> 
                </div>
                <?php
                } // endif
            } // endforeach
            ?>
            </div>
    
            <div style="display:none" id="master-row">
            <div class="field_row">
                <div class="field_left">
                    <div class="form_field">
                        <!--<label>Image URL</label>-->
                        <input class="meta_image_url" value=""  name="gallery[image_url][]" />
                        <input class="meta_image_id" value=""  name="gallery[image_id][]" />
                    </div>
                    <div class="form_field" style="margin-bottom: 20px">
                        <label>Description</label>
                        <textarea class="meta_image_desc" name="gallery[image_desc][]" rows="3" style="width: 100%"></textarea>
                    </div>
                    <input type="button" class="button" value="Choose Image" onclick="add_image(this)" />&nbsp;&nbsp;&nbsp;
                    <input class="button" type="button" value="Remove" onclick="remove_field(this)" />
                </div>
                <div class="field_right image_wrap">
                </div>
                <div class="clear"></div>
            </div>
            </div>
    
            <div id="add_field_row">
              <input class="button" type="button" value="Add Image" onclick="add_field_row();" />
            </div>
            <?php if ( 'trend' == get_post_type( $post->ID ) ) { ?>
            <p style="color: #a00;">Make sure the number if images you add is a <b>multiple of 5</b>.</p>
            <?php } ?>
        </div>
        <?php
    }
    
    /**
     * Print styles and scripts
     */
    function print_scripts_so_14445904()
    {
        // Check for correct post_type
        global $post, $rep_fields_templates, $rep_fields_posts;
        if ( !in_array( get_post_meta( $post->ID, '_wp_page_template', true ), $rep_fields_templates ) && 
             !in_array( get_post_type( $post->ID)                           , $rep_fields_posts ) )
            return;
        ?>  
        <style type="text/css">
          .field_left {
            float:left;
            width: 75%;
            padding-right: 20px;
            box-sizing:border-box;  
          }
          .field_right {
            float:left;
            width: 25%;
          }
          .image_wrap img {
              max-width: 100%;
          }
          #dynamic_form input[type=text] {
            width:100%;
          }
          #dynamic_form .field_row {
            border:1px solid #cecece;
            margin-bottom:10px;
            padding:10px;
          }
          #dynamic_form label {
            display: block;
            margin-bottom: 5px;
          }
        </style>
    
        <script type="text/javascript">
            function add_image(obj) {
    
                var parent=jQuery(obj).parent().parent('div.field_row');
                var inputField = jQuery(parent).find("input.meta_image_url");
                var inputFieldID = jQuery(parent).find("input.meta_image_id");
                var fileFrame = wp.media.frames.file_frame = wp.media({
                    multiple: false
                });
                fileFrame.on('select', function() {
                    var selection = fileFrame.state().get('selection').first().toJSON();
                    inputField.val(selection.url);
                    inputFieldID.val(selection.id);
                    jQuery(parent)
                    .find("div.image_wrap")
                    .html('<img src="'+selection.url+'" />');
                });
                fileFrame.open();
            //});
            };
    
            function remove_field(obj) {
                var parent=jQuery(obj).parent().parent();
                parent.remove();
            }
    
            function add_field_row() {
                var row = jQuery('#master-row').html();
                jQuery(row).appendTo('#field_wrap');
            }
        </script>
        <?php
    }
    
    /**
     * Save post action, process fields
     */
    function update_post_gallery_so_14445904( $post_id, $post_object ) 
    {
        // Doing revision, exit earlier **can be removed**
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )  
            return;
    
        // Doing revision, exit earlier
        if ( 'revision' == $post_object->post_type )
            return;
    
        // Verify authenticity
        if ( !wp_verify_nonce( $_POST['noncename_so_14445904'], plugin_basename( __FILE__ ) ) )
            return;
    
        global $rep_fields_templates, $rep_fields_posts;
        if ( !in_array( get_post_meta( $post_id, '_wp_page_template', true ), $rep_fields_templates ) && 
             !in_array( get_post_type( $post_id)                            , $rep_fields_posts ) ) 
            return;
    
        if ( $_POST['gallery'] ) 
        {
            // Build array for saving post meta
            $gallery_data = array();
            for ($i = 0; $i < count( $_POST['gallery']['image_url'] ); $i++ ) 
            {
                if ( '' != $_POST['gallery']['image_url'][ $i ] ) 
                {
                    $gallery_data['image_url'][]  = $_POST['gallery']['image_url'][ $i ];
                    $gallery_data['image_id'][]  = $_POST['gallery']['image_id'][ $i ];
                    $gallery_data['image_desc'][] = $_POST['gallery']['image_desc'][ $i ];
                }
            }
    
            if ( $gallery_data ) 
                update_post_meta( $post_id, 'gallery_data', $gallery_data );
            else 
                delete_post_meta( $post_id, 'gallery_data' );
        } 
        // Nothing received, all fields are empty, delete option
        else 
        {
            delete_post_meta( $post_id, 'gallery_data' );
        }
    }
    

    Here’s an example of how you can output the data to your theme.

    <?php 
    if ( '' != get_post_meta( get_the_ID(), 'gallery_data', true ) ) { $gallery = get_post_meta( get_the_ID(), 'gallery_data', true ); }
    
    if ( isset( $gallery['image_id'] ) ) :
    
        for( $i = 0; $i < count( $gallery['image_id'] ); $i++ ) { 
            if ( '' != $gallery['image_id'][$i] ) {
                echo wp_get_attachment_image( $gallery['image_id'][$i], 'gallery_large' );
                if ( isset($gallery['image_desc'][$i]) ) echo $gallery['image_desc'][$i];
            }   
        }
    
    endif; 
    ?>
    
  3. For WordPress 3.5 new media uploader will open with this script change script with old one.

    <script type="text/javascript">
        function add_image(obj) {
    
            var parent=jQuery(obj).parent().parent('div.field_row');
            var inputField = jQuery(parent).find("input.meta_image_url");
            var fileFrame = wp.media.frames.file_frame = wp.media({
                multiple: false
            });
            fileFrame.on('select', function() {
                var url = fileFrame.state().get('selection').first().toJSON();
                inputField.val(url.url);
                jQuery(parent)
                .find("div.image_wrap")
                .html('<img src="'+url.url+'" height="48" width="48" />');
            });
            fileFrame.open();
        //});
        };
    
        function reomove_field(obj) {
            var parent=jQuery(obj).parent().parent();
            //console.log(parent)
            parent.remove();
        }
    
        function add_field_row() {
            var row = jQuery('#master-row').html();
            jQuery(row).appendTo('#field_wrap');
        }
    </script>