PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / trunk
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization vtrunk
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / classes / WordPress / Notifications / Notifications.php
nitropack / classes / WordPress / Notifications Last commit date
AppNotifications.php 4 months ago Notifications.php 4 days ago
Notifications.php
799 lines
1 <?php
2 namespace NitroPack\WordPress\Notifications;
3
4 use NitroPack\WordPress\Settings\TestMode;
5 use NitroPack\HttpClient\HttpClient;
6 use Nitropack\WordPress\NitroPack;
7 use Nitropack\WordPress\AdvancedCache\AdvancedCache;
8 /*
9 * Class Notifications
10 *
11 * This class handles the notifications for the NitroPack plugin in WordPress.
12 *
13 * @package NitroPack\WordPress\Notifications
14 */
15 class Notifications {
16 private static $instance = NULL;
17 public function __construct() {
18
19 add_action( 'admin_init', [ $this, 'move_existing_notices' ] );
20 add_action( 'admin_notices', [ $this, 'nitropack_admin_notices' ] );
21 /* Using 'init' because it fixes issue when get_home_url() in updateCurrentBlogConfig() is not found in multisites */
22 add_action( 'init', function () {
23 add_action( 'plugins_loaded', [ $this, 'nitropack_plugin_notices' ] );
24 } );
25 //ajax
26 add_action( 'wp_ajax_nitropack_safemode_notification', [ $this, 'nitropack_safemode_notification' ] );
27 add_action( 'wp_ajax_nitropack_dismiss_permanently_notification', [ $this, 'nitropack_dismiss_permanently_notification' ] );
28 add_action( 'wp_ajax_nitropack_dismiss_notification_by_transient', [ $this, 'nitropack_dismiss_notification_by_transient' ] );
29 add_action( 'wp_ajax_nitropack_conflict_plugin_deactivate', [ $this, 'nitropack_conflict_plugin_deactivate' ] );
30
31 }
32 public static function getInstance() {
33 if ( ! self::$instance ) {
34 self::$instance = new Notifications();
35 }
36
37 return self::$instance;
38 }
39
40 /**
41 * Displays general admin notices for the NitroPack plugin in WordPress dashboard.
42 *
43 * @return void
44 */
45 public function nitropack_admin_notices() {
46 $components = new \NitroPack\WordPress\Settings\Components;
47 if ( defined( 'NITROPACK_DATA_DIR_WARNING' ) ) {
48 $components->render_notification( NITROPACK_DATA_DIR_WARNING, 'warning', 'Unable to initialize cache dir' );
49 }
50
51 if ( defined( 'NITROPACK_PLUGIN_DATA_DIR_WARNING' ) ) {
52 $components->render_notification( NITROPACK_PLUGIN_DATA_DIR_WARNING, 'warning', 'Unable to initialize plugin data dir' );
53 }
54
55 if ( ! empty( $_COOKIE["nitropack_after_activate_notice"] ) && ! get_nitropack()->isConnected() ) {
56 $components->render_notification( "Please complete the setup process to activate optimizations.",
57 'promo',
58 esc_html__( 'Connect your website to enable NitroPack\'s optimizations', 'nitropack' ),
59 '<a href="' . admin_url( 'admin.php?page=nitropack' ) . '" class="btn btn-primary">' . esc_html__( 'Connect your website', 'nitropack' ) . '</a>' );
60 }
61
62 $this->render_app_notifications();
63 $this->nitropack_print_hosting_notice();
64 $this->nitropack_print_woocommerce_notice();
65 }
66 /**
67 * Display NitroPack plugin notices in the WordPress admin area.
68 *
69 * This function is responsible for showing various notifications related to the NitroPack plugin.
70 *
71 * @return null|array
72 */
73 public function nitropack_plugin_notices() {
74 if ( ! $this->pass_notification_capabilities() )
75 return;
76
77 static $npPluginNotices = NULL;
78
79 if ( $npPluginNotices !== NULL ) {
80 return $npPluginNotices;
81 }
82
83 $errors = [];
84 $warnings = [];
85 $infos = [];
86
87 /* Sets a warning if there are any conflicting plugins - mostly caching plugins. */
88 $conflictingPlugins = \NitroPack\WordPress\ConflictingPlugins::getInstance();
89 $conflictingPlugins_list = $conflictingPlugins->nitropack_get_conflicting_plugins();
90
91 if ( $conflictingPlugins_list ) {
92
93 foreach ( $conflictingPlugins_list as $clashingPlugin ) {
94 $warnings[] = array(
95 'title' => sprintf( "%s is active and may conflict with NitroPack", $clashingPlugin['name'] ),
96 'msg' => esc_html__( "Some of its features overlap with NitroPack's optimizations which could lead to issues. We recommend disabling it to avoid potential conflicts.", 'nitropack' ),
97 'actions' => '<a class="btn btn-secondary modal-plugin-deactivate" data-plugin-path="' . $clashingPlugin['plugin'] . '" data-plugin-name="' . $clashingPlugin['name'] . '" title="Disable ' . $clashingPlugin['name'] . ' ">' . sprintf( "Deactivate %s", $clashingPlugin['name'] ) . '</a>',
98 'classes' => [ 'conflicting-plugins plugin-' . sanitize_title( $clashingPlugin['name'] ) ],
99 );
100 NitroPack::getInstance()->getLogger()->notice( sprintf( "Conflicting plugin detected: %s", $clashingPlugin['name'] ) );
101 }
102
103 }
104 /* Add residual cache notices if found */
105 $residualCachePlugins = \NitroPack\Integration\Plugin\RC::detectThirdPartyCaches();
106 foreach ( $residualCachePlugins as $rcpName ) {
107 $warnings[] = array(
108 'title' => esc_html__( "Residual cache files", 'nitropack' ),
109 /* translators: %s: Name of the plugin that left residual cache files */
110 'msg' => sprintf( esc_html__( 'We found residual cache files from %s. These files can interfere with the caching process and must be deleted.', 'nitropack' ), $rcpName, $rcpName ),
111 'actions' => '<a class="btn btn-warning" nitropack-rc-data="' . $rcpName . '">' . esc_html__( 'Delete now', 'nitropack' ) . '</a>',
112 );
113 NitroPack::getInstance()->getLogger()->notice( sprintf( "Residual cache files detected from plugin: %s", $rcpName ) );
114 }
115 /* Sets a warning if there is any activity in the plugins such as new activations, updates, or deletions. */
116 if ( isset( $_COOKIE['nitropack_apwarning'] ) ) {
117 $cookie_path = nitropack_cookiepath();
118 $warnings[] = array(
119 'title' => esc_html__( "Plugins activity", 'nitropack' ),
120 'msg' => esc_html__( 'It seems plugins have been activated, deactivated or updated. It is recommended that you purge the cache to reflect the latest changes.', 'nitropack' ),
121 'actions' => "<a class=\"btn btn-secondary\" href=\"javascript:void(0);\" id=\"np-onstate-cache-purge\" onclick=\"document.cookie = 'nitropack_apwarning=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=$cookie_path';window.location.reload();\">" . esc_html__( 'Dismiss', 'nitropack' ) . "</a>",
122 'classes' => [ 'plugins-state' ],
123 );
124 NitroPack::getInstance()->getLogger()->notice( "Plugin activity detected: new activations, updates, or deletions." );
125 }
126
127 /* Sets a warning if the Test Mode is enabled. */
128 if ( TestMode::getInstance()->is_test_mode_enabled() ) {
129 $safeModeMessage = __( 'Visitors are accessing your unoptimized pages. Make sure to disable it once you are done testing.', 'nitropack' );
130 if ( get_nitropack()->getDistribution() == "oneclick" ) {
131 $safeModeMessage = apply_filters( "nitropack_oneclick_safemode_message", $safeModeMessage );
132 }
133
134 $warnings[] = array(
135 'title' => esc_html__( "Test Mode Enabled", 'nitropack' ),
136 'msg' => $safeModeMessage,
137 'classes' => [ 'test-mode' ],
138 );
139 }
140
141 $nitropackIsConnected = get_nitropack()->isConnected();
142 $advanced_cache = new AdvancedCache();
143 if ( $nitropackIsConnected ) {
144 if ( nitropack_is_advanced_cache_allowed() ) {
145 $notification_title = esc_html__( "File advanced-cache.php cannot be created", 'nitropack' );
146 $notification_class = [ 'advanced-cache' ];
147
148 if ( ! $advanced_cache->has_advanced_cache() ) {
149
150 $advancedCacheFile = nitropack_trailingslashit( WP_CONTENT_DIR ) . 'advanced-cache.php';
151 if ( ! file_exists( $advancedCacheFile ) || strpos( file_get_contents( $advancedCacheFile ), "NITROPACK_ADVANCED_CACHE" ) === false ) { // For some reason we get the notice right after connecting (even though the advanced-cache file is already in place). This check works around this issue :(
152
153 if ( $advanced_cache->install_advanced_cache() ) {
154 if ( ! \NitroPack\Integration\Hosting\WPEngine::detect() ) { // The advanced-cache.php file in WP Engine is reset fairly often and we don't want to show the notice every time. This is an info we can skip in this case.
155
156 /* Sets an info notifications if the advanced-cache.php file was re-installed. */
157 $infos[] = array(
158 'title' => esc_html__( 'File advanced-cache.php re-installed', 'nitropack' ),
159 'msg' => esc_html__( 'The file /wp-content/advanced-cache.php was either missing or not the one generated by NitroPack. NitroPack re-installed its version of the file, so it can function properly. Possibly, there is another active page caching plugin in your system. For correct operation, please deactivate any other page caching plugins.', 'nitropack' ),
160 'actions' => '<a href="' . admin_url() . 'plugins.php" target="_blank" class="btn btn-secondary">' . esc_html__( 'Plugins page', 'nitropack' ) . '</a>',
161 'classes' => $notification_class,
162 );
163 NitroPack::getInstance()->getLogger()->info( "File advanced-cache.php re-installed." );
164 }
165 } else {
166 if ( ! $conflictingPlugins->nitropack_is_conflicting_plugin_active() ) {
167
168 /* Sets an error notifications for not being able to create the advanced-cache.php file due to conflicting caching plugins */
169
170 $errors[] = array(
171 'title' => $notification_title,
172 'msg' => __( 'Please make sure that the /wp-content/ directory is writable and refresh this page.', 'nitropack' ),
173 'classes' => $notification_class,
174 );
175 NitroPack::getInstance()->getLogger()->error( "Please make sure that the /wp-content/ directory is writable." );
176 }
177 }
178 }
179 } else {
180 if ( ! defined( "NITROPACK_ADVANCED_CACHE_VERSION" ) || NITROPACK_VERSION != NITROPACK_ADVANCED_CACHE_VERSION ) {
181 if ( ! $advanced_cache->install_advanced_cache() ) {
182 if ( $conflictingPlugins->nitropack_is_conflicting_plugin_active() ) {
183 $errors[] = array(
184 'title' => $notification_title,
185 'msg' => esc_html__( 'The file /wp-content/advanced-cache.php cannot be created because a conflicting plugin is active. Please make sure to disable all conflicting plugins.', 'nitropack' ),
186 'actions' => '<a href="' . admin_url() . 'plugins.php" target="_blank" class="btn btn-primary">Plugins page</a>',
187 'classes' => $notification_class,
188 );
189 NitroPack::getInstance()->getLogger()->error( "The file /wp-content/advanced-cache.php cannot be created because a conflicting plugin is active." );
190 } else {
191 $errors[] = array(
192 'title' => $notification_title,
193 'msg' => esc_html__( 'The file /wp-content/advanced-cache.php cannot be created. Please make sure that the /wp-content/ directory is writable and refresh this page.', 'nitropack' ),
194 'classes' => $notification_class,
195 );
196 NitroPack::getInstance()->getLogger()->error( "The file /wp-content/advanced-cache.php cannot be created. Please make sure that the /wp-content/ directory is writable." );
197 }
198 }
199 }
200 }
201 } else {
202 if ( $advanced_cache->has_advanced_cache() ) {
203 $advanced_cache->uninstall_advanced_cache();
204 }
205 }
206
207 if ( ( ! defined( "WP_CACHE" ) || ! WP_CACHE ) ) {
208 $notification_class = [ 'wp-cache' ];
209 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) { // Flywheel: This is configured throught the FW control panel
210 $warnings[] = array(
211 'title' => esc_html__( "Constant WP_CACHE not enabled", 'nitropack' ),
212 'msg' => esc_html__( "Please go to your FlyWheel control panel and enable this setting.", 'nitropack' ),
213 'actions' => '<a href="https://getflywheel.com/wordpress-support/how-to-enable-wp_cache/" target="_blank" class="btn btn-primary">View more</a>',
214 'classes' => $notification_class,
215 );
216 NitroPack::getInstance()->getLogger()->notice( "Constant WP_CACHE not enabled." );
217 } else if ( ! nitropack_set_wp_cache_const( true ) ) {
218 $errors[] = array(
219 'title' => esc_html__( 'Constant WP_CACHE cannot be set', 'nitropack' ),
220 'msg' => esc_html__( 'This can lead to slower cache delivery. Please make sure that the /wp-config.php file is writable and refresh this page.', 'nitropack' ),
221 'classes' => $notification_class,
222 );
223 NitroPack::getInstance()->getLogger()->error( "Constant WP_CACHE cannot be set." );
224 }
225 }
226
227 if ( apply_filters( 'nitropack_needs_htaccess_changes', false ) ) {
228 if ( ! nitropack_set_htaccess_rules( true ) ) {
229 $errors[] = array(
230 'title' => esc_html__( "LiteSpeed configuration needed", 'nitropack' ),
231 'msg' => esc_html__( 'NitroPack is optimizing your pages but it can\'t set up the caching rules your LiteSpeed server needs. Your site will work but it will be slower than it should be. Make .htaccess writable and reload this page to fix it.', 'nitropack' ),
232 'actions' => '<a href="https://support.nitropack.io/en/articles/14301910-how-nitropack-works-with-litespeed-servers/" target="_blank" class="btn btn-primary">How to fix this</a>',
233 'classes' => [ 'litespeed' ],
234 );
235 NitroPack::getInstance()->getLogger()->error( "LiteSpeed configuration needed." );
236 }
237 }
238
239 if ( ! get_nitropack()->dataDirExists() && ! get_nitropack()->initDataDir() ) {
240 $errors[] = array(
241 'title' => esc_html__( 'NitroPack data directory cannot be created', 'nitropack' ),
242 'msg' => esc_html__( 'Please make sure that the /wp-content/ directory is writable and refresh this page.', 'nitropack' ),
243 'classes' => [ 'np-data-dir' ],
244 );
245 NitroPack::getInstance()->getLogger()->error( "NitroPack data directory cannot be created." );
246 return [
247 'error' => $errors,
248 'warning' => $warnings,
249 'info' => $infos
250 ];
251 }
252
253 if ( ! get_nitropack()->pluginDataDirExists() && ! get_nitropack()->initPluginDataDir() ) {
254 $errors[] = array(
255 'title' => esc_html__( 'NitroPack plugin data directory cannot be created', 'nitropack' ),
256 'msg' => esc_html__( 'Please make sure that the /wp-content/ directory is writable and refresh this page.', 'nitropack' ),
257
258 'classes' => [ 'np-data-dir' ],
259 );
260 NitroPack::getInstance()->getLogger()->error( "NitroPack plugin data directory cannot be created." );
261 return [
262 'error' => $errors,
263 'warning' => $warnings,
264 'info' => $infos
265 ];
266 }
267
268 $siteConfig = nitropack_get_site_config();
269 $siteId = $siteConfig ? $siteConfig["siteId"] : NULL;
270 $siteSecret = $siteConfig ? $siteConfig["siteSecret"] : NULL;
271 $webhookToken = esc_attr( get_option( 'nitropack-webhookToken' ) );
272 $blogId = get_current_blog_id();
273 $isConfigOutdated = ! nitropack_is_config_up_to_date();
274 if ( ! get_nitropack()->Config->exists() && ! get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId ) ) {
275 $errors[] = array(
276 'title' => esc_html__( "NitroPack static config file cannot be created", 'nitropack' ),
277 'msg' => esc_html__( 'Please make sure that the /wp-content/config-nitropack/ directory is writable and refresh this page.', 'nitropack' ),
278 );
279 NitroPack::getInstance()->getLogger()->error( "NitroPack static config file cannot be created." );
280 } else if ( $isConfigOutdated ) {
281 if ( ! get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId ) ) {
282 $errors[] = array(
283 'title' => esc_html__( "NitroPack static config file cannot be updated", 'nitropack' ),
284 'msg' => esc_html__( 'Please make sure that the /wp-content/config-nitropack/ directory is writable and refresh this page.', 'nitropack' ),
285 );
286 NitroPack::getInstance()->getLogger()->error( "NitroPack static config file cannot be updated." );
287 } else {
288
289 if ( ! $siteConfig ) {
290 nitropack_event( "update" );
291 } else {
292 $prevVersion = ! empty( $siteConfig["pluginVersion"] ) ? $siteConfig["pluginVersion"] : "1.1.4 or older";
293 nitropack_event( "update", null, array( "previous_version" => $prevVersion ) );
294 if ( empty( $siteConfig["pluginVersion"] ) || version_compare( $siteConfig["pluginVersion"], "1.4", "<" ) ) {
295 $nitropack_v1_3_notice_id = 'nitropack_upgrade_to_1_3';
296 }
297 }
298 }
299
300 try {
301 nitropack_setup_webhooks( get_nitropack_sdk(), $webhookToken );
302 } catch (\NitroPack\SDK\WebhookException $e) {
303 $warnings[] = array(
304 'title' => esc_html__( "Unable to configure webhooks", 'nitropack' ),
305 'msg' => esc_html__( 'This can impact the stability of the plugin. Please disconnect and connect again in order to retry configuring the webhooks.', 'nitropack' ),
306 );
307 NitroPack::getInstance()->getLogger()->notice( "Unable to configure webhooks." );
308 }
309 } else {
310 $optionsMismatch = false;
311 if ( array_key_exists( 'options_cache', $siteConfig ) ) {
312 foreach ( \NitroPack\WordPress\NitroPack::$optionsToCache as $opt ) {
313 if ( is_array( $opt ) ) {
314 foreach ( $opt as $option => $suboption ) {
315 // Handle both nested and flat structures
316 if ( is_array( $suboption ) ) {
317 // Nested structure
318 if ( ! isset( $siteConfig['options_cache'][ $option ] ) || ! is_array( $siteConfig['options_cache'][ $option ] ) ) {
319 $optionsMismatch = true;
320 break 2;
321 }
322 foreach ( $suboption as $subkey => $subvalue ) {
323 if (
324 ! isset( $siteConfig['options_cache'][ $option ][ $subkey ] ) ||
325 $siteConfig['options_cache'][ $option ][ $subkey ] !== get_option( $option )[ $subkey ]
326 ) {
327 $optionsMismatch = true;
328 break 3;
329 }
330 }
331 } else {
332 // Flat structure within the nested loop
333 if (
334 ! isset( $siteConfig['options_cache'][ $option ] ) ||
335 $siteConfig['options_cache'][ $option ] !== get_option( $option )
336 ) {
337 $optionsMismatch = true;
338 break 2;
339 }
340 }
341 }
342 } else {
343 // Flat structure outside the nested loop
344 if (
345 ! isset( $siteConfig['options_cache'][ $opt ] ) ||
346 is_bool( $siteConfig['options_cache'][ $opt ] ) ||
347 $siteConfig['options_cache'][ $opt ] !== get_option( $opt )
348 ) {
349 $optionsMismatch = true;
350 break;
351 }
352 }
353 }
354 } else {
355 $optionsMismatch = true;
356 }
357
358 if (
359 $optionsMismatch ||
360 ( ! array_key_exists( "isEzoicActive", $siteConfig ) || $siteConfig["isEzoicActive"] !== \NitroPack\Integration\Plugin\Ezoic::isActive() ) ||
361 ( ! array_key_exists( "isLateIntegrationInitRequired", $siteConfig ) || $siteConfig["isLateIntegrationInitRequired"] !== nitropack_is_late_integration_init_required() ) ||
362 ( ! array_key_exists( "isDlmActive", $siteConfig ) || $siteConfig["isDlmActive"] !== \NitroPack\Integration\Plugin\DownloadManager::isActive() ) ||
363 ( ! array_key_exists( "isAeliaCurrencySwitcherActive", $siteConfig ) || $siteConfig["isAeliaCurrencySwitcherActive"] !== \NitroPack\Integration\Plugin\AeliaCurrencySwitcher::isActive() ) ||
364 ( ! array_key_exists( "isGeoTargetingWPActive", $siteConfig ) || $siteConfig["isGeoTargetingWPActive"] !== \NitroPack\Integration\Plugin\GeoTargetingWP::isActive() ) ||
365 ( ! array_key_exists( "isWoocommerceActive", $siteConfig ) || $siteConfig["isWoocommerceActive"] !== \NitroPack\Integration\Plugin\WooCommerce::isActive() ) ||
366 ( ! array_key_exists( "isWoocommerceCacheHandlerActive", $siteConfig ) || $siteConfig["isWoocommerceCacheHandlerActive"] !== \NitroPack\Integration\Plugin\WoocommerceCacheHandler::isActive() )
367 ) {
368 if ( ! get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId ) ) {
369 $errors[] = array(
370 'title' => esc_html__( "NitroPack static config file cannot be updated", 'nitropack' ),
371 'msg' => esc_html__( 'Please make sure that the /wp-content/config-nitropack/ directory is writable and refresh this page.', 'nitropack' ),
372 );
373 NitroPack::getInstance()->getLogger()->error( "NitroPack static config file cannot be updated." );
374 }
375 }
376
377 if ( empty( $_COOKIE["nitropack_webhook_sync"] ) || ! $siteConfig["webhookToken"] ) {
378 if ( null !== $nitro = get_nitropack_sdk() ) {
379 try {
380 if ( ! headers_sent() ) {
381 nitropack_setcookie( "nitropack_webhook_sync", "1", time() + 300 ); // Do these checks in 5 minute intervals.
382 }
383 $configWebhook = $nitro->getApi()->getWebhook( "config" );
384 if ( ! empty( $configWebhook ) ) {
385 $query = parse_url( $configWebhook, PHP_URL_QUERY );
386 if ( $query ) {
387 parse_str( $query, $webhookParams );
388 if ( empty( $webhookParams["token"] ) || $webhookParams["token"] != $webhookToken ) {
389 $warnings[] = array(
390 'title' => esc_html__( "Connection problems detected", 'nitropack' ),
391 'msg' => esc_html__( 'Most likely you have used the same API credentials to connect another website (e.g. dev or staging). Click to restore the connection to this site.', 'nitropack' ),
392 'actions' => '<a id="nitro-restore-connection-btn" class="btn btn-warning">Restore connection</a>',
393 );
394 NitroPack::getInstance()->getLogger()->notice( "Connection problems detected. Webhook token mismatch." );
395 }
396 }
397 }
398 } catch (\Exception $e) {
399 //Do nothing
400 }
401 }
402 }
403
404 if ( apply_filters( 'nitropack_should_modify_htaccess', false ) && ( empty( $_SERVER["NitroPackHtaccessVersion"] ) || NITROPACK_VERSION != $_SERVER["NitroPackHtaccessVersion"] ) ) {
405 if ( ! nitropack_set_htaccess_rules( true ) ) {
406 $errors[] = array(
407 'title' => esc_html__( "The .htaccess file cannot be modified", 'nitropack' ),
408 'msg' => esc_html__( 'Please make sure that it is writable and refresh this page.', 'nitropack' ),
409 );
410 NitroPack::getInstance()->getLogger()->error( "The .htaccess file cannot be modified." );
411 }
412 }
413 }
414 if ( isset( $nitropack_v1_3_notice_id ) ) {
415 $warnings[] = array(
416 'title' => esc_html__( "NitroPack upgraded to 1.3", 'nitropack' ),
417 'msg' => esc_html__( 'Your new version of NitroPack has a new better way of recaching updated content. However, it is incompatible with the page relationships built by your previous version. Please invalidate your cache manually one-time so that content updates start working with the updated logic.', 'nitropack' ),
418 'dismissibleId' => $nitropack_v1_3_notice_id,
419 'dismissBy' => 'option',
420 );
421 }
422
423 if ( \NitroPack\Integration\Plugin\Cloudflare::isApoActive() && ! \NitroPack\Integration\Plugin\Cloudflare::isApoCacheByDeviceTypeEnabled() ) {
424 $warnings[] = array(
425 'title' => esc_html__( "Cache By Device Type is not activate", 'nitropack' ),
426 'msg' => esc_html__( 'It seems Cache By Device Type is not activate with the Cloudflare APO. It is recommended that you enable it for a more optimized experience.', 'nitropack' ),
427 );
428 NitroPack::getInstance()->getLogger()->notice( "Cache By Device Type is not activate with the Cloudflare APO." );
429 }
430 }
431
432 $npPluginNotices = [
433 'error' => $errors,
434 'warning' => $warnings,
435 'info' => $infos
436 ];
437
438 return $npPluginNotices;
439 }
440
441 /**
442 * Display admin notices in the NitroPack -> Dashboard plugin page.
443 *
444 * This function checks if the current user has the necessary capabilities to view the notices.
445 * It renders specific notifications related to hosting information, system, compatibilities and notifications coming from the NitroPack app
446 *
447 * @return void
448 */
449 public function nitropack_display_admin_notices() {
450 if ( ! $this->pass_notification_capabilities() )
451 return;
452
453 $noticesArray = $this->nitropack_plugin_notices();
454 $components = new \NitroPack\WordPress\Settings\Components;
455 foreach ( $noticesArray as $type => $notices ) {
456 foreach ( $notices as $notice ) {
457 $components->render_notification( $notice['msg'], $type, $notice['title'], isset( $notice['actions'] ) ? $notice['actions'] : null, isset( $notice['classes'] ) ? $notice['classes'] : null, isset( $notice['dismissibleId'] ) ? $notice['dismissibleId'] : null, isset( $notice['dismissBy'] ) ? $notice['dismissBy'] : null );
458 }
459 }
460
461 //render app notifications
462 $this->render_app_notifications();
463 }
464
465 /**
466 * Render notifications coming from notifications.json file such as ones from the NitroPack app.
467 *
468 * @return void
469 */
470 public function render_app_notifications() {
471 $components = new \NitroPack\WordPress\Settings\Components();
472 $app_notifications = AppNotifications::getInstance();
473 foreach ( $app_notifications->get( 'system' ) as $notification ) {
474 $msg = $notification['message'];
475 $type = 'info';
476 $title = '';
477
478 if ( ! empty( $notification['type'] ) ) {
479 $type = $notification['type'];
480 }
481 if ( ! empty( $notification['message_details']['title'] ) ) {
482 $title = $notification['message_details']['title'];
483 }
484 if ( ! empty( $notification['message_details']['message'] ) ) {
485 $msg = $notification['message_details']['message'];
486 }
487
488 $components->render_notification( $msg, $type, $title, '', [ 'app-notification' ], $notification['id'], 'transient', $notification );
489 }
490 }
491 /**
492 * Prints a hosting notice for NitroPack.
493 *
494 * @return void
495 */
496 private function nitropack_print_hosting_notice() {
497
498 $hostingNoticeFile = nitropack_get_hosting_notice_file();
499 if ( ! get_nitropack()->isConnected() || file_exists( $hostingNoticeFile ) )
500 return;
501
502 $documentedHostingSetups = array(
503 "flywheel" => array(
504 "name" => "Flywheel",
505 "helpUrl" => "https://getflywheel.com/wordpress-support/how-to-enable-wp_cache/"
506 ),
507 "cloudways" => array(
508 "name" => "Cloudways",
509 "helpUrl" => "https://support.nitropack.io/hc/en-us/articles/360060916674-Cloudways-Hosting-Configuration-for-NitroPack"
510 )
511 );
512
513 $siteConfig = nitropack_get_site_config();
514
515 if ( $siteConfig && ! empty( $siteConfig["hosting"] ) && array_key_exists( $siteConfig["hosting"], $documentedHostingSetups ) ) {
516
517 $hostingInfo = $documentedHostingSetups[ $siteConfig["hosting"] ];
518 $showNotice = true;
519 if ( $siteConfig["hosting"] == "flywheel" && defined( "WP_CACHE" ) && WP_CACHE ) {
520 $showNotice = false;
521 }
522
523 if ( $showNotice ) {
524 $components = new \NitroPack\WordPress\Settings\Components;
525 $components->render_notification( esc_html__( "Please follow the instructions in order to make sure that everything works correctly.", 'nitropack' ), 'info',
526 /* translators: %s: Name of the hosting provider */
527 sprintf( esc_html__( 'It looks like you are hosted on %s', 'nitropack' ), $hostingInfo['name'] ),
528 '<a href="' . $hostingInfo["helpUrl"] . '" target="_blank" class="btn btn-info btn-ghost">' . esc_html__( 'Read Instructions', 'nitropack' ) . '</a>',
529 [ 'hosting-notice' ], 'hosting-' . $siteConfig["hosting"], 'option' );
530 }
531 }
532 }
533 /**
534 * Prints a WooCommerce notice for NitroPack across WordPress admin
535 * @return void
536 */
537 private function nitropack_print_woocommerce_notice() {
538 if ( get_nitropack()->isConnected() ) {
539 if ( class_exists( 'WooCommerce' ) ) {
540 $np_notices = get_option( 'nitropack-dismissed-notices', [] );
541 $woocommerce_notice = in_array( 'WooCommerce', $np_notices, true ) ? true : false;
542
543 if ( ! $woocommerce_notice ) {
544 $components = new \NitroPack\WordPress\Settings\Components;
545 $components->render_notification( __( 'Your <strong>account</strong>, <strong>cart</strong>, and <strong>checkout</strong> pages are automatically excluded from optimization.', 'nitropack' ),
546 'success',
547 esc_html__( 'WooCommerce detected', 'nitropack' ),
548 '<a class="btn btn-secondary" href="' . admin_url( 'admin.php?page=nitropack' ) . '">' . esc_html__( 'Settings', 'nitropack' ) . '</a>',
549 [ 'woocommerce-notice' ],
550 'WooCommerce', 'option' );
551 }
552 }
553 }
554 }
555
556 public function admin_bar_notices_counter() {
557 if ( ! $this->pass_notification_capabilities() )
558 return;
559
560 $notices = $this->nitropack_plugin_notices();
561
562 $numberOfPluginErrors = 0;
563 $numberOfPluginWarnings = 0;
564 $notificationCount = 0;
565 foreach ( array( "warning", "error", "info" ) as $type ) {
566
567 foreach ( $notices[ $type ] as $notice ) {
568
569
570 switch ( $type ) {
571 case "error":
572 $numberOfPluginErrors++;
573 break;
574 case "warning":
575 $numberOfPluginWarnings++;
576 break;
577 case "info":
578 $notificationCount++;
579 break;
580 }
581
582
583 }
584 }
585
586 /* Notifications from the app */
587 $app_notifications = AppNotifications::getInstance();
588 foreach ( $app_notifications->get( 'system' ) as $notification ) {
589
590 if ( ! empty( $notification['id'] ) ) {
591
592 /* Don't count if dismissed by transient and the time has passed */
593 $notice = get_transient( $notification['id'] );
594 if ( ! empty( $notice ) && ( $notice && time() < $notice ) ) {
595 continue;
596 }
597
598 if ( ! empty( $notification['type'] ) ) {
599 switch ( $notification['type'] ) {
600 case 'error':
601 $numberOfPluginErrors++;
602 break;
603 case 'warning':
604 $numberOfPluginWarnings++;
605 break;
606 case 'info':
607 $notificationCount++;
608 break;
609 }
610 } else {
611 $notificationCount++;
612 }
613 }
614 }
615
616 $numberOfPluginIssues = $numberOfPluginErrors + $numberOfPluginWarnings;
617
618 if ( $numberOfPluginErrors > 0 ) {
619 $pluginStatus = 'error';
620 } else if ( $numberOfPluginWarnings > 0 ) {
621 $pluginStatus = 'warning';
622 } else {
623 $pluginStatus = 'ok';
624 }
625 $data = [ 'issues' => $numberOfPluginIssues, 'status' => $pluginStatus, 'errors' => $numberOfPluginErrors, 'warnings' => $numberOfPluginWarnings, 'notifications' => $notificationCount ];
626
627 return $data;
628 }
629 /**
630 * Checks if the user has capabilities to manage options - administrators typically have this capability.
631 * @return void|bool
632 */
633 private function pass_notification_capabilities() {
634 if ( ! current_user_can( 'manage_options' ) )
635 return;
636 else {
637 return true;
638 }
639 }
640
641 public function test_mode_notification_html() {
642 $components = new \NitroPack\WordPress\Settings\Components;
643 $components->render_notification( 'Visitors are accessing your unoptimized pages. Make sure to disable it once you are done testing.', 'warning', 'Test Mode Enabled', false, [ 'test-mode' ] );
644 }
645 public function nitropack_safemode_notification() {
646 nitropack_verify_ajax_nonce( $_REQUEST );
647 $this->test_mode_notification_html();
648 wp_die();
649 }
650
651 /* Dismiss notification by using set_transient -> temporary dismissal with auto-expiry or by dismiss url if set by the app notification */
652 public function nitropack_dismiss_notification_by_transient() {
653 if ( ! $this->pass_notification_capabilities() ) {
654 wp_die( __( 'You do not have sufficient permissions.', 'nitropack' ) );
655 }
656
657 if ( empty( $_POST['notification_id'] ) ) {
658 wp_send_json_error( [ 'message' => __( 'Missing notification ID.', 'nitropack' ) ] );
659 }
660 nitropack_verify_ajax_nonce( $_REQUEST );
661
662 $notification_id = $_POST['notification_id'];
663 $notification_end = $_POST['notification_end'];
664 $midpoint = get_date_midpoint( $notification_end );
665 $notification_end = strtotime( $notification_end ) - time();
666
667 //Use dimiss url from the app notification if set. It will remove it from notifications.json
668 if ( ! empty( $_POST["dismiss_url"] ) ) {
669 $dismiss_url = $_POST["dismiss_url"];
670 $http_client = new HttpClient( $dismiss_url );
671 $http_client->fetch( true, "GET" );
672 $resp = $http_client->getStatusCode() == 200 ? json_decode( $http_client->getBody(), true ) : false;
673 if ( $resp['status'] ) {
674 $app_notifications = AppNotifications::getInstance();
675 $removed = $app_notifications->removeNotificationById( $notification_id );
676 if ( $removed ) {
677 nitropack_json_and_exit( array(
678 "status" => true,
679 ) );
680 } else {
681 wp_send_json_error( [ 'message' => __( 'Failed to dismiss the notification.', 'nitropack' ) ] );
682 }
683 } else {
684 wp_send_json_error( [ 'message' => __( 'Failed to dismiss the notification.', 'nitropack' ) ] );
685 }
686 } else {
687 //if there is not dismiss url, use the transient dismissal method which will remove the notification from the screen and it will not show it again until the time set in notification_end
688 $transient_status = set_transient( $notification_id, $midpoint, $notification_end );
689 nitropack_json_and_exit( array(
690 "status" => $transient_status,
691 ) );
692 }
693 }
694
695 /**
696 * Handles the dismissal of a notification permanently by updating nitropack-dismissed-notices option in the database.
697 *
698 * @return void Outputs a JSON response and terminates the script execution.
699 */
700
701 public function nitropack_dismiss_permanently_notification() {
702 if ( ! $this->pass_notification_capabilities() ) {
703 wp_send_json_error( [ 'message' => __( 'You do not have sufficient permissions.', 'nitropack' ) ] );
704 }
705
706 if ( empty( $_POST['notification_id'] ) ) {
707 wp_send_json_error( [ 'message' => __( 'Missing notification ID.', 'nitropack' ) ] );
708 }
709
710 nitropack_verify_ajax_nonce( $_REQUEST );
711
712 $notification_id = sanitize_text_field( wp_unslash( $_POST['notification_id'] ) );
713 $notices = get_option( 'nitropack-dismissed-notices', [] );
714
715 if ( ! in_array( $notification_id, $notices, true ) ) {
716 $notices[] = $notification_id;
717 update_option( 'nitropack-dismissed-notices', $notices );
718 }
719
720 wp_send_json_success();
721 }
722
723 /**
724 * Moves existing dismissed notices to the new dismissed option as an array.
725 *
726 * Notices being migrated:
727 * - `nitropack-wcNotice` (mapped to `WooCommerce`)
728 * - `nitropack-noticeOptimizeCPT` (mapped to `OptimizeCPT`)
729 *
730 * @return void
731 */
732
733 public function move_existing_notices() {
734 $existing_notices = [ 'nitropack-wcNotice' => 'WooCommerce', 'nitropack-noticeOptimizeCPT' => 'OptimizeCPT' ];
735 foreach ( $existing_notices as $notice => $new_notice ) {
736 if ( get_option( $notice ) ) {
737 $notices = get_option( 'nitropack-dismissed-notices', [] );
738 if ( ! in_array( $notice, $notices, true ) ) {
739 $notices[] = $new_notice;
740 update_option( 'nitropack-dismissed-notices', $notices );
741 }
742 delete_option( $notice );
743 }
744 }
745 }
746 /**
747 * Deactivates a conflicting plugin from NitroPack.
748 *
749 * It verifies the nonce for security and checks if the specified plugin is in the list of conflicting plugins
750 * and finally deactivates it if it is active.
751 *
752 * @return void Outputs a JSON response indicating success or failure.
753 */
754 public function nitropack_conflict_plugin_deactivate() {
755 if ( ! $this->pass_notification_capabilities() ) {
756 wp_send_json_error( [ 'message' => __( 'You do not have sufficient permissions.', 'nitropack' ) ] );
757 }
758
759 if ( empty( $_POST['plugin'] ) ) {
760 wp_send_json_error( [ 'message' => __( 'Missing plugin.', 'nitropack' ) ] );
761 }
762 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
763
764 if ( empty( $_POST['plugin_name'] ) ) {
765 wp_send_json_error( [ 'message' => __( 'Missing plugin name.', 'nitropack' ) ] );
766 }
767 $plugin_name = sanitize_text_field( wp_unslash( $_POST['plugin_name'] ) );
768
769 nitropack_verify_ajax_nonce( $_REQUEST );
770
771 // Check if the plugin is in the list of conflicting plugins for extra security measures.
772 $conflictingPlugins = \NitroPack\WordPress\ConflictingPlugins::getInstance();
773 $conflictingPlugins_list = $conflictingPlugins->nitropack_get_conflicting_plugins();
774 $plugin_found = false;
775 foreach ( $conflictingPlugins_list as $conflict_plugin ) {
776 if ( $conflict_plugin['plugin'] === $plugin ) {
777 $plugin_found = true;
778 break;
779 }
780 }
781 if ( ! $plugin_found ) {
782 wp_send_json_error( [ 'message' => __( 'Plugin not found in the list of conflicting plugins.', 'nitropack' ) ] );
783 }
784
785 if ( is_plugin_active( $plugin ) ) {
786 deactivate_plugins( $plugin );
787 if ( ! is_plugin_active( $plugin ) ) {
788 /* translators: %s: Name of the plugin that was deactivated */
789 wp_send_json_success( [ 'message' => sprintf( esc_html__( '%s deactivated successfully.', 'nitropack' ), $plugin_name ) ] );
790 } else {
791 wp_send_json_error( [ 'message' => __( 'Failed to deactivate the plugin.', 'nitropack' ) ] );
792 }
793 } else {
794 /* translators: %s: Name of the plugin */
795 wp_send_json_error( [ 'message' => sprintf( esc_html__( '%s is not active.', 'nitropack' ), $plugin_name ) ] );
796 }
797 }
798 }
799