0

I am developing a payments gateway, and wanted to have a page with a form to save authentication credentials for reaching out to an external API used for payment. I made a settings page using WordPress's Settings API for my plugin, which has been great, except for some odd behavior I haven't seen anybody else have. Whenever I am on the page created by my code, input the settings into my fields, and click "save", the fields completely empty themselves. I have also checked to see whether or not my settings have saved to the database by echoing them to the display of that page, which you can see in the function elv_render_forms_description near the bottom of my code, and found that, at first, entering ONE field functioned properly, but after trying to save more than one, none of the fields saved. I have not been able to recreate this behavior. To my understanding, only using the "register_setting()" function ONCE, and then putting multiple settings within it via "add_settings_group()", and "add_settings_field()", makes it such that everything may be retrieved in ONE database query as an array, and since the settings shown here will all be used at once when reaching out to an external API, I figured this was best for my use case. My "$default_values" also seemed to function for the display, but did not show themselves in my display of the data from elv_render_forms_description, and do not seem to function after trying to input data into the fields and save them.

I have looked around here on the Wordpress Stack Exchange, at the WordPress documentation of Settings API, at what the functions I'm using actually mean and require on the WordPress Developer resources page, at Stack Overflow, and over many sites found via Google, but have not been able to figure out what my problem is. I am totally stuck. I would appreciate any discussion or help.


// If this file is accessed directly, abort it
defined ('ABSPATH' ) or die( 'Unauthorized Access' );

//--------------------------------------------------------------------------------------------------------------------------------------
// 1.) Hooks settings page onto wordpress dashboard under "Settings" menu
// 2.) Hooks elv_register_settings() into Settings API to maintain plugin options in the database.
//--------------------------------------------------------------------------------------------------------------------------------------

add_action( 'admin_menu', 'elv_add_settings_page' );
add_action( 'admin_init', 'elv_register_settings' );

//This function adds the submenu page which contains the settings for the test and for the plugin in general.
function elv_add_settings_page() {
    add_submenu_page( 'elavon-post-api-test', // Slug of parent, aka the function just above this one
     'Settings API Form Fields',              // Name displayed on title tags of page when menu is selected
     'Elavon Converge Settings',              // Name displayed as title of submenu page on admin menu
     'manage_options',                        // Permissions of user who may use this, in this case, administrators only
     'elv_settings_page',                     // Slug of the submenu page
     'elv_render_settings_page'               // Callback function that renders the submenu page
    );
}

