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