Performance - Remove Google Fonts

				
					add_filter( 'elementor/frontend/print_google_fonts', '__return_false' );
				
			

Performance - Ensure Webfont is Loaded

				
					add_filter( 'elementor_pro/custom_fonts/font_display', function( $current_value, $font_family, $data ) {
	return 'swap';
}, 10, 3 );
				
			

Database - Reduce Revisions to 3

				
					define('WP_POST_REVISIONS', 3);
				
			

Admin - Remove Screen Options

				
					function remove_dashboard_widgets() {
    remove_action( 'welcome_panel', 'wp_welcome_panel' );
    remove_meta_box( 'dashboard_right_now', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_activity', 'dashboard', 'normal' );
    remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );
    remove_meta_box( 'dashboard_primary', 'dashboard', 'side' );
    remove_meta_box( 'dashboard_site_health', 'dashboard', 'normal' );
	 remove_meta_box( 'e-dashboard-overview', 'dashboard', 'normal' );
}
add_action( 'wp_dashboard_setup', 'remove_dashboard_widgets' );
				
			

Admin - Color Contrast Checker

				
					// Add the Contrast Checker Menu
add_action('admin_menu', 'contrast_checker_menu');
add_action('admin_init', 'contrast_checker_settings');

function contrast_checker_menu() {
    add_options_page('Contrast Checker', 'Contrast Checker', 'manage_options', 'contrast-checker', 'contrast_checker_page');
}

function contrast_checker_settings() {
    add_settings_section('contrast_checker_section', 'Contrast Checker Settings', null, 'contrast-checker');

    add_settings_field('background_color', 'Background Color', 'background_color_callback', 'contrast-checker', 'contrast_checker_section');
    add_settings_field('text_color', 'Foreground Color', 'foreground_color_callback', 'contrast-checker', 'contrast_checker_section');

    register_setting('contrast_checker_group', 'background_color');
    register_setting('contrast_checker_group', 'text_color');
}

function background_color_callback() {
    $background_color = get_option('background_color', '#FFFFFF');
    echo '<div style="display: flex; align-items: center;"><input type="text" id="background_color" name="background_color" value="' . esc_attr($background_color) . '" class="my-color-field" style="font-size: 15px !important;" />';
    echo '<span id="background_color_hex" style="margin-left: 10px; font-size: 15px;">' . esc_attr($background_color) . '</span></div>';
}

function foreground_color_callback() {
    $foreground_color = get_option('text_color', '#000000');
    echo '<div style="display: flex; align-items: center;"><input type="text" id="text_color" name="text_color" value="' . esc_attr($foreground_color) . '" class="my-color-field" style="font-size: 15px !important;" />';
    echo '<span id="text_color_hex" style="margin-left: 10px; font-size: 15px;">' . esc_attr($foreground_color) . '</span></div>';
}

function contrast_checker_page() {
    ?>
    <div class="wrap" style="font-size: 15px; line-height: 1.2em; background-color: #ffffff; padding: 20px; border-radius: 5px;">
        <h1 style="font-size: 20px; line-height: 1.2em;">Contrast Checker</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields('contrast_checker_group');
            do_settings_sections('contrast-checker');
            submit_button('Save Colors', 'primary', 'submit', true, array('style' => 'background-color: #000; font-size: 16px; font-weight: 600;'));
            ?>
        </form>
        <div id="contrast-result" style="margin-top: 20px;"></div>
        <div style="margin-top: 20px; font-size: 15px; line-height: 1.2em;">
            <h2 style="font-size: 15px; line-height: 1.2em;">What is AA?</h2>
            <p style="font-size: 15px; line-height: 1.2em;">AA compliance refers to the Web Content Accessibility Guidelines (WCAG) 2.1, which require a contrast ratio of at least 4.5:1 for normal text (minimum 16px) and 3:1 for large text (minimum 24px or 19px bold). This ensures that content is readable and accessible for individuals with moderate visual impairments.</p>
            <h2 style="font-size: 15px; line-height: 1.2em;">What is AAA?</h2>
            <p style="font-size: 15px; line-height: 1.2em;">AAA compliance is the highest level of conformance in WCAG 2.1. It requires a contrast ratio of at least 7:1 for normal text (minimum 16px) and 4.5:1 for large text (minimum 24px or 19px bold). Achieving AAA compliance ensures that content is accessible to the widest range of users, including those with significant visual impairments.</p>
        </div>
    </div>
    <style>
        .my-color-field {
            font-size: 15px !important;
        }
        .contrast-pass-yes {
            color: #007E33; font-weight: 600;
        }
        .contrast-pass-no {
            color: #E60000; font-weight: 600;
        }
    </style>
    <?php
}

// Enqueue the color picker script and add inline script
add_action('admin_enqueue_scripts', 'contrast_checker_enqueue_scripts');
function contrast_checker_enqueue_scripts($hook_suffix) {
    if ($hook_suffix != 'settings_page_contrast-checker') {
        return;
    }
    wp_enqueue_style('wp-color-picker');
    wp_enqueue_script('wp-color-picker');
    wp_add_inline_script('wp-color-picker', '
        jQuery(document).ready(function($) {
            function updateContrast() {
                var bgColor = $("#background_color").val();
                var txtColor = $("#text_color").val();

                $.ajax({
                    url: ajaxurl,
                    type: "POST",
                    data: {
                        action: "check_contrast",
                        bg_color: bgColor,
                        txt_color: txtColor
                    },
                    success: function(response) {
                        if (response.success) {
                            $("#contrast-result").html(response.data);
                        }
                    }
                });
            }

            $(".my-color-field").wpColorPicker({
                defaultColor: {
                    background_color: "#FFFFFF",
                    text_color: "#000000"
                },
                change: function(event, ui) {
                    var color = ui.color.toString();
                    if (event.target.id === "background_color") {
                        $("#background_color_hex").text(color);
                    } else if (event.target.id === "text_color") {
                        $("#text_color_hex").text(color);
                    }
                    updateContrast();
                }
            });

            // Initial contrast check
            updateContrast();
        });
    ');
}

// AJAX handler for checking contrast
add_action('wp_ajax_check_contrast', 'ajax_check_contrast');
function ajax_check_contrast() {
    $bg_color = sanitize_hex_color($_POST['bg_color']);
    $txt_color = sanitize_hex_color($_POST['txt_color']);
    $result = check_contrast($bg_color, $txt_color);
    wp_send_json_success($result);
}

function check_contrast($bg_hex, $txt_hex) {
    $bg_rgb = hex_to_rgb($bg_hex);
    $txt_rgb = hex_to_rgb($txt_hex);

    $bg_luminance = luminance($bg_rgb);
    $txt_luminance = luminance($txt_rgb);

    $contrast_ratio = contrast_ratio($bg_luminance, $txt_luminance);
    $aa_pass = ($contrast_ratio >= 4.5) ? '<span class="contrast-pass-yes">Yes</span>' : '<span class="contrast-pass-no">No</span>';
    $aaa_pass = ($contrast_ratio >= 7) ? '<span class="contrast-pass-yes">Yes</span>' : '<span class="contrast-pass-no">No</span>';

    $suggested_aa = adjust_foreground_color($bg_rgb, $txt_rgb, 4.5);
    $suggested_aaa = adjust_foreground_color($bg_rgb, $txt_rgb, 7);

    return '
        <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
            <div style="background-color: ' . esc_attr($bg_hex) . '; width: 20px; height: 20px; border: 1px solid #000;"></div>
            <div style="background-color: ' . esc_attr($txt_hex) . '; width: 20px; height: 20px; border: 1px solid #000;"></div>
        </div>
        <div style="font-size: 15px; line-height: 1.2em;">
            <div style="margin-bottom: 5px;"><strong>Contrast Ratio:</strong> ' . round($contrast_ratio, 2) . '</div>
            <div style="margin-bottom: 5px;"><strong>AA Compliant:</strong> ' . $aa_pass . '</div>
            <div style="margin-bottom: 5px;"><strong>AAA Compliant:</strong> ' . $aaa_pass . '</div>
        </div>
        <div style="margin-top: 20px; font-size: 15px; line-height: 1.2em;">
            <div style="margin-bottom: 20px;">
                <strong>Suggested Foreground Color for AA:</strong>
                <span style="background-color: ' . $suggested_aa . '; width: 20px; height: 20px; display: inline-block; border: 1px solid #000;"></span> ' . $suggested_aa . '
            </div>
            <div>
                <strong>Suggested Foreground Color for AAA:</strong>
                <span style="background-color: ' . $suggested_aaa . '; width: 20px; height: 20px; display: inline-block; border: 1px solid #000;"></span> ' . $suggested_aaa . '
            </div>
        </div>';
}

function hex_to_rgb($hex) {
    $hex = ltrim($hex, '#');
    $r = hexdec(substr($hex, 0, 2)) / 255;
    $g = hexdec(substr($hex, 2, 2)) / 255;
    $b = hexdec(substr($hex, 4, 2)) / 255;
    return array($r, $g, $b);
}

function rgb_to_hex($rgb) {
    return sprintf("#%02x%02x%02x", round($rgb[0] * 255), round($rgb[1] * 255), round($rgb[2] * 255));
}

function luminance($rgb) {
    list($r, $g, $b) = $rgb;
    $r = ($r <= 0.03928) ? $r / 12.92 : pow(($r + 0.055) / 1.055, 2.4);
    $g = ($g <= 0.03928) ? $g / 12.92 : pow(($g + 0.055) / 1.055, 2.4);
    $b = ($b <= 0.03928) ? $b / 12.92 : pow(($b + 0.055) / 1.055, 2.4);
    return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
}

function contrast_ratio($l1, $l2) {
    $lighter = max($l1, $l2);
    $darker = min($l1, $l2);
    return ($lighter + 0.05) / ($darker + 0.05);
}

function adjust_foreground_color($bg_rgb, $txt_rgb, $target_ratio) {
    $bg_luminance = luminance($bg_rgb);
    $step = 0.02; // Adjust step value for precision and performance

    // Incrementally adjust the RGB values to find a compliant color
    for ($i = 0; $i <= 1; $i += $step) {
        $test_rgb = adjust_rgb_lightness($txt_rgb, $i);
        $test_luminance = luminance($test_rgb);
        if (contrast_ratio($test_luminance, $bg_luminance) >= $target_ratio) {
            return rgb_to_hex($test_rgb);
        }

        $test_rgb = adjust_rgb_lightness($txt_rgb, -$i);
        $test_luminance = luminance($test_rgb);
        if (contrast_ratio($test_luminance, $bg_luminance) >= $target_ratio) {
            return rgb_to_hex($test_rgb);
        }
    }

    return "#000000"; // fallback to black if no suitable color found
}

function adjust_rgb_lightness($rgb, $adjustment) {
    return array(
        min(1, max(0, $rgb[0] + $adjustment)),
        min(1, max(0, $rgb[1] + $adjustment)),
        min(1, max(0, $rgb[2] + $adjustment))
    );
}

				
			

Custom Global Colors CSS

				
					add_action('save_post', function ($post_id, $post, $update) {
    // Check if the post is an instance of Elementor's Kit
    if ('elementor_library' === get_post_type($post_id) && 'kit' === get_post_meta($post_id, '_elementor_template_type', true)) {
        $meta = get_post_meta($post_id, '_elementor_page_settings', true);

        // Handle custom colors
        if (!empty($meta['custom_colors'])) {
            foreach ($meta['custom_colors'] as &$custom_color) {
                // Replace the hyphens inside of titles with underscores to ensure consistency
                $custom_color['_id'] = str_replace('-', '_', sanitize_title($custom_color['title']));
            }
        }

        // Update post meta
        update_post_meta($post_id, '_elementor_page_settings', $meta);
    }
}, 10, 3);

				
			

Admin - Password Lock Snippets Tab

				
					if (!function_exists('restrict_snippets_pages')) {
    function restrict_snippets_pages() {
        // Check if the current user is trying to access the Snippets pages
        global $pagenow;
        $restricted_pages = array(
            'admin.php?page=snippets',
            'admin.php?page=snippet-manager',
            'admin.php?page=snippet-import',
            'admin.php?page=add-snippet',
            'admin.php?page=import-code-snippets'
        );

        // Check if the current request URI contains any of the restricted pages
        $is_restricted_page = false;
        foreach ($restricted_pages as $restricted_page) {
            if (strpos($_SERVER['REQUEST_URI'], $restricted_page) !== false) {
                $is_restricted_page = true;
                break;
            }
        }

        if ($is_restricted_page) {
            // Start a session if not already started
            if (!session_id()) {
                session_start();
            }
            
            // SET THE PASSWORD HERE !!
            $required_password = '123'; // Change this to your desired password
            
            // Check if the password has been submitted and is correct
            if (isset($_POST['snippets_password']) && $_POST['snippets_password'] === $required_password) {
                $_SESSION['snippets_access_granted'] = true;
            }
            
            // Check if the session variable is not set, meaning access is not granted
            if (empty($_SESSION['snippets_access_granted'])) {
                // Display the password form
                echo '<form method="post" action="">
                    <p>Enter the password to access the Snippets pages:</p>
                    <input type="password" name="snippets_password" />
                    <input type="submit" value="Submit" />
                </form>';
                
                // Prevent the rest of the Snippets page from loading
                exit;
            }
        }
    }
    add_action('admin_init', 'restrict_snippets_pages');
}

				
			

Admin - Password Lock Settings Tab

				
					function restrict_settings_page() {
    // Check if the current user is trying to access the Settings page
    global $pagenow;
    $settings_pages = array(
        'options-general.php',
        'options-writing.php',
        'options-reading.php',
        'options-discussion.php',
        'options-media.php',
        'options-permalink.php',
        // Add more settings pages if you have plugins that add their own settings
    );

    if (in_array($pagenow, $settings_pages)) {
        
        // Start a session if not already started
        if (!session_id()) {
            session_start();
        }
        
        // Set the password
        $required_password = '123'; // Change this to your desired password
        
        // Check if the password has been submitted and is correct
        if (isset($_POST['settings_password']) && $_POST['settings_password'] === $required_password) {
            $_SESSION['settings_access_granted'] = true;
        }
        
        // Check if the session variable is not set, meaning access is not granted
        if (empty($_SESSION['settings_access_granted'])) {
            // Display the password form
            echo '<form method="post" action="">
                <p>Enter the password to access the Settings page:</p>
                <input type="password" name="settings_password" />
                <input type="submit" value="Submit" />
            </form>';
            
            // Prevent the rest of the Settings page from loading
            exit;
        }
    }
}
add_action('admin_init', 'restrict_settings_page');

				
			

Admin - Password Lock Users Tab

				
					function restrict_users_page() {
    // Check if the current user is trying to access the Users page
    global $pagenow;
    if ($pagenow === 'users.php' || $pagenow === 'user-new.php' || $pagenow === 'profile.php') {
        
        // Start a session if not already started
        if (!session_id()) {
            session_start();
        }
        
        // Set the password
        $required_password = '123'; // Change this to your desired password
        
        // Check if the password has been submitted and is correct
        if (isset($_POST['users_password']) && $_POST['users_password'] === $required_password) {
            $_SESSION['users_access_granted'] = true;
        }
        
        // Check if the session variable is not set, meaning access is not granted
        if (empty($_SESSION['users_access_granted'])) {
            // Display the password form
            echo '<form method="post" action="">
                <p>Enter the password to access the Users page:</p>
                <input type="password" name="users_password" />
                <input type="submit" value="Submit" />
            </form>';
            
            // Prevent the rest of the Users page from loading
            exit;
        }
    }
}
add_action('admin_init', 'restrict_users_page');

				
			

Admin - Password Lock Plugins Tab

				
					function restrict_plugins_page() {
    // Check if the current user is trying to access the Plugins page
    global $pagenow;
    if ($pagenow === 'plugins.php' || strpos($_SERVER['REQUEST_URI'], 'plugin-install.php') !== false || strpos($_SERVER['REQUEST_URI'], 'plugin-editor.php') !== false) {
        
        // Start a session if not already started
        if (!session_id()) {
            session_start();
        }
        
        // SET THE PASSWORD HERE !!
        $required_password = '123'; // Change this to your desired password
        
        // Check if the password has been submitted and is correct
        if (isset($_POST['plugins_password']) && $_POST['plugins_password'] === $required_password) {
            $_SESSION['plugins_access_granted'] = true;
        }
        
        // Check if the session variable is not set, meaning access is not granted
        if (empty($_SESSION['plugins_access_granted'])) {
            // Display the password form
            echo '<form method="post" action="">
                <p>Enter the password to access the Plugins page:</p>
                <input type="password" name="plugins_password" />
                <input type="submit" value="Submit" />
            </form>';
            
            // Prevent the rest of the Plugins page from loading
            exit;
        }
    }
}
add_action('admin_init', 'restrict_plugins_page');

				
			

Admin - Permalinks Set

				
					if ( ! function_exists( 'custom_set_permalink_structure_to_post_name' ) ) {
    /**
     * Sets the default permalink structure to 'Post Name' if it is not already set.
     *
     * To change the default permalink structure, update the `$desired_permalink_structure`
     * variable with one of the following values:
     *
     * - Plain: ''
     * - Day and Name: '/%year%/%monthnum%/%day%/%postname%/'
     * - Month and Name: '/%year%/%monthnum%/%postname%/'
     * - Numeric: '/%post_id%/'
     * - Post Name: '/%postname%/'
     * - Custom Structure: '/your/custom/structure/'
     */
    function custom_set_permalink_structure_to_post_name() {
        // Get the current permalink structure
        $current_permalink_structure = get_option('permalink_structure');

        // Set the desired permalink structure here
        // Default is 'Post Name'
        $desired_permalink_structure = '/%postname%/';

        // Uncomment one of the following lines to set a different permalink structure
        // Plain: 
        // $desired_permalink_structure = '';
        
        // Day and Name: 
        // $desired_permalink_structure = '/%year%/%monthnum%/%day%/%postname%/';
        
        // Month and Name: 
        // $desired_permalink_structure = '/%year%/%monthnum%/%postname%/';
        
        // Numeric: 
        // $desired_permalink_structure = '/%post_id%/';
        
        // Custom Structure: 
        // $desired_permalink_structure = '/your/custom/structure/';

        // Check if the current structure is not already set to the desired structure
        if ($current_permalink_structure != $desired_permalink_structure) {
            // Update the permalink structure
            update_option('permalink_structure', $desired_permalink_structure);
            // Flush rewrite rules to apply the new permalink structure
            flush_rewrite_rules();
        }
    }
    add_action('admin_init', 'custom_set_permalink_structure_to_post_name');
}

				
			

Admin - Media Original and 150x150

				
					function limit_image_sizes($sizes) {
    // Keep only the thumbnail size
    return array(
        'thumbnail' => $sizes['thumbnail']
    );
}
add_filter('intermediate_image_sizes_advanced', 'limit_image_sizes');

function set_thumbnail_size() {
    // Set the thumbnail size to 150x150
    update_option('thumbnail_size_w', 150);
    update_option('thumbnail_size_h', 150);
    update_option('thumbnail_crop', 1); // 1 for hard crop, 0 for soft crop
}
add_action('admin_init', 'set_thumbnail_size');

// Remove the unnecessary regenerate_thumbnails function as the thumbnail is already handled

				
			

Admin - Add Categories to Pages

				
					add_action( 'init', 'add_categories_to_pages' );
 /*
  * Add CATEGORIES to pages
  */
function add_categories_to_pages() {
  
  register_taxonomy_for_object_type( 'category', 'page' );
  
}
				
			

Admin - Purge the Cache

				
					/*
Plugin Name: Purge Cache
Description: Adds a button to the WordPress dashboard to clear the object cache
*/

add_action( 'admin_bar_menu', 'add_purge_cache_button', 999 );

function add_purge_cache_button( $wp_admin_bar ) {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }

    $args = array(
        'id'    => 'purge-cache',
        'title' => 'Purge Cache',
        'href'  => '#',
        'meta'  => array( 'class' => 'purge-cache' )
    );
    $wp_admin_bar->add_node( $args );
}

add_action( 'admin_footer', 'add_purge_cache_script' );

function add_purge_cache_script() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <script>
    jQuery(document).ready(function($) {
        $('#wp-admin-bar-purge-cache').click(function() {
            if (confirm('Are you sure you want to purge the cache?')) {
                $.ajax({
                    url: '<?php echo admin_url( 'admin-ajax.php' ); ?>',
                    data: {
                        action: 'purge_cache',
                    },
                    success: function() {
                        alert('Cache purged successfully!');
                    },
                    error: function() {
                        alert('An error occurred while purging the cache.');
                    }
                });
            }
        });
    });
    </script>
    <?php
}

add_action( 'wp_ajax_purge_cache', 'purge_cache_callback' );

function purge_cache_callback() {
    global $wp_object_cache;
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die();
    }

    wp_cache_flush();

    wp_die();
}
				
			

Admin - Font Clamp Calculator

				
					// Function to add the calculator to the admin menu
function ft_calculator_admin_menu()
{
    add_menu_page(
        "Fluid Typography Calculator", // Page title
        "Clamp Calculator", // Menu title
        "manage_options", // Capability
        "fluid-typography-calculator", // Menu slug
        "ft_calculator_page" // Function that displays the page content
    );
}

add_action("admin_menu", "ft_calculator_admin_menu");

// Function that generates the calculator page
function ft_calculator_page() {
    // Initialize the $useRem variable at the start of the function
    $useRem = false;

    echo '<div class="wrap"><h1>Fluid Typography Calculator</h1>';

    // Basic styles for the form and output
    echo '<style>
            .ft-form input[type="number"] {
                width: 100px;
                padding: 8px;
                margin: 4px 0;
                box-sizing: border-box;
                border: 1px solid #ccc;
                border-radius: 4px;
            }
            .ft-form label {
                font-weight: bold;
            }
            .ft-form div {
                margin-bottom: 10px;
            }
			.ft-form textarea {
				width: 100%;  // Adjust this to your preference
				height: 150px; // Or any height that suits your layout
				padding: 8px;
				border: 1px solid #ccc;
				border-radius: 4px;
				margin-top: 10px;
			}
			
			#cssOutput, #cssVariablesOutput {
				width: 50%; // Makes the textarea take up the full width of its container
				max-width: 50%; // You can adjust this as needed
				min-width: 300px; // Ensures a minimum width
				padding: 8px;
				border: 1px solid #ccc;
				border-radius: 4px;
				margin-top: 10px;
			}

            .ft-form button {
                background-color: #4CAF50;
                color: white;
                padding: 10px 15px;
                border: none;
                border-radius: 4px;
                cursor: pointer;
            }
            .ft-form button:hover {
                background-color: #45a049;
            }
            .grid-container {
                display: grid;
                grid-template-columns: repeat(5, 1fr);
                gap: 10px;
            }
          </style>';

