Beyond the basic
In a previous post, I showed you how to install a simple WordPress plugin. But the plugin used a non-standard way of changing the font, and the color and style were hard-coded (You have to edit the plugin and upgrade it every time you want to change the text and font style.) Let’s look at a better way of doing it. Here is the prompt I gave Gemini.
| Please write me a WordPress plugin called “My Customizable Shortcode Plugin” that uses a short code called [my_customisable_shortcode] that uses a settings tab in the dashboard to change the text and font style. Use a color picker and bold and italic checkboxes. The code should uses standard WordPress function calls to change the font instead of updating the header directly. |
Example output: Hello, World!
The first version worked fine. But I tinkered with it. So here are two versions. The last version allows multiple lines of text and to select from 3 fonts.
Version
<?php
/**
* Plugin Name: My Customizable Shortcode Plugin
* Description: Creates a customizable shortcode [my_customisable_shortcode] managed via a dashboard settings page.
* Version: 1.0
* Author: Les Ey
* Author URI: https://les-ey.online
* License: GPL-2.0-or-later
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: mscp
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// --- CONSTANTS ---
define( 'MSCP_SLUG', 'mscp-settings' );
define( 'MSCP_OPTION_GROUP', 'mscp_options' );
define( 'MSCP_OPTION_NAME', 'mscp_shortcode_options' );
/**
* Load plugin textdomain for translation support.
*/
function mscp_load_textdomain() {
load_plugin_textdomain( 'mscp', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'mscp_load_textdomain' );
/**
* 1. ADMIN MENU AND SETTINGS SETUP
* Registers the settings page and settings fields.
*/
function mscp_add_admin_menu() {
add_menu_page(
esc_html__( 'Shortcode Customizer', 'mscp' ),
esc_html__( 'Custom Shortcode', 'mscp' ),
'manage_options',
MSCP_SLUG,
'mscp_settings_page',
'dashicons-edit-page',
80
);
}
add_action( 'admin_menu', 'mscp_add_admin_menu' );
function mscp_settings_init() {
register_setting( MSCP_OPTION_GROUP, MSCP_OPTION_NAME, 'mscp_options_validate' );
add_settings_section(
'mscp_main_section',
esc_html__( 'Customize Shortcode Appearance', 'mscp' ),
'mscp_settings_section_callback',
MSCP_SLUG
);
// Text Field
add_settings_field(
'mscp_text_content',
esc_html__( 'Shortcode Text', 'mscp' ),
'mscp_text_content_render',
MSCP_SLUG,
'mscp_main_section'
);
// Color Picker Field
add_settings_field(
'mscp_text_color',
esc_html__( 'Text Color', 'mscp' ),
'mscp_text_color_render',
MSCP_SLUG,
'mscp_main_section'
);
// Font Size Field
add_settings_field(
'mscp_font_size',
esc_html__( 'Font Size (px)', 'mscp' ),
'mscp_font_size_render',
MSCP_SLUG,
'mscp_main_section'
);
// Bold Checkbox
add_settings_field(
'mscp_is_bold',
esc_html__( 'Bold Text', 'mscp' ),
'mscp_is_bold_render',
MSCP_SLUG,
'mscp_main_section'
);
// Italics Checkbox
add_settings_field(
'mscp_is_italic',
esc_html__( 'Italic Text', 'mscp' ),
'mscp_is_italic_render',
MSCP_SLUG,
'mscp_main_section'
);
}
add_action( 'admin_init', 'mscp_settings_init' );
/**
* Settings Field Render Functions
*/
function mscp_get_options() {
// Note: Default text content must be wrapped in __() to be translatable
$default_text = esc_html__( 'Hello, World!', 'mscp' );
return get_option( MSCP_OPTION_NAME, array(
'text_content' => $default_text,
'text_color' => '#0073AA',
'font_size' => 16,
'is_bold' => 0,
'is_italic' => 0,
) );
}
function mscp_settings_section_callback() {
echo '<p>' . esc_html__( 'Configure the text and style for the [my_customisable_shortcode].', 'mscp' ) . '</p>';
}
function mscp_text_content_render() {
$options = mscp_get_options();
?>
<input type="text" name="<?php echo MSCP_OPTION_NAME; ?>[text_content]" value="<?php echo esc_attr( $options['text_content'] ); ?>" style="width: 400px;" />
<?php
}
function mscp_text_color_render() {
$options = mscp_get_options();
?>
<input type="text" name="<?php echo MSCP_OPTION_NAME; ?>[text_color]" id="mscp_text_color" value="<?php echo esc_attr( $options['text_color'] ); ?>" class="mscp-color-field" data-default-color="#0073AA" />
<?php
}
function mscp_font_size_render() {
$options = mscp_get_options();
?>
<input type="number" name="<?php echo MSCP_OPTION_NAME; ?>[font_size]" value="<?php echo esc_attr( $options['font_size'] ); ?>" min="8" max="72" /> px
<p class="description"><?php esc_html_e( 'Enter font size in pixels.', 'mscp' ); ?></p>
<?php
}
function mscp_is_bold_render() {
$options = mscp_get_options();
$checked = ( isset( $options['is_bold'] ) && $options['is_bold'] == 1 ) ? 'checked="checked"' : '';
?>
<input type="checkbox" name="<?php echo MSCP_OPTION_NAME; ?>[is_bold]" value="1" <?php echo $checked; ?> />
<?php
}
function mscp_is_italic_render() {
$options = mscp_get_options();
$checked = ( isset( $options['is_italic'] ) && $options['is_italic'] == 1 ) ? 'checked="checked"' : '';
?>
<input type="checkbox" name="<?php echo MSCP_OPTION_NAME; ?>[is_italic]" value="1" <?php echo $checked; ?> />
<?php
}
function mscp_options_validate( $input ) {
$output = mscp_get_options(); // Start with current options
// Sanitize text content
if ( isset( $input['text_content'] ) ) {
$output['text_content'] = sanitize_text_field( $input['text_content'] );
}
// Sanitize color (must be a valid hex color)
if ( isset( $input['text_color'] ) ) {
$output['text_color'] = sanitize_hex_color( $input['text_color'] );
}
// Sanitize font size (integer check)
if ( isset( $input['font_size'] ) ) {
$output['font_size'] = absint( $input['font_size'] );
if ( $output['font_size'] < 8 || $output['font_size'] > 72 ) {
$output['font_size'] = 16; // Default safe value
}
}
// Sanitize checkboxes (0 or 1)
$output['is_bold'] = ( isset( $input['is_bold'] ) && $input['is_bold'] == 1 ) ? 1 : 0;
$output['is_italic'] = ( isset( $input['is_italic'] ) && $input['is_italic'] == 1 ) ? 1 : 0;
return $output;
}
/**
* Settings Page HTML
*/
function mscp_settings_page() {
// Check user capabilities
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form action="options.php" method="post">
<?php
// Output security fields for the registered setting group
settings_fields( MSCP_OPTION_GROUP );
// Output settings sections and fields
do_settings_sections( MSCP_SLUG );
// Output save settings button, translated
submit_button( esc_attr__( 'Save Customizations', 'mscp' ) );
?>
</form>
</div>
<?php
}
/**
* 2. ADMIN SCRIPTS AND STYLES (for Color Picker)
* Enqueues the color picker and initializes it with inline JavaScript.
*/
function mscp_admin_scripts( $hook ) {
// Check if we are on the correct admin page before loading assets
if ( strpos( $hook, MSCP_SLUG ) === false ) {
return;
}
// Enqueue the native WordPress color picker scripts and styles
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
// Inline script to initialize the color picker on the color input field
$inline_script = 'jQuery(document).ready(function($){ $("#mscp_text_color").wpColorPicker(); });';
wp_add_inline_script( 'wp-color-picker', $inline_script );
}
add_action( 'admin_enqueue_scripts', 'mscp_admin_scripts' );
/**
* 3. SHORTCODE IMPLEMENTATION
* The main function to output the customized text.
*/
function mscp_shortcode_output( $atts ) {
// Get the current options
$options = mscp_get_options();
// Define the output
$content = esc_html( $options['text_content'] );
$output = '<span class="mscp-shortcode-text">' . $content . '</span>';
// The shortcode should not be rendered if the text is empty
if ( empty( $content ) ) {
return '';
}
// Enqueue styles when the shortcode is used (this is important)
mscp_enqueue_styles();
return $output;
}
add_shortcode( 'my_customisable_shortcode', 'mscp_shortcode_output' );
/**
* 4. FRONT-END STYLING (The solution for inline style injection)
* Enqueues a base style handle and uses wp_add_inline_style to inject custom CSS.
*/
function mscp_enqueue_styles() {
$options = mscp_get_options();
// 1. Register a style handle (we use false for the source as it's purely for inline styles)
wp_register_style( 'mscp-custom-style', false );
// 2. Enqueue the registered handle
wp_enqueue_style( 'mscp-custom-style' );
// 3. Build the dynamic CSS based on options
$css = '.mscp-shortcode-text { ';
$css .= 'color: ' . esc_attr( $options['text_color'] ) . ';';
$css .= 'font-size: ' . absint( $options['font_size'] ) . 'px;';
if ( isset( $options['is_bold'] ) && $options['is_bold'] == 1 ) {
$css .= 'font-weight: bold;';
} else {
$css .= 'font-weight: normal;';
}
if ( isset( $options['is_italic'] ) && $options['is_italic'] == 1 ) {
$css .= 'font-style: italic;';
} else {
$css .= 'font-style: normal;';
}
// Add a default font-family for aesthetic consistency, if the user requested "font style"
$css .= 'font-family: sans-serif;';
$css .= 'display: inline-block;'; // Ensures all properties apply correctly
$css .= '}';
// 4. Inject the custom CSS using the standard WordPress function
wp_add_inline_style( 'mscp-custom-style', $css );
}
// NOTE: We don't hook mscp_enqueue_styles to 'wp_enqueue_scripts' directly.
// Instead, we call mscp_enqueue_styles() *inside* the shortcode function (mscp_shortcode_output)
// to ensure the styles are only loaded when the shortcode is actually present on the page.
?>
<?php
/**
* Plugin Name: My Customizable Shortcode Plugin
* Description: Creates a customizable shortcode [my_customisable_shortcode] managed via a dashboard settings page.
* Version: 1.1
* Author: Les Ey
* Author URI: https://les-ey.online
* License: GPL-2.0-or-later
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: mscp
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// --- CONSTANTS ---
define( 'MSCP_SLUG', 'mscp-settings' );
define( 'MSCP_OPTION_GROUP', 'mscp_options' );
define( 'MSCP_OPTION_NAME', 'mscp_shortcode_options' );
/**
* Defines the available fonts and their CSS stacks, mapping them to reliable Google Fonts
* for consistent front-end display.
*/
function mscp_get_available_fonts() {
return array(
'roboto' => array(
'label' => 'Arial-like (Roboto Sans-Serif)',
'stack' => 'Roboto, Arial, sans-serif',
),
'lora' => array(
'label' => 'Times-like (Lora Serif)',
'stack' => 'Lora, "Times New Roman", Times, serif',
),
'inconsolata' => array(
'label' => 'Consolas-like (Inconsolata Monospace)',
'stack' => 'Inconsolata, Consolas, monospace',
),
);
}
/**
* Load plugin textdomain for translation support.
*/
function mscp_load_textdomain() {
load_plugin_textdomain( 'mscp', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'mscp_load_textdomain' );
/**
* 1. ADMIN MENU AND SETTINGS SETUP
* Registers the settings page and settings fields.
*/
function mscp_add_admin_menu() {
add_menu_page(
esc_html__( 'Shortcode Customizer', 'mscp' ),
esc_html__( 'Custom Shortcode', 'mscp' ),
'manage_options',
MSCP_SLUG,
'mscp_settings_page',
'dashicons-edit-page',
80
);
}
add_action( 'admin_menu', 'mscp_add_admin_menu' );
function mscp_settings_init() {
register_setting( MSCP_OPTION_GROUP, MSCP_OPTION_NAME, 'mscp_options_validate' );
add_settings_section(
'mscp_main_section',
esc_html__( 'Customize Shortcode Appearance', 'mscp' ),
'mscp_settings_section_callback',
MSCP_SLUG
);
// Text Field (Memo Field)
add_settings_field(
'mscp_text_content',
esc_html__( 'Shortcode Text/Memo', 'mscp' ),
'mscp_text_content_render',
MSCP_SLUG,
'mscp_main_section'
);
// Color Picker Field
add_settings_field(
'mscp_text_color',
esc_html__( 'Text Color', 'mscp' ),
'mscp_text_color_render',
MSCP_SLUG,
'mscp_main_section'
);
// Font Name Dropdown (New Field)
add_settings_field(
'mscp_font_name',
esc_html__( 'Font Name', 'mscp' ),
'mscp_font_name_render',
MSCP_SLUG,
'mscp_main_section'
);
// Font Size Field
add_settings_field(
'mscp_font_size',
esc_html__( 'Font Size (px)', 'mscp' ),
'mscp_font_size_render',
MSCP_SLUG,
'mscp_main_section'
);
// Bold Checkbox
add_settings_field(
'mscp_is_bold',
esc_html__( 'Bold Text', 'mscp' ),
'mscp_is_bold_render',
MSCP_SLUG,
'mscp_main_section'
);
// Italics Checkbox
add_settings_field(
'mscp_is_italic',
esc_html__( 'Italic Text', 'mscp' ),
'mscp_is_italic_render',
MSCP_SLUG,
'mscp_main_section'
);
}
add_action( 'admin_init', 'mscp_settings_init' );
/**
* Settings Field Render Functions
*/
function mscp_get_options() {
// Note: Default text content must be wrapped in __() to be translatable
$default_text = esc_html__( 'Hello, World!', 'mscp' );
return get_option( MSCP_OPTION_NAME, array(
'text_content' => $default_text,
'text_color' => '#0073AA',
'font_name' => 'roboto', // Default font name updated
'font_size' => 16,
'is_bold' => 0,
'is_italic' => 0,
) );
}
function mscp_settings_section_callback() {
echo '<p>' . esc_html__( 'Configure the text and style for the [my_customisable_shortcode].', 'mscp' ) . '</p>';
}
function mscp_text_content_render() {
$options = mscp_get_options();
?>
<textarea name="<?php echo MSCP_OPTION_NAME; ?>[text_content]" rows="5" cols="50" style="width: 400px;"><?php echo esc_textarea( $options['text_content'] ); ?></textarea>
<?php
}
function mscp_text_color_render() {
$options = mscp_get_options();
?>
<input type="text" name="<?php echo MSCP_OPTION_NAME; ?>[text_color]" id="mscp_text_color" value="<?php echo esc_attr( $options['text_color'] ); ?>" class="mscp-color-field" data-default-color="#0073AA" />
<?php
}
/**
* Renders the font name dropdown selection.
*/
function mscp_font_name_render() {
$options = mscp_get_options();
$fonts = mscp_get_available_fonts();
?>
<select name="<?php echo MSCP_OPTION_NAME; ?>[font_name]">
<?php foreach ( $fonts as $key => $font ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $options['font_name'], $key ); ?>>
<?php echo esc_html( $font['label'] ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'These are Google Fonts equivalents for reliable cross-browser display.', 'mscp' ); ?></p>
<?php
}
function mscp_font_size_render() {
$options = mscp_get_options();
?>
<input type="number" name="<?php echo MSCP_OPTION_NAME; ?>[font_size]" value="<?php echo esc_attr( $options['font_size'] ); ?>" min="8" max="72" /> px
<p class="description"><?php esc_html_e( 'Enter font size in pixels.', 'mscp' ); ?></p>
<?php
}
function mscp_is_bold_render() {
$options = mscp_get_options();
$checked = ( isset( $options['is_bold'] ) && $options['is_bold'] == 1 ) ? 'checked="checked"' : '';
?>
<input type="checkbox" name="<?php echo MSCP_OPTION_NAME; ?>[is_bold]" value="1" <?php echo $checked; ?> />
<?php
}
function mscp_is_italic_render() {
$options = mscp_get_options();
$checked = ( isset( $options['is_italic'] ) && $options['is_italic'] == 1 ) ? 'checked="checked"' : '';
?>
<input type="checkbox" name="<?php echo MSCP_OPTION_NAME; ?>[is_italic]" value="1" <?php echo $checked; ?> />
<?php
}
function mscp_options_validate( $input ) {
$output = mscp_get_options(); // Start with current options
$fonts = mscp_get_available_fonts();
// Sanitize text content - allows line breaks
if ( isset( $input['text_content'] ) ) {
$output['text_content'] = sanitize_textarea_field( $input['text_content'] );
}
// Sanitize color (must be a valid hex color)
if ( isset( $input['text_color'] ) ) {
$output['text_color'] = sanitize_hex_color( $input['text_color'] );
}
// Sanitize font name - ensures it matches an available key
if ( isset( $input['font_name'] ) ) {
$font_keys = array_keys( $fonts );
$selected_font = sanitize_key( $input['font_name'] );
if ( in_array( $selected_font, $font_keys, true ) ) {
$output['font_name'] = $selected_font;
} else {
// Fallback to new default if somehow an invalid key is submitted
$output['font_name'] = 'roboto';
}
}
// Sanitize font size (integer check)
if ( isset( $input['font_size'] ) ) {
$output['font_size'] = absint( $input['font_size'] );
if ( $output['font_size'] < 8 || $output['font_size'] > 72 ) {
$output['font_size'] = 16; // Default safe value
}
}
// Sanitize checkboxes (0 or 1)
$output['is_bold'] = ( isset( $input['is_bold'] ) && $input['is_bold'] == 1 ) ? 1 : 0;
$output['is_italic'] = ( isset( $input['is_italic'] ) && $input['is_italic'] == 1 ) ? 1 : 0;
return $output;
}
/**
* Settings Page HTML
*/
function mscp_settings_page() {
// Check user capabilities
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form action="options.php" method="post">
<?php
// Output security fields for the registered setting group
settings_fields( MSCP_OPTION_GROUP );
// Output settings sections and fields
do_settings_sections( MSCP_SLUG );
// Output save settings button, translated
submit_button( esc_attr__( 'Save Customizations', 'mscp' ) );
?>
</form>
</div>
<?php
}
/**
* 2. ADMIN SCRIPTS AND STYLES (for Color Picker)
* Enqueues the color picker and initializes it with inline JavaScript.
*/
function mscp_admin_scripts( $hook ) {
// Check if we are on the correct admin page before loading assets
if ( strpos( $hook, MSCP_SLUG ) === false ) {
return;
}
// Enqueue the native WordPress color picker scripts and styles
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
// Inline script to initialize the color picker on the color input field
$inline_script = 'jQuery(document).ready(function($){ $("#mscp_text_color").wpColorPicker(); });';
wp_add_inline_script( 'wp-color-picker', $inline_script );
}
add_action( 'admin_enqueue_scripts', 'mscp_admin_scripts' );
/**
* 3. SHORTCODE IMPLEMENTATION
* The main function to output the customized text.
*/
function mscp_shortcode_output( $atts ) {
// Get the current options
$options = mscp_get_options();
$content_raw = $options['text_content'];
// The shortcode should not be rendered if the text is empty
if ( empty( $content_raw ) ) {
return '';
}
// Convert newlines to HTML <br> tags
$content_with_breaks = nl2br( $content_raw );
// Use wp_kses to strip any malicious HTML tags, but explicitly allow the safe <br> tag
$display_content = wp_kses( $content_with_breaks, array( 'br' => array() ) );
// Define the output
$output = '<span class="mscp-shortcode-text">' . $display_content . '</span>';
// Enqueue styles when the shortcode is used (this is important)
mscp_enqueue_styles();
return $output;
}
add_shortcode( 'my_customisable_shortcode', 'mscp_shortcode_output' );
/**
* 4. FRONT-END STYLING (The solution for inline style injection)
* Enqueues a base style handle and uses wp_add_inline_style to inject custom CSS.
*/
function mscp_enqueue_styles() {
$options = mscp_get_options();
$fonts = mscp_get_available_fonts();
// Determine the font data to use
$selected_font_key = isset( $options['font_name'] ) && array_key_exists( $options['font_name'], $fonts )
? $options['font_name']
: 'roboto'; // Fallback to 'roboto' key
$font_data = $fonts[$selected_font_key];
// --- ENQUEUE GOOGLE FONTS ---
// Combined URL to load all three font families (regular and bold weights) in one request.
$font_stack_url = 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&family=Lora:wght@400;700&family=Inconsolata:wght@400;700&display=swap';
// Enqueue the Google Fonts URL using a standard WordPress function.
wp_enqueue_style( 'mscp-google-fonts', $font_stack_url, array(), null );
// 1. Register a style handle (we use false for the source as it's purely for inline styles)
// Dependencies array ensures inline styles load *after* the Google Font faces.
wp_register_style( 'mscp-custom-style', false, array('mscp-google-fonts') );
// 2. Enqueue the registered handle
wp_enqueue_style( 'mscp-custom-style' );
// 3. Build the dynamic CSS based on options
$css = '.mscp-shortcode-text { ';
// Using !important on all custom properties to ensure theme styles do not override
$css .= 'color: ' . esc_attr( $options['text_color'] ) . ' !important;';
$css .= 'font-size: ' . absint( $options['font_size'] ) . 'px !important;';
$css .= 'font-family: ' . esc_attr( $font_data['stack'] ) . ' !important;'; // Re-adding !important here too
if ( isset( $options['is_bold'] ) && $options['is_bold'] == 1 ) {
$css .= 'font-weight: bold !important;';
} else {
// Explicitly set normal weight to ensure consistency with Google Fonts
$css .= 'font-weight: 400 !important;';
}
if ( isset( $options['is_italic'] ) && $options['is_italic'] == 1 ) {
$css .= 'font-style: italic !important;';
} else {
$css .= 'font-style: normal !important;';
}
$css .= 'display: inline-block;'; // Ensures all properties apply correctly
$css .= 'white-space: pre-wrap;'; // Ensures wrapping works correctly with <br> and spaces
$css .= '}';
// 4. Inject the custom CSS using the standard WordPress function
wp_add_inline_style( 'mscp-custom-style', $css );
}
// NOTE: We don't hook mscp_enqueue_styles to 'wp_enqueue_scripts' directly.
// Instead, we call mscp_enqueue_styles() *inside* the shortcode function (mscp_shortcode_output)
// to ensure the styles are only loaded when the shortcode is actually present on the page.
?>
Got Questions?

Views: 73
