File "class-wpb-sdk-logger.php"

Full Path: /home/theinspectionboy/public_html/suffolk/lib/wpb-sdk/includes/class-wpb-sdk-logger.php
File size: 19.78 KB
MIME-type: text/x-php
Charset: utf-8

<?php

class WPBRIGADE_Logger
{
    private static $_instances = array();
    private static $product_data = array();
    private static $_module_id;

    private static $current_uninstall_slug = null;

    // Constructor for the Logger class
    private function __construct($module_id, $slug = false, $is_init = false)
    {
        if (!$is_init && !is_numeric($module_id) && !is_string($slug)) {
            return false;
        }
        self::$_module_id = $module_id;
        self::$current_uninstall_slug = $slug; // Set current uninstall slug
    }

    // Method to create or retrieve a Logger instance
    public static function instance($module_id, $slug = false, $is_init = false)
    {
        if (empty($module_id)) {
            return false;
        }
        if (!$is_init && true === $slug) {
            $is_init = true;
        }

        if (!isset(self::$_instances[$slug])) {
            self::$_instances[$slug] = new WPBRIGADE_Logger($module_id, $slug, $is_init);
        }

        return self::$_instances[$slug];
    }

    // Method to initialize the Logger with module data
    public function wpb_init(array $module)
    {
        $key = $module['slug'];
        self::$product_data[$key] = [];
        self::$product_data[$key]['module'] = $module;
        $this->hooks($module['slug']);
    }

    // Method to attach hooks for scheduled events and AJAX
    public function hooks($slug)
    {
        // Initialize custom schedules
        add_action('init', function () use ($slug) {
            $this->set_logs_schedule($slug);
        });

        // Daily log plugin execution
        add_action('wpb_data_sync_' . $slug, function () use ($slug) {
            $this->daily_log_plugin($slug);
        });

        // Admin footer hook
        add_action('admin_footer', function () use ($slug) {
            $this->deactivation_model($slug);
        });

        // AJAX deactivation action
        add_action('wp_ajax_wpb_sdk_' . $slug . '_deactivation', function () use ($slug) {
            $this->ajax_deactivation($slug);
        });

        // Plugin activation hook
        register_activation_hook(wpb_get_plugin_path($slug), function () use ($slug) {
            $this->log_activation($slug);
        });

        // Plugin deactivation hook
        register_deactivation_hook(wpb_get_plugin_path($slug), function () use ($slug) {
            $this->product_deactivation($slug);
        });

        // Plugin uninstallation hook
        register_uninstall_hook(wpb_get_plugin_path($slug), array(__CLASS__, 'log_uninstallation'));
    }

    // Method to set scheduled events for logging
    public function set_logs_schedule($slug)
    {
         //Clean Old Cron jobs
         wp_clear_scheduled_hook('wpb_logger_cron_' . $slug);
         wp_clear_scheduled_hook('wpb_daily_sync_cron_' . $slug);

        // Calculate future timestamps for scheduling
        $daily_start_time = strtotime('+1 day');
        // Schedule daily cron event if not already scheduled
        if (!wp_next_scheduled('wpb_data_sync_' . $slug)) {
            wp_schedule_event($daily_start_time, 'daily', 'wpb_data_sync_' . $slug);
        }

    }

    public static function reset_logs_schedule($slug)
    {
        // Calculate future timestamps for scheduling
        $daily_start_time = strtotime('+1 day');

        // Schedule daily cron event if not already scheduled
        if (!wp_next_scheduled('wpb_data_sync_' . $slug)) {
            wp_schedule_event($daily_start_time, 'daily', 'wpb_data_sync_' . $slug);
        }
    }

    public static function remove_logs_schedule($slug)
    {
        wp_clear_scheduled_hook('wpb_data_sync_' . $slug);
    }

    // Method to log plugin activity on daily scheduled events
    public function daily_log_plugin($slug)
    {
        $sdk_data = json_decode(get_option('wpb_sdk_' . $slug), true);
        $user_skip = isset($sdk_data['user_skip']) ? $sdk_data['user_skip'] : false;
        $user_skip = $user_skip === "1" ? true : false;
        if ($user_skip) {
            $logs_data = self::get_logs_data($slug, 'user_skip');
            $sdk_data['user_skip'] = "0";
            $sdk_data_json = json_encode($sdk_data);
            update_option('wpb_sdk_' . $slug, $sdk_data_json);
        } else {
            $logs_data = self::get_logs_data($slug, 'daily');
        }

        if (!empty($logs_data)) {
            $logs_to_send = array_merge(
                $logs_data,
                array(
                    'explicit_logs' => array(
                        'action' => 'daily',
                    ),
                )
            );
            self::send($slug, $logs_to_send);
        }


    }

