[wp-hackers] Settings API: Showing errors if validation fails

Jeremy Clarke jer at simianuprising.com
Thu Dec 17 21:00:26 UTC 2009


On Thu, Dec 17, 2009 at 2:18 AM, Ade Walker <photofantaisie at gmail.com> wrote:
>
> I ended up using two functions: the register_settings callback function just
> to sanitise the user input and a separate validation function to recheck
> what's been entered and generate error messages if necessary. The validation
> function is called each time the Settings page is loaded. As for the Reset
> message, I have ended up with a rather clunky (or so it seems to me) method,
> which involves a hidden option called "just-reset". This is captured in the
> $input array and, if "true", $input is replaced with the default settings,
> and $input['just-reset'] is set to true (all options are kept in one array,
> which is the reason for this), and then the $input array is returned. I then
> have another function hooked to admin_notices which checks if the
> $options['just-reset'] is true and, if so, prints out a "You've reset your
> settings to default" message, then changes $options['just-reset'] to false,
> and then runs update_option. It's probably easier to follow me if you look
> at the source code of the plugin in question, :-/

Yeah I see what you mean and that is a good idea. During validation I
can set up a $option['messages'] sub-array before passing back the
full settings array, then in admin_notices I check for it and erase
its values after displaying the messages.

It's annoying (mostly conceptually) to have to re-save the option
after returning to your settings page but at least it doesn't involve
re-validating all the data or much code duplication. This is probably
a good interim solution for people who need it. Thanks.

I worked out this system in my own code last night so I'll post it
here for anyone interested. I ended up preferring to avoid
admin_notices and leave it alone. The settings updated message still
makes sense in case there were other settings that were saved, and I
just plain like the look of the errors under my settings page heading
and intro text:

PASTE w/ SYNTAX COLORING: http://pastie.org/747439

I'll paste the actual php for the interim solution at the bottom of this email.

I also worked out exactly what would be necessary to fix this in core
and what I think is a workable solution for adding error messaging to
the Settings API. You can read a novel worth of it on the trac ticket:
http://core.trac.wordpress.org/ticket/11474

One part I think is particularly relevant to wp-hackers:
--------------
This missing feature is a huge defect of the Settings API because it
gives no sane way for eager developers to add errors to their own
settings sections/pages, but also because it fails to inspire them to
do so with an elegant framework. IMHO error reporting to users should
be a first-class citizen of the Settings API, and pushing error
reporting in the faces of developers will cause an overall usability
improvement for ALL WordPress plugins, not just those who's authors
were already concerned about reporting validation errors.

Put another way: Usability improvements to the Settings API through
easy error reporting should be treated like security improvements
through mandatory validation functions: Both are functionalities that
remind/inspire developers to do what they should be doing anyway, but
often forget about/don't think to do on their own.
--------------

I'm interested in feedback on the API addition on any level, but one
particularly interesting question is how to refer to them. I'm not
sure if there is a standard elsewhere in WP-Land to follow.

For functions and global variables I think it should be referred to as
either 'settings_errors' or 'settings_messages'. I.e.

add_settings_error();
settings_errors();
global $wp_settings_errors;

OR

add_settings_mesages();
settings_messages();
global $wp_settings_messages;

In either case I think the API should allow for different $types like
'error', 'updated' and maybe others. To me this implies that
*_messages should be used as the suffix so it will be completely
accurate.

That said, I personally find *_errors to be a lot more obvious in
terms of what it does. Any developer who saw add_settings_error()
would probably be able to figure out what would happen if you used it,
while add_settings_message() might be more confusing. People don't
think of the word "messaging" when they think of input validation (or
maybe I just don't).

Interested to hear thoughts :)

--
Jeremy Clarke | http://jeremyclarke.org
Code and Design | globalvoicesonline.org


---------------------------------------------------------------------------------------
INTERIM SETTINGS VALIDATION ERRORS CODE FOR FUTURE EXPLORERS:
---------------------------------------------------------------------------------------

/**
 * Validation callback for all of 'gv_settings' option.
 *
 * Referenced in register_setting() for gv_settings, runs whenever
update_option('gv_settings') is used.
 * Must account for every field added to gv_settings via add_settings_field
 *
 * @param array $input Full array of data passed into update_option()
 * @return array Sanitized array of data
 */
function gv_settings_validate($input){
    global $gv;

    /**
     * Validate an email address
     */
    $email = sanitize_email( $input['email'] );
    // If its valid add the email to the output array
    if ( $email AND is_email( $email ) ) :
        $output['email'] = $email;
    // Otherwise add a message with a slug and text value to be output
on the settings page
    elseif ( $input['email'] ) :
        $messages['email_error'] = "Email Error: The 'email address'
given, <strong>" . $input['email'] . " </strong> is not a valid
email";
    endif;

    /**
     * Validate a page id
     */
    if ($input['special_page_id']) :
        $page_object = get_page( $input['special_page_id'] );
        // Make sure a valid page object was returned.
        if ( is_object( $page_object ) AND !is_wp_error($page_object) )
            $output['special_page_id'] = $input['special_page_id'];
        else
            $messages['page_id_error'] = "Page ID Error: The page id
given, <strong>" . $input['special_page_id'] . " </strong> is not a
page on this site";
    endif;

    /**
     * Add any messages to the array
     */
    if (is_array($messages))
         $output['messages'] = $messages;

    /**
     * Return the array of new values
     */
    return $output;
}

/**
 * Show any messages/errors saved to a setting during automated validation
 *
 * Needed because validation system has no error reporting.
 * Uses a ['messages'] array inside the settings array
 * Format should be ['messages'][$slug][$message_text], $slug is
unique id, $message is just text.
 *
 * @param string $setting The setting name as used in get_option()
 */
function gv_settings_display_errors($setting) {
    $option = get_option($setting);
    if (is_array($option['messages'])) :
        foreach ( (array) $option['messages'] as $slug => $message) :
            echo "<div id='gv_messages' class='error fade
$slug'><p>$message</p></div>";
            unset($option['messages'][$slug]);
        endforeach;
        update_option('gv_settings', $option);
    endif;
}

/**
 * Page display function for GV Settings
 *
 * Defined during gv_add_settings_pages()
 */
function gv_settings_page() {
    ?>
    <div>
        <h2><?php _e('Global Voices Settings'); ?></h2>
        <p>
            These settings are related specifically to Global Voices
sites and the gv-plugin plugin.
            They cover various specific and general functionality
added by gv-plugin.
        </p>

        <?php
                // HERE IT IS: DISPLAY ANY SETTINGS WE HAVE IN OUR OPTION ARRAY
                gv_settings_display_errors('gv_settings');
        ?>

        <form action="options.php" method="post">
            <p class="submit"><input type="submit" name="submit"
value="<?php _e('Update options &raquo;'); ?>" /></p>
            <?php
            // Output fundamental security etc. form fields
            settings_fields('gv_settings');
            // Output any sections defined for page gv_settings
            do_settings_sections('gv_settings');
            ?>
            <p class="submit"><input type="submit" name="submit"
value="<?php _e('Update options &raquo;'); ?>" /></p>
        </form>
    </div>
    <?php
}


More information about the wp-hackers mailing list