WordPress - Creating our plugin settings page with inputs

Adrian Voicu
5 min readMay 17, 2021

In the previous tutorial, we’ve created our two admin pages and linked them from our menu items for the admins. Now, let’s add inputs to our settings page, namely the ($this->plugin_name.’-admin-settings-display.php’).

<div class="wrap"> 
<h2>MarketoForms Settings</h2>
<?php settings_errors(); ?>
<form method="POST" action="options.php">
<?php
settings_fields( 'marketoforms_settings' );
do_settings_sections( 'marketoforms_settings' );
?>
<?php submit_button(); ?>
</form>
</div>

Now, let’s see what we are doing here. First of all we want to see if there are any errors. That is why we start by calling the settings_errors() function (https://developer.wordpress.org/reference/functions/settings_errors/). After this, we call the settings_fields() function, which should output the security fields for our settings form; and we call do_settings_sections() which outputs all the settings sections (and form fields) — identified here as “marketo_settings” — that we create for this particular page.

Once we did this, we have to register the section and the fields necessary for setting the plugin. So, let’s go to our class-marketoforms-admin.php file and update the __construct() method to register these by using the registerAndBuildFields(). We use the admin_init hook in order to call the necessary functions that will register the sections and fields:

public function __construct( $plugin_name, $version ) {
$this->plugin_name = $plugin_name;
$this->version = $version;
add_action('admin_menu', array($this, 'addAdminMenuItems'), 9);
add_action('admin_init', array( $this, 'registerAndBuildFields' ));
}

Now, after we create the registerAndBuildFields() method, we start calling the necessary functions for creating our first input.

We start by creating a section named “marketoforms_connection_settings”, that will be part of the “marketoforms_settings” sections group that we called in the admin-settings-display.php file. In the WordPress documentation you will see that the last parameter of the function is “$page”. Actually this is the id that we’ve called in our template file when we used the do_settings_sections(‘marketoforms_settings’):

// Register a new section in the "marketoforms_settings" page.
// add_settings_section( string $id, string $title, callable $callback, string $page )
add_settings_section(
'marketoforms_connection_settings',
__( 'In here we set the connection settings.', $this->plugin_name ), array($this, 'settingsCallback'),
'marketoforms_settings'
);

Once we created the “marketoforms_connection_settings”, we are registering a new field that will be part of this section. To register the field, I’ve used the method used in the following tutorial (https://blog.wplauncher.com/create-wordpress-plugin-settings-page/):

// Register a new field in the “marketoforms_connection_settings” section, inside the “marketoforms_settings” page.

// Register a new field in the "marketoforms_connection_settings" section, inside the "marketoforms_settings" page.
unset($args);
$args = array (
'type' => 'input',
'subtype' => 'text',
'id' => 'marketoforms_rest_api_base',
'name' => 'marketoforms_rest_api_base',
'required' => 'true',
'get_options_list' => '',
'value_type'=>'normal',
'wp_data' => 'option',
);
add_settings_field(
'marketoforms_rest_api_base',
__('REST API base URL', $this->plugin_name),
array( $this, 'renderField' ),
'marketoforms_settings',
'marketoforms_connection_settings',
$args
);
register_setting(
'marketoforms_settings',
'marketoforms_rest_api_base',
array(
'type' => 'string',
'description' => 'URL needed for connecting to Marketo',
//'sanitize_callback' => array($this, 'calidateRestApiBase'),
//'show_in_rest' => false,
'default' => '',
)
);

Let’s see what we did here. We first started by creating an $args array that will be passed to the renderField() method that we will copy/paste from https://blog.wplauncher.com/create-wordpress-plugin-settings-page/ . This $args variable is passed to the method by using the WordPress’ add_settings_field() function. After we create the field — named here “marketoforms_rest_api_base” — , we also need to register the option that the field is representing. And we do that by using the register_setting() function. As you can see, we can also pass a “sanitize_callback” parameter to the register_setting(). This can be used in order to validate/sanitize the value of the field before sending it to the database.

OK. Let’s see again the registerAndBuildFields() method:

public function registerAndBuildFields() {// Register a new section in the "marketoforms_settings" page.
add_settings_section(
'marketoforms_connection_settings',
__( 'In here we set the connection settings.', $this->plugin_name ), array($this, 'settingsCallback'),
'marketoforms_settings'
);

// Register a new field in the "marketoforms_connection_settings" section, inside the "marketoforms_settings" page.
unset($args);
$args = array (
'type' => 'input',
'subtype' => 'text',
'id' => 'marketoforms_rest_api_base',
'name' => 'marketoforms_rest_api_base',
'required' => 'true',
'get_options_list' => '',
'value_type'=>'normal',
'wp_data' => 'option',
);

add_settings_field(
'marketoforms_rest_api_base',
__('REST API base URL', $this->plugin_name),
array( $this, 'renderField' ),
'marketoforms_settings',
'marketoforms_connection_settings',
$args
);
register_setting(
'marketoforms_settings',
'marketoforms_rest_api_base',
array(
'type' => 'string',
'description' => 'URL needed for connecting to Marketo',
'sanitize_callback' => array($this, 'validateUrl'),
//'show_in_rest' => false,
'default' => '',
)
);
}

Now, I’ve talked about the renderField() method, so let’s just put it in here:

public function renderField( $args ) {
if( $args['wp_data'] == 'option' ) {
$wp_data_value = get_option( $args['name'] );
} elseif( $args['wp_data'] == 'post_meta' ) {
$wp_data_value = get_post_meta($args['post_id'], $args['name'], true );
}
switch ( $args['type'] ) {
case 'input':
$value = ( $args['value_type'] == 'serialized' ) ? serialize($wp_data_value) : $wp_data_value;
if( $args['subtype'] != 'checkbox' ) {
$prependStart = (isset($args['prepend_value'])) ? '<div class="input-prepend"> <span class="add-on">'.$args['prepend_value'].'</span>' : '';
$prependEnd = (isset($args['prepend_value'])) ? '</div>' : '';
$step = (isset($args['step'])) ? 'step="'.$args['step'].'"' : '';
$min = (isset($args['min'])) ? 'min="'.$args['min'].'"' : '';
$max = (isset($args['max'])) ? 'max="'.$args['max'].'"' : '';
if( isset( $args['disabled'] ) ) {
// hide the actual input bc if it was just a disabled input the informaiton saved in the database would be wrong - bc it would pass empty values and wipe the actual information
echo $prependStart.'<input type="'.$args['subtype'].'" id="'.$args['id'].'_disabled" '.$step.' '.$max.' '.$min.' name="'.$args['name'].'_disabled" size="40" disabled value="' . esc_attr($value) . '" /><input type="hidden" id="'.$args['id'].'" '.$step.' '.$max.' '.$min.' name="'.$args['name'].'" size="40" value="' . esc_attr($value) . '" />'.$prependEnd;
} else {
echo $prependStart.'<input type="'.$args['subtype'].'" id="'.$args['id'].'" required="'.$args['required'].'" '.$step.' '.$max.' '.$min.' name="'.$args['name'].'" size="40" value="' . esc_attr($value) . '" />'.$prependEnd;
}
/*<input required="required" '.$disabled.' type="number" step="any" id="'.$this->plugin_name.'_cost2" name="'.$this->plugin_name.'_cost2" value="' . esc_attr( $cost ) . '" size="25" /><input type="hidden" id="'.$this->plugin_name.'_cost" step="any" name="'.$this->plugin_name.'_cost" value="' . esc_attr( $cost ) . '" />*/
} else {
$checked = ($value) ? 'checked' : '';
echo '<input type="'.$args['subtype'].'" id="'.$args['id'].'" required="'.$args['required'].'" name="'.$args['name'].'" size="40" value="1" '.$checked.' />';
}
break;
default:
# code...
break;
}
}

There is no use of talking about it here, as it simply takes a list of parameters and creates an input field.

I was mentioning the sanitize_callback, which calls the validateUrl() method in our class, so let’s create it just as an example:

public function validateUrl($value) {
$url = filter_var($value, FILTER_SANITIZE_URL);
if( ! filter_var($url, FILTER_VALIDATE_URL)) {
//add_settings_error( string $setting, string $code, string $message, string $type = 'error' )
add_settings_error( 'marketoforms_rest_api_base', 'marketoforms_rest_api_base', 'URL not valid' );
return false;
}
return $url;
}

As you can see here, we start by sanitizing the value we receive, and if the value is not a valid URL we are adding a new settings error by calling the add_settings_error() function. To make sure the user understands, we are not returning the value. Otherwise, if everything is ok, we are returning the sanitized value.

This was only as an example. You can also use WordPress functions to validate/sanitize inputs: sanitize_email(), sanitize_file_name(), sanitize_html_class(), sanitize_key(), sanitize_meta(), sanitize_mime_type(), sanitize_option(), sanitize_sql_orderby(), sanitize_text_field(), sanitize_textarea_field(), sanitize_title(), sanitize_title_for_query(), sanitize_title_with_dashes(), sanitize_user(). Take a look here: https://codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Data.

--

--