    // Method to log plugin activation
    public function log_activation($slug)
    {
        $logs_data = self::get_logs_data($slug, 'activate');
        if (!empty($logs_data)) {
            $logs_to_send = array_merge(
                $logs_data,
                array(
                    'explicit_logs' => array(
                        'action' => 'activate',
                    ),
                )
            );
            self::send($slug, $logs_to_send);
        }
    }

    // Method to add deactivation model HTML to admin footer
    public function deactivation_model($slug)
    {
        if (function_exists('get_current_screen')) {
            $screen = get_current_screen();
            if ('plugins.php' === $screen->parent_file) {
                $plugin_data = wpb_get_plugin_details($slug);
                $product_name = $plugin_data['Name'];
                $product_slug = $slug;
                $has_pro_version = self::$product_data[$slug]['module']['is_premium'] === true;
                include dirname(__DIR__) . '/views/wpb-sdk-deactivate-form.php';
            }
        }
    }

    // Method to handle AJAX request for plugin deactivation
    public function ajax_deactivation($slug)
    {
        $path = wpb_get_plugin_path($slug);

        if (isset($_POST['nonce']) && empty($_POST['nonce'])) {
            return;
        }

        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $verify_nonce = wp_verify_nonce($nonce, 'deactivate-plugin_' . plugin_basename($path));

        if (!$verify_nonce) {
            return;
        }

        $this->log_deactivation($slug);

        wp_die();
    }

    // Method to handle plugin deactivation
    public function product_deactivation($slug)
    {
        wp_clear_scheduled_hook('wpb_data_sync_' . $slug);
    }

    // Method to log plugin deactivation
    public function log_deactivation($slug)
    {
        $reason = isset($_POST['reason']) ? $_POST['reason'] : '';
        $reason_detail = isset($_POST['reason_detail']) ? $_POST['reason_detail'] : '';
        $logs_data = self::get_logs_data($slug, 'deactivate');
        if (!empty($logs_data)) {
            $logs_to_send = array_merge(
                $logs_data,
                array(
                    'explicit_logs' => array(
                        'action' => 'deactivate',
                        'reason' => sanitize_text_field(wp_unslash($reason)),
                        'reason_detail' => sanitize_text_field(wp_unslash($reason_detail)),
                    ),
                )
            );
            self::send($slug, $logs_to_send);
        }
    }

    // Method to log plugin uninstallation
    public static function log_uninstallation()
    {
        $slug = self::$current_uninstall_slug;
        $logs_data = self::get_logs_data($slug, 'uninstall');
        if (!empty($logs_data)) {
            $logs_to_send = array_merge(
                $logs_data,
                array(
                    'explicit_logs' => array(
                        'action' => 'uninstall',
                    ),
                )
            );
            self::send($slug, $logs_to_send);
        }
        // Call Plugin uninstall hook
        do_action('wp_wpb_sdk_after_uninstall');
    }