// Initialize variables
    $rootFontSize = isset($_POST["rootFontSize"]) ? $_POST["rootFontSize"] : 16;
    $defaultValues = [
        "h1" => [
            "MinWidth" => 380,
            "MinFontSize" => 29,
            "MaxWidth" => 1600,
            "MaxFontSize" => 68,
        ],
        "h2" => [
            "MinWidth" => 380,
            "MinFontSize" => 24,
            "MaxWidth" => 1600,
            "MaxFontSize" => 51,
        ],
        "h3" => [
            "MinWidth" => 380,
            "MinFontSize" => 20,
            "MaxWidth" => 1600,
            "MaxFontSize" => 38,
        ],
        "h4" => [
            "MinWidth" => 380,
            "MinFontSize" => 17,
            "MaxWidth" => 1600,
            "MaxFontSize" => 29,
        ],
        "h5" => [
            "MinWidth" => 380,
            "MinFontSize" => 14,
            "MaxWidth" => 1600,
            "MaxFontSize" => 22,
        ],
        "h6" => [
            "MinWidth" => 380,
            "MinFontSize" => 12,
            "MaxWidth" => 1600,
            "MaxFontSize" => 16,
        ],
        "body" => [
            "MinWidth" => 380,
            "MinFontSize" => 16,
            "MaxWidth" => 1600,
            "MaxFontSize" => 24,
        ],
        "p" => [
            "MinWidth" => 380,
            "MinFontSize" => 16,
            "MaxWidth" => 1600,
            "MaxFontSize" => 24,
        ],
    ];

    $cssOutput = ""; // Initialize traditional CSS output string
    $cssVariablesOutput = ""; // Initialize CSS variables output string

    // Check if form is submitted
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $useRem = isset($_POST["unitToggle"]) && $_POST["unitToggle"] === 'on';
        // Security check using nonce
        if (!isset($_POST["ft_calculator_nonce_field"]) || !wp_verify_nonce($_POST["ft_calculator_nonce_field"], "ft_calculator_action")) {
            echo "<p>Security check failed. Please try again.</p>";
            return;
        }

foreach (["h1", "h2", "h3", "h4", "h5", "h6", "body", "p"] as $tag) {
            $minWidth = $_POST[$tag . "MinWidth"]; // In pixels
            $minFontSizePx = $_POST[$tag . "MinFontSize"]; // In pixels or REM based on $useRem
            $maxWidth = $_POST[$tag . "MaxWidth"]; // In pixels
            $maxFontSizePx = $_POST[$tag . "MaxFontSize"]; // In pixels or REM based on $useRem

            // Handle unit conversion
            if ($useRem) {
                // Convert REM to Pixels for internal calculation
                $minFontSizePx *= $rootFontSize;
                $maxFontSizePx *= $rootFontSize;
            }

            // Convert pixel values to REM for CSS generation
            $minFontSize = $minFontSizePx / $rootFontSize; // In rem
            $maxFontSize = $maxFontSizePx / $rootFontSize; // In rem

            // Convert widths from px to vw (assuming 1rem = rootFontSize px)
            $minWidthVW = $minWidth / $rootFontSize; // In vw
            $maxWidthVW = $maxWidth / $rootFontSize; // In vw

            // Calculate the CSS
            $vwUnit =
                (($maxFontSize - $minFontSize) / ($maxWidthVW - $minWidthVW)) *
                100;
            $constant = $minFontSize - ($vwUnit * $minWidthVW) / 100;

            // Format to a maximum of 5 decimal places
            $vwUnitFormatted = number_format($vwUnit, 5, ".", "");
            $constantFormatted = number_format($constant, 5, ".", "");

            // Add to traditional CSS output
            $cssOutput .=
                "{$tag} {font-size: clamp(" .
                $minFontSize .
                "rem, " .
                $constantFormatted .
                "rem + " .
                $vwUnitFormatted .
                "vw, " .
                $maxFontSize .
                "rem);}\n";

            // Add to CSS variables output
            $cssVariablesOutput .=
                "  --{$tag}-font-size: clamp(" .
                $minFontSize .
                "rem, " .
                $constantFormatted .
                "rem + " .
                $vwUnitFormatted .
                "vw, " .
                $maxFontSize .
                "rem);\n";
        }

        // Wrap CSS variables in a :root selector
        $cssVariablesOutput = ":root {\n" . $cssVariablesOutput . "}\n";
    }

// Display the form
echo '<form class="ft-form" method="post">';
wp_nonce_field("ft_calculator_action", "ft_calculator_nonce_field");

echo '<div>
        <label for="rootFontSize">Root HTML Font Size (px):</label>
        <input type="number" id="rootFontSize" name="rootFontSize" required value="' . htmlspecialchars($rootFontSize) . '">
      </div>
      <div>
        <label for="unitToggle">Use REM Units for Font Size:</label>
        <input type="checkbox" id="unitToggle" name="unitToggle" ' . ($useRem ? 'checked' : '') . ' onchange="toggleUnits()">
      </div>';

echo '<div class="grid-container">
        <div><strong>Tag</strong></div>
        <div><strong>Min Width (px)</strong></div>
        <div><strong>Min Font Size (' . ($useRem ? 'rem' : 'px') . ')</strong></div>
        <div><strong>Max Width (px)</strong></div>
        <div><strong>Max Font Size (' . ($useRem ? 'rem' : 'px') . ')</strong></div>';

// Display input fields for each tag
foreach (["h1", "h2", "h3", "h4", "h5", "h6", "body", "p"] as $tag) {
    $minWidthValue = isset($_POST[$tag . "MinWidth"]) ? $_POST[$tag . "MinWidth"] : $defaultValues[$tag]["MinWidth"];
    $minFontSizeValue = isset($_POST[$tag . "MinFontSize"]) ? $_POST[$tag . "MinFontSize"] : $defaultValues[$tag]["MinFontSize"];
    $maxWidthValue = isset($_POST[$tag . "MaxWidth"]) ? $_POST[$tag . "MaxWidth"] : $defaultValues[$tag]["MaxWidth"];
    $maxFontSizeValue = isset($_POST[$tag . "MaxFontSize"]) ? $_POST[$tag . "MaxFontSize"] : $defaultValues[$tag]["MaxFontSize"];

echo "<div><strong>{$tag}</strong></div>
          <div><input type='number' id='{$tag}MinWidth' name='{$tag}MinWidth' class='unit-input' required value='{$minWidthValue}'></div>
          <div><input type='number' id='{$tag}MinFontSize' name='{$tag}MinFontSize' class='unit-input' step='0.01' required value='{$minFontSizeValue}'></div>
          <div><input type='number' id='{$tag}MaxWidth' name='{$tag}MaxWidth' class='unit-input' required value='{$maxWidthValue}'></div>
          <div><input type='number' id='{$tag}MaxFontSize' name='{$tag}MaxFontSize' class='unit-input' step='0.01' required value='{$maxFontSizeValue}'></div>";
}


echo '</div><input type="submit" value="Generate CSS"></form>';


    // Display the traditional CSS output
    if (!empty($cssOutput)) {
        echo "<h2>Generated CSS:</h2>";
        echo '<textarea id="cssOutput" rows="10">' . htmlspecialchars($cssOutput) . "</textarea><br>";
        echo '<button onclick="copyToClipboard(\'cssOutput\')">Copy CSS</button>';
    }

    // Display the CSS variables output
    if (!empty($cssVariablesOutput)) {
        echo "<h2>Generated CSS Variables:</h2>";
        echo '<textarea id="cssVariablesOutput" rows="10">' . htmlspecialchars($cssVariablesOutput) . "</textarea><br>";
        echo '<button onclick="copyToClipboard(\'cssVariablesOutput\')">Copy CSS Variables</button>';
    }

    // JavaScript for copy to clipboard functionality and unit conversion
    echo '<script>
            function copyToClipboard(elementId) {
                var copyText = document.getElementById(elementId);
                copyText.select();
                document.execCommand("copy");
            }

function toggleUnits() {
    var useRem = document.getElementById("unitToggle").checked;
    var rootFontSize = parseFloat(document.getElementById("rootFontSize").value);
    var fontSizeElements = document.querySelectorAll(".unit-input"); // Class for font size inputs

    fontSizeElements.forEach(function(element) {
        if (element.id.endsWith("MinFontSize") || element.id.endsWith("MaxFontSize")) {
            var value = parseFloat(element.value);
            if (!isNaN(value)) {
                if (useRem) {
                    element.value = (value / rootFontSize).toFixed(2); // Convert to REM
                } else {
                    element.value = (value * rootFontSize).toFixed(0); // Convert to Pixels
                }
            }
        }
    });
}

          </script>';

    echo "</div>";
}


// Define the function for the fluid typography calculator
function fluid_typography_calculator() {
    // Call the existing function to display the fluid typography calculator
    ft_calculator_page();
}

// Add the shortcode
add_shortcode('Fluid_calc', 'fluid_typography_calculator');

				
			

Admin - Duplicate Pages and Posts

				
					function duplicate_post_as_draft() {
    global $wpdb;

    // Verify the nonce for security
    $nonce_action = 'duplicate_post_as_draft';
    $nonce_name = 'duplicate_nonce';
    if (!isset($_GET[$nonce_name]) || !wp_verify_nonce($_GET[$nonce_name], $nonce_action)) {
        wp_die(esc_html__('Security check failed.', 'wpturbo'));
    }

    // Check if the 'post' parameter is set in either GET or POST request
    $post_id = filter_input(INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT) ?: filter_input(INPUT_POST, 'post', FILTER_SANITIZE_NUMBER_INT);

    if (!$post_id) {
        wp_die(esc_html__('No post to duplicate has been supplied!', 'wpturbo'));
    }

    // Check if the post exists
    $post = get_post($post_id);
    if (!$post) {
        wp_die(esc_html(sprintf(__('Post creation failed, could not find original post: %s', 'wpturbo'), $post_id)));
    }

    $current_user = wp_get_current_user();
    $new_post_author = $current_user->ID;

    $args = [
        "comment_status" => $post->comment_status,
        "ping_status" => $post->ping_status,
        "post_author" => $new_post_author,
        "post_content" => $post->post_content,
        "post_excerpt" => $post->post_excerpt,
        "post_name" => $post->post_name,
        "post_parent" => $post->post_parent,
        "post_password" => $post->post_password,
        "post_status" => "draft",
        "post_title" => $post->post_title . " (Copy)",
        "post_type" => $post->post_type,
        "to_ping" => $post->to_ping,
        "menu_order" => $post->menu_order
    ];

    $new_post_id = wp_insert_post($args);

    $taxonomies = get_object_taxonomies($post->post_type);
    foreach ($taxonomies as $taxonomy) {
        $post_terms = wp_get_object_terms($post_id, $taxonomy, ["fields" => "slugs"]);
        wp_set_object_terms($new_post_id, $post_terms, $taxonomy, false);
    }

    $post_meta_infos = $wpdb->get_results(
        $wpdb->prepare("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id = %d", $post_id)
    );
    if (count($post_meta_infos) != 0) {
        foreach ($post_meta_infos as $meta_info) {
            $meta_key = $meta_info->meta_key;
            $meta_value = sanitize_meta($meta_info->meta_key, $meta_info->meta_value, "post");
            $wpdb->insert($wpdb->postmeta, [
                "post_id" => $new_post_id,
                "meta_key" => $meta_key,
                "meta_value" => $meta_value
            ]);
        }
    }

    // Redirect to the post list screen and show a success message
    $redirect_url = admin_url("edit.php?post_type=" . $post->post_type);
    wp_redirect(add_query_arg("message", "101", $redirect_url));
    exit();
}

add_action("admin_action_duplicate_post_as_draft", "duplicate_post_as_draft");

function duplicate_post_link($actions, $post) {
    if (current_user_can('edit_posts')) {
        $actions['duplicate'] = '<a href="' .
            wp_nonce_url(
                admin_url("admin.php?action=duplicate_post_as_draft&post=" . $post->ID),
                'duplicate_post_as_draft',
                'duplicate_nonce'
            ) .
            '" title="' . esc_attr__('Duplicar este ítem', 'wpturbo') .
            '" rel="permalink">' . esc_html__('Duplicar', 'wpturbo') . '</a>';
    }
    return $actions;
}

add_filter("post_row_actions", "duplicate_post_link", 10, 2);
add_filter("page_row_actions", "duplicate_post_link", 10, 2);

function apply_duplicate_post_link_to_cpts() {
    $post_types = get_post_types(["public" => true], "names");
    foreach ($post_types as $post_type) {
        add_filter("{$post_type}_row_actions", "duplicate_post_link", 10, 2);
    }
}

add_action("admin_init", "apply_duplicate_post_link_to_cpts");

function show_duplicate_admin_notice() {
    if (isset($_GET['message']) && $_GET['message'] === '101') {
        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html('Post duplicated successfully.') . '</p></div>';
    }
}

add_action('admin_notices', 'show_duplicate_admin_notice');
				
			

Admin - Infinity Scroll Media Library

				
					add_filter( 'media_library_infinite_scrolling', '__return_true' );
				
			

Admin - Load Woff2 directly to Media Library

				
					function custom_mime_types($mimes) {
    $mimes['woff'] = 'font/woff';
    $mimes['woff2'] = 'font/woff2';
    return $mimes;
}
add_filter('upload_mimes', 'custom_mime_types');

				
			

Admin - Disable User Registration

				
					// Disable user registration
add_filter('pre_option_users_can_register', '__return_zero');

				
			

Admin - Remove Comments

				
					/*
Comments in WordPress are like an open mic.
Sometimes you want to turn the mic off.
Silencing the comment section might just be your ticket to serenity.*/

function codesnippets_disable_comments() {
    // Remove comment support for posts
    remove_post_type_support( 'post', 'comments' );

    // Remove comment support for pages
    remove_post_type_support( 'page', 'comments' );

    // Remove comments from the admin menu
    remove_menu_page( 'edit-comments.php' );

    // Redirect comment-related URLs to the homepage
    add_action( 'template_redirect', 'codesnippets_disable_comments_redirect' );
}

function codesnippets_disable_comments_redirect() {
    global $wp_query;
    if ( is_single() || is_page() || is_attachment() ) {
        if ( have_comments() || comments_open() ) {
            wp_redirect( home_url(), 301 );
            exit;
        }
    }
}

add_action( 'admin_init', 'codesnippets_disable_comments' );
				
			

Performance - Stop Lazy Load

				
					add_filter( 'wp_lazy_loading_enabled', '__return_false' );
				
			

No Zoom on Mobile

Es un HTML y va en head

				
					<meta name="viewport" content="width=device-width, user-scalable=no">
				
			

Hide ReCAPTCHA

Es un HTML y va en body

				
					<style>
.grecaptcha-badge { visibility: hidden; }
</style>
				
			

PixRefiner

				
					//PixRefiner v3.4
// Helper function for formatting file sizes
function formatBytes($bytes, $precision = 2) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $bytes = max($bytes, 0);
    $pow = ($bytes > 0) ? floor(log($bytes) / log(1024)) : 0;
    $pow = min($pow, count($units) - 1);
    $bytes /= pow(1024, $pow);
    return round($bytes, $precision) . ' ' . $units[$pow];
}

// Limit default WordPress sizes to thumbnail only when auto-conversion is enabled
function wpturbo_limit_image_sizes($sizes) {
    if (wpturbo_get_disable_auto_conversion()) {
        return $sizes;
    }
    return isset($sizes['thumbnail']) ? ['thumbnail' => $sizes['thumbnail']] : $sizes;return ['thumbnail' => $sizes['thumbnail']];
}
//Global override for default sizes
add_filter('intermediate_image_sizes_advanced', function($sizes) {
    if (!wpturbo_get_disable_auto_conversion()) {
        return ['thumbnail' => [
            'width' => 150,
            'height' => 150,
            'crop' => true
        ]];
    }
    return $sizes;
}, 99);

add_filter('intermediate_image_sizes_advanced', 'wpturbo_limit_image_sizes');

// Set thumbnail size to 150x150
function wpturbo_set_thumbnail_size() {
    update_option('thumbnail_size_w', 150);
    update_option('thumbnail_size_h', 150);
    update_option('thumbnail_crop', 1);
}
add_action('admin_init', 'wpturbo_set_thumbnail_size');

// Register custom sizes (up to 3 additional sizes beyond the main one)
add_action('after_setup_theme', 'wpturbo_register_custom_sizes');
function wpturbo_register_custom_sizes() {
    $mode = wpturbo_get_resize_mode();
    if ($mode === 'width') {
        $max_values = wpturbo_get_max_widths();
        $additional_values = array_slice($max_values, 1, 3);
        foreach ($additional_values as $width) {
            add_image_size("custom-$width", $width, 0, false);
        }
    } else {
        $max_values = wpturbo_get_max_heights();
        $additional_values = array_slice($max_values, 1, 3);
        foreach ($additional_values as $height) {
            add_image_size("custom-$height", 0, $height, false);
        }
    }
}

// Get or set max widths (default to mobile-friendly set, limit to 4)
function wpturbo_get_max_widths() {
    $value = get_option('webp_max_widths', '1920,1200,600,300');
    $widths = array_map('absint', array_filter(explode(',', $value)));
    $widths = array_filter($widths, function($w) { return $w > 0 && $w <= 9999; });
    return array_slice($widths, 0, 4);
}

// Get or set max heights (default to mobile-friendly set, limit to 4)
function wpturbo_get_max_heights() {
    $value = get_option('webp_max_heights', '1080,720,480,360');
    $heights = array_map('absint', array_filter(explode(',', $value)));
    $heights = array_filter($heights, function($h) { return $h > 0 && $h <= 9999; });
    return array_slice($heights, 0, 4);
}

// Get or set resize mode
function wpturbo_get_resize_mode() {
    return get_option('webp_resize_mode', 'width');
}

// Get or set quality (0-100)
function wpturbo_get_quality() {
    return (int) get_option('webp_quality', 80);
}

// Get or set batch size
function wpturbo_get_batch_size() {
    return (int) get_option('webp_batch_size', 5);
}

// Get or set preserve originals
function wpturbo_get_preserve_originals() {
    return (bool) get_option('webp_preserve_originals', false);
}

// Get or set disable auto-conversion on upload
function wpturbo_get_disable_auto_conversion() {
    return (bool) get_option('webp_disable_auto_conversion', false);
}

// Get or set minimum size threshold in KB (default to 0, meaning no minimum)
function wpturbo_get_min_size_kb() {
    return (int) get_option('webp_min_size_kb', 0);
}

// Get or set whether to use AVIF instead of WebP
function wpturbo_get_use_avif() {
    return (bool) get_option('webp_use_avif', false);
}

// Get excluded image IDs
function wpturbo_get_excluded_images() {
    $excluded = get_option('webp_excluded_images', []);
    return is_array($excluded) ? array_map('absint', $excluded) : [];
}

// Add an image to the excluded list
function wpturbo_add_excluded_image($attachment_id) {
    $attachment_id = absint($attachment_id);
    $excluded = wpturbo_get_excluded_images();
    if (!in_array($attachment_id, $excluded)) {
        $excluded[] = $attachment_id;
        update_option('webp_excluded_images', array_unique($excluded));
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Excluded image added: Attachment ID %d', 'wpturbo'), $attachment_id);
        update_option('webp_conversion_log', array_slice((array)$log, -500));

        return true;
    }
    return false;
}