// Important note; the SECOND field of register_setting(), option name, is what the other functions use, NOT the group.
function elv_register_settings() {

    // This should be the option name used in the second field of "register_setting()". 
    $option_name =  'elv_settings_fields'; 

    // Fetch existing option values
    $option_current_values = get_option( $option_name );

    $default_values = array(
        'elv_setting_merchant_id' => '0123456',
        'elv_setting_api_user_id' => 'apiusername',
        'elv_setting_api_key' => 'K38qxPvFQhZneeIuygOaqwFuP0vg3DHKN93qfwynyQGMMAtXvb03Ms0ytLeQ1hKL'
        );

    // Parse option current values into predefined keys, throw the rest away
    $data = shortcode_atts( $default_values, $option_current_values );

    register_setting( 
        'elv_settings_fields',                  // $option_group. Defines settings group name. Used for settings_fields()
        'elv_settings_fields',                  // $option_name. Name of option to sanitize and save to database. Used by get_option()
        'elv_validate_settings_fields'          // Validation callback function. 
         );

         add_settings_section( 
            'api_settings',                                   // $id, slug name to identify section. Used in 'id' attribute of tags.
            'API Settings',                                   // $title, Formatted title of the section. Shown as the heading for the section. 
            'elv_render_forms_description',                   // $callback, echoes out any content at top of section between heading and fields. 
            'elv_settings_page'                               // $page, Page slug on which you want to show the section (used by settings fields)
            );

            add_settings_field( 
                'elv_setting_merchant_id',                    // $id, slug name to identify field. Used in 'id' attribute of tags
                'Merchant ID',                                // $title, shown as label for field during output
                'elv_render_settings_field1',                 // Callback function that fills the form with the desired inputs
                'elv_settings_page',                          // Slug name of settings page on which to show the section (eg; general, reading, writing)
                'api_settings',                               // SLUG NAME of settings section in which field resides
                array(                                        // Array contains actual values used by the field.
                    'label_for'   => 'label1',                                          // HTML <label> for Attribute
                    'name'        => 'elv_setting_merchant_id',                         // HTML "name" used for submitting form, and submitting to database
                    'value'       => esc_textarea( $data['elv_setting_merchant_id'] ),  // Actual value of row, hence $data
                    'option_name' => $option_name                                       // Option name stored as variable
                )
                );
            
            add_settings_field( 
                'elv_setting_api_user_id',
                'API User ID',
                'elv_render_settings_field2',
                'elv_settings_page',
                'api_settings',
                array(
                    'label_for'   => 'label2',
                    'name'        => 'elv_setting_api_user_id', 
                    'value'       => esc_textarea( $data['elv_setting_api_user_id'] ),
                    'option_name' => $option_name
                )
                );
            
             add_settings_field( 
                'elv_setting_api_key',
                'API Key',
                'elv_render_settings_field3',
                'elv_settings_page',
                'api_settings',
                array(
                    'label_for'   => 'label3',
                    'name'        => 'elv_setting_api_key', 
                    'value'       => esc_textarea( $data['elv_setting_api_key'] ), 
                    'option_name' => $option_name
                )
             );
}

function elv_render_settings_page() {
    ?>
        <h2> Elavon Converge WooCommerce Payment Gateway Settings </h2>
        <form action="options.php" method="post">
            <?php
            settings_fields( 'elv_settings_fields' );     // Outputs nonce, action, and options page fields for a settings page. String inside is name of settings group.
            do_settings_sections( 'elv_settings_page' ); // Prints out all settings sections. String inside is the slug name of the page whose settings you want to output.
            ?>
            <input name="submit" class="button button-primary" type="submit" value="<?php esc_attr_e( 'Save' ); ?>" />
        </form>
    <?php
}

//----------------------------------------------------------------------------------------------------------------
// Validating data entered when saving the options to the database.
//----------------------------------------------------------------------------------------------------------------

function elv_validate_settings_fields( $input ) {
    // Strips any non a-z, A-Z, or 0-9 characters out of submitted data via sanitization.
    $output['elv_setting_merchant_id'] = sanitize_text_field( $input['elv_setting_merchant_id'] );
    $output['elv_setting_api_user_id'] = sanitize_text_field( $input['elv_setting_api_user_id'] );
    $output['elv_setting_api_key']     = sanitize_text_field( $input['elv_setting_api_key'] );
    return $output;
}

//------------------------------------------------------------------------------------------------------------------------------------------------
// Callback functions that display the description of what the form fields do at the top of the admin settings page. For the Noobs! :) 
//
// HERE'S SOME EXPLANATIONS AS TO VARIOUS BITS OF WORDPRESS AND PHP SPECIFIC CODE FOR LATER READABILITY AND REFERENCE:
// 1.) Function "get_option()" uses the name of the option group to retrieve the settings fields from the register settings function.
// 2.) "$options" is predefined using get_option, which retrieves the option value based on an option name
// 3.) The "name" attribute for the input field of the settings needs to match the $option_name that was set in the register_setting function (I think?)
//------------------------------------------------------------------------------------------------------------------------------------------------

function elv_render_forms_description() {

//------------------------------------------------------------------------------------
// Attempt at checking database for existing variables by echoing them to the display
    foreach( get_option('elv_settings_fields') as $value) {
        echo "$value <br>";
    }
//------------------------------------------------------------------------------------

    echo '<p>Here is where you define the settings to make the gateway function. You MUST input your Merchant ID, API Terminal User, and your API Key, so that Elavon can speak with your website.</p>';
}