    /**
     * Collect all data for logging.
     *
     * @return array
     */
    public static function get_logs_data($slug, $action = '')
    {
        global $wpdb;

        // Get product data
        $module = self::$product_data[$slug]['module'];
        // Initialize variables
        $data = array();
        $theme_data = wp_get_theme();
        $curl_version = '';
        $external_http_blocked = '';
        $users_count = '';

        $sdk_data = json_decode(get_option('wpb_sdk_' . $slug), true);

        $sdk_communication = isset($sdk_data['communication']) ? $sdk_data['communication'] : '0';
        $sdk_diagnostic_info = isset($sdk_data['diagnostic_info']) ? $sdk_data['diagnostic_info'] : '0';
        $sdk_extensions = isset($sdk_data['extensions']) ? $sdk_data['extensions'] : '0';


        $send_wpb_sdk_communication = $sdk_communication === "1" ? true : false;
        $send_wpb_sdk_diagnostic_info = $sdk_diagnostic_info === "1" ? true : false;
        $send_wpb_sdk_extensions = $sdk_extensions === "1" ? true : false;


        if ($action != "user_skip") {
            if (!$send_wpb_sdk_communication && !$send_wpb_sdk_diagnostic_info && !$send_wpb_sdk_extensions) {
                WPBRIGADE_Logger::remove_logs_schedule($slug);
                return [];
            } else {
                WPBRIGADE_Logger::reset_logs_schedule($slug);
            }
        }
        // Get admin user data
        $admin_users = get_users(array('role' => 'Administrator'));
        $admin = isset($admin_users[0]) ? $admin_users[0]->data : '';
        $admin_meta = !empty($admin) ? get_user_meta($admin->ID) : '';


        // Collect data
        $data['authentication']['public_key'] = $module['public_key'];

        if ($action == "user_skip"||$send_wpb_sdk_communication) {
            // USER INFO
            $data['user_info'] = array(
                'user_email' => !empty($admin) ? sanitize_email($admin->user_email) : '',
                'user_nickname' => !empty($admin) ? sanitize_text_field($admin->user_nicename) : '',
                'user_firstname' => isset($admin_meta['first_name'][0]) ? sanitize_text_field($admin_meta['first_name'][0]) : '',
                'user_lastname' => isset($admin_meta['last_name'][0]) ? sanitize_text_field($admin_meta['last_name'][0]) : '',
            );
        }

        // PRODUCT INFO MUST HAVE
        $data['product_info'] = self::get_product_data($slug);
        $data['product_info']['sdk_version'] = WP_WPBRIGADE_SDK_VERSION;

        if ($action == "user_skip"||$send_wpb_sdk_diagnostic_info) {
            $data['product_info']['product_settings'] = self::get_product_settings($slug);
        }

        // SITE INFO MUST HAVE
        $data['site_info'] = array(
            'site_url' => site_url(),
            'home_url' => home_url(),
        );

        if ($action == "user_skip"||$send_wpb_sdk_diagnostic_info) {
            $ip = self::get_ip();
            $location = self::get_location_details($ip);

            // Check if get_plugins function exists
            if (!function_exists('get_plugins')) {
                include ABSPATH . '/wp-admin/includes/plugin.php';
            }

            // Get users count if function exists
            if (function_exists('count_users')) {
                $users_count = count_users();
                $users_count = isset($users_count['total_users']) ? intval($users_count['total_users']) : '';
            }

            // Check external http request blocking
            if (!defined('WP_HTTP_BLOCK_EXTERNAL') || !WP_HTTP_BLOCK_EXTERNAL) {
                $external_http_blocked = 'none';
            } else {
                $external_http_blocked = defined('WP_ACCESSIBLE_HOSTS') ? 'partially (accessible hosts: ' . esc_html(WP_ACCESSIBLE_HOSTS) . ')' : 'all';
            }

            // Get curl version if function exists
            if (function_exists('curl_init')) {
                $curl = curl_version();
                $curl_version = '(' . $curl['version'] . ' ' . $curl['ssl_version'] . ')';
            }


            $data['site_info']['site_meta_info'] = array(
                'is_multisite' => is_multisite(),
                'multisites' => self::get_multisites(),
                'php_version' => phpversion(),
                'wp_version' => get_bloginfo('version'),
                'server' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '',
                'timezoneoffset' => date('P'),
                'ext/mysqli' => isset($wpdb->use_mysqli) && !empty($wpdb->use_mysqli) ? true : false,
                'mysql_version' => function_exists('mysqli_get_server_info') ? mysqli_get_server_info($wpdb->dbh) : '',
                'memory_limit' => (defined(WP_MEMORY_LIMIT) ? WP_MEMORY_LIMIT : ini_get('memory_limit')) ? ini_get('memory_limit') : '',
                'external_http_blocked' => $external_http_blocked,
                'wp_locale' => get_locale(),
                'db_charset' => defined('DB_CHARSET') ? DB_CHARSET : '',
                'debug_mode' => defined('WP_DEBUG') && WP_DEBUG ? true : false,
                'wp_max_upload' => size_format(wp_max_upload_size()),
                'php_time_limit' => function_exists('ini_get') ? ini_get('max_execution_time') : '',
                'php_error_log' => function_exists('ini_get') ? ini_get('error_log') : '',
                'fsockopen' => function_exists('fsockopen') ? true : false,
                'open_ssl' => defined('OPENSSL_VERSION_TEXT') ? OPENSSL_VERSION_TEXT : '',
                'curl' => $curl_version,
                'ip' => $ip,
                'user_count' => $users_count,
                'admin_email' => sanitize_email(get_bloginfo('admin_email')),
                'theme_name' => sanitize_text_field($theme_data->Name),
                'theme_version' => sanitize_text_field($theme_data->Version),
            );
            $data['site_info']['location_details'] = $location !== null ? $location : '';
        }

        // SITE PLUGINS
        if ($action == "user_skip"||$send_wpb_sdk_extensions) {
            $data['site_plugins'] = self::get_all_plugins();
        }

        return $data;
    }


    /**
     * Retrieve plugin settings related to the product.
     *
     * @return array
     */
    private static function get_product_settings($slug)
    {
        $product_data = self::$product_data[$slug]['module'];
        $plugin_options = array();

        // Pull settings data from db.
        foreach ($product_data['settings'] as $option_name => $default_value) {
            $get_option = get_option($option_name);
            $plugin_options[] = array(
                'option' => $option_name,
                'value' => !empty($get_option) ? wp_json_encode($get_option) : $default_value
            );
        }

        return $plugin_options;
    }