// Remove an image from the excluded list
function wpturbo_remove_excluded_image($attachment_id) {
    $attachment_id = absint($attachment_id);
    $excluded = wpturbo_get_excluded_images();
    $index = array_search($attachment_id, $excluded);
    if ($index !== false) {
        unset($excluded[$index]);
        update_option('webp_excluded_images', array_values($excluded));
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Excluded image removed: Attachment ID %d', 'wpturbo'), $attachment_id);
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Ensure MIME types are supported in .htaccess (Apache only)
function wpturbo_ensure_mime_types() {
    $htaccess_file = ABSPATH . '.htaccess';
    if (!file_exists($htaccess_file) || !is_writable($htaccess_file)) {
        return false;
    }

    $content = file_get_contents($htaccess_file);
    $webp_mime = "AddType image/webp .webp";
    $avif_mime = "AddType image/avif .avif";

    if (strpos($content, $webp_mime) === false || strpos($content, $avif_mime) === false) {
        $new_content = "# BEGIN WebP Converter MIME Types\n";
        if (strpos($content, $webp_mime) === false) {
            $new_content .= "$webp_mime\n";
        }
        if (strpos($content, $avif_mime) === false) {
            $new_content .= "$avif_mime\n";
        }
        $new_content .= "# END WebP Converter MIME Types\n";
        $content .= "\n" . $new_content;
        file_put_contents($htaccess_file, $content);
        return true;
    }
    return true;
}

// Core conversion function (supports WebP or AVIF)
function wpturbo_convert_to_format($file_path, $dimension, &$log = null, $attachment_id = null, $suffix = '') {
    $use_avif = wpturbo_get_use_avif();
    $format = $use_avif ? 'image/avif' : 'image/webp';
    $extension = $use_avif ? '.avif' : '.webp';
    $path_info = pathinfo($file_path);
    $new_file_path = $path_info['dirname'] . '/' . $path_info['filename'] . $suffix . $extension;
    $quality = wpturbo_get_quality();
    $mode = wpturbo_get_resize_mode();

    if (!(extension_loaded('imagick') || extension_loaded('gd'))) {
        if ($log !== null) $log[] = sprintf(__('Error: No image library (Imagick/GD) available for %s', 'wpturbo'), basename($file_path));
        return false;
    }

    $has_avif_support = (extension_loaded('imagick') && in_array('AVIF', Imagick::queryFormats())) || (extension_loaded('gd') && function_exists('imageavif'));
    if ($use_avif && !$has_avif_support) {
        if ($log !== null) $log[] = sprintf(__('Error: AVIF not supported on this server for %s', 'wpturbo'), basename($file_path));
        return false;
    }

    $editor = wp_get_image_editor($file_path);
    if (is_wp_error($editor)) {
        if ($log !== null) $log[] = sprintf(__('Error: Image editor failed for %s - %s', 'wpturbo'), basename($file_path), $editor->get_error_message());
        return false;
    }

    $dimensions = $editor->get_size();
    $resized = false;
    if ($mode === 'width' && $dimensions['width'] > $dimension) {
        $editor->resize($dimension, null, false);
        $resized = true;
    } elseif ($mode === 'height' && $dimensions['height'] > $dimension) {
        $editor->resize(null, $dimension, false);
        $resized = true;
    }

    $result = $editor->save($new_file_path, $format, ['quality' => $quality]);
    if (is_wp_error($result)) {
        if ($log !== null) $log[] = sprintf(__('Error: Conversion failed for %s - %s', 'wpturbo'), basename($file_path), $result->get_error_message());
        return false;
    }

    if ($log !== null) {
        $log[] = sprintf(
            __('Converted: %s → %s %s', 'wpturbo'),
            basename($file_path),
            basename($new_file_path),
            $resized ? sprintf(__('(resized to %dpx %s, quality %d)', 'wpturbo'), $dimension, $mode, $quality) : sprintf(__('(quality %d)', 'wpturbo'), $quality)
        );
    }

    return $new_file_path;
}

// Handle new uploads with format conversion
add_filter('wp_handle_upload', 'wpturbo_handle_upload_convert_to_format', 10, 1);
function wpturbo_handle_upload_convert_to_format($upload) {
    if (wpturbo_get_disable_auto_conversion()) {
        return $upload;
    }

    $file_extension = strtolower(pathinfo($upload['file'], PATHINFO_EXTENSION));
    $allowed_extensions = ['jpg', 'jpeg', 'png', 'webp', 'avif'];
    if (!in_array($file_extension, $allowed_extensions)) {
        return $upload;
    }

    $use_avif = wpturbo_get_use_avif();
    $format = $use_avif ? 'image/avif' : 'image/webp';
    $extension = $use_avif ? '.avif' : '.webp';

    $file_path = $upload['file'];
    $uploads_dir = dirname($file_path);
    $log = get_option('webp_conversion_log', []);

    // Check if uploads directory is writable
    if (!is_writable($uploads_dir)) {
        $log[] = sprintf(__('Error: Uploads directory %s is not writable', 'wpturbo'), $uploads_dir);
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return $upload;
    }

    $file_size_kb = filesize($file_path) / 1024;
    $min_size_kb = wpturbo_get_min_size_kb();

    if ($min_size_kb > 0 && $file_size_kb < $min_size_kb) {
        $log[] = sprintf(__('Skipped: %s (size %s KB < %d KB)', 'wpturbo'), basename($file_path), round($file_size_kb, 2), $min_size_kb);
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return $upload;
    }

    $mode = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
    $attachment_id = attachment_url_to_postid($upload['url']);
    $new_files = [];
    $success = true;

    // Get original image dimensions
    $editor = wp_get_image_editor($file_path);
    if (is_wp_error($editor)) {
        $log[] = sprintf(__('Error: Image editor failed for %s - %s', 'wpturbo'), basename($file_path), $editor->get_error_message());
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return $upload;
    }
    $dimensions = $editor->get_size();
    $original_width = $dimensions['width'];

    // Convert only for sizes smaller than or equal to original width (in width mode)
    $valid_max_values = $max_values;
    if ($mode === 'width') {
        $valid_max_values = array_filter($max_values, function($width, $index) use ($original_width) {
            // Always include the first size (original) or sizes smaller than original width
            return $index === 0 || $width <= $original_width;
        }, ARRAY_FILTER_USE_BOTH);
    }

    // Convert all valid sizes and rollback if any fail
    foreach ($valid_max_values as $index => $dimension) {
        $suffix = ($index === 0) ? '' : "-{$dimension}";
        $new_file_path = wpturbo_convert_to_format($file_path, $dimension, $log, $attachment_id, $suffix);
        if ($new_file_path) {
            if ($index === 0) {
                $upload['file'] = $new_file_path;
                $upload['url'] = str_replace(basename($file_path), basename($new_file_path), $upload['url']);
                $upload['type'] = $format;
            }
            $new_files[] = $new_file_path;
        } else {
            $success = false;
            break;
        }
    }

    // Generate thumbnail
    if ($success) {
        $editor = wp_get_image_editor($file_path);
        if (!is_wp_error($editor)) {
            $editor->resize(150, 150, true);
            $thumbnail_path = dirname($file_path) . '/' . pathinfo($file_path, PATHINFO_FILENAME) . '-150x150' . $extension;
            $saved = $editor->save($thumbnail_path, $format, ['quality' => wpturbo_get_quality()]);
            if (!is_wp_error($saved)) {
                $log[] = sprintf(__('Generated thumbnail: %s', 'wpturbo'), basename($thumbnail_path));
                $new_files[] = $thumbnail_path;
            } else {
                $success = false;
            }
        } else {
            $success = false;
        }
    }

    // Rollback if any conversion failed
    if (!$success) {
        foreach ($new_files as $file) {
            if (file_exists($file)) @unlink($file);
        }
        $log[] = sprintf(__('Error: Conversion failed for %s, rolling back', 'wpturbo'), basename($file_path));
        $log[] = sprintf(__('Original preserved: %s', 'wpturbo'), basename($file_path));
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return $upload;
    }

    // Update metadata only if all conversions succeeded
    if ($attachment_id && !empty($new_files)) {
        $metadata = wp_generate_attachment_metadata($attachment_id, $upload['file']);
        if (!is_wp_error($metadata)) {
            $base_name = pathinfo($file_path, PATHINFO_FILENAME);
            $dirname = dirname($file_path);
            // Add custom sizes
            foreach ($valid_max_values as $index => $dimension) {
                if ($index === 0) continue;
                $size_file = "$dirname/$base_name-$dimension$extension";
                if (file_exists($size_file)) {
                    $metadata['sizes']["custom-$dimension"] = [
                        'file' => "$base_name-$dimension$extension",
                        'width' => ($mode === 'width') ? $dimension : 0,
                        'height' => ($mode === 'height') ? $dimension : 0,
                        'mime-type' => $format
                    ];
                }
            }
            // Ensure thumbnail metadata is always updated
            $thumbnail_file = "$dirname/$base_name-150x150$extension";
            if (file_exists($thumbnail_file)) {
                $metadata['sizes']['thumbnail'] = [
                    'file' => "$base_name-150x150$extension",
                    'width' => 150,
                    'height' => 150,
                    'mime-type' => $format
                ];
            } else {
                // Regenerate thumbnail if missing
                $editor = wp_get_image_editor($upload['file']);
                if (!is_wp_error($editor)) {
                    $editor->resize(150, 150, true);
                    $saved = $editor->save($thumbnail_file, $format, ['quality' => wpturbo_get_quality()]);
                    if (!is_wp_error($saved)) {
                        $metadata['sizes']['thumbnail'] = [
                            'file' => "$base_name-150x150$extension",
                            'width' => 150,
                            'height' => 150,
                            'mime-type' => $format
                        ];
                        $log[] = sprintf(__('Regenerated missing thumbnail: %s', 'wpturbo'), basename($thumbnail_file));
                    }
                }
            }
            $metadata['webp_quality'] = wpturbo_get_quality();
            $metadata['pixrefiner_stamp'] = [
                'format' => wpturbo_get_use_avif() ? 'avif' : 'webp',
                'quality' => wpturbo_get_quality(),
                'resize_mode' => wpturbo_get_resize_mode(),
                'max_values' => array_values($valid_max_values), // Use valid sizes in stamp
            ];
            update_attached_file($attachment_id, $upload['file']);
            wp_update_post(['ID' => $attachment_id, 'post_mime_type' => $format]);
            wp_update_attachment_metadata($attachment_id, $metadata);
            if (!empty($metadata['pixrefiner_stamp'])) {
                $log[] = "Stamp check for Attachment ID {$attachment_id}:";
                $log[] = "Expected stamp: " . json_encode($metadata['pixrefiner_stamp']);
                $log[] = "Existing stamp: none (new upload)";
            }
        } else {
            $log[] = sprintf(__('Error: Metadata regeneration failed for %s - %s', 'wpturbo'), basename($file_path), $metadata->get_error_message());
        }
    }

    // Delete original only if all conversions succeeded and not preserved
    if ($file_extension !== ($use_avif ? 'avif' : 'webp') && file_exists($file_path) && !wpturbo_get_preserve_originals()) {
        $attempts = 0;
        $chmod_failed = false;
        while ($attempts < 5 && file_exists($file_path)) {
            if (!is_writable($file_path)) {
                @chmod($file_path, 0644);
                if (!is_writable($file_path)) {
                    if ($chmod_failed) {
                        $log[] = sprintf(__('Error: Cannot make %s writable after retry - skipping deletion', 'wpturbo'), basename($file_path));
                        break;
                    }
                    $chmod_failed = true;
                }
            }
            if (@unlink($file_path)) {
                $log[] = sprintf(__('Deleted original: %s', 'wpturbo'), basename($file_path));
                break;
            }
            $attempts++;
            sleep(1);
        }
        if (file_exists($file_path)) {
            $log[] = sprintf(__('Error: Failed to delete original %s after 5 retries', 'wpturbo'), basename($file_path));
        }
    }

    update_option('webp_conversion_log', array_slice((array)$log, -500));
    return $upload;
}

// Fix metadata for converted images
add_filter('wp_generate_attachment_metadata', 'wpturbo_fix_format_metadata', 10, 2);
function wpturbo_fix_format_metadata($metadata, $attachment_id) {
    $use_avif  = wpturbo_get_use_avif();
    $extension = $use_avif ? 'avif' : 'webp';
    $format    = $use_avif ? 'image/avif' : 'image/webp';

    $file = get_attached_file($attachment_id);
    if (pathinfo($file, PATHINFO_EXTENSION) !== $extension) {
        return $metadata;
    }

    $uploads    = wp_upload_dir();
    $file_path  = $file;
    $file_name  = basename($file_path);
    $dirname    = dirname($file_path);
    $base_name  = pathinfo($file_name, PATHINFO_FILENAME);
    $mode       = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();

    $metadata['file']      = str_replace($uploads['basedir'] . '/', '', $file_path);
    $metadata['mime_type'] = $format;

    foreach ($max_values as $index => $dimension) {
        if ($index === 0) continue;
        $size_file = "$dirname/$base_name-$dimension.$extension";
        if (file_exists($size_file)) {
            $metadata['sizes']["custom-$dimension"] = [
                'file'      => "$base_name-$dimension.$extension",
                'width'     => ($mode === 'width') ? $dimension : 0,
                'height'    => ($mode === 'height') ? $dimension : 0,
                'mime-type' => $format
            ];
        }
    }

    $thumbnail_file = "$dirname/$base_name-150x150.$extension";
    if (file_exists($thumbnail_file)) {
        $metadata['sizes']['thumbnail'] = [
            'file'      => "$base_name-150x150.$extension",
            'width'     => 150,
            'height'    => 150,
            'mime-type' => $format
        ];
    }

    // ✅ Add stamp
    $metadata['pixrefiner_stamp'] = [
        'format'      => $use_avif ? 'avif' : 'webp',
        'quality'     => wpturbo_get_quality(),
        'resize_mode' => $mode,
        'max_values'  => $max_values,
    ];

    // ✅ Log stamp
    $log = get_option('webp_conversion_log', []);
    $log[] = "Stamp set via metadata hook for Attachment ID {$attachment_id}:";
    $log[] = "Stamp: " . json_encode($metadata['pixrefiner_stamp']);
    update_option('webp_conversion_log', array_slice((array)$log, -500));

    return $metadata;
}


//
function wpturbo_convert_single_image_checker($attachments) {
    if (empty($attachments) || !is_array($attachments)) {
        return;
    }

    $log = [];

    foreach ($attachments as $attachment_id) {
        $file_path = get_attached_file($attachment_id);
        $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
        if (!file_exists($file_path) || in_array($ext, ['jpg', 'jpeg', 'png'])) {
            $log[] = sprintf(__('Skipped: Missing or original format (%s) for Attachment ID %d', 'wpturbo'), $ext, $attachment_id);
            continue;
        }

        $metadata = wp_get_attachment_metadata($attachment_id);

        // Build the expected stamp from current settings
        $expected_stamp = [
            'format'      => wpturbo_get_use_avif() ? 'avif' : 'webp',
            'quality'     => wpturbo_get_quality(),
            'resize_mode' => wpturbo_get_resize_mode(),
            'max_values'  => (wpturbo_get_resize_mode() === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights(),
        ];

        $existing_stamp = isset($metadata['pixrefiner_stamp']) ? $metadata['pixrefiner_stamp'] : null;

        // 🔍 Stamp comparison log
        $log[] = "Stamp check for Attachment ID {$attachment_id}:";
        $log[] = "Expected stamp: " . json_encode($expected_stamp);
        $log[] = "Existing stamp: " . ($existing_stamp ? json_encode($existing_stamp) : 'none');

        if (!empty($existing_stamp) && $existing_stamp !== $expected_stamp && empty($_GET['force_reconvert'])) {
            foreach ($expected_stamp as $key => $value) {
                if (!isset($existing_stamp[$key]) || $existing_stamp[$key] !== $value) {
                    $log[] = "Mismatch in stamp key '$key': expected " . json_encode($value) . " but got " . json_encode($existing_stamp[$key] ?? null);
                }
            }
        }

        // ✅ If already optimized and no force reconvert
        if (!empty($existing_stamp) && $existing_stamp === $expected_stamp && empty($_GET['force_reconvert'])) {
            $log[] = sprintf(__('Skipped: Already optimized Attachment ID %d', 'wpturbo'), $attachment_id);
            continue;
        }

        // 🔥 Proceed with conversion
        $conversion_result = wpturbo_convert_image($file_path, $attachment_id); // <-- your custom image converter

        if (!$conversion_result) {
            $log[] = sprintf(__('Failed to convert Attachment ID %d.', 'wpturbo'), $attachment_id);
            continue;
        }

        // 🛡️ If successful, update metadata with the new stamp
        if (!empty($metadata)) {
            $metadata['pixrefiner_stamp'] = $expected_stamp;
            wp_update_attachment_metadata($attachment_id, $metadata);
        }

        $log[] = sprintf(__('Converted Attachment ID %d successfully.', 'wpturbo'), $attachment_id);
    }

    update_option('webp_conversion_log', array_slice((array)$log, -500));
    return $log;
}

function wpturbo_convert_single_image() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options') || !isset($_POST['offset'])) {
        wp_send_json_error(__('Permission denied or invalid offset', 'wpturbo'));
    }

    $offset       = absint($_POST['offset']);
    $batch_size   = wpturbo_get_batch_size();
    $mode         = wpturbo_get_resize_mode();
    $max_values   = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
    $current_ext  = wpturbo_get_use_avif() ? 'avif' : 'webp';
    $format       = wpturbo_get_use_avif() ? 'image/avif' : 'image/webp';
    $current_qual = wpturbo_get_quality();

    wp_raise_memory_limit('image');
    set_time_limit(max(30, 10 * $batch_size));

    $attachments = get_posts([
        'post_type'      => 'attachment',
        'post_mime_type' => ['image/jpeg', 'image/png', 'image/webp', 'image/avif'],
        'posts_per_page' => $batch_size,
        'offset'         => $offset,
        'fields'         => 'ids',
        'post__not_in'   => wpturbo_get_excluded_images(),
    ]);

    $log = get_option('webp_conversion_log', []);

    if (empty($attachments)) {
        update_option('webp_conversion_complete', true);
        $log[] = "<strong style='color:#281E5D;'>" . __('Conversion Complete', 'wpturbo') . '</strong>: ' . __('No more images to process', 'wpturbo');
        update_option('webp_conversion_log', array_slice((array) $log, -500));
        wp_send_json_success(['complete' => true]);
    }

    foreach ($attachments as $attachment_id) {
        $file_path = get_attached_file($attachment_id);
        if (!file_exists($file_path)) continue;

        // Skip if already optimized and settings match
        $meta = wp_get_attachment_metadata($attachment_id);
        $expected_stamp = [
            'format'      => $current_ext,
            'quality'     => $current_qual,
            'resize_mode' => $mode,
            'max_values'  => $max_values,
        ];
        $existing_stamp = isset($meta['pixrefiner_stamp']) ? $meta['pixrefiner_stamp'] : null;
        if (!empty($existing_stamp) && $existing_stamp === $expected_stamp && empty($_GET['force_reconvert'])) {
            $log[] = sprintf(__('Skipped: Already optimized Attachment ID %d', 'wpturbo'), $attachment_id);
            continue;
        }

        $dirname   = dirname($file_path);
        $base_name = pathinfo($file_path, PATHINFO_FILENAME);
        $ext       = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
        $new_files = [];
        $success   = true;

        // DELETE OUTDATED or DUPLICATE SIZES FROM FILESYSTEM AND METADATA
        $old_sizes = isset($meta['sizes']) ? $meta['sizes'] : [];
        $main_size = $max_values[0] ?? null;
        $meta['sizes'] = []; // Initialize sizes to empty to clear outdated entries

        foreach ($old_sizes as $size_key => $size_data) {
            if (preg_match('/custom-(\d+)/', $size_key, $m)) {
                $old_dim = (int) $m[1];
                $is_redundant = (
                    !in_array($old_dim, $max_values)
                    || ($main_size && $old_dim === $main_size)
                );
                if ($is_redundant) {
                    $old_file = "$dirname/$base_name-$old_dim.$current_ext";
                    if (file_exists($old_file)) {
                        @unlink($old_file);
                        $log[] = sprintf(__('Deleted duplicate or outdated size: %s', 'wpturbo'), basename($old_file));
                    }
                } else {
                    // Preserve valid sizes that still exist
                    $meta['sizes'][$size_key] = $size_data;
                }
            } else if ($size_key === 'thumbnail') {
                // Always preserve thumbnail if it exists
                $meta['sizes']['thumbnail'] = $size_data;
            }
        }

        // Get original image dimensions
        $editor = wp_get_image_editor($file_path);
        if (is_wp_error($editor)) {
            $log[] = sprintf(__('Error: Image editor failed for %s - %s', 'wpturbo'), basename($file_path), $editor->get_error_message());
            continue;
        }
        $dimensions = $editor->get_size();
        $original_width = $dimensions['width'];

        // Filter max_values to include only sizes less than or equal to original width
        $valid_max_values = $max_values;
        if ($mode === 'width') {
            $valid_max_values = array_filter($max_values, function($width, $index) use ($original_width) {
                return $index === 0 || $width <= $original_width;
            }, ARRAY_FILTER_USE_BOTH);
            // Log skipped sizes for debugging
            if (count($valid_max_values) < count($max_values)) {
                $skipped = array_diff($max_values, $valid_max_values);
                $log[] = sprintf(__('Skipped sizes %s for %s (original width %dpx)', 'wpturbo'), implode(', ', $skipped), basename($file_path), $original_width);
            }
        }

        // GENERATE NEW SIZES
        foreach ($valid_max_values as $index => $dimension) {
            $suffix = ($index === 0) ? '' : "-$dimension";
            $output = wpturbo_convert_to_format($file_path, $dimension, $log, $attachment_id, $suffix);
            if ($output) {
                if ($index === 0) {
                    update_attached_file($attachment_id, $output);
                    wp_update_post(['ID' => $attachment_id, 'post_mime_type' => $format]);
                }
                $new_files[] = $output;
            } else {
                $success = false;
                break;
            }
        }

        // GENERATE THUMBNAIL
        $thumb_path = "$dirname/$base_name-150x150.$current_ext";
        if (!file_exists($thumb_path)) {
            $editor = wp_get_image_editor($file_path);
            if (!is_wp_error($editor)) {
                $editor->resize(150, 150, true);
                $saved = $editor->save($thumb_path, $format, ['quality' => $current_qual]);
                if (!is_wp_error($saved)) {
                    $new_files[] = $thumb_path;
                    $log[] = sprintf(__('Generated thumbnail: %s', 'wpturbo'), basename($thumb_path));
                } else {
                    $success = false;
                }
            } else {
                $success = false;
            }
        }

        if (!$success) {
            foreach ($new_files as $f) if (file_exists($f)) @unlink($f);
            $log[] = sprintf(__('Error: Conversion failed for %s, rolled back.', 'wpturbo'), basename($file_path));
            continue;
        }

        // UPDATE METADATA & STAMP
        if (!empty($new_files)) {
            $meta = wp_generate_attachment_metadata($attachment_id, $new_files[0]);
            if (!is_wp_error($meta)) {
                $meta['sizes'] = []; // Clear sizes again to ensure only new sizes are included
                foreach ($valid_max_values as $index => $dimension) {
                    if ($index === 0) continue;
                    $size_file = "$dirname/$base_name-$dimension.$current_ext";
                    if (file_exists($size_file)) {
                        $meta['sizes']["custom-$dimension"] = [
                            'file'      => "$base_name-$dimension.$current_ext",
                            'width'     => ($mode === 'width') ? $dimension : 0,
                            'height'    => ($mode === 'height') ? $dimension : 0,
                            'mime-type' => $format,
                        ];
                    }
                }
                if (file_exists($thumb_path)) {
                    $meta['sizes']['thumbnail'] = [
                        'file'      => "$base_name-150x150.$current_ext",
                        'width'     => 150,
                        'height'    => 150,
                        'mime-type' => $format,
                    ];
                }
                $meta['webp_quality']      = $current_qual;
                $meta['pixrefiner_stamp'] = $expected_stamp;
                wp_update_attachment_metadata($attachment_id, $meta);
            }
        }

        // DELETE ORIGINAL IF NOT PRESERVED
        if (!wpturbo_get_preserve_originals() && file_exists($file_path) && $ext !== $current_ext) {
            @unlink($file_path);
            $log[] = sprintf(__('Deleted original: %s', 'wpturbo'), basename($file_path));
        }

        $log[] = sprintf(__('Converted Attachment ID %d successfully.', 'wpturbo'), $attachment_id);
    }

    update_option('webp_conversion_log', array_slice((array)$log, -500));
    wp_send_json_success([
        'complete' => false,
        'offset'   => $offset + $batch_size
    ]);
}

// Progress tracking via AJAX
function wpturbo_webp_conversion_status() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Permission denied', 'wpturbo'));
    }

    $total = wp_count_posts('attachment')->inherit;
    $per_page = 50; // Chunk size for pagination
    $converted = 0;
    $skipped = 0;

    // Validate total count
    if ($total < 0) {
        $log = get_option('webp_conversion_log', []);
        $log[] = __('Error: Invalid attachment count detected', 'wpturbo');
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        wp_send_json_error(__('Invalid attachment count', 'wpturbo'));
    }

    // Count converted images in chunks
    for ($offset = 0; $offset < $total; $offset += $per_page) {
        $chunk_result = get_posts([
            'post_type' => 'attachment',
            'post_mime_type' => wpturbo_get_use_avif() ? 'image/avif' : 'image/webp',
            'posts_per_page' => $per_page,
            'offset' => $offset,
            'fields' => 'ids',
        ]);
        $converted += count($chunk_result);
    }

    // Count skipped images in chunks
    for ($offset = 0; $offset < $total; $offset += $per_page) {
        $chunk_result = get_posts([
            'post_type' => 'attachment',
            'post_mime_type' => ['image/jpeg', 'image/png'],
            'posts_per_page' => $per_page,
            'offset' => $offset,
            'fields' => 'ids',
        ]);
        $skipped += count($chunk_result);
    }

    $remaining = $total - $converted - $skipped;
    $excluded_images = wpturbo_get_excluded_images();
    $excluded_data = [];
    foreach ($excluded_images as $id) {
        $thumbnail = wp_get_attachment_image_src($id, 'thumbnail');
        $excluded_data[] = [
            'id' => $id,
            'title' => get_the_title($id),
            'thumbnail' => $thumbnail ? $thumbnail[0] : ''
        ];
    }

    $mode = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();

    wp_send_json([
        'total' => $total,
        'converted' => $converted,
        'skipped' => $skipped,
        'remaining' => $remaining,
        'excluded' => count($excluded_images),
        'excluded_images' => $excluded_data,
        'log' => get_option('webp_conversion_log', []),
        'complete' => get_option('webp_conversion_complete', false),
        'resize_mode' => $mode,
        'max_values' => implode(', ', $max_values),
        'max_widths' => implode(', ', wpturbo_get_max_widths()),
        'max_heights' => implode(', ', wpturbo_get_max_heights()),
        'quality' => wpturbo_get_quality(),
        'preserve_originals' => wpturbo_get_preserve_originals(),
        'disable_auto_conversion' => wpturbo_get_disable_auto_conversion(),
        'min_size_kb' => wpturbo_get_min_size_kb(),
        'use_avif' => wpturbo_get_use_avif()
    ]);
}

