| @@ -1,8 +1,10 @@ | ||
| 1 | 1 | <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName |
| 2 | 2 | /** |
| 3 | - * Adds the PSH functionality to Jetpack. | |
| 3 | + * Plugin Search Hints, aka Feature Suggestions. | |
| 4 | 4 | * |
| 5 | + * @since 7.1.0 | |
| 6 | + * | |
| 5 | 7 | * @package automattic/jetpack |
| 6 | 8 | */ |
| 7 | 9 | |
| 8 | 10 | // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files. |
| @@ -7,16 +9,15 @@ | ||
| 7 | 9 | |
| 8 | 10 | // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files. |
| 9 | 11 | |
| 10 | 12 | use Automattic\Jetpack\Constants; |
| 13 | +use Automattic\Jetpack\Current_Plan as Jetpack_Plan; | |
| 11 | 14 | use Automattic\Jetpack\Redirect; |
| 12 | 15 | use Automattic\Jetpack\Tracking; |
| 13 | 16 | |
| 14 | -/** | |
| 15 | - * Disable direct access and execution. | |
| 16 | - */ | |
| 17 | +// Disable direct access and execution. | |
| 17 | 18 | if ( ! defined( 'ABSPATH' ) ) { |
| 18 | - exit; | |
| 19 | + exit( 0 ); | |
| 19 | 20 | } |
| 20 | 21 | |
| 21 | 22 | if ( |
| 22 | 23 | is_admin() && |
| @@ -213,9 +214,9 @@ | ||
| 213 | 214 | * |
| 214 | 215 | * @return bool True if $hint should be displayed. |
| 215 | 216 | */ |
| 216 | 217 | protected function should_display_hint( $hint ) { |
| 217 | - $dismissed_hints = $this->get_dismissed_hints(); | |
| 218 | + $dismissed_hints = static::get_dismissed_hints(); | |
| 218 | 219 | // If more than 2 hints have been dismissed, then show no more. |
| 219 | 220 | if ( 2 < count( $dismissed_hints ) ) { |
| 220 | 221 | return false; |
| 221 | 222 | } |
| @@ -238,9 +239,8 @@ | ||
| 238 | 239 | 'jetpackPluginSearch', |
| 239 | 240 | array( |
| 240 | 241 | 'nonce' => wp_create_nonce( 'wp_rest' ), |
| 241 | 242 | 'base_rest_url' => rest_url( '/jetpack/v4' ), |
| 242 | - 'poweredBy' => esc_html__( 'by Jetpack (installed)', 'jetpack' ), | |
| 243 | 243 | 'manageSettings' => esc_html__( 'Configure', 'jetpack' ), |
| 244 | 244 | 'activateModule' => esc_html__( 'Activate Module', 'jetpack' ), |
| 245 | 245 | 'getStarted' => esc_html__( 'Get started', 'jetpack' ), |
| 246 | 246 | 'activated' => esc_html__( 'Activated', 'jetpack' ), |
| @@ -300,9 +300,9 @@ | ||
| 300 | 300 | * @return array List of features. |
| 301 | 301 | */ |
| 302 | 302 | public function get_extra_features() { |
| 303 | 303 | return array( |
| 304 | - 'akismet' => array( | |
| 304 | + 'akismet' => array( | |
| 305 | 305 | 'name' => 'Akismet', |
| 306 | 306 | 'search_terms' => 'akismet, anti-spam, antispam, comments, spam, spam protection, form spam, captcha, no captcha, nocaptcha, recaptcha, phising, google', |
| 307 | 307 | 'short_description' => esc_html__( 'Keep your visitors and search engines happy by stopping comment and contact form spam with Akismet.', 'jetpack' ), |
| 308 | 308 | 'requires_connection' => true, |
| @@ -310,12 +310,33 @@ | ||
| 310 | 310 | 'sort' => '16', |
| 311 | 311 | 'learn_more_button' => Redirect::get_url( 'plugin-hint-upgrade-akismet' ), |
| 312 | 312 | 'configure_url' => admin_url( 'admin.php?page=akismet-key-config' ), |
| 313 | 313 | ), |
| 314 | + 'sharing-block' => array( | |
| 315 | + 'name' => esc_html__( 'Sharing buttons block', 'jetpack' ), | |
| 316 | + 'search_terms' => 'share, sharing, sharing block, sharing button, social buttons, buttons, share facebook, share twitter, social share, icons, email, facebook, twitter, x, linkedin, pinterest, social media', | |
| 317 | + 'short_description' => esc_html__( 'Add sharing buttons blocks anywhere on your website to help your visitors share your content.', 'jetpack' ), | |
| 318 | + 'requires_connection' => false, | |
| 319 | + 'module' => 'sharing-block', | |
| 320 | + 'sort' => '13', | |
| 321 | + 'learn_more_button' => Redirect::get_url( 'jetpack-support-sharing-block' ), | |
| 322 | + 'configure_url' => $this->get_sharing_block_editor_url(), | |
| 323 | + ), | |
| 314 | 324 | ); |
| 315 | 325 | } |
| 316 | 326 | |
| 317 | 327 | /** |
| 328 | + * Site Editor URL for the template the Sharing Buttons block goes in. | |
| 329 | + * | |
| 330 | + * @return string | |
| 331 | + */ | |
| 332 | + private function get_sharing_block_editor_url() { | |
| 333 | + return admin_url( | |
| 334 | + 'site-editor.php?p=' . rawurlencode( '/wp_template/' . get_stylesheet() . '//single' ) . '&canvas=edit' | |
| 335 | + ); | |
| 336 | + } | |
| 337 | + | |
| 338 | + /** | |
| 318 | 339 | * Intercept the plugins API response and add in an appropriate card for Jetpack |
| 319 | 340 | * |
| 320 | 341 | * @param object $result Plugin search results. |
| 321 | 342 | * @param string $action unused. |
| @@ -321,31 +342,59 @@ | ||
| 321 | 342 | * @param string $action unused. |
| 322 | 343 | * @param object $args Search args. |
| 323 | 344 | */ |
| 324 | 345 | public function inject_jetpack_module_suggestion( $result, $action, $args ) { |
| 346 | + /* | |
| 347 | + * Bail if something else hooks into the Plugins' API response | |
| 348 | + * and does not return results. | |
| 349 | + */ | |
| 350 | + if ( empty( $result->plugins ) || is_wp_error( $result ) ) { | |
| 351 | + return $result; | |
| 352 | + } | |
| 353 | + | |
| 325 | 354 | // Looks like a search query; it's matching time. |
| 326 | 355 | if ( ! empty( $args->search ) ) { |
| 356 | + $searchable_modules = array( | |
| 357 | + 'contact-form', | |
| 358 | + 'monitor', | |
| 359 | + 'photon', | |
| 360 | + 'photon-cdn', | |
| 361 | + 'protect', | |
| 362 | + 'publicize', | |
| 363 | + 'related-posts', | |
| 364 | + 'akismet', | |
| 365 | + 'vaultpress', | |
| 366 | + 'videopress', | |
| 367 | + 'search', | |
| 368 | + ); | |
| 369 | + | |
| 370 | + /* | |
| 371 | + * Let's handle the Sharing feature differently. | |
| 372 | + * If we're using a block-based theme, we should suggest the sharing block. | |
| 373 | + * If using a classic theme, we should suggest the old sharing module. | |
| 374 | + */ | |
| 375 | + if ( wp_is_block_theme() ) { | |
| 376 | + $searchable_modules[] = 'sharing-block'; | |
| 377 | + } else { | |
| 378 | + $searchable_modules[] = 'sharedaddy'; | |
| 379 | + } | |
| 380 | + | |
| 381 | + /* | |
| 382 | + * Only surface the SEO Tools hint when the new Jetpack SEO admin page is | |
| 383 | + * available and registered, so its CTA never points to a missing page. | |
| 384 | + */ | |
| 385 | + if ( | |
| 386 | + \Automattic\Jetpack\SEO\Initializer::is_available() | |
| 387 | + && \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible() | |
| 388 | + ) { | |
| 389 | + $searchable_modules[] = 'seo-tools'; | |
| 390 | + } | |
| 391 | + | |
| 327 | 392 | require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php'; |
| 328 | 393 | $tracking = new Tracking(); |
| 329 | 394 | $jetpack_modules_list = array_intersect_key( |
| 330 | 395 | array_merge( $this->get_extra_features(), Jetpack_Admin::init()->get_modules() ), |
| 331 | - array_flip( | |
| 332 | - array( | |
| 333 | - 'contact-form', | |
| 334 | - 'lazy-images', | |
| 335 | - 'monitor', | |
| 336 | - 'photon', | |
| 337 | - 'photon-cdn', | |
| 338 | - 'protect', | |
| 339 | - 'publicize', | |
| 340 | - 'related-posts', | |
| 341 | - 'sharedaddy', | |
| 342 | - 'akismet', | |
| 343 | - 'vaultpress', | |
| 344 | - 'videopress', | |
| 345 | - 'search', | |
| 346 | - ) | |
| 347 | - ) | |
| 396 | + array_flip( $searchable_modules ) | |
| 348 | 397 | ); |
| 349 | 398 | uasort( $jetpack_modules_list, array( $this, 'by_sorting_option' ) ); |
| 350 | 399 | |
| 351 | 400 | // Record event when user searches for a term over 3 chars (less than 3 is not very useful). |
| @@ -391,8 +440,9 @@ | ||
| 391 | 440 | esc_html_x( 'Jetpack: %s', 'Jetpack: Module Name', 'jetpack' ), |
| 392 | 441 | $jetpack_modules_list[ $matching_module ]['name'] |
| 393 | 442 | ), |
| 394 | 443 | 'short_description' => $jetpack_modules_list[ $matching_module ]['short_description'], |
| 444 | + 'author' => esc_attr__( 'Jetpack (installed)', 'jetpack' ), | |
| 395 | 445 | 'requires_connection' => (bool) $jetpack_modules_list[ $matching_module ]['requires_connection'], |
| 396 | 446 | 'slug' => self::$slug, |
| 397 | 447 | 'version' => JETPACK__VERSION, |
| 398 | 448 | 'icons' => array( |
| @@ -467,9 +517,9 @@ | ||
| 467 | 517 | * @param array $m1 Array 1 to sort. |
| 468 | 518 | * @param array $m2 Array 2 to sort. |
| 469 | 519 | */ |
| 470 | 520 | private function by_sorting_option( $m1, $m2 ) { |
| 471 | - return $m1['sort'] - $m2['sort']; | |
| 521 | + return $m1['sort'] <=> $m2['sort']; | |
| 472 | 522 | } |
| 473 | 523 | |
| 474 | 524 | /** |
| 475 | 525 | * Modify the URL to the feature settings, for example Publicize. |
| @@ -488,14 +538,11 @@ | ||
| 488 | 538 | case 'publicize': |
| 489 | 539 | $configure_url = Redirect::get_url( 'calypso-marketing-connections' ); |
| 490 | 540 | break; |
| 491 | 541 | case 'seo-tools': |
| 492 | - $configure_url = Redirect::get_url( | |
| 493 | - 'calypso-marketing-traffic', | |
| 494 | - array( | |
| 495 | - 'anchor' => 'seo', | |
| 496 | - ) | |
| 497 | - ); | |
| 542 | + // Jetpack SEO has its own wp-admin page (the new SEO home base); | |
| 543 | + // send users there rather than the legacy Traffic page. | |
| 544 | + $configure_url = admin_url( 'admin.php?page=jetpack-seo' ); | |
| 498 | 545 | break; |
| 499 | 546 | case 'google-analytics': |
| 500 | 547 | $configure_url = Redirect::get_url( |
| 501 | 548 | 'calypso-marketing-traffic', |
| @@ -526,13 +573,22 @@ | ||
| 526 | 573 | remove_filter( 'self_admin_url', array( $this, 'plugin_details' ) ); |
| 527 | 574 | |
| 528 | 575 | $links = array(); |
| 529 | 576 | |
| 530 | - if ( 'akismet' === $plugin['module'] || 'vaultpress' === $plugin['module'] ) { | |
| 577 | + if ( 'sharing-block' === $plugin['module'] ) { | |
| 531 | 578 | $links['jp_get_started'] = '<a |
| 532 | 579 | id="plugin-select-settings" |
| 533 | 580 | class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button" |
| 581 | + href="' . esc_url( $this->get_sharing_block_editor_url() ) . '" | |
| 582 | + data-module="' . esc_attr( $plugin['module'] ) . '" | |
| 583 | + data-track="get_started" | |
| 584 | + >' . esc_html__( 'Add block', 'jetpack' ) . '</a>'; | |
| 585 | + } elseif ( 'akismet' === $plugin['module'] || 'vaultpress' === $plugin['module'] ) { | |
| 586 | + $links['jp_get_started'] = '<a | |
| 587 | + id="plugin-select-settings" | |
| 588 | + class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button" | |
| 534 | 589 | href="' . esc_url( Redirect::get_url( 'plugin-hint-learn-' . $plugin['module'] ) ) . '" |
| 590 | + target="_blank" | |
| 535 | 591 | data-module="' . esc_attr( $plugin['module'] ) . '" |
| 536 | 592 | data-track="get_started" |
| 537 | 593 | >' . esc_html__( 'Get started', 'jetpack' ) . '</a>'; |
| 538 | 594 | // Jetpack installed, active, feature not enabled; prompt to enable. |
| @@ -563,13 +619,26 @@ | ||
| 563 | 619 | data-module="' . esc_attr( $plugin['module'] ) . '" |
| 564 | 620 | data-track="configure" |
| 565 | 621 | >' . esc_html__( 'Configure', 'jetpack' ) . '</a>'; |
| 566 | 622 | // Module is active, doesn't have options to configure. |
| 623 | + } elseif ( 'seo-tools' === $plugin['module'] && Jetpack::is_module_active( $plugin['module'] ) ) { | |
| 624 | + // Jetpack SEO has its own wp-admin page; send users there (same tab) | |
| 625 | + // rather than a jetpack.com doc redirect, which isn't registered for | |
| 626 | + // this module. Reuse get_configure_url() so the destination stays in | |
| 627 | + // one place. | |
| 628 | + $links['jp_get_started'] = '<a | |
| 629 | + id="plugin-select-settings" | |
| 630 | + class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button" | |
| 631 | + href="' . esc_url( $this->get_configure_url( $plugin['module'], $plugin['configure_url'] ) ) . '" | |
| 632 | + data-module="' . esc_attr( $plugin['module'] ) . '" | |
| 633 | + data-track="get_started" | |
| 634 | + >' . esc_html__( 'Get started', 'jetpack' ) . '</a>'; | |
| 567 | 635 | } elseif ( Jetpack::is_module_active( $plugin['module'] ) ) { |
| 568 | 636 | $links['jp_get_started'] = '<a |
| 569 | 637 | id="plugin-select-settings" |
| 570 | 638 | class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button" |
| 571 | 639 | href="' . esc_url( Redirect::get_url( 'plugin-hint-learn-' . $plugin['module'] ) ) . '" |
| 640 | + target="_blank" | |
| 572 | 641 | data-module="' . esc_attr( $plugin['module'] ) . '" |
| 573 | 642 | data-track="get_started" |
| 574 | 643 | >' . esc_html__( 'Get started', 'jetpack' ) . '</a>'; |
| 575 | 644 | } |
| @@ -592,9 +661,8 @@ | ||
| 592 | 661 | >' . esc_html__( 'Hide this suggestion', 'jetpack' ) . '</a>'; |
| 593 | 662 | |
| 594 | 663 | return $links; |
| 595 | 664 | } |
| 596 | - | |
| 597 | 665 | } |
| 598 | 666 | |
| 599 | 667 | /** |
| 600 | 668 | * Master control that checks if Plugin search hints is active. |