PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.18.0
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.18.0
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 / functions.php
nitropack Last commit date
classes 1 year ago languages 1 year ago nitropack-sdk 1 year ago view 1 year ago advanced-cache.php 2 years ago batcache-compat.php 4 years ago constants.php 1 year ago diagnostics.php 2 years ago functions.php 1 year ago helpers.php 3 years ago main.php 1 year ago readme.txt 1 year ago uninstall.php 1 year ago
functions.php
4134 lines
1 <?php
2
3 defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
4
5 $np_basePath = dirname( __FILE__ ) . '/';
6 require_once $np_basePath . 'nitropack-sdk/autoload.php';
7 require_once $np_basePath . 'constants.php';
8
9 $np_originalRequestCookies = $_COOKIE;
10 $np_customExpirationTimes = array();
11 $np_queriedObj = NULL;
12 $np_loggedPurges = array();
13 $np_loggedInvalidations = array();
14 $np_integrationSetupEvent = "muplugins_loaded";
15
16 function nitropack_is_logged_in() {
17 $nitro = get_nitropack_sdk();
18 $useAccountOverride = $nitro !== NULL && $nitro->isStatefulCacheSatisfied( "account" );
19 if ( $useAccountOverride ) {
20 return false;
21 }
22
23 $loginCookies = array( defined( 'NITROPACK_LOGGED_IN_COOKIE' ) ? NITROPACK_LOGGED_IN_COOKIE : ( defined( 'LOGGED_IN_COOKIE' ) ? LOGGED_IN_COOKIE : '' ) );
24 foreach ( $loginCookies as $loginCookie ) {
25 if ( ! empty( $_COOKIE[ $loginCookie ] ) ) {
26 $parts = explode( '|', urldecode( $_COOKIE[ $loginCookie ] ) );
27 if ( count( $parts ) < 3 ) {
28 continue; // Invalid cookie
29 }
30
31 return time() <= (int) $parts[1];
32 }
33 }
34 $cookieStr = implode( "|", array_keys( $_COOKIE ) );
35 return strpos( $cookieStr, "wordpress_logged_in_" ) !== false;
36 }
37
38 function nitropack_passes_cookie_requirements() {
39 $isUserLoggedIn = nitropack_is_logged_in();
40 $cookieStr = implode( "|", array_keys( $_COOKIE ) );
41 $safeCookie = (
42 ( strpos( $cookieStr, "comment_author" ) === false || ! ! get_nitropack()->setDisabledReason( "comment author" ) )
43 && ( strpos( $cookieStr, "wp-postpass_" ) === false || ! ! get_nitropack()->setDisabledReason( "password protected page" ) )
44 );
45
46 $isItemsInCart = ! empty( $_COOKIE["woocommerce_items_in_cart"] );
47 $useCartOverride = nitropack_is_cart_cache_active();
48
49 if ( $isUserLoggedIn ) {
50 get_nitropack()->setDisabledReason( "logged in" );
51 }
52
53 if ( $isItemsInCart && ! $useCartOverride ) {
54 get_nitropack()->setDisabledReason( "items in cart" );
55 }
56
57 // allow registering filters to "nitropack_passes_cookie_requirements"
58 return apply_filters( "nitropack_passes_cookie_requirements", $safeCookie && ( ! $isItemsInCart || $useCartOverride ) && ( ! $isUserLoggedIn ) );
59 }
60
61 function nitropack_activate() {
62 nitropack_set_wp_cache_const( true );
63
64 $htaccessFile = nitropack_trailingslashit( NITROPACK_DATA_DIR ) . ".htaccess";
65 if ( ! file_exists( $htaccessFile ) && get_nitropack()->initDataDir() ) {
66 file_put_contents( $htaccessFile, "deny from all" );
67 }
68
69 $pluginHtaccessFile = nitropack_trailingslashit( NITROPACK_PLUGIN_DATA_DIR ) . ".htaccess";
70 if ( ! file_exists( $pluginHtaccessFile ) && get_nitropack()->initPluginDataDir() ) {
71 file_put_contents( $pluginHtaccessFile, "deny from all" ); // TODO: Convert this to use the Filesystem abstraction for better Redis support
72 }
73
74 nitropack_install_advanced_cache();
75
76 // Htaccess mods need to happen after installing the advanced cache file so the healthcheck can execute fast
77 nitropack_set_htaccess_rules( true );
78
79 try {
80 do_action( 'nitropack_integration_purge_all' );
81 } catch (\Exception $e) {
82 // Exception while signaling our 3rd party integration addons to purge their cache
83 }
84
85 if ( get_nitropack()->isConnected() ) {
86 nitropack_event( "enable_extension" );
87 // Refresh needed to make sure we have the latest config
88 get_nitropack()->settings->set_required_settings();
89 } else {
90 setcookie( "nitropack_after_activate_notice", 1, time() + 3600 );
91 }
92
93 if ( function_exists( "opcache_reset" ) ) {
94 opcache_reset();
95 }
96 ( new \NitroPack\WordPress\Cron() )->schedule_events();
97 }
98
99 function nitropack_deactivate() {
100 nitropack_set_htaccess_rules( false );
101 nitropack_set_wp_cache_const( false );
102 nitropack_uninstall_advanced_cache();
103
104 try {
105 do_action( 'nitropack_integration_purge_all' );
106 } catch (\Exception $e) {
107 // Exception while signaling our 3rd party integration addons to purge their cache
108 }
109
110 if ( get_nitropack()->isConnected() ) {
111 nitropack_event( "disable_extension" );
112 }
113
114 if ( function_exists( "opcache_reset" ) ) {
115 opcache_reset();
116 }
117 // Unscheduling events from the cron.
118 \NitroPack\WordPress\Cron::unschedule_events();
119 }
120
121 function nitropack_install_advanced_cache() {
122 if ( nitropack_is_conflicting_plugin_active() )
123 return false;
124 if ( ! nitropack_is_advanced_cache_allowed() )
125 return false;
126
127 $templatePath = nitropack_trailingslashit( __DIR__ ) . "advanced-cache.php";
128 if ( file_exists( $templatePath ) ) {
129 $contents = file_get_contents( $templatePath );
130 $contents = str_replace( "/*NITROPACK_FUNCTIONS_FILE*/", __FILE__, $contents );
131 $contents = str_replace( "/*NITROPACK_ABSPATH*/", ABSPATH, $contents );
132 $contents = str_replace( "/*LOGIN_COOKIES*/", defined( "LOGGED_IN_COOKIE" ) ? LOGGED_IN_COOKIE : "", $contents );
133 $contents = str_replace( "/*NP_VERSION*/", NITROPACK_VERSION, $contents );
134
135 $advancedCacheFile = nitropack_trailingslashit( WP_CONTENT_DIR ) . 'advanced-cache.php';
136 if ( WP_DEBUG ) {
137 return file_put_contents( $advancedCacheFile, $contents );
138 } else {
139 return @file_put_contents( $advancedCacheFile, $contents );
140 }
141 }
142 }
143
144 function nitropack_uninstall_advanced_cache() {
145 $advancedCacheFile = nitropack_trailingslashit( WP_CONTENT_DIR ) . 'advanced-cache.php';
146 if ( file_exists( $advancedCacheFile ) ) {
147 if ( WP_DEBUG ) {
148 return file_put_contents( $advancedCacheFile, "" );
149 } else {
150 return @file_put_contents( $advancedCacheFile, "" );
151 }
152 }
153 }
154
155 function nitropack_set_wp_cache_const( $status ) {
156 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) { // Flywheel: This is configured throught the FW control panel
157 return true;
158 }
159
160 if ( \NitroPack\Integration\Hosting\Pressable::detect() ) { // Pressable: We need to deal with Batcache here
161 return nitropack_set_batcache_compat( $status );
162 }
163
164 $configFilePath = nitropack_get_wpconfig_path();
165 if ( ! $configFilePath )
166 return false;
167
168 $newVal = sprintf( "define( 'WP_CACHE', %s /* Modified by NitroPack */ );\n", ( $status ? "true" : "false" ) );
169 $replacementVal = sprintf( " %s /* Modified by NitroPack */ ", ( $status ? "true" : "false" ) );
170 $lines = file( $configFilePath );
171
172 if ( empty( $lines ) )
173 return false;
174
175 $wpCacheFound = false;
176 $phpOpeningTagLine = false;
177
178 foreach ( $lines as $lineIndex => &$line ) {
179 if ( strpos( $line, "<?php" ) !== false && strpos( $line, "?>" ) === false ) {
180 $phpOpeningTagLine = $lineIndex;
181 }
182
183 if ( ! $wpCacheFound && preg_match( "/define\s*\(\s*['\"](.*?)['\"].?,(.*?)\)/", $line, $matches ) ) {
184 if ( $matches[1] == "WP_CACHE" ) {
185 $line = str_replace( $matches[2], $replacementVal, $line );
186 $wpCacheFound = true;
187 }
188 }
189
190 if ( $phpOpeningTagLine !== false && $wpCacheFound !== false )
191 break;
192 }
193
194 if ( ! $wpCacheFound ) {
195 if ( ! $status )
196 return true; // No need to modify the file at all
197
198 if ( $phpOpeningTagLine !== false ) {
199 array_splice( $lines, $phpOpeningTagLine + 1, 0, [ $newVal ] );
200 } else {
201 array_unshift( $lines, "<?php " . trim( $newVal ) . " ?>\n" );
202 }
203 }
204
205 return WP_DEBUG ? file_put_contents( $configFilePath, implode( "", $lines ) ) : @file_put_contents( $configFilePath, implode( "", $lines ) );
206 }
207
208 function nitropack_set_htaccess_rules( $status ) {
209 if ( ! apply_filters( 'nitropack_should_modify_htaccess', false ) )
210 return true;
211
212 $htaccessFilePath = nitropack_get_htaccess_path();
213 if ( ! $htaccessFilePath )
214 return false;
215
216 $htaccessBackupFilePath = $htaccessFilePath . ".nitrobackup";
217 $backupExists = WP_DEBUG ? file_exists( $htaccessBackupFilePath ) : @file_exists( $htaccessBackupFilePath );
218 if ( ! $backupExists ) {
219 $isBackupSuccess = WP_DEBUG ? copy( $htaccessFilePath, $htaccessBackupFilePath ) : @copy( $htaccessFilePath, $htaccessBackupFilePath );
220 if ( ! $isBackupSuccess )
221 return false;
222 }
223
224 $lines = file( $htaccessFilePath );
225 $linesBackup = $lines;
226
227 if ( empty( $lines ) )
228 return false; // We might want to remove this check
229
230 // Remove the old LiteSpeed rules. We need to do this because LiteSpeed's rules are not compatible with NitroPack's rules.
231 if ( $status ) {
232
233 $nitroLsOpenLine = false;
234 $nitroLsCloseLine = false;
235
236 foreach ( $lines as $lineIndex => &$line ) {
237 if ( trim( $line ) == "# BEGIN LSCACHE" ) {
238 $nitroLsOpenLine = $lineIndex;
239 }
240
241 if ( trim( $line ) == "# END LSCACHE" ) {
242 $nitroLsCloseLine = $lineIndex;
243 }
244 }
245
246 if ( $nitroLsOpenLine !== false && $nitroLsCloseLine !== false && $nitroLsCloseLine > $nitroLsOpenLine ) {
247
248 array_splice( $lines, $nitroLsOpenLine, $nitroLsCloseLine - $nitroLsOpenLine + 1 );
249 }
250 }
251
252 $nitroOpenLine = false;
253 $nitroCloseLine = false;
254
255 foreach ( $lines as $lineIndex => &$line ) {
256 if ( trim( $line ) == "# BEGIN NITROPACK" ) {
257 $nitroOpenLine = $lineIndex;
258 }
259
260 if ( trim( $line ) == "# END NITROPACK" ) {
261 $nitroCloseLine = $lineIndex;
262 }
263 }
264
265 $nitroLines = [];
266
267 if ( // We either didn't find the NitroPack markers or we found both in the correct order
268 ( $nitroOpenLine === false && $nitroCloseLine === false ) ||
269 ( $nitroOpenLine !== false && $nitroCloseLine !== false && $nitroCloseLine > $nitroOpenLine )
270 ) {
271 $nitroLines[] = "# BEGIN NITROPACK";
272 if ( $status ) {
273 $rules = apply_filters( "nitropack_htaccess_rules", [] );
274
275 if ( is_string( $rules ) ) {
276 $rules = explode( "\n", $rules );
277 }
278
279 if ( is_array( $rules ) ) {
280 $nitroLines = array_merge( $nitroLines, $rules );
281 }
282 }
283 $nitroLines[] = "# END NITROPACK";
284 $nitroLines = array_map( function ($line) {
285 return trim( $line ) . "\n";
286 }, $nitroLines );
287
288 // Begin .htaccess modification
289 $offset = $nitroOpenLine !== false ? $nitroOpenLine : 0;
290 $length = $nitroOpenLine !== false ? $nitroCloseLine - $nitroOpenLine + 1 : 0;
291 array_splice( $lines, $offset, $length, $nitroLines );
292 $writeResult = WP_DEBUG ? file_put_contents( $htaccessFilePath, implode( "", $lines ) ) : @file_put_contents( $htaccessFilePath, implode( "", $lines ) );
293 if ( $writeResult ) {
294 $homeUrl = NULL;
295 $siteConfig = get_nitropack()->getSiteConfig();
296
297 if ( $siteConfig && ! empty( $siteConfig["home_url"] ) ) {
298 $homeUrl = $siteConfig["home_url"];
299 } else if ( function_exists( get_home_url() ) ) {
300 $homeUrl = get_home_url();
301 }
302
303 if ( $homeUrl ) {
304 $homeUrl .= ( strpos( $homeUrl, "?" ) === false ? "?" : "&" ) . "nitroHealthcheck=1";
305 try {
306 $client = new \NitroPack\HttpClient\HttpClient( $homeUrl );
307 $client->timeout = 5;
308 $client->setHeader( "Accept", "text/html" );
309 $client->fetch();
310 if ( $client->getStatusCode() != 200 ) {
311 // Restore the initial version of the file
312 WP_DEBUG ? file_put_contents( $htaccessFilePath, implode( "", $linesBackup ) ) : @file_put_contents( $htaccessFilePath, implode( "", $linesBackup ) );
313 return false;
314 }
315 } catch (\Exception $e) {
316 return false;
317 // Unfortunately we can't be certain whether an issue appeared due to the .htaccess mods
318 // There are no known cases of this happening, so it's fairly safe to assume that all is fine
319 // There are server setups which do not allow loopback requests, which is the more likely reason to end up here
320 // However we can't be certain which one it is, so we are taking the safer approach
321 }
322 } else {
323 return false;
324 }
325 }
326 return $writeResult;
327 }
328
329 return true;
330 }
331
332 function nitropack_set_batcache_compat( $status ) {
333 $currentCompatStatus = defined( "NITROPACK_BATCACHE_COMPAT" ) && NITROPACK_BATCACHE_COMPAT;
334 if ( $currentCompatStatus === $status )
335 return true;
336
337 $configFilePath = nitropack_get_wpconfig_path();
338 if ( ! $configFilePath )
339 return false;
340
341 $batCacheFilePath = NITROPACK_PLUGIN_DIR . "batcache-compat.php";
342 $compatInclude = sprintf( "if (file_exists(\"%s\")) { require_once \"%s\"; } // NitroPack compatibility with Batcache\n", $batCacheFilePath, $batCacheFilePath );
343 $lines = file( $configFilePath );
344
345 if ( empty( $lines ) )
346 return false;
347
348 foreach ( $lines as $lineIndex => &$line ) {
349 if ( preg_match( "/nitropack.*?batcache/i", $line ) ) {
350 $line = "//REMOVE AT FILTER";
351 }
352 }
353
354 $newLines = array_filter( $lines, function ($line) {
355 return $line != "//REMOVE AT FILTER";
356 } );
357
358 if ( $status ) {
359 $phpOpeningTagLine = false;
360
361 unset( $line );
362 foreach ( $newLines as $lineIndex => $line ) {
363 if ( strpos( $line, "<?php" ) !== false && strpos( $line, "?>" ) === false ) {
364 $phpOpeningTagLine = $lineIndex;
365 break;
366 }
367 }
368
369 if ( $phpOpeningTagLine !== false ) {
370 array_splice( $newLines, $phpOpeningTagLine + 1, 0, [ $compatInclude ] );
371 } else {
372 array_unshift( $newLines, "<?php " . trim( $compatInclude ) . " ?>\n" );
373 }
374 }
375
376 return WP_DEBUG ? file_put_contents( $configFilePath, implode( "", $newLines ) ) : @file_put_contents( $configFilePath, implode( "", $newLines ) );
377 }
378
379 function is_valid_nitropack_webhook() {
380 return ! empty( $_GET["nitroWebhook"] ) && ! empty( $_GET["token"] ) && nitropack_validate_webhook_token( $_GET["token"] );
381 }
382
383 function is_valid_nitropack_beacon() {
384 if ( ! isset( $_POST["nitroBeaconUrl"] ) || ! isset( $_POST["nitroBeaconHash"] ) )
385 return false;
386
387 $siteConfig = nitropack_get_site_config();
388 if ( ! $siteConfig || empty( $siteConfig["siteSecret"] ) )
389 return false;
390
391
392 if ( function_exists( "hash_hmac" ) && function_exists( "hash_equals" ) ) {
393 $url = base64_decode( $_POST["nitroBeaconUrl"] );
394 $cookiesJson = ! empty( $_POST["nitroBeaconCookies"] ) ? base64_decode( $_POST["nitroBeaconCookies"] ) : ""; // We need to fall back to empty string to remain backwards compatible. Otherwise cache files invalidated before an upgrade will never get updated :(
395 $layout = ! empty( $_POST["layout"] ) ? $_POST["layout"] : "";
396 $localHash = hash_hmac( "sha512", $url . $cookiesJson . $layout, $siteConfig["siteSecret"] );
397 return hash_equals( $_POST["nitroBeaconHash"], $localHash );
398 } else {
399 return ! empty( $_POST["nitroBeaconUrl"] );
400 }
401 }
402
403 function nitropack_handle_beacon() {
404 global $np_originalRequestCookies;
405 if ( ! defined( "NITROPACK_BEACON_HANDLED" ) ) {
406 define( "NITROPACK_BEACON_HANDLED", 1 );
407 } else {
408 return;
409 }
410
411 $siteConfig = nitropack_get_site_config();
412 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) && ! empty( $_POST["nitroBeaconUrl"] ) ) {
413 $url = base64_decode( $_POST["nitroBeaconUrl"] );
414
415 if ( ! empty( $_POST["nitroBeaconCookies"] ) ) {
416 $np_originalRequestCookies = json_decode( base64_decode( $_POST["nitroBeaconCookies"] ), true );
417 }
418
419 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Beacon request received for URL: ' . $url );
420
421 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"], $url ) ) {
422 try {
423 $hasLocalCache = $nitro->hasLocalCache( false );
424 $needsHeartbeat = nitropack_is_heartbeat_needed();
425 $proxyPurgeOnly = ! empty( $_POST["proxyPurgeOnly"] );
426 $layout = ! empty( $_POST["layout"] ) ? $_POST["layout"] : "default";
427 $output = "";
428
429 if ( ! $proxyPurgeOnly ) {
430 if ( ! $hasLocalCache ) {
431 nitropack_header( "X-Nitro-Beacon: FORWARD" );
432 try {
433 $hasCache = $nitro->hasRemoteCache( $layout, false ); // Download the new cache file
434 $hasLocalCache = $hasCache;
435 $output = sprintf( "Cache %s", $hasCache ? "fetched" : "requested" );
436 } catch (\Exception $e) {
437 // not a critical error, do nothing
438 }
439 } else {
440 nitropack_header( "X-Nitro-Beacon: SKIP" );
441 $output = sprintf( "Cache exists already" );
442 }
443 }
444
445 if ( $hasLocalCache || $proxyPurgeOnly/* || $needsHeartbeat*/ ) { // proxyPurgeOnly is set for unsupported browsers, in which case we need to purge the cache regardless of the existence of local NP cache
446 nitropack_header( "X-Nitro-Proxy-Purge: true" );
447 $nitro->purgeProxyCache( $url );
448 do_action( 'nitropack_integration_purge_url', $url );
449 }
450
451 \NitroPack\ModuleHandler::onShutdown( function () use ($output) {
452 echo $output;
453 } );
454 } catch (Exception $e) {
455 // not a critical error, do nothing
456 }
457 }
458 }
459 \NitroPack\ModuleHandler::onCriticalInit( function () {
460 exit;
461 } );
462 }
463
464 /**
465 * Handle NitroPack webhooks
466 *
467 * @return void
468 */
469 /**
470 * Handles the NitroPack webhook request
471 *
472 * @return void
473 */
474 function nitropack_handle_webhook() {
475 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
476 do_action( 'nitropack_debug_webhook', $_REQUEST );
477 }
478 if ( ! defined( "NITROPACK_WEBHOOK_HANDLED" ) ) {
479 define( "NITROPACK_WEBHOOK_HANDLED", 1 );
480 } else {
481 return;
482 }
483
484 $siteConfig = nitropack_get_site_config();
485 if ( $siteConfig && $siteConfig["webhookToken"] == $_GET["token"] ) {
486 switch ( $_GET["nitroWebhook"] ) {
487 case "config":
488 nitropack_fetch_config();
489 get_nitropack()->resetSdkInstances(); // This is needed in order to obtain a new SDK instance with the fresh config
490 nitropack_set_htaccess_rules( true );
491 if ( null !== $nitro = get_nitropack_sdk() ) {
492 $nitro->purgeProxyCache();
493 }
494 do_action( 'nitropack_integration_purge_all' );
495 break;
496 case "cache_ready":
497 if ( isset( $_POST['url'] ) ) {
498 $urls = array( $_POST['url'] );
499 } elseif ( isset( $_POST['urls'] ) ) {
500 $urls = $_POST['urls'];
501 } else {
502 $urls = array();
503 }
504 if ( ! empty( $urls ) ) {
505 $readyUrls = [];
506 foreach ( $urls as $url ) {
507 $readyUrl = nitropack_sanitize_url_input( $url );
508 if ( $readyUrl ) {
509 $readyUrls[] = $readyUrl;
510 }
511 }
512
513 if ( $readyUrls && null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"], $readyUrls[0] ) ) {
514 $hasCache = $nitro->hasRemoteCacheMulti( $readyUrls, "default", false ); // Download the new cache file
515 foreach ( $readyUrls as $readyUrl ) {
516 $nitro->purgeProxyCache( $readyUrl );
517 do_action( 'nitropack_integration_purge_url', $readyUrl );
518 }
519 }
520 }
521 break;
522 case "cache_clear":
523 if ( isset( $_POST['url'] ) ) {
524 $urls = array( $_POST['url'] );
525 } elseif ( isset( $_POST['urls'] ) ) {
526 $urls = $_POST['urls'];
527 }
528
529 $proxyPurgeOnly = ! empty( $_POST["proxyPurgeOnly"] );
530 $doAction = ! empty( $_POST['useInvalidate'] )
531 ? static function ($url = null) {
532 nitropack_sdk_invalidate_local( $url );
533 }
534 : static function ($url = null) {
535 nitropack_sdk_purge_local( $url );
536 };
537
538 if ( ! empty( $_POST["url"] ) ) {
539 $urls = is_array( $_POST["url"] ) ? $_POST["url"] : array( $_POST["url"] );
540 foreach ( $urls as $url ) {
541 $sanitizedUrl = nitropack_sanitize_url_input( $url );
542 if ( $proxyPurgeOnly ) {
543 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
544 $nitro->purgeProxyCache( $sanitizedUrl );
545 }
546 do_action( 'nitropack_integration_purge_url', $sanitizedUrl );
547 } else {
548 $doAction( $sanitizedUrl );
549 }
550 }
551 } else {
552 if ( $proxyPurgeOnly ) {
553 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
554 $nitro->purgeProxyCache();
555 }
556 do_action( 'nitropack_integration_purge_all' );
557 } else {
558 $doAction();
559 nitropack_sdk_delete_backlog();
560 }
561 }
562 break;
563 }
564 }
565 \NitroPack\ModuleHandler::onCriticalInit( function () {
566 nitropack_json_and_exit( array(
567 "type" => "success",
568 ) );
569 } );
570 }
571
572 function nitropack_sanitize_url_input( $url ) {
573 $result = NULL;
574 if ( ! function_exists( "esc_url" ) ) {
575 $sanitizedUrl = filter_var( $url, FILTER_SANITIZE_URL );
576 if ( $sanitizedUrl !== false && filter_var( $sanitizedUrl, FILTER_VALIDATE_URL ) !== false ) {
577 $result = $sanitizedUrl;
578 }
579 } else if ( $validatedUrl = esc_url( $url, array( "http", "https" ), "notdisplay" ) ) {
580 $result = $validatedUrl;
581 }
582
583 return $result;
584 }
585
586 function nitropack_is_amp_page() {
587 return ( function_exists( 'amp_is_request' ) && amp_is_request() && ! get_nitropack()->setDisabledReason( "amp page" ) ) ||
588 ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() && ! get_nitropack()->setDisabledReason( "amp page" ) );
589 }
590
591 function nitropack_passes_page_requirements( $detectIfNoCachedResult = true ) {
592 static $cachedResult = NULL;
593 $reduceCheckoutChecks = defined( "NITROPACK_REDUCE_CHECKOUT_CHECKS" ) && NITROPACK_REDUCE_CHECKOUT_CHECKS;
594 $reduceCartChecks = defined( "NITROPACK_REDUCE_CART_CHECKS" ) && NITROPACK_REDUCE_CART_CHECKS;
595
596 if ( $cachedResult === NULL && $detectIfNoCachedResult ) {
597 $cachedResult = ! (
598 ( is_404() && ! get_nitropack()->setDisabledReason( "404" ) ) ||
599 ( is_preview() && ! get_nitropack()->setDisabledReason( "preview page" ) ) ||
600 ( is_feed() && ! get_nitropack()->setDisabledReason( "feed" ) ) ||
601 ( is_comment_feed() && ! get_nitropack()->setDisabledReason( "comment feed" ) ) ||
602 ( is_trackback() && ! get_nitropack()->setDisabledReason( "trackback" ) ) ||
603 ( nitropack_is_logged_in() && ! get_nitropack()->setDisabledReason( "logged in" ) ) ||
604 ( is_search() && ! get_nitropack()->setDisabledReason( "search" ) ) ||
605 ( nitropack_is_ajax() && ! get_nitropack()->setDisabledReason( "ajax" ) ) ||
606 ( nitropack_is_post() && ! get_nitropack()->setDisabledReason( "post request" ) ) ||
607 ( nitropack_is_xmlrpc() && ! get_nitropack()->setDisabledReason( "xmlrpc" ) ) ||
608 ( nitropack_is_robots() && ! get_nitropack()->setDisabledReason( "robots" ) ) ||
609 nitropack_is_amp_page() ||
610 ! nitropack_is_allowed_request() ||
611 ( nitropack_is_wp_cron() && ! get_nitropack()->setDisabledReason( "doing cron" ) ) || // CRON request
612 ( nitropack_is_wp_cli() ) || // CLI request
613 ( defined( 'WC_PLUGIN_FILE' ) && ( is_page( 'cart' ) || ( ! $reduceCartChecks && is_cart() ) ) && ! get_nitropack()->setDisabledReason( "cart page" ) ) || // WooCommerce
614 ( defined( 'WC_PLUGIN_FILE' ) && ( is_page( 'checkout' ) || ( ! $reduceCheckoutChecks && is_checkout() ) ) && ! get_nitropack()->setDisabledReason( "checkout page" ) ) || // WooCommerce
615 ( defined( 'WC_PLUGIN_FILE' ) && is_account_page() && ! get_nitropack()->setDisabledReason( "account page" ) ) // WooCommerce
616 );
617 }
618
619 return $cachedResult;
620 }
621
622 function nitropack_is_home() {
623 if ( 'posts' == get_option( 'show_on_front' ) ) {
624 return is_front_page() || is_home();
625 } else {
626 return is_front_page();
627 }
628 }
629
630 function nitropack_is_blogindex() {
631 return is_home();
632 }
633
634 function nitropack_is_archive() {
635 return apply_filters( "nitropack_is_archive_page", is_author() || is_archive() );
636 }
637
638 function nitropack_is_allowed_request() {
639 global $np_queriedObj;
640 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
641 if ( is_array( $cacheableObjectTypes ) ) {
642 if ( nitropack_is_home() ) {
643 if ( ! in_array( 'home', $cacheableObjectTypes ) ) {
644 get_nitropack()->setDisabledReason( "page type not allowed (home)" );
645 return false;
646 }
647 } else {
648 if ( is_tax() || is_category() || is_tag() ) {
649 $np_queriedObj = get_queried_object();
650 if ( ! empty( $np_queriedObj ) && ! in_array( $np_queriedObj->taxonomy, $cacheableObjectTypes ) ) {
651 get_nitropack()->setDisabledReason( "page type not allowed ({$np_queriedObj->taxonomy})" );
652 return false;
653 }
654 } else {
655 if ( nitropack_is_archive() ) {
656 if ( ! in_array( 'archive', $cacheableObjectTypes ) ) {
657 get_nitropack()->setDisabledReason( "page type not allowed (archive)" );
658 return false;
659 }
660 } else {
661 $postType = get_post_type();
662 if ( ! empty( $postType ) && ! in_array( $postType, $cacheableObjectTypes ) ) {
663 get_nitropack()->setDisabledReason( "page type not allowed ($postType)" );
664 return false;
665 }
666 }
667 }
668 }
669 }
670
671 if ( null !== $nitro = get_nitropack_sdk() ) {
672 return ( $nitro->isAllowedUrl( $nitro->getUrl() ) || get_nitropack()->setDisabledReason( "url not allowed" ) ) &&
673 ( $nitro->isAllowedRequest( true ) || get_nitropack()->setDisabledReason( "request type not allowed" ) );
674 }
675
676 get_nitropack()->setDisabledReason( "site not connected" );
677 return false;
678 }
679
680 function nitropack_is_ajax() {
681 return ( function_exists( "wp_doing_ajax" ) && wp_doing_ajax() ) ||
682 ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
683 ( ! empty( $_SERVER["HTTP_X_REQUESTED_WITH"] ) && $_SERVER["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ) ||
684 ( ! empty( $_SERVER["REQUEST_URI"] ) && basename( $_SERVER["REQUEST_URI"] ) == "admin-ajax.php" ) ||
685 ! empty( $_GET["wc-ajax"] );
686 }
687
688 /**
689 * Checking if the current request is wp-cli request
690 *
691 * @return bool
692 */
693 function nitropack_is_wp_cli() {
694 return NitroPack\WordPress\NitroPack::isWpCli();
695 }
696
697 function nitropack_is_wp_cron() {
698 return defined( 'DOING_CRON' ) && DOING_CRON;
699 }
700
701 function nitropack_is_rest() {
702 // Source: https://wordpress.stackexchange.com/a/317041
703 $prefix = rest_get_url_prefix();
704 if (
705 defined( 'REST_REQUEST' ) && REST_REQUEST // (#1)
706 || isset( $_GET['rest_route'] ) // (#2)
707 && strpos( trim( $_GET['rest_route'], '\\/' ), $prefix, 0 ) === 0
708 )
709 return true;
710 // (#3)
711 global $wp_rewrite;
712 if ( $wp_rewrite === null )
713 $wp_rewrite = new WP_Rewrite();
714
715 // (#4)
716 $rest_url = wp_parse_url( trailingslashit( rest_url() ) );
717 $current_url = wp_parse_url( add_query_arg( array() ) );
718 return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
719 }
720
721 function nitropack_is_post() {
722 return ( ! empty( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' ) || ( empty( $_SERVER['REQUEST_METHOD'] ) && ! empty( $_POST ) );
723 }
724
725 function nitropack_is_xmlrpc() {
726 return defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST;
727 }
728
729 function nitropack_is_robots() {
730 return is_robots() || ( ! empty( $_SERVER["REQUEST_URI"] ) && basename( parse_url( $_SERVER["REQUEST_URI"], PHP_URL_PATH ) ) === "robots.txt" );
731 }
732
733 // IMPORTANT: This function should only be trusted if NitroPack is connected. Otherwise we may not have information about the admin URL in the config file and it may return an incorrect result
734 function nitropack_is_admin() {
735 if ( ( nitropack_is_ajax() || nitropack_is_rest() ) && ! empty( $_SERVER["HTTP_REFERER"] ) ) {
736 $adminUrl = NULL;
737 $siteConfig = nitropack_get_site_config();
738 if ( $siteConfig && ! empty( $siteConfig["admin_url"] ) ) {
739 $adminUrl = $siteConfig["admin_url"];
740 } else if ( function_exists( "admin_url" ) ) {
741 $adminUrl = admin_url();
742 } else {
743 return is_admin();
744 }
745
746 return strpos( $_SERVER["HTTP_REFERER"], $adminUrl ) === 0;
747 } else {
748 return is_admin();
749 }
750 }
751
752 function nitropack_is_warmup_request() {
753 return ! empty( $_SERVER["HTTP_X_NITRO_WARMUP"] );
754 }
755
756 function nitropack_is_lighthouse_request() {
757 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "lighthouse" ) !== false;
758 }
759
760 function nitropack_is_gtmetrix_request() {
761 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "gtmetrix" ) !== false;
762 }
763
764 function nitropack_is_pingdom_request() {
765 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "pingdom" ) !== false;
766 }
767
768 function nitropack_is_optimizer_request() {
769 return isset( $_SERVER["HTTP_X_NITROPACK_REQUEST"] );
770 }
771
772 function nitropack_init() {
773 global $np_queriedObj;
774 nitropack_header( 'X-Nitro-Cache: MISS' );
775 $GLOBALS["NitroPack.tags"] = array();
776
777 if ( is_valid_nitropack_webhook() ) {
778 nitropack_handle_webhook();
779 } else {
780 if ( is_valid_nitropack_beacon() ) {
781 nitropack_handle_beacon();
782 } else {
783 /* The following if statement should stay as it is written.
784 * is_archive() can return true if visiting a tax, category or tag page, so is_acrchive must be checked last
785 */
786 if ( is_tax() || is_category() || is_tag() ) {
787 $np_queriedObj = get_queried_object();
788 get_nitropack()->setPageType( $np_queriedObj->taxonomy );
789 } else {
790 $layout = nitropack_get_layout();
791 get_nitropack()->setPageType( $layout );
792 }
793
794 add_action( 'wp_footer', 'nitropack_print_element_override', 9999999 );
795 if ( ! isset( $_GET["wpf_action"] ) && nitropack_passes_cookie_requirements() && nitropack_passes_page_requirements() ) {
796 add_action( 'wp_footer', 'nitropack_print_beacon_script' );
797 add_action( 'get_footer', 'nitropack_print_beacon_script' );
798
799 if ( nitropack_is_optimizer_request() ) { // Only care about tags for requests coming from our service. There is no need to do an API request when handling a standard client request.
800 if ( defined( 'FUSION_BUILDER_VERSION' ) ) {
801 add_filter( 'do_shortcode_tag', 'nitropack_handle_fusion_builder_conatainer_expiration', 10, 3 );
802 add_action( 'wp_footer', 'nitropack_set_custom_expiration' );
803 } else {
804 nitropack_set_custom_expiration();
805 }
806
807 $GLOBALS["NitroPack.tags"][ "pageType:" . get_nitropack()->getPageType()] = 1;
808
809 /* The following if statement should stay as it is written.
810 * is_archive() can return true if visiting a tax, category or tag page, so is_acrchive must be checked last
811 */
812 if ( is_tax() || is_category() || is_tag() ) {
813 $np_queriedObj = get_queried_object();
814 $GLOBALS["NitroPack.tags"][ "tax:" . $np_queriedObj->term_taxonomy_id ] = 1;
815 } else {
816 if ( is_single() || is_page() || is_attachment() ) {
817 $singlePost = get_post();
818 if ( $singlePost ) {
819 $GLOBALS["NitroPack.tags"][ "single:" . $singlePost->ID ] = 1;
820 }
821 }
822 }
823
824 // Uncomment the code below in case object cache interferes with correct URL taggig
825 // The code below will attempt to temporarily disable using the object cache only for the requests coming from NitroPack
826 //wp_using_ext_object_cache(false);
827 //add_action("pre_get_posts", function($query) {
828 // $query->query_vars["cache_results"] = false;
829 //});
830 //
831 //add_filter("all", function() {
832 // $args = func_get_args();
833 // if (count($args) > 1) {
834 // list($filterName, $value) = func_get_args();
835 // if (preg_match("/^transient_(.*)/", $filterName, $matches) && $value) {
836 // return false;
837 // }
838 // }
839 //}, 10, 2);
840
841 add_filter( 'post_link', 'nitropack_post_link_listener', 10, 3 );
842 add_action( 'the_post', 'nitropack_handle_the_post' );
843 add_action( 'wp_footer', 'nitropack_log_tags' );
844 }
845 } else {
846 nitropack_header( "X-Nitro-Disabled: 1" );
847 if ( ( null !== $nitro = get_nitropack_sdk() ) && ! $nitro->isAllowedBrowser() ) { // This clears any proxy cache when a proxy cached non-optimized request due to unsupported browser
848 add_action( 'wp_footer', 'nitropack_print_beacon_script' );
849 add_action( 'get_footer', 'nitropack_print_beacon_script' );
850 }
851 }
852
853 if ( ! nitropack_is_optimizer_request() ) {
854 add_action( 'wp_head', 'nitropack_print_generic_nitro_script' );
855 }
856 }
857 }
858 }
859
860 function nitropack_handle_fusion_builder_conatainer_expiration( $output, $tag, $attr ) {
861 global $np_customExpirationTimes;
862 if ( $tag == "fusion_builder_container" ) {
863 if ( ! empty( $attr["publish_date"] ) && ! empty( $attr["status"] ) && in_array( $attr["status"], array( "published_until", "publish_after" ) ) ) {
864 $timezone = get_option( 'timezone_string' );
865 $offset = get_option( 'gmt_offset' );
866 $dt = new DateTime( $attr["publish_date"] );
867 if ( $timezone ) {
868 $timeZone = new DateTimeZone( $timezone );
869 $timeZoneOffset = $timeZone->getOffset( $dt );
870 } else if ( $offset ) {
871 $timeZoneOffset = (int) $offset * 3600;
872 }
873 $time = $dt->getTimestamp() - $timeZoneOffset;
874 if ( $time > time() ) { // We only need to look at future dates
875 $np_customExpirationTimes[] = $time;
876 }
877 }
878 }
879 return $output;
880 }
881 function nitropack_set_custom_expiration() {
882
883 //check which CPTs are marked for optimization
884 $get_optimized_CPTs = nitropack_get_optimized_CPTs();
885 if ( empty( $get_optimized_CPTs ) )
886 return;
887
888 global $np_customExpirationTimes, $wpdb;
889
890 $placeholders = implode( ',', array_fill( 0, count( $get_optimized_CPTs ), '%s' ) );
891 $currentDate = date( "Y-m-d H:i:s" );
892
893 //For better security, we use prepared statements
894 $unmodifiedPosts_query = $wpdb->prepare(
895 "SELECT ID, post_date
896 FROM {$wpdb->prefix}posts
897 WHERE {$wpdb->prefix}posts.post_date > %s
898 AND {$wpdb->prefix}posts.post_type IN ($placeholders)
899 AND ({$wpdb->prefix}posts.post_status = 'future')
900 ORDER BY {$wpdb->prefix}posts.post_date ASC
901 LIMIT 0, 1",
902 array_merge( [ $currentDate ], $get_optimized_CPTs )
903 );
904 $unmodifiedPosts = $wpdb->get_results( $unmodifiedPosts_query );
905
906 if ( ! empty( $unmodifiedPosts ) && strtotime( $unmodifiedPosts[0]->post_date ) > time() ) {
907 $np_customExpirationTimes[] = strtotime( $unmodifiedPosts[0]->post_date );
908 }
909
910 if ( ! empty( $np_customExpirationTimes ) ) {
911 sort( $np_customExpirationTimes, SORT_NUMERIC );
912 nitropack_header( "X-Nitro-Expires: " . $np_customExpirationTimes[0] );
913 }
914 }
915
916 function nitropack_print_beacon_script() {
917 if ( defined( "NITROPACK_BEACON_PRINTED" ) || ! nitropack_passes_page_requirements() )
918 return;
919 define( "NITROPACK_BEACON_PRINTED", true );
920 echo apply_filters( "nitro_script_output", nitropack_get_beacon_script() );
921 }
922
923 function nitropack_get_beacon_script() {
924 $siteConfig = nitropack_get_site_config();
925 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
926 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
927 $url = $nitro->getUrl();
928 $cookiesJson = json_encode( $nitro->supportedCookiesFilter( NitroPack\SDK\NitroPack::getCookies() ) );
929 $layout = nitropack_get_layout();
930
931 if ( function_exists( "hash_hmac" ) && function_exists( "hash_equals" ) ) {
932 $hash = hash_hmac( "sha512", $url . $cookiesJson . $layout, $siteConfig["siteSecret"] );
933 } else {
934 $hash = "";
935 }
936 $url = base64_encode( $url ); // We want only ASCII
937 $cookiesb64 = base64_encode( $cookiesJson );
938 $proxyPurgeOnly = ! $nitro->isAllowedBrowser();
939
940 return "
941 <script nitro-exclude>
942 if (!window.NITROPACK_STATE || window.NITROPACK_STATE != 'FRESH') {
943 var proxyPurgeOnly = " . ( $proxyPurgeOnly ? 1 : 0 ) . ";
944 if (typeof navigator.sendBeacon !== 'undefined') {
945 var nitroData = new FormData(); nitroData.append('nitroBeaconUrl', '$url'); nitroData.append('nitroBeaconCookies', '$cookiesb64'); nitroData.append('nitroBeaconHash', '$hash'); nitroData.append('proxyPurgeOnly', '$proxyPurgeOnly'); nitroData.append('layout', '$layout'); navigator.sendBeacon(location.href, nitroData);
946 } else {
947 var xhr = new XMLHttpRequest(); xhr.open('POST', location.href, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('nitroBeaconUrl={$url}&nitroBeaconCookies={$cookiesb64}&nitroBeaconHash={$hash}&proxyPurgeOnly={$proxyPurgeOnly}&layout={$layout}');
948 }
949 }
950 </script>";
951 }
952 }
953 }
954
955 function nitropack_print_cookie_handler_script() {
956
957 if ( defined( "NITROPACK_COOKIE_HANDLER_PRINTED" ) )
958 return;
959 define( "NITROPACK_COOKIE_HANDLER_PRINTED", true );
960
961 echo apply_filters( "nitro_script_output", nitropack_get_cookie_handler_script() );
962 }
963
964 function nitropack_get_cookie_handler_script() {
965 return "
966 <script nitro-exclude>
967 document.cookie = 'nitroCachedPage=' + (!window.NITROPACK_STATE ? '0' : '1') + '; path=/; SameSite=Lax';
968 </script>";
969 }
970
971 function nitropack_print_generic_nitro_script() {
972 if ( defined( "NITROPACK_GENERIC_NITRO_SCRIPT_PRINTED" ) )
973 return;
974 define( "NITROPACK_GENERIC_NITRO_SCRIPT_PRINTED", true );
975 echo apply_filters( "nitro_script_output", nitropack_get_telemetry_meta() );
976 echo apply_filters( "nitro_script_output", nitropack_get_generic_nitro_script() );
977 }
978
979 function nitropack_get_generic_nitro_script() {
980 $siteConfig = nitropack_get_site_config();
981 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
982 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
983 $config = $nitro->getConfig();
984 if ( ! empty( $config->GenericNitroScript->Script ) ) {
985 return "<script id='nitro-generic' nitro-exclude>" . $config->GenericNitroScript->Script . "</script>";
986 }
987 }
988 }
989
990 return "";
991 }
992
993 function nitropack_get_telemetry_meta() {
994 $disabledReason = get_nitropack()->getDisabledReason();
995 $missReason = $disabledReason !== NULL ? $disabledReason : "cache not found";
996 $pageType = get_nitropack()->getPageType();
997 $isEligibleForOptimization = nitropack_passes_page_requirements();
998 $metaObj = "window.NPTelemetryMetadata={";
999
1000 if ( $missReason ) {
1001 $metaObj .= "missReason: (!window.NITROPACK_STATE ? '$missReason' : 'hit'),";
1002 }
1003
1004 if ( $pageType ) {
1005 $metaObj .= "pageType: '$pageType',";
1006 }
1007
1008 $metaObj .= "isEligibleForOptimization: " . ( $isEligibleForOptimization ? "true" : "false" ) . ",";
1009
1010 $metaObj .= "}";
1011
1012 return "<script id='nitro-telemetry-meta' nitro-exclude>$metaObj</script>";
1013 }
1014
1015 function nitropack_print_element_override() {
1016 if ( defined( "NITROPACK_ELEMENT_OVERRIDE_PRINTED" ) )
1017 return;
1018 define( "NITROPACK_ELEMENT_OVERRIDE_PRINTED", true );
1019 echo apply_filters( "nitro_script_output", nitropack_get_element_override_script() );
1020 }
1021
1022 function nitropack_get_element_override_script() {
1023 $nitro = get_nitropack_sdk();
1024 return $nitro !== NULL ? $nitro->getStatefulCacheHandlerScript() : "";
1025 }
1026
1027 function nitropack_has_advanced_cache() {
1028 return defined( 'NITROPACK_ADVANCED_CACHE' );
1029 }
1030
1031 function nitropack_validate_site_id( $siteId ) {
1032 return preg_match( "/^([a-zA-Z]{32})$/", trim( $siteId ) );
1033 }
1034
1035 function nitropack_validate_site_secret( $siteSecret ) {
1036 return preg_match( "/^([a-zA-Z0-9]{64})$/", trim( $siteSecret ) );
1037 }
1038
1039 function nitropack_validate_webhook_token( $token ) {
1040 return preg_match( "/^([abcdef0-9]{32})$/", strtolower( $token ) );
1041 }
1042
1043 function nitropack_validate_wc_currency( $cookieValue ) {
1044 return preg_match( "/^([a-z]{3})$/", strtolower( $cookieValue ) );
1045 }
1046
1047 function nitropack_validate_wc_currency_language( $cookieValue ) {
1048 return preg_match( "/^([a-z_\\-]{2,})$/", strtolower( $cookieValue ) );
1049 }
1050
1051 function nitropack_get_default_cacheable_object_types() {
1052 $result = array( "home", "archive" );
1053 $postTypes = get_post_types( array( 'public' => true ), 'names' );
1054 $result = array_merge( $result, $postTypes );
1055 foreach ( $postTypes as $postType ) {
1056 $result = array_merge( $result, get_taxonomies( array( 'object_type' => array( $postType ), 'public' => true ), 'names' ) );
1057 }
1058 return $result;
1059 }
1060
1061 function nitropack_get_object_types() {
1062 $objectTypes = get_post_types( array( 'public' => true ), 'objects' );
1063 $taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );
1064
1065 foreach ( $objectTypes as &$objectType ) {
1066 $objectType->taxonomies = [];
1067 foreach ( $taxonomies as $tax ) {
1068 if ( in_array( $objectType->name, $tax->object_type ) ) {
1069 $objectType->taxonomies[] = $tax;
1070 }
1071 }
1072 }
1073
1074 return $objectTypes;
1075 }
1076
1077 /**
1078 * Retrievs all CPTs eligable for optimization and checking if they are optimized
1079 */
1080 function nitropack_get_CPTs_with_optimization_status() {
1081
1082 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
1083 $objectTypes = nitropack_get_object_types();
1084
1085 $result = [
1086 "home" => [
1087 'name' => 'Home',
1088 'isOptimized' => in_array( 'home', $cacheableObjectTypes ) ? 1 : 0,
1089 ],
1090 "archive" => [
1091 'name' => 'Archive',
1092 'isOptimized' => in_array( 'archive', $cacheableObjectTypes ) ? 1 : 0,
1093 ]
1094 ];
1095
1096 // Determine new object types by comparing current with stored
1097 foreach ( $objectTypes as $slug => $objectType ) {
1098 $isOptimized = in_array( $objectType->name, $cacheableObjectTypes );
1099 $taxonomies = [];
1100
1101 if ( ! empty( $objectType->taxonomies ) ) {
1102 foreach ( $objectType->taxonomies as $tax ) {
1103 $taxName = $tax->name;
1104 $taxonomies[ $taxName ] = [
1105 'isOptimized' => in_array( $taxName, $cacheableObjectTypes ) ? 1 : 0,
1106 'name' => $tax->labels->name
1107 ];
1108 }
1109 }
1110
1111 $result[ $slug ] = [
1112 'isOptimized' => $isOptimized ? 1 : 0,
1113 'name' => $objectType->labels->name,
1114 'taxonomies' => $taxonomies
1115 ];
1116 }
1117
1118 return $result;
1119 }
1120 /**
1121 * Filters the CPTs which are not optimized.
1122 * Show it only once.
1123 */
1124 function nitropack_filter_non_optimized() {
1125 $filteredArr = [];
1126 $objectTypes = nitropack_get_CPTs_with_optimization_status();
1127 foreach ( $objectTypes as $slug => $objectType ) {
1128 $includeObjectType = ! $objectType['isOptimized'];
1129 $filteredTaxonomies = [];
1130 if ( isset( $objectType['taxonomies'] ) ) {
1131 foreach ( $objectType['taxonomies'] as $taxSlug => $taxonomy ) {
1132 if ( ! $taxonomy['isOptimized'] ) {
1133 $filteredTaxonomies[ $taxSlug ] = $taxonomy;
1134 $includeObjectType = true; // Include CPT if it has any non-optimized taxonomy
1135 }
1136 }
1137 }
1138 if ( $includeObjectType ) {
1139 $filteredArr[ $slug ] = [
1140 'isOptimized' => $objectType['isOptimized'],
1141 'name' => $objectType['name'],
1142 'taxonomies' => $filteredTaxonomies
1143 ];
1144 }
1145 }
1146 return $filteredArr;
1147 }
1148 /**
1149 * Retrieve the list of optimized Custom Post Types (CPTs).
1150 *
1151 * This function fetches the CPTs with their optimization status and returns an array of CPT keys
1152 * that are marked as optimized. It excludes 'home' and 'archive' as they are not valid CPTs.
1153 *
1154 * @return array An array of optimized CPT keys.
1155 */
1156 function nitropack_get_optimized_CPTs() {
1157 $get_CPTs_with_optimization_status = nitropack_get_CPTs_with_optimization_status();
1158
1159 $optimizedKeys = [];
1160
1161 foreach ( $get_CPTs_with_optimization_status as $key => $value ) {
1162
1163 if ( $key === 'home' || $key === 'archive' )
1164 continue; //not valid CPTs
1165
1166 if ( isset( $value['isOptimized'] ) && $value['isOptimized'] === 1 ) {
1167 $optimizedKeys[] = $key;
1168 }
1169 }
1170
1171 return $optimizedKeys;
1172 }
1173
1174 function nitropack_autooptimize_new_post_types_and_taxonomies() {
1175 //start optimizing after the notice is shown
1176 if ( ! get_option( 'nitropack-noticeOptimizeCPT' ) )
1177 return;
1178 //check if the non-optimized CPTs are stored, if not store them
1179 $nonCacheableObjectTypes = get_option( 'nitropack-nonCacheableObjectTypes' );
1180 if ( ! $nonCacheableObjectTypes ) {
1181 $notOptimizedCPTs = nitropack_filter_non_optimized();
1182 $nonCacheableObjectTypes = [];
1183 foreach ( $notOptimizedCPTs as $slug => $objectType ) {
1184 if ( ! $objectType['isOptimized'] ) {
1185 $nonCacheableObjectTypes[] = $slug;
1186 }
1187 foreach ( $objectType['taxonomies'] as $taxSlug => $taxonomy ) {
1188 if ( ! $taxonomy['isOptimized'] ) {
1189 $nonCacheableObjectTypes[] = $taxSlug;
1190 }
1191 }
1192 }
1193 update_option( 'nitropack-nonCacheableObjectTypes', $nonCacheableObjectTypes );
1194 }
1195
1196 $postTypes = get_post_types( array( 'public' => true ), 'objects' );
1197 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
1198 foreach ( $postTypes as $slug => $postType ) {
1199 if ( $postType->public ) {
1200 // Check and add post type to cacheable object types
1201 if ( ! in_array( $slug, $nonCacheableObjectTypes ) && ! in_array( $slug, $cacheableObjectTypes ) ) {
1202 $cacheableObjectTypes[] = $slug;
1203 }
1204
1205 // Fetch and add connected taxonomies
1206 $taxonomies = get_object_taxonomies( $slug, 'names' );
1207 foreach ( $taxonomies as $taxonomy ) {
1208 if ( ! in_array( $taxonomy, $nonCacheableObjectTypes ) && ! in_array( $taxonomy, $cacheableObjectTypes ) ) {
1209 $cacheableObjectTypes[] = $taxonomy;
1210 }
1211 }
1212 }
1213 }
1214
1215 update_option( 'nitropack-cacheableObjectTypes', $cacheableObjectTypes );
1216 }
1217
1218
1219
1220
1221 function nitropack_get_cacheable_object_types() {
1222 return apply_filters( "nitropack_cacheable_post_types", get_option( "nitropack-cacheableObjectTypes", nitropack_get_default_cacheable_object_types() ) );
1223 }
1224
1225 /** Step 3. */
1226 function nitropack_options() {
1227 if ( ! current_user_can( 'manage_options' ) ) {
1228 wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
1229 }
1230
1231 wp_enqueue_script( 'jquery-form' );
1232
1233 // Manually add home and archive page object
1234 $homeCustomObject = new stdClass();
1235 $homeCustomObject->name = 'home';
1236 $homeCustomObject->label = 'Home';
1237 $homeCustomObject->taxonomies = array();
1238
1239 $archiveCustomObject = new stdClass();
1240 $archiveCustomObject->name = 'archive';
1241 $archiveCustomObject->label = 'Archive';
1242 $archiveCustomObject->taxonomies = array();
1243 $objectTypes = array_merge( array( 'home' => $homeCustomObject, 'archive' => $archiveCustomObject ), nitropack_get_object_types() );
1244
1245 $enableCompression = get_option( 'nitropack-enableCompression' );
1246 $canEditorClearCache = get_option( 'nitropack-canEditorClearCache' );
1247 $autoCachePurge = get_option( 'nitropack-autoCachePurge', 1 );
1248 $bbCacheSyncPurge = get_option( 'nitropack-bbCacheSyncPurge', 0 );
1249 $legacyPurge = get_option( 'nitropack-legacyPurge', 0 );
1250 $checkedCompression = get_option( 'nitropack-checkedCompression' );
1251 $stockReduceStatus = get_option( 'nitropack-stockReduceStatus' );
1252 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
1253
1254 if ( get_nitropack()->isConnected() ) {
1255 $planDetailsUrl = get_nitropack_integration_url( "plan_details_json" );
1256 $optimizationDetailsUrl = get_nitropack_integration_url( "optimization_details_json" );
1257 $quickSetupUrl = get_nitropack_integration_url( "quicksetup_json" );
1258 $quickSetupSaveUrl = get_nitropack_integration_url( "quicksetup" );
1259 $helpLabels = [ 'wordpress_plugin_help' ];
1260
1261
1262 if ( get_nitropack()->getDistribution() == "oneclick" ) {
1263 $helpLabels = [ 'wordpress_plugin_help_oneclick' ];
1264 $oneClickVendorWidget = apply_filters( "nitropack_oneclick_vendor_widget", "" );
1265 include plugin_dir_path( __FILE__ ) . nitropack_trailingslashit( 'view' ) . 'oneclick.php';
1266 } else {
1267 include plugin_dir_path( __FILE__ ) . nitropack_trailingslashit( 'view' ) . 'admin.php';
1268 }
1269 } else {
1270 if ( get_nitropack()->getDistribution() == "oneclick" ) {
1271 $oneClickConnectUrl = apply_filters( "nitropack_oneclick_connect_url", "" );
1272 include plugin_dir_path( __FILE__ ) . nitropack_trailingslashit( 'view' ) . 'connect-oneclick.php';
1273 } else {
1274 include plugin_dir_path( __FILE__ ) . nitropack_trailingslashit( 'view' ) . 'connect.php';
1275 }
1276 }
1277 }
1278 function load_nitropack_scripts_styles( $page ) {
1279 //global WP
1280 wp_enqueue_style( 'nitropack-notifications', plugin_dir_url( __FILE__ ) . 'view/stylesheet/nitro-notifications.min.css', array(), NITROPACK_VERSION );
1281 wp_enqueue_script( 'nitropack_notices_js', plugin_dir_url( __FILE__ ) . 'view/javascript/np_notices.js', array(), NITROPACK_VERSION, true );
1282 wp_localize_script( 'nitropack_notices_js', 'nitropack_notices_vars', array(
1283 'nonce' => wp_create_nonce( NITROPACK_NONCE ),
1284 ) );
1285 //plugin only
1286 if ( $page === 'toplevel_page_nitropack' ) {
1287 //css
1288 wp_enqueue_style( 'nitropack', plugin_dir_url( __FILE__ ) . 'view/stylesheet/style.min.css', array(), NITROPACK_VERSION );
1289 //js
1290 wp_enqueue_script( 'nitropack_flowbite_js', plugin_dir_url( __FILE__ ) . 'view/javascript/flowbite.min.js', array(), NITROPACK_VERSION, true );
1291 wp_enqueue_script( 'nitropack_ui', plugin_dir_url( __FILE__ ) . 'view/javascript/nitropackUI.js', array(), NITROPACK_VERSION, true );
1292
1293 if ( get_nitropack()->isConnected() ) {
1294 wp_enqueue_script( 'nitropack_settings', plugin_dir_url( __FILE__ ) . 'view/javascript/np_settings.js', array(), NITROPACK_VERSION, true );
1295 wp_localize_script(
1296 'nitropack_settings',
1297 'np_settings',
1298 array(
1299 'nitroNonce' => wp_create_nonce( NITROPACK_NONCE ),
1300 'nitro_plugin_url' => plugin_dir_url( __FILE__ ),
1301 'error_msg' => esc_html__( 'Something went wrong.', 'nitropack' ),
1302 'success_msg' => esc_html__( 'Settings updated.', 'nitropack' ),
1303 'est_cachewarmup_msg' => esc_html__( 'Estimating optimizations usage', 'nitropack' ),
1304 )
1305 );
1306 //select2
1307 wp_register_script( 'np-select2', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js', array( 'jquery' ), NITROPACK_VERSION, true );
1308 wp_enqueue_script( 'np-select2' );
1309 }
1310 }
1311 }
1312 add_action( 'admin_enqueue_scripts', 'load_nitropack_scripts_styles' );
1313
1314 function nitropack_get_conflicting_plugins() {
1315 $clashingPlugins = array();
1316
1317 if ( defined( 'BREEZE_PLUGIN_DIR' ) ) { // Breeze cache plugin
1318 $clashingPlugins[] = "Breeze";
1319 }
1320
1321 if ( defined( 'WP_ROCKET_VERSION' ) ) { // WP-Rocket
1322 $clashingPlugins[] = "WP-Rocket";
1323 }
1324
1325 if ( defined( 'W3TC' ) ) { // W3 Total Cache
1326 $clashingPlugins[] = "W3 Total Cache";
1327 }
1328
1329 if ( defined( 'WPFC_MAIN_PATH' ) ) { // WP Fastest Cache
1330 $clashingPlugins[] = "WP Fastest Cache";
1331 }
1332
1333 if ( defined( 'PHASTPRESS_VERSION' ) ) { // PhastPress
1334 $clashingPlugins[] = "PhastPress";
1335 }
1336
1337 if ( defined( 'WPCACHEHOME' ) && function_exists( "wp_cache_phase2" ) ) { // WP Super Cache
1338 $clashingPlugins[] = "WP Super Cache";
1339 }
1340
1341 if ( defined( 'LSCACHE_ADV_CACHE' ) || defined( 'LSCWP_DIR' ) ) { // LiteSpeed Cache
1342 $clashingPlugins[] = "LiteSpeed Cache";
1343 }
1344
1345 if ( class_exists( 'Swift_Performance' ) || class_exists( 'Swift_Performance_Lite' ) ) { // Swift Performance
1346 $clashingPlugins[] = "Swift Performance";
1347 }
1348
1349 if ( class_exists( 'PagespeedNinja' ) ) { // PageSpeed Ninja
1350 $clashingPlugins[] = "PageSpeed Ninja";
1351 }
1352
1353 if ( defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) ) { // Autoptimize
1354 $clashingPlugins[] = "Autoptimize";
1355 }
1356
1357 if ( defined( 'PEGASAAS_ACCELERATOR_VERSION' ) ) { // Pegasaas Accelerator WP
1358 $clashingPlugins[] = "Pegasaas Accelerator WP";
1359 }
1360
1361 if ( class_exists( 'WP_Hummingbird' ) || class_exists( 'Hummingbird\\WP_Hummingbird' ) ) { // Hummingbird
1362 $clashingPlugins[] = "Hummingbird";
1363 }
1364
1365 if ( defined( 'WP_SMUSH_VERSION' ) ) { // Smush by WPMU DEV
1366 if ( class_exists( 'Smush\\Core\\Settings' ) && defined( 'WP_SMUSH_PREFIX' ) ) {
1367 $smushLazy = Smush\Core\Settings::get_instance()->get( 'lazy_load' );
1368 if ( $smushLazy ) {
1369 $clashingPlugins[] = "Smush Lazy Load";
1370 }
1371 } else {
1372 $clashingPlugins[] = "Smush";
1373 }
1374 }
1375
1376 if ( defined( 'COMET_CACHE_PLUGIN_FILE' ) ) { // Comet Cache by WP Sharks
1377 $clashingPlugins[] = "Comet Cache";
1378 }
1379
1380 if ( defined( 'WPO_VERSION' ) && class_exists( 'WPO_Cache_Config' ) ) { // WP Optimize
1381 $wpo_cache_config = WPO_Cache_Config::instance();
1382 if ( $wpo_cache_config->get_option( 'enable_page_caching', false ) ) {
1383 $clashingPlugins[] = "WP Optimize page caching";
1384 }
1385 }
1386
1387 if ( class_exists( 'BJLL' ) ) { // BJ Lazy Load
1388 $clashingPlugins[] = "BJ Lazy Load";
1389 }
1390
1391 if ( defined( 'SHORTPIXEL_IMAGE_OPTIMISER_VERSION' ) && class_exists( '\ShortPixel\ShortPixelPlugin' ) ) { //ShortPixel WebP
1392 $sp_config = \ShortPixel\ShortPixelPlugin::getInstance();
1393 if ( $sp_config->settings()->createWebp ) {
1394 $clashingPlugins[] = "ShortPixel WebP image creation";
1395 }
1396 }
1397 if ( defined( 'UUCSS_VERSION' ) || defined( 'UUCSS_PLUGIN_URL' ) ) { // RapidLoad
1398 $clashingPlugins[] = "RapidLoad";
1399 }
1400
1401 return $clashingPlugins;
1402 }
1403
1404 function nitropack_is_conflicting_plugin_active() {
1405 $conflictingPlugins = nitropack_get_conflicting_plugins();
1406 return ! empty( $conflictingPlugins );
1407 }
1408
1409 function nitropack_is_advanced_cache_allowed() {
1410 return ! in_array( nitropack_detect_hosting(), array(
1411 "pressable"
1412 ) );
1413 }
1414
1415
1416 function nitropack_get_hosting_notice_file() {
1417 return nitropack_trailingslashit( NITROPACK_DATA_DIR ) . "hosting_notice";
1418 }
1419
1420
1421
1422 function nitropack_dismiss_hosting_notice() {
1423 nitropack_verify_ajax_nonce( $_REQUEST );
1424 $hostingNoticeFile = nitropack_get_hosting_notice_file();
1425 if ( WP_DEBUG ) {
1426 touch( $hostingNoticeFile );
1427 } else {
1428 @touch( $hostingNoticeFile );
1429 }
1430 }
1431
1432
1433
1434
1435 function nitropack_is_config_up_to_date() {
1436 $siteConfig = nitropack_get_site_config();
1437 return ! empty( $siteConfig ) && ! empty( $siteConfig["pluginVersion"] ) && $siteConfig["pluginVersion"] == NITROPACK_VERSION;
1438 }
1439
1440 function nitropack_filter_non_original_cookies( &$cookies ) {
1441 global $np_originalRequestCookies;
1442 $ogNames = is_array( $np_originalRequestCookies ) ? array_keys( $np_originalRequestCookies ) : array();
1443 foreach ( $cookies as $name => $val ) {
1444 if ( ! in_array( $name, $ogNames ) ) {
1445 unset( $cookies[ $name ] );
1446 }
1447 }
1448 }
1449
1450 function nitropack_add_meta_box() {
1451 $editor = get_option( "nitropack-canEditorClearCache" );
1452 $allowed_capabilities = current_user_can( 'manage_options' ) || current_user_can( 'nitropack_meta_box' ) || ( $editor && current_user_can( 'editor' ) );
1453 if ( $allowed_capabilities ) {
1454 foreach ( nitropack_get_cacheable_object_types() as $objectType ) {
1455 add_meta_box( 'nitropack_manage_cache_box', 'NitroPack', 'nitropack_print_meta_box', $objectType, 'side' );
1456 }
1457 }
1458 }
1459
1460
1461 // This is only used for post types that can have "single" pages
1462 function nitropack_print_meta_box( $post ) {
1463 wp_enqueue_script( 'nitropack_metabox_js', plugin_dir_url( __FILE__ ) . 'view/javascript/metabox.js?np_v=' . NITROPACK_VERSION, true );
1464 wp_localize_script( 'nitropack_metabox_js', 'metaboxdata', array( 'nitroNonce' => wp_create_nonce( NITROPACK_NONCE ), 'nitro_plugin_url' => plugin_dir_url( __FILE__ ) ) );
1465
1466 $html = '';
1467 $html .= '<p><a class="button nitropack-invalidate-single" data-post_id="' . $post->ID . '" data-post_url="' . get_permalink( $post ) . '" style="width:100%;text-align:center;padding: 3px 0;">Invalidate cache</a></p>';
1468 $html .= '<p><a class="button nitropack-purge-single" data-post_id="' . $post->ID . '" data-post_url="' . get_permalink( $post ) . '" style="width:100%;text-align:center;padding: 3px 0;">Purge cache</a></p>';
1469 $html .= '<p id="nitropack-status-msg" style="display:none;"></p>';
1470 echo $html;
1471 }
1472
1473 function get_nitropack_sdk( $siteId = null, $siteSecret = null, $urlOverride = NULL, $forwardExceptions = false ) {
1474 return get_nitropack()->getSdk( $siteId, $siteSecret, $urlOverride, $forwardExceptions );
1475 }
1476
1477 function get_nitropack_integration_url( $integration, $nitro = null ) {
1478 if ( $nitro || ( null !== $nitro = get_nitropack_sdk() ) ) {
1479 return $nitro->integrationUrl( $integration );
1480 }
1481
1482 return "#";
1483 }
1484
1485 function register_nitropack_settings() {
1486 register_setting( NITROPACK_OPTION_GROUP, 'nitropack-enableCompression', array( 'default' => -1 ) );
1487 }
1488
1489 function nitropack_get_layout() {
1490 $layout = "default";
1491
1492 if ( nitropack_is_home() ) {
1493 $layout = "home";
1494 } else if ( nitropack_is_blogindex() ) {
1495 $layout = "blogindex";
1496 } else if ( is_page() ) {
1497 $layout = "page";
1498 } else if ( is_attachment() ) {
1499 $layout = "attachment";
1500 } else if ( is_author() ) {
1501 $layout = "author";
1502 } else if ( is_search() ) {
1503 $layout = "search";
1504 } else if ( is_tag() ) {
1505 $layout = "tag";
1506 } else if ( is_tax() ) {
1507 $layout = "taxonomy";
1508 } else if ( is_category() ) {
1509 $layout = "category";
1510 } else if ( nitropack_is_archive() ) {
1511 $layout = "archive";
1512 } else if ( is_feed() ) {
1513 $layout = "feed";
1514 } else if ( is_page() ) {
1515 $layout = "page";
1516 } else if ( is_single() ) {
1517 $layout = get_post_type();
1518 }
1519
1520 return $layout;
1521 }
1522
1523 function nitropack_sdk_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1524
1525 $status = false;
1526
1527 if ( null !== $nitro = get_nitropack_sdk() ) {
1528 try {
1529 $siteConfig = nitropack_get_site_config();
1530 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1531
1532 if ( $tag ) {
1533 if ( is_array( $tag ) ) {
1534 $tag = array_map( 'nitropack_filter_tag', $tag );
1535 } else {
1536 $tag = nitropack_filter_tag( $tag );
1537 }
1538 }
1539
1540 $nitro->invalidateCache( $url, $tag, $reason );
1541
1542 try {
1543
1544 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
1545 do_action( 'nitropack_debug_invalidate', $url, $tag, $reason );
1546 }
1547
1548 do_action( 'nitropack_integration_purge_url', $homeUrl );
1549
1550 if ( $tag ) {
1551 do_action( 'nitropack_integration_purge_all' );
1552 } else if ( $url ) {
1553 do_action( 'nitropack_integration_purge_url', $url );
1554 } else {
1555 do_action( 'nitropack_integration_purge_all' );
1556 }
1557 } catch (\Exception $e) {
1558 // Exception while signaling 3rd party integration addons to purge their cache
1559 }
1560 } catch (\Exception $e) {
1561 $status = false;
1562 }
1563
1564 $status = true;
1565 }
1566
1567 return $status;
1568 }
1569
1570 /* Start Heartbeat Related Functions */
1571 function nitropack_is_heartbeat_needed() {
1572 return ! nitropack_is_optimizer_request() &&
1573 ! nitropack_is_amp_page() &&
1574 ! nitropack_is_heartbeat_running() &&
1575 ( ! nitropack_is_heartbeat_completed() || time() - nitropack_last_heartbeat() > NITROPACK_HEARTBEAT_INTERVAL );
1576 }
1577
1578 function nitropack_print_heartbeat_script() {
1579 if ( nitropack_is_heartbeat_needed() ) {
1580 if ( defined( "NITROPACK_HEARTBEAT_PRINTED" ) )
1581 return;
1582 define( "NITROPACK_HEARTBEAT_PRINTED", true );
1583 echo apply_filters( "nitro_script_output", nitropack_get_heartbeat_script() );
1584 }
1585 }
1586
1587 function nitropack_get_heartbeat_script() {
1588 $siteConfig = nitropack_get_site_config();
1589 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
1590 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
1591 if ( is_admin() ) {
1592 $credentials = "same-origin";
1593 } else {
1594 $credentials = "omit";
1595 }
1596
1597 return "
1598 <script nitro-exclude>
1599 var heartbeatData = new FormData(); heartbeatData.append('nitroHeartbeat', '1');
1600 fetch(location.href, {method: 'POST', body: heartbeatData, credentials: '$credentials'});
1601 </script>";
1602 }
1603 }
1604 }
1605
1606 function is_valid_nitropack_heartbeat() {
1607 return ! empty( $_POST['nitroHeartbeat'] );
1608 }
1609
1610 function nitropack_get_heartbeat_file() {
1611 if ( null !== $nitro = get_nitropack_sdk() ) {
1612 return nitropack_trailingslashit( $nitro->getCacheDir() ) . "heartbeat";
1613 } else {
1614 return nitropack_trailingslashit( NITROPACK_DATA_DIR ) . "heartbeat";
1615 }
1616 }
1617
1618 function nitropack_last_heartbeat() {
1619 if ( null !== $nitro = get_nitropack_sdk() ) {
1620 try {
1621 return \NitroPack\SDK\Filesystem::fileMTime( nitropack_get_heartbeat_file() );
1622 } catch (\Exception $e) {
1623 return 0;
1624 }
1625 }
1626 }
1627
1628 function nitropack_is_heartbeat_running() {
1629 if ( null !== $nitro = get_nitropack_sdk() ) {
1630 try {
1631 $heartbeatContent = \NitroPack\SDK\Filesystem::fileGetContents( nitropack_get_heartbeat_file() );
1632 if ( $heartbeatContent == "1" ) {
1633 return time() - nitropack_last_heartbeat() < NITROPACK_HEARTBEAT_INTERVAL;
1634 }
1635 } catch (\Exception $e) {
1636 return false;
1637 }
1638 }
1639 }
1640
1641 function nitropack_is_heartbeat_completed() {
1642 if ( null !== $nitro = get_nitropack_sdk() ) {
1643 try {
1644 $heartbeatContent = \NitroPack\SDK\Filesystem::fileGetContents( nitropack_get_heartbeat_file() );
1645 return $heartbeatContent == "0"; // 0 - Job Done, 1 - Job Running, 2 - Job Needs Repeat
1646 } catch (\Exception $e) {
1647 return true;
1648 }
1649 }
1650 }
1651
1652 function nitropack_handle_heartbeat() {
1653 // TODO: Lock the file before checking this
1654 if ( nitropack_is_heartbeat_running() )
1655 return;
1656
1657 session_write_close();
1658 if ( null !== $nitro = get_nitropack_sdk() ) {
1659 try {
1660 $success = true;
1661 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 1 );
1662 if ( nitropack_healthcheck() ) {
1663 $success &= nitropack_flush_backlog();
1664 }
1665 $success &= nitropack_cache_cleanup();
1666
1667 if ( $success ) {
1668 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 0 );
1669 } else {
1670 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 2 );
1671 }
1672 } catch (\Exception $e) {
1673 return false;
1674 }
1675 }
1676 exit;
1677 }
1678
1679 function nitropack_healthcheck() {
1680 if ( null !== $nitro = get_nitropack_sdk() ) {
1681 return $nitro->getHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY || $nitro->checkHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY;
1682 }
1683 return true;
1684 }
1685
1686 function nitropack_flush_backlog() {
1687 if ( null !== $nitro = get_nitropack_sdk() ) {
1688 try {
1689 if ( $nitro->backlog->exists() ) {
1690 return $nitro->backlog->replay( 30 );
1691 }
1692 } catch (\NitroPack\SDK\BacklogReplayTimeoutException $e) {
1693 $nitro->backlog->delete();
1694 return nitropack_sdk_purge( NULL, NULL, "Full purge after backlog timeout" );
1695 } catch (\Exception $e) {
1696 return false;
1697 }
1698 }
1699 return true;
1700 }
1701
1702 function nitropack_cache_cleanup() {
1703 if ( null !== $nitro = get_nitropack_sdk() ) {
1704 $cacheDirParent = dirname( $nitro->getCacheDir() );
1705 $entries = scandir( $cacheDirParent );
1706 foreach ( $entries as $entry ) {
1707 if ( strpos( $entry, ".stale." ) !== false ) {
1708 $cacheDir = nitropack_trailingslashit( $cacheDirParent ) . $entry;
1709 try {
1710 \NitroPack\SDK\Filesystem::deleteDir( $cacheDir );
1711 } catch (\Exception $e) {
1712 // TODO: Log this
1713 return false;
1714 }
1715 }
1716 }
1717 }
1718 return true;
1719 }
1720 /* End Heartbeat Related Functions */
1721
1722 function nitropack_sdk_purge( $url = NULL, $tag = NULL, $reason = NULL, $type = \NitroPack\SDK\PurgeType::COMPLETE ) {
1723
1724 $status = false;
1725
1726 if ( null !== $nitro = get_nitropack_sdk() ) {
1727 try {
1728 $siteConfig = nitropack_get_site_config();
1729 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1730
1731 if ( $tag ) {
1732 if ( is_array( $tag ) ) {
1733 $tag = array_map( 'nitropack_filter_tag', $tag );
1734 } else {
1735 $tag = nitropack_filter_tag( $tag );
1736 }
1737 }
1738
1739 if ( ! $url && ! $tag ) {
1740 $nitro->purgeLocalCache( true );
1741 }
1742
1743 $nitro->purgeCache( $url, $tag, $type, $reason );
1744
1745 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
1746 do_action( 'nitropack_debug_purge', $url, $tag, $reason );
1747 }
1748
1749 try {
1750 do_action( 'nitropack_integration_purge_url', $homeUrl );
1751
1752 if ( $tag ) {
1753 do_action( 'nitropack_integration_purge_all' );
1754 } else if ( $url ) {
1755 do_action( 'nitropack_integration_purge_url', $url );
1756 } else {
1757 do_action( 'nitropack_integration_purge_all' );
1758 }
1759 } catch (\Exception $e) {
1760 // Exception while signaling 3rd party integration addons to purge their cache
1761 }
1762 } catch (\Exception $e) {
1763 $status = false;
1764 }
1765
1766 $status = true;
1767 }
1768
1769 return $status;
1770 }
1771
1772 /**
1773 * @param string|null $url
1774 * @return bool
1775 */
1776 function nitropack_sdk_purge_local( $url = NULL ) {
1777 if ( null === $nitro = get_nitropack_sdk() ) {
1778 return false;
1779 }
1780
1781 try {
1782 if ( $url ) {
1783 $nitro->purgeLocalUrlCache( $url );
1784 do_action( 'nitropack_integration_purge_url', $url );
1785 return true;
1786 }
1787
1788 $nitro->purgeLocalCache( true );
1789
1790 try {
1791 do_action( 'nitropack_integration_purge_all' );
1792 } catch (\Exception $e) {
1793 // Exception while signaling our 3rd party integration addons to purge their cache
1794 }
1795
1796 return true;
1797 } catch (\Exception $e) {
1798 return false;
1799 }
1800 }
1801
1802 /**
1803 * @param string|null $url
1804 * @return bool
1805 */
1806 function nitropack_sdk_invalidate_local( $url = NULL ) {
1807 if ( null === $nitro = get_nitropack_sdk() ) {
1808 return false;
1809 }
1810
1811 try {
1812 if ( $url ) {
1813 $nitro->invalidateLocalUrlCache( $url );
1814 do_action( 'nitropack_integration_purge_url', $url );
1815 return true;
1816 }
1817
1818 $nitro->invalidateLocalCache( true );
1819
1820 try {
1821 do_action( 'nitropack_integration_purge_all' );
1822 } catch (\Exception $e) {
1823 // Exception while signaling our 3rd party integration addons to purge their cache
1824 }
1825
1826 return true;
1827 } catch (\Exception $e) {
1828 return false;
1829 }
1830 }
1831
1832 function nitropack_sdk_delete_backlog() {
1833 if ( null !== $nitro = get_nitropack_sdk() ) {
1834 try {
1835 if ( $nitro->backlog->exists() ) {
1836 $nitro->backlog->delete();
1837 }
1838 } catch (\Exception $e) {
1839 return false;
1840 }
1841
1842 return true;
1843 }
1844
1845 return false;
1846 }
1847
1848 function nitropack_purge( $url = NULL, $tag = NULL, $reason = NULL ) {
1849 if ( $tag != "pageType:home" ) {
1850 $siteConfig = nitropack_get_site_config();
1851 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1852 nitropack_log_invalidate( $homeUrl, "pageType:home", $reason );
1853 }
1854
1855 if ( $tag != "pageType:archive" ) {
1856 nitropack_log_invalidate( NULL, "pageType:archive", $reason );
1857 }
1858
1859 nitropack_log_purge( $url, $tag, $reason );
1860 }
1861
1862 function nitropack_log_purge( $url = NULL, $tag = NULL, $reason = NULL ) {
1863 global $np_loggedPurges;
1864 if ( $tag && is_array( $tag ) ) {
1865 foreach ( $tag as $tagSingle ) {
1866 nitropack_log_purge( $url, $tagSingle, $reason );
1867 }
1868 return;
1869 }
1870
1871 $keyBase = "";
1872 if ( $url ) {
1873 $keyBase .= $url;
1874 }
1875
1876 if ( $tag ) {
1877 $tag = nitropack_filter_tag( $tag );
1878 $keyBase .= $tag;
1879 }
1880
1881 $purgeRequestKey = md5( $keyBase );
1882 if ( is_array( $np_loggedPurges ) && array_key_exists( $purgeRequestKey, $np_loggedPurges ) ) {
1883 $np_loggedPurges[ $purgeRequestKey ]["reason"] = $reason;
1884 $np_loggedPurges[ $purgeRequestKey ]["priority"]++;
1885 } else {
1886 $np_loggedPurges[ $purgeRequestKey ] = array(
1887 "url" => $url,
1888 "tag" => $tag,
1889 "reason" => $reason,
1890 "priority" => 1
1891 );
1892 }
1893 }
1894
1895 function nitropack_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1896 if ( $tag != "pageType:home" ) {
1897 $siteConfig = nitropack_get_site_config();
1898 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1899 nitropack_log_invalidate( $homeUrl, "pageType:home", $reason );
1900 }
1901
1902 if ( $tag != "pageType:archive" ) {
1903 nitropack_log_invalidate( NULL, "pageType:archive", $reason );
1904 }
1905
1906 nitropack_log_invalidate( $url, $tag, $reason );
1907 }
1908
1909 function nitropack_log_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1910 global $np_loggedInvalidations;
1911 if ( $tag && is_array( $tag ) ) {
1912 foreach ( $tag as $tagSingle ) {
1913 nitropack_log_invalidate( $url, $tagSingle, $reason );
1914 }
1915 return;
1916 }
1917
1918 $keyBase = "";
1919 if ( $url ) {
1920 $keyBase .= $url;
1921 }
1922
1923 if ( $tag ) {
1924 $tag = nitropack_filter_tag( $tag );
1925 $keyBase .= $tag;
1926 }
1927
1928 $invalidateRequestKey = md5( $keyBase );
1929 if ( is_array( $np_loggedInvalidations ) && array_key_exists( $invalidateRequestKey, $np_loggedInvalidations ) ) {
1930 $np_loggedInvalidations[ $invalidateRequestKey ]["reason"] = $reason;
1931 $np_loggedInvalidations[ $invalidateRequestKey ]["priority"]++;
1932 } else {
1933 $np_loggedInvalidations[ $invalidateRequestKey ] = array(
1934 "url" => $url,
1935 "tag" => $tag,
1936 "reason" => $reason,
1937 "priority" => 1
1938 );
1939 }
1940 }
1941
1942 function nitropack_queue_sort( $a, $b ) {
1943 if ( $a["priority"] == $b["priority"] ) {
1944 return 0;
1945 }
1946 return ( $a["priority"] < $b["priority"] ) ? -1 : 1;
1947 }
1948
1949 function nitropack_execute_purges() {
1950 global $np_loggedPurges;
1951 // if (!empty($_GET["action"]) && ($_GET["action"] === "edit") && !empty($_GET["meta-box-loader"])) {
1952 // return;
1953 // }
1954 if ( ! empty( $np_loggedPurges ) ) {
1955 uasort( $np_loggedPurges, "nitropack_queue_sort" );
1956 foreach ( $np_loggedPurges as $requestKey => $data ) {
1957 nitropack_sdk_purge( $data["url"], $data["tag"], $data["reason"] );
1958 }
1959 }
1960 }
1961
1962 function nitropack_execute_invalidations() {
1963 global $np_loggedInvalidations;
1964 // if (!empty($_GET["action"]) && ($_GET["action"] === "edit") && !empty($_GET["meta-box-loader"])) {
1965 // return;
1966 // }
1967 if ( ! empty( $np_loggedInvalidations ) ) {
1968 uasort( $np_loggedInvalidations, "nitropack_queue_sort" );
1969 foreach ( $np_loggedInvalidations as $requestKey => $data ) {
1970 nitropack_sdk_invalidate( $data["url"], $data["tag"], $data["reason"] );
1971 }
1972 }
1973 }
1974
1975 function nitropack_execute_warmups() {
1976 if ( ! empty( $_GET["action"] ) && ( $_GET["action"] === "edit" ) && ! empty( $_GET["meta-box-loader"] ) ) {
1977 return;
1978 }
1979
1980 try {
1981 if ( ! empty( \NitroPack\WordPress\NitroPack::$np_loggedWarmups ) && ( null !== $nitro = get_nitropack_sdk() ) ) {
1982 $warmupStats = $nitro->getApi()->getWarmupStats();
1983 if ( ! empty( $warmupStats["status"] ) ) {
1984 foreach ( array_unique( \NitroPack\WordPress\NitroPack::$np_loggedWarmups ) as $url ) {
1985 $nitro->getApi()->runWarmup( $url );
1986 }
1987 }
1988 }
1989 } catch (\Exception $e) {
1990 }
1991 }
1992
1993 function nitropack_fetch_config() {
1994 if ( null !== $nitro = get_nitropack_sdk() ) {
1995 try {
1996 $nitro->fetchConfig();
1997 } catch (\Exception $e) {
1998 }
1999 }
2000 }
2001
2002 function nitropack_theme_handler( $event = NULL ) {
2003 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
2004 return;
2005
2006 $msg = $event ? $event : 'Theme switched';
2007
2008 try {
2009 nitropack_sdk_purge( NULL, NULL, $msg ); // purge entire cache
2010 } catch (\Exception $e) {
2011 }
2012 }
2013
2014 function nitropack_purge_cache() {
2015 nitropack_verify_ajax_nonce( $_REQUEST );
2016 try {
2017 if ( nitropack_sdk_purge( NULL, NULL, 'Light purge of all caches', \NitroPack\SDK\PurgeType::LIGHT_PURGE ) ) {
2018 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Light purge of all caches' );
2019 nitropack_json_and_exit( array(
2020 "type" => "success",
2021 "message" => __( 'Cache has been purged successfully!', 'nitropack' )
2022 ) );
2023 }
2024 } catch (\Exception $e) {
2025 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Light purge of all caches. Error: ' . $e );
2026 }
2027 nitropack_json_and_exit( array(
2028 "type" => "error",
2029 "message" => __( 'Error! There was an error and the cache was not purged!', 'nitropack' )
2030 ) );
2031 }
2032
2033 function nitropack_invalidate_cache() {
2034 nitropack_verify_ajax_nonce( $_REQUEST );
2035 try {
2036 if ( nitropack_sdk_invalidate( NULL, NULL, 'Manual invalidation of all pages' ) ) {
2037 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Manual invalidation of all pages' );
2038 nitropack_json_and_exit( array(
2039 "type" => "success",
2040 "message" => __( 'Cache has been invalidated successfully!', 'nitropack' )
2041
2042 ) );
2043 }
2044 } catch (\Exception $e) {
2045 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Manual invalidation of all pages. Error: ' . $e );
2046 }
2047
2048 nitropack_json_and_exit( array(
2049 "type" => "error",
2050 "message" => __( 'There was an error and the cache was not invalidated!', 'nitropack' )
2051 ) );
2052 }
2053
2054 function nitropack_clear_residual_cache() {
2055 nitropack_verify_ajax_nonce( $_REQUEST );
2056 $gde = ! empty( $_POST["gde"] ) ? $_POST["gde"] : NULL;
2057 if ( $gde && array_key_exists( $gde, NitroPack\Integration\Plugin\RC::$modules ) ) {
2058 $result = call_user_func( array( NitroPack\Integration\Plugin\RC::$modules[ $gde ], "clearCache" ) ); // This needs to be like this because of compatibility with PHP 5.6
2059 if ( ! in_array( false, $result ) ) {
2060 nitropack_json_and_exit( array(
2061 "type" => "success",
2062 "message" => __( 'Success! The residual cache has been cleared successfully!', 'nitropack' )
2063 ) );
2064 }
2065 }
2066 nitropack_json_and_exit( array(
2067 "type" => "error",
2068 "message" => __( 'Error! There was an error clearing the residual cache!', 'nitropack' )
2069 ) );
2070 }
2071
2072 function nitropack_json_and_exit( $array ) {
2073 if ( nitropack_is_wp_cli() ) {
2074 $type = NULL;
2075 if ( array_key_exists( "status", $array ) ) {
2076 $type = $array["status"];
2077 } else if ( array_key_exists( "type", $array ) ) {
2078 $type = $array["type"];
2079 }
2080
2081 if ( $type && array_key_exists( "message", $array ) ) {
2082 if ( $type == "success" ) {
2083 WP_CLI::success( $array["message"] );
2084 } else {
2085 WP_CLI::error( $array["message"] );
2086 }
2087 }
2088 } else {
2089 echo json_encode( $array );
2090 }
2091 exit;
2092 }
2093 function nitropack_admin_toast_msgs( $type ) {
2094 if ( $type === 'success' ) {
2095 $msg = esc_html__( 'Settings updated.', 'nitropack' );
2096 } else {
2097 $msg = esc_html__( 'Something went wrong.', 'nitropack' );
2098 }
2099 return $msg;
2100 }
2101 function nitropack_verify_ajax_nonce( $request_data ) {
2102 // If not an ajax request
2103 if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
2104 return;
2105 }
2106
2107 // If nonce fails verification
2108 if ( empty( $request_data['nonce'] ) || ! wp_verify_nonce( $request_data['nonce'], NITROPACK_NONCE ) ) {
2109 wp_die( 'Unauthorized request' );
2110 }
2111 }
2112
2113 function nitropack_has_post_important_change( $post ) {
2114 $prevPost = nitropack_get_post_pre_update( $post );
2115 return $prevPost && ( $prevPost->post_title != $post->post_title || $prevPost->post_name != $post->post_name || $prevPost->post_excerpt != $post->post_excerpt );
2116 }
2117
2118 function nitropack_purge_single_cache() {
2119 nitropack_verify_ajax_nonce( $_REQUEST );
2120 if ( ! empty( $_POST["postId"] ) && is_numeric( $_POST["postId"] ) ) {
2121 $postId = $_POST["postId"];
2122 $postUrl = ! empty( $_POST["postUrl"] ) ? $_POST["postUrl"] : NULL;
2123 $reason = sprintf( "Manual purge of post %s via the WordPress admin panel", $postId );
2124 $tag = $postId > 0 ? "single:$postId" : NULL;
2125
2126 if ( $postUrl ) {
2127 if ( is_array( $postUrl ) ) {
2128 foreach ( $postUrl as &$url ) {
2129 $url = nitropack_sanitize_url_input( $url );
2130 }
2131 } else {
2132 $postUrl = nitropack_sanitize_url_input( $postUrl );
2133 $reason = "Manual purge of " . $postUrl;
2134 }
2135 }
2136
2137 try {
2138 if ( nitropack_sdk_purge( $postUrl, $tag, $reason ) ) {
2139 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Manual purge of post ' . $postId . ' via WordPress.' );
2140 nitropack_json_and_exit( array(
2141 "type" => "success",
2142 "message" => __( 'Success! Cache has been purged successfully!', 'nitropack' )
2143 ) );
2144 }
2145 } catch (\Exception $e) {
2146 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Manual purge of post ' . $postId . ' via WordPress. Error: ' . $e );
2147 }
2148 }
2149
2150 nitropack_json_and_exit( array(
2151 "type" => "error",
2152 "message" => __( 'Error! There was an error and the cache was not purged!', 'nitropack' )
2153 ) );
2154 }
2155
2156 function nitropack_purge_entire_cache() {
2157 nitropack_verify_ajax_nonce( $_REQUEST );
2158 try {
2159 if ( nitropack_sdk_purge( null, null, 'Manual purge of all pages' ) ) {
2160 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Manual purge of all pages' );
2161 nitropack_json_and_exit( [
2162 "type" => "success",
2163 "message" => __( 'Success! Cache has been purged successfully!', 'nitropack' )
2164 ] );
2165 }
2166 } catch (\Exception $e) {
2167 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Manual purge of all pages. Error: ' . $e );
2168 }
2169
2170 nitropack_json_and_exit( [
2171 "type" => "error",
2172 "message" => __( 'Error! There was an error and the cache was not purged!', 'nitropack' )
2173 ] );
2174 }
2175
2176 function nitropack_invalidate_single_cache() {
2177 nitropack_verify_ajax_nonce( $_REQUEST );
2178 if ( ! empty( $_POST["postId"] ) && is_numeric( $_POST["postId"] ) ) {
2179 $postId = $_POST["postId"];
2180 $postUrl = ! empty( $_POST["postUrl"] ) ? $_POST["postUrl"] : NULL;
2181 $reason = sprintf( "Manual invalidation of post %s via the WordPress admin panel", $postId );
2182 $tag = $postId > 0 ? "single:$postId" : NULL;
2183
2184 if ( $postUrl ) {
2185 if ( is_array( $postUrl ) ) {
2186 foreach ( $postUrl as &$url ) {
2187 $url = nitropack_sanitize_url_input( $url );
2188 }
2189 } else {
2190 $postUrl = nitropack_sanitize_url_input( $postUrl );
2191 $reason = "Manual invalidation of " . $postUrl;
2192 }
2193 }
2194
2195 try {
2196 if ( nitropack_sdk_invalidate( $postUrl, $tag, $reason ) ) {
2197 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Manual invalidation of post ' . $postId . ' via WordPress.' );
2198 nitropack_json_and_exit( array(
2199 "type" => "success",
2200 "message" => __( 'Success! Cache has been invalidated successfully!', 'nitropack' )
2201 ) );
2202 }
2203 } catch (\Exception $e) {
2204 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Manual invalidation of post ' . $postId . ' via WordPress. Error: ' . $e );
2205 }
2206 }
2207
2208 nitropack_json_and_exit( array(
2209 "type" => "error",
2210 "message" => __( 'Error! There was an error and the cache was not invalidated!', 'nitropack' )
2211 ) );
2212 }
2213
2214 function nitropack_clean_post_cache( $post, $taxonomies = NULL, $hasImportantChangeInPost = NULL, $reason = NULL, $usePurge = false ) {
2215 try {
2216 $postID = $post->ID;
2217 $postType = isset( $post->post_type ) ? $post->post_type : "post";
2218 $nicePostTypeLabel = nitropack_get_nice_post_type_label( $postType );
2219 $reason = $reason ? $reason : sprintf( "Updated %s '%s'", $nicePostTypeLabel, $post->post_title );
2220 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
2221
2222 if ( in_array( $postType, $cacheableObjectTypes ) ) {
2223 if ( $usePurge ) {
2224 // We only purge the single pages because they have to immediately stop serving cache
2225 // These pages no longer exists and if their URL is requested we must not server cache
2226 nitropack_purge( NULL, "single:$postID", $reason );
2227 } else {
2228 nitropack_invalidate( NULL, "single:$postID", $reason );
2229 }
2230
2231 nitropack_invalidate( NULL, "post:$postID", $reason );
2232
2233 if ( $hasImportantChangeInPost === NULL ) {
2234 $hasImportantChangeInPost = nitropack_has_post_important_change( $post );
2235 }
2236 if ( $taxonomies === NULL ) {
2237 if ( $hasImportantChangeInPost ) { // This change should be reflected in all taxonomy pages
2238 $taxonomies = array( 'related' => nitropack_get_taxonomies( $post ) );
2239 } else { // No important change, so only update taxonomy pages which have been added or removed from the post
2240 $taxonomies = nitropack_get_taxonomies_for_update( $post );
2241 }
2242 }
2243 if ( $taxonomies ) {
2244 if ( ! empty( $taxonomies['added'] ) ) { // taxonomies that the post was just added to, must purge all pages for these taxonomies
2245 foreach ( $taxonomies['added'] as $term_taxonomy_id ) {
2246 nitropack_invalidate( NULL, "tax:$term_taxonomy_id", $reason );
2247 }
2248 }
2249 if ( ! empty( $taxonomies['deleted'] ) ) { // taxonomy pages that the post was just removed from (also accounts for paginations via the taxpost: tag instead of only tax:)
2250 foreach ( $taxonomies['deleted'] as $term_taxonomy_id ) {
2251 nitropack_invalidate( NULL, "taxpost:$term_taxonomy_id:$postID", $reason );
2252 }
2253 }
2254 if ( ! empty( $taxonomies['related'] ) ) { // taxonomy pages that the post is linked to (also accounts for paginations via the taxpost: tag instead of only tax:)
2255 foreach ( $taxonomies['related'] as $term_taxonomy_id ) {
2256 nitropack_invalidate( NULL, "taxpost:$term_taxonomy_id:$postID", $reason );
2257 }
2258 }
2259 }
2260 } else {
2261 if ( $post->public ) {
2262 nitropack_invalidate( NULL, "post:$postID", $reason );
2263 }
2264
2265 $posts = get_post_ancestors( $postID );
2266 foreach ( $posts as $parentID ) {
2267 $parent = get_post( $parentID );
2268 nitropack_clean_post_cache( $parent, false, false, $reason );
2269 }
2270 }
2271 } catch (\Exception $e) {
2272 }
2273 }
2274
2275 function nitropack_get_nice_post_type_label( $postType ) {
2276 $postTypes = get_post_types( array(
2277 "name" => $postType
2278 ), "objects" );
2279
2280 return ! empty( $postTypes[ $postType ] ) && ! empty( $postTypes[ $postType ]->labels ) ? $postTypes[ $postType ]->labels->singular_name : $postType;
2281 }
2282
2283 function nitropack_handle_comment_transition( $new, $old, $comment ) {
2284 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
2285 return;
2286
2287 try {
2288 $postID = $comment->comment_post_ID;
2289 $post = get_post( $postID );
2290 $postType = isset( $post->post_type ) ? $post->post_type : "post";
2291 $cacheableObjectTypes = nitropack_get_cacheable_object_types();
2292
2293 if ( in_array( $postType, $cacheableObjectTypes ) ) {
2294 nitropack_invalidate( NULL, "single:" . $postID, sprintf( "Invalidation of '%s' due to changing related comment status", $post->post_title ) );
2295 }
2296 } catch (\Exception $e) {
2297 // TODO: Log the error
2298 }
2299 }
2300
2301 function nitropack_handle_comment_post( $commentID, $isApproved ) {
2302 if ( ! get_option( "nitropack-autoCachePurge", 1 ) || $isApproved !== 1 )
2303 return;
2304
2305 try {
2306 $comment = get_comment( $commentID );
2307 $postID = $comment->comment_post_ID;
2308 $post = get_post( $postID );
2309 nitropack_invalidate( NULL, "single:" . $postID, sprintf( "Invalidation of '%s' due to posting a new approved comment", $post->post_title ) );
2310 } catch (\Exception $e) {
2311 // TODO: Log the error
2312 }
2313 }
2314
2315 function custom_reduce_stock_after_order_placed( $order ) {
2316
2317 if ( (int) get_option( 'nitropack-stockReduceStatus' ) !== 1 )
2318 return;
2319
2320 $items = $order->get_items();
2321
2322 foreach ( $items as $item ) {
2323 $product_id = $item->get_product_id();
2324 $product_url = get_permalink( $product_id );
2325
2326 $stock_quantity = get_post_meta( $product_id, '_stock', true );
2327
2328 if ( $stock_quantity > 0 ) {
2329 nitropack_sdk_invalidate( $product_url, NULL, 'Invalidated to adjust order quantity' );
2330 }
2331 }
2332 }
2333
2334 function nitropack_detect_changes_and_clean_post_cache( $post ) {
2335
2336 $post_before = nitropack_get_post_pre_update( $post );
2337 $ignoredComparisonKeys = array( 'post_modified', 'post_modified_gmt' );
2338 $canCleanPostCache = false;
2339 $postStatesEqual = nitropack_compare_posts( (array) $post_before, (array) $post, $ignoredComparisonKeys );
2340
2341 if ( $postStatesEqual ) {
2342 $taxCurrent = nitropack_get_taxonomies( $post );
2343 $taxPreUpdate = nitropack_get_taxonomies_pre_update( $post );
2344 $taxAreEqual = nitropack_compare_posts( $taxCurrent, $taxPreUpdate );
2345 if ( $taxAreEqual ) {
2346 $metaCurrent = get_post_meta( $post->ID );
2347 $metaPreUpdate = nitropack_get_meta_pre_update( $post );
2348 $metaIsEqual = nitropack_compare_posts( $metaCurrent, $metaPreUpdate );
2349 if ( ! $metaIsEqual ) {
2350 $canCleanPostCache = true;
2351 }
2352 } else {
2353 $canCleanPostCache = true;
2354 }
2355 } else {
2356 $canCleanPostCache = true;
2357 }
2358
2359 if ( $canCleanPostCache ) {
2360 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post );
2361 nitropack_clean_post_cache( $post );
2362 define( 'NITROPACK_PURGE_CACHE', true );
2363 }
2364 }
2365
2366
2367 function nitropack_handle_post_transition( $new, $old, $post ) {
2368 if ( wp_is_post_revision( $post ) )
2369 return;
2370 if ( ! empty( $post->ID ) && in_array( $post->ID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
2371 return;
2372 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
2373 return;
2374
2375 try {
2376 if ( $new === "auto-draft" || ( $new === "draft" && $old === "auto-draft" ) || ( $new === "draft" && $old != "publish" ) || $new === "inherit" ) { // Creating a new post or draft, don't do anything for now.
2377 return;
2378 }
2379
2380 $ignoredPostTypes = array(
2381 "revision",
2382 "scheduled-action",
2383 "flamingo_contact",
2384 "carts"/*WooCommerce Cart Reports*/
2385 );
2386
2387 $nicePostTypes = array(
2388 "post" => "Post",
2389 "page" => "Page",
2390 "tribe_events" => "Calendar Event",
2391 );
2392 $postType = isset( $post->post_type ) ? $post->post_type : "post";
2393 $nicePostTypeLabel = nitropack_get_nice_post_type_label( $postType );
2394
2395 if ( in_array( $postType, $ignoredPostTypes ) )
2396 return;
2397
2398 switch ( $postType ) {
2399 case "nav_menu_item":
2400 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to modifying menu entries" ) );
2401 break;
2402 case "customize_changeset":
2403 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to applying appearance customization" ) );
2404 break;
2405 case "custom_css":
2406 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to modifying custom CSS" ) );
2407 break;
2408 default:
2409 if ( $new == "future" ) {
2410 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to scheduling %s '%s'", $nicePostTypeLabel, $post->post_title ) );
2411 } else if ( $new === 'publish' && $old === 'trash' ) {
2412 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to restoring %s '%s'", $nicePostTypeLabel, $post->post_title ) );
2413 } else if ( $new == "publish" && $old != "publish" ) {
2414 /* Handle first publish */
2415 // set_transient($post->ID . '_np_first_publish', $post, 120);
2416 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post->ID );
2417 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) ) {
2418 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to publishing %s '%s'", $nicePostTypeLabel, $post->post_title ), true );
2419 }
2420 if ( $post->post_type === 'post' ) {
2421 nitropack_invalidate( NULL, "pageType:blogindex", 'Invalidation of blog page due to changing related post status' );
2422 }
2423 } else if ( $new == "trash" && $old == "publish" ) {
2424 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to deleting %s '%s'", $nicePostTypeLabel, $post->post_title ), true );
2425 } else if ( $new == "private" && $old == "publish" ) {
2426 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to making %s '%s' private", $nicePostTypeLabel, $post->post_title ), true );
2427 } else if ( $new == "draft" && $old == "publish" ) {
2428 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to making %s '%s' a draft", $nicePostTypeLabel, $post->post_title ), true );
2429 } else if ( $new != "trash" ) {
2430 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) ) {
2431 nitropack_detect_changes_and_clean_post_cache( $post );
2432 }
2433 if ( $new == 'publish' ) {
2434 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post->ID );
2435 }
2436 }
2437 break;
2438 }
2439 } catch (\Exception $e) {
2440 // TODO: Log the error
2441 }
2442 }
2443
2444 function nitropack_handle_first_publish( $post_id ) {
2445 $first_publish_post = get_transient( $post_id . '_np_first_publish', false );
2446
2447 if ( ! $first_publish_post ) {
2448 return;
2449 }
2450
2451 try {
2452 nitropack_clean_post_cache( $first_publish_post, array( 'added' => nitropack_get_taxonomies( $first_publish_post ) ), true, sprintf( "Invalidate related pages due to publishing %s '%s'", $first_publish_post->nicePostTypeLabel, $first_publish_post->post_title ) );
2453 nitropack_invalidate( NULL, "pageType:blogindex", 'Invalidation of blog page due to changing related post status' );
2454 delete_transient( $post_id . '_np_first_publish' );
2455 } catch (\Exception $e) {
2456 // TODO: Log the error
2457 }
2458 }
2459
2460 function nitropack_postmeta_updated( $meta_id, $object_id, $meta_key, $meta_value ) {
2461 if ( $meta_key !== '_edit_lock' ) {
2462 if ( get_post_type( $object_id ) === 'product' ) {
2463 ! defined( 'NITROPACK_PURGE_PRODUCT' ) && define( 'NITROPACK_PURGE_PRODUCT', true );
2464 }
2465 }
2466 }
2467
2468 function nitropack_sot( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
2469 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
2470 return;
2471
2472 $post = get_post( $object_id );
2473 $post_status = $post->post_status;
2474
2475 if ( $post_status === 'auto-draft' || $post_status === 'draft' ) {
2476 return;
2477 }
2478
2479 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) ) {
2480 $purgeCache = ! nitropack_compare_posts( $tt_ids, $old_tt_ids );
2481 $cleanCache = $purgeCache ? "YES" : "NO";
2482 if ( $purgeCache ) {
2483 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post );
2484 nitropack_clean_post_cache( $post );
2485 define( 'NITROPACK_PURGE_CACHE', true );
2486 }
2487 }
2488 }
2489
2490 function nitropack_handle_product_updates( $product, $updated ) {
2491 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
2492 return;
2493
2494 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) && defined( 'NITROPACK_PURGE_PRODUCT' ) ) {
2495 try {
2496 $post = get_post( $product->get_id() );
2497 $reasons = 'updated ';
2498 $reasons .= implode( ',', $updated );
2499 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post );
2500 nitropack_clean_post_cache( $post, NULL, true, sprintf( "Invalidate product '%s'. Reason '%s'", $product->get_name(), $reasons ) ); // Update the product page and all related pages, because a quantity change might have to add/remove "Out of stock" labels
2501 define( 'NITROPACK_PURGE_CACHE', true );
2502 } catch (\Exception $e) {
2503 // TODO: Log the error
2504 }
2505 }
2506 }
2507
2508 function nitropack_post_link_listener( $permalink, $post, $leavename ) {
2509 if ( is_object( $post ) ) {
2510 nitropack_handle_the_post( $post );
2511 }
2512
2513 return $permalink;
2514 }
2515
2516 function nitropack_handle_the_post( $post ) {
2517 global $np_customExpirationTimes, $np_queriedObj;
2518 if ( defined( 'POSTEXPIRATOR_VERSION' ) ) {
2519 $postExpiryDate = get_post_meta( $post->ID, "_expiration-date", true );
2520 if ( ! empty( $postExpiryDate ) && $postExpiryDate > time() ) { // We only need to look at future dates
2521 $np_customExpirationTimes[] = $postExpiryDate;
2522 }
2523 }
2524
2525 if ( function_exists( "sort_portfolio" ) ) { // Portfolio Sorting plugin
2526 $portfolioStartDate = get_post_meta( $post->ID, "start_date", true );
2527 $portfolioEndDate = get_post_meta( $post->ID, "end_date", true );
2528 if ( ! empty( $portfolioStartDate ) && strtotime( $portfolioStartDate ) > time() ) { // We only need to look at future dates
2529 $np_customExpirationTimes[] = strtotime( $portfolioStartDate );
2530 } else if ( ! empty( $portfolioEndDate ) && strtotime( $portfolioEndDate ) > time() ) { // We only need to look at future dates
2531 $np_customExpirationTimes[] = strtotime( $portfolioEndDate );
2532 }
2533 }
2534
2535 $GLOBALS["NitroPack.tags"][ "post:" . $post->ID ] = 1;
2536 $GLOBALS["NitroPack.tags"][ "author:" . $post->post_author ] = 1;
2537 if ( $np_queriedObj ) {
2538 $GLOBALS["NitroPack.tags"][ "taxpost:" . $np_queriedObj->term_taxonomy_id . ":" . $post->ID ] = 1;
2539 }
2540 }
2541
2542 function nitropack_ignore_post_updates( $postID ) {
2543 \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs[] = $postID;
2544 }
2545
2546 function nitropack_get_taxonomies( $post ) {
2547 $term_taxonomy_ids = array();
2548 $taxonomies = get_object_taxonomies( $post->post_type );
2549 foreach ( $taxonomies as $taxonomy ) {
2550 $terms = get_the_terms( $post->ID, $taxonomy );
2551 if ( ! empty( $terms ) ) {
2552 foreach ( $terms as $term ) {
2553 $term_taxonomy_ids[] = $term->term_taxonomy_id;
2554 }
2555 }
2556 }
2557 return $term_taxonomy_ids;
2558 }
2559
2560 function nitropack_get_taxonomies_for_update( $post ) {
2561 $prevTaxonomies = nitropack_get_taxonomies_pre_update( $post );
2562 $newTaxonomies = nitropack_get_taxonomies( $post );
2563 $intersection = array_intersect( $newTaxonomies, $prevTaxonomies );
2564 $prevTaxonomies = array_diff( $prevTaxonomies, $intersection );
2565 $newTaxonomies = array_diff( $newTaxonomies, $intersection );
2566 return array(
2567 "added" => array_diff( $newTaxonomies, $prevTaxonomies ),
2568 "deleted" => array_diff( $prevTaxonomies, $newTaxonomies )
2569 );
2570 }
2571
2572 function nitropack_get_post_pre_update( $post ) {
2573 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $post->ID ] : NULL;
2574 }
2575
2576 function nitropack_get_taxonomies_pre_update( $post ) {
2577 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $post->ID ] : array();
2578 }
2579
2580 function nitropack_get_meta_pre_update( $post ) {
2581 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $post->ID ] : array();
2582 }
2583
2584 function nitropack_log_post_pre_update( $postID ) {
2585 if ( in_array( $postID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
2586 return;
2587
2588
2589 $post = get_post( $postID );
2590 \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $postID ] = $post;
2591 \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $postID ] = nitropack_get_taxonomies( $post );
2592 //Is post meta updated at this point? Or maybe this block should be moved to a different action?
2593 \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $postID ] = get_post_meta( $postID );
2594 }
2595
2596 function nitropack_log_product_pre_api_update( $product, $request, $creating ) {
2597
2598 if ( ! $creating ) {
2599
2600 $postID = $product->get_id();
2601 if ( in_array( $postID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
2602 return;
2603
2604
2605 $post = get_post( $postID );
2606 \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $postID ] = $post;
2607 \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $postID ] = nitropack_get_taxonomies( $post );
2608 //Is post meta updated at this point? Or maybe this block should be moved to a different action?
2609 \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $postID ] = get_post_meta( $postID );
2610 }
2611
2612 return $product;
2613 }
2614
2615 function nitropack_compare_posts( array $p1, array $p2, $ignoredKeys = null ) {
2616 $p1keys = array_keys( $p1 );
2617 $p2keys = array_keys( $p2 );
2618 if ( count( $p1keys ) !== count( $p2keys ) ) {
2619 return false;
2620 }
2621 if ( array_diff( $p1keys, $p2keys ) ) {
2622 return false;
2623 }
2624
2625 $isP1assoc = false;
2626 $expectedKey = 0;
2627 foreach ( $p2 as $i => $_ ) {
2628 if ( $i !== $expectedKey ) {
2629 $isP1assoc = true;
2630 }
2631 $expectedKey++;
2632 }
2633
2634 $isP2assoc = false;
2635 $expectedKey = 0;
2636 foreach ( $p2 as $i => $_ ) {
2637 if ( $i !== $expectedKey ) {
2638 $isP2assoc = true;
2639 }
2640 $expectedKey++;
2641 }
2642
2643 if ( $isP1assoc !== $isP2assoc ) {
2644 return false;
2645 }
2646
2647 if ( ! $isP1assoc && ! $isP2assoc ) {
2648 sort( $p1 );
2649 sort( $p2 );
2650 }
2651
2652 foreach ( $p1 as $poKey => $poVal ) {
2653 if ( $ignoredKeys && in_array( $poKey, $ignoredKeys, true ) ) {
2654 continue;
2655 }
2656 $checkpoint01 = is_array( $poVal );
2657 $checkpoint02 = is_array( $p2[ $poKey ] );
2658 if ( $checkpoint01 && $checkpoint02 ) {
2659 if ( ! nitropack_compare_posts( $poVal, $p2[ $poKey ], $ignoredKeys, 'Re:' ) ) { //'Re:' left for debug purpose to destinguish between main and recursive call
2660 return false;
2661 }
2662 } elseif ( ! $checkpoint01 && ! $checkpoint02 ) {
2663 if ( $poVal != $p2[ $poKey ] ) {
2664 return false;
2665 }
2666 } else {
2667 return false;
2668 }
2669 }
2670 return true;
2671 }
2672
2673 function nitropack_filter_tag( $tag ) {
2674 return preg_replace( "/[^a-zA-Z0-9:]/", ":", $tag );
2675 }
2676
2677 function nitropack_log_tags() {
2678 if ( ! empty( $GLOBALS["NitroPack.instance"] ) && ! empty( $GLOBALS["NitroPack.tags"] ) ) {
2679 $nitro = $GLOBALS["NitroPack.instance"];
2680 $layout = nitropack_get_layout();
2681 try {
2682 $config = $nitro->getConfig();
2683 $useHeader = ! empty( $config->TagsViaHeader );
2684
2685 if ( $layout == "home" ) {
2686 if ( $useHeader ) {
2687 nitropack_header( "x-nitro-tags:pageType:home" );
2688 } else {
2689 $nitro->getApi()->tagUrl( $nitro->getUrl(), "pageType:home" );
2690 }
2691 } else if ( $layout == "archive" ) {
2692 if ( $useHeader ) {
2693 nitropack_header( "x-nitro-tags:pageType:archive" );
2694 } else {
2695 $nitro->getApi()->tagUrl( $nitro->getUrl(), "pageType:archive" );
2696 }
2697 } else {
2698 if ( $useHeader && count( $GLOBALS["NitroPack.tags"] ) <= 100 ) {
2699 nitropack_header( "x-nitro-tags:" . implode( "|", array_map( "nitropack_filter_tag", array_keys( $GLOBALS["NitroPack.tags"] ) ) ) );
2700 } else {
2701 $nitro->getApi()->tagUrl( $nitro->getUrl(), array_map( "nitropack_filter_tag", array_keys( $GLOBALS["NitroPack.tags"] ) ) );
2702 }
2703 }
2704 } catch (\Exception $e) {
2705 }
2706 }
2707 }
2708
2709 function nitropack_extend_nonce_life( $life ) {
2710 // Nonce life should be extended only:
2711 // - if NitroPack is connected for this site
2712 // - if the current value is shorter than the life time of a cache file
2713 // - if no user is logged in
2714 // - for cacheable requests
2715 //
2716 // Reasons why we might need to extend the nonce life time even for requests that are not cacheable:
2717 // - a request may be cachable at first, but become uncachable during changes at runtime or user actions on the page (example: log in via AJAX on a category page. Once logged in the page will not redirect, but if there is an infinite scroll it will stop working if we stop extending the nonce life time)
2718 // - a request may seem cachable at first, but be determined uncachable during runtime (example: visit to a URL of a page whose post type does not match the enabled cacheable post types, or a cart, checkout page, etc.)
2719
2720 if ( ( null !== $nitro = get_nitropack_sdk() ) ) {
2721 $siteConfig = nitropack_get_site_config();
2722 if ( $siteConfig && ! empty( $siteConfig["isDlmActive"] ) && ! empty( $siteConfig["dlm_downloading_url"] ) && ! empty( $siteConfig["dlm_download_endpoint"] ) ) {
2723 $currentUrl = $nitro->getUrl();
2724 if ( strpos( $currentUrl, $siteConfig["dlm_downloading_url"] ) !== false || strpos( $currentUrl, $siteConfig["dlm_download_endpoint"] ) !== false ) {
2725 // Do not modify the nonce times on pages of Download Monitor
2726 return $life;
2727 }
2728 }
2729 $cacheExpiration = $nitro->getConfig()->PageCache->ExpireTime;
2730 return $cacheExpiration > $life ? $cacheExpiration : $life; // Extend the life of cacheable nonces up to the cache expiration time if needed
2731 }
2732 return $life;
2733 }
2734
2735 function nitropack_reconfigure_webhooks() {
2736 nitropack_verify_ajax_nonce( $_REQUEST );
2737 $siteConfig = nitropack_get_site_config();
2738
2739 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) ) {
2740 $siteId = $siteConfig["siteId"];
2741 if ( null !== $nitro = get_nitropack_sdk() ) {
2742 $token = nitropack_generate_webhook_token( $siteId );
2743 try {
2744 nitropack_setup_webhooks( $nitro, $token );
2745 update_option( "nitropack-webhookToken", $token );
2746 nitropack_json_and_exit( array( "status" => "success", 'message' => __( 'Connection reconfigured successfully', 'nitropack' ) ) );
2747 } catch (\NitroPack\SDK\WebhookException $e) {
2748 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Webhook Error: ' . $e );
2749 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Webhook Error: ', 'nitropack' ) . $e->getTraceAsString() ) );
2750 }
2751 } else {
2752 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Unable to get SDK instance' );
2753 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Unable to get SDK instance', 'nitropack' ) ) );
2754 }
2755 } else {
2756 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Incomplete site config. Please reinstall the plugin' );
2757 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Incomplete site config. Please reinstall the plugin!', 'nitropack' ) ) );
2758 }
2759 }
2760
2761 function nitropack_generate_webhook_token( $siteId ) {
2762 return md5( __FILE__ . ":" . $siteId );
2763 }
2764
2765 function nitropack_verify_connect_ajax() {
2766 nitropack_verify_ajax_nonce( $_REQUEST );
2767 $siteId = ! empty( $_POST["siteId"] ) ? $_POST["siteId"] : "";
2768 $siteSecret = ! empty( $_POST["siteSecret"] ) ? $_POST["siteSecret"] : "";
2769 nitropack_verify_connect( $siteId, $siteSecret );
2770 }
2771
2772 function nitropack_check_func_availability( $func_name ) {
2773 if ( function_exists( 'ini_get' ) ) {
2774 $existsResult = stripos( ini_get( 'disable_functions' ), $func_name ) === false;
2775 } else {
2776 $existsResult = function_exists( $func_name );
2777 }
2778 return $existsResult;
2779 }
2780
2781 function nitropack_prevent_connecting( $nitroSDK ) {
2782 $remoteUrl = $nitroSDK->getApi()->getWebhook( "config" );
2783 if ( empty( $remoteUrl ) ) {
2784 return false;
2785 }
2786 $siteConfig = nitropack_get_site_config();
2787 $localUrl = new \NitroPack\Url\Url( $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url() );
2788 $localHome = strtolower( $localUrl->getHost() . $localUrl->getPath() );
2789 $storedUrl = new \NitroPack\Url\Url( $remoteUrl );
2790 $remoteHome = strtolower( $storedUrl->getHost() . $storedUrl->getPath() );
2791 if ( $localHome === $remoteHome ) {
2792 return false;
2793 }
2794 return array( 'local' => $localHome, 'remote' => $remoteHome );
2795 }
2796
2797 function nitropack_verify_connect( $siteId, $siteSecret ) {
2798
2799 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Verifying connection to NitroPack API' );
2800
2801 if ( ! nitropack_check_func_availability( 'stream_socket_client' ) ) {
2802
2803 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'stream_socket_client function is not allowed by your host.' );
2804
2805 nitropack_json_and_exit( array( "status" => "error", "message" => "stream_socket_client function is not allowed by your host. <a href=\"https://support.nitropack.io/hc/en-us/articles/360020898137\" target=\"_blank\" rel=\"noreferrer noopener\">Read more</a>" ) );
2806 }
2807
2808 if ( ! nitropack_check_func_availability( 'stream_context_create' ) ) {
2809 // <a href=\"https://support.nitropack.io/hc/en-us/articles/360020898137\" target=\"_blank\" rel=\"noreferrer noopener\">Read more</a>
2810 // ^ Similar article needed on website for stream_context_create function
2811 nitropack_json_and_exit( array( "status" => "error", "message" => "stream_context_create function is not allowed by your host." ) );
2812 }
2813
2814 if ( empty( $siteId ) || empty( $siteSecret ) ) {
2815
2816 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Site ID and Site Secret cannot be empty' );
2817
2818 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Site ID and Site Secret cannot be empty', 'nitropack' ) ) );
2819 }
2820
2821 //remove tags and whitespaces
2822 $siteId = trim( esc_attr( $siteId ) );
2823 $siteSecret = trim( esc_attr( $siteSecret ) );
2824
2825 if ( ! nitropack_validate_site_id( $siteId ) || ! nitropack_validate_site_secret( $siteSecret ) ) {
2826
2827 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Invalid Site ID or Site Secret value' );
2828
2829 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Invalid Site ID or Site Secret value', 'nitropack' ) ) );
2830 }
2831
2832 try {
2833 $blogId = get_current_blog_id();
2834 if ( null !== $nitro = get_nitropack_sdk( $siteId, $siteSecret, NULL, true ) ) {
2835 if ( ! $nitro->checkHealthStatus() ) {
2836
2837 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error when trying to communicate with NitroPack\'s servers. Current health status: ' . $nitro->getHealthStatus() );
2838
2839 nitropack_json_and_exit( array(
2840 "status" => "error",
2841 "message" => __( 'Error when trying to communicate with NitroPack\'s servers. Please try again in a few minutes. If the issue persists, please', 'nitropack' ) . " <a href='https://support." . NITROPACK_HOST . "/hc/en-us' target='_blank'>contact us</a>."
2842 ) );
2843 }
2844
2845 $preventParing = apply_filters( 'nitropack_prevent_connect', nitropack_prevent_connecting( $nitro ) );
2846 if ( $preventParing ) {
2847
2848 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'It looks like another site ' . $preventParing['remote'] . ' is already connected using these credentials. Either disconnect it or register a new site in your NitroPack dashboard.' );
2849
2850 nitropack_json_and_exit( array(
2851 "status" => "error",
2852 "message" => "It looks like another site <strong>({$preventParing['remote']})</strong> is already connected using these credentials. Either disconnect it or register a new site in your NitroPack dashboard.<br/>
2853 <a href='https://support.nitropack.io/hc/en-us/articles/4405254569745' target='_blank' rel='noreferrer noopener'>Read more</a>"
2854 ) );
2855 }
2856 $token = nitropack_generate_webhook_token( $siteId );
2857 get_nitropack()->settings->set_required_settings( $token );
2858
2859 nitropack_setup_webhooks( $nitro, $token );
2860
2861 // _icl_current_language is WPML cookie, it is added here for compatibility with this module
2862 $customVariationCookies = array( "np_wc_currency", "np_wc_currency_language", "_icl_current_language" );
2863 $variationCookies = $nitro->getApi()->getVariationCookies();
2864 foreach ( $variationCookies as $cookie ) {
2865 $index = array_search( $cookie["name"], $customVariationCookies );
2866 if ( $index !== false ) {
2867 array_splice( $customVariationCookies, $index, 1 );
2868 }
2869 }
2870
2871 foreach ( $customVariationCookies as $cookieName ) {
2872 $nitro->getApi()->setVariationCookie( $cookieName );
2873 }
2874
2875 $nitro->fetchConfig(); // Reload the variation cookies
2876
2877 get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId );
2878 nitropack_install_advanced_cache();
2879
2880 try {
2881 do_action( 'nitropack_integration_purge_all' );
2882 } catch (\Exception $e) {
2883 // Exception while signaling our 3rd party integration addons to purge their cache
2884 }
2885
2886 nitropack_event( "connect", $nitro );
2887 nitropack_event( "enable_extension", $nitro );
2888
2889 // Optimize front page
2890 $siteConfig = nitropack_get_site_config();
2891 if ( $siteConfig ) {
2892 $nitro->getApi()->runWarmup( [ $siteConfig['home_url'] ], true ); // force run a warmup on the home page
2893 }
2894
2895 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'NitroPack connected' );
2896 nitropack_json_and_exit( array(
2897 "status" => "success",
2898 "message" => __( "Connected", "nitropack" )
2899 ) );
2900 }
2901 } catch (\NitroPack\SDK\WebhookException $e) {
2902
2903 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( $e );
2904
2905 nitropack_json_and_exit( array( "status" => "error", "message" => $e ) );
2906 } catch (\NitroPack\SDK\StorageException $e) {
2907
2908 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Permission Error: ' . $e );
2909
2910 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Permission Error: ', 'nitropack' ) . $e ) );
2911 } catch (\NitroPack\SDK\EmptyConfigException $e) {
2912
2913 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error while fetching remote config: ' . $e );
2914
2915 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Error while fetching remote config: ', 'nitropack' ) . $e ) );
2916 } catch (\NitroPack\SocketOpenException $e) {
2917
2918 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Can\'t establish connection with NitroPack\'s servers. ' . $e );
2919
2920 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Can\'t establish connection with NitroPack\'s servers', 'nitropack' ) ) );
2921 } catch (\Exception $e) {
2922
2923 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Incorrect API credentials. Please make sure that you copied them correctly and try again. ' . $e );
2924
2925 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Incorrect API credentials. Please make sure that you copied them correctly and try again.', 'nitropack' ) ) );
2926 }
2927
2928 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error verifying connection to NitroPack.' );
2929
2930 nitropack_json_and_exit( array( "status" => "error" ) );
2931 }
2932
2933 function nitropack_reset_webhooks( $nitroSDK ) {
2934 $nitroSDK->getApi()->unsetWebhook( "config" );
2935 $nitroSDK->getApi()->unsetWebhook( "cache_clear" );
2936 $nitroSDK->getApi()->unsetWebhook( "cache_ready" );
2937 }
2938
2939 function nitropack_setup_webhooks( $nitro, $token = NULL ) {
2940 if ( ! $nitro || ! $token ) {
2941 throw new \NitroPack\SDK\WebhookException( 'Webhook token cannot be empty.' );
2942 }
2943
2944 $homeUrl = strtolower( get_home_url() );
2945 $configUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=config&token=$token" );
2946 $cacheClearUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=cache_clear&token=$token" );
2947 $cacheReadyUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=cache_ready&token=$token" );
2948
2949 $nitro->getApi()->setWebhook( "config", $configUrl );
2950 $nitro->getApi()->setWebhook( "cache_clear", $cacheClearUrl );
2951 $nitro->getApi()->setWebhook( "cache_ready", $cacheReadyUrl );
2952 }
2953
2954 function nitropack_disconnect() {
2955 nitropack_verify_ajax_nonce( $_REQUEST );
2956
2957 nitropack_uninstall_advanced_cache();
2958
2959 try {
2960 nitropack_event( "disconnect" );
2961 if ( null !== $nitro = get_nitropack_sdk() ) {
2962 nitropack_reset_webhooks( $nitro );
2963 }
2964 } catch (\Exception $e) {
2965 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'NitroPack cannot be disconnected. Error: ' . $e );
2966 nitropack_json_and_exit( array( "status" => "error", "message" => $e ) );
2967 }
2968
2969 get_nitropack()->unsetCurrentBlogConfig();
2970
2971 $hostingNoticeFile = nitropack_get_hosting_notice_file();
2972 if ( file_exists( $hostingNoticeFile ) ) {
2973 if ( WP_DEBUG ) {
2974 unlink( $hostingNoticeFile );
2975 } else {
2976 @unlink( $hostingNoticeFile );
2977 }
2978 }
2979 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'NitroPack disconnected' );
2980 nitropack_json_and_exit( array( "status" => "success", "message" => __( "Disconnected", "nitropack" ) ) );
2981 }
2982
2983
2984 function nitropack_set_compression_ajax() {
2985 nitropack_verify_ajax_nonce( $_REQUEST );
2986 $option = (int) ! empty( $_POST["data"]["compressionStatus"] );
2987 $updated = update_option( "nitropack-enableCompression", $option );
2988
2989 if ( $updated ) {
2990 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'HTML Compression is ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
2991 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), "hasCompression" => $option ) );
2992 } else {
2993 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'HTML Compression cannot be ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
2994 nitropack_json_and_exit( array(
2995 "type" => "error",
2996 "message" => nitropack_admin_toast_msgs( 'error' )
2997 ) );
2998 }
2999 }
3000 function nitropack_set_can_editor_clear_cache() {
3001 nitropack_verify_ajax_nonce( $_REQUEST );
3002 $option = (int) ! empty( $_POST["data"]["canEditorClearCache"] );
3003 $updated = update_option( "nitropack-canEditorClearCache", $option );
3004 if ( $updated ) {
3005 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Allow Editors to purge cache is ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3006 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), "allowedEditors" => $option ) );
3007 } else {
3008 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Allow Editors to purge cache cannot be ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3009 nitropack_json_and_exit( array(
3010 "type" => "error",
3011 "message" => nitropack_admin_toast_msgs( 'error' )
3012 ) );
3013 }
3014 }
3015
3016 /* Verification is handled in JS, therefore there are no errors here to be handled here. Used for logging purposes. */
3017 function nitropack_set_optimization_mode() {
3018 nitropack_verify_ajax_nonce( $_REQUEST );
3019 $option = ! empty( $_POST["mode"] && is_numeric( $_POST['mode'] ) ) ? (int) $_POST["mode"] : null;
3020 $modes = [ 1 => 'Standard', 2 => 'Medium', 3 => 'Strong', 4 => 'Ludicrous', 5 => 'Custom' ];
3021 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Optimization mode set to: ' . $modes[ $option ] );
3022 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), "mode" => $option ) );
3023 }
3024 function nitropack_set_auto_cache_purge_ajax() {
3025 nitropack_verify_ajax_nonce( $_REQUEST );
3026 $option = (int) ! empty( $_POST["autoCachePurgeStatus"] );
3027 $updated = update_option( "nitropack-autoCachePurge", $option );
3028 if ( $updated ) {
3029 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Auto cache purge is ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3030 nitropack_json_and_exit( [ "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), 'autoCachePurgeStatus' => $option ] );
3031 } else {
3032 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Auto cache purge cannot be ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3033 nitropack_json_and_exit( [
3034 "type" => "error",
3035 "message" => nitropack_admin_toast_msgs( 'error' )
3036 ] );
3037 }
3038 }
3039 function nitropack_set_ajax_shortcodes_ajax() {
3040 nitropack_verify_ajax_nonce( $_REQUEST );
3041
3042 $new_shortcodes = isset( $_POST['shortcodes'] ) ? $_POST['shortcodes'] : null;
3043 $enabled = isset( $_POST['enabled'] ) ? $_POST['enabled'] : null;
3044
3045 if ( $new_shortcodes === null && $enabled === null ) {
3046 nitropack_json_and_exit( array(
3047 "type" => "error",
3048 "message" => nitropack_admin_toast_msgs( 'error' )
3049 ) );
3050 }
3051 $nitropack = get_nitropack();
3052 $siteConfig = $nitropack->Config->get();
3053 $configKey = \NitroPack\WordPress\NitroPack::getConfigKey();
3054
3055 if ( ! is_null( $enabled ) ) {
3056 $siteConfig[ $configKey ]['options_cache']['ajaxShortcodes']['enabled'] = $enabled === '1';
3057 }
3058
3059 if ( ! is_null( $new_shortcodes ) ) {
3060 $existing_options = ( is_array( $new_shortcodes ) && $new_shortcodes[0] === '' ) ? [] : $new_shortcodes;
3061 if ( $existing_options )
3062 $siteConfig[ $configKey ]['options_cache']['ajaxShortcodes']['shortcodes'] = $existing_options;
3063 }
3064 $updated = $nitropack->Config->set( $siteConfig );
3065
3066 if ( $updated ) {
3067
3068 if ( $enabled ) {
3069 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Shortcodes are enabled' );
3070 } elseif ( ! $enabled && ! $new_shortcodes ) {
3071 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Shortcodes are disabled' );
3072 }
3073
3074 if ( is_array( $new_shortcodes ) && $new_shortcodes[0] === '' ) {
3075 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( "Shortcodes are empty" );
3076 } elseif ( is_array( $new_shortcodes ) ) {
3077 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( "Shortcodes are: " . implode( ", ", $new_shortcodes ) );
3078 }
3079
3080 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ) ) );
3081 } else {
3082
3083 if ( $enabled ) {
3084 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Shortcodes are ' . $enabled ? 'enabled' : 'disabled' );
3085 } elseif ( ! $enabled && ! $new_shortcodes ) {
3086 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Shortcodes are disabled' );
3087 }
3088 if ( is_array( $new_shortcodes ) && $new_shortcodes[0] === '' ) {
3089 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( "Shortcodes are empty" );
3090 } elseif ( is_array( $new_shortcodes ) ) {
3091 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( "Shortcodes are: " . implode( ", ", $new_shortcodes ) );
3092 }
3093
3094 nitropack_json_and_exit( array(
3095 "type" => "error",
3096 "message" => nitropack_admin_toast_msgs( 'error' )
3097 ) );
3098 }
3099 }
3100
3101
3102 function nitropack_set_cart_cache_ajax() {
3103 nitropack_verify_ajax_nonce( $_REQUEST );
3104 if ( get_nitropack()->isConnected() && nitropack_render_woocommerce_cart_cache_option() ) {
3105 $cartCacheStatus = (int) ( ! empty( $_POST["cartCacheStatus"] ) );
3106
3107 if ( $cartCacheStatus == 1 ) {
3108 nitropack_enable_cart_cache();
3109 } else {
3110 nitropack_disable_cart_cache();
3111 }
3112 }
3113
3114 nitropack_json_and_exit( array(
3115 "type" => "error",
3116 "message" => nitropack_admin_toast_msgs( 'error' )
3117 ) );
3118 }
3119
3120 function nitropack_set_stock_reduce_status() {
3121 nitropack_verify_ajax_nonce( $_REQUEST );
3122 $option = (int) ! empty( $_POST["data"]["stockReduceStatus"] );
3123 $updated = update_option( "nitropack-stockReduceStatus", $option );
3124 if ( $updated ) {
3125 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Stock Reduce Status is ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3126 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), "stockReduceStatus" => $option ) );
3127 } else {
3128 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Stock Reduce Status cannot be' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3129 nitropack_json_and_exit( array(
3130 "type" => "error",
3131 "message" => nitropack_admin_toast_msgs( 'error' )
3132 ) );
3133 }
3134 }
3135
3136 function nitropack_set_bb_cache_purge_sync_ajax() {
3137 nitropack_verify_ajax_nonce( $_REQUEST );
3138 $option = (int) ! empty( $_POST["bbCachePurgeSyncStatus"] );
3139 $updated = update_option( "nitropack-bbCacheSyncPurge", $option );
3140 if ( $updated ) {
3141 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Beaver Builder Status is ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3142 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), 'bbCacheSyncPurgeStatus' => $option ) );
3143 } else {
3144 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Beaver Builder Status cannot be ' . ( $option === 1 ? 'enabled' : 'disabled' ) );
3145 nitropack_json_and_exit( array(
3146 "type" => "error",
3147 "message" => nitropack_admin_toast_msgs( 'error' )
3148 ) );
3149 }
3150 }
3151
3152 function nitropack_set_cacheable_post_types() {
3153 nitropack_verify_ajax_nonce( $_REQUEST );
3154 $currentCacheableObjectTypes = nitropack_get_cacheable_object_types();
3155 $cacheableObjectTypes = ! empty( $_POST["cacheableObjectTypes"] ) ? $_POST["cacheableObjectTypes"] : array();
3156 $noncacheableObjectTypes = ! empty( $_POST["noncacheableObjectTypes"] ) ? $_POST["noncacheableObjectTypes"] : array();
3157
3158 /* Used for non optimized CPT modal which is shown only once */
3159 if ( isset( $_POST["append"] ) && $_POST["append"] === "yes" ) {
3160 $cacheableObjectTypes = array_merge( $currentCacheableObjectTypes, $cacheableObjectTypes );
3161 }
3162
3163 update_option( "nitropack-cacheableObjectTypes", $cacheableObjectTypes );
3164 update_option( "nitropack-nonCacheableObjectTypes", $noncacheableObjectTypes );
3165
3166 foreach ( $currentCacheableObjectTypes as $objectType ) {
3167 if ( ! in_array( $objectType, $cacheableObjectTypes ) ) {
3168 nitropack_purge( NULL, "pageType:" . $objectType, "Optimizing '$objectType' pages was manually disabled" );
3169 }
3170 }
3171
3172 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( "Optimized CPTs: " . implode( ", ", $cacheableObjectTypes ) );
3173 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( "Non-optimized CPTs: " . ( ! empty( $noncacheableObjectTypes ) && is_array( $noncacheableObjectTypes ) ? implode( ", ", $noncacheableObjectTypes ) : 'N/A' ) );
3174
3175 nitropack_json_and_exit( array(
3176 "type" => "success",
3177 "message" => nitropack_admin_toast_msgs( 'success' )
3178 ) );
3179 }
3180
3181 function nitropack_test_compression_ajax() {
3182 nitropack_verify_ajax_nonce( $_REQUEST );
3183 $hasCompression = true;
3184 try {
3185 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) { // Flywheel: Compression is enabled by default
3186 $updated = update_option( "nitropack-enableCompression", 0 );
3187 } else {
3188 require_once plugin_dir_path( __FILE__ ) . nitropack_trailingslashit( 'nitropack-sdk' ) . 'autoload.php';
3189 $http = new NitroPack\HttpClient\HttpClient( get_site_url() );
3190 $http->setHeader( "X-NitroPack-Request", 1 );
3191 $http->timeout = 25;
3192 $http->fetch();
3193 $headers = $http->getHeaders();
3194 if ( ! empty( $headers["content-encoding"] ) && strtolower( $headers["content-encoding"] ) == "gzip" ) { // compression is present, so there is no need to enable it in NitroPack. We only check for GZIP, because this is the only supported compression in the HttpClient
3195 $updated = update_option( "nitropack-enableCompression", 0 );
3196 $hasCompression = true;
3197 } else { // no compression, we must enable it from NitroPack
3198 $updated = update_option( "nitropack-enableCompression", 1 );
3199 $hasCompression = false;
3200 }
3201 }
3202 update_option( "nitropack-checkedCompression", 1 );
3203 } catch (\Exception $e) {
3204 nitropack_json_and_exit( array( "type" => "error", "message" => nitropack_admin_toast_msgs( 'error' ) ) );
3205 }
3206 if ( $updated )
3207 nitropack_json_and_exit( array( "type" => "success", "message" => nitropack_admin_toast_msgs( 'success' ), "hasCompression" => $hasCompression ) );
3208 }
3209
3210 function nitropack_render_woocommerce_cart_cache_option() {
3211 return class_exists( 'WooCommerce' );
3212 }
3213
3214 function nitropack_is_cart_cache_active() {
3215 $nitro = get_nitropack()->getSdk();
3216 if ( $nitro ) {
3217 $config = $nitro->getConfig();
3218 if ( ! empty( $config->StatefulCache->Status ) && ! empty( $config->StatefulCache->CartCache ) ) {
3219 return nitropack_is_cart_cache_available();
3220 }
3221 }
3222 return false;
3223 }
3224
3225 function nitropack_is_cart_cache_available() {
3226 $nitro = get_nitropack()->getSdk();
3227 if ( $nitro ) {
3228 $config = $nitro->getConfig();
3229 if ( ! empty( $config->StatefulCache->isCartCacheAvailable ) ) {
3230 return true;
3231 }
3232 }
3233 return false;
3234 }
3235
3236 function nitropack_handle_compression_toggle( $old_value, $new_value ) {
3237 nitropack_update_blog_compression( $new_value == 1 );
3238 }
3239
3240 function nitropack_update_blog_compression( $enableCompression = false ) {
3241 if ( get_nitropack()->isConnected() ) {
3242 $siteConfig = nitropack_get_site_config();
3243 $siteId = $siteConfig["siteId"];
3244 $siteSecret = $siteConfig["siteSecret"];
3245 $blogId = get_current_blog_id();
3246 get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId, $enableCompression );
3247 }
3248 }
3249
3250 function nitropack_enable_warmup() {
3251 nitropack_verify_ajax_nonce( $_REQUEST );
3252 $append = '';
3253 if ( nitropack_is_wp_cli() )
3254 $append = '[CLI] ';
3255 if ( null !== $nitro = get_nitropack_sdk() ) {
3256 try {
3257 $nitro->getApi()->enableWarmup();
3258 $nitro->getApi()->setWarmupHomepage( get_home_url() );
3259 $nitro->getApi()->runWarmup();
3260 } catch (\Exception $e) {
3261 }
3262 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cache warmup is enabled' );
3263 nitropack_json_and_exit( array(
3264 "type" => "success",
3265 "message" => __( 'Cache warmup has been enabled successfully!', 'nitropack' )
3266 ) );
3267 }
3268 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cache warmup cannot be enabled' );
3269 nitropack_json_and_exit( array(
3270 "type" => "error",
3271 "message" => __( 'There was an error while enabling the cache warmup!', 'nitropack' )
3272 ) );
3273 }
3274
3275 function nitropack_disable_warmup() {
3276 nitropack_verify_ajax_nonce( $_REQUEST );
3277 if ( null !== $nitro = get_nitropack_sdk() ) {
3278 try {
3279 $nitro->getApi()->disableWarmup();
3280 $nitro->getApi()->resetWarmup();
3281 delete_option( 'np_warmup_sitemap' );
3282 } catch (\Exception $e) {
3283 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Cache warmup cannot be disabled. Error: ' . $e->getTraceAsString() );
3284 }
3285 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cache warmup is disabled' );
3286 nitropack_json_and_exit( array(
3287 "type" => "success",
3288 "message" => __( 'Cache warmup has been disabled successfully!', 'nitropack' )
3289 ) );
3290 }
3291 nitropack_json_and_exit( array(
3292 "type" => "error",
3293 "message" => __( 'There was an error while disabling the cache warmup!', 'nitropack' )
3294 ) );
3295 }
3296
3297 function nitropack_run_warmup() {
3298 nitropack_verify_ajax_nonce( $_REQUEST );
3299
3300 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Running warmup' );
3301
3302 if ( null !== $nitro = get_nitropack_sdk() ) {
3303 try {
3304 $nitro->getApi()->runWarmup();
3305 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cache warmup has been started' );
3306 nitropack_json_and_exit( array(
3307 "type" => "success",
3308 "message" => __( 'Success! Cache warmup has been started successfully!', 'nitropack' )
3309 ) );
3310 } catch (\Exception $e) {
3311 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'There was an error while starting the cache warmup. Error: ' . $e->getTraceAsString() );
3312 }
3313 }
3314
3315 nitropack_json_and_exit( array(
3316 "type" => "error",
3317 "message" => __( 'Error! There was an error while starting the cache warmup!', 'nitropack' )
3318 ) );
3319 }
3320
3321 function nitropack_estimate_warmup() {
3322 nitropack_verify_ajax_nonce( $_REQUEST );
3323 if ( null !== $nitro = get_nitropack_sdk() ) {
3324 try {
3325 if ( ! session_id() ) {
3326 session_start();
3327 }
3328 $id = ! empty( $_POST["estId"] ) ? preg_replace( "/[^a-fA-F0-9]/", "", (string) $_POST["estId"] ) : NULL;
3329 if ( $id !== NULL && ( ! is_string( $id ) || $id != $_SESSION["nitroEstimateId"] ) ) {
3330 nitropack_json_and_exit( array(
3331 "type" => "error",
3332 "message" => __( 'Invalid estimation ID!', 'nitropack' )
3333 ) );
3334 }
3335
3336 $sitemapUrls = nitropack_active_sitemap_plugins() ? nitropack_get_site_maps() : NULL;
3337 $configuredSitemap = false;
3338
3339 if ( $sitemapUrls === NULL ) {
3340
3341 $defaultSitemap = get_default_sitemap();
3342 if ( $defaultSitemap ) {
3343 $nitro->getApi()->setWarmupSitemap( $defaultSitemap );
3344 $configuredSitemap = true;
3345 }
3346
3347 delete_option( 'np_warmup_sitemap' );
3348 } else {
3349
3350 $warmupSitemap = evaluate_warmup_sitemap( $sitemapUrls );
3351 if ( $warmupSitemap ) {
3352 $nitro->getApi()->setWarmupSitemap( $warmupSitemap );
3353 $configuredSitemap = true;
3354 }
3355 }
3356
3357 if ( ! $configuredSitemap ) {
3358 $nitro->getApi()->setWarmupSitemap( NULL );
3359 }
3360
3361 $nitro->getApi()->setWarmupHomepage( get_home_url() );
3362
3363 $optimizationsEstimate = $nitro->getApi()->estimateWarmup( $id );
3364
3365 if ( $id === NULL ) {
3366 $_SESSION["nitroEstimateId"] = $optimizationsEstimate; // When id is NULL, $optimizationsEstimate holds the ID for the newly started estimate
3367 }
3368 } catch (\Exception $e) {
3369 }
3370 $json_data = array(
3371 "type" => "success",
3372 "res" => $optimizationsEstimate,
3373 "sitemap_indication" => get_option( 'np_warmup_sitemap', false ),
3374 "message" => __( 'Warmup estimation failed.', 'nitropack' ),
3375 );
3376 if ( is_int( $optimizationsEstimate ) && $optimizationsEstimate > 0 ) {
3377 $json_data["message"] = __( 'Cache warmup has been estimated successfully.', 'nitropack' );
3378 } else if ( $optimizationsEstimate === 0 ) {
3379 $json_data["message"] = __( 'We could not find any links for warming up on your home page.', 'nitropack' );
3380 } else {
3381 $json_data["message"] = __( 'Warmup estimation failed. Please try again or contact support if the issue persists', 'nitropack' );
3382 }
3383 nitropack_json_and_exit( $json_data );
3384 }
3385
3386 nitropack_json_and_exit( array(
3387 "type" => "error",
3388 "message" => __( 'Warmup estimation failed.', 'nitropack' )
3389 ) );
3390 }
3391
3392 function nitropack_warmup_stats() {
3393 nitropack_verify_ajax_nonce( $_REQUEST );
3394 if ( null !== $nitro = get_nitropack_sdk() ) {
3395 try {
3396 $stats = $nitro->getApi()->getWarmupStats();
3397 } catch (\Exception $e) {
3398 nitropack_json_and_exit( array(
3399 "type" => "error",
3400 "message" => __( 'Error! There was an error while fetching warmup stats!', 'nitropack' )
3401 ) );
3402 }
3403
3404 nitropack_json_and_exit( array(
3405 "type" => "success",
3406 "stats" => $stats
3407 ) );
3408 }
3409
3410 nitropack_json_and_exit( array(
3411 "type" => "error",
3412 "message" => __( 'Error! There was an error while fetching warmup stats!', 'nitropack' )
3413 ) );
3414 }
3415
3416
3417 function nitropack_enable_cart_cache() {
3418 if ( null !== $nitro = get_nitropack_sdk() ) {
3419 try {
3420 $nitro->enableCartCache();
3421 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cart cache is enabled' );
3422 nitropack_json_and_exit( array(
3423 "type" => "success",
3424 "message" => nitropack_admin_toast_msgs( 'success' )
3425 ) );
3426 } catch (\Exception $e) {
3427 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Cart cache cannot be enabled. Error: ' . $e );
3428 }
3429 }
3430
3431 nitropack_json_and_exit( array(
3432 "type" => "error",
3433 "message" => nitropack_admin_toast_msgs( 'error' )
3434 ) );
3435 }
3436
3437 function nitropack_disable_cart_cache() {
3438 if ( null !== $nitro = get_nitropack_sdk() ) {
3439 try {
3440 $nitro->disableCartCache();
3441 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Cart cache is be disabled' );
3442 nitropack_json_and_exit( array(
3443 "type" => "success",
3444 "message" => nitropack_admin_toast_msgs( 'success' )
3445 ) );
3446 } catch (\Exception $e) {
3447 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Cart cache cannot be disabled. Error: ' . $e );
3448 }
3449 }
3450
3451 nitropack_json_and_exit( array(
3452 "type" => "error",
3453 "message" => nitropack_admin_toast_msgs( 'error' )
3454 ) );
3455 }
3456
3457
3458
3459 function nitropack_get_site_config() {
3460 return get_nitropack()->getSiteConfig();
3461 }
3462
3463 function nitropack_get_current_site_id() {
3464
3465 $site_config = nitropack_get_site_config();
3466
3467 if ( $site_config && isset( $site_config['siteId'] ) ) {
3468 return $site_config['siteId'];
3469 }
3470 }
3471
3472 function get_nitropack() {
3473 return \NitroPack\WordPress\NitroPack::getInstance();
3474 }
3475
3476 function nitropack_event( $event, $nitro = null, $additional_meta_data = null ) {
3477 global $wp_version;
3478
3479 try {
3480 $eventUrl = get_nitropack_integration_url( "extensionEvent", $nitro );
3481 $domain = ! empty( $_SERVER["HTTP_HOST"] ) ? $_SERVER["HTTP_HOST"] : "Unknown";
3482
3483
3484 if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
3485 $platform = 'WooCommerce';
3486 } else {
3487 $platform = 'WordPress';
3488 }
3489
3490 $query_data = array(
3491 'event' => $event,
3492 'platform' => $platform,
3493 'platform_version' => $wp_version,
3494 'nitropack_extension_version' => NITROPACK_VERSION,
3495 'additional_meta_data' => $additional_meta_data ? json_encode( $additional_meta_data ) : "{}",
3496 'domain' => $domain
3497 );
3498
3499 $client = new NitroPack\HttpClient\HttpClient( $eventUrl . '&' . http_build_query( $query_data ) );
3500 $client->doNotDownload = true;
3501 $client->fetch();
3502 } catch (\Exception $e) {
3503 }
3504 }
3505
3506 function nitropack_get_wpconfig_path() {
3507 $configFilePath = nitropack_trailingslashit( ABSPATH ) . "wp-config.php";
3508 if ( ! file_exists( $configFilePath ) ) {
3509 $configFilePath = nitropack_trailingslashit( dirname( ABSPATH ) ) . "wp-config.php";
3510 $settingsFilePath = nitropack_trailingslashit( dirname( ABSPATH ) ) . "wp-settings.php"; // We need to check for this file to avoid confusion if the current installation is a nested directory of another WP installation. Refer to wp-load.php for more information.
3511 if ( ! file_exists( $configFilePath ) || file_exists( $settingsFilePath ) ) {
3512 return false;
3513 }
3514 }
3515
3516
3517 if ( ! is_writable( $configFilePath ) ) {
3518 return false;
3519 }
3520
3521 return $configFilePath;
3522 }
3523
3524 function nitropack_get_htaccess_path() {
3525 $configFilePath = nitropack_trailingslashit( ABSPATH ) . ".htaccess";
3526 if ( ! file_exists( $configFilePath ) ) {
3527 return false;
3528 }
3529
3530
3531 if ( ! is_writable( $configFilePath ) ) {
3532 return false;
3533 }
3534
3535 return $configFilePath;
3536 }
3537
3538 function nitropack_detect_hosting() {
3539 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) {
3540 return "flywheel";
3541 } else if ( \NitroPack\Integration\Hosting\Cloudways::detect() ) {
3542 return "cloudways";
3543 } else if ( \NitroPack\Integration\Hosting\WPEngine::detect() ) {
3544 return "wpengine";
3545 } else if ( \NitroPack\Integration\Hosting\SiteGround::detect() ) {
3546 return "siteground";
3547 } else if ( \NitroPack\Integration\Hosting\GoDaddyWPaaS::detect() ) {
3548 return "godaddy_wpaas";
3549 } else if ( \NitroPack\Integration\Hosting\GridPane::detect() ) {
3550 return "gridpane";
3551 } else if ( \NitroPack\Integration\Hosting\Kinsta::detect() ) {
3552 return "kinsta";
3553 } else if ( \NitroPack\Integration\Hosting\Closte::detect() ) {
3554 return "closte";
3555 } else if ( \NitroPack\Integration\Hosting\Pagely::detect() ) {
3556 return "pagely";
3557 } else if ( \NitroPack\Integration\Hosting\WPX::detect() ) {
3558 return "wpx";
3559 } else if ( \NitroPack\Integration\Hosting\Vimexx::detect() ) {
3560 return "vimexx";
3561 } else if ( \NitroPack\Integration\Hosting\Pressable::detect() ) {
3562 return "pressable";
3563 } else if ( \NitroPack\Integration\Hosting\RocketNet::detect() ) {
3564 return "rocketnet";
3565 } else if ( \NitroPack\Integration\Hosting\Savvii::detect() ) {
3566 return "savvii";
3567 } else if ( \NitroPack\Integration\Hosting\DreamHost::detect() ) {
3568 return "dreamhost";
3569 } else if ( \NitroPack\Integration\Hosting\Raidboxes::detect() ) {
3570 return "raidboxes";
3571 } else {
3572 return "unknown";
3573 }
3574 }
3575
3576 function nitropack_removeCacheBustParam( $content ) {
3577 $content = preg_replace( "/(\?|%26|&#0?38;|&#x0?26;|&(amp;)?)ignorenitro(%3D|=)[a-fA-F0-9]{32}(?!%26|&#0?38;|&#x0?26;|&(amp;)?)\/?/mu", "", $content );
3578 return preg_replace( "/(\?|%26|&#0?38;|&#x0?26;|&(amp;)?)ignorenitro(%3D|=)[a-fA-F0-9]{32}(%26|&#0?38;|&#x0?26;|&(amp;)?)/mu", "$1", $content );
3579 }
3580
3581 function nitropack_handle_request( $servedFrom = "unknown" ) {
3582 global $np_integrationSetupEvent;
3583
3584 if ( isset( $_GET["ignorenitro"] ) ) {
3585 unset( $_GET["ignorenitro"] );
3586 }
3587
3588 if ( defined( "NITROPACK_STRIP_IGNORENITRO" ) && NITROPACK_STRIP_IGNORENITRO && $_SERVER['REQUEST_URI'] != '' ) {
3589 $_SERVER['REQUEST_URI'] = nitropack_removeCacheBustParam( $_SERVER['REQUEST_URI'] );
3590 }
3591
3592 nitropack_header( 'Cache-Control: no-cache' );
3593 do_action( "nitropack_early_cache_headers" ); // Overrides the Cache-Control header on supported platforms
3594 $isManageWpRequest = ! empty( $_GET["mwprid"] );
3595 $isWpCli = nitropack_is_wp_cli();
3596
3597 if ( file_exists( NITROPACK_CONFIG_FILE ) && ! empty( $_SERVER["HTTP_HOST"] ) && ! empty( $_SERVER["REQUEST_URI"] ) && ! $isManageWpRequest && ! $isWpCli ) {
3598 try {
3599 $siteConfig = nitropack_get_site_config();
3600 if ( $siteConfig && null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
3601 if ( is_valid_nitropack_webhook() ) {
3602 nitropack_handle_webhook();
3603 } else if ( is_valid_nitropack_beacon() ) {
3604 nitropack_handle_beacon();
3605 } else if ( is_valid_nitropack_heartbeat() ) {
3606 nitropack_handle_heartbeat();
3607 } else {
3608 $GLOBALS["NitroPack.instance"] = $nitro;
3609
3610 if ( nitropack_passes_cookie_requirements() || ( nitropack_is_ajax() && ! empty( $_COOKIE["nitroCachedPage"] ) ) ) {
3611 // Check whether the current URL is cacheable
3612 // If this is an AJAX request, check whether the referer is cachable - this is needed for cases when NitroPack's "Enabled URLs" option is being used to whitelist certain URLs.
3613 // If we are not checking the referer, the AJAX requests on these pages can fail.
3614 $urlToCheck = nitropack_is_ajax() && ! empty( $_SERVER["HTTP_REFERER"] ) ? $_SERVER["HTTP_REFERER"] : $nitro->getUrl();
3615 if ( $nitro->isAllowedUrl( $urlToCheck ) ) {
3616 add_filter( 'nonce_life', 'nitropack_extend_nonce_life' );
3617 }
3618 }
3619
3620 if ( nitropack_passes_cookie_requirements() && apply_filters( "nitropack_can_serve_cache", true ) ) {
3621 if ( $nitro->isCacheAllowed() ) {
3622 if ( ! nitropack_is_ajax() ) {
3623 do_action( "nitropack_cacheable_cache_headers" );
3624 }
3625
3626 if ( ! empty( $siteConfig["compression"] ) ) {
3627 $nitro->enableCompression();
3628 }
3629
3630 if ( $nitro->hasLocalCache() ) {
3631 // TODO: Make this work so we can provide the reverse proxies with this information $remainingTtl = $nitr->pageCache->getRemainingTtl();
3632 do_action( "nitropack_cachehit_cache_headers" ); // TODO: Pass the remaining TTL here
3633 $cacheControlOverride = defined( "NITROPACK_CACHE_CONTROL_OVERRIDE" ) ? NITROPACK_CACHE_CONTROL_OVERRIDE : NULL;
3634 if ( $cacheControlOverride ) {
3635 nitropack_header( 'Cache-Control: ' . $cacheControlOverride );
3636 }
3637
3638 nitropack_header( 'X-Nitro-Cache: HIT' );
3639 nitropack_header( 'X-Nitro-Cache-From: ' . $servedFrom );
3640 $cjHandler = new \NitroPack\SDK\Utils\CjHandler( $nitro );
3641 $cjHandler->handleQueryParams();
3642 $nitro->pageCache->readfile();
3643 exit;
3644 } else {
3645 // We need the following if..else block to handle bot requests which will not be firing our beacon
3646 if ( nitropack_is_warmup_request() ) {
3647 if ( ! empty( $_SERVER["HTTP_ACCEPT_LANGUAGE"] ) ) {
3648 add_action( "init", function () use ($nitro) {
3649 $nitro->hasRemoteCache( "default" ); // Only ping the API letting our service know that this page must be cached.
3650 exit;
3651 }, 9999 );
3652 return; // We need to wait for a language plugin (if present) to redirect
3653 } else {
3654 $nitro->hasRemoteCache( "default" ); // Only ping the API letting our service know that this page must be cached.
3655 exit; // No need to continue handling this request. The response is not important.
3656 }
3657 } else if ( nitropack_is_lighthouse_request() || nitropack_is_gtmetrix_request() || nitropack_is_pingdom_request() ) {
3658 $nitro->hasRemoteCache( "default" ); // Ping the API letting our service know that this page must be cached.
3659 }
3660
3661 $nitro->pageCache->useInvalidated( true );
3662 if ( $nitro->hasLocalCache() ) {
3663 nitropack_header( 'X-Nitro-Cache: STALE' );
3664 nitropack_header( 'X-Nitro-Cache-From: ' . $servedFrom );
3665 $cjHandler = new \NitroPack\SDK\Utils\CjHandler( $nitro );
3666 $cjHandler->handleQueryParams();
3667 $nitro->pageCache->readfile();
3668 exit;
3669 } else {
3670 $nitro->pageCache->useInvalidated( false );
3671 }
3672 }
3673 }
3674 }
3675 }
3676 }
3677 } catch (\Exception $e) {
3678 // Do nothing, cache serving will be handled by nitropack_init
3679 }
3680 }
3681 }
3682
3683 function nitropack_is_dropin_cache_allowed() {
3684 $siteConfig = nitropack_get_site_config();
3685 return $siteConfig && empty( $siteConfig["isEzoicActive"] );
3686 }
3687
3688
3689
3690 function nitropack_admin_bar_menu( $wp_admin_bar ) {
3691 if ( nitropack_is_amp_page() )
3692 return;
3693 $notifications = get_nitropack()->Notifications;
3694
3695 $nitropackPluginNotices = $notifications->nitropack_plugin_notices();
3696 $numberOfPluginErrors = 0;
3697 $numberOfPluginWarnings = 0;
3698 foreach ( array( "warning", "error" ) as $type ) {
3699 foreach ( $nitropackPluginNotices[ $type ] as $notice ) {
3700 if ( ! $notifications->nitropack_is_dismissed_notice( nitropack_get_notice_id( $notice['msg'] ) ) ) {
3701 switch ( $type ) {
3702 case "error":
3703 $numberOfPluginErrors++;
3704 break;
3705 case "warning":
3706 $numberOfPluginWarnings++;
3707 break;
3708 }
3709 }
3710
3711 }
3712 }
3713 /* Notifications from the app */
3714 $notificationCount = 0;
3715 $app_notifications = NitroPack\WordPress\Notifications\AppNotifications::getInstance();
3716 foreach ( $app_notifications->get( 'system' ) as $notification ) {
3717 if ( ! $notifications->nitropack_is_dismissed_notice( $notification["id"] ) ) {
3718 $notificationCount++;
3719 }
3720 }
3721
3722 $numberOfPluginIssues = $numberOfPluginErrors + $numberOfPluginWarnings;
3723
3724 if ( $numberOfPluginErrors > 0 ) {
3725 $pluginStatus = 'error';
3726 } else if ( $numberOfPluginWarnings > 0 ) {
3727 $pluginStatus = 'warning';
3728 } else {
3729 $pluginStatus = 'ok';
3730 }
3731
3732
3733 if ( ! get_nitropack()->isConnected() ) {
3734 $node = array(
3735 'id' => 'nitropack-top-menu',
3736 'title' => '&nbsp;&nbsp;<i style="" class="circle nitro nitro-status nitro-status-error" aria-hidden="true"></i>&nbsp;&nbsp;NitroPack is disconnected',
3737 'href' => admin_url( 'admin.php?page=nitropack' ),
3738 'meta' => array(
3739 'class' => 'nitropack-menu'
3740 )
3741 );
3742
3743 $wp_admin_bar->add_node(
3744 array(
3745 'parent' => 'nitropack-top-menu',
3746 'id' => 'optimizations-plugin-status',
3747 'title' => __( 'Connect NitroPack&nbsp;&nbsp;', 'nitropack' ),
3748 'href' => admin_url( 'admin.php?page=nitropack' ),
3749 'meta' => array(
3750 'class' => 'nitropack-plugin-status',
3751 )
3752 )
3753 );
3754 } else {
3755 $title = '&nbsp;&nbsp;<i style="" class="circle nitro nitro-status nitro-status-' . $pluginStatus . '" aria-hidden="true"></i>&nbsp;&nbsp;NitroPack';
3756 if ( $pluginStatus != "ok" ) {
3757 $title .= ' <span class="circle-with-text nitro-color-issues" id="nitro-total-issues-count">' . ( $numberOfPluginIssues + $notificationCount ) . '</span>';
3758 } else if ( $notificationCount > 0 ) {
3759 $title .= ' <span class="circle-with-text nitro-color-notification" id="nitro-total-issues-count">' . $notificationCount . '</span>';
3760 }
3761 $node = array(
3762 'id' => 'nitropack-top-menu',
3763 'title' => $title,
3764 'href' => admin_url( 'admin.php?page=nitropack' ),
3765 'meta' => array(
3766 'class' => 'nitropack-menu'
3767 )
3768 );
3769
3770 $settings_extra = '';
3771 if ( $notificationCount ) {
3772 $settings_extra .= ' <span class="circle-with-text nitro-color-notification" id="nitro-notification-issues-count">' . $notificationCount . '</span>';
3773 }
3774 $wp_admin_bar->add_node( array(
3775 'id' => 'nitropack-top-menu-settings',
3776 'parent' => 'nitropack-top-menu',
3777 'title' => __( 'Settings', 'nitropack' ) . ' ' . $settings_extra . '',
3778 'href' => admin_url( 'admin.php?page=nitropack' ),
3779 'meta' => array(
3780 'class' => 'nitropack-settings'
3781 )
3782
3783 ) );
3784
3785 $wp_admin_bar->add_node(
3786 array(
3787 'id' => 'nitropack-top-menu-purge-entire-cache',
3788 'parent' => 'nitropack-top-menu',
3789 'title' => __( 'Purge Entire Cache', 'nitropack' ),
3790 'href' => '#',
3791 'meta' => array(
3792 'class' => 'nitropack-purge-cache-entire-site',
3793 ),
3794 )
3795 );
3796
3797 if ( ! is_admin() ) { // menu otions available when browsing front-end pages
3798
3799 $wp_admin_bar->add_node(
3800 array(
3801 'parent' => 'nitropack-top-menu',
3802 'id' => 'optimizations-purge-cache',
3803 'title' => __( 'Purge Current Page', 'nitropack' ),
3804 'href' => "#",
3805 'meta' => array(
3806 'class' => 'nitropack-purge-cache',
3807 )
3808 )
3809 );
3810
3811 $wp_admin_bar->add_node(
3812 array(
3813 'parent' => 'nitropack-top-menu',
3814 'id' => 'optimizations-invalidate-cache',
3815 'title' => __( 'Invalidate Current Page', 'nitropack' ),
3816 'href' => "#",
3817 'meta' => array(
3818 'class' => 'nitropack-invalidate-cache',
3819 )
3820 )
3821 );
3822 }
3823
3824 if ( $pluginStatus != "ok" ) {
3825 $wp_admin_bar->add_node(
3826 array(
3827 'parent' => 'nitropack-top-menu',
3828 'id' => 'optimizations-plugin-status',
3829 'title' => 'Issues <span class="circle-with-text nitro-color-issues">' . $numberOfPluginIssues . '</span>',
3830 'href' => admin_url( 'admin.php?page=nitropack' ),
3831 'meta' => array(
3832 'class' => 'nitropack-plugin-status',
3833 )
3834 )
3835 );
3836 }
3837 }
3838
3839 $wp_admin_bar->add_node( $node );
3840 }
3841
3842 function nitropack_admin_bar_script( $hook ) {
3843 if ( ! nitropack_is_amp_page() ) {
3844 wp_enqueue_script( 'nitropack_admin_bar_menu_script', plugin_dir_url( __FILE__ ) . 'view/javascript/admin_bar_menu.js?np_v=' . NITROPACK_VERSION, [ 'jquery' ], false, true );
3845 wp_localize_script( 'nitropack_admin_bar_menu_script', 'frontendajax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nitroNonce' => wp_create_nonce( NITROPACK_NONCE ), 'nitro_plugin_url' => plugin_dir_url( __FILE__ ) ) );
3846 }
3847 }
3848
3849
3850 function enqueue_nitropack_admin_bar_menu_stylesheet() {
3851 if ( ! nitropack_is_amp_page() ) {
3852 wp_enqueue_style( 'nitropack_admin_bar_menu_stylesheet', plugin_dir_url( __FILE__ ) . 'view/stylesheet/admin_bar_menu.min.css?np_v=' . NITROPACK_VERSION );
3853 }
3854 }
3855
3856 function nitropack_cookiepath() {
3857 $siteConfig = nitropack_get_site_config();
3858 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
3859 $url = new \NitroPack\Url\Url( $homeUrl );
3860 return $url ? $url->getPath() : "/";
3861 }
3862
3863 function nitropack_setcookie( $name, $value, $expires = NULL, $options = [] ) {
3864 if ( headers_sent() )
3865 return;
3866 $cookie_options = '';
3867 $cookie_path = nitropack_cookiepath();
3868
3869 if ( $expires && is_numeric( $expires ) ) {
3870 $options["Expires"] = date( "D, d M Y H:i:s", (int) $expires ) . ' GMT';
3871 }
3872
3873 if ( empty( $options["SameSite"] ) ) {
3874 $options["SameSite"] = "Lax";
3875 }
3876
3877 foreach ( $options as $optName => $optValue ) {
3878 $cookie_options .= "$optName=$optValue; ";
3879 }
3880 nitropack_header( "set-cookie: $name=$value; Path=$cookie_path; " . $cookie_options, false );
3881 }
3882
3883 function nitropack_header( $header, $replace = true, $response_code = 0 ) {
3884 if ( ! nitropack_is_wp_cron() && ! nitropack_is_wp_cli() ) {
3885 header( $header, $replace, $response_code );
3886 }
3887 }
3888
3889
3890 function nitropack_upgrade_handler( $entity ) {
3891 $np = 'nitropack/main.php';
3892 $trigger = $entity;
3893 if ( $entity instanceof Plugin_Upgrader ) {
3894 $trigger = $entity->plugin_info();
3895 if ( ! is_plugin_active( $trigger ) ) {
3896 return;
3897 }
3898 }
3899
3900 if ( $entity instanceof Theme_Upgrader ) {
3901 if ( $entity->theme_info()->Name === wp_get_theme()->Name ) {
3902 nitropack_theme_handler( 'Theme updated' );
3903 }
3904 return;
3905 }
3906
3907 if ( $trigger !== $np ) {
3908 $cookie_expires = date( "D, d M Y H:i:s", time() + 600 ) . ' GMT';
3909 nitropack_setcookie( 'nitropack_apwarning', "1", time() + 600 );
3910 }
3911 }
3912 /**
3913 * Caches some options in the config so that we can access them before get_option() is defined
3914 * which is in advanced_cache.php, functions.php and Integrations
3915 */
3916 function nitropack_updated_option( $option, $oldValue, $value ) {
3917 $neededOptions = \NitroPack\WordPress\NitroPack::$optionsToCache;
3918 if ( ! in_array( $option, $neededOptions ) )
3919 return;
3920
3921 $np = get_nitropack();
3922 $siteConfig = $np->Config->get();
3923
3924 if ( function_exists( 'get_home_url' ) ) {
3925 $configKey = \NitroPack\WordPress\NitroPack::getConfigKey();
3926 $siteConfig[ $configKey ]['options_cache'][ $option ] = $value;
3927 $np->Config->set( $siteConfig );
3928 }
3929 }
3930
3931 function nitropack_is_late_integration_init_required() {
3932 return \NitroPack\Integration\Plugin\NginxHelper::isActive() || \NitroPack\Integration\Plugin\Cloudflare::isApoActive();
3933 }
3934
3935 function nitropack_get_notice_id( $message ) {
3936 return md5( $message );
3937 }
3938
3939 function nitropack_active_sitemap_plugins() {
3940 return
3941 NitroPack\Integration\Plugin\YoastSEO::isActive() ||
3942 NitroPack\Integration\Plugin\JetPackNP::isActive() ||
3943 NitroPack\Integration\Plugin\SquirrlySEO::isActive() ||
3944 NitroPack\Integration\Plugin\RankMathNP::isActive();
3945 }
3946
3947 function nitropack_get_site_maps() {
3948 $sitemapUrls['YoastSEO'] = NitroPack\Integration\Plugin\YoastSEO::getSitemapURL();
3949 $sitemapUrls['JetPack'] = NitroPack\Integration\Plugin\JetPackNP::getSitemapURL();
3950 $sitemapUrls['SquirrlySEO'] = NitroPack\Integration\Plugin\SquirrlySEO::getSitemapURL();
3951 $sitemapUrls['RankMath'] = NitroPack\Integration\Plugin\RankMathNP::getSitemapURL();
3952
3953 return $sitemapUrls;
3954 }
3955
3956 function get_default_sitemap() {
3957
3958 $defaultSiteMap = NitroPack\Integration\Plugin\WPCacheHelper::getSitemapURL();
3959 if ( $defaultSiteMap ) {
3960 set_sitemap_indication_msg( 'WordPress', $defaultSiteMap );
3961 return $defaultSiteMap;
3962 }
3963
3964 return false;
3965 }
3966
3967 function evaluate_warmup_sitemap( $sitemapUrls ) {
3968
3969 $sitemapProviders = array(
3970 'YoastSEO' => 'Yoast!',
3971 'SquirrlySEO' => 'Squirrly SEO',
3972 'RankMath' => 'Rank Math',
3973 'JetPack' => 'Jetpack',
3974 );
3975
3976 foreach ( $sitemapProviders as $provider => $name ) {
3977 if ( isset( $sitemapUrls[ $provider ] ) && $sitemapUrls[ $provider ] ) {
3978 set_sitemap_indication_msg( $name, $sitemapUrls[ $provider ] );
3979 return $sitemapUrls[ $provider ];
3980 }
3981 }
3982
3983 return get_default_sitemap();
3984 }
3985
3986 function set_sitemap_indication_msg( $pluginName, $sitemapURL ) {
3987 $sitemapURI = explode( "/", parse_url( $sitemapURL, PHP_URL_PATH ) );
3988 $msg = $sitemapURI[1] . ' used by ' . $pluginName;
3989 update_option( 'np_warmup_sitemap', $msg );
3990 }
3991
3992
3993
3994 function get_date_midpoint( $endDate ) {
3995 return ( time() + strtotime( $endDate ) ) / 2;
3996 }
3997
3998 function nitropack_ignore_dismissed_notifications( $notifications, $type ) {
3999 foreach ( $notifications as $key => $notification ) {
4000 $display_time = get_transient( $notification['id'] );
4001 if ( $display_time && time() < $display_time ) {
4002 unset( $notifications[ $key ] );
4003 }
4004 }
4005
4006 return $notifications;
4007 }
4008 function initVariationCookies( $customVariationCookies ) {
4009 $api = get_nitropack_sdk()->getApi();
4010 try {
4011 $variationCookies = $api->getVariationCookies();
4012 foreach ( $variationCookies as $cookie ) {
4013 $index = array_search( $cookie["name"], $customVariationCookies );
4014 if ( $index !== false ) {
4015 array_splice( $customVariationCookies, $index, 1 );
4016 }
4017 }
4018
4019 foreach ( $customVariationCookies as $cookieName ) {
4020 $api->setVariationCookie( $cookieName );
4021 }
4022 } catch (\Exception $e) {
4023 // what to do here? possible reason for exception is the API not responding
4024 return false;
4025 }
4026 }
4027
4028 function removeVariationCookies( $cookiesToRemove ) {
4029 $api = get_nitropack_sdk()->getApi();
4030 try {
4031 $variationCookies = $api->getVariationCookies();
4032 foreach ( $variationCookies as $cookie ) {
4033 if ( in_array( $cookie["name"], $cookiesToRemove ) ) {
4034 $api->unsetVariationCookie( $cookie["name"] );
4035 }
4036 }
4037 } catch (\Exception $e) {
4038 // what to do here? possible reason for exception is the API not responding
4039 return false;
4040 }
4041 }
4042
4043 function getNewCookie( $name ) {
4044 $cookies = getNewCookies();
4045 return ! empty( $cookies[ $name ] ) ? $cookies[ $name ] : null;
4046 }
4047
4048 /**
4049 * Returns an array of newly set cookies (not in $_COOKIE), that will be sent along with the headers of the current response.
4050 */
4051 function getNewCookies() {
4052 $cookies = [];
4053 $headers = headers_list();
4054 foreach ( $headers as $header ) {
4055 if ( strpos( $header, 'Set-Cookie: ' ) === 0 ) {
4056 $value = str_replace( '&', urlencode( '&' ), substr( $header, 12 ) );
4057 parse_str( current( explode( ';', $value ) ), $pair );
4058 $cookies = array_merge_recursive( $cookies, $pair );
4059 }
4060 }
4061
4062 return array_filter( array_map( function ($val) {
4063 if ( is_array( $val ) ) {
4064 $lastEl = end( $val );
4065 if ( is_array( $lastEl ) ) {
4066 return NULL;
4067 }
4068 return $lastEl;
4069 }
4070
4071 return $val;
4072 }, $cookies ) );
4073 }
4074
4075 /**
4076 * Purge entire cache when permalink structure is changed.
4077 *
4078 * @param string $old_permalink_structure The previous permalink structure.
4079 * @param string $permalink_structure The new permalink structure.
4080 *
4081 * @return void
4082 */
4083 function nitropack_permalink_structure_changed_handler( $old_permalink_structure, $permalink_structure ) {
4084
4085 if ( $old_permalink_structure != $permalink_structure && get_option( "nitropack-autoCachePurge", 1 ) ) {
4086 $msg = 'The permalink structure is changed. Purging the cache for the home page.';
4087 $url = get_home_url();
4088
4089 try {
4090 nitropack_sdk_purge( $url, NULL, $msg ); // purge cache for the home page
4091 } catch (\Exception $e) {
4092 }
4093
4094 // run warmup
4095 if ( null !== $nitro = get_nitropack_sdk() ) {
4096 try {
4097 $nitro->getApi()->runWarmup();
4098 } catch (\Exception $e) {
4099 }
4100 }
4101 }
4102 }
4103
4104 add_action( 'permalink_structure_changed', 'nitropack_permalink_structure_changed_handler', 10, 2 );
4105
4106 /**
4107 * Purge entire cache when front page is changed.
4108 *
4109 * @param array $old_value An array of previous settings values.
4110 * @param array $value An array of submitted settings values.
4111 *
4112 * @return void
4113 */
4114 function nitropack_frontpage_changed_handler( $old_value, $value ) {
4115
4116 if ( $old_value !== $value ) {
4117 $msg = 'The front page is changed';
4118 $url = get_home_url();
4119
4120 try {
4121 nitropack_sdk_purge( $url, NULL, $msg ); // purge entire cache
4122 } catch (\Exception $e) {
4123 }
4124 }
4125 }
4126
4127 add_action( 'update_option_show_on_front', 'nitropack_frontpage_changed_handler', 10, 2 );
4128 add_action( 'update_option_page_on_front', 'nitropack_frontpage_changed_handler', 10, 2 );
4129 add_action( 'update_option_page_for_posts', 'nitropack_frontpage_changed_handler', 10, 2 );
4130
4131 // Init integration action handlers
4132 $modHandler = NitroPack\ModuleHandler::getInstance();
4133 $modHandler->init();
4134