// Clear log
function wpturbo_clear_log() {
    if (!isset($_GET['clear_log']) || !current_user_can('manage_options')) {
        return false;
    }
    update_option('webp_conversion_log', [__('Log cleared', 'wpturbo')]);
    return true;
}

// Reset to defaults
function wpturbo_reset_defaults() {
    if (!isset($_GET['reset_defaults']) || !current_user_can('manage_options')) {
        return false;
    }
    update_option('webp_max_widths', '1920,1200,600,300');
    update_option('webp_max_heights', '1080,720,480,360');
    update_option('webp_resize_mode', 'width');
    update_option('webp_quality', 80);
    update_option('webp_batch_size', 5);
    update_option('webp_preserve_originals', false);
    update_option('webp_disable_auto_conversion', false);
    update_option('webp_min_size_kb', 0);
    update_option('webp_use_avif', false);
    $log = get_option('webp_conversion_log', []);
    $log[] = __('Settings reset to defaults', 'wpturbo');
    update_option('webp_conversion_log', array_slice((array)$log, -500));
    return true;
}

// Set max widths
function wpturbo_set_max_widths() {
    if (!isset($_GET['set_max_width']) || !current_user_can('manage_options') || !isset($_GET['max_width'])) {
        return false;
    }
    $max_widths = sanitize_text_field($_GET['max_width']);
    $width_array = array_filter(array_map('absint', explode(',', $max_widths)));
    $width_array = array_filter($width_array, function($w) { return $w > 0 && $w <= 9999; });
    $width_array = array_slice($width_array, 0, 4);
    if (!empty($width_array)) {
        update_option('webp_max_widths', implode(',', $width_array));
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Max widths set to: %spx', 'wpturbo'), implode(', ', $width_array));
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Set max heights
function wpturbo_set_max_heights() {
    if (!isset($_GET['set_max_height']) || !current_user_can('manage_options') || !isset($_GET['max_height'])) {
        return false;
    }
    $max_heights = sanitize_text_field($_GET['max_height']);
    $height_array = array_filter(array_map('absint', explode(',', $max_heights)));
    $height_array = array_filter($height_array, function($h) { return $h > 0 && $h <= 9999; });
    $height_array = array_slice($height_array, 0, 4);
    if (!empty($height_array)) {
        update_option('webp_max_heights', implode(',', $height_array));
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Max heights set to: %spx', 'wpturbo'), implode(', ', $height_array));
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Set resize mode
function wpturbo_set_resize_mode() {
    if (!isset($_GET['set_resize_mode']) || !current_user_can('manage_options') || !isset($_GET['resize_mode'])) {
        return false;
    }
    $mode = sanitize_text_field($_GET['resize_mode']);
    if (in_array($mode, ['width', 'height'])) {
        $current_mode = get_option('webp_resize_mode', 'width');
        if ($current_mode !== $mode) {
            update_option('webp_resize_mode', $mode);
            $log = get_option('webp_conversion_log', []);
            $log[] = sprintf(__('Resize mode set to: %s', 'wpturbo'), $mode);
            update_option('webp_conversion_log', array_slice((array)$log, -500));
        }
        return true;
    }
    return false;
}

// Set quality
function wpturbo_set_quality() {
    if (!isset($_GET['set_quality']) || !current_user_can('manage_options') || !isset($_GET['quality'])) {
        return false;
    }
    $quality = absint($_GET['quality']);
    if ($quality >= 0 && $quality <= 100) {
        $current_quality = (int) get_option('webp_quality', 80);
        if ($current_quality !== $quality) {
            update_option('webp_quality', $quality);
            $log = get_option('webp_conversion_log', []);
            $log[] = sprintf(__('Quality set to: %d', 'wpturbo'), $quality);
            update_option('webp_conversion_log', array_slice((array)$log, -500));
        }
        return true;
    }
    return false;
}

// Set batch size
function wpturbo_set_batch_size() {
    if (!isset($_GET['set_batch_size']) || !current_user_can('manage_options') || !isset($_GET['batch_size'])) {
        return false;
    }
    $batch_size = absint($_GET['batch_size']);
    if ($batch_size > 0 && $batch_size <= 50) {
        update_option('webp_batch_size', $batch_size);
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Batch size set to: %d', 'wpturbo'), $batch_size);
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Set preserve originals
function wpturbo_set_preserve_originals() {
    if (!isset($_GET['set_preserve_originals']) || !current_user_can('manage_options') || !isset($_GET['preserve_originals'])) {
        return false;
    }
    $preserve = rest_sanitize_boolean($_GET['preserve_originals']);
    $current_preserve = wpturbo_get_preserve_originals();
    if ($current_preserve !== $preserve) {
        update_option('webp_preserve_originals', $preserve);
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Preserve originals set to: %s', 'wpturbo'), $preserve ? __('Yes', 'wpturbo') : __('No', 'wpturbo'));
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Set disable auto-conversion on upload
function wpturbo_set_disable_auto_conversion() {
    if (!isset($_GET['set_disable_auto_conversion']) || !current_user_can('manage_options') || !isset($_GET['disable_auto_conversion'])) {
        return false;
    }
    $disable = rest_sanitize_boolean($_GET['disable_auto_conversion']);
    $current_disable = wpturbo_get_disable_auto_conversion();
    if ($current_disable !== $disable) {
        update_option('webp_disable_auto_conversion', $disable);
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Auto-conversion on upload set to: %s', 'wpturbo'), $disable ? __('Disabled', 'wpturbo') : __('Enabled', 'wpturbo'));
       update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Set minimum size threshold
function wpturbo_set_min_size_kb() {
    if (!isset($_GET['set_min_size_kb']) || !current_user_can('manage_options') || !isset($_GET['min_size_kb'])) {
        return false;
    }
    $min_size = absint($_GET['min_size_kb']);
    if ($min_size >= 0) {
        $current_min_size = wpturbo_get_min_size_kb();
        if ($current_min_size !== $min_size) {
            update_option('webp_min_size_kb', $min_size);
            $log = get_option('webp_conversion_log', []);
            $log[] = sprintf(__('Minimum size threshold set to: %d KB', 'wpturbo'), $min_size);
            update_option('webp_conversion_log', array_slice((array)$log, -500));
        }
        return true;
    }
    return false;
}

// Set use AVIF option and ensure MIME types
function wpturbo_set_use_avif() {
    if (!isset($_GET['set_use_avif']) || !current_user_can('manage_options') || !isset($_GET['use_avif'])) {
        return false;
    }
    $use_avif = rest_sanitize_boolean($_GET['use_avif']);
    $current_use_avif = wpturbo_get_use_avif();
    if ($current_use_avif !== $use_avif) {
        update_option('webp_use_avif', $use_avif);
        wpturbo_ensure_mime_types(); // Ensure MIME types are updated
        $log = get_option('webp_conversion_log', []);
        $log[] = sprintf(__('Conversion format set to: %s', 'wpturbo'), $use_avif ? 'AVIF' : 'WebP');
        $log[] = __('Please reconvert all images to ensure consistency after changing formats.', 'wpturbo');
        update_option('webp_conversion_log', array_slice((array)$log, -500));
        return true;
    }
    return false;
}

// Enhanced cleanup of leftover originals and alternate formats
function wpturbo_cleanup_leftover_originals() {
    if (!isset($_GET['cleanup_leftover_originals']) || !current_user_can('manage_options')) {
        return false;
    }

    $log = get_option('webp_conversion_log', []);
    $uploads_dir = wp_upload_dir()['basedir'];
    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($uploads_dir));
    $deleted = 0;
    $failed = 0;
    $preserve_originals = wpturbo_get_preserve_originals();
    $use_avif = wpturbo_get_use_avif();
    $current_extension = $use_avif ? 'avif' : 'webp';
    $alternate_extension = $use_avif ? 'webp' : 'avif';

    $attachments = get_posts([
        'post_type' => 'attachment',
        'posts_per_page' => -1,
        'fields' => 'ids',
        'post_mime_type' => ['image/jpeg', 'image/png', 'image/webp', 'image/avif'],
    ]);
    $active_files = [];
    $mode = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
    $excluded_images = wpturbo_get_excluded_images();

    foreach ($attachments as $attachment_id) {
        $file = get_attached_file($attachment_id);
        $metadata = wp_get_attachment_metadata($attachment_id);
        $dirname = dirname($file);
        $base_name = pathinfo($file, PATHINFO_FILENAME);

        if (in_array($attachment_id, $excluded_images)) {
            if ($file && file_exists($file)) $active_files[$file] = true;
            $possible_extensions = ['jpg', 'jpeg', 'png', 'webp', 'avif'];
            foreach ($possible_extensions as $ext) {
                $potential_file = "$dirname/$base_name.$ext";
                if (file_exists($potential_file)) $active_files[$potential_file] = true;
            }
            foreach ($max_values as $index => $dimension) {
                $suffix = ($index === 0) ? '' : "-{$dimension}";
                foreach (['webp', 'avif'] as $ext) {
                    $file_path = "$dirname/$base_name$suffix.$ext";
                    if (file_exists($file_path)) $active_files[$file_path] = true;
                }
            }
            $thumbnail_files = ["$dirname/$base_name-150x150.webp", "$dirname/$base_name-150x150.avif"];
            foreach ($thumbnail_files as $thumbnail_file) {
                if (file_exists($thumbnail_file)) $active_files[$thumbnail_file] = true;
            }
            if ($metadata && isset($metadata['sizes'])) {
                foreach ($metadata['sizes'] as $size_data) {
                    $size_file = "$dirname/" . $size_data['file'];
                    if (file_exists($size_file)) $active_files[$size_file] = true;
                }
            }
            continue;
        }

        if ($file && file_exists($file)) {
            $active_files[$file] = true;
            foreach ($max_values as $index => $dimension) {
                $suffix = ($index === 0) ? '' : "-{$dimension}";
                $current_file = "$dirname/$base_name$suffix.$current_extension";
                if (file_exists($current_file)) $active_files[$current_file] = true;
            }
            $thumbnail_file = "$dirname/$base_name-150x150.$current_extension";
            if (file_exists($thumbnail_file)) $active_files[$thumbnail_file] = true;
        }
    }

    if (!$preserve_originals) {
        foreach ($files as $file) {
            if ($file->isDir()) continue;

            $file_path = $file->getPathname();
            $extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
            if (!in_array($extension, ['webp', 'avif', 'jpg', 'jpeg', 'png'])) continue;

            $relative_path = str_replace($uploads_dir . '/', '', $file_path);
            $path_parts = explode('/', $relative_path);
            $is_valid_path = (count($path_parts) === 1) || (count($path_parts) === 3 && is_numeric($path_parts[0]) && is_numeric($path_parts[1]));

            if (!$is_valid_path || isset($active_files[$file_path])) continue;

            if (in_array($extension, ['jpg', 'jpeg', 'png']) || $extension === $alternate_extension) {
                $attempts = 0;
                while ($attempts < 5 && file_exists($file_path)) {
                    if (!is_writable($file_path)) {
                        @chmod($file_path, 0644);
                        if (!is_writable($file_path)) {
                            $log[] = sprintf(__('Error: Cannot make %s writable - skipping deletion', 'wpturbo'), basename($file_path));
                            $failed++;
                            break;
                        }
                    }
                    if (@unlink($file_path)) {
                        $log[] = sprintf(__('Cleanup: Deleted %s', 'wpturbo'), basename($file_path));
                        $deleted++;
                        break;
                    }
                    $attempts++;
                    sleep(1);
                }
                if (file_exists($file_path)) {
                    $log[] = sprintf(__('Cleanup: Failed to delete %s', 'wpturbo'), basename($file_path));
                    $failed++;
                }
            }
        }
    }

    $log[] = "<span style='font-weight: bold; color: #281E5D;'>" . __('Cleanup Complete', 'wpturbo') . "</span>: " . sprintf(__('Deleted %d files, %d failed', 'wpturbo'), $deleted, $failed);
    update_option('webp_conversion_log', array_slice((array)$log, -500));

    foreach ($attachments as $attachment_id) {
        if (in_array($attachment_id, $excluded_images)) continue;

        $file_path = get_attached_file($attachment_id);
        if (file_exists($file_path) && strtolower(pathinfo($file_path, PATHINFO_EXTENSION)) === $current_extension) {
            $metadata = wp_get_attachment_metadata($attachment_id);
            $thumbnail_file = $uploads_dir . '/' . dirname($metadata['file']) . '/' . pathinfo($file_path, PATHINFO_FILENAME) . '-150x150.' . $current_extension;
            if (!file_exists($thumbnail_file)) {
                $metadata = wp_generate_attachment_metadata($attachment_id, $file_path);
                if (!is_wp_error($metadata)) {
                    wp_update_attachment_metadata($attachment_id, $metadata);
                    $log[] = sprintf(__('Regenerated thumbnail for %s', 'wpturbo'), basename($file_path));
                }
            }
        }
    }

    $log[] = "<span style='font-weight: bold; color: #281E5D;'>" . __('Thumbnail Regeneration Complete', 'wpturbo') . "</span>";
    update_option('webp_conversion_log', array_slice((array)$log, -500));
    return true;
}

// AJAX handlers for exclusion
add_action('wp_ajax_webp_add_excluded_image', 'wpturbo_add_excluded_image_ajax');
function wpturbo_add_excluded_image_ajax() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options') || !isset($_POST['attachment_id'])) {
        wp_send_json_error(__('Permission denied or invalid attachment ID', 'wpturbo'));
    }
    $attachment_id = absint($_POST['attachment_id']);
    if (wpturbo_add_excluded_image($attachment_id)) {
        wp_send_json_success(['message' => __('Image excluded successfully', 'wpturbo')]);
    } else {
        wp_send_json_error(__('Image already excluded or invalid ID', 'wpturbo'));
    }
}

add_action('wp_ajax_webp_remove_excluded_image', 'wpturbo_remove_excluded_image_ajax');
function wpturbo_remove_excluded_image_ajax() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options') || !isset($_POST['attachment_id'])) {
        wp_send_json_error(__('Permission denied or invalid attachment ID', 'wpturbo'));
    }
    $attachment_id = absint($_POST['attachment_id']);
    if (wpturbo_remove_excluded_image($attachment_id)) {
        wp_send_json_success(['message' => __('Image removed from exclusion list', 'wpturbo')]);
    } else {
        wp_send_json_error(__('Image not in exclusion list', 'wpturbo'));
    }
}

// Convert post content image URLs to current format
add_action('wp_ajax_convert_post_images_to_webp', 'wpturbo_convert_post_images_to_format');
function wpturbo_convert_post_images_to_format() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Permission denied', 'wpturbo'));
    }

    $log = get_option('webp_conversion_log', []);
    function add_log_entry($message) {
        global $log;
        $log[] = "[" . date("Y-m-d H:i:s") . "] " . $message;
        update_option('webp_conversion_log', array_slice((array)$log, -500));
    }

    $use_avif = wpturbo_get_use_avif();
    $extension = $use_avif ? 'avif' : 'webp';
    add_log_entry(sprintf(__('Starting post/page/FSE-template image conversion to %s...', 'wpturbo'), $use_avif ? 'AVIF' : 'WebP'));

    $public_post_types = get_post_types(['public' => true], 'names');
    $fse_post_types = ['wp_template', 'wp_template_part', 'wp_block'];
    $post_types = array_unique(array_merge($public_post_types, $fse_post_types));

    $args = [
        'post_type' => $post_types,
        'posts_per_page' => -1,
        'fields' => 'ids'
    ];
    $posts = get_posts($args);

    if (!$posts) {
        add_log_entry(__('No posts/pages/FSE-templates found', 'wpturbo'));
        wp_send_json_success(['message' => __('No posts/pages/FSE-templates found', 'wpturbo')]);
    }

    $upload_dir = wp_upload_dir();
    $upload_baseurl = $upload_dir['baseurl'];
    $upload_basedir = $upload_dir['basedir'];
    $updated_count = 0;
    $checked_images = 0;

    foreach ($posts as $post_id) {
        $content = get_post_field('post_content', $post_id);
        $original_content = $content;

        $content = preg_replace_callback(
            '/<img[^>]+src=["\']([^"\']+\.(?:jpg|jpeg|png))["\'][^>]*>/i',
            function ($matches) use (&$checked_images, $upload_baseurl, $upload_basedir, $extension) {
                $original_url = $matches[1];
                if (strpos($original_url, $upload_baseurl) !== 0) {
                    add_log_entry("Skipping <img> (external): {$original_url}");
                    return $matches[0];
                }
                $checked_images++;

                $dirname = pathinfo($original_url, PATHINFO_DIRNAME);
                $filename = pathinfo($original_url, PATHINFO_FILENAME);

                $new_url = $dirname . '/' . $filename . '.' . $extension;
                $scaled_url = $dirname . '/' . $filename . '-scaled.' . $extension;
                $new_path = str_replace($upload_baseurl, $upload_basedir, $new_url);
                $scaled_path = str_replace($upload_baseurl, $upload_basedir, $scaled_url);

                if (file_exists($scaled_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $scaled_url));
                    return str_replace($original_url, $scaled_url, $matches[0]);
                }
                if (file_exists($new_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $new_url));
                    return str_replace($original_url, $new_url, $matches[0]);
                }

                $base_name = preg_replace('/(-\d+x\d+|-scaled)$/', '', $filename);
                $fallback_url = $dirname . '/' . $base_name . '.' . $extension;
                $fallback_scaled_url = $dirname . '/' . $base_name . '-scaled.' . $extension;
                $fallback_path = str_replace($upload_baseurl, $upload_basedir, $fallback_url);
                $fallback_scaled_path = str_replace($upload_baseurl, $upload_basedir, $fallback_scaled_url);

                if (file_exists($fallback_scaled_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $fallback_scaled_url));
                    return str_replace($original_url, $fallback_scaled_url, $matches[0]);
                }
                if (file_exists($fallback_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $fallback_url));
                    return str_replace($original_url, $fallback_url, $matches[0]);
                }

                return $matches[0];
            },
            $content
        );

        $content = preg_replace_callback(
            '/background-image\s*:\s*url\(\s*[\'"]?([^\'"]+\.(?:jpg|jpeg|png))[\'"]?\s*\)/i',
            function ($matches) use (&$checked_images, $upload_baseurl, $upload_basedir, $extension) {
                $original_url = $matches[1];
                if (strpos($original_url, $upload_baseurl) !== 0) {
                    add_log_entry("Skipping BG (external): {$original_url}");
                    return $matches[0];
                }
                $checked_images++;

                $dirname = pathinfo($original_url, PATHINFO_DIRNAME);
                $filename = pathinfo($original_url, PATHINFO_FILENAME);

                $new_url = $dirname . '/' . $filename . '.' . $extension;
                $scaled_url = $dirname . '/' . $filename . '-scaled.' . $extension;
                $new_path = str_replace($upload_baseurl, $upload_basedir, $new_url);
                $scaled_path = str_replace($upload_baseurl, $upload_basedir, $scaled_url);

                if (file_exists($scaled_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $scaled_url));
                    return str_replace($original_url, $scaled_url, $matches[0]);
                }
                if (file_exists($new_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $new_url));
                    return str_replace($original_url, $new_url, $matches[0]);
                }

                $base_name = preg_replace('/(-\d+x\d+|-scaled)$/', '', $filename);
                $fallback_url = $dirname . '/' . $base_name . '.' . $extension;
                $fallback_scaled_url = $dirname . '/' . $base_name . '-scaled.' . $extension;
                $fallback_path = str_replace($upload_baseurl, $upload_basedir, $fallback_url);
                $fallback_scaled_path = str_replace($upload_baseurl, $upload_basedir, $fallback_scaled_url);

                if (file_exists($fallback_scaled_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $fallback_scaled_url));
                    return str_replace($original_url, $fallback_scaled_url, $matches[0]);
                }
                if (file_exists($fallback_path)) {
                    add_log_entry(sprintf(__('Replacing: %s → %s', 'wpturbo'), $original_url, $fallback_url));
                    return str_replace($original_url, $fallback_url, $matches[0]);
                }

                return $matches[0];
            },
            $content
        );

        if ($content !== $original_content) {
            wp_update_post(['ID' => $post_id, 'post_content' => $content]);
            $updated_count++;
        }

        $thumbnail_id = get_post_thumbnail_id($post_id);
        if ($thumbnail_id && !in_array($thumbnail_id, wpturbo_get_excluded_images())) {
            $thumbnail_path = get_attached_file($thumbnail_id);
            if ($thumbnail_path && !str_ends_with($thumbnail_path, '.' . $extension)) {
                $new_path = preg_replace('/\.(jpg|jpeg|png)$/i', '.' . $extension, $thumbnail_path);
                if (file_exists($new_path)) {
                    update_attached_file($thumbnail_id, $new_path);
                    wp_update_post(['ID' => $thumbnail_id, 'post_mime_type' => $use_avif ? 'image/avif' : 'image/webp']);
                    $metadata = wp_generate_attachment_metadata($thumbnail_id, $new_path);
                    wp_update_attachment_metadata($thumbnail_id, $metadata);
                    add_log_entry(sprintf(__('Updated thumbnail: %s → %s', 'wpturbo'), basename($thumbnail_path), basename($new_path)));
                }
            }
        }
    }

    add_log_entry(sprintf(__('Checked %d images (including BG), updated %d items', 'wpturbo'), $checked_images, $updated_count));
    wp_send_json_success(['message' => sprintf(__('Checked %d images (including BG), updated %d items', 'wpturbo'), $checked_images, $updated_count)]);
}

