Adding extra pages to plugin that shouldn’t appear in the sidebar

If you need admin pages that don’t appear in the menu, use add_submenu_page and set $parent to null. This will create a functional page that doesn’t get rendered in the menu.

add_action( 'admin_menu', 'wpd_hidden_plugin_submenu_page' );
function wpd_hidden_plugin_submenu_page() {
    add_submenu_page( 
        null,
        'My Hidden Submenu Page',
        'My Hidden Submenu Page',
        'manage_options',
        'my-hidden-submenu-page',
        'my_hidden_submenu_page_callback',
    );
}

Users will need whatever capability you specify to visit those pages.


If you want to create action links that do things outside the context of the admin interface, use the admin_post_ action. You use add_action to hook a handler function to admin_post_ plus the slug of your custom action. Note that there are two hooks- for logged in and not logged in visitors.

// hook your handler function for logged in users
add_action( 'admin_post_my_action', 'wpd_my_action_function' );

//hook your handler function for non logged in users
add_action( 'admin_post_nopriv_my_action', 'wpd_my_action_function' );

function wpd_my_action_function(){
    // WordPress API works in here
    status_header( 200 );

    // do your plugin stuff

    // and always die() at the end to halt execution
    die();
}

To output the URL of the handler script, use admin_url(). Note the action slug my_action is passed in the action form element. This will trigger our hooked function.

<form action="<?php echo admin_url( 'admin-post.php' ); ?>" method="post">
    <input type="hidden" name="action" value="my_action">
    <input type="hidden" name="data" value="some-data">
    <input type="submit" value="Submit">
</form>

Adding AJAX action handlers is the same as the admin_post_ action, just the tag name you hook differs, and the URL is admin-ajax.php.

add_action( 'wp_ajax_add_foobar', 'prefix_ajax_add_foobar' );
add_action( 'wp_ajax_nopriv_add_foobar', 'prefix_ajax_add_foobar' );

function prefix_ajax_add_foobar() {
    // Handle request then generate response using WP_Ajax_Response
}