Ever been troubleshooting a WordPress site and found yourself constantly switching between browser dev tools, page source, and the admin dashboard just to figure out what’s going on with a single post? You’re not alone – we’ve all done the awkward dance of opening five different tabs trying to piece together post metadata, SEO tags, and WordPress-specific information.

What it does…

The Enhanced WordPress Post Inspector transforms your debugging workflow by bringing all the technical information you need directly to the page you’re viewing. Instead of hunting through page source or jumping between admin screens, you get a clean, organized panel that automatically extracts and displays post IDs, slugs, meta tags, Open Graph data, JSON-LD structured data, and WordPress-specific information like body classes and REST API endpoints. The floating panel is completely configurable – you can choose its position, delay timing, visual theme, and even which data sections to display. It includes handy controls for refreshing data, copying everything to your clipboard, or hiding the panel when you don’t need it, making it feel like a natural extension of your development toolkit.

Why it does it…

Traditional WordPress debugging involves a frustrating workflow of constantly context-switching between different tools and interfaces. You might start by viewing page source to check meta tags, then switch to browser dev tools to inspect elements, then jump to the WordPress admin to verify settings, then back to the frontend to see actual output. This scattered approach is not only time-consuming but also prone to errors since you’re never seeing a complete picture in one place. The Enhanced Post Inspector solves this by implementing a unified debugging interface that extracts data using multiple fallback methods and presents it in real-time on the actual page. This approach is particularly valuable for SEO work, social media integration, and structured data implementation where you need to verify that the correct information is being output exactly as search engines and social platforms will see it.

How it does it…

The plugin demonstrates several sophisticated WordPress development techniques that make it both powerful and reliable:

  • Multi-method data extraction strategy using the extractPostInfo() function that doesn’t rely on a single data source – it tries body classes first, then article elements, then URL parsing, ensuring it captures post information regardless of theme implementation or WordPress configuration
  • Comprehensive meta tag harvesting through targeted DOM queries that systematically collect Open Graph properties (meta[property^="og:"]), Twitter Card data (meta[name^="twitter:"]), and standard meta tags, providing complete social media and SEO debugging coverage
  • Smart JSON-LD structured data parsing that safely handles multiple structured data scripts on a page, gracefully managing parse errors while preserving readable output for complex schema markup
  • Theme-aware responsive design system using PHP-generated CSS with configurable color schemes and position handling, ensuring the inspector integrates seamlessly with any site design without visual conflicts
  • Robust browser compatibility approach using traditional JavaScript patterns (for loops, var declarations, manual DOM manipulation) instead of modern ES6+ features, ensuring the debugging tool works reliably across all browser environments
  • Intelligent permission-based loading that checks user capabilities before outputting any code, combined with admin-only default settings to maintain site security while providing developer access
  • Graceful clipboard integration with automatic fallback to older document.execCommand() methods when modern Clipboard API isn’t available, ensuring the copy functionality works consistently across different browser versions and security contexts

Installation

Save this code as a new snippet in Code Snippets Pro ❤️ or add it to your theme’s functions.php file. Once activated, navigate to Settings → Post Inspector Enhanced in your WordPress admin to configure the panel settings. Enable the inspector, adjust your preferred position and styling, then visit any frontend page while logged in as an administrator to see the debugging panel in action.

View on…

See the code…

<?php


/**
* Title: Enhanced WordPress Post Inspector with Admin Controls [SnipSnip.pro]
* Description: A comprehensive debugging tool that displays post metadata, SEO information, and WordPress data in a floating panel. Features configurable position, admin settings, light/dark themes, enhanced data extraction, and security controls for developers.
* Version: 3.2.1
* Author: Brandon Pfeiffer
* Last Updated: 2025-07-01
* Blog URL: https://snipsnip.pro/s/884
* Gist URL: https://gist.github.com/brandonjp/025592669bce65f7031a00f8b04d5c97
* Code Snippets Cloud: https://codesnippets.cloud/snippet/brandonjp/WordPress-Post-541
* Requirements: WordPress 5.0+, Admin user role
* License: GPL v2 or later
* 
* Features:
* - Floating inspector panel with configurable position
* - Admin settings page with full controls
* - Light/dark theme support
* - Enhanced data extraction with multiple fallback methods
* - Control buttons (refresh, copy, hide)
* - Security and permission controls
* - Query parameter trigger support
* - Post meta data extraction and display
* - Mobile responsive design
* - Browser compatibility with fallback support
* 
* Installation: Add to Code Snippets Pro and activate
* 
* Changelog:
* 3.2.1 - Fixed compatibility issues with saved settings, ensured query parameter defaults
* 3.2.0 - Enhanced browser compatibility, improved data extraction, query parameter support
* 3.0.0 - Complete rewrite with admin controls and configuration options
* 2.0.0 - Added class-based architecture and security improvements
* 1.0.0 - Initial version with basic functionality
*/

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * WordPress Post Inspector Enhanced Class
 */
class WP_Post_Inspector_Enhanced {
    
    const VERSION = '3.2.1';
    const OPTION_NAME = 'wp_post_inspector_enhanced_settings';
    
    private $settings;
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->load_settings();
        $this->init_hooks();
    }
    
    /**
     * Load settings with defaults
     */
    private function load_settings() {
        $defaults = array(
            'enabled' => true,
            'position' => 'bottom-left',
            'delay' => 2000,
            'width' => 400,
            'height' => 500,
            'theme' => 'light',
            'admin_only' => true,
            'query_param_enabled' => false,
            'query_param_name' => 'inspect',
            'query_param_value' => 'yes',
            'show_sections' => array(
                'post_info' => true,
                'wp_data' => true,
                'meta_tags' => true,
                'structured_data' => true,
                'post_meta' => true
            )
        );
        
        // Force reset if there are database conflicts
        $saved_settings = get_option(self::OPTION_NAME, array());
        
        // If saved settings don't have the new keys, reset to ensure compatibility
        if (!isset($saved_settings['query_param_enabled']) || !isset($saved_settings['post_meta'])) {
            delete_option(self::OPTION_NAME);
            $saved_settings = array();
        }
        
        $saved_settings = get_option(self::OPTION_NAME, array());
        $this->settings = wp_parse_args($saved_settings, $defaults);
    }
    
    /**
     * Initialize WordPress hooks
     */
    private function init_hooks() {
        // Admin hooks
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_init', array($this, 'register_settings'));
        
        // Frontend hooks
        if ($this->get_setting('enabled')) {
            add_action('wp_enqueue_scripts', array($this, 'maybe_enqueue_inspector'));
        }
        
        // AJAX hooks for post meta data
        add_action('wp_ajax_wp_post_inspector_get_meta', array($this, 'ajax_get_post_meta'));
        add_action('wp_ajax_nopriv_wp_post_inspector_get_meta', array($this, 'ajax_get_post_meta'));
    }
    
    /**
     * Get setting value
     */
    private function get_setting($key, $default = null) {
        return isset($this->settings[$key]) ? $this->settings[$key] : $default;
    }
    
    /**
     * Check if user can view inspector
     */
    private function can_view_inspector() {
        if (!is_user_logged_in()) {
            return false;
        }
        
        if ($this->get_setting('admin_only')) {
            return current_user_can('manage_options');
        }
        
        return current_user_can('edit_posts');
    }
    
    /**
     * Maybe enqueue inspector on frontend
     */
    public function maybe_enqueue_inspector() {
        // Only load on frontend
        if (is_admin() || wp_doing_ajax()) {
            return;
        }
        
        // Check permissions
        if (!$this->can_view_inspector()) {
            return;
        }
        
        // Check query parameter if enabled
        if ($this->get_setting('query_param_enabled')) {
            $param_name = $this->get_setting('query_param_name', 'inspect');
            $param_value = $this->get_setting('query_param_value', 'yes');
            
            if (!isset($_GET[$param_name]) || $_GET[$param_name] !== $param_value) {
                return;
            }
        }
        
        add_action('wp_footer', array($this, 'output_inspector'), 999);
    }
    
    /**
     * Output the inspector
     */
    public function output_inspector() {
        $position = $this->get_setting('position');
        $width = $this->get_setting('width');
        $height = $this->get_setting('height');
        $delay = $this->get_setting('delay');
        $theme = $this->get_setting('theme');
        $show_sections = $this->get_setting('show_sections');
        
        // Get position styles
        $position_css = $this->get_position_css($position);
        
        // Get theme colors
        $colors = $this->get_theme_colors($theme);
        
        // Convert PHP settings to JavaScript
        $js_config = array(
            'delay' => $delay,
            'showSections' => $show_sections,
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('wp_post_inspector_meta')
        );
        
        ?>
        <style id="wp-post-inspector-enhanced-styles">
            #wp-post-inspector-enhanced {
                position: fixed;
                <?php echo $position_css; ?>
                width: <?php echo $width; ?>px;
                max-height: <?php echo $height; ?>px;
                background: <?php echo $colors['background']; ?>;
                border: 1px solid <?php echo $colors['border']; ?>;
                border-radius: 8px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                z-index: 999999;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-size: 12px;
                overflow: hidden;
                transition: all 0.3s ease;
                opacity: 0;
                transform: translateY(20px);
                color: <?php echo $colors['text']; ?>;
            }
            
            #wp-post-inspector-enhanced.show {
                opacity: 1;
                transform: translateY(0);
            }
            
            #wp-post-inspector-enhanced.collapsed {
                height: 40px;
            }
            
            #wp-post-inspector-enhanced-header {
                background: <?php echo $colors['header_bg']; ?>;
                color: <?php echo $colors['header_text']; ?>;
                padding: 8px 12px;
                cursor: pointer;
                display: flex;
                justify-content: space-between;
                align-items: center;
                user-select: none;
                font-weight: 600;
            }
            
            #wp-post-inspector-enhanced-header:hover {
                background: <?php echo $colors['header_hover']; ?>;
            }
            
            #wp-post-inspector-enhanced-toggle {
                font-size: 14px;
                font-weight: bold;
            }
            
            #wp-post-inspector-enhanced-content {
                max-height: <?php echo ($height - 80); ?>px;
                overflow-y: auto;
                padding: 0;
            }
            
            #wp-post-inspector-enhanced table {
                width: 100%;
                border-collapse: collapse;
                margin: 0;
            }
            
            #wp-post-inspector-enhanced th,
            #wp-post-inspector-enhanced td {
                padding: 6px 8px;
                text-align: left;
                border-bottom: 1px solid <?php echo $colors['border']; ?>;
                word-wrap: break-word;
                max-width: 200px;
            }
            
            #wp-post-inspector-enhanced th {
                background: <?php echo $colors['th_bg']; ?>;
                font-weight: 600;
                width: 30%;
                font-size: 11px;
            }
            
            #wp-post-inspector-enhanced td {
                font-size: 11px;
                font-family: monospace;
            }
            
            #wp-post-inspector-enhanced tr:hover {
                background: <?php echo $colors['row_hover']; ?>;
            }
            
            .wp-inspector-enhanced-section {
                background: <?php echo $colors['section_bg']; ?> !important;
                font-weight: bold !important;
                color: <?php echo $colors['section_text']; ?> !important;
            }
            
            .wp-inspector-enhanced-empty {
                color: <?php echo $colors['empty_text']; ?>;
                font-style: italic;
            }
            
            .wp-inspector-enhanced-json {
                max-height: 100px;
                overflow-y: auto;
                background: <?php echo $colors['json_bg']; ?>;
                padding: 4px;
                border-radius: 3px;
                white-space: pre-wrap;
                font-size: 10px;
            }
            
            .wp-inspector-enhanced-controls {
                padding: 8px;
                background: <?php echo $colors['controls_bg']; ?>;
                border-top: 1px solid <?php echo $colors['border']; ?>;
                font-size: 10px;
            }
            
            .wp-inspector-enhanced-controls button {
                background: <?php echo $colors['button_bg']; ?>;
                color: <?php echo $colors['button_text']; ?>;
                border: 1px solid <?php echo $colors['button_border']; ?>;
                padding: 4px 8px;
                margin-right: 4px;
                border-radius: 3px;
                cursor: pointer;
                font-size: 10px;
            }
            
            .wp-inspector-enhanced-controls button:hover {
                background: <?php echo $colors['button_hover']; ?>;
            }
            
            @media (max-width: 768px) {
                #wp-post-inspector-enhanced {
                    width: calc(100vw - 40px) !important;
                    max-width: 350px;
                }
            }
        </style>
        
        <div id="wp-post-inspector-enhanced">
            <div id="wp-post-inspector-enhanced-header">
                <span>WP Post Inspector Enhanced v<?php echo self::VERSION; ?></span>
                <span id="wp-post-inspector-enhanced-toggle">−</span>
            </div>
            <div id="wp-post-inspector-enhanced-content">
                <table>
                    <tbody id="wp-post-inspector-enhanced-table"></tbody>
                </table>
                <div class="wp-inspector-enhanced-controls">
                    <button onclick="wpPostInspectorEnhanced.refresh()">Refresh</button>
                    <button onclick="wpPostInspectorEnhanced.copy()">Copy Data</button>
                    <button onclick="wpPostInspectorEnhanced.hide()">Hide</button>
                </div>
            </div>
        </div>
        
        <script id="wp-post-inspector-enhanced-script">
        (function() {
            'use strict';
            
            var config = <?php echo json_encode($js_config); ?>;
            var inspectorPanel = null;
            var isCollapsed = false;
            
            function initPostInspector() {
                setTimeout(function() {
                    inspectorPanel = document.getElementById('wp-post-inspector-enhanced');
                    if (!inspectorPanel) return;
                    
                    setupEventListeners();
                    populateInspectorData();
                    showPanel();
                }, config.delay);
            }
            
            function setupEventListeners() {
                var header = document.getElementById('wp-post-inspector-enhanced-header');
                var toggle = document.getElementById('wp-post-inspector-enhanced-toggle');
                
                if (header && toggle) {
                    header.addEventListener('click', function() {
                        isCollapsed = !isCollapsed;
                        inspectorPanel.classList.toggle('collapsed', isCollapsed);
                        toggle.textContent = isCollapsed ? '+' : '−';
                    });
                }
            }
            
            function extractPostInfo() {
                var postData = {};
                var bodyClasses = document.body.className;
                
                // Post ID extraction
                var postIdMatch = bodyClasses.match(/postid-(\d+)/);
                if (postIdMatch) {
                    postData['Post ID'] = postIdMatch[1];
                }
                
                var article = document.querySelector('article[id*="post-"]');
                if (article && !postData['Post ID']) {
                    var articleIdMatch = article.id.match(/post-(\d+)/);
                    if (articleIdMatch) {
                        postData['Post ID'] = articleIdMatch[1];
                    }
                }
                
                // Post slug
                var url = window.location.pathname;
                var slugMatch = url.match(/\/([^\/]+)\/?$/);
                if (slugMatch && slugMatch[1] !== '' && !slugMatch[1].match(/^\d+$/)) {
                    postData['Post Slug'] = slugMatch[1];
                }
                
                // Post type
                var postTypeMatch = bodyClasses.match(/single-([a-zA-Z_-]+)/);
                if (postTypeMatch) {
                    postData['Post Type'] = postTypeMatch[1];
                }
                
                // Title
                var titleElement = document.querySelector('h1.entry-title, h1.post-title, .entry-header h1, article h1');
                if (titleElement) {
                    postData['Post Title'] = titleElement.textContent.trim();
                }
                
                // Author
                var authorElement = document.querySelector('.author .fn, .by-author, .entry-author, [rel="author"]');
                if (authorElement) {
                    postData['Author'] = authorElement.textContent.trim();
                }
                
                // Published date
                var dateElement = document.querySelector('time[datetime], .entry-date, .published');
                if (dateElement) {
                    postData['Published'] = dateElement.getAttribute('datetime') || dateElement.textContent.trim();
                }
                
                // Categories and tags
                var categories = [];
                var tags = [];
                bodyClasses.split(' ').forEach(function(className) {
                    if (className.indexOf('category-') === 0) {
                        categories.push(className.replace('category-', ''));
                    }
                    if (className.indexOf('tag-') === 0) {
                        tags.push(className.replace('tag-', ''));
                    }
                });
                
                if (categories.length > 0) {
                    postData['Categories'] = categories.join(', ');
                }
                if (tags.length > 0) {
                    postData['Tags'] = tags.join(', ');
                }
                
                return postData;
            }
            
            function extractMetaTags() {
                var metaData = {};
                
                // Open Graph tags
                var ogTags = document.querySelectorAll('meta[property^="og:"]');
                for (var i = 0; i < ogTags.length; i++) {
                    var property = ogTags[i].getAttribute('property');
                    var content = ogTags[i].getAttribute('content');
                    if (content) {
                        metaData[property] = content;
                    }
                }
                
                // Twitter tags
                var twitterTags = document.querySelectorAll('meta[name^="twitter:"]');
                for (var i = 0; i < twitterTags.length; i++) {
                    var name = twitterTags[i].getAttribute('name');
                    var content = twitterTags[i].getAttribute('content');
                    if (content) {
                        metaData[name] = content;
                    }
                }
                
                // Standard meta tags
                var standardTags = ['description', 'keywords', 'author'];
                for (var i = 0; i < standardTags.length; i++) {
                    var tag = document.querySelector('meta[name="' + standardTags[i] + '"]');
                    if (tag && tag.getAttribute('content')) {
                        metaData[standardTags[i]] = tag.getAttribute('content');
                    }
                }
                
                return metaData;
            }
            
            function extractStructuredData() {
                var structuredData = {};
                var jsonLdScripts = document.querySelectorAll('script[type="application/ld+json"]');
                
                for (var i = 0; i < jsonLdScripts.length; i++) {
                    try {
                        var data = JSON.parse(jsonLdScripts[i].textContent);
                        structuredData['JSON-LD ' + (i + 1)] = JSON.stringify(data, null, 2);
                    } catch (e) {
                        structuredData['JSON-LD ' + (i + 1)] = 'Invalid JSON';
                    }
                }
                
                return structuredData;
            }
            
            function extractWPSpecificData() {
                var wpData = {};
                
                wpData['Current URL'] = window.location.href;
                wpData['User Agent'] = navigator.userAgent;
                wpData['Viewport'] = window.innerWidth + 'x' + window.innerHeight;
                
                var restLink = document.querySelector('link[rel="https://api.w.org/"]');
                if (restLink) {
                    wpData['REST API'] = restLink.getAttribute('href');
                }
                
                var commentPostId = document.querySelector('input[name="comment_post_ID"]');
                if (commentPostId) {
                    wpData['Comment Post ID'] = commentPostId.value;
                }
                
                var wpVersion = document.querySelector('meta[name="generator"][content*="WordPress"]');
                if (wpVersion) {
                    wpData['WP Version'] = wpVersion.getAttribute('content');
                }
                
                wpData['Body Classes'] = document.body.className;
                
                return wpData;
            }
            
            function extractPostMeta(callback) {
                // Extract post ID first
                var postId = null;
                var bodyClasses = document.body.className;
                var postIdMatch = bodyClasses.match(/postid-(\d+)/);
                if (postIdMatch) {
                    postId = postIdMatch[1];
                }
                
                if (!postId) {
                    var article = document.querySelector('article[id*="post-"]');
                    if (article) {
                        var articleIdMatch = article.id.match(/post-(\d+)/);
                        if (articleIdMatch) {
                            postId = articleIdMatch[1];
                        }
                    }
                }
                
                if (!postId) {
                    callback({});
                    return;
                }
                
                // Make AJAX request to get post meta
                var xhr = new XMLHttpRequest();
                xhr.open('POST', config.ajaxUrl, true);
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                
                xhr.onreadystatechange = function() {
                    if (xhr.readyState === 4) {
                        if (xhr.status === 200) {
                            try {
                                var response = JSON.parse(xhr.responseText);
                                if (response.success) {
                                    callback(response.data);
                                } else {
                                    callback({});
                                }
                            } catch (e) {
                                callback({});
                            }
                        } else {
                            callback({});
                        }
                    }
                };
                
                var params = 'action=wp_post_inspector_get_meta&post_id=' + postId + '&nonce=' + config.nonce;
                xhr.send(params);
            }
            
            function populateInspectorData() {
                var tableBody = document.getElementById('wp-post-inspector-enhanced-table');
                if (!tableBody) return;
                
                tableBody.innerHTML = '';
                
                // Add loading indicator for post meta if enabled
                if (config.showSections['post_meta']) {
                    var loadingRow = document.createElement('tr');
                    var loadingCell = document.createElement('td');
                    loadingCell.colSpan = 2;
                    loadingCell.className = 'wp-inspector-enhanced-section';
                    loadingCell.textContent = 'Post Meta Data (Loading...)';
                    loadingRow.appendChild(loadingCell);
                    tableBody.appendChild(loadingRow);
                }
                
                var sections = [
                    { key: 'post_info', title: 'Post Information', data: extractPostInfo() },
                    { key: 'wp_data', title: 'WordPress Data', data: extractWPSpecificData() },
                    { key: 'meta_tags', title: 'Meta Tags', data: extractMetaTags() },
                    { key: 'structured_data', title: 'Structured Data', data: extractStructuredData() }
                ];
                
                for (var i = 0; i < sections.length; i++) {
                    var section = sections[i];
                    if (config.showSections[section.key] && Object.keys(section.data).length > 0) {
                        addSectionToTable(tableBody, section.title, section.data);
                    }
                }
                
                // Load post meta data asynchronously
                if (config.showSections['post_meta']) {
                    extractPostMeta(function(postMetaData) {
                        // Remove loading indicator
                        var loadingRows = tableBody.querySelectorAll('tr');
                        for (var i = 0; i < loadingRows.length; i++) {
                            var cell = loadingRows[i].querySelector('td');
                            if (cell && cell.textContent.indexOf('Loading...') !== -1) {
                                tableBody.removeChild(loadingRows[i]);
                                break;
                            }
                        }
                        
                        // Add post meta section
                        if (Object.keys(postMetaData).length > 0) {
                            addSectionToTable(tableBody, 'Post Meta Data', postMetaData);
                        }
                        
                        // Check if we need the no data message
                        checkNoDataMessage();
                    });
                } else {
                    // Check if we need the no data message
                    checkNoDataMessage();
                }
                
                function checkNoDataMessage() {
                    if (tableBody.children.length === 0) {
                        var noDataRow = document.createElement('tr');
                        var noDataCell = document.createElement('td');
                        noDataCell.colSpan = 2;
                        noDataCell.className = 'wp-inspector-enhanced-empty';
                        noDataCell.textContent = 'No data found or all sections disabled';
                        noDataRow.appendChild(noDataCell);
                        tableBody.appendChild(noDataRow);
                    }
                }
            }
            
            function addSectionToTable(tableBody, title, data) {
                // Section header
                var sectionRow = document.createElement('tr');
                var sectionCell = document.createElement('td');
                sectionCell.colSpan = 2;
                sectionCell.className = 'wp-inspector-enhanced-section';
                sectionCell.textContent = title;
                sectionRow.appendChild(sectionCell);
                tableBody.appendChild(sectionRow);
                
                // Data rows
                var keys = Object.keys(data);
                for (var i = 0; i < keys.length; i++) {
                    var key = keys[i];
                    var value = data[key];
                    
                    var row = document.createElement('tr');
                    var keyCell = document.createElement('th');
                    var valueCell = document.createElement('td');
                    
                    keyCell.textContent = key;
                    
                    if (key.indexOf('JSON-LD') === 0 && typeof value === 'string' && value.length > 100) {
                        var jsonDiv = document.createElement('div');
                        jsonDiv.className = 'wp-inspector-enhanced-json';
                        jsonDiv.textContent = value;
                        valueCell.appendChild(jsonDiv);
                    } else {
                        valueCell.textContent = value || 'N/A';
                        if (!value) {
                            valueCell.className = 'wp-inspector-enhanced-empty';
                        }
                    }
                    
                    row.appendChild(keyCell);
                    row.appendChild(valueCell);
                    tableBody.appendChild(row);
                }
            }
            
            function showPanel() {
                if (inspectorPanel) {
                    inspectorPanel.classList.add('show');
                }
            }
            
            function hidePanel() {
                if (inspectorPanel) {
                    inspectorPanel.style.display = 'none';
                }
            }
            
            function copyData() {
                var sections = [
                    { title: 'Post Information', data: extractPostInfo() },
                    { title: 'WordPress Data', data: extractWPSpecificData() },
                    { title: 'Meta Tags', data: extractMetaTags() },
                    { title: 'Structured Data', data: extractStructuredData() }
                ];
                
                // Add post meta data if available
                if (config.showSections['post_meta']) {
                    extractPostMeta(function(postMetaData) {
                        if (Object.keys(postMetaData).length > 0) {
                            sections.push({ title: 'Post Meta Data', data: postMetaData });
                        }
                        performCopy(sections);
                    });
                    return;
                }
                
                performCopy(sections);
            }
            
            function performCopy(sections) {
                var copyText = 'WordPress Post Inspector Enhanced Data\n';
                copyText += '=====================================\n\n';
                
                for (var i = 0; i < sections.length; i++) {
                    var section = sections[i];
                    var keys = Object.keys(section.data);
                    if (keys.length > 0) {
                        copyText += section.title + ':\n';
                        copyText += Array(section.title.length + 2).join('-') + '\n';
                        for (var j = 0; j < keys.length; j++) {
                            copyText += keys[j] + ': ' + section.data[keys[j]] + '\n';
                        }
                        copyText += '\n';
                    }
                }
                
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(copyText).then(function() {
                        alert('Inspector data copied to clipboard!');
                    }).catch(function() {
                        alert('Failed to copy data to clipboard');
                    });
                } else {
                    // Fallback for older browsers
                    var textArea = document.createElement('textarea');
                    textArea.value = copyText;
                    document.body.appendChild(textArea);
                    textArea.select();
                    try {
                        document.execCommand('copy');
                        alert('Inspector data copied to clipboard!');
                    } catch (err) {
                        alert('Failed to copy data to clipboard');
                    }
                    document.body.removeChild(textArea);
                }
            }
            
            // Global API
            window.wpPostInspectorEnhanced = {
                refresh: populateInspectorData,
                hide: hidePanel,
                copy: copyData,
                show: showPanel
            };
            
            // Initialize
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', initPostInspector);
            } else {
                initPostInspector();
            }
            
        })();
        </script>
        <?php
    }
    
    /**
     * Get position CSS
     */
    private function get_position_css($position) {
        switch ($position) {
            case 'top-left':
                return 'top: 20px; left: 20px;';
            case 'top-right':
                return 'top: 20px; right: 20px;';
            case 'bottom-right':
                return 'bottom: 20px; right: 20px;';
            case 'bottom-left':
            default:
                return 'bottom: 20px; left: 20px;';
        }
    }
    
    /**
     * Get theme colors
     */
    private function get_theme_colors($theme) {
        $colors = array(
            'light' => array(
                'background' => '#fff',
                'text' => '#333',
                'border' => '#ccc',
                'header_bg' => '#23282d',
                'header_text' => '#fff',
                'header_hover' => '#32373c',
                'th_bg' => '#f8f9fa',
                'row_hover' => '#f0f8ff',
                'section_bg' => '#e8f4f8',
                'section_text' => '#0073aa',
                'empty_text' => '#999',
                'json_bg' => '#f4f4f4',
                'controls_bg' => '#f8f9fa',
                'button_bg' => '#0073aa',
                'button_text' => '#fff',
                'button_border' => '#0073aa',
                'button_hover' => '#005a87'
            ),
            'dark' => array(
                'background' => '#1e1e1e',
                'text' => '#e0e0e0',
                'border' => '#444',
                'header_bg' => '#0073aa',
                'header_text' => '#fff',
                'header_hover' => '#005a87',
                'th_bg' => '#2a2a2a',
                'row_hover' => '#2a2a2a',
                'section_bg' => '#0073aa',
                'section_text' => '#fff',
                'empty_text' => '#888',
                'json_bg' => '#2a2a2a',
                'controls_bg' => '#2a2a2a',
                'button_bg' => '#0073aa',
                'button_text' => '#fff',
                'button_border' => '#0073aa',
                'button_hover' => '#005a87'
            )
        );
        
        return isset($colors[$theme]) ? $colors[$theme] : $colors['light'];
    }
    
    /**
     * AJAX handler to get post meta data
     */
    public function ajax_get_post_meta() {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'], 'wp_post_inspector_meta')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        // Check permissions
        if (!$this->can_view_inspector()) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $post_id = intval($_POST['post_id']);
        if (!$post_id || !get_post($post_id)) {
            wp_send_json_error('Invalid post ID');
            return;
        }
        
        // Get all post meta
        $post_meta = get_post_meta($post_id);
        $filtered_meta = array();
        
        // Filter and format meta data
        foreach ($post_meta as $key => $values) {
            // Skip private meta keys (starting with _) unless they're common ones
            if (strpos($key, '_') === 0) {
                $common_private_keys = array(
                    '_edit_last',
                    '_edit_lock', 
                    '_wp_page_template',
                    '_thumbnail_id',
                    '_wp_attachment_metadata',
                    '_wp_attached_file'
                );
                if (!in_array($key, $common_private_keys)) {
                    continue;
                }
            }
            
            // Handle single vs multiple values
            if (count($values) === 1) {
                $value = $values[0];
                // Try to unserialize if it looks like serialized data
                if (is_serialized($value)) {
                    $unserialized = maybe_unserialize($value);
                    if (is_array($unserialized) || is_object($unserialized)) {
                        $value = json_encode($unserialized, JSON_PRETTY_PRINT);
                    }
                }
                $filtered_meta[$key] = $value;
            } else {
                $filtered_meta[$key] = implode(', ', $values);
            }
        }
        
        // Limit output if too many meta keys
        if (count($filtered_meta) > 50) {
            $filtered_meta = array_slice($filtered_meta, 0, 50, true);
            $filtered_meta['_truncated'] = '... (' . (count($post_meta) - 50) . ' more meta keys not shown)';
        }
        
        wp_send_json_success($filtered_meta);
    }
    
    /**
     * Add admin menu
     */
    public function add_admin_menu() {
        add_options_page(
            'Post Inspector Enhanced Settings',
            'Post Inspector Enhanced', 
            'manage_options',
            'wp-post-inspector-enhanced',
            array($this, 'admin_page')
        );
    }
    
    /**
     * Register settings
     */
    public function register_settings() {
        register_setting('wp_post_inspector_enhanced_settings', self::OPTION_NAME);
    }
    
    /**
     * Admin page
     */
    public function admin_page() {
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.'));
        }
        
        // Handle form submission
        if (isset($_POST['submit'])) {
            $new_settings = array();
            $new_settings['enabled'] = isset($_POST[self::OPTION_NAME]['enabled']);
            $new_settings['position'] = sanitize_text_field($_POST[self::OPTION_NAME]['position']);
            $new_settings['delay'] = intval($_POST[self::OPTION_NAME]['delay']);
            $new_settings['width'] = intval($_POST[self::OPTION_NAME]['width']);
            $new_settings['height'] = intval($_POST[self::OPTION_NAME]['height']);
            $new_settings['theme'] = sanitize_text_field($_POST[self::OPTION_NAME]['theme']);
            $new_settings['admin_only'] = isset($_POST[self::OPTION_NAME]['admin_only']);
            $new_settings['query_param_enabled'] = isset($_POST[self::OPTION_NAME]['query_param_enabled']);
            $new_settings['query_param_name'] = sanitize_text_field($_POST[self::OPTION_NAME]['query_param_name']);
            $new_settings['query_param_value'] = sanitize_text_field($_POST[self::OPTION_NAME]['query_param_value']);
            
            $new_settings['show_sections'] = array(
                'post_info' => isset($_POST[self::OPTION_NAME]['show_sections']['post_info']),
                'wp_data' => isset($_POST[self::OPTION_NAME]['show_sections']['wp_data']),
                'meta_tags' => isset($_POST[self::OPTION_NAME]['show_sections']['meta_tags']),
                'structured_data' => isset($_POST[self::OPTION_NAME]['show_sections']['structured_data']),
                'post_meta' => isset($_POST[self::OPTION_NAME]['show_sections']['post_meta'])
            );
            
            update_option(self::OPTION_NAME, $new_settings);
            $this->settings = $new_settings;
            
            echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully!</p></div>';
        }
        
        $settings = $this->settings;
        ?>
        <div class="wrap">
            <h1>WordPress Post Inspector Enhanced Settings</h1>
            <form method="post" action="">
                <?php wp_nonce_field('update-options'); ?>
                <table class="form-table">
                    <tr>
                        <th scope="row">Enable Inspector</th>
                        <td>
                            <label for="enabled">
                                <input type="checkbox" id="enabled" name="<?php echo self::OPTION_NAME; ?>[enabled]" value="1" <?php checked($settings['enabled'], true); ?> />
                                Enable the post inspector on frontend pages
                            </label>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Position</th>
                        <td>
                            <select name="<?php echo self::OPTION_NAME; ?>[position]">
                                <option value="bottom-left" <?php selected($settings['position'], 'bottom-left'); ?>>Bottom Left</option>
                                <option value="bottom-right" <?php selected($settings['position'], 'bottom-right'); ?>>Bottom Right</option>
                                <option value="top-left" <?php selected($settings['position'], 'top-left'); ?>>Top Left</option>
                                <option value="top-right" <?php selected($settings['position'], 'top-right'); ?>>Top Right</option>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Display Delay (ms)</th>
                        <td>
                            <input type="number" name="<?php echo self::OPTION_NAME; ?>[delay]" value="<?php echo $settings['delay']; ?>" min="0" max="10000" step="100" />
                            <p class="description">Delay before inspector appears (in milliseconds)</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Panel Width (px)</th>
                        <td>
                            <input type="number" name="<?php echo self::OPTION_NAME; ?>[width]" value="<?php echo $settings['width']; ?>" min="300" max="800" step="10" />
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Panel Height (px)</th>
                        <td>
                            <input type="number" name="<?php echo self::OPTION_NAME; ?>[height]" value="<?php echo $settings['height']; ?>" min="300" max="800" step="10" />
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Theme</th>
                        <td>
                            <select name="<?php echo self::OPTION_NAME; ?>[theme]">
                                <option value="light" <?php selected($settings['theme'], 'light'); ?>>Light Theme</option>
                                <option value="dark" <?php selected($settings['theme'], 'dark'); ?>>Dark Theme</option>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Access Control</th>
                        <td>
                            <label for="admin_only">
                                <input type="checkbox" id="admin_only" name="<?php echo self::OPTION_NAME; ?>[admin_only]" value="1" <?php checked($settings['admin_only'], true); ?> />
                                Only show to administrators
                            </label>
                            <p class="description">If unchecked, will show to any user who can edit posts</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Query Parameter Trigger</th>
                        <td>
                            <label for="query_param_enabled">
                                <input type="checkbox" id="query_param_enabled" name="<?php echo self::OPTION_NAME; ?>[query_param_enabled]" value="1" <?php checked($settings['query_param_enabled'], true); ?> />
                                Only show inspector when specific query parameter is present
                            </label>
                            <p class="description">When enabled, inspector will only appear when the URL contains the specified parameter</p>
                            <br>
                            <label for="query_param_name">Parameter Name:</label>
                            <input type="text" id="query_param_name" name="<?php echo self::OPTION_NAME; ?>[query_param_name]" value="<?php echo esc_attr($settings['query_param_name']); ?>" placeholder="inspect" style="width: 150px;" />
                            <br><br>
                            <label for="query_param_value">Parameter Value:</label>
                            <input type="text" id="query_param_value" name="<?php echo self::OPTION_NAME; ?>[query_param_value]" value="<?php echo esc_attr($settings['query_param_value']); ?>" placeholder="yes" style="width: 150px;" />
                            <p class="description">Example: If name is "inspect" and value is "yes", use URL like: <code>yoursite.com/page/?inspect=yes</code></p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Sections to Display</th>
                        <td>
                            <fieldset>
                                <label for="show_post_info">
                                    <input type="checkbox" id="show_post_info" name="<?php echo self::OPTION_NAME; ?>[show_sections][post_info]" value="1" <?php checked($settings['show_sections']['post_info'], true); ?> />
                                    Post Information
                                </label><br>
                                <label for="show_wp_data">
                                    <input type="checkbox" id="show_wp_data" name="<?php echo self::OPTION_NAME; ?>[show_sections][wp_data]" value="1" <?php checked($settings['show_sections']['wp_data'], true); ?> />
                                    WordPress Data
                                </label><br>
                                <label for="show_meta_tags">
                                    <input type="checkbox" id="show_meta_tags" name="<?php echo self::OPTION_NAME; ?>[show_sections][meta_tags]" value="1" <?php checked($settings['show_sections']['meta_tags'], true); ?> />
                                    Meta Tags
                                </label><br>
                                <label for="show_structured_data">
                                    <input type="checkbox" id="show_structured_data" name="<?php echo self::OPTION_NAME; ?>[show_sections][structured_data]" value="1" <?php checked($settings['show_sections']['structured_data'], true); ?> />
                                    Structured Data
                                </label><br>
                                <label for="show_post_meta">
                                    <input type="checkbox" id="show_post_meta" name="<?php echo self::OPTION_NAME; ?>[show_sections][post_meta]" value="1" <?php checked($settings['show_sections']['post_meta'], true); ?> />
                                    Post Meta Data
                                </label>
                            </fieldset>
                        </td>
                    </tr>
                </table>
                
                <p class="submit">
                    <input type="submit" name="submit" id="submit" class="button-primary" value="Save Changes" />
                </p>
            </form>
            
            <div class="card" style="margin-top: 20px;">
                <h2>Current Status</h2>
                <p><strong>Inspector Status:</strong> 
                    <span style="color: <?php echo $settings['enabled'] ? '#00a32a' : '#d63638'; ?>;">
                        <?php echo $settings['enabled'] ? 'Active' : 'Disabled'; ?>
                    </span>
                </p>
                <p><strong>Access Level:</strong> <?php echo $settings['admin_only'] ? 'Administrators Only' : 'All Users Who Can Edit Posts'; ?></p>
                <p><strong>Query Parameter:</strong> <?php echo $settings['query_param_enabled'] ? 'Enabled (?'.$settings['query_param_name'].'='.$settings['query_param_value'].')' : 'Disabled (Always show)'; ?></p>
                <p><strong>Active Sections:</strong> <?php echo count(array_filter($settings['show_sections'])); ?> of 5 sections enabled</p>
            </div>
        </div>
        <?php
    }
}

// Initialize the plugin
new WP_Post_Inspector_Enhanced();

See the code on github…

Leave a Reply

Your email address will not be published. Required fields are marked *