// Export all media as a ZIP file
add_action('wp_ajax_webp_export_media_zip', 'wpturbo_export_media_zip');
function wpturbo_export_media_zip() {
    check_ajax_referer('webp_converter_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Permission denied', 'wpturbo'));
    }

    wp_raise_memory_limit('admin');
    set_time_limit(0);

    $args = [
        'post_type' => 'attachment',
        'post_mime_type' => 'image',
        'posts_per_page' => -1,
        'fields' => 'ids',
    ];
    $attachments = get_posts($args);

    if (empty($attachments)) {
        wp_send_json_error(__('No media files found', 'wpturbo'));
    }

    $temp_file = tempnam(sys_get_temp_dir(), 'webp_media_export_');
    if (!$temp_file) {
        wp_send_json_error(__('Failed to create temporary file', 'wpturbo'));
    }

    $zip = new ZipArchive();
    if ($zip->open($temp_file, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
        @unlink($temp_file);
        wp_send_json_error(__('Failed to create ZIP archive', 'wpturbo'));
    }

    $upload_dir = wp_upload_dir()['basedir'];
    $log = get_option('webp_conversion_log', []);
    $possible_extensions = ['jpg', 'jpeg', 'png', 'webp', 'avif'];

    foreach ($attachments as $attachment_id) {
        $file_path = get_attached_file($attachment_id);
        if (!$file_path || !file_exists($file_path)) {
            $log[] = sprintf(__('Skipped: Main file not found for Attachment ID %d', 'wpturbo'), $attachment_id);
            continue;
        }

        $dirname = dirname($file_path);
        $base_name = pathinfo($file_path, PATHINFO_FILENAME);
        $relative_dir = str_replace($upload_dir . '/', '', $dirname);

        // Add main file
        $relative_path = $relative_dir . '/' . basename($file_path);
        $zip->addFile($file_path, $relative_path);
        $log[] = sprintf(__('Added to ZIP: %s (Attachment ID %d)', 'wpturbo'), basename($file_path), $attachment_id);

        // Add metadata sizes
        $metadata = wp_get_attachment_metadata($attachment_id);
        if ($metadata && isset($metadata['sizes'])) {
            foreach ($metadata['sizes'] as $size => $size_data) {
                $size_file = $dirname . '/' . $size_data['file'];
                if (file_exists($size_file)) {
                    $relative_size_path = $relative_dir . '/' . $size_data['file'];
                    $zip->addFile($size_file, $relative_size_path);
                    $log[] = sprintf(__('Added to ZIP: %s (size: %s, Attachment ID %d)', 'wpturbo'), $size_data['file'], $size, $attachment_id);
                }
            }
        }

        // Add all related files with the same base name
        foreach ($possible_extensions as $ext) {
            // Check for base file with different extensions (e.g., image.jpg, image.png)
            $related_file = "$dirname/$base_name.$ext";
            if (file_exists($related_file) && $related_file !== $file_path) {
                $relative_related_path = $relative_dir . '/' . "$base_name.$ext";
                $zip->addFile($related_file, $relative_related_path);
                $log[] = sprintf(__('Added to ZIP: Related file %s (Attachment ID %d)', 'wpturbo'), "$base_name.$ext", $attachment_id);
            }

            // Check for sized versions (e.g., image-500.jpg, image-150x150.png)
            $pattern = "$dirname/$base_name-*.$ext";
            $related_files = glob($pattern);
            foreach ($related_files as $related_file) {
                // Skip if already added as main file or metadata size
                if ($related_file === $file_path || in_array(basename($related_file), array_column($metadata['sizes'] ?? [], 'file'))) {
                    continue;
                }
                $relative_related_path = $relative_dir . '/' . basename($related_file);
                $zip->addFile($related_file, $relative_related_path);
                $log[] = sprintf(__('Added to ZIP: Related size %s (Attachment ID %d)', 'wpturbo'), basename($related_file), $attachment_id);
            }
        }
    }

    $zip->close();
    update_option('webp_conversion_log', array_slice((array)$log, -500));

    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="media_export_' . date('Y-m-d_H-i-s') . '.zip"');
    header('Content-Length: ' . filesize($temp_file));

    readfile($temp_file);
    flush();

    @unlink($temp_file);
    exit;
}

// Custom srcset to include all sizes in current format
add_filter('wp_calculate_image_srcset', 'wpturbo_custom_srcset', 10, 5);
function wpturbo_custom_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
    if (in_array($attachment_id, wpturbo_get_excluded_images())) {
        return $sources;
    }

    $use_avif = wpturbo_get_use_avif();
    $extension = $use_avif ? '.avif' : '.webp';

    $mode = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
    $upload_dir = wp_upload_dir();
    $base_path = $upload_dir['basedir'] . '/' . dirname($image_meta['file']);
    $base_name = pathinfo($image_meta['file'], PATHINFO_FILENAME);
    $base_url = $upload_dir['baseurl'] . '/' . dirname($image_meta['file']);

    foreach ($max_values as $index => $dimension) {
        if ($index === 0) continue;
        $file = "$base_path/$base_name-$dimension$extension";
        if (file_exists($file)) {
            $size_key = "custom-$dimension";
            $width = ($mode === 'width') ? $dimension : (isset($image_meta['sizes'][$size_key]['width']) ? $image_meta['sizes'][$size_key]['width'] : 0);
            $sources[$width] = [
                'url' => "$base_url/$base_name-$dimension$extension",
                'descriptor' => 'w',
                'value' => $width
            ];
        }
    }

    $thumbnail_file = "$base_path/$base_name-150x150$extension";
    if (file_exists($thumbnail_file)) {
        $sources[150] = [
            'url' => "$base_url/$base_name-150x150$extension",
            'descriptor' => 'w',
            'value' => 150
        ];
    }

    return $sources;
}


function wpturbo_convert_single_image_stamp_check($attachment_id) {
    // Your existing settings - you might already have these defined elsewhere
    $use_avif = false; // or however you detect AVIF
    $current_quality = 80; // your WebP/AVIF quality setting
    $mode = 'width'; // resize mode: width, height, or auto
    $max_values = [1920, 1200, 600, 300]; // your target sizes

    // 🔥 1. Load the current metadata
    $metadata = wp_get_attachment_metadata($attachment_id);
    if (!$metadata) {
        return; // Can't process without metadata
    }

    // 🔥 2. Build the expected stamp
    $expected_stamp = [
        'format' => $use_avif ? 'avif' : 'webp',
        'quality' => $current_quality,
        'resize_mode' => $mode,
        'max_values' => $max_values,
    ];

    // 🔥 3. Check existing stamp
    $stamp = isset($metadata['pixrefiner_stamp']) ? $metadata['pixrefiner_stamp'] : null;

    // 🔥 4. Compare — if matches and no force, skip
    if ($stamp === $expected_stamp && empty($_GET['force_reconvert'])) {
        return; // Already converted correctly, no need to reprocess
    }

    // 🔥 5. If no match — continue normal conversion
    // 👉 Now here you do your actual conversion logic like:
    //     - Resize images
    //     - Generate WebP / AVIF
    //     - Compress
    // etc.

    // (This part is your existing code that does the heavy work.)

    // 🔥 6. After conversion, update the stamp
    $metadata['pixrefiner_stamp'] = $expected_stamp;
    wp_update_attachment_metadata($attachment_id, $metadata);
}


// Admin interface
add_action('admin_menu', function() {
    add_media_page(
        __('PixRefiner', 'wpturbo'),
        __('PixRefiner', 'wpturbo'),
        'manage_options',
        'webp-converter',
        'wpturbo_webp_converter_page'
    );
});

function wpturbo_webp_converter_page() {
    wp_enqueue_media();
    wp_enqueue_script('media-upload');
    wp_enqueue_style('media');

    if (isset($_GET['set_max_width'])) wpturbo_set_max_widths();
    if (isset($_GET['set_max_height'])) wpturbo_set_max_heights();
    if (isset($_GET['set_resize_mode'])) wpturbo_set_resize_mode();
    if (isset($_GET['set_quality'])) wpturbo_set_quality();
    if (isset($_GET['set_batch_size'])) wpturbo_set_batch_size();
    if (isset($_GET['set_preserve_originals'])) wpturbo_set_preserve_originals();
    if (isset($_GET['set_disable_auto_conversion'])) wpturbo_set_disable_auto_conversion();
    if (isset($_GET['set_min_size_kb'])) wpturbo_set_min_size_kb();
    if (isset($_GET['set_use_avif'])) wpturbo_set_use_avif();
    if (isset($_GET['cleanup_leftover_originals'])) wpturbo_cleanup_leftover_originals();
    if (isset($_GET['clear_log'])) wpturbo_clear_log();
    if (isset($_GET['reset_defaults'])) wpturbo_reset_defaults();

    $has_image_library = extension_loaded('imagick') || extension_loaded('gd');
    $has_avif_support = (extension_loaded('imagick') && in_array('AVIF', Imagick::queryFormats())) || (extension_loaded('gd') && function_exists('imageavif'));
    $htaccess_file = ABSPATH . '.htaccess';
    // Original check kept as comment for reference:
    // $mime_configured = file_exists($htaccess_file) && strpos(file_get_contents($htaccess_file), 'AddType image/webp .webp') !== false;
    $mime_configured = true; // Force to true to suppress the MIME type warning
    ?>
    <div class="wrap" style="padding: 0; font-size: 14px;">
        <div style="display: flex; gap: 10px; align-items: flex-start;">
            <!-- Column 1: Controls, Excluded Images, How It Works -->
            <div style="width: 38%; display: flex; flex-direction: column; gap: 10px;">
                <!-- Pane 1: Controls -->
                <div style="background: #FFFFFF; padding: 20px; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
                    <h1 style="font-size: 20px; font-weight: bold; color: #333; margin: -5px 0 15px 0;"><?php _e('PixRefiner - Image Optimization - v3.4', 'wpturbo'); ?></h1>

                    <?php if (!$has_image_library): ?>
                        <div class="notice notice-error" style="margin-bottom: 20px;">
                            <p><?php _e('Warning: No image processing libraries (Imagick or GD) available. Conversion requires one of these.', 'wpturbo'); ?></p>
                        </div>
                    <?php endif; ?>
                    <?php if (wpturbo_get_use_avif() && !$has_avif_support): ?>
                        <div class="notice notice-warning" style="margin-bottom: 20px;">
                            <p><?php _e('Warning: AVIF support is not detected on this server. Enable Imagick with AVIF or GD with AVIF support to use this option.', 'wpturbo'); ?></p>
                        </div>
                    <?php endif; ?>
                    <?php if (!$mime_configured): ?>
                        <div class="notice notice-warning" style="margin-bottom: 20px;">
                            <p><?php _e('Warning: WebP/AVIF MIME types may not be configured on your server. Images might not display correctly. Check your server settings (e.g., .htaccess for Apache or MIME types for Nginx).', 'wpturbo'); ?></p>
                        </div>
                    <?php endif; ?>

                    <?php if (current_user_can('manage_options')): ?>
                        <div style="margin-bottom: 20px;">
                            <label for="resize-mode" style="font-weight: bold;"><?php _e('Resize Mode:', 'wpturbo'); ?></label><br>
                            <select id="resize-mode" style="width: 100px; margin-right: 10px; padding: 0px 0px 0px 5px;">
                                <option value="width" <?php echo wpturbo_get_resize_mode() === 'width' ? 'selected' : ''; ?>><?php _e('Width', 'wpturbo'); ?></option>
                                <option value="height" <?php echo wpturbo_get_resize_mode() === 'height' ? 'selected' : ''; ?>><?php _e('Height', 'wpturbo'); ?></option>
                            </select>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label for="max-width-input" style="font-weight: bold;"><?php _e('Max Widths (up to 4, e.g., 1920, 1200, 600, 300) - 150 is set automatically:', 'wpturbo'); ?></label><br>
                            <input type="text" id="max-width-input" value="<?php echo esc_attr(implode(', ', wpturbo_get_max_widths())); ?>" style="width: 200px; margin-right: 10px; padding: 5px;" placeholder="1920,1200,600,300">
                            <button id="set-max-width" class="button"><?php _e('Set Widths', 'wpturbo'); ?></button>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label for="max-height-input" style="font-weight: bold;"><?php _e('Max Heights (up to 4, e.g., 1080, 720, 480, 360) - 150 is set automatically:', 'wpturbo'); ?></label><br>
                            <input type="text" id="max-height-input" value="<?php echo esc_attr(implode(', ', wpturbo_get_max_heights())); ?>" style="width: 200px; margin-right: 10px; padding: 5px;" placeholder="1080,720,480,360">
                            <button id="set-max-height" class="button"><?php _e('Set Heights', 'wpturbo'); ?></button>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label for="min-size-kb" style="font-weight: bold;"><?php _e('Min Size for Conversion (KB, Set to 0 to disable):', 'wpturbo'); ?></label><br>
                            <input type="number" id="min-size-kb" value="<?php echo esc_attr(wpturbo_get_min_size_kb()); ?>" min="0" style="width: 50px; margin-right: 10px; padding: 5px;" placeholder="0">
                            <button id="set-min-size-kb" class="button"><?php _e('Set Min Size', 'wpturbo'); ?></button>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label><input type="checkbox" id="use-avif" <?php echo wpturbo_get_use_avif() ? 'checked' : ''; ?>> <?php _e('Set to AVIF Conversion (not WebP)', 'wpturbo'); ?></label>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label><input type="checkbox" id="preserve-originals" <?php echo wpturbo_get_preserve_originals() ? 'checked' : ''; ?>> <?php _e('Preserve Original Files', 'wpturbo'); ?></label>
                        </div>
                        <div style="margin-bottom: 20px;">
                            <label><input type="checkbox" id="disable-auto-conversion" <?php echo wpturbo_get_disable_auto_conversion() ? 'checked' : ''; ?>> <?php _e('Disable Auto-Conversion on Upload', 'wpturbo'); ?></label>
                        </div>
                        <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                            <button id="start-conversion" class="button"><?php _e('1. Convert/Scale', 'wpturbo'); ?></button>
                            <button id="cleanup-originals" class="button"><?php _e('2. Cleanup Images', 'wpturbo'); ?></button>
                            <button id="convert-post-images" class="button"><?php _e('3. Fix URLs', 'wpturbo'); ?></button>
                            <button id="run-all" class="button button-primary"><?php _e('Run All (1-3)', 'wpturbo'); ?></button>
                            <button id="stop-conversion" class="button" style="display: none;"><?php _e('Stop', 'wpturbo'); ?></button>
                        </div>
                        <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                            <button id="clear-log" class="button"><?php _e('Clear Log', 'wpturbo'); ?></button>
                            <button id="reset-defaults" class="button"><?php _e('Reset Defaults', 'wpturbo'); ?></button>
                            <button id="export-media-zip" class="button"><?php _e('Export Media as ZIP', 'wpturbo'); ?></button>
                        </div>
                    <?php else: ?>
                        <p><?php _e('You need manage_options permission to use this tool.', 'wpturbo'); ?></p>
                    <?php endif; ?>
                </div>

				                <!-- Pane 2: Exclude Images -->
                <div style="background: #FFFFFF; padding: 20px; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
                    <h2 style="font-size: 16px; margin: 0 0 15px 0;"><?php _e('Exclude Images', 'wpturbo'); ?></h2>
                    <button id="open-media-library" class="button" style="margin-bottom: 20px;"><?php _e('Add from Media Library', 'wpturbo'); ?></button>
                    <div id="excluded-images">
                        <h3 style="font-size: 14px; margin: 0 0 10px 0;"><?php _e('Excluded Images', 'wpturbo'); ?></h3>
                        <ul id="excluded-images-list" style="list-style: none; padding: 0; max-height: 300px; overflow-y: auto;"></ul>
                    </div>
                </div>

                <!-- Pane 3: How It Works -->
                <div style="background: #FFFFFF; padding: 20px; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
                    <h2 style="font-size: 16px; margin: 0 0 15px 0;"><?php _e('How It Works', 'wpturbo'); ?></h2>
                    <p style="line-height: 1.5;">
                        <?php _e('Refine images to WebP or AVIF, and remove excess files to save space.', 'wpturbo'); ?><br><br>
                        <b><?php _e('Set Auto-Conversion for New Uploads:', 'wpturbo'); ?></b><br>
                        <b>1. Resize Mode:</b> <?php _e('Pick if images shrink by width or height.', 'wpturbo'); ?><br>
                        <b>2. Set Max Sizes:</b> <?php _e('Choose up to 4 sizes (150x150 thumbnail is automatic).', 'wpturbo'); ?><br>
                        <b>3. Min Size for Conversion:</b> <?php _e('Sizes below the min are not affected. Default is 0.', 'wpturbo'); ?><br>
                        <b>4. Conversion Format:</b> <?php _e('Check to use AVIF. WebP is default.', 'wpturbo'); ?><br>
                        <b>5. Preserve Originals:</b> <?php _e('Check to stop original files from converting/deleting.', 'wpturbo'); ?><br>
                        <b>6. Disable Auto-Conversion:</b> <?php _e('Images will convert on upload unless this is ticked.', 'wpturbo'); ?><br>
                        <b>7. Upload:</b> <?php _e('Upload to Media Library or via elements/widgets.', 'wpturbo'); ?><br><br>
                        <b><?php _e('Apply for Existing Images:', 'wpturbo'); ?></b><br>
                        <b>1. Repeat:</b> <?php _e('Set up steps 1-6 above.', 'wpturbo'); ?><br>
                        <b>2. Run All:</b> <?php _e('Hit "Run All" to do everything at once.', 'wpturbo'); ?><br><br>
                        <b><?php _e('Apply Manually for Existing Images:', 'wpturbo'); ?></b><br>
                        <b>1. Repeat:</b> <?php _e('Set up steps 1-6 above.', 'wpturbo'); ?><br>
                        <b>2. Convert:</b> <?php _e('Change image sizes and format.', 'wpturbo'); ?><br>
                        <b>3. Cleanup:</b> <?php _e('Delete old formats/sizes (if not preserved).', 'wpturbo'); ?><br>
                        <b>4. Fix Links:</b> <?php _e('Update image links to the new format.', 'wpturbo'); ?><br><br>
                        <b><?php _e('IMPORTANT:', 'wpturbo'); ?></b><br>
                        <b>a) Usability:</b> <?php _e('This tool is ideal for New Sites. Using with Legacy Sites must be done with care as variation due to methods, systems, sizes, can affect the outcome. Please use this tool carefully and at your own risk, as I cannot be held responsible for any issues that may arise from its use.', 'wpturbo'); ?><br>
                        <b>b) Backups:</b> <?php _e('Use a strong backup tool like All-in-One WP Migration before using this tool. Check if your host saves backups - as some charge a fee to restore.', 'wpturbo'); ?><br>
                        <b>c) Export Media:</b> <?php _e('Export images as a Zipped Folder prior to running.', 'wpturbo'); ?><br>
                        <b>d) Reset Defaults:</b> <?php _e('Resets all Settings 1-6.', 'wpturbo'); ?><br>
                        <b>e) Speed:</b> <?php _e('Bigger sites take longer to run. This depends on your server.', 'wpturbo'); ?><br>
                        <b>f) Log Wait:</b> <?php _e('Updates show every 50 images.', 'wpturbo'); ?><br>
                        <b>g) Stop Anytime:</b> <?php _e('Click "Stop" to pause.', 'wpturbo'); ?><br>
                        <b>h) AVIF Needs:</b> <?php _e('Your server must support AVIF. Check logs if it fails.', 'wpturbo'); ?><br>
                        <b>i) Old Browsers:</b> <?php _e('AVIF might not work on older browsers, WebP is safer.', 'wpturbo'); ?><br>
                        <b>j) MIME Types:</b> <?php _e('Server must support WebP/AVIF MIME (check with host).', 'wpturbo'); ?><br>
                        <b>k) Rollback:</b> <?php _e('If conversion fails, then rollback occurs, and prevents deletion of the original, regardless of whether the Preserve Originals is checked or not.', 'wpturbo'); ?>
                    </p>
                    <!-- Donate Button -->
                    <div style="margin-top: 20px; display: flex; justify-content: flex-start;">
                        <a href="https://www.paypal.com/paypalme/iamimransiddiq" target="_blank" class="button" style="border: none;" rel="noopener"><?php _e('Support Imran', 'wpturbo'); ?></a>
                    </div>
                </div>
            </div>

            <!-- Column 2: Log -->
            <div style="width: 62%; min-height: 100vh; background: #FFFFFF; padding: 20px; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); display: flex; flex-direction: column;">
                <h3 style="font-size: 16px; margin: 0 0 10px 0;"><?php _e('Log (Last 500 Entries)', 'wpturbo'); ?></h3>
                <pre id="log" style="background: #f9f9f9; padding: 15px; flex: 1; overflow-y: auto; border: 1px solid #ddd; border-radius: 5px; font-size: 13px;"></pre>
            </div>
        </div>
    </div>

    <style>
    #quality-slider {
        -webkit-appearance: none;
        height: 6px;
        border-radius: 3px;
        background: #ddd;
    }
    #quality-slider::-webkit-slider-thumb {
        -webkit-appearance: none;
        width: 16px;
        height: 16px;
        background: var(--primary-color);
        border-radius: 50%;
        cursor: pointer;
    }
    .button.button-primary {
        background: #FF0050;
        color: #fff;
        padding: 2px 10px;
        height: 30px;
        line-height: 26px;
        transition: all 0.2s;
        font-size: 14px;
        font-weight: 600;
        border: none;
    }
    .button.button-primary:hover {
        background: #444444;
    }
    .button:not(.button-primary) {
        background: #dbe2e9;
        color: #444444;
        padding: 2px 10px;
        height: 30px;
        line-height: 26px;
        transition: all 0.2s;
        border: none;
    }
    .button:not(.button-primary):hover {
        background: #444444;
        color: #FFF;
    }
    #excluded-images-list li {
        display: flex;
        align-items: center;
        margin-bottom: 10px;
    }
    #excluded-images-list img {
        max-width: 50px;
        margin-right: 10px;
    }
    input[type="text"],
    input[type="number"],
    select {
        padding: 2px;
        height: 30px;
        box-sizing: border-box;
    }
    @media screen and (max-width: 782px) {
        div[style*="width: 55%"] {
            height: calc(100vh - 46px) !important;
        }
    }
    </style>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            let isConverting = false;

            function updateStatus() {
                fetch('<?php echo admin_url('admin-ajax.php?action=webp_status&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>')
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response.json();
                    })
                    .then(data => {
                        document.getElementById('log').innerHTML = data.log.reverse().join('<br>');
                        document.getElementById('resize-mode').value = data.resize_mode;
                        document.getElementById('max-width-input').value = data.max_widths;
                        document.getElementById('max-height-input').value = data.max_heights;
                        document.getElementById('preserve-originals').checked = data.preserve_originals;
                        document.getElementById('disable-auto-conversion').checked = data.disable_auto_conversion;
                        document.getElementById('min-size-kb').value = data.min_size_kb;
                        document.getElementById('use-avif').checked = data.use_avif;
                        updateExcludedImages(data.excluded_images);
                    })
                    .catch(error => {
                        console.error('Error in updateStatus:', error);
                        alert('Failed to update status: ' + error.message);
                    });
            }

            function updateExcludedImages(excludedImages) {
                const list = document.getElementById('excluded-images-list');
                list.innerHTML = '';
                excludedImages.forEach(image => {
                    const li = document.createElement('li');
                    li.innerHTML = `<img decoding="async" src="${image.thumbnail}" alt="${image.title}"><span>${image.title} (ID: ${image.id})</span><button class="remove-excluded button" data-id="${image.id}"><?php echo esc_html__('Remove', 'wpturbo'); ?></button>`;
                    list.appendChild(li);
                });
                document.querySelectorAll('.remove-excluded').forEach(button => {
                    button.addEventListener('click', () => {
                        fetch('<?php echo admin_url('admin-ajax.php?action=webp_remove_excluded_image&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                            body: 'attachment_id=' + encodeURIComponent(button.getAttribute('data-id'))
                        })
                        .then(response => response.json())
                        .then(data => {
                            if (data.success) updateStatus();
                            else alert('Error: ' + data.data);
                        })
                        .catch(error => {
                            console.error('Error removing excluded image:', error);
                            alert('Failed to remove excluded image: ' + error.message);
                        });
                    });
                });
            }

			let retryCounts = {};

			function convertNextImage(offset) {
				if (!isConverting) return;
				retryCounts = {}; // Clear retry counts when starting

				fetch('<?php echo admin_url('admin-ajax.php?action=webp_convert_single&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>', {
					method: 'POST',
					headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
					body: 'offset=' + encodeURIComponent(offset)
				})
				.then(response => {
					if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
					return response.json();
				})
				.then(data => {
					if (data.success) {
						updateStatus();
						if (!data.data.complete && isConverting) {
							retryCounts[offset] = 0; // Reset retry count
							convertNextImage(data.data.offset);
						} else {
							document.getElementById('stop-conversion').style.display = 'none';
						}
					} else {
						// Failed: Retry up to 2 times
						retryCounts[offset] = (retryCounts[offset] || 0) + 1;
						if (retryCounts[offset] <= 2) {
							console.warn('Retrying offset:', offset, 'Attempt:', retryCounts[offset]);
							setTimeout(() => convertNextImage(offset), 1000); // Wait 1s before retry
						} else {
							console.error('Giving up on offset:', offset);
							if (isConverting) {
								convertNextImage(offset + <?php echo wpturbo_get_batch_size(); ?>); // Skip to next batch
							}
						}
					}
				})
				.catch(error => {
					console.error('Error in convertNextImage:', error);
					alert('Conversion failed: ' + error.message);
					document.getElementById('stop-conversion').style.display = 'none';
				});
			}

            <?php if (current_user_can('manage_options')): ?>
            const mediaFrame = wp.media({
                title: '<?php echo esc_js(__('Select Images to Exclude', 'wpturbo')); ?>',
                button: { text: '<?php echo esc_js(__('Add to Excluded List', 'wpturbo')); ?>' },
                multiple: true,
                library: { type: 'image' }
            });
            document.getElementById('open-media-library').addEventListener('click', () => mediaFrame.open());
            mediaFrame.on('select', () => {
                const selection = mediaFrame.state().get('selection');
                selection.each(attachment => {
                    fetch('<?php echo admin_url('admin-ajax.php?action=webp_add_excluded_image&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                        body: 'attachment_id=' + encodeURIComponent(attachment.id)
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.success) updateStatus();
                    })
                    .catch(error => {
                        console.error('Error adding excluded image:', error);
                        alert('Failed to add excluded image: ' + error.message);
                    });
                });
            });

            document.getElementById('set-max-width').addEventListener('click', () => {
                const maxWidths = document.getElementById('max-width-input').value;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_max_width=1&max_width='); ?>' + encodeURIComponent(maxWidths))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting max width:', error);
                        alert('Failed to set max width: ' + error.message);
                    });
            });

            document.getElementById('set-max-height').addEventListener('click', () => {
                const maxHeights = document.getElementById('max-height-input').value;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_max_height=1&max_height='); ?>' + encodeURIComponent(maxHeights))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting max height:', error);
                        alert('Failed to set max height: ' + error.message);
                    });
            });

            document.getElementById('resize-mode').addEventListener('change', () => {
                const mode = document.getElementById('resize-mode').value;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_resize_mode=1&resize_mode='); ?>' + encodeURIComponent(mode))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting resize mode:', error);
                        alert('Failed to set resize mode: ' + error.message);
                    });
            });

            document.getElementById('preserve-originals').addEventListener('click', () => {
                const preserve = document.getElementById('preserve-originals').checked;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_preserve_originals=1&preserve_originals='); ?>' + encodeURIComponent(preserve ? 1 : 0))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting preserve originals:', error);
                        alert('Failed to set preserve originals: ' + error.message);
                    });
            });

            document.getElementById('disable-auto-conversion').addEventListener('click', () => {
                const disable = document.getElementById('disable-auto-conversion').checked;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_disable_auto_conversion=1&disable_auto_conversion='); ?>' + encodeURIComponent(disable ? 1 : 0))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting disable auto-conversion:', error);
                        alert('Failed to set disable auto-conversion: ' + error.message);
                    });
            });

            document.getElementById('set-min-size-kb').addEventListener('click', () => {
                const minSizeKB = document.getElementById('min-size-kb').value;
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_min_size_kb=1&min_size_kb='); ?>' + encodeURIComponent(minSizeKB))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting minimum size:', error);
                        alert('Failed to set minimum size: ' + error.message);
                    });
            });

            document.getElementById('use-avif').addEventListener('click', () => {
                const useAvif = document.getElementById('use-avif').checked;
                if (useAvif && !confirm('<?php echo esc_js(__('Switching to AVIF requires reconverting all images for consistency. Continue?', 'wpturbo')); ?>')) {
                    document.getElementById('use-avif').checked = false;
                    return;
                }
                fetch('<?php echo admin_url('admin.php?page=webp-converter&set_use_avif=1&use_avif='); ?>' + encodeURIComponent(useAvif ? 1 : 0))
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error setting AVIF option:', error);
                        alert('Failed to set AVIF option: ' + error.message);
                    });
            });

            document.getElementById('start-conversion').addEventListener('click', () => {
                isConverting = true;
                document.getElementById('stop-conversion').style.display = 'inline-block';
                fetch('<?php echo admin_url('admin.php?page=webp-converter&convert_existing_images_to_webp=1'); ?>')
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => {
                        updateStatus();
                        convertNextImage(0);
                    })
                    .catch(error => {
                        console.error('Error starting conversion:', error);
                        alert('Failed to start conversion: ' + error.message);
                    });
            });

            document.getElementById('cleanup-originals').addEventListener('click', () => {
                fetch('<?php echo admin_url('admin.php?page=webp-converter&cleanup_leftover_originals=1'); ?>')
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error cleaning up originals:', error);
                        alert('Failed to cleanup originals: ' + error.message);
                    });
            });

            document.getElementById('convert-post-images').addEventListener('click', () => {
                if (confirm('<?php echo esc_js(__('Update all post images to the selected format?', 'wpturbo')); ?>')) {
                    fetch('<?php echo admin_url('admin-ajax.php?action=convert_post_images_to_webp&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                    })
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response.json();
                    })
                    .then(data => {
                        alert(data.success ? data.data.message : 'Error: ' + data.data);
                        updateStatus();
                    })
                    .catch(error => {
                        console.error('Error converting post images:', error);
                        alert('Failed to convert post images: ' + error.message);
                    });
                }
            });

            document.getElementById('run-all').addEventListener('click', () => {
                if (confirm('<?php echo esc_js(__('Run all steps?', 'wpturbo')); ?>')) {
                    isConverting = true;
                    document.getElementById('stop-conversion').style.display = 'inline-block';
                    fetch('<?php echo admin_url('admin.php?page=webp-converter&convert_existing_images_to_webp=1'); ?>')
                        .then(response => {
                            if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                            return response;
                        })
                        .then(() => {
                            convertNextImage(0);
                            return new Promise(resolve => {
                                const checkComplete = setInterval(() => {
                                    fetch('<?php echo admin_url('admin-ajax.php?action=webp_status&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>')
                                        .then(response => response.json())
                                        .then(data => {
                                            updateStatus();
                                            if (data.complete) {
                                                clearInterval(checkComplete);
                                                resolve();
                                            }
                                        })
                                        .catch(error => {
                                            console.error('Error checking conversion status:', error);
                                            clearInterval(checkComplete);
                                            resolve();
                                        });
                                }, 1000);
                            });
                        })
                        .then(() => {
                            return fetch('<?php echo admin_url('admin-ajax.php?action=convert_post_images_to_webp&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>', {
                                method: 'POST',
                                headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                            })
                            .then(response => {
                                if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                                return response.json();
                            })
                            .then(data => {
                                updateStatus();
                                alert(data.success ? data.data.message : 'Error: ' + data.data);
                            });
                        })
                        .then(() => {
                            return fetch('<?php echo admin_url('admin.php?page=webp-converter&cleanup_leftover_originals=1'); ?>');
                        })
                        .then(() => {
                            isConverting = false;
                            document.getElementById('stop-conversion').style.display = 'none';
                            updateStatus();
                            alert('<?php echo esc_js(__('All steps completed!', 'wpturbo')); ?>');
                        })
                        .catch(error => {
                            console.error('Error in Run All:', error);
                            alert('Run All failed: ' + error.message);
                            isConverting = false;
                            document.getElementById('stop-conversion').style.display = 'none';
                        });
                }
            });

            document.getElementById('stop-conversion').addEventListener('click', () => {
                isConverting = false;
                document.getElementById('stop-conversion').style.display = 'none';
            });

            document.getElementById('clear-log').addEventListener('click', () => {
                fetch('<?php echo admin_url('admin.php?page=webp-converter&clear_log=1'); ?>')
                    .then(response => {
                        if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                        return response;
                    })
                    .then(() => updateStatus())
                    .catch(error => {
                        console.error('Error clearing log:', error);
                        alert('Failed to clear log: ' + error.message);
                    });
            });

            document.getElementById('reset-defaults').addEventListener('click', () => {
                if (confirm('<?php echo esc_js(__('Reset all settings to defaults?', 'wpturbo')); ?>')) {
                    fetch('<?php echo admin_url('admin.php?page=webp-converter&reset_defaults=1'); ?>')
                        .then(response => {
                            if (!response.ok) throw new Error('Network response was not ok: ' + response.statusText);
                            return response;
                        })
                        .then(() => updateStatus())
                        .catch(error => {
                            console.error('Error resetting defaults:', error);
                            alert('Failed to reset defaults: ' + error.message);
                        });
                }
            });

            document.getElementById('export-media-zip').addEventListener('click', () => {
                if (confirm('<?php echo esc_js(__('Export all media as a ZIP file?', 'wpturbo')); ?>')) {
                    const url = '<?php echo admin_url('admin-ajax.php?action=webp_export_media_zip&nonce=' . wp_create_nonce('webp_converter_nonce')); ?>';
                    window.location.href = url;
                }
            });
            <?php endif; ?>

            updateStatus();
        });
    </script>
    <?php
}

