What is the recommended way to create plugin administration forms?

I have seen and been using the following technique for adding php scripts to my plugin for handling custom forms in a wordpress plugin.

from the quizzin plugin:

Read More
$code_pages = array('quiz_form.php','quiz_action.php', 'question_form.php', 'question.php');
        foreach($code_pages as $code_page) {
            $hookname = get_plugin_page_hookname("quizzin/$code_page", '' );
            $_registered_pages[$hookname] = true;

For example, the ‘quiz_action.php’ is later used as the target for an administration form (these forms are used only in wp-admin)

  <form name="post" action="<?php echo $GLOBALS['my_plugin_folder'] ?>/quiz_action.php" method="post" id="post">

UPDATE: This method is discussed here – Admin config screen without menu

The final comment below by a WordPress core dev seems to discourage this:

http://wordpress.org/support/topic/how-can-i-execute-php-scripts-in-my-plugin-folder

So what is best practice here? Should administration forms be posting to wp-admin/admin.php?action=foo or wp-admin/edit.php?action=bar. How does one register these actions? Is this documented anywhere? To be clear, these files should not be linked from an admin menu.

I’m using WordPress 3.0.4

Thanks!

Related posts

Leave a Reply

3 comments

  1. I personally just add a menu link and in the function for it handle the form. With $_SERVER[‘REQUEST_URI’] as the action. Example below.

    add_action("admin_menu", "menu" );
    function menu(){
        add_menu_page('Test form', 'Test form', 'manage_options', 'show_form' );
    }
    function show_form(){
        if ( $_SERVER["REQUEST_METHOD"] == "POST" ){
                print "do stuff";
        } else {
             ?><form method="post" action="<?php echo $_SERVER['REQUEST_URI']; ?>"><input type="submit" /></form><?php
            }
    }
    
  2. Basically, it seems there are two ways to do this:

    1) The $_registered_pages way detailed in the original question. This seems a bit non-standard and might confuse someone looking at your code.

    2) Post your form to an admin url like admin_url('admin.php?page=show_form')
    where show_form() is a registered menu item/function. Inside show_form(), you can switch on whether or not a form has been submitted. If it has you can include another php file conditionally i.e:

    function show_form()
    {
        if ( $_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['submit'])) {
    
            require_once('name_of_your_processing_script.php');
    
        } else {
            //do something else. 
        }
    }
    

    Or just include the file and do any processing / switching in there (probably cleaner).

    If you try and just use a file you have created as the form’s post action you will get an error. As mentioned in the comments above above you should prevent your files from being called directly and protect and guard against CSRF attacks using nonces.

    Apologies for the long comment thread. Would be grateful if people could confirm this is what they meant.

  3. My approach for the last 12 months has been to call every function that processes $_POST or $_GET request. Within every function a couple of values, sometimes more, are checked to determine if that function is the applicable one i.e. a form name and a specific value from the form or URL that data validation can also be applied to.

    I put all $_POST and $_GET through a single function which checks nonce, so that the nonce function is not called within every function. That process_POST_GET() function then includes() form processing functions.

    add_action('admin_init,process_POST_GET');
    

    It works fine and has no performance drawback. We’re simply talking about 100 x if(isset()) and a very simple way to keeping it all tidy. I considered an add_action() for every function and realized that this adds more work for WordPress than my approach because it still needs to call every function.

    The example below has a lot of “wpecus_” because I was not using classes originally. We wouldn’t need to prefix everything otherwise.

    eval()

    Today I’m considering using eval() to call the function using the form_name.

    /**
    * $_POST and $_GET request processing procedure.
    * 
    * Checks nonce from any form or URL, then includes functions that processing submission, then includes the
    * file that determines which function to use to process the submission.
    *
    * @package WP e-Customers
    * @since 0.0.1  
    */
    public function process_POST_GET(){       
    
        // form submission
        if(isset($_POST['wpecus_post_processing_required']) && $_POST['wpecus_post_processing_required'] == true){        
            if(isset($_POST['wpecus_admin_referer'])){        
                // a few forms have the wpecus_admin_referer where the default hidden values are not in use
                check_admin_referer( $_POST['wpecus_admin_referer'] );         
            }else{                                       
                // 99% of forms will use this method
                check_admin_referer( $_POST['wpecus_hidden_panel_name'] );
            }        
        }
    
        // url submission
        if(isset($_GET['wpecusprocess']) && isset($_GET['nonceaction'])){           
            check_admin_referer( $_GET['nonceaction'] );        
        }     
    
        // arriving here means check_admin_referer() security is positive       
        global $wpecus_debug_mode,$cont,$wpecus_is_free;
    
        // if $wpecus_debug_mode set to true or 1 on wpecustomers.php we dump $_POST
        if($wpecus_debug_mode){
            echo '<h1>$_POST</h1>';
            wpecus_var_dump($_POST);           
            echo '<h1>$_GET</h1>';
            wpecus_var_dump($_GET);    
        }
    
        // include form processing functions
        require_once(WTG_WPECUS_PATH . 'processing/wpecus_form_two.php');   
        require_once(WTG_WPECUS_PATH . 'processing/wpecus_form_one.php');
    
        eval('wpecus_postget_' . $_POST['wpecus_form_name']);                        
    
    }
    

    WP e-Customers
    I expect WP e-Customers to have around 200 admin forms by 2015. There will be a large collection of “Tools” for administrators to perform all sorts of tasks on WordPress and phpBB through the WordPress admin.

    Debug var_dump()
    My approach to putting all requests through a single function creates a good point to do a dump. You may want to do this without the headers. My wpecus_var_dump() function also ensures an administrator is logged in, important security. Another point to consider is $wpecus_debug_mode is only set to true when the plugin is on the development blog. It checks the domain and installation path in the plugins main file. This means I can quickly distribute a copy of my plugins and debugging is automatically off. Accidentally leaving it on is fatal so if you want to setup a procedure for debugging WordPress $_POST and $_GET requests do not just use var_dump(). You want the dumps to vanish when your no the one viewing the screen.