PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.16.8
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.16.8
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / classes / Feature / AjaxShortcodes.php
nitropack / classes / Feature Last commit date
AjaxShortcodes.php 1 year ago SubrequestCache.php 2 years ago
AjaxShortcodes.php
180 lines
1 <?php
2
3 namespace NitroPack\Feature;
4
5 class AjaxShortcodes {
6 const STAGE = "very_early";
7
8 private $shortcodes = array();
9 private $scriptHandle = 'nitropack-ajax-shortcodes';
10 private $processedShortcodes = [];
11 private $emptyScript = "let nitroAjaxShortcode = false";
12
13 private function generateNitroScLoader() {
14 if (empty($this->processedShortcodes)) return $this->emptyScript;
15
16 $adminUrl = admin_url('admin-ajax.php');
17 $js = <<<SCRIPT
18 (async () => {
19 let domIsReady = new Promise((resolve) => {
20 if (document.readyState !== 'complete') {
21 document.addEventListener('DOMContentLoaded', resolve);
22 } else {
23 resolve();
24 }
25 });
26
27 let formData = new URLSearchParams();
28 formData.append("action", "nitro_shortcode_ajax");
29 formData.append("nonce", "NONCE_VALUE");
30 formData.append("tags", "TAGS_JSON_DATA");
31
32 fetch('$adminUrl', {
33 method: "POST",
34 headers: {
35 "Content-Type": "application/x-www-form-urlencoded",
36 },
37 body: formData.toString(),
38 })
39 .then(response => response.json())
40 .then(async (data) => {
41 let scData = data.data;
42 await domIsReady;
43 for (let key in scData) {
44 document.querySelectorAll(`.nitro-sc-load[data-sc-meta-id='\${key}']`).forEach(el => {
45 el.outerHTML = scData[key];
46 });
47 }
48 })
49 .catch(error => {
50 console.error('Error:', error);
51 });
52 })()
53
54 SCRIPT;
55
56 return str_replace(
57 [
58 "NONCE_VALUE",
59 "TAGS_JSON_DATA"
60 ],
61 [
62 $this->generateNonce($this->processedShortcodes),
63 base64_encode(json_encode($this->processedShortcodes))
64 ],
65 $js
66 );
67 }
68
69 private function generateNonce($tags) {
70 return wp_create_nonce($this->getNonceAction($tags));
71 }
72
73 private function getNonceAction($tags) {
74 $jsons = array_map("json_encode", $tags);
75 sort($jsons);
76 return base64_encode(json_encode($jsons));
77 }
78 private function mergeShortcodes() {
79
80 if (defined("NITROPACK_AJAX_SHORTCODES") && strpos(NITROPACK_AJAX_SHORTCODES, ',') !== false) {
81 $wp_config_shortcodes = explode(',', NITROPACK_AJAX_SHORTCODES);
82 $this->shortcodes = array_unique(array_merge($this->shortcodes, $wp_config_shortcodes));
83 }
84 }
85 public function init($stage) {
86 if (!defined("NITROPACK_AJAX_SHORTCODES")) {
87 // This init method can be run at any stage. This gives the opportunity to define the constant at a later point
88 // For example in a MU plugin
89 return true;
90 }
91 $this->mergeShortcodes();
92 //stop execution if no shortcodes are found
93 if (!$this->shortcodes) return;
94
95 add_filter('pre_do_shortcode_tag', function ($out, $tag, $attr) {
96
97 if (defined("NITRO_DOING_AJAX_SHORTCODES") && NITRO_DOING_AJAX_SHORTCODES) return $out;
98
99 if (in_array($tag, $this->shortcodes)) {
100 $entry = ["tag" => $tag, "attr" => $attr];
101 if (!in_array($entry, $this->processedShortcodes)) {
102 $this->processedShortcodes[] = $entry;
103 }
104 $np_out = '<span class="nitro-sc-load" data-sc-meta-id="' . base64_encode(json_encode($entry)) . '"></span>';
105
106 return $np_out;
107 }
108
109 return $out;
110 }, 10, 3);
111
112
113 add_action('wp_ajax_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));
114 add_action('wp_ajax_nopriv_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));
115
116 add_action('wp_enqueue_scripts', [$this, 'add_scripts']);
117 $this->startBuffering();
118 }
119
120 private function startBuffering() {
121 ob_start(function ($buffer) {
122 return str_replace($this->emptyScript, $this->generateNitroScLoader(), $buffer);
123 }, 0, PHP_OUTPUT_HANDLER_FLUSHABLE | PHP_OUTPUT_HANDLER_REMOVABLE);
124 }
125
126 public function add_scripts() {
127 wp_register_script($this->scriptHandle, '', [], '1.0.0', false);
128 wp_add_inline_script($this->scriptHandle, $this->emptyScript);
129 //wp_add_inline_script($this->scriptHandle, $this->generateNitroScLoader());
130 wp_enqueue_script($this->scriptHandle);
131
132 add_filter('wp_inline_script_attributes', function ($attr, $js) {
133
134 if (isset($attr['id']) && strpos($attr['id'], $this->scriptHandle) === 0) {
135 $attr = array_merge(array('nitro-exclude' => true), $attr);
136 }
137
138 return $attr;
139 }, 10, 2);
140 }
141
142 public function shortcodeAjax() {
143 if (!defined("NITRO_DOING_AJAX_SHORTCODES")) define("NITRO_DOING_AJAX_SHORTCODES", true);
144 $this->runShortcodes();
145
146 // In case a later hook is needed, we can and an option for it and use something like this
147 //if (did_action('plugins_loaded')) {
148 // $this->runShortcodes();
149 //} else {
150 // add_action('plugins_loaded', [$this, 'runShortcodes']);
151 //}
152 }
153
154 private function validateNonce($tags) {
155 if (empty($_POST['nonce']) || empty($_POST['tags']) || !is_string($_POST['tags'])) {
156 die(__('Security check', 'nitropack'));
157 }
158
159 $action = $this->getNonceAction($tags);
160 if (!wp_verify_nonce($_POST['nonce'], $action)) die(__('Security check', 'nitropack'));
161 }
162
163 public function runShortcodes() {
164 $tags = !empty($_POST["tags"]) && is_string($_POST["tags"]) ? json_decode(base64_decode($_POST['tags']), true) : [];
165 if (empty($tags)) wp_send_json_error(["message" => "Invalid shortcode input"], 400);
166 $this->validateNonce($tags);
167
168 $resp = [];
169 foreach ($tags as $sc) {
170 $attrFlat = is_array($sc["attr"]) ? array_map(function ($k, $v) {
171 return "$k=$v";
172 }, array_keys($sc["attr"]), array_values($sc["attr"])) : [];
173 $arrKey = base64_encode(json_encode($sc));
174 $resp[$arrKey] = do_shortcode("[{$sc['tag']} " . implode(" ", $attrFlat) . "]");
175 }
176
177 wp_send_json_success($resp);
178 }
179 }
180