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