// Setup AJAX hooks
add_action('admin_init', function() {
    add_action('wp_ajax_webp_status', 'wpturbo_webp_conversion_status');
    add_action('wp_ajax_webp_convert_single', 'wpturbo_convert_single_image');
    add_action('wp_ajax_webp_export_media_zip', 'wpturbo_export_media_zip');
    if (isset($_GET['convert_existing_images_to_webp']) && current_user_can('manage_options')) {
        delete_option('webp_conversion_complete');
    }
});

// Admin notices
add_action('admin_notices', function() {
    if (isset($_GET['convert_existing_images_to_webp'])) {
        echo '<div class="notice notice-success"><p>' . __('Conversion started. Monitor progress in Media.', 'wpturbo') . '</p></div>';
    }
    if (isset($_GET['set_max_width']) && wpturbo_set_max_widths()) {
        echo '<div class="notice notice-success"><p>' . __('Max widths updated.', 'wpturbo') . '</p></div>';
    }
    if (isset($_GET['set_max_height']) && wpturbo_set_max_heights()) {
        echo '<div class="notice notice-success"><p>' . __('Max heights updated.', 'wpturbo') . '</p></div>';
    }
    if (isset($_GET['reset_defaults']) && wpturbo_reset_defaults()) {
        echo '<div class="notice notice-success"><p>' . __('Settings reset to defaults.', 'wpturbo') . '</p></div>';
    }
    if (isset($_GET['set_min_size_kb']) && wpturbo_set_min_size_kb()) {
        echo '<div class="notice notice-success"><p>' . __('Minimum size threshold updated.', 'wpturbo') . '</p></div>';
    }
    if (isset($_GET['set_use_avif']) && wpturbo_set_use_avif()) {
        echo '<div class="notice notice-success"><p>' . __('Conversion format updated. Please reconvert all images.', 'wpturbo') . '</p></div>';
    }
});

// Custom image size names
add_filter('image_size_names_choose', 'wpturbo_disable_default_sizes', 999);
function wpturbo_disable_default_sizes($sizes) {
    $mode = wpturbo_get_resize_mode();
    $max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
    $custom_sizes = ['thumbnail' => __('Thumbnail (150x150)', 'wpturbo')];
    $additional_values = array_slice($max_values, 1, 3);
    foreach ($additional_values as $value) {
        $custom_sizes["custom-$value"] = ($mode === 'width') ? sprintf(__('Custom %dpx Width', 'wpturbo'), $value) : sprintf(__('Custom %dpx Height', 'wpturbo'), $value);
    }
    return $custom_sizes;
}

// Disable scaling
add_filter('big_image_size_threshold', '__return_false', 999);

// Clean up attachment files on deletion
add_action('wp_delete_attachment', 'wpturbo_delete_attachment_files', 10, 1);
function wpturbo_delete_attachment_files($attachment_id) {
    if (in_array($attachment_id, wpturbo_get_excluded_images())) return;

    $file = get_attached_file($attachment_id);
    if ($file && file_exists($file)) @unlink($file);

    $metadata = wp_get_attachment_metadata($attachment_id);
    if ($metadata && isset($metadata['sizes'])) {
        $upload_dir = wp_upload_dir()['basedir'];
        foreach ($metadata['sizes'] as $size) {
            $size_file = $upload_dir . '/' . dirname($metadata['file']) . '/' . $size['file'];
            if (file_exists($size_file)) @unlink($size_file);
        }
    }
}

// Ensure MIME types on plugin activation or format switch
register_activation_hook(__FILE__, 'wpturbo_ensure_mime_types');
add_action('update_option_webp_use_avif', 'wpturbo_ensure_mime_types');


