HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux bsx-1-dev 6.8.0-101-generic #101-Ubuntu SMP PREEMPT_DYNAMIC Mon Feb 9 10:15:05 UTC 2026 x86_64
User: www-data (33)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/memberpress/app/helpers/MeprDrmDebugHelper.php
<?php

if (!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

/**
 * Helper class for DRM debug functionality
 *
 * Provides methods to display and analyze DRM-related information in the WordPress site health section
 */
class MeprDrmDebugHelper
{
    /**
     * Adds DRM debug information to the WordPress site health page
     *
     * @param array $tests Array of existing site health tests.
     *
     * @return array Modified array of site health tests including DRM debug information
     */
    public static function site_health_debug_status($tests)
    {
        if (!isset($_GET['mepr_drm_debug']) || !MeprUtils::is_logged_in_and_an_admin()) {
            return $tests;
        }

        $tests['direct']['mepr_drm_debug'] = [
            'label' => __('MemberPress - DRM Info', 'memberpress'),
            'test' => ['MeprDrmDebugHelper', 'run_drm_debug'],
        ];

        return $tests;
    }

    /**
     * Main method to generate the DRM debug report
     *
     * Combines all debug sections and adds download functionality
     *
     * @return array Site health test result array containing the debug information
     */
    public static function run_drm_debug()
    {
        $content = '<div id="mepr-drm-debug-content" class="mepr-drm-debug">';

        // Build content from separate sections.
        $content .= self::get_drm_status_section();
        $content .= self::get_app_fee_section();
        $content .= self::get_drm_options_section();
        $content .= self::get_drm_events_section();
        $content .= self::get_debug_styles();
        $content .= '</div>';

        // Get HTML and JS for the download button.
        $generate_report_parts = self::generate_html_js_button();
        if (!empty($generate_report_parts['html']) && !empty($generate_report_parts['js'])) {
            $content = $generate_report_parts['html'] . $content . $generate_report_parts['js'];
        }

        return [
            'label' => __('DRM Debug Information', 'memberpress'),
            'status' => 'critical',
            'badge' => [
                'label' => 'DRM',
                'color' => 'orange',
            ],
            'description' => $content,
            'actions' => '',
            'test' => 'run_drm_debug',
        ];
    }

    /**
     * Generates the HTML button and JavaScript for downloading the debug report
     *
     * @return array {
     * Array containing button HTML and JavaScript
     *
     * @type string $html HTML for the download button
     * @type string $js   JavaScript for download functionality
     * }
     */
    private static function generate_html_js_button()
    {
        $button_html = '<div style="text-align: right; margin-bottom: 5px;">';
        $button_html .= sprintf(
            '<button class="button button-small copy-debug" onclick="meprDownloadDebug(this)" data-target="mepr-drm-debug-content">%s</button>',
            esc_html__('Download', 'memberpress')
        );
        $button_html .= '</div>';

        $button_js = '
        <script>
        function meprDownloadDebug(button) {
            const debugId = button.getAttribute("data-target");
            const debugContent = document.getElementById(debugId);

            if (! debugContent) {
                console.error("Debug content not found");
                return;
            }

            try {
                // Clone the content so we can modify it without affecting the page.
                const contentClone = debugContent.cloneNode(true);

                // Remove all copy buttons.
                const copyButtons = contentClone.querySelectorAll(".copy-sql, .copy-debug");
                copyButtons.forEach(btn => btn.remove());

                // Sanitize content - remove any script tags.
                const scripts = contentClone.getElementsByTagName("script");
                while (scripts.length > 0) {
                    scripts[0].parentNode.removeChild(scripts[0]);
                }

                // Create the HTML document.
                const html = `
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <meta charset="utf-8">
                        <meta name="robots" content="noindex, nofollow">
                        <meta name="viewport" content="width=device-width, initial-scale=1">
                        <title>DRM Debug Information</title>
                        <style>
                            :root {
                                --border-color: #c3c4c7;
                                --background-light: #f6f7f7;
                                --text-primary: #1d2327;
                                --text-secondary: #50575e;
                                --accent-color: #2271b1;
                                --spacing-sm: 8px;
                                --spacing-md: 16px;
                                --spacing-lg: 24px;
                                --spacing-xl: 32px;
                            }

                            body {
                                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
                                line-height: 1.5;
                                padding: var(--spacing-xl);
                                max-width: 1200px;
                                margin: 0 auto;
                                background: #f0f0f1;
                                color: var(--text-primary);
                            }
                            .mepr-drm-debug {
                                background: #fff;
                                border: 1px solid var(--border-color);
                                box-shadow: 0 1px 1px rgba(0,0,0,.04);
                                padding: var(--spacing-xl);
                                border-radius: 4px;
                            }
                            table {
                                width: 100%;
                                border-collapse: separate;
                                border-spacing: 0;
                                margin: var(--spacing-lg) 0 var(--spacing-xl);
                                background: #fff;
                                border: 1px solid var(--border-color);
                                box-shadow: 0 1px 1px rgba(0,0,0,.04);
                                border-radius: 4px;
                                overflow: hidden;
                            }
                            th, td {
                                padding: var(--spacing-md);
                                text-align: left;
                                border-bottom: 1px solid var(--border-color);
                                border-right: 1px solid var(--border-color);
                                vertical-align: top;
                            }
                            th:last-child, td:last-child { border-right: none; }
                            tr:last-child td, tr:last-child th { border-bottom: none; }
                            th {
                                background: var(--background-light);
                                font-weight: 600;
                                color: var(--text-primary);
                            }
                            table.two-col th { width: 30%; }
                            table.three-col th { width: auto; }
                            .striped tbody tr:nth-child(odd) { background-color: #fff; }
                            .striped tbody tr:nth-child(even) { background-color: var(--background-light); }
                            h1 {
                                color: var(--text-primary);
                                font-size: 24px;
                                margin: 0 0 var(--spacing-xl);
                                padding-bottom: var(--spacing-md);
                                border-bottom: 2px solid var(--accent-color);
                                font-weight: 600;
                            }
                            h2 {
                                color: var(--text-primary);
                                font-size: 20px;
                                margin: var(--spacing-xl) 0 var(--spacing-md);
                                padding-bottom: var(--spacing-sm);
                                border-bottom: 1px solid var(--border-color);
                                font-weight: 600;
                            }
                            h3 {
                                color: var(--text-primary);
                                font-size: 16px;
                                margin: var(--spacing-lg) 0 var(--spacing-md);
                                font-weight: 600;
                            }
                            .mepr-serialized-data {
                                margin: var(--spacing-sm) 0;
                                padding: var(--spacing-md);
                                background: var(--background-light);
                                border: 1px solid var(--border-color);
                                border-radius: 3px;
                            }
                            .mepr-data-row {
                                margin: var(--spacing-sm) 0;
                                line-height: 1.5;
                                display: flex;
                                align-items: flex-start;
                            }
                            .mepr-key {
                                font-weight: 600;
                                margin-right: var(--spacing-md);
                                color: var(--accent-color);
                                min-width: 120px;
                            }
                            .mepr-meta {
                                color: var(--text-secondary);
                                font-size: 12px;
                                margin-top: var(--spacing-xl);
                                padding-top: var(--spacing-md);
                                border-top: 1px solid var(--border-color);
                            }
                            @media print {
                                body {
                                    background: #fff;
                                    padding: var(--spacing-md);
                                }
                                .mepr-drm-debug {
                                    border: none;
                                    box-shadow: none;
                                    padding: 0;
                                }
                                table {
                                    box-shadow: none;
                                    border: 1px solid #000;
                                }
                                th, td {
                                    border-color: #000;
                                }
                                .mepr-serialized-data {
                                    border: 1px solid #000;
                                }
                            }
                        </style>
                    </head>
                    <body>
                        <h1>DRM Debug Information</h1>
                        ${contentClone.innerHTML}
                        <div class="mepr-meta">
                            Generated: ${new Date().toLocaleString()}
                            <br>
                            User ID: ' . (int) get_current_user_id() . '
                        </div>
                    </body>
                    </html>`;

                // Create blob and download with error handling
                try {
                    const blob = new Blob([html], { type: "text/html" });
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement("a");
                    a.href = url;
                    a.download = "memberpress-drm-debug-" + new Date().toISOString().split("T")[0] + ".html";
                    document.body.appendChild(a);
                    a.click();
                    window.URL.revokeObjectURL(url);
                    document.body.removeChild(a);

                    // Show feedback.
                    const originalText = button.textContent;
                    button.textContent = "' . esc_js(__('Downloaded!', 'memberpress')) . '";
                    setTimeout(function() {
                        button.textContent = originalText;
                    }, 2000);
                } catch (error) {
                    console.error("Download failed:", error);
                    button.textContent = "' . esc_js(__('Download Failed', 'memberpress')) . '";
                    setTimeout(function() {
                        button.textContent = originalText;
                    }, 2000);
                }
            } catch (error) {
                console.error("Content processing failed:", error);
            }
        }
        </script>';

        return [
            'html' => $button_html,
            'js' => $button_js,
        ];
    }

    /**
     * Generates the DRM status section of the debug report
     *
     * Shows current DRM status, license information, and active event details
     *
     * @return string HTML content for the DRM status section
     */
    private static function get_drm_status_section()
    {
        $drm_status = MeprDrmHelper::get_status();
        $license_key = MeprDrmHelper::get_key();

        if (empty($drm_status)) {
            $drm_status = '-';
        }

        if (empty($license_key)) {
            $license_key = '-';
        }

        $drm_event_data = self::get_current_drm_event_data();
        $content = '<h2>' . esc_html__('Current DRM Status', 'memberpress') . '</h2>';
        $content .= '<table id="drm-status-table" class="widefat striped">';
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('DRM Event Type', 'memberpress'),
            esc_html($drm_event_data['type'] ?? '-')
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('DRM Days Elapsed', 'memberpress'),
            esc_html($drm_event_data['days_elapsed'] ?? '-')
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('DRM Status', 'memberpress'),
            esc_html($drm_status)
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('License Key', 'memberpress'),
            esc_html($license_key)
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('License Valid', 'memberpress'),
            MeprDrmHelper::is_valid() ? esc_html__('Yes', 'memberpress') : esc_html__('No', 'memberpress')
        );
        $content .= '</table>';

        if (!empty($drm_event_data['type']) && !empty($drm_event_data['event']) && $drm_event_data['event'] instanceof MeprEvent) {
            $content .= '<h3>' . esc_html__('Current DRM Event Information', 'memberpress') . '</h3>';
            $content .= '<table class="widefat striped">';
            $content .= sprintf(
                '<tr><th>%s</th><th>%s</th><th>%s</th></tr>',
                esc_html__('Event', 'memberpress'),
                esc_html__('Created At', 'memberpress'),
                esc_html__('Args', 'memberpress')
            );
            $content .= self::format_event_row($drm_event_data['event']);
            $content .= '</table>';
        }
        return $content;
    }

    /**
     * Gets the DRM options from the WordPress options table
     *
     * Displays all options with 'mepr_drm_' prefix
     *
     * @return string HTML content showing all DRM-related options
     */
    private static function get_drm_options_section()
    {
        global $wpdb;

        $content = sprintf('<h2>%s</h2>', esc_html__('DRM Options Log', 'memberpress'));
        $content .= '<table class="widefat striped">';
        $content .= sprintf(
            '<tr><th>%s</th><th>%s</th></tr>',
            esc_html__('Name', 'memberpress'),
            esc_html__('Value', 'memberpress')
        );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $drm_options = $wpdb->get_results(
            'SELECT option_name, option_value ' .
            "FROM {$wpdb->options} " .
            'WHERE option_name LIKE "mepr_drm_%" ' .
            'ORDER BY option_id ASC'
        );

        foreach ($drm_options as $option) {
            $value = self::format_option_value($option->option_name, $option->option_value);
            $content .= sprintf(
                '<tr><th>%s</th><td>%s</td></tr>',
                esc_html($option->option_name),
                wp_kses_post($value)
            );
        }
        $content .= '</table>';

        return $content;
    }

    /**
     * Gets the chronological history of DRM events
     *
     * @return string HTML content showing all DRM events in chronological order
     */
    private static function get_drm_events_section()
    {
        $content = sprintf('<h2>%s</h2>', esc_html__('DRM Events Chronological', 'memberpress'));
        $content .= '<table class="widefat striped">';
        $content .= sprintf(
            '<tr><th>%s</th><th>%s</th><th>%s</th></tr>',
            esc_html__('Event', 'memberpress'),
            esc_html__('Created At', 'memberpress'),
            esc_html__('Args', 'memberpress')
        );

        $drm_events = MeprEvent::get_all_by_evt_id_type(MeprEvent::$drm_str, 'id ASC');

        foreach ($drm_events as $event) {
            if ($event) {
                $content .= self::format_event_row($event);
            }
        }
        $content .= '</table>';

        return $content;
    }

    /**
     * Generates the application fee section of the debug report
     *
     * Shows fee settings, subscription counts, and fee history
     *
     * @return string HTML content for the application fee section
     */
    private static function get_app_fee_section()
    {
        $content = '<h2>' . esc_html__('Application Fee Information', 'memberpress') . '</h2>';

        // Basic information table.
        $content .= self::get_app_fee_basic_info();

        // Fee history table.
        $content .= self::get_app_fee_history();

        return $content;
    }

    /**
     * Generates the basic app fee information table
     *
     * Shows current settings and subscription statistics
     *
     * @return string HTML content for the basic fee information table
     */
    private static function get_app_fee_basic_info()
    {
        global $wpdb;

        $content = '<table class="widefat striped">';

        // Add basic settings.
        $content .= self::get_app_fee_settings();

        // Check for fee history before showing subscription stats.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $has_fee_history = $wpdb->get_var(
            "SELECT EXISTS (
                SELECT 1
                FROM {$wpdb->options}
                WHERE option_name LIKE 'mepr_drm_app_fee_%_data_%'
                LIMIT 1
            )"
        );

        // Only show subscription stats if fee history exists.
        if ($has_fee_history) {
            $content .= self::get_app_fee_subscription_stats();
        }

        $content .= '</table>';

        return $content;
    }

    /**
     * Gets the current application fee settings
     *
     * @return string HTML content showing current fee settings
     */
    private static function get_app_fee_settings()
    {
        $content = '';
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('App Fee Enabled', 'memberpress'),
            MeprDrmHelper::is_app_fee_enabled() ? esc_html__('Yes', 'memberpress') : esc_html__('No', 'memberpress')
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s%%</td></tr>',
            esc_html__('Current Fee Percentage', 'memberpress'),
            esc_html(MeprDrmHelper::get_application_fee_percentage())
        );
        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('Fee API Version', 'memberpress'),
            esc_html(MeprDrmHelper::get_drm_app_fee_version())
        );

        return $content;
    }

    /**
     * Gets the subscription statistics for application fees
     *
     * Includes counts of active subscriptions with fees applied/revoked and historical totals
     *
     * @return string HTML content showing subscription statistics with SQL copy buttons
     */
    private static function get_app_fee_subscription_stats()
    {
        $counts = self::get_app_fee_subscription_counts();
        $content = '';

        $content .= sprintf(
            '<tr><td colspan="2"><h4>%s</h4></td></tr>',
            esc_html__('Active Subscriptions', 'memberpress')
        );

        $content .= sprintf(
            '<tr><th>%s</th><td>%s <button class="button button-small copy-sql" data-sql="%s" onclick="meprcopySql(this)">%s</button></td></tr>',
            esc_html__('With Fee Applied', 'memberpress'),
            esc_html($counts['applied']['count']),
            esc_attr($counts['applied']['copy_sql']),
            esc_html__('Copy SQL', 'memberpress')
        );

        $content .= sprintf(
            '<tr><th>%s</th><td>%s <button class="button button-small copy-sql" data-sql="%s" onclick="meprcopySql(this)">%s</button></td></tr>',
            esc_html__('With Fee Revoked', 'memberpress'),
            esc_html($counts['revoked']['count']),
            esc_attr($counts['revoked']['copy_sql']),
            esc_html__('Copy SQL', 'memberpress')
        );

        $content .= sprintf(
            '<tr><td colspan="2"><h4>%s</h4></td></tr>',
            esc_html__('Historical Totals', 'memberpress')
        );

        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('Total Ever Fee Applied', 'memberpress'),
            esc_html($counts['all_applied']['count'])
        );

        $content .= sprintf(
            '<tr><th>%s</th><td>%s</td></tr>',
            esc_html__('Total Ever Fee Revoked', 'memberpress'),
            esc_html($counts['all_revoked']['count'])
        );

        $content .= '
        <script>
        function meprcopySql(button) {
            const sql = button.getAttribute("data-sql");
            navigator.clipboard.writeText(sql).then(function() {
                const originalText = button.textContent;
                button.textContent = "' . esc_js(__('Copied!', 'memberpress')) . '";
                setTimeout(function() {
                    button.textContent = originalText;
                }, 2000);
            });
        }
        </script>
        <style>
        .copy-sql {
            margin-left: 10px !important;
        }
        </style>';

        return $content;
    }

    /**
     * Gets the history of application fee enable/disable events
     *
     * @return string HTML content showing the fee enable/disable history
     */
    private static function get_app_fee_history()
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $fee_history_data = $wpdb->get_results(
            'SELECT option_name, option_value ' .
            "FROM {$wpdb->options} " .
            'WHERE option_name LIKE "mepr_drm_app_fee_%_data_%" ' .
            'ORDER BY option_id ASC'
        );

        if (empty($fee_history_data)) {
            return '';
        }

        $content = sprintf('<h3>%s</h3>', esc_html__('Fee Enable/Disable History', 'memberpress'));
        $content .= '<table class="widefat striped">';
        $content .= sprintf(
            '<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>',
            esc_html__('Action', 'memberpress'),
            esc_html__('Date', 'memberpress'),
            esc_html__('User', 'memberpress'),
            esc_html__('Domain', 'memberpress')
        );

        foreach ($fee_history_data as $data) {
            $content .= self::format_fee_history_row($data);
        }

        $content .= '</table>';

        return $content;
    }

    /**
     * Gets counts of subscriptions with application fee applied and revoked
     *
     * @return array {
     * Array containing subscription counts and their SQL queries
     *
     * @type array $applied {
     * Active subscriptions with fee applied
     * @type string $count     Number of subscriptions with fee applied
     * @type string $copy_sql  SQL query for copying subscription IDs
     * }
     * @type array $revoked {
     * Active subscriptions with fee revoked
     * @type string $count     Number of subscriptions with fee revoked
     * @type string $copy_sql  SQL query for copying subscription IDs
     * }
     * @type array $all_applied {
     * All subscriptions that ever had fee applied
     * @type string $count     Total number of subscriptions
     * @type string $copy_sql  Empty string, no SQL available
     * }
     * @type array $all_revoked {
     * All subscriptions that ever had fee revoked
     * @type string $count     Total number of subscriptions
     * @type string $copy_sql  Empty string, no SQL available
     * }
     * }
     */
    private static function get_app_fee_subscription_counts()
    {
        global $wpdb;

        $all_applied_sql = $wpdb->prepare(
            'SELECT COUNT(DISTINCT s.id) ' .
            "FROM {$wpdb->mepr_subscriptions} s " .
            "JOIN {$wpdb->mepr_subscription_meta} sm ON s.id = sm.subscription_id " .
            'WHERE sm.meta_key = \'application_fee_percent\' ' .
            'AND sm.meta_value > 0'
        );

        $all_revoked_sql = $wpdb->prepare(
            'SELECT COUNT(DISTINCT s.id) ' .
            "FROM {$wpdb->mepr_subscriptions} s " .
            "JOIN {$wpdb->mepr_subscription_meta} sm ON s.id = sm.subscription_id " .
            'WHERE sm.meta_key = \'application_fee_revoked\''
        );

        $active_applied_sql = $wpdb->prepare(
            'SELECT COUNT(DISTINCT s.id) as count ' .
            "FROM {$wpdb->mepr_subscriptions} s " .
            "JOIN {$wpdb->mepr_transactions} t ON s.id = t.subscription_id " .
            "JOIN {$wpdb->mepr_subscription_meta} sm ON s.id = sm.subscription_id " .
            'WHERE t.status IN (%s, %s) ' .
            'AND s.status = %s ' .
            'AND t.expires_at > %s ' .
            'AND t.expires_at != \'0000-00-00 00:00:00\' ' .
            'AND sm.meta_key = \'application_fee_percent\' ' .
            'AND sm.meta_value > 0',
            MeprTransaction::$complete_str,
            MeprTransaction::$confirmed_str,
            MeprSubscription::$active_str,
            MeprUtils::db_now()
        );

        $active_revoked_sql = $wpdb->prepare(
            'SELECT COUNT(DISTINCT s.id) as count ' .
            "FROM {$wpdb->mepr_subscriptions} s " .
            "JOIN {$wpdb->mepr_transactions} t ON s.id = t.subscription_id " .
            "JOIN {$wpdb->mepr_subscription_meta} sm ON s.id = sm.subscription_id " .
            'WHERE t.status IN (%s, %s) ' .
            'AND s.status = %s ' .
            'AND t.expires_at > %s ' .
            'AND t.expires_at != \'0000-00-00 00:00:00\' ' .
            'AND sm.meta_key = \'application_fee_revoked\'',
            MeprTransaction::$complete_str,
            MeprTransaction::$confirmed_str,
            MeprSubscription::$active_str,
            MeprUtils::db_now()
        );

        $copy_applied_sql = str_replace('COUNT(DISTINCT s.id) as count', 'DISTINCT s.id', $active_applied_sql);
        $copy_revoked_sql = str_replace('COUNT(DISTINCT s.id) as count', 'DISTINCT s.id', $active_revoked_sql);

        return [
            'applied' => [
                'count' => number_format((int) $wpdb->get_var($active_applied_sql)), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
                'copy_sql' => $copy_applied_sql,
            ],
            'revoked' => [
                'count' => number_format((int) $wpdb->get_var($active_revoked_sql)), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
                'copy_sql' => $copy_revoked_sql,
            ],
            'all_applied' => [
                'count' => number_format((int) $wpdb->get_var($all_applied_sql)), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
                'copy_sql' => '',
            ],
            'all_revoked' => [
                'count' => number_format((int) $wpdb->get_var($all_revoked_sql)), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
                'copy_sql' => '',
            ],
        ];
    }

    /**
     * Formats a single row of fee history data for display
     *
     * @param object $data The history data object containing option name and value.
     *
     * @return string HTML for a single history row
     */
    private static function format_fee_history_row($data)
    {
        $value = maybe_unserialize($data->option_value);

        if (!is_array($value) || !isset($value['time']) || !isset($value['user_id']) || !isset($value['domain'])) {
            return '';
        }

        $action = (strpos($data->option_name, 'enabled_data_') !== false) ? esc_html__('Enabled', 'memberpress') : esc_html__('Disabled', 'memberpress');

        $user = null;
        if ($value['user_id']) {
            $user = get_user_by('id', (int) $value['user_id']);
        }

        $user_info = esc_html__('Unknown User', 'memberpress');
        if ($user) {
            $user_info = sprintf(
                '<a href="%s" target="_blank">%s</a><br/>(ID: %d, Username: %s)',
                esc_url(admin_url('user-edit.php?user_id=' . $user->ID)),
                esc_html($user->display_name),
                $user->ID,
                esc_html($user->user_login)
            );
        }

        return sprintf(
            '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',
            esc_html($action),
            esc_html($value['time']),
            wp_kses_post($user_info),
            esc_html($value['domain'])
        );
    }

    /**
     * Formats an option value for display based on its type and name
     *
     * @param string $option_name  The name of the option.
     * @param string $option_value The raw value of the option.
     *
     * @return string Formatted HTML for displaying the option value
     */
    private static function format_option_value($option_name, $option_value)
    {
        if (is_serialized($option_value)) {
            $unserialized = maybe_unserialize($option_value);
            if (is_array($unserialized)) {
                $formatted = '<div class="mepr-serialized-data">';
                foreach ($unserialized as $key => $value) {
                    if (is_numeric($value) && strlen($value) === 10) {
                        $value = sprintf(
                            '%s (%s)',
                            $value,
                            gmdate('Y-m-d H:i:s', (int) $value)
                        );
                    }
                    $formatted .= sprintf(
                        '<div class="mepr-data-row"><span class="mepr-key">%s:</span> <span class="mepr-value">%s</span></div>',
                        esc_html($key),
                        esc_html($value)
                    );
                }
                $formatted .= '</div>';

                return $formatted;
            }
        }

        if (is_numeric($option_value) && strlen($option_value) === 10) {
            return sprintf(
                '%s (%s)',
                $option_value,
                gmdate('Y-m-d H:i:s', (int) $option_value)
            );
        }

        return $option_value;
    }

    /**
     * Formats a DRM event row for display in tables
     *
     * @param object $event The event object to format.
     *
     * @return string HTML for the event table row
     */
    private static function format_event_row($event)
    {
        $args_data = json_decode($event->args, true);
        $formatted_args = '';

        if (is_array($args_data)) {
            foreach ($args_data as $key => $value) {
                if (is_numeric($value) && strlen($value) === 10) {
                    $value = sprintf(
                        '%s (%s)',
                        $value,
                        gmdate('Y-m-d H:i:s', (int) $value)
                    );
                }
                $formatted_args .= sprintf(
                    '%s: %s<br>',
                    esc_html($key),
                    esc_html($value)
                );
            }
        }

        return sprintf(
            '<tr><td>%s</td><td>%s</td><td>%s</td></tr>',
            esc_html($event->event),
            esc_html($event->created_at),
            $formatted_args
        );
    }

    /**
     * Gets the CSS styles for the debug display
     *
     * @return string CSS styles for formatting the debug information
     */
    private static function get_debug_styles()
    {
        return '<style>
            .mepr-drm-debug table { margin-bottom: 20px; }
            .mepr-drm-debug h3 { margin: 20px 0 10px; }
            .mepr-drm-debug th { width: 250px; }
        </style>';
    }

    /**
     * Gets the current DRM event data including type, event object, and days elapsed
     *
     * @return array {
     * Array containing current DRM event information
     *
     * @type string      $type         The type of DRM event
     * @type MeprEvent   $event        The event object
     * @type int         $days_elapsed Number of days since the event
     * }
     */
    private static function get_current_drm_event_data()
    {
        // Check DRM license status flags.
        $drm_no_license = get_option('mepr_drm_no_license', false);
        $drm_invalid_license = get_option('mepr_drm_invalid_license', false);

        $drm_event_type = '';
        if ($drm_no_license) {
            $drm_event_type = MeprDrmHelper::NO_LICENSE_EVENT;
        } elseif ($drm_invalid_license) {
            $drm_event_type = MeprDrmHelper::INVALID_LICENSE_EVENT;
        }

        $data = [];
        if (!empty($drm_event_type)) {
            $event = MeprEvent::latest($drm_event_type);
            if ($event) {
                $data['type'] = $drm_event_type;
                $data['event'] = $event;
                $data['days_elapsed'] = MeprDrmHelper::days_elapsed($event->created_at);
            }
        }

        return $data;
    }
}