HEX
Server: Apache
System: Linux srv674466.hstgr.cloud 5.14.0-503.19.1.el9_5.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jan 7 17:08:27 EST 2025 x86_64
User: rialindia (1055)
PHP: 8.2.30
Disabled: exec,passthru,shell_exec,system
Upload Files
File: /home/rialindia/public_html/wp-content/plugins/nitropack/classes/Feature/AjaxShortcodes.php
<?php

namespace NitroPack\Feature;

class AjaxShortcodes {
    const STAGE = "very_early";

    private $shortcodes = array();
    private $scriptHandle = 'nitropack-ajax-shortcodes';
    private $processedShortcodes = [];
    private $emptyScript = "let nitroAjaxShortcode = false";

    private function generateNitroScLoader() {
        if (empty($this->processedShortcodes)) return $this->emptyScript;

        $adminUrl = admin_url('admin-ajax.php');
        $js = <<<SCRIPT
    (async () => {
        let domIsReady = new Promise((resolve) => {
            if (document.readyState !== 'complete') {
                document.addEventListener('DOMContentLoaded', resolve);
            } else {
                resolve();
            }
        });

        let formData = new URLSearchParams();
        formData.append("action", "nitro_shortcode_ajax");  
        formData.append("nonce", "NONCE_VALUE");
        formData.append("tags", "TAGS_JSON_DATA");

        fetch('$adminUrl', {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: formData.toString(),
        })
        .then(response => response.json())
        .then(async (data) => {
            let scData = data.data;
            await domIsReady;
            for (let key in scData) {
                document.querySelectorAll(`.nitro-sc-load[data-sc-meta-id='\${key}']`).forEach(el => {
                    el.outerHTML = scData[key];
                });            
            }
        })
        .catch(error => {
            console.error('Error:', error);
        });
    })()

SCRIPT;

        return str_replace(
            [
                "NONCE_VALUE",
                "TAGS_JSON_DATA"
            ],
            [
                $this->generateNonce($this->processedShortcodes),
                base64_encode(json_encode($this->processedShortcodes))
            ],
            $js
        );
    }

    private function generateNonce($tags) {
        return wp_create_nonce($this->getNonceAction($tags));
    }

    private function getNonceAction($tags) {
        $jsons = array_map("json_encode", $tags);
        sort($jsons);
        return base64_encode(json_encode($jsons));
    }
    /* Merge shortcodes from wp-config and site config */
    private function mergeShortcodes() {

        $siteConfig = get_nitropack()->getSiteConfig();        
        $options = $siteConfig['options_cache'];

        $ajax_shortcodes = isset($options['ajaxShortcodes']) ? $options['ajaxShortcodes'] : null; //initially ajaxShortcodes is empty

        if (self::isEnabled() && (!empty($ajax_shortcodes['shortcodes']) && is_array($ajax_shortcodes['shortcodes']))) {
            $this->shortcodes = $ajax_shortcodes['shortcodes'];
        }
        if (defined("NITROPACK_AJAX_SHORTCODES") && strpos(NITROPACK_AJAX_SHORTCODES, ',') !== false) {
            $wp_config_shortcodes = explode(',', NITROPACK_AJAX_SHORTCODES);
            $this->shortcodes = array_unique(array_merge($this->shortcodes, $wp_config_shortcodes));
        }
    }
    public function init($stage) {
        if (!defined("NITROPACK_AJAX_SHORTCODES") && !self::isEnabled()) {
            // This init method can be run at any stage. This gives the opportunity to define the constant at a later point
            // For example in a MU plugin
            return true;
        }
        $this->mergeShortcodes();
        //stop execution if no shortcodes are found
        if (!$this->shortcodes) return;

        add_filter('pre_do_shortcode_tag', function ($out, $tag, $attr) {

            if (defined("NITRO_DOING_AJAX_SHORTCODES") && NITRO_DOING_AJAX_SHORTCODES) return $out;

            if (in_array($tag, $this->shortcodes)) {
                $entry = ["tag" => $tag, "attr" => $attr];
                if (!in_array($entry, $this->processedShortcodes)) {
                    $this->processedShortcodes[] = $entry;
                }
                $np_out = '<span class="nitro-sc-load" data-sc-meta-id="' . base64_encode(json_encode($entry)) . '"></span>';

                return $np_out;
            }

            return $out;
        }, 10, 3);


        add_action('wp_ajax_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));
        add_action('wp_ajax_nopriv_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));

        add_action('wp_enqueue_scripts', [$this, 'add_scripts']);
        $this->startBuffering();
    }

    private function startBuffering() {
        ob_start(function($buffer) {
            return str_replace($this->emptyScript, $this->generateNitroScLoader(), $buffer);
        }, 0, PHP_OUTPUT_HANDLER_FLUSHABLE | PHP_OUTPUT_HANDLER_REMOVABLE);
    }

    public function add_scripts() {
        wp_register_script($this->scriptHandle, '', [], '1.0.0', false);
        wp_add_inline_script($this->scriptHandle, $this->emptyScript);
        wp_enqueue_script($this->scriptHandle);
        
        add_filter('wp_inline_script_attributes', function ($attr, $js) {

            if(isset($attr['id']) && strpos($attr['id'], $this->scriptHandle) === 0) {
                $attr = array_merge(array( 'nitro-exclude' => true ), $attr);
            } 

            return $attr;

        }, 10, 2);
    }

    public function shortcodeAjax() {
        if (!defined("NITRO_DOING_AJAX_SHORTCODES")) define("NITRO_DOING_AJAX_SHORTCODES", true);
        $this->runShortcodes();

        // In case a later hook is needed, we can and an option for it and use something like this
        //if (did_action('plugins_loaded')) {
        //    $this->runShortcodes();
        //} else {
        //    add_action('plugins_loaded', [$this, 'runShortcodes']);
        //}
    }

    private function validateNonce($tags) {
        if (empty($_POST['nonce']) || empty($_POST['tags']) || !is_string($_POST['tags'])) {
            die(__('Security check', 'nitropack'));
        }

        $action = $this->getNonceAction($tags);
        if (!wp_verify_nonce($_POST['nonce'], $action)) die(__('Security check', 'nitropack'));
    }

    public function runShortcodes() {
        $tags = !empty($_POST["tags"]) && is_string($_POST["tags"]) ? json_decode(base64_decode($_POST['tags']), true) : [];
        if (empty($tags)) wp_send_json_error(["message" => "Invalid shortcode input"], 400);
        $this->validateNonce($tags);

        $resp = [];
        foreach ($tags as $sc) {
            $attrFlat = is_array($sc["attr"]) ? array_map(function ($k, $v) {
                return "$k=\"$v\"";
            }, array_keys($sc["attr"]), array_values($sc["attr"])) : [];
            $arrKey = base64_encode(json_encode($sc));
            $resp[$arrKey] = do_shortcode("[{$sc['tag']} " . implode(" ", $attrFlat) . "]");
        }

        wp_send_json_success($resp);
    }
    public function isEnabled() {
        $siteConfig = get_nitropack()->getSiteConfig();
        return !empty($siteConfig['options_cache']['ajaxShortcodes']['enabled'])
            && $siteConfig['options_cache']['ajaxShortcodes']['enabled'] == 1;
    }
}