function elv_render_settings_field1( $args ) {
    printf(
        '<textarea name="%1$s[$2$s]" id="%3$s" rows="1" cols="30" class="code">%4$s</textarea>',
        $args['option_name'],
        $args['name'],
        $args['label_for'],
        $args['value']
    );
}

function elv_render_settings_field2( $args ) {
    printf(
        '<textarea name="%1$s[$2$s]" id="%3$s" rows="1" cols="30" class="code">%4$s</textarea>',
        $args['option_name'],
        $args['name'],
        $args['label_for'],
        $args['value']
    );
}

function elv_render_settings_field3( $args) {
    printf(
        '<textarea name="%1$s[$2$s]" id="%3$s" rows="1" cols="64" class="code">%4$s</textarea>',
        $args['option_name'],
        $args['name'],
        $args['label_for'],
        $args['value']
    );
}
0

1 Answer 1

0

The issue with the data saving lies in this area of the code (all three similar functions).

printf(
    '<textarea name="%1$s[$2$s]" id="%3$s" rows="1" cols="30" class="code">%4$s</textarea>',
    $args['option_name'],
    $args['name'],
    $args['label_for'],
    $args['value']
);

Or more specifically it's, this part [$2$s], which should be, [%2$s]. I totally get it though, these specifiers can be fiddly and easy to make typos with, it took running the code to work out where the issue was, i didn't spot it just glancing at the code.

Moving on..

RE: sanitize_text_field, your code comments say:

// Strips any non a-z, A-Z, or 0-9 characters out of submitted data via sanitization.

The documentation however says:

Checks for invalid UTF-8,
Converts single < characters to entities
Strips all tags
Removes line breaks, tabs, and extra whitespace
Strips octets

You might wish to have a browse through the different sanitize functions and see which matches best with the character restrictions you want for your values.

Not sure about the shortcode atts use, wp_parse_args might be more fitting (although they do very similar things). Setting defaults for options can be done at the time you call get_option, fyi.

$default_values = array(
    'elv_setting_merchant_id' => '0123456',
    'elv_setting_api_user_id' => 'apiusername',
    'elv_setting_api_key' => 'K38qxPvFQhZneeIuygOaqwFuP0vg3DHKN93qfwynyQGMMAtXvb03Ms0ytLeQ1hKL'
);
$data = get_option( $option_name, $default_values );

And i don't know if you have plans to change the textarea functions later, but given the 3 textarea functions all do exactly the same and the args are passed into the same places, they could just be a single function (one function to manage instead of three).

function elv_render_settings_field( $args ) {
    printf(
        '<textarea name="%1$s[%2$s]" id="%3$s" rows="1" cols="30" class="code">%4$s</textarea>',
        $args['option_name'],
        $args['name'],
        $args['label_for'],
        $args['value']
    );
}

And then update each add_settings_field to use elv_render_settings_field instead of elv_render_settings_field1, 2, 3.. etc.

3
  • Thank you! I seem to always have some typo that takes me days to find LOL. It is fully functional now, and the check I used displays my array like I thought it would. I'd be interested in trying one callback to render the settings fields. I will look into styling the fields separately. Nov 18, 2022 at 21:10
  • I appreciate the pointers to better optimizing my code and my comments. I decided upon shortcode_atts() when I was poking around online, and it worked for me. Why would wp_parse_args() be better in this case? Nov 18, 2022 at 21:13
  • I don't think it's necessarily better, only a different function with similar uses in mind. They do much of the same, merge a set of values compared against a default set. I'd really only argue for wp_parse_args on it making more sense from a readability standpoint, whether that's you 3 years down the line or someone else looking at it wondering why there's a shortcode function being called on options data.
    – t31os
    Nov 18, 2022 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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