meta box on new admin page

You are trying to create a new admin page with add_pages_page(). Meta boxes are only for existing post types, not for admin pages. You need to create your fields like on a normal admin page.

You should read the official codex about this first: Creating Options Pages

For a simple start take a look at this example:

function register_homepage_subpage() {
    add_pages_page( 'Customize Homepage', 'Homepage', 'manage_options', 'my_homepage', 'my_homepage_callback' );

    //call to register settings function, to create our settings group
    add_action( 'admin_init', 'register_homepage_settings' );
}
add_action( 'admin_menu', 'register_homepage_subpage' );

We create your admin page like you did already in your code. We also add an add_action call to another function which will register your new admin settings:

function register_homepage_settings() {
    //register our settings
    register_setting( 'my-homepage-settings-group', 'my_option' );
}

After this you can now start your callback function:

function my_homepage_callback() { ?>
    <div class="wrap">
        <h1>My Title</h1>

        <form method="post" action="options.php">

            <?php // get registered fields and section
            settings_fields( 'my-homepage-settings-group' ); 
            do_settings_sections( 'my-homepage-settings-group' ); ?>

            <table class="form-table">
                <tr valign="top">
                    <th scope="row">My Field</th>
                    <td><input type="text" name="my_option" value="<?php echo esc_attr( get_option('my_option') ); ?>" /></td>
                </tr>
            </table>

            <?php submit_button(); ?>
        </form>

    </div>
<?php }

We need the form created in this function and also the submit_button(); to be able to save the new field.

We get our registered settings with settings_fields( 'my-homepage-settings-group' );.

After you added this code, you should see an heading and also a new text-field on your admin page. You can enter data and save it already.


To create an media upload / image select field you will need some more code and also some jQuery.

First you need a new button to open the wp.media window. In your callback function, add a new button directly after (this is important later on) our text field:

...
<td>
    <input type="text" name="my_option" value="<?php echo esc_attr( get_option('my_option') ); ?>" />
    <input type="button" class="button upload_image_button" value="Add Image" />
</td>
...

Than we have to enqueue the default wp_enqueue_media(); and also another JS file which will hold our code for the wp.media window.

function prfx_admin_enqueue() {
    // enqueue default WP media uploader
    wp_enqueue_media();

    // enqueue our media uploader script
    wp_enqueue_script( 'prfx-media-script', plugins_url('scripts.js', __FILE__) );
}
add_action( 'admin_enqueue_scripts', 'prfx_admin_enqueue' );

Now we need to create the scripts.js file and its content:

jQuery(document).ready(function($){
    // if button with class upload_image_button is clicked
    $( '.upload_image_button' ).on('click', function() {

        var mediaUploader;
        var button = $(this); // the button with the class uploaded_image_button

        // If the uploader object has already been created, reopen the dialog
        if (mediaUploader) {
            mediaUploader.open();
            return;
        }

        // Extend the wp.media object
        mediaUploader = wp.media.frames.file_frame = wp.media({
            title: 'Choose Image', // title text
            button: { text: 'Add Image' }, // but7on text
            library: { type: 'image' }, // type
            multiple: false // only one selection
        });

        // When a file is selected, grab the URL and set it as the text field's value
        mediaUploader.on('select', function() {
            var attachment = mediaUploader.state().get('selection').first().toJSON();

            // find the text input field (previous element from button) and add the new value (URL from media)
            $(button).prev().val(attachment.url);
        });

        // Open the uploader dialog
        mediaUploader.open();

        return false;
    });
});

If you now click the new button, a normal WP media window should open. If you select an image and click on “Add Image” the URL from the image should be added to the text-field as the new value.