PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / trunk
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization vtrunk
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
Logger 3 months ago AjaxShortcodes.php 11 months ago SubrequestCache.php 1 year ago
AjaxShortcodes.php
193 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 /* Merge shortcodes from wp-config and site config */
79 private function mergeShortcodes() {
80
81 $siteConfig = get_nitropack()->getSiteConfig();
82 $options = $siteConfig['options_cache'];
83
84 $ajax_shortcodes = isset($options['ajaxShortcodes']) ? $options['ajaxShortcodes'] : null; //initially ajaxShortcodes is empty
85
86 if (self::isEnabled() && (!empty($ajax_shortcodes['shortcodes']) && is_array($ajax_shortcodes['shortcodes']))) {
87 $this->shortcodes = $ajax_shortcodes['shortcodes'];
88 }
89 if (defined("NITROPACK_AJAX_SHORTCODES") && strpos(NITROPACK_AJAX_SHORTCODES, ',') !== false) {
90 $wp_config_shortcodes = explode(',', NITROPACK_AJAX_SHORTCODES);
91 $this->shortcodes = array_unique(array_merge($this->shortcodes, $wp_config_shortcodes));
92 }
93 }
94 public function init($stage) {
95 if (!defined("NITROPACK_AJAX_SHORTCODES") && !self::isEnabled()) {
96 // This init method can be run at any stage. This gives the opportunity to define the constant at a later point
97 // For example in a MU plugin
98 return true;
99 }
100 $this->mergeShortcodes();
101 //stop execution if no shortcodes are found
102 if (!$this->shortcodes) return;
103
104 add_filter('pre_do_shortcode_tag', function ($out, $tag, $attr) {
105
106 if (defined("NITRO_DOING_AJAX_SHORTCODES") && NITRO_DOING_AJAX_SHORTCODES) return $out;
107
108 if (in_array($tag, $this->shortcodes)) {
109 $entry = ["tag" => $tag, "attr" => $attr];
110 if (!in_array($entry, $this->processedShortcodes)) {
111 $this->processedShortcodes[] = $entry;
112 }
113 $np_out = '<span class="nitro-sc-load" data-sc-meta-id="' . base64_encode(json_encode($entry)) . '"></span>';
114
115 return $np_out;
116 }
117
118 return $out;
119 }, 10, 3);
120
121
122 add_action('wp_ajax_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));
123 add_action('wp_ajax_nopriv_nitro_shortcode_ajax', array($this, 'shortcodeAjax'));
124
125 add_action('wp_enqueue_scripts', [$this, 'add_scripts']);
126 $this->startBuffering();
127 }
128
129 private function startBuffering() {
130 ob_start(function($buffer) {
131 return str_replace($this->emptyScript, $this->generateNitroScLoader(), $buffer);
132 }, 0, PHP_OUTPUT_HANDLER_FLUSHABLE | PHP_OUTPUT_HANDLER_REMOVABLE);
133 }
134
135 public function add_scripts() {
136 wp_register_script($this->scriptHandle, '', [], '1.0.0', false);
137 wp_add_inline_script($this->scriptHandle, $this->emptyScript);
138 wp_enqueue_script($this->scriptHandle);
139
140 add_filter('wp_inline_script_attributes', function ($attr, $js) {
141
142 if(isset($attr['id']) && strpos($attr['id'], $this->scriptHandle) === 0) {
143 $attr = array_merge(array( 'nitro-exclude' => true ), $attr);
144 }
145
146 return $attr;
147
148 }, 10, 2);
149 }
150
151 public function shortcodeAjax() {
152 if (!defined("NITRO_DOING_AJAX_SHORTCODES")) define("NITRO_DOING_AJAX_SHORTCODES", true);
153 $this->runShortcodes();
154
155 // In case a later hook is needed, we can and an option for it and use something like this
156 //if (did_action('plugins_loaded')) {
157 // $this->runShortcodes();
158 //} else {
159 // add_action('plugins_loaded', [$this, 'runShortcodes']);
160 //}
161 }
162
163 private function validateNonce($tags) {
164 if (empty($_POST['nonce']) || empty($_POST['tags']) || !is_string($_POST['tags'])) {
165 die(__('Security check', 'nitropack'));
166 }
167
168 $action = $this->getNonceAction($tags);
169 if (!wp_verify_nonce($_POST['nonce'], $action)) die(__('Security check', 'nitropack'));
170 }
171
172 public function runShortcodes() {
173 $tags = !empty($_POST["tags"]) && is_string($_POST["tags"]) ? json_decode(base64_decode($_POST['tags']), true) : [];
174 if (empty($tags)) wp_send_json_error(["message" => "Invalid shortcode input"], 400);
175 $this->validateNonce($tags);
176
177 $resp = [];
178 foreach ($tags as $sc) {
179 $attrFlat = is_array($sc["attr"]) ? array_map(function ($k, $v) {
180 return "$k=\"$v\"";
181 }, array_keys($sc["attr"]), array_values($sc["attr"])) : [];
182 $arrKey = base64_encode(json_encode($sc));
183 $resp[$arrKey] = do_shortcode("[{$sc['tag']} " . implode(" ", $attrFlat) . "]");
184 }
185
186 wp_send_json_success($resp);
187 }
188 public function isEnabled() {
189 $siteConfig = get_nitropack()->getSiteConfig();
190 return !empty($siteConfig['options_cache']['ajaxShortcodes']['enabled'])
191 && $siteConfig['options_cache']['ajaxShortcodes']['enabled'] == 1;
192 }
193 }