// Stop Processed Images from being Processed again unless Settings Change
add_action('admin_init', function () {
    if (!current_user_can('manage_options') || !isset($_GET['patch_pixrefiner_stamp'])) return;

    $attachments = get_posts([
        'post_type' => 'attachment',
        'post_mime_type' => ['image/webp', 'image/avif'],
        'posts_per_page' => -1,
        'fields' => 'ids',
    ]);

    $expected_stamp = [
        'format'      => wpturbo_get_use_avif() ? 'avif' : 'webp',
        'quality'     => wpturbo_get_quality(),
        'resize_mode' => wpturbo_get_resize_mode(),
        'max_values'  => (wpturbo_get_resize_mode() === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights(),
    ];

    foreach ($attachments as $id) {
        $meta = wp_get_attachment_metadata($id);
        if (empty($meta['pixrefiner_stamp'])) {
            $meta['pixrefiner_stamp'] = $expected_stamp;
            wp_update_attachment_metadata($id, $meta);
        }
    }

    echo "<div class='notice notice-success'><p>✅ PixRefiner stamp patch complete.</p></div>";
});
				
			

Custom WordPress Admin Dashboard

				
					
class DocumentacionDashboard {

    private $grupo_opciones = 'dashboard_docs_settings';
    private $capacidad_requerida = 'manage_options';
    private $grupo_cache = 'dashboard_docs';
    private $expiracion_cache = 3600; // 1 hora

    public function __construct() {
        // Solo cargar funcionalidad de administración en el área de administración
        if (is_admin()) {
            add_action('admin_menu', array($this, 'agregar_pagina_configuracion'));
            add_action('admin_menu', array($this, 'agregar_menu_tutoriales'), 11);
            add_action('admin_bar_menu', array($this, 'agregar_menu_barra_admin'), 100);
            add_action('admin_init', array($this, 'registrar_configuraciones'));
            add_action('load-index.php', array($this, 'personalizar_dashboard'));
            add_action('wp_dashboard_setup', array($this, 'eliminar_todos_widgets_dashboard'), 999);
            add_action('admin_init', array($this, 'validar_envio_configuraciones'));

            // Seguridad: Agregar cabeceras CSP para área de administración
            add_action('admin_head', array($this, 'agregar_cabeceras_csp_admin'));
        }
        // Funcionalidad frontend: solo ocultación de barra de administración e inyección de CSS
        if (!is_admin()) {
            add_action('template_redirect', array($this, 'ocultar_barra_admin_en_pagina_doc'));
            add_action('wp_head', array($this, 'agregar_css_frontend'));
            add_action('wp_before_admin_bar_render', array($this, 'agregar_menu_barra_admin'));

            // Seguridad: Agregar cabeceras CSP para páginas de documentación frontend
            add_action('wp_head', array($this, 'agregar_cabeceras_csp_frontend'));
        }

        // Seguridad: Inicializar limitación de velocidad para cambios de configuración
        add_action('init', array($this, 'inicializar_limitacion_velocidad'));
    }

    // Agregar página de configuración bajo el menú Configuración
    public function agregar_pagina_configuracion() {
        add_options_page(
            __('Configuración Dashboard Personalizado', 'dashboard-docs'),
            __('Dashboard personalizado', 'dashboard-docs'),
            $this->capacidad_requerida,
            'dashboard-documentation',
            array($this, 'pagina_configuracion')
        );
    }

    // Agregar menú Tutoriales si está habilitado
    public function agregar_menu_tutoriales() {
        $mostrar_menu_tutoriales = get_option('dashboard_docs_show_tutorials_menu', 0);
        if (!$mostrar_menu_tutoriales || !current_user_can($this->capacidad_requerida)) {
            return;
        }

        add_menu_page(
            __('Tutoriales', 'dashboard-docs'),
            __('Tutoriales', 'dashboard-docs'),
            $this->capacidad_requerida,
            'dashboard-tutorials',
            array($this, 'renderizar_pagina_tutoriales'),
            'dashicons-editor-help',
            2
        );
    }

    // Renderizar página de tutoriales (igual que el dashboard)
    public function renderizar_pagina_tutoriales() {
        $this->mostrar_contenido_dashboard_personalizado();
    }

    // Agregar menú de documentación externa a la barra de administración
    public function agregar_menu_barra_admin($wp_admin_bar = null) {
        $mostrar_menu_barra_admin = get_option('dashboard_docs_show_admin_bar_menu', 0);
        if (!$mostrar_menu_barra_admin || !current_user_can($this->capacidad_requerida)) {
            return;
        }

        // Obtener barra de administración si no se pasa (para frontend)
        if (!$wp_admin_bar) {
            global $wp_admin_bar;
        }

        if (!$wp_admin_bar) {
            return;
        }

        $menus_externos = get_option('dashboard_docs_external_menus', array());
        if (empty($menus_externos)) {
            return;
        }

        // Agregar menú principal
        $nombre_menu_barra_admin = get_option('dashboard_docs_admin_bar_menu_name', __('Todos los tutoriales', 'dashboard-docs'));
        $wp_admin_bar->add_menu(array(
            'id'    => 'all-tutorials',
            'title' => esc_html($nombre_menu_barra_admin),
            'href'  => '#',
        ));

        // Agregar submenús
        foreach ($menus_externos as $indice => $menu) {
            if (!empty($menu['name']) && !empty($menu['url'])) {
                $meta = array();
                if (isset($menu['open_in_new_tab']) && $menu['open_in_new_tab']) {
                    $meta['target'] = '_blank';
                    $meta['rel'] = 'noopener noreferrer';
                } else {
                    $meta['target'] = '_self';
                }

                $wp_admin_bar->add_menu(array(
                    'id'     => 'tutorial-' . $indice,
                    'parent' => 'all-tutorials',
                    'title'  => esc_html($menu['name']),
                    'href'   => esc_url($menu['url']),
                    'meta'   => $meta,
                ));
            }
        }
    }

    // Registrar configuraciones del plugin con validación
    public function registrar_configuraciones() {
        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_page',
            array(
                'type' => 'integer',
                'sanitize_callback' => array($this, 'sanitizar_id_pagina'),
                'default' => 0
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_hide_header',
            array(
                'type' => 'boolean',
                'sanitize_callback' => array($this, 'sanitizar_checkbox'),
                'default' => false
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_hide_footer',
            array(
                'type' => 'boolean',
                'sanitize_callback' => array($this, 'sanitizar_checkbox'),
                'default' => false
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_role_pages',
            array(
                'type' => 'array',
                'sanitize_callback' => array($this, 'sanitizar_paginas_roles'),
                'default' => array()
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_show_tutorials_menu',
            array(
                'type' => 'boolean',
                'sanitize_callback' => array($this, 'sanitizar_checkbox'),
                'default' => false
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_show_admin_bar_menu',
            array(
                'type' => 'boolean',
                'sanitize_callback' => array($this, 'sanitizar_checkbox'),
                'default' => false
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_admin_bar_menu_name',
            array(
                'type' => 'string',
                'sanitize_callback' => array($this, 'sanitizar_campo_texto'),
                'default' => __('Todos los tutoriales', 'dashboard-docs')
            )
        );

        register_setting(
            $this->grupo_opciones,
            'dashboard_docs_external_menus',
            array(
                'type' => 'array',
                'sanitize_callback' => array($this, 'sanitizar_menus_externos'),
                'default' => array()
            )
        );
    }

    // Seguridad: Validar envío de formulario de configuraciones
    public function validar_envio_configuraciones() {
        if (isset($_POST['submit']) && isset($_POST['option_page']) && $_POST['option_page'] === $this->grupo_opciones) {
            if (!current_user_can($this->capacidad_requerida)) {
                wp_die(__('No tienes permisos suficientes para acceder a esta página.', 'dashboard-docs'));
            }

            if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], $this->grupo_opciones . '-options')) {
                wp_die(__('Verificación de seguridad falló.', 'dashboard-docs'));
            }

            // Limpiar cache cuando las configuraciones se actualizan
            $this->limpiar_cache_documentacion();
        }
    }

    // Rendimiento: Limpiar cache de página de documentación
    private function limpiar_cache_documentacion() {
        // Limpiar todos los datos de la página de documentación en cache
        // Respaldo para sistemas sin wp_cache_delete_group
        if (function_exists('wp_cache_delete_group')) {
            wp_cache_delete_group($this->grupo_cache);
        }
    }

    // Seguridad: Sanitizar entrada de ID de página
    public function sanitizar_id_pagina($entrada) {
        $id_pagina = absint($entrada);

        if ($id_pagina > 0) {
            $pagina = get_post($id_pagina);
            if (!$pagina || $pagina->post_type !== 'page') {
                add_settings_error(
                    'dashboard_docs_page',
                    'invalid_page',
                    __('Página seleccionada inválida.', 'dashboard-docs')
                );
                return get_option('dashboard_docs_page', 0);
            }
        }

        return $id_pagina;
    }

    // Seguridad: Sanitizar entrada de checkbox
    public function sanitizar_checkbox($entrada) {
        return !empty($entrada) ? 1 : 0;
    }

    // Seguridad: Sanitizar entrada de campo de texto
    public function sanitizar_campo_texto($entrada) {
        return sanitize_text_field($entrada);
    }

    // Seguridad: Sanitizar array de páginas de roles
    public function sanitizar_paginas_roles($entrada) {
        if (!is_array($entrada)) {
            return array();
        }

        $sanitizado = array();
        $roles_validos = wp_roles()->get_names();

        foreach ($entrada as $rol => $id_pagina) {
            // Validar que el rol existe
            if (!array_key_exists($rol, $roles_validos)) {
                continue;
            }

            // Validar ID de página
            $id_pagina = absint($id_pagina);
            if ($id_pagina > 0) {
                $pagina = get_post($id_pagina);
                if ($pagina && $pagina->post_type === 'page') {
                    $sanitizado[sanitize_key($rol)] = $id_pagina;
                }
            }
        }

        return $sanitizado;
    }

    // Seguridad: Sanitizar array de menús externos
    public function sanitizar_menus_externos($entrada) {
        if (!is_array($entrada)) {
            return array();
        }

        $sanitizado = array();
        foreach ($entrada as $menu) {
            if (is_array($menu) && !empty($menu['name']) && !empty($menu['url'])) {
                $nombre = sanitize_text_field($menu['name']);
                $url = esc_url_raw($menu['url']);
                $abrir_nueva_pestana = (isset($menu['open_in_new_tab']) && $menu['open_in_new_tab'] == 1) ? 1 : 0;

                // Validación mejorada de URL
                if (!empty($nombre) && !empty($url) && $this->validar_url_externa($url)) {
                    $sanitizado[] = array(
                        'name' => $nombre,
                        'url' => $url,
                        'open_in_new_tab' => $abrir_nueva_pestana
                    );
                } else {
                    add_settings_error(
                        'dashboard_docs_external_menus',
                        'invalid_external_url',
                        sprintf(
                            __('URL inválida o insegura eliminada: %s', 'dashboard-docs'),
                            esc_html($menu['url'])
                        )
                    );
                }
            }
        }

        return $sanitizado;
    }

    // Página de configuración HTML con seguridad adecuada
    public function pagina_configuracion() {
        // Seguridad: Verificar capacidades de usuario
        if (!current_user_can($this->capacidad_requerida)) {
            wp_die(__('No tienes permisos suficientes para acceder a esta página.', 'dashboard-docs'));
        }

        $roles = wp_roles()->get_names();
        $paginas_roles = get_option('dashboard_docs_role_pages', array());
        $menus_externos = get_option('dashboard_docs_external_menus', array());

        // Seguridad: Obtener páginas con verificación adecuada de estado de publicación y límite para rendimiento
        $paginas = get_pages(array(
            'post_status' => array('publish', 'private'),
            'number' => 100 // Limitar a 100 páginas para rendimiento
        ));
        ?>
        <div class="wrap">
            <h1><?php echo esc_html__('Configuración Dashboard Personalizado', 'dashboard-docs'); ?></h1>

            <form method="post" action="options.php">
                <?php
                settings_fields($this->grupo_opciones);
                wp_nonce_field($this->grupo_opciones . '-options');
                ?>

                <table class="form-table">
                    <tr>
                        <th scope="row"><?php esc_html_e('Página de Dashboard por Defecto', 'dashboard-docs'); ?></th>
                        <td>
                            <select name="dashboard_docs_page" id="dashboard_docs_page">
                                <option value=""><?php esc_html_e('-- Seleccionar una página --', 'dashboard-docs'); ?></option>
                                <?php
                                $pagina_seleccionada = absint(get_option('dashboard_docs_page', 0));
                                foreach ($paginas as $pagina) {
                                    $seleccionada = selected($pagina_seleccionada, $pagina->ID, false);
                                    $texto_estado = ($pagina->post_status === 'private') ? ' ' . esc_html__('(privada)', 'dashboard-docs') : '';
                                    printf(
                                        '<option value="%d" %s>%s%s</option>',
                                        esc_attr($pagina->ID),
                                        $seleccionada,
                                        esc_html($pagina->post_title),
                                        $texto_estado
                                    );
                                }
                                ?>
                            </select>
                            <p class="description"><?php esc_html_e('Elige la página por defecto para mostrar en el dashboard', 'dashboard-docs'); ?></p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e('Páginas por Rol', 'dashboard-docs'); ?></th>
                        <td>
                            <div id="contenedor-configuracion-roles">
                                <div style="margin-bottom: 15px;">
                                    <button type="button" id="boton-agregar-rol" class="button"><?php esc_html_e('+ Agregar Página Específica por Rol', 'dashboard-docs'); ?></button>
                                </div>

                                <div id="asignaciones-roles">
                                    <?php
                                    $roles_configurados = array_filter($paginas_roles);
                                    if (!empty($roles_configurados)):
                                        foreach ($roles_configurados as $clave_rol => $id_pagina):
                                            if (isset($roles[$clave_rol])):
                                    ?>
                                        <div class="asignacion-rol" style="display: flex; align-items: center; margin-bottom: 10px; padding: 10px; background: #f9f9f9; border-radius: 4px;">
                                            <select name="selector_rol_<?php echo esc_attr(uniqid()); ?>" class="selector-rol" style="margin-right: 10px; min-width: 150px;">
                                                <option value=""><?php esc_html_e('-- Seleccionar Rol --', 'dashboard-docs'); ?></option>
                                                <?php foreach ($roles as $c_rol => $n_rol): ?>
                                                    <option value="<?php echo esc_attr($c_rol); ?>" <?php selected($clave_rol, $c_rol); ?>><?php echo esc_html($n_rol); ?></option>
                                                <?php endforeach; ?>
                                            </select>

                                            <select name="dashboard_docs_role_pages[<?php echo esc_attr($clave_rol); ?>]" class="selector-pagina" style="margin-right: 10px; flex: 1;">
                                                <option value=""><?php esc_html_e('-- Usar página por defecto --', 'dashboard-docs'); ?></option>
                                                <?php foreach ($paginas as $pagina): ?>
                                                    <option value="<?php echo esc_attr($pagina->ID); ?>" <?php selected($id_pagina, $pagina->ID); ?>>
                                                        <?php echo esc_html($pagina->post_title); ?>
                                                        <?php echo ($pagina->post_status === 'private') ? ' ' . esc_html__('(privada)', 'dashboard-docs') : ''; ?>
                                                    </option>
                                                <?php endforeach; ?>
                                            </select>

                                            <button type="button" class="button eliminar-rol" style="color: #dc3232;"><?php esc_html_e('Eliminar', 'dashboard-docs'); ?></button>
                                        </div>
                                    <?php
                                            endif;
                                        endforeach;
                                    endif;
                                    ?>
                                </div>
                            </div>

                            <p class="description"><?php esc_html_e('Agregar páginas de documentación específicas para diferentes roles de usuario. Solo los roles configurados anularán la página por defecto.', 'dashboard-docs'); ?></p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e('Opciones de Visualización', 'dashboard-docs'); ?></th>
                        <td>
                            <label>
                                <input type="checkbox" name="dashboard_docs_hide_header" value="1" <?php checked(get_option('dashboard_docs_hide_header'), 1); ?>>
                                <?php esc_html_e('Ocultar encabezado de la página del dashboard', 'dashboard-docs'); ?>
                            </label><br>
                            <label>
                                <input type="checkbox" name="dashboard_docs_hide_footer" value="1" <?php checked(get_option('dashboard_docs_hide_footer'), 1); ?>>
                                <?php esc_html_e('Ocultar pie de página de la página del dashboard', 'dashboard-docs'); ?>
                            </label>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><?php esc_html_e('Opciones de Menú Extra', 'dashboard-docs'); ?></th>
                        <td>
                            <label>
                                <input type="checkbox" name="dashboard_docs_show_tutorials_menu" value="1" <?php checked(get_option('dashboard_docs_show_tutorials_menu'), 1); ?>>
                                <?php esc_html_e('Mostrar menú de página "Tutoriales" en la barra lateral', 'dashboard-docs'); ?>
                            </label>
                            <p class="description"><?php esc_html_e('Muestra un menú separado "Tutoriales" en la barra lateral de administración que abre la página del dashboard.', 'dashboard-docs'); ?></p>

                            <br>

                            <label>
                                <input type="checkbox" name="dashboard_docs_show_admin_bar_menu" value="1" <?php checked(get_option('dashboard_docs_show_admin_bar_menu'), 1); ?> id="mostrar-menu-barra-admin">
                                <?php esc_html_e('Mostrar menú de documentación externa en la barra de administración', 'dashboard-docs'); ?>
                            </label>
                            <p class="description"><?php esc_html_e('Muestra el menú "Todos los tutoriales" en la barra superior de administración con enlaces externos personalizados.', 'dashboard-docs'); ?></p>

                            <div id="nombre-menu-barra-admin" style="margin-top: 10px; <?php echo get_option('dashboard_docs_show_admin_bar_menu') ? '' : 'display: none;'; ?>">
                                <label for="dashboard_docs_admin_bar_menu_name">
                                    <?php esc_html_e('Nombre del menú de la barra de administración:', 'dashboard-docs'); ?>
                                </label>
                                <input type="text" name="dashboard_docs_admin_bar_menu_name" id="dashboard_docs_admin_bar_menu_name" value="<?php echo esc_attr(get_option('dashboard_docs_admin_bar_menu_name', __('Todos los tutoriales', 'dashboard-docs'))); ?>" class="regular-text" style="margin-left: 10px;">
                                <p class="description"><?php esc_html_e('Personaliza el nombre del menú de la barra de administración. Por defecto es "Todos los tutoriales".', 'dashboard-docs'); ?></p>
                            </div>
                        </td>
                    </tr>
                    <tr id="fila-menus-externos">
                        <th scope="row"><?php esc_html_e('Agregar Tus Enlaces', 'dashboard-docs'); ?></th>
                        <td>
                            <div id="contenedor-menus-externos">
                                <div style="margin-bottom: 15px;">
                                    <button type="button" id="boton-agregar-menu-externo" class="button"><?php esc_html_e('+ Agregar enlace', 'dashboard-docs'); ?></button>
                                </div>

                                <div id="asignaciones-menu-externo">
                                    <?php if (!empty($menus_externos)): ?>
                                        <?php foreach ($menus_externos as $indice => $menu): ?>
                                            <div class="asignacion-menu-externo" style="display: flex; align-items: center; margin-bottom: 10px; padding: 10px; background: #f9f9f9; border-radius: 4px;">
                                                <input type="text" name="dashboard_docs_external_menus[<?php echo esc_attr($indice); ?>][name]" placeholder="<?php esc_attr_e('Nombre del menú', 'dashboard-docs'); ?>" value="<?php echo esc_attr($menu['name']); ?>" style="margin-right: 10px; width: 200px;">
                                                <input type="url" name="dashboard_docs_external_menus[<?php echo esc_attr($indice); ?>][url]" placeholder="<?php esc_attr_e('URL (https://ejemplo.com)', 'dashboard-docs'); ?>" value="<?php echo esc_attr($menu['url']); ?>" style="margin-right: 10px; flex: 1;">
                                                <label style="margin-right: 10px; white-space: nowrap;">
                                                    <input type="checkbox" name="dashboard_docs_external_menus[<?php echo esc_attr($indice); ?>][open_in_new_tab]" value="1" <?php checked(isset($menu['open_in_new_tab']) && $menu['open_in_new_tab'] == 1, true); ?>>
                                                    <?php esc_html_e('Abrir en nueva pestaña', 'dashboard-docs'); ?>
                                                </label>
                                                <button type="button" class="button eliminar-menu-externo" style="color: #dc3232;"><?php esc_html_e('Eliminar', 'dashboard-docs'); ?></button>
                                            </div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                            </div>

                            <p class="description"><?php esc_html_e('Agregar enlaces de documentación externa que aparecerán en el menú "Todos los tutoriales" de la barra de administración.', 'dashboard-docs'); ?></p>
                        </td>
                    </tr>
                </table>

                <?php submit_button(); ?>
            </form>
        </div>

        <script>
        jQuery(document).ready(function($) {
            var opcionesRoles = <?php echo wp_json_encode($roles, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>;
            var opcionesPaginas = <?php echo wp_json_encode(array_map(function($pagina) {
                return array(
                    'id' => absint($pagina->ID),
                    'title' => sanitize_text_field($pagina->post_title),
                    'status' => sanitize_text_field($pagina->post_status)
                );
            }, $paginas), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>;

            var cadenasTraducidas = {
                seleccionarRol: <?php echo wp_json_encode(__('-- Seleccionar Rol --', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>,
                usarPaginaPorDefecto: <?php echo wp_json_encode(__('-- Usar página por defecto --', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>,
                etiquetaPrivada: <?php echo wp_json_encode(__('(privada)', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>,
                botonEliminar: <?php echo wp_json_encode(__('Eliminar', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>,
                placeholderNombreMenu: <?php echo wp_json_encode(__('Nombre del menú', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>,
                placeholderUrl: <?php echo wp_json_encode(__('URL (https://ejemplo.com)', 'dashboard-docs'), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>
            };

            $('#boton-agregar-rol').click(function() {
                var idUnico = Date.now();
                var htmlOpcionesRoles = '<option value="">' + cadenasTraducidas.seleccionarRol + '</option>';
                $.each(opcionesRoles, function(clave, nombre) {
                    htmlOpcionesRoles += '<option value="' + $('<div>').text(clave).html() + '">' + $('<div>').text(nombre).html() + '</option>';
                });

                var htmlOpcionesPaginas = '<option value="">' + cadenasTraducidas.usarPaginaPorDefecto + '</option>';
                $.each(opcionesPaginas, function(indice, pagina) {
                    var textoEstado = pagina.status === 'private' ? ' ' + cadenasTraducidas.etiquetaPrivada : '';
                    htmlOpcionesPaginas += '<option value="' + pagina.id + '">' + $('<div>').text(pagina.title).html() + textoEstado + '</option>';
                });

                var nuevaFila = $('<div class="asignacion-rol" style="display: flex; align-items: center; margin-bottom: 10px; padding: 10px; background: #f9f9f9; border-radius: 4px;">' +
                    '<select name="selector_rol_' + idUnico + '" class="selector-rol" style="margin-right: 10px; min-width: 150px;">' + htmlOpcionesRoles + '</select>' +
                    '<select name="selector_pagina_' + idUnico + '" class="selector-pagina" style="margin-right: 10px; flex: 1;">' + htmlOpcionesPaginas + '</select>' +
                    '<button type="button" class="button eliminar-rol" style="color: #dc3232;">' + cadenasTraducidas.botonEliminar + '</button>' +
                    '</div>');

                $('#asignaciones-roles').append(nuevaFila);
            });

            $(document).on('click', '.eliminar-rol', function() {
                $(this).closest('.asignacion-rol').remove();
            });

            $(document).on('change', '.selector-rol', function() {
                var $fila = $(this).closest('.asignacion-rol');
                var rolSeleccionado = $(this).val();
                var $selectorPagina = $fila.find('.selector-pagina');

                if (rolSeleccionado) {
                    $selectorPagina.attr('name', 'dashboard_docs_role_pages[' + $('<div>').text(rolSeleccionado).html() + ']');
                } else {
                    $selectorPagina.attr('name', 'selector_pagina_' + Date.now());
                }
            });

            var indiceMenuExterno = <?php echo count($menus_externos); ?>;

            $('#boton-agregar-menu-externo').click(function() {
                var nuevaFila = $('<div class="asignacion-menu-externo" style="display: flex; align-items: center; margin-bottom: 10px; padding: 10px; background: #f9f9f9; border-radius: 4px;">' +
                    '<input type="text" name="dashboard_docs_external_menus[' + indiceMenuExterno + '][name]" placeholder="' + cadenasTraducidas.placeholderNombreMenu + '" style="margin-right: 10px; width: 200px;">' +
                    '<input type="url" name="dashboard_docs_external_menus[' + indiceMenuExterno + '][url]" placeholder="' + cadenasTraducidas.placeholderUrl + '" style="margin-right: 10px; flex: 1;">' +
                    '<label style="margin-right: 10px; white-space: nowrap;">' +
                    '<input type="checkbox" name="dashboard_docs_external_menus[' + indiceMenuExterno + '][open_in_new_tab]" value="1"> ' +
                    '<?php echo esc_js(__('Abrir en nueva pestaña', 'dashboard-docs')); ?>' +
                    '</label>' +
                    '<button type="button" class="button eliminar-menu-externo" style="color: #dc3232;">' + cadenasTraducidas.botonEliminar + '</button>' +
                    '</div>');

                $('#asignaciones-menu-externo').append(nuevaFila);
                indiceMenuExterno++;
            });

            $(document).on('click', '.eliminar-menu-externo', function() {
                $(this).closest('.asignacion-menu-externo').remove();
            });

            function alternarSeccionMenusExternos() {
                if ($('#mostrar-menu-barra-admin').is(':checked')) {
                    $('#fila-menus-externos').show();
                    $('#nombre-menu-barra-admin').show();
                } else {
                    $('#fila-menus-externos').hide();
                    $('#nombre-menu-barra-admin').hide();
                }
            }

            $('#mostrar-menu-barra-admin').change(alternarSeccionMenusExternos);
            alternarSeccionMenusExternos();
        });
        </script>
        <?php
    }

    public function ocultar_barra_admin_en_pagina_doc() {
        $id_pagina_seleccionada = $this->obtener_pagina_documentacion_usuario();
        if ($id_pagina_seleccionada && is_page($id_pagina_seleccionada)) {
            add_filter('show_admin_bar', '__return_false', 10, 1);
        }
    }

    public function eliminar_todos_widgets_dashboard() {
        $id_pagina_seleccionada = $this->obtener_pagina_documentacion_usuario();
        if (!$id_pagina_seleccionada) return;

        global $wp_meta_boxes;
        $wp_meta_boxes['dashboard']['normal']['core'] = array();
        $wp_meta_boxes['dashboard']['side']['core'] = array();
        $wp_meta_boxes['dashboard']['normal']['high'] = array();
        $wp_meta_boxes['dashboard']['side']['high'] = array();
    }

    public function mostrar_contenido_dashboard_personalizado() {
        $id_pagina_seleccionada = $this->obtener_pagina_documentacion_usuario();
        if (!$id_pagina_seleccionada) return;

        $pagina = get_post($id_pagina_seleccionada);
        if (!$pagina || $pagina->post_type !== 'page') {
            echo '<div class="error"><p>' . esc_html__('Página de documentación seleccionada no encontrada.', 'dashboard-docs') . '</p></div>';
            return;
        }

        if ($pagina->post_status === 'private' && !current_user_can('read_private_pages')) {
            echo '<div class="error"><p>' . esc_html__('No tienes permisos para ver esta página de documentación.', 'dashboard-docs') . '</p></div>';
            return;
        }

        $enlace_pagina = get_permalink($id_pagina_seleccionada);
        if (!$enlace_pagina) {
            echo '<div class="error"><p>' . esc_html__('No se puede generar la URL de la página.', 'dashboard-docs') . '</p></div>';
            return;
        }

        $ocultar_encabezado = get_option('dashboard_docs_hide_header');
        $ocultar_pie = get_option('dashboard_docs_hide_footer');

        $parametros_url = array();
        if ($ocultar_encabezado) $parametros_url['hide_header'] = '1';
        if ($ocultar_pie) $parametros_url['hide_footer'] = '1';

        if (!empty($parametros_url)) {
            $enlace_pagina = add_query_arg($parametros_url, $enlace_pagina);
        }
        ?>
        <style>
            #wpbody-content .wrap {
                display: none;
            }
            #wpbody-content {
                margin: 0;
                padding: 0;
            }
            #contenido-pagina {
                width: 100%;
                min-height: 100vh;
                height: calc(100vh - 32px);
                border: 0;
                overflow: hidden;
            }
        </style>
        <iframe id="contenido-pagina"
                src="<?php echo esc_url($enlace_pagina); ?>"
                scrolling="no"
                frameborder="0"
                sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation allow-presentation allow-downloads">
        </iframe>

        <script>
        document.addEventListener('DOMContentLoaded', function() {
            var iframe = document.getElementById('contenido-pagina');

            function redimensionarIframe() {
                try {
                    iframe.onload = function() {
                        try {
                            var docIframe = iframe.contentDocument || iframe.contentWindow.document;
                            var cuerpo = docIframe.body;
                            var html = docIframe.documentElement;

                            var altura = Math.max(
                                cuerpo.scrollHeight,
                                cuerpo.offsetHeight,
                                html.clientHeight,
                                html.scrollHeight,
                                html.offsetHeight
                            );

                            iframe.style.height = altura + 'px';
                            iframe.style.minHeight = altura + 'px';

                            var estilo = docIframe.createElement('style');
                            estilo.textContent = 'html, body { overflow: hidden !important; margin: 0; padding: 0; }';
                            docIframe.head.appendChild(estilo);

                        } catch(e) {
                            iframe.style.height = 'calc(100vh - 32px)';
                        }
                    };
                } catch(e) {
                    console.log('Redimensionamiento de iframe falló:', e);
                }
            }

            redimensionarIframe();
            setTimeout(redimensionarIframe, 1000);
        });
        </script>
        <?php
    }

    public function personalizar_dashboard() {
        $id_pagina_seleccionada = $this->obtener_pagina_documentacion_usuario();
        if (!$id_pagina_seleccionada) return;

        add_filter('screen_options_show_screen', '__return_false');
        add_action('current_screen', array($this, 'eliminar_pestanas_ayuda'));
        add_action('in_admin_header', array($this, 'mostrar_contenido_dashboard_personalizado'), 100);
    }

    public function eliminar_pestanas_ayuda() {
        $pantalla = get_current_screen();
        if ($pantalla && $pantalla->id === 'dashboard') {
            $pantalla->remove_help_tabs();
        }
    }

    private function obtener_pagina_documentacion_usuario() {
        $usuario_actual = wp_get_current_user();
        if (!$usuario_actual || !$usuario_actual->exists()) {
            return false;
        }

        $clave_cache = 'pagina_doc_usuario_' . $usuario_actual->ID;
        $id_pagina_cache = wp_cache_get($clave_cache, $this->grupo_cache);

        if ($id_pagina_cache !== false) {
            return $id_pagina_cache;
        }

        $paginas_roles = get_option('dashboard_docs_role_pages', array());
        $id_pagina = false;

        foreach ($usuario_actual->roles as $rol) {
            $rol = sanitize_key($rol);
            if (isset($paginas_roles[$rol]) && !empty($paginas_roles[$rol])) {
                $id_pagina_temp = absint($paginas_roles[$rol]);
                if ($id_pagina_temp > 0) {
                    $pagina = get_post($id_pagina_temp);
                    if ($pagina && $pagina->post_type === 'page') {
                        $id_pagina = $id_pagina_temp;
                        break;
                    }
                }
            }
        }

        if (!$id_pagina) {
            $pagina_por_defecto = absint(get_option('dashboard_docs_page', 0));
            if ($pagina_por_defecto > 0) {
                $pagina = get_post($pagina_por_defecto);
                if ($pagina && $pagina->post_type === 'page') {
                    $id_pagina = $pagina_por_defecto;
                }
            }
        }

        wp_cache_set($clave_cache, $id_pagina, $this->grupo_cache, $this->expiracion_cache);

        return $id_pagina;
    }

    public function agregar_css_frontend() {
        $ocultar_encabezado = isset($_GET['hide_header']) && sanitize_text_field($_GET['hide_header']) === '1';
        $ocultar_pie = isset($_GET['hide_footer']) && sanitize_text_field($_GET['hide_footer']) === '1';

        if (!$ocultar_encabezado && !$ocultar_pie) {
            return;
        }

        $id_pagina_actual = get_the_ID();
        if (!$this->es_pagina_documentacion($id_pagina_actual)) {
            return;
        }

        $css = '';
        if ($ocultar_encabezado) {
            $css .= 'header, .site-header, #masthead { display: none !important; }';
        }
        if ($ocultar_pie) {
            $css .= 'footer, .site-footer, #colophon { display: none !important; }';
        }

        if (!empty($css)) {
            echo '<style>' . esc_html($css) . '</style>';
        }
    }

    private function es_pagina_documentacion($id_pagina) {
        if (!$id_pagina) {
            return false;
        }

        $clave_cache = 'es_pagina_doc_' . $id_pagina;
        $resultado_cache = wp_cache_get($clave_cache, $this->grupo_cache);

        if ($resultado_cache !== false) {
            return $resultado_cache;
        }

        $resultado = false;

        $pagina_por_defecto = absint(get_option('dashboard_docs_page', 0));
        if ($id_pagina === $pagina_por_defecto) {
            $resultado = true;
        } else {
            $paginas_roles = get_option('dashboard_docs_role_pages', array());
            $resultado = in_array($id_pagina, array_map('absint', $paginas_roles));
        }

        wp_cache_set($clave_cache, $resultado, $this->grupo_cache, $this->expiracion_cache);

        return $resultado;
    }

    public function agregar_cabeceras_csp_admin() {
        $pantalla = get_current_screen();
        if ($pantalla && $pantalla->id === 'dashboard') {
            echo '<meta http-equiv="Content-Security-Policy" content="frame-src \'self\' https://*.youtube.com https://*.vimeo.com https://*.dailymotion.com; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\';">';
        }
    }

    public function agregar_cabeceras_csp_frontend() {
        $id_pagina_actual = get_the_ID();
        if ($this->es_pagina_documentacion($id_pagina_actual)) {
            echo '<meta http-equiv="Content-Security-Policy" content="frame-src \'self\' https://*.youtube.com https://*.vimeo.com https://*.dailymotion.com; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\';">';
        }
    }

    public function inicializar_limitacion_velocidad() {
        if (isset($_POST['submit']) && isset($_POST['option_page']) && $_POST['option_page'] === $this->grupo_opciones) {
            $id_usuario = get_current_user_id();
            $clave_limite_velocidad = 'dashboard_docs_limite_velocidad_' . $id_usuario;
            $intentos = get_transient($clave_limite_velocidad);

            if ($intentos === false) {
                set_transient($clave_limite_velocidad, 1, HOUR_IN_SECONDS);
            } elseif ($intentos >= 100) {
                wp_die(
                    esc_html__('Demasiados cambios de configuración. Por favor espera una hora antes de intentar de nuevo.', 'dashboard-docs'),
                    esc_html__('Límite de Velocidad Excedido', 'dashboard-docs'),
                    array('response' => 429)
                );
            } else {
                set_transient($clave_limite_velocidad, $intentos + 1, HOUR_IN_SECONDS);
            }
        }
    }

    private function validar_url_externa($url) {
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            return false;
        }

        $url_parseada = parse_url($url);
        if (!in_array($url_parseada['scheme'], array('http', 'https'))) {
            return false;
        }

        $hosts_bloqueados = array(
            'localhost',
            '127.0.0.1',
            '::1',
            '0.0.0.0',
            '10.0.0.0/8',
            '172.16.0.0/12',
            '192.168.0.0/16'
        );

        foreach ($hosts_bloqueados as $host_bloqueado) {
            if (strpos($url_parseada['host'], $host_bloqueado) !== false) {
                return false;
            }
        }

        return true;
    }
}

// Inicializar la clase
new DocumentacionDashboard();

				
			

Borrar y Eliminar Plugin

				
					
/*
Plugin Name: Desactivar y Eliminar
Description: Agrega una opción para desactivar y eliminar plugins en un solo clic.
Version: 1.2
Author: WP Simple Hacks
Author URI: https://wpsimplehacks.com
*/

// Check if we're on the plugins page or handling a plugin action
add_action('current_screen', 'dad_initialize_plugin');
add_action('admin_init', 'dad_handle_plugin_actions');

function dad_initialize_plugin() {
    $screen = get_current_screen();
    
    // Only load on plugins page
    if (!$screen || $screen->id !== 'plugins') {
        return;
    }
    
    // Register all plugin functionality
    add_filter('bulk_actions-plugins', 'dad_add_bulk_actions');
    add_filter('handle_bulk_actions-plugins', 'dad_handle_bulk_actions', 10, 3);
    add_action('admin_notices', 'dad_bulk_action_admin_notice');
    add_filter('plugin_action_links', 'dad_add_quick_action', 10, 4);
    add_action('pre_current_active_plugins', 'dad_sort_plugins_list');
    add_filter('plugin_row_meta', 'dad_add_plugin_info', 10, 2);
    add_action('activated_plugin', 'dad_track_plugin_activation');
    add_action('deactivated_plugin', 'dad_track_plugin_deactivation');
    
    // Run once to check existing plugins
    dad_check_existing_plugins();
}

function dad_handle_plugin_actions() {
    // Only handle our specific quick action
    if (isset($_GET['action']) && $_GET['action'] === 'deactivate_and_delete' && isset($_GET['plugin'])) {
        dad_handle_quick_action();
    }
}

function dad_add_bulk_actions($bulk_actions) {
    // Security: Check if user has capability to manage plugins
    if (!current_user_can('delete_plugins')) {
        return $bulk_actions;
    }
    
    $bulk_actions['deactivate_and_delete'] = 'Desactivar y Eliminar';
    return $bulk_actions;
}

function dad_handle_bulk_actions($redirect_to, $doaction, $plugins) {
    if ($doaction !== 'deactivate_and_delete') {
        return $redirect_to;
    }

    // Security: Check user capabilities
    if (!current_user_can('delete_plugins') || !current_user_can('deactivate_plugins')) {
        return $redirect_to;
    }

    // Security: Verify nonce
    if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'], 'bulk-plugins')) {
        return $redirect_to;
    }

    // Security: Validate and sanitize plugin list
    if (!is_array($plugins) || empty($plugins)) {
        return $redirect_to;
    }

    // Optimization: Cache plugins list to avoid multiple calls
    static $valid_plugins = null;
    if ($valid_plugins === null) {
        $valid_plugins = array_keys(get_plugins());
    }
    
    $processed_count = 0;

    foreach ($plugins as $plugin) {
        // Security: Sanitize and validate plugin path
        $plugin = sanitize_text_field($plugin);
        
        // Security: Ensure plugin exists and is valid
        if (!in_array($plugin, $valid_plugins, true)) {
            continue;
        }
        
        // Security: Prevent self-deletion
        if (plugin_basename(__FILE__) === $plugin) {
            continue;
        }

        deactivate_plugins($plugin);
        dad_track_plugin_deactivation($plugin);
        delete_plugins(array($plugin));
        $processed_count++;
    }

    $redirect_to = add_query_arg('deactivate_and_delete', $processed_count, $redirect_to);
    return $redirect_to;
}


function dad_bulk_action_admin_notice() {
    if (!empty($_REQUEST['deactivate_and_delete'])) {
        $count = intval($_REQUEST['deactivate_and_delete']);
        if ($count > 0) {
            $message = sprintf(
                _n(
                    '%d plugin desactivado y eliminado.',
                    '%d plugins desactivados y eliminados.',
                    $count,
                    'deactivate-and-delete'
                ),
                $count
            );
            echo '<div id="message" class="updated notice notice-success is-dismissible"><p>' . esc_html($message) . '</p></div>';
        }
    }
}


function dad_add_quick_action($actions, $plugin_file, $plugin_data, $context) {
    // Security: Check user capabilities
    if (!current_user_can('delete_plugins') || !current_user_can('deactivate_plugins')) {
        return $actions;
    }
    
    // Security: Prevent self-deletion
    if (plugin_basename(__FILE__) === $plugin_file) {
        return $actions;
    }
    
    if (is_plugin_active($plugin_file)) {
        $url = wp_nonce_url(
            admin_url('plugins.php?action=deactivate_and_delete&plugin=' . urlencode($plugin_file)),
            'deactivate_and_delete_plugin_' . $plugin_file
        );
        
        $actions['deactivate_and_delete'] = sprintf(
            '<a href="%s" onclick="return confirm(%s)">%s</a>',
            esc_url($url),
            esc_attr("'" . esc_js(__('¿Estás seguro de que quieres desactivar y eliminar este plugin? Esta acción no se puede deshacer.', 'deactivate-and-delete')) . "'"),
            esc_html__('Desactivar y Eliminar', 'deactivate-and-delete')
        );
    }
    return $actions;
}



function dad_sort_plugins_list() {
    global $wp_list_table;

    if (!is_admin() || !$wp_list_table || get_current_screen()->id !== 'plugins') {
        return;
    }

    $all_plugins = get_plugins();
    $active_plugins = get_option('active_plugins', []);

    // Sort plugins into active and inactive
    $active = [];
    $inactive = [];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
        if (in_array($plugin_file, $active_plugins, true)) {
            $active[$plugin_file] = $plugin_data;
        } else {
            $inactive[$plugin_file] = $plugin_data;
        }
    }

    // Sort both arrays alphabetically by plugin name
    uasort($active, function ($a, $b) {
        return strcasecmp($a['Name'], $b['Name']);
    });

    uasort($inactive, function ($a, $b) {
        return strcasecmp($a['Name'], $b['Name']);
    });

    // Merge sorted arrays
    $sorted_plugins = $active + $inactive;

    // Replace plugin list table items
    $wp_list_table->items = [];
    foreach ($sorted_plugins as $plugin_file => $plugin_data) {
        $wp_list_table->items[$plugin_file] = $plugin_data;
    }
}

function dad_track_plugin_activation($plugin) {
    // Security: Validate input
    if (empty($plugin) || !is_string($plugin)) {
        return;
    }
    
    $plugin = sanitize_text_field($plugin);
    $plugin_dates = get_option('dad_plugin_dates', []);
    
    // Track installation date if this is the first time we see this plugin
    if (!isset($plugin_dates[$plugin]['installed'])) {
        $plugin_dates[$plugin]['installed'] = current_time('mysql');
    }
    
    $plugin_dates[$plugin]['activated'] = current_time('mysql');
    update_option('dad_plugin_dates', $plugin_dates);
}

function dad_track_plugin_deactivation($plugin) {
    // Security: Validate input
    if (empty($plugin) || !is_string($plugin)) {
        return;
    }
    
    $plugin = sanitize_text_field($plugin);
    $plugin_dates = get_option('dad_plugin_dates', []);
    
    // Track installation date if this is the first time we see this plugin
    if (!isset($plugin_dates[$plugin]['installed'])) {
        $plugin_dates[$plugin]['installed'] = current_time('mysql');
    }
    
    $plugin_dates[$plugin]['deactivated'] = current_time('mysql');
    update_option('dad_plugin_dates', $plugin_dates);
}

// Check and track installation dates for existing plugins
function dad_check_existing_plugins() {
    $all_plugins = get_plugins();
    $plugin_dates = get_option('dad_plugin_dates', []);
    $updated = false;
    
    foreach ($all_plugins as $plugin_file => $plugin_data) {
        if (!isset($plugin_dates[$plugin_file]['installed'])) {
            $plugin_dates[$plugin_file]['installed'] = current_time('mysql');
            $updated = true;
        }
    }
    
    if ($updated) {
        update_option('dad_plugin_dates', $plugin_dates);
    }
}


function dad_get_plugin_size($plugin_file) {
    // Optimization: Cache plugin sizes to avoid recalculation
    static $size_cache = [];
    if (isset($size_cache[$plugin_file])) {
        return $size_cache[$plugin_file];
    }
    
    // Security: Validate input
    if (empty($plugin_file) || !is_string($plugin_file)) {
        return $size_cache[$plugin_file] = 0;
    }
    
    // Security: Sanitize plugin file path
    $original_plugin_file = $plugin_file;
    $plugin_file = sanitize_text_field($plugin_file);
    
    // Security: Validate plugin exists
    static $valid_plugins = null;
    if ($valid_plugins === null) {
        $valid_plugins = array_keys(get_plugins());
    }
    
    if (!in_array($plugin_file, $valid_plugins, true)) {
        return $size_cache[$original_plugin_file] = 0;
    }
    
    // Security: Prevent path traversal in the plugin file name
    if (strpos($plugin_file, '..') !== false) {
        return $size_cache[$original_plugin_file] = 0;
    }
    
    // Get the directory name from plugin file
    $plugin_dir = dirname($plugin_file);
    
    // Debug: For understanding plugin structure
    // Single file plugins: dirname() returns '.'
    // Directory plugins: dirname() returns the folder name (e.g., 'akismet' for 'akismet/akismet.php')
    
    // If it's a single file plugin (in root plugins directory)
    if ($plugin_dir === '.') {
        $file_path = WP_PLUGIN_DIR . '/' . $plugin_file;
        if (file_exists($file_path) && is_readable($file_path)) {
            return $size_cache[$original_plugin_file] = filesize($file_path);
        }
        return $size_cache[$original_plugin_file] = 0;
    }
    
    // For directory-based plugins
    $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_dir;
    
    // Security: Ensure path is within plugin directory
    $real_plugin_dir = realpath(WP_PLUGIN_DIR);
    $real_plugin_path = realpath($plugin_path);
    
    if (!$real_plugin_path || strpos($real_plugin_path, $real_plugin_dir) !== 0) {
        return $size_cache[$original_plugin_file] = 0;
    }
    
    // Calculate total size for directory plugins
    if (!is_dir($plugin_path) || !is_readable($plugin_path)) {
        return $size_cache[$original_plugin_file] = 0;
    }
    
    $size = 0;
    $file_count = 0;
    $max_files = 1000; // Security: Limit number of files to prevent resource exhaustion
    
    try {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($plugin_path, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::LEAVES_ONLY
        );
        
        foreach ($iterator as $file) {
            if ($file_count++ > $max_files) {
                break; // Security: Prevent resource exhaustion
            }
            
            if ($file->isFile() && $file->isReadable()) {
                $file_size = $file->getSize();
                if ($file_size !== false) {
                    $size += $file_size;
                }
            }
        }
    } catch (Exception $e) {
        // Security: Handle exceptions gracefully
        return $size_cache[$original_plugin_file] = 0;
    }
    
    return $size_cache[$original_plugin_file] = $size;
}

function dad_format_file_size($bytes) {
    if ($bytes === 0) {
        return '0 B';
    }
    
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = 0;
    
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    
    return round($bytes, 2) . ' ' . $units[$i];
}

function dad_add_plugin_info($plugin_meta, $plugin_file) {
    // Security: Validate inputs
    if (!is_array($plugin_meta) || empty($plugin_file) || !is_string($plugin_file)) {
        return $plugin_meta;
    }
    
    // Security: Sanitize plugin file
    $plugin_file = sanitize_text_field($plugin_file);
    
    // Get plugin dates
    $plugin_dates = get_option('dad_plugin_dates', []);
    $dates_info = [];
    
    // First: Installation date
    if (isset($plugin_dates[$plugin_file]['installed'])) {
        $installed_date = date('j M Y g:i A', strtotime($plugin_dates[$plugin_file]['installed']));
        $dates_info[] = '<strong>' . esc_html__('Instalado:', 'deactivate-and-delete') . '</strong> ' . esc_html($installed_date);
    }
    
    // Second: Last activated date
    if (isset($plugin_dates[$plugin_file]['activated'])) {
        $activated_date = date('j M Y g:i A', strtotime($plugin_dates[$plugin_file]['activated']));
        $dates_info[] = '<strong>' . esc_html__('Última activación:', 'deactivate-and-delete') . '</strong> ' . esc_html($activated_date);
    }
    
    // Third: Last deactivated date
    if (isset($plugin_dates[$plugin_file]['deactivated'])) {
        $deactivated_date = date('j M Y g:i A', strtotime($plugin_dates[$plugin_file]['deactivated']));
        $dates_info[] = '<strong>' . esc_html__('Última desactivación:', 'deactivate-and-delete') . '</strong> ' . esc_html($deactivated_date);
    }
    
    // Last: Plugin file size
    $plugin_size = dad_get_plugin_size($plugin_file);
    $size_info = '<strong>' . esc_html__('Tamaño:', 'deactivate-and-delete') . '</strong> ' . esc_html(dad_format_file_size($plugin_size));
    
    // Add the information to plugin meta in the specified order
    if (!empty($dates_info)) {
        $plugin_meta[] = implode(' | ', $dates_info);
    }
    $plugin_meta[] = $size_info;
    
    return $plugin_meta;
}

function dad_handle_quick_action() {
    // Security: Check if this is the correct action
    if (!isset($_GET['action']) || $_GET['action'] !== 'deactivate_and_delete' || !isset($_GET['plugin'])) {
        return;
    }
    
    // Security: Check user capabilities
    if (!current_user_can('delete_plugins') || !current_user_can('deactivate_plugins')) {
        wp_die(__('Lo siento, no tienes permisos para eliminar plugins en este sitio.', 'deactivate-and-delete'));
    }
    
    // Security: Sanitize and validate plugin parameter
    $plugin = sanitize_text_field($_GET['plugin']);
    
    // Security: Verify nonce
    check_admin_referer('deactivate_and_delete_plugin_' . $plugin);
    
    // Security: Validate plugin exists
    $valid_plugins = array_keys(get_plugins());
    if (!in_array($plugin, $valid_plugins, true)) {
        wp_die(__('El plugin especificado no es válido.', 'deactivate-and-delete'));
    }
    
    // Security: Prevent self-deletion
    if (plugin_basename(__FILE__) === $plugin) {
        wp_die(__('No puedes eliminar este plugin mientras está activo.', 'deactivate-and-delete'));
    }

    // Deactivate the plugin
    deactivate_plugins($plugin);
    // Track deactivation date
    dad_track_plugin_deactivation($plugin);

    // Delete the plugin
    $result = delete_plugins(array($plugin));
    
    // Security: Check if deletion was successful
    $redirect_url = admin_url('plugins.php');
    if (is_wp_error($result)) {
        $redirect_url = add_query_arg('error', 'delete_failed', $redirect_url);
    } else {
        $redirect_url = add_query_arg('deactivate_and_delete', '1', $redirect_url);
    }

    wp_safe_redirect($redirect_url);
    exit;
}