    /**
     * Collect multisite data.
     *
     * @return array|false
     */
    private static function get_multisites()
    {
        if (!is_multisite()) {
            return false;
        }

        $sites_info = array();
        $sites = get_sites();

        foreach ($sites as $site) {
            $sites_info[$site->blog_id] = array(
                'name' => get_blog_details($site->blog_id)->blogname,
                'domain' => $site->domain,
                'path' => $site->path,
            );
        }

        return $sites_info;
    }

    /**
     * Get user IP information.
     *
     * @return string|null
     */
    private static function get_ip()
    {
        $fields = array(
            'HTTP_CF_CONNECTING_IP',
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR',
        );

        foreach ($fields as $ip_field) {
            if (!empty($_SERVER[$ip_field])) {
                return $_SERVER[$ip_field];
            }
        }

        return null;
    }

    /**
     * Collect plugins information: Active/Inactive plugins.
     *
     * @return array
     */
    private static function get_all_plugins()
    {
        $all_plugins = array_keys(get_plugins());
        $active_plugins = get_option('active_plugins', array());
        $in_active_plugins = [];

        foreach ($all_plugins as $plugin) {
            if (!in_array($plugin, $active_plugins)) {
                // add in-active plugins in list.
                $in_active_plugins[] = $plugin;
            }
        }

        return array(
            'active' => $active_plugins,
            'inactive' => $in_active_plugins,
        );
    }

    /**
     * Get location details based on IP.
     *
     * @param string|null $ip
     * @return array
     */
    private static function get_location_details($ip)
    {
        $location_details = array();
        try {
            $ch = curl_init();

            curl_setopt($ch, CURLOPT_URL, "https://api.iplocation.net/?ip={$ip}");

            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

            $execute = curl_exec($ch);

            curl_close($ch);

            $result = json_decode($execute);

            if ($result && $result->response_code === '200') {
                if ($result->country_name !== '-' && $result->country_code2 !== '-') {
                    $location_details['response_code'] = $result->response_code;
                    $location_details['message'] = 'Success';
                    $location_details['data']['country_name'] = $result->country_name;
                    $location_details['data']['country_code'] = $result->country_code2;
                } else {
                    $missing_info = array();
                    if ($result->country_name === '-') {
                        $missing_info[] = 'country_name';
                    }
                    if ($result->country_code2 === '-') {
                        $missing_info[] = 'country_code';
                    }
                    $location_details['response_code'] = '400';
                    $location_details['message'] = 'Error: Missing information for ' . implode(', ', $missing_info) . ' for the IP Address: ' . $ip;
                }
            } else {
                $location_details['response_code'] = '400';
                $location_details['message'] = 'Error: Invalid response code or data for the IP Address: ' . $ip;
            }

            return $location_details;
        } catch (\Exception $e) {
            $location_details['response_code'] = '400';
            $location_details['message'] = 'Error: ' . $e->getMessage();
            return $location_details;
        }
    }

    /**
     * Get product data.
     *
     * @param string $slug
     * @return array
     */
    private static function get_product_data($slug)
    {
        $plugin_data = wpb_get_plugin_details($slug);
        $data = array();
        $data['name'] = isset($plugin_data['Name']) ? $plugin_data['Name'] : $plugin_data['Title'];
        $data['slug'] = $slug;
        $data['id'] = self::$_module_id;
        $data['type'] = 'Plugin';
        $data['path'] = wpb_get_plugin_path($slug);
        $data['version'] = $plugin_data['Version'];
        return $data;
    }


    /**
     * Send log data to the API.
     *
     * @param array $payload The log data payload.
     */
    private static function send($slug, $payload)
    {
        // Add timestamp to the payload
        $payload['sent_at'] = current_time('mysql', 1);

        // Determine the log status
        $logStatus = 'new';
        $payload['log_status'] = $logStatus;

        self::sendDataToAPI($slug, $payload);

    }


    /**
     * Send data to the API endpoint.
     *
     * @param array $data The data to be sent.
     */
    private static function sendDataToAPI($slug, $data)
    {
        $token = self::$product_data[$slug]['module']['public_key'];

        $response = wp_remote_post(
            WPBRIGADE_SDK_API_ENDPOINT . '/logger',
            array(
                'method' => 'POST',
                'body' => $data,
                'headers' => array(
                    'Authorization' => 'Bearer ' . $token, // Add the token in the request headers
                ),
            )
        );
    }
}