' . esc_html( $this->get_c2c_string( 'See the "Help" link to the top-right of the page for more help.' ) ) . "
\n";
}
}
/**
* Gets the label for a given option.
*
* @param string $opt The option.
*
* @return string The label for the option.
*/
public function get_option_label( $opt ) {
$label = isset( $this->config[ $opt ]['label'] ) ? $this->config[ $opt ]['label'] : '';
if ( true === $this->config[ $opt ]['numbered'] ) {
$label = ++$this->setting_index . ". $label";
}
return $label;
}
/**
* Resets plugin options.
*
* @return array
*/
public function reset_options() {
$this->reset_caches();
// Delete the setting from the database.
delete_option( $this->admin_options_name );
$this->options = $this->get_options( false );
return $this->options;
}
/**
* Resets caches and data memoization.
*
* @since 044
*/
public function reset_caches() {
$this->options = array();
$this->option_names = array();
$this->options_from_db = '';
}
/**
* Sanitizes user inputs prior to saving.
*
* @param string[] $inputs
* @return string[] Sanitized inputs.
*/
public function sanitize_inputs( $inputs ) {
do_action( $this->get_hook( 'before_save_options' ), $this );
// PHPCS: The Settings API already verifies nonce, so no need to do so here.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['Reset'] ) ) {
$options = $this->reset_options();
add_settings_error( 'general', 'settings_reset', $this->get_c2c_string( 'Settings reset.' ), 'updated' );
unset( $_POST['Reset'] );
} else {
// Start with the existing options, then start overwriting their potential override value. (This prevents
// unscrupulous addition of fields by the user)
$options = $this->get_options();
$option_names = $this->get_option_names();
$option_names = (array) apply_filters( $this->get_hook( 'sanitized_option_names' ), $option_names, $inputs );
foreach ( $option_names as $opt ) {
if ( !isset( $inputs[ $opt ] ) ) {
if ( $this->config[ $opt ]['input'] == 'checkbox' ) {
$options[ $opt ] = '';
} elseif ( true === $this->config[ $opt ]['required'] ) {
$msg = sprintf( $this->get_c2c_string( 'A value is required for: "%s"' ), $this->config[ $opt ]['label'] );
add_settings_error( 'general', 'setting_required', $msg, 'error' );
}
}
else {
$val = $inputs[ $opt ];
$error = false;
if ( empty( $val ) && ( true === $this->config[ $opt ]['required'] ) ) {
$msg = sprintf( $this->get_c2c_string( 'A value is required for: "%s"' ), $this->config[ $opt ]['label'] );
$error = true;
} else {
$input = $this->config[ $opt ]['input'];
switch ( $this->config[ $opt ]['datatype'] ) {
case 'checkbox':
break;
case 'int':
if ( $val && is_string( $val ) ) {
$val = str_replace( ',', '', $val );
}
if ( ! empty( $val ) && ( ! is_numeric( $val ) || ( intval( $val ) != round( $val ) ) ) ) {
$msg = sprintf( $this->get_c2c_string( 'Expected integer value for: %s' ), $this->config[ $opt ]['label'] );
$error = true;
$val = '';
}
break;
case 'array':
if ( empty( $val ) )
$val = array();
elseif ( is_array( $val ) )
$val = array_map( 'trim', $val );
elseif ( $input == 'text' )
$val = explode( ',', str_replace( array( ', ', ' ', ',' ), ',', $val ) );
else
$val = array_map( 'trim', explode( "\n", trim( $val ) ) );
break;
case 'hash':
if ( 'select' !== $input && ! is_array( $val ) && '' !== $val ) {
$new_values = array();
foreach ( explode( "\n", $val ) AS $line ) {
// TODO: It's possible to allow multi-line replacement text, in which case
// instead of skipping invalid looking lines, simply append them to the
// previous line, joined with "\n".
if ( false === strpos( $line, '=>' ) ) {
continue;
}
list( $shortcut, $text ) = array_map( 'trim', explode( '=>', $line, 2 ) );
if ( $shortcut && '' !== $text ) {
$new_values[ str_replace( '\\', '', $shortcut ) ] = str_replace( '\\', '', $text );
}
}
$val = $new_values;
}
break;
}
}
if ( $error ) {
add_settings_error( 'general', 'setting_not_int', $msg, 'error' );
}
$options[ $opt ] = $val;
}
}
$options = apply_filters( $this->get_hook( 'before_update_option' ), $options, $this );
}
$options['_version'] = $this->version;
return $options;
}
/**
* Initializes the plugin's configuration and localizable text variables.
*/
abstract protected function load_config();
/**
* Returns translated strings used by c2c_Plugin parent class.
*
* @since 060
*
* @return string[]
*/
abstract public function get_c2c_string( $string );
/**
* Registers filters.
*
* @since 065 Changed to be abstract.
*
* NOTE: This occurs during the 'init' filter, so you can't use this to hook
* anything that happens earlier.
*/
abstract public function register_filters();
/**
* Adds a new option to the plugin's configuration.
*
* Intended to be used for dynamically adding a new option after the config
* is initially created via load_config(), but it can be called earlier.
*
* @since 044
*
* @param string $option_name The option name.
* @param array $args The configuration for the setting.
* @return array The fully initialized option.
*/
public function add_option( $option_name, $args ) {
$this->config[ $option_name ] = $args;
// This function may be running after the config array has already been
// processed by the plugin, thus this new option won't be automatically
// verified, which includes setting defaults for setting attributes that
// weren't explicitly specified.
$this->verify_options( array( $option_name ) );
return $this->config[ $option_name ];
}
/**
* Verifies that the necessary configuration options were set in the inheriting class.
*/
public function verify_config() {
// Ensure required configuration options have been configured via the sub-class. Die if any aren't.
foreach ( $this->required_config as $config ) {
if ( empty( $this->$config ) ) {
die( esc_html( sprintf( $this->get_c2c_string( "The plugin configuration option '%s' must be supplied." ), $config ) ) );
}
}
// Set/change configuration options based on sub-class changes.
if ( empty( $this->config ) ) {
$this->show_admin = false;
} else {
$this->verify_options();
}
}
/**
* Initializes any option attributes that weren't specified by the plugin.
*
* @since 044
*
* @param array $options Array of all the option names to verify. Leave empty
* to verify them all. Default empty array.
*/
public function verify_options( $options = array() ) {
// If no options specified, assume them all.
if ( ! $options ) {
$options = $this->get_option_names( true );
}
foreach ( $options as $opt ) {
foreach ( $this->config_attributes as $attrib => $default) {
// Use the default if not configured.
if ( ! isset( $this->config[ $opt ][ $attrib ] ) ) {
$this->config[ $opt ][ $attrib ] = $default;
}
// Ensure configured value is of same datatype as default.
else {
// Config attributes that can be any datatype (or at least can differ from the default).
$can_be_any_datatype = [ 'default' ];
$configured_value = $this->config[ $opt ][ $attrib ];
$datatype = '';
if ( in_array( $attrib, $can_be_any_datatype ) ) {
// Do nothing.
} elseif ( is_array( $default ) && ! is_array( $configured_value ) ) {
$datatype = 'an array';
} elseif ( is_bool( $default ) && ! is_bool( $configured_value ) ) {
$datatype = 'a boolean';
} elseif ( is_int( $default ) && ! is_int( $configured_value ) ) {
$datatype = 'an integer';
} elseif ( is_float( $default ) && ! is_float( $configured_value ) ) {
$datatype = 'a float';
} elseif ( is_string( $default ) && ! is_string( $configured_value ) ) {
$datatype = 'a string';
}
if ( $datatype ) {
_doing_it_wrong( 'c2c_Plugin::verify_options', esc_html( "The c2c_Plugin configuration {$opt} attribute {$attrib} should be {$datatype} and thus the default is being used instead." ), '067' );
$this->config[ $opt ][ $attrib ] = $default;
}
}
}
if ( 'array' === $this->config[ $opt ]['datatype'] && ! is_array( $this->config[ $opt ]['default'] ) ) {
$this->config[ $opt ]['default'] = $this->config[ $opt ]['default'] ?
array( $this->config[ $opt ]['default'] ) :
array();
}
}
$this->reset_caches();
}
/**
* Returns markup for simple contextual help text, comprising solely of a
* thickboxed link to the plugin's hosted readme.txt file.
*
* NOTE: If overriding this in a sub-class, be sure to include the check at
* the beginning of the function to ensure it shows up on its own settings
* admin page.
*
* @param string $contextual_help The default contextual help.
* @param int $screen_id The screen ID.
* @param object $screen The screen object (only supplied in WP 3.0).
* @return string
*/
public function contextual_help( $contextual_help, $screen_id, $screen = null ) {
if ( $screen_id != $this->options_page ) {
return $contextual_help;
}
$help = '| ";
}
printf(
'' . "\n",
// PHPCS: The keys and values of all attributes are being escaped by esc_attributes(), so this is safe.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->esc_attributes( $attributes ),
wp_kses_post( $value )
);
} elseif ( $input == 'select' ) {
// PHPCS: The keys and values of all attributes are being escaped by esc_attributes(), so this is safe.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '";
} elseif ( $input == 'multiselect' ) {
echo '';
} elseif ( $input == 'checkbox' ) {
printf(
'' . "\n",
esc_attr( $input ),
// PHPCS: The keys and values of all attributes are being escaped by esc_attributes(), so this is safe.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->esc_attributes( $attributes )
);
if ( ! empty( $this->config[ $opt ]['help'] ) ) {
printf(
"\n",
esc_attr( $opt ),
wp_kses_post( $this->config[ $opt ]['help'] )
);
$this->config[ $opt ]['help'] = '';
}
} else { // Only 'text' and 'password' should fall through to here.
printf(
'' . "\n",
esc_attr( $input ),
// PHPCS: The keys and values of all attributes are being escaped by esc_attributes(), so this is safe.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->esc_attributes( $attributes ),
esc_attr( $value )
);
}
// Help intended to be inline (usually with a text field; checkboxes naturally have their help inline)
if ( $help = apply_filters( $this->get_hook( 'option_help'), $this->config[ $opt ]['inline_help'], $opt, 'inline_help' ) ) {
echo ' ' . wp_kses_post( $help ) . " \n";
}
// Help intended to be shown below an input field.
if ( $help = apply_filters( $this->get_hook( 'option_help'), $this->config[ $opt ]['help'], $opt, 'help' ) ) {
echo '' . wp_kses_post( $help ) . " \n";
}
// Additional paragraph of help intended to follow the main 'help'.
if ( $help = apply_filters( $this->get_hook( 'option_help'), $this->config[ $opt ]['more_help'], $opt, 'more_help' ) ) {
echo '' . wp_kses_post( $help ) . " \n";
}
// Additional help of custom markup (block elements that wouldn't fit into the default help paragraph markup).
if ( $help = apply_filters( $this->get_hook( 'option_help'), $this->config[ $opt ]['raw_help'], $opt, 'raw_help' ) ) {
echo wp_kses_post( $help ) . "\n";
}
do_action( $this->get_hook( 'post_display_option' ), $opt );
}
/**
* Outputs the options page for the plugin, and saves user updates to the
* options.
*/
public function options_page() {
$options = $this->get_options();
if ( $this->saved_settings ) {
echo "" . esc_html( $this->saved_settings_msg ) . ' ';
}
echo "\n";
do_action( $this->get_hook( 'before_settings_form' ), $this );
printf(
' ' . "\n";
do_action( $this->get_hook( 'after_settings_form' ), $this );
echo ' ' . "\n";
printf(
wp_kses_data( $this->get_c2c_string( 'This plugin brought to you by %s.' ) ),
' Scott Reilly (coffee2code)'
);
printf(
' %3$s',
esc_url( $this->donation_url ),
esc_attr( $this->get_c2c_string( "Thanks for the consideration; it's much appreciated." ) ),
esc_html( $this->get_c2c_string( 'If this plugin has been useful to you, please consider a donation.' ) )
);
echo " \n";
echo " \n";
}
/**
* Returns the full plugin-specific name for a hook.
*
* @param string $hook The name of a hook, to be made plugin-specific.
* @return string The plugin-specific version of the hook name.
*/
public function get_hook( $hook ) {
return $this->hook_prefix . '_' . $hook;
}
/**
* Returns the URL for the plugin's readme.txt file on wordpress.org/plugins
*
* @since 005
*
* @return string The URL.
*/
public function readme_url() {
return 'https://plugins.svn.wordpress.org/' . $this->id_base . '/tags/' . $this->version . '/readme.txt';
}
} // end class
endif; // end if !class_exists()
|