26

I'm constantly running into the same annoyance, so i thought i'd see if there's any ideas or experience out there...

I've created a plugin that uses it's own admin page. It has to. Now that i sorted out the WP_List_Table() stuff, i must say it's great...but....

Custom plugin pages always load as admin.php?page=... unless i want to load them from the plugin directory directly, which i don't. Now if i do an 'action' from that page, i need to process that somehow and then redirect back to the page without the action parameter. No matter if i do a GET or POST, really.

On all it's internal pages WP does this on the same page, it checks if there's an action, if so processes it and then redirects to itself without the action. This is possible, because on these pages the admin-header hasn't been loaded, yet.

If you try doing it on your own page, though, half the admin interface has already been sent to the browser, so a redirect isn't possible anymore. Clearly, the solution is to POST/GET directly to another page, load the WP framework on that, do the processing and then redirect back to the original page...but...that's a bit annoying, because...my original page is loaded via a callback, so it runs within a method of my class. That's beautiful.

If i load a separate page, i have to manually include wp-load.php and am outside of my class, which is annoying, and in my particular case bugs me especially, because i'm only instanciating my plugin class anonymously so that no-one can access it from the outside.

So after this long story...did anyone come up with a good solution to load another page via a callback without having the whole admin interface already setup around it?

(I know of a workaround...i can hook a function into load-.... that checks for the action parameter and does the processing and redirect. But i'm wondering if there's a better way.)

Thanks.

4
  • Why is this tagged with [plugin-wp-pagenavi]? [plugin-development] is certainly welcome here.
    – Jan Fabry
    Commented Feb 28, 2011 at 21:10
  • @Jan Fabry: Not sure what plugin-wp-pagenavi is for...i was assuming it was for things concerning the correlation between plugins and the admin menu. Since my question is related to that, i selected that tag.
    – wyrfel
    Commented Feb 28, 2011 at 22:11
  • WP-PageNavi is a plugin with a more advanced paging navigation for the front-end. You could use [admin-menu] here, but I don't think it's really related to that. I changed the tags to what I think fits it, you can of course edit it again.
    – Jan Fabry
    Commented Mar 1, 2011 at 8:12
  • @Jan Fabry: Thanks for the re-tagging...not that familiar with the whole tag pool, yet (quite obviously).
    – wyrfel
    Commented Mar 1, 2011 at 9:52

3 Answers 3

32

As a rule of thumb, you should use a POST request for most actions, to make sure they are not executed by accident. But it is also a good practice to redirect to a normal page after a POST request, to prevent duplicate execution when the user refreshes the page.

So the flow is like this:

  1. Your plugin page with a POST form, which submits to
  2. A page that handles the request, which redirects to
  3. Your plugin page, which shows the result of the action

The middle page doesn't have to be your plugin page. This means that you can use the "generic POST handler" that was included three years ago, the 'admin_action_' . $_REQUEST['action'] hook in admin.php.

An example user is the Akismet plugin. If you want to use it reliably, you have to submit to admin.php directly, not to another page that happens to include admin.php.

Here is a very basic example of how to use it:

add_action( 'admin_action_wpse10500', 'wpse10500_admin_action' );
function wpse10500_admin_action()
{
    // Do your stuff here

    wp_redirect( $_SERVER['HTTP_REFERER'] );
    exit();
}

add_action( 'admin_menu', 'wpse10500_admin_menu' );
function wpse10500_admin_menu()
{
    add_management_page( 'WPSE 10500 Test page', 'WPSE 10500 Test page', 'administrator', 'wpse10500', 'wpse10500_do_page' );
}

function wpse10500_do_page()
{
?>
<form method="POST" action="<?php echo admin_url( 'admin.php' ); ?>">
    <input type="hidden" name="action" value="wpse10500" />
    <input type="submit" value="Do it!" />
</form>
<?php
}
7
  • Hei, i'll look at the code again, i obviously didn't see that, but just to confirm...so what you're saying is that if i call admin.php directly without a page parameter, it skips all page loading and just does some initialization and runs the hook? That would be awesome...ish (i still don't get why they didn't put the hook before the page loading).
    – wyrfel
    Commented Feb 28, 2011 at 22:27
  • @wyrfel: Yes, calling admin.php directly is the "trick" that the Akismet source taught me. You are right when you are displaying a form and want to display it again in case of errors: then it would be easy if the destination is your plugin page but the hook somewhere at the start (so you could redirect if successful, or display the form again with error messages if not). Maybe suggest it in an Trac ticket?
    – Jan Fabry
    Commented Mar 1, 2011 at 8:14
  • I will file a ticket. As a workaround, i found the 'load-<pagehook>' hook to work...it's called before the page is loaded...but the admin_action_...concept seems a whole lot nicer and more specific. Also, on a note, the error messages are still problematic if you do POST's and don't want to repost on reload, but that's a different topic.
    – wyrfel
    Commented Mar 1, 2011 at 9:50
  • @wyrfel: Why would error messages still be problematic? If there is an error message, stay on the page and display the form again with the messages (of course a refresh would not make much sense here - but it also would do no harm, because the errors would still be there and no action will be executed). If there are no errors, execute the action and redirect to a "safe" overview page. This would work - if the admin_action_ hook would be moved before the plugin page loader.
    – Jan Fabry
    Commented Mar 1, 2011 at 10:23
  • Ok...i was thinking too complicated.
    – wyrfel
    Commented Mar 1, 2011 at 10:57
3

I approached this slightly differently by simply adding noheader=true to the action url on the page where the user submits takes the action

My handler then performs the action (ie. typically an add, update or delete) then finisheswith a wp_redirect() to the next page action (e.g add page -> edit page, delete page -> list page, edit page -> edit page). I also pass a message on the URL so I can displayed a status such as update successfully or failed.

This approach keeps all the actions: list, add, edit, delete, bulk-delete, etc in the same class and with the same admin slug so it is pretty easy to maintain and to understand.

1
  • Man, you are genius! I've been struggling for two days straight and it appears all I needed was the "noheader=true" part. Thanks!
    – roman
    Commented Apr 9, 2014 at 10:37
0

Another different approach is just adding a hidden input field to the form:

<input type="hidden" name="page" value="your-page-slug" />

This way, WordPress seems to handle the redirect automatically.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.