Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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.

share|improve this question
    
Why is this tagged with [plugin-wp-pagenavi]? [plugin-development] is certainly welcome here. –  Jan Fabry Feb 28 '11 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 Feb 28 '11 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 Mar 1 '11 at 8:12
    
@Jan Fabry: Thanks for the re-tagging...not that familiar with the whole tag pool, yet (quite obviously). –  wyrfel Mar 1 '11 at 9:52

2 Answers 2

up vote 15 down vote accepted

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
}
share|improve this answer
    
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 Feb 28 '11 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 Mar 1 '11 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 Mar 1 '11 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 Mar 1 '11 at 10:23
    
Ok...i was thinking too complicated. –  wyrfel Mar 1 '11 at 10:57

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.

share|improve this answer
    
Man, you are genius! I've been struggling for two days straight and it appears all I needed was the "noheader=true" part. Thanks! –  r00m Apr 9 at 10:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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