Files
2026-06-18 16:00:11 +02:00

370 lines
12 KiB
PHP

<?php
/**
* Plugin settings.
*
* @package Blocksy_Light_Dark_Toggle
*/
defined( 'ABSPATH' ) || exit;
/**
* Settings registration and helpers.
*/
class BLC_LDT_Settings {
const OPTION = 'blc_ldt_settings';
/**
* Default settings.
*
* @return array<string, mixed>
*/
public static function defaults() {
return array(
'position' => 'bottom-right',
'offset_x' => '1.25rem',
'offset_y' => '1.25rem',
'z_index' => 999999,
'hide_header_switch' => true,
'respect_os_preference' => true,
'enable_animation' => true,
'show_on_mobile' => true,
'show_on_desktop' => true,
);
}
/**
* Initialize admin settings.
*/
public static function init() {
add_action( 'admin_init', array( __CLASS__, 'register' ) );
add_action( 'admin_menu', array( __CLASS__, 'add_menu' ) );
add_filter( 'plugin_action_links_' . plugin_basename( BLC_LDT_FILE ), array( __CLASS__, 'plugin_links' ) );
}
/**
* Get merged settings.
*
* @return array<string, mixed>
*/
public static function get() {
$stored = get_option( self::OPTION, array() );
if ( ! is_array( $stored ) ) {
$stored = array();
}
$settings = wp_parse_args( $stored, self::defaults() );
return apply_filters( 'blc_ldt_settings', $settings );
}
/**
* Get a single setting.
*
* @param string $key Setting key.
* @return mixed
*/
public static function get_value( $key ) {
$settings = self::get();
return isset( $settings[ $key ] ) ? $settings[ $key ] : null;
}
/**
* Register settings with the Settings API.
*/
public static function register() {
register_setting(
'blc_ldt_settings_group',
self::OPTION,
array(
'type' => 'array',
'sanitize_callback' => array( __CLASS__, 'sanitize' ),
'default' => self::defaults(),
)
);
add_settings_section(
'blc_ldt_main',
__( 'Toggle appearance', 'blocksy-light-dark-toggle' ),
'__return_false',
'blc-ldt-settings'
);
self::add_field( 'position', __( 'Position', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_position' ) );
self::add_field( 'offset_x', __( 'Horizontal offset', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_offset_x' ) );
self::add_field( 'offset_y', __( 'Vertical offset', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_offset_y' ) );
self::add_field( 'z_index', __( 'Z-index', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_z_index' ) );
add_settings_section(
'blc_ldt_behavior',
__( 'Behavior', 'blocksy-light-dark-toggle' ),
array( __CLASS__, 'section_behavior' ),
'blc-ldt-settings'
);
self::add_field( 'hide_header_switch', __( 'Hide header colour switch', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_hide_header_switch' ), 'blc_ldt_behavior' );
self::add_field( 'respect_os_preference', __( 'Follow system preference', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_respect_os_preference' ), 'blc_ldt_behavior' );
self::add_field( 'enable_animation', __( 'Animate transitions', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_enable_animation' ), 'blc_ldt_behavior' );
self::add_field( 'show_on_mobile', __( 'Show on mobile', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_show_on_mobile' ), 'blc_ldt_behavior' );
self::add_field( 'show_on_desktop', __( 'Show on desktop', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_show_on_desktop' ), 'blc_ldt_behavior' );
}
/**
* Register a settings field.
*
* @param string $id Field ID.
* @param string $title Field title.
* @param callable $callback Render callback.
* @param string $section Settings section.
*/
private static function add_field( $id, $title, $callback, $section = 'blc_ldt_main' ) {
add_settings_field(
'blc_ldt_' . $id,
$title,
$callback,
'blc-ldt-settings',
$section,
array( 'id' => $id )
);
}
/**
* Sanitize submitted settings.
*
* @param array<string, mixed> $input Raw input.
* @return array<string, mixed>
*/
public static function sanitize( $input ) {
$defaults = self::defaults();
$output = self::defaults();
if ( ! is_array( $input ) ) {
return $output;
}
$positions = array( 'bottom-right', 'bottom-left', 'top-right', 'top-left' );
$output['position'] = in_array( $input['position'] ?? '', $positions, true )
? $input['position']
: $defaults['position'];
$output['offset_x'] = self::sanitize_css_length( $input['offset_x'] ?? $defaults['offset_x'], $defaults['offset_x'] );
$output['offset_y'] = self::sanitize_css_length( $input['offset_y'] ?? $defaults['offset_y'], $defaults['offset_y'] );
$output['z_index'] = max( 1, min( 9999999, absint( $input['z_index'] ?? $defaults['z_index'] ) ) );
$output['hide_header_switch'] = ! empty( $input['hide_header_switch'] );
$output['respect_os_preference'] = ! empty( $input['respect_os_preference'] );
$output['enable_animation'] = ! empty( $input['enable_animation'] );
$output['show_on_mobile'] = ! empty( $input['show_on_mobile'] );
$output['show_on_desktop'] = ! empty( $input['show_on_desktop'] );
return $output;
}
/**
* Sanitize a CSS length value.
*
* @param string $value Raw value.
* @param string $default Default value.
* @return string
*/
private static function sanitize_css_length( $value, $default ) {
$value = trim( (string) $value );
if ( preg_match( '/^\d+(\.\d+)?(px|rem|em|vh|vw|%)$/', $value ) ) {
return $value;
}
return $default;
}
/**
* Add settings page.
*/
public static function add_menu() {
add_options_page(
__( 'Light/Dark Toggle', 'blocksy-light-dark-toggle' ),
__( 'Light/Dark Toggle', 'blocksy-light-dark-toggle' ),
'manage_options',
'blc-ldt-settings',
array( __CLASS__, 'render_page' )
);
}
/**
* Settings link on plugins list.
*
* @param string[] $links Plugin action links.
* @return string[]
*/
public static function plugin_links( $links ) {
$links[] = '<a href="' . esc_url( admin_url( 'options-general.php?page=blc-ldt-settings' ) ) . '">' . esc_html__( 'Settings', 'blocksy-light-dark-toggle' ) . '</a>';
return $links;
}
/**
* Behavior section description.
*/
public static function section_behavior() {
echo '<p>' . esc_html__( 'These options control how the toggle interacts with Blocksy and visitor preferences.', 'blocksy-light-dark-toggle' ) . '</p>';
}
/**
* Render settings page.
*/
public static function render_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$has_color_mode = BLC_LDT::has_blocksy_color_mode();
?>
<div class="wrap">
<h1><?php esc_html_e( 'Blocksy Light/Dark Toggle', 'blocksy-light-dark-toggle' ); ?></h1>
<?php if ( ! BLC_LDT::is_target_theme() ) : ?>
<div class="notice notice-warning"><p>
<?php esc_html_e( 'Blocksy is not the active theme. The toggle only appears on the front end when Blocksy (or a child theme) is active.', 'blocksy-light-dark-toggle' ); ?>
</p></div>
<?php elseif ( ! $has_color_mode ) : ?>
<div class="notice notice-info"><p>
<?php
echo wp_kses_post(
__( 'For heavily customized sites, enable Blocksy\'s <strong>Colour Mode Switch</strong> extension and configure your dark palette in the Customizer. The plugin will then use your custom dark colors instead of the built-in fallback palette.', 'blocksy-light-dark-toggle' )
);
?>
</p></div>
<?php else : ?>
<div class="notice notice-success"><p>
<?php esc_html_e( 'Blocksy Colour Mode Switch is active. Your Customizer dark palette will be used.', 'blocksy-light-dark-toggle' ); ?>
</p></div>
<?php endif; ?>
<form method="post" action="options.php">
<?php
settings_fields( 'blc_ldt_settings_group' );
do_settings_sections( 'blc-ldt-settings' );
submit_button();
?>
</form>
</div>
<?php
}
/**
* Read a checkbox value from settings.
*
* @param string $id Setting ID.
* @return bool
*/
private static function is_checked( $id ) {
return (bool) self::get_value( $id );
}
/**
* Render position field.
*/
public static function field_position() {
$value = self::get_value( 'position' );
$options = array(
'bottom-right' => __( 'Bottom right', 'blocksy-light-dark-toggle' ),
'bottom-left' => __( 'Bottom left', 'blocksy-light-dark-toggle' ),
'top-right' => __( 'Top right', 'blocksy-light-dark-toggle' ),
'top-left' => __( 'Top left', 'blocksy-light-dark-toggle' ),
);
?>
<select name="<?php echo esc_attr( self::OPTION ); ?>[position]">
<?php foreach ( $options as $key => $label ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $value, $key ); ?>><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
<?php
}
/**
* Render horizontal offset field.
*/
public static function field_offset_x() {
?>
<input type="text" class="small-text" name="<?php echo esc_attr( self::OPTION ); ?>[offset_x]" value="<?php echo esc_attr( (string) self::get_value( 'offset_x' ) ); ?>">
<p class="description"><?php esc_html_e( 'CSS value, e.g. 1.25rem or 20px', 'blocksy-light-dark-toggle' ); ?></p>
<?php
}
/**
* Render vertical offset field.
*/
public static function field_offset_y() {
?>
<input type="text" class="small-text" name="<?php echo esc_attr( self::OPTION ); ?>[offset_y]" value="<?php echo esc_attr( (string) self::get_value( 'offset_y' ) ); ?>">
<p class="description"><?php esc_html_e( 'CSS value, e.g. 1.25rem or 20px', 'blocksy-light-dark-toggle' ); ?></p>
<?php
}
/**
* Render z-index field.
*/
public static function field_z_index() {
?>
<input type="number" class="small-text" min="1" max="9999999" name="<?php echo esc_attr( self::OPTION ); ?>[z_index]" value="<?php echo esc_attr( (string) self::get_value( 'z_index' ) ); ?>">
<?php
}
/**
* Render hide header switch field.
*/
public static function field_hide_header_switch() {
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[hide_header_switch]" value="1" <?php checked( self::is_checked( 'hide_header_switch' ) ); ?>>
<?php esc_html_e( 'Hide Blocksy header colour switch to avoid duplicate toggles', 'blocksy-light-dark-toggle' ); ?>
</label>
<?php
}
/**
* Render respect OS preference field.
*/
public static function field_respect_os_preference() {
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[respect_os_preference]" value="1" <?php checked( self::is_checked( 'respect_os_preference' ) ); ?>>
<?php esc_html_e( 'Use system light/dark preference on first visit (before the visitor toggles)', 'blocksy-light-dark-toggle' ); ?>
</label>
<?php
}
/**
* Render animation field.
*/
public static function field_enable_animation() {
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[enable_animation]" value="1" <?php checked( self::is_checked( 'enable_animation' ) ); ?>>
<?php esc_html_e( 'Smooth colour transition when switching modes', 'blocksy-light-dark-toggle' ); ?>
</label>
<?php
}
/**
* Render show on mobile field.
*/
public static function field_show_on_mobile() {
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[show_on_mobile]" value="1" <?php checked( self::is_checked( 'show_on_mobile' ) ); ?>>
<?php esc_html_e( 'Show the floating toggle on mobile screens', 'blocksy-light-dark-toggle' ); ?>
</label>
<?php
}
/**
* Render show on desktop field.
*/
public static function field_show_on_desktop() {
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[show_on_desktop]" value="1" <?php checked( self::is_checked( 'show_on_desktop' ) ); ?>>
<?php esc_html_e( 'Show the floating toggle on desktop screens', 'blocksy-light-dark-toggle' ); ?>
</label>
<?php
}
}