PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / modules / stats / class-jetpack-stats-upgrade-nudges.php

class-jetpack-stats-upgrade-nudges.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at modules/stats/class-jetpack-stats-upgrade-nudges.php

584 lines 18.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Adds a section with Upgrade nudges to the Status Report page
4 *
5 * @package jetpack
6 */
7
8 use Automattic\Jetpack\Connection\Manager;
9 use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
10 use Automattic\Jetpack\Jetpack_CRM_Data;
11 use Automattic\Jetpack\Plugins_Installer;
12 use Automattic\Jetpack\Redirect;
13 use Automattic\Jetpack\Tracking;
14
15 /**
16 * Class that adds a new section to the Stats Report page
17 */
18 class Jetpack_Stats_Upgrade_Nudges {
19
20 /**
21 * Indicates whether the class initialized or not
22 *
23 * @var bool
24 */
25 private static $initialized = false;
26
27 /**
28 * Initialize the class by registering the action
29 *
30 * @return void
31 */
32 public static function init() {
33 if ( ! self::$initialized ) {
34 self::$initialized = true;
35 add_action( 'jetpack_admin_pages_wrap_ui_after_callback', array( __CLASS__, 'render' ) );
36 }
37 }
38
39 /**
40 * Unsets the collapse nudges setting.
41 */
42 public static function unset_nudges_setting() {
43 $stats_options = get_option( 'stats_options' );
44
45 if ( $stats_options ) {
46 unset( $stats_options['collapse_nudges'] );
47 update_option( 'stats_options', $stats_options );
48 }
49 }
50
51 /**
52 * Determines whether Backup is active
53 *
54 * @return boolean
55 */
56 private static function is_backup_active() {
57 $rewind_data = Jetpack_Core_Json_Api_Endpoints::rewind_data();
58 return is_object( $rewind_data ) && isset( $rewind_data->state ) && 'unavailable' !== $rewind_data->state;
59 }
60
61 /**
62 * Checks if a plugin is installed.
63 *
64 * @param string $plugin_file The plugin filename.
65 * @return boolean
66 */
67 private static function is_plugin_installed( $plugin_file ) {
68 $plugins = Plugins_Installer::get_plugins();
69 return isset( $plugins[ $plugin_file ] );
70 }
71
72 /**
73 * Checks if a plugin is active.
74 *
75 * @param string $plugin_file The plugin filename.
76 * @return boolean
77 */
78 private static function is_plugin_active( $plugin_file ) {
79 $plugins = Plugins_Installer::get_plugins();
80 return isset( $plugins[ $plugin_file ] ) && isset( $plugins[ $plugin_file ]['active'] ) && $plugins[ $plugin_file ]['active'];
81 }
82
83 /**
84 * Determines whether Scan is active
85 *
86 * @return boolean
87 */
88 private static function is_scan_active() {
89 $scan_data = Jetpack_Core_Json_Api_Endpoints::scan_state();
90 return is_object( $scan_data ) && isset( $scan_data->state ) && 'unavailable' !== $scan_data->state;
91 }
92
93 /**
94 * Determines whether Search module is active
95 *
96 * @return boolean
97 */
98 private static function is_search_active() {
99 return Jetpack::is_module_active( 'search' );
100 }
101
102 /**
103 * Determines whether the Site is on a Security Plan. It will also return true if site has backup, scan and akismet.
104 *
105 * @return boolean
106 */
107 private static function has_security_plan() {
108 $plan_data = Jetpack_Plan::get();
109 if ( is_array( $plan_data ) && isset( $plan_data['product_slug'] ) ) {
110 $has_plan = wp_startswith( $plan_data['product_slug'], 'jetpack_security' );
111 return (
112 $has_plan || (
113 self::is_backup_active() &&
114 self::is_scan_active() &&
115 self::is_akismet_active()
116 )
117 );
118 }
119 return false;
120 }
121
122 /**
123 * Determines whether the Site is on the Complete Plan.
124 *
125 * @return boolean
126 */
127 private static function has_complete_plan() {
128 $plan_data = Jetpack_Plan::get();
129 if ( is_array( $plan_data ) && isset( $plan_data['product_slug'] ) ) {
130 return wp_startswith( $plan_data['product_slug'], 'jetpack_complete' );
131 }
132 return false;
133 }
134
135 /**
136 * Determines whether the Site has a specific product.
137 *
138 * @param string $target_product_slug The product slug we are looking for.
139 *
140 * @return boolean
141 */
142 private static function has_product( $target_product_slug ) {
143 $site_products_slugs = array_column( Jetpack_Plan::get_products(), 'product_slug' );
144
145 foreach ( $site_products_slugs as $product_slug ) {
146 if ( wp_startswith( $product_slug, $target_product_slug ) ) {
147 return true;
148 }
149 }
150
151 return false;
152 }
153
154 /**
155 * Determines whether Akismet is active
156 *
157 * @return boolean
158 */
159 private static function is_akismet_active() {
160 return Jetpack::is_akismet_active();
161 }
162
163 /**
164 * Outputs the header of the Upgrade Secion
165 *
166 * @return void
167 */
168 private static function print_header() {
169 if ( self::has_security_plan() ) {
170 // translators: %s is the Site Name.
171 $title = __( 'Performance and growth tools for %s', 'jetpack' );
172 } else {
173 // translators: %s is the Site Name.
174 $title = __( 'Security, performance, and growth tools for %s', 'jetpack' );
175 }
176 $title = sprintf( $title, get_bloginfo( 'site_name' ) );
177
178 $aria_expanded = 'true';
179 $postbox_closed = '';
180
181 $stats_options = get_option( 'stats_options' );
182
183 if ( isset( $stats_options['collapse_nudges'] ) && $stats_options['collapse_nudges'] ) {
184 $aria_expanded = 'false';
185 $postbox_closed = ' closed';
186 }
187
188 ?>
189 <div id="jp-stats-report-upgrade-wrap">
190 <div class="meta-box-sortables">
191 <div class="postbox<?php echo esc_attr( $postbox_closed ); ?>">
192 <div class="dops-card dops-section-header is-compact jp-stats-report-upgrade-header">
193 <div class="dops-section-header__label">
194 <span class="dops-section-header__label-text">
195 <?php echo esc_html( $title ); ?>
196 </span>
197 </div>
198 <div class="dops-section-header__actions handle-actions hide-if-no-js">
199 <button type="button" id="stats_nudges_toggle" class="handlediv" aria-expanded="<?php echo esc_attr( $aria_expanded ); ?>">
200 <span class="screen-reader-text">Toggle Upgrade Nudges</span>
201 <span class="toggle-indicator" aria-hidden="true"></span>
202 </button>
203 </div>
204 </div>
205 <div class="inside">
206 <?php
207 }
208
209 /**
210 * Outputs the footer of the Upgrade Section
211 *
212 * @return void
213 */
214 private static function print_footer() {
215 ?>
216 </div>
217 </div>
218 </div>
219 </div>
220 <?php
221 }
222
223 /**
224 * Outputs the custom css rules of the Upgrade Section
225 *
226 * @return void
227 */
228 private static function print_styles() {
229 ?>
230 <style>
231 .dops-section-header.dops-card.jp-stats-report-upgrade-header {
232 font-weight: bold;
233 box-shadow: none;
234 flex-wrap: nowrap;
235 }
236 #jp-stats-report-upgrade-wrap .dops-section-header__label-text {
237 white-space: normal;
238 }
239 #stats_nudges_toggle {
240 height: 100%;
241 }
242 .dops-banner.dops-card.is-product.jp-stats-report-upgrade-item {
243 margin-bottom: 0;
244 border-left: 0;
245 box-shadow: none;
246 border-top: 1px solid #c3c4c7;
247 padding: 12px 24px;
248 }
249 .dops-banner.dops-card.jp-stats-report-upgrade-item.jp-stats-report-upgrade-subitem {
250 margin-left: 72px;
251 padding-left: 0;
252 }
253 .jp-stats-report-upgrade-item .dops-banner__action {
254 margin-right: 0;
255 }
256 #jp-stats-report-upgrade-wrap .dops-card::after {
257 content: "";
258 }
259 .jp-stats-report-upgrade-item .dops-banner__title p {
260 margin: 5px 0 0 0;
261 font-weight: normal;
262 }
263 #jp-stats-report-upgrade-wrap .postbox {
264 background-color: white;
265 border: 1px solid #c3c4c7;
266 margin-bottom: 0;
267 }
268 #jp-stats-report-upgrade-wrap .postbox .inside {
269 padding: 0;
270 }
271 </style>
272 <?php
273 }
274 /**
275 * Add a script which handles collapse/expansion of the nudge UI.
276 */
277 private static function print_script() {
278 ?>
279 <script>
280 (function(window, document, undefined){
281 window.onload = set_up_click_handler;
282
283 const stats_wrap = document.getElementById( 'jp-stats-wrap' );
284 stats_wrap.addEventListener( 'stats-loaded', function () {
285 const stat_chart = document.getElementById( 'statchart' );
286 if ( stat_chart === null ) {
287 document.getElementById( 'stats_nudges_toggle' ).style.display = 'none';
288 }
289 });
290
291 function set_up_click_handler(){
292 document.getElementById( 'stats_nudges_toggle' ).onclick = function () {
293 const collapseValue = 'true' === this.getAttribute( 'aria-expanded' );
294
295 fetch( '/wp-json/jetpack/v4/settings', {
296 method: 'post',
297 body: JSON.stringify( { collapse_nudges: collapseValue } ),
298 headers: {
299 'X-WP-Nonce': <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>,
300 'Content-type': 'application/json' }
301 } );
302 };
303 }
304 })(window, document, undefined);
305 </script>
306 <?php
307 }
308
309 /**
310 * Gets the upgrade Redirect link
311 *
312 * @param string $source The source of the redirect link.
313 * @return string
314 */
315 private static function get_upgrade_link( $source ) {
316 $args = array();
317 if ( ! ( new Manager( 'jetpack' ) )->has_connected_owner() ) {
318 $args['query'] = 'unlinked=1';
319 }
320 return Redirect::get_url( $source, $args );
321 }
322
323 /**
324 * Gets the product description link.
325 *
326 * @param string $product_key The product key of the product we wish to display.
327 * @return string
328 */
329 private static function get_product_description_link( $product_key ) {
330 return Jetpack::admin_url( array( 'page' => sprintf( 'jetpack#/product/%s', $product_key ) ) );
331 }
332
333 /**
334 * Tracks an event in Tracks
335 *
336 * @param string $event_name The event name.
337 * @return void
338 */
339 private static function track_event( $event_name ) {
340 $connection_manager = new Manager( 'jetpack' );
341 $tracking = new Tracking( 'jetpack', $connection_manager );
342 $tracking->record_user_event(
343 $event_name,
344 array(
345 'has_connected_owner' => $connection_manager->has_connected_owner(),
346 )
347 );
348 }
349
350 /**
351 * Outputs one Upgrade item
352 *
353 * @param string $title The title of the item.
354 * @param string $text The description of the item.
355 * @param string $icon The path of the icon, relative to Jetpack images folder.
356 * @param string $link The link of the button.
357 * @param string $tracks_id The id used to identify the tracks events. Automatically prefixed with "jetpack_stats_nudges_{view|click|learn_more}_".
358 * @param string $learn_more_link The target of the "Learn more" link.
359 * @param boolean $subitem Whether it is a subitem or not.
360 * @param string $button_label The button label.
361 * @return void
362 */
363 private static function print_item( $title, $text, $icon, $link, $tracks_id, $learn_more_link, $subitem = false, $button_label = null ) {
364 $additional_classes = $subitem ? 'jp-stats-report-upgrade-subitem' : '';
365 $button_class = $subitem ? 'is-secondary' : 'is-primary';
366 $icon_url = plugins_url( '', JETPACK__PLUGIN_FILE ) . '/images/products/' . $icon;
367 $button_label = $button_label === null ? _x( 'Upgrade', 'Call to action to buy a new plan', 'jetpack' ) : $button_label;
368 $view_event = "stats_nudges_view_$tracks_id";
369 $click_event = "stats_nudges_click_$tracks_id";
370 $learn_more_event = "stats_nudges_learn_more_$tracks_id";
371 self::track_event( $view_event );
372
373 ?>
374 <div class="dops-card dops-banner has-call-to-action is-product jp-stats-report-upgrade-item <?php echo esc_attr( $additional_classes ); ?>">
375 <div class="dops-banner__icon-plan">
376 <img src="<?php echo esc_attr( $icon_url ); ?>" alt="" width="32" height="32">
377 </div>
378 <div class="dops-banner__content">
379 <div class="dops-banner__info">
380 <div class="dops-banner__title">
381 <?php echo esc_html( $title ); ?>
382 <p>
383 <?php echo esc_html( $text ); ?>
384 <a href="<?php echo esc_attr( $learn_more_link ); ?>" class="jptracks" target="_blank" rel="noopener noreferrer" data-jptracks-name="<?php echo esc_attr( $learn_more_event ); ?>">
385 <?php esc_html_e( 'Learn more', 'jetpack' ); ?>
386 </a>
387 </p>
388 </div>
389 </div>
390 <div class="dops-banner__action">
391 <a href="<?php echo esc_attr( $link ); ?>" type="button" class="jptracks dops-button is-compact <?php echo esc_attr( $button_class ); ?>" data-jptracks-name="<?php echo esc_attr( $click_event ); ?>">
392 <?php echo esc_html( $button_label ); ?>
393 </a>
394 </div>
395 </div>
396 </div>
397 <?php
398 }
399
400 /**
401 * Prints the Security item
402 *
403 * @return void
404 */
405 private static function print_security() {
406 $link = self::get_product_description_link( 'security' );
407 $learn_link = self::get_upgrade_link( 'stats-nudges-security-learn' );
408 $text = __( 'Comprehensive protection for your site, including Backup, Scan, and Anti-spam.', 'jetpack' );
409 self::print_item( _x( 'Security', 'Jetpack product name', 'jetpack' ), $text, 'product-jetpack.svg', $link, 'security', $learn_link );
410 }
411
412 /**
413 * Prints the Backup item
414 *
415 * @return void
416 */
417 private static function print_backup() {
418 $link = self::get_product_description_link( 'backup' );
419 $learn_link = self::get_upgrade_link( 'stats-nudges-backup-learn' );
420 $text = __( 'Save every change and get back online quickly with one-click restores.', 'jetpack' );
421 self::print_item( __( 'VaultPress Backup', 'jetpack' ), $text, 'product-jetpack-backup.svg', $link, 'backup', $learn_link, true );
422 }
423
424 /**
425 * Prints the Scan item
426 *
427 * @since 10.1
428 * @since 10.3 The scan nudge has been removed, but leaving this here in case we reverse course.
429 *
430 * @todo Remove this function is not used ~6 months.
431 *
432 * @return void
433 */
434 private static function print_scan() {
435 $link = self::get_product_description_link( 'scan' );
436 $learn_link = self::get_upgrade_link( 'stats-nudges-scan-learn' );
437 $text = __( 'Stay ahead of security threats with automated scanning and one-click fixes.', 'jetpack' );
438 self::print_item( __( 'Scan', 'jetpack' ), $text, 'product-jetpack-scan.svg', $link, 'scan', $learn_link, true );
439 }
440
441 /**
442 * Prints the Akismet item
443 *
444 * @since 10.1
445 * @since 10.3 The anti-spam nudge has been removed, but leaving this here in case we reverse course.
446 *
447 * @todo Remove this function is not used ~6 months.
448 *
449 * @return void
450 */
451 private static function print_akismet() {
452 $link = self::get_product_description_link( 'akismet' );
453 $learn_link = self::get_upgrade_link( 'stats-nudges-akismet-learn' );
454 $text = __( 'Automatically clear spam from comments and forms.', 'jetpack' );
455 self::print_item( __( 'Akismet Anti-spam', 'jetpack' ), $text, 'product-jetpack-anti-spam.svg', $link, 'akismet', $learn_link, true );
456 }
457
458 /**
459 * Prints the Search item
460 *
461 * @return void
462 */
463 private static function print_search() {
464 $link = self::get_product_description_link( 'search' );
465 $learn_link = self::get_upgrade_link( 'stats-nudges-search-learn' );
466 $text = __( 'Help your site visitors instantly find what they\'re looking for so they read and buy more.', 'jetpack' );
467 self::print_item( __( 'Search', 'jetpack' ), $text, 'product-jetpack-search.svg', $link, 'search', $learn_link );
468 }
469
470 /**
471 * Prints the VideoPress item
472 *
473 * @return void
474 */
475 private static function print_videopress() {
476 $link = self::get_product_description_link( 'videopress' );
477 $learn_link = self::get_upgrade_link( 'stats-nudges-videopress-learn' );
478 $text = __( 'Engage your visitors with high-quality, ad-free videos built specifically for WordPress.', 'jetpack' );
479 self::print_item( __( 'VideoPress', 'jetpack' ), $text, 'product-jetpack-videopress.svg', $link, 'videopress', $learn_link );
480 }
481
482 /**
483 * Prints the Boost item
484 *
485 * @param bool $print Whether to print the item output or just check whether it would be printed or not.
486 *
487 * @return bool
488 */
489 private static function get_boost_output( $print = true ) {
490 $plugin_file = 'jetpack-boost/jetpack-boost.php';
491 $plugin_slug = 'jetpack-boost';
492 if ( self::is_plugin_active( $plugin_file ) ) {
493 return false;
494 } elseif ( self::is_plugin_installed( $plugin_file ) ) {
495 $label = __( 'Activate Boost', 'jetpack' );
496 $link = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . rawurlencode( $plugin_file ) . '&amp;plugin_status=all&amp;paged=1', 'activate-plugin_' . $plugin_file );
497 } else {
498 $label = __( 'Install Boost', 'jetpack' );
499 $link = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
500 }
501 if ( $print ) {
502 $learn_link = self::get_upgrade_link( 'stats-nudges-boost-learn' );
503 $text = __( 'Improve your site\'s performance and SEO in a few clicks with the free Jetpack Boost plugin.', 'jetpack' );
504 self::print_item( __( 'Boost', 'jetpack' ), $text, 'product-jetpack-boost.svg', $link, 'boost', $learn_link, false, $label );
505 }
506 return true;
507 }
508
509 /**
510 * Prints the CRM item
511 *
512 * @param bool $print Whether to print the item output or just check whether it would be printed or not.
513 *
514 * @since 10.1
515 *
516 * @return bool
517 */
518 private static function get_crm_output( $print = true ) {
519 $plugin_file = Jetpack_CRM_Data::JETPACK_CRM_PLUGIN_SLUG;
520 $plugin_slug = substr( $plugin_file, 0, strpos( $plugin_file, '/' ) );
521 if ( self::is_plugin_active( $plugin_file ) ) {
522 return false;
523 } elseif ( self::is_plugin_installed( $plugin_file ) ) {
524 $link = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . rawurlencode( $plugin_file ) . '&amp;plugin_status=all&amp;paged=1', 'activate-plugin_' . $plugin_file );
525 $label = __( 'Activate CRM', 'jetpack' );
526 } else {
527 $link = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
528 $label = __( 'Install CRM', 'jetpack' );
529 }
530 if ( $print ) {
531 $learn_link = self::get_upgrade_link( 'stats-nudges-crm-learn' );
532 $text = __( 'Sell more and get more leads with the free Jetpack CRM plugin built specifically for WordPress.', 'jetpack' );
533 self::print_item( __( 'CRM', 'jetpack' ), $text, 'product-jetpack-crm.svg', $link, 'crm', $learn_link, false, $label );
534 }
535 return true;
536 }
537
538 /**
539 * Outputs the section to the Stats Report page
540 *
541 * @param string $callback The callback passed to the jetpack admin page.
542 * @return void
543 */
544 public static function render( $callback ) {
545 /** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
546 if ( 'stats_reports_page' !== $callback || ! apply_filters( 'jetpack_show_promotions', true ) || ! current_user_can( 'manage_options' ) ) {
547 return;
548 }
549
550 if ( self::has_complete_plan() ) {
551 return;
552 }
553
554 if (
555 self::has_security_plan() &&
556 self::is_backup_active() &&
557 self::is_search_active() &&
558 ! self::get_boost_output( false ) &&
559 ! self::get_crm_output( false )
560 ) {
561 return;
562 }
563
564 self::print_styles();
565 self::print_script();
566 self::print_header();
567 if ( ! self::has_security_plan() ) {
568 self::print_security();
569 if ( ! self::is_backup_active() ) {
570 self::print_backup();
571 }
572 }
573 if ( ! self::is_search_active() ) {
574 self::print_search();
575 }
576 if ( ! self::has_product( 'jetpack_videopress' ) ) {
577 self::print_videopress();
578 }
579 self::get_boost_output();
580 self::get_crm_output();
581 self::print_footer();
582 }
583 }
584