Yoast SEO - Version 9.3

Version Description

Download this release

Release Info

Developer Yoast
Plugin Icon 128x128 Yoast SEO
Version 9.3
Comparing to
See all releases

Code changes from version 9.2.1 to 9.3

Files changed (84) hide show
  1. admin/class-admin-asset-analysis-worker-location.php +15 -4
  2. admin/class-admin-asset-manager.php +19 -4
  3. admin/class-admin-init.php +3 -26
  4. admin/class-admin-utils.php +12 -0
  5. admin/class-admin.php +10 -4
  6. admin/class-expose-shortlinks.php +1 -0
  7. admin/class-gutenberg-compatibility.php +2 -2
  8. admin/class-premium-upsell-admin-block.php +1 -1
  9. admin/class-recalibration-beta.php +232 -0
  10. admin/class-yoast-form.php +2 -2
  11. admin/endpoints/class-endpoint-file-size.php +66 -0
  12. admin/exceptions/class-file-size-exception.php +46 -0
  13. admin/filters/class-cornerstone-filter.php +21 -0
  14. admin/formatter/class-metabox-formatter.php +5 -4
  15. admin/google_search_console/class-gsc-category-filters.php +2 -2
  16. admin/my-yoast-proxy.php +26 -0
  17. admin/notifiers/class-post-type-archive-notification-handler.php +19 -92
  18. admin/notifiers/class-recalibration-beta.php +105 -0
  19. admin/notifiers/dismissible-notification.php +121 -0
  20. admin/services/class-file-size.php +107 -0
  21. admin/views/sidebar.php +58 -45
  22. admin/views/tabs/dashboard/features.php +3 -0
  23. css/dist/admin-global-921-rtl.min.css +0 -1
  24. css/dist/admin-global-921.min.css +0 -1
  25. css/dist/admin-global-930-rtl.min.css +1 -0
  26. css/dist/admin-global-930.min.css +1 -0
  27. css/dist/{adminbar-921-rtl.min.css → adminbar-930-rtl.min.css} +0 -0
  28. css/dist/{adminbar-921.min.css → adminbar-930.min.css} +0 -0
  29. css/dist/{alerts-921-rtl.min.css → alerts-930-rtl.min.css} +0 -0
  30. css/dist/{alerts-921.min.css → alerts-930.min.css} +0 -0
  31. css/dist/{dashboard-921-rtl.min.css → dashboard-930-rtl.min.css} +0 -0
  32. css/dist/{dashboard-921.min.css → dashboard-930.min.css} +0 -0
  33. css/dist/{edit-page-921-rtl.min.css → edit-page-930-rtl.min.css} +0 -0
  34. css/dist/{edit-page-921.min.css → edit-page-930.min.css} +0 -0
  35. css/dist/{featured-image-921-rtl.min.css → featured-image-930-rtl.min.css} +0 -0
  36. css/dist/{featured-image-921.min.css → featured-image-930.min.css} +0 -0
  37. css/dist/{filter-explanation-921-rtl.min.css → filter-explanation-930-rtl.min.css} +0 -0
  38. css/dist/{filter-explanation-921.min.css → filter-explanation-930.min.css} +0 -0
  39. css/dist/{inside-editor-921-rtl.min.css → inside-editor-930-rtl.min.css} +0 -0
  40. css/dist/{inside-editor-921.min.css → inside-editor-930.min.css} +0 -0
  41. css/dist/metabox-921-rtl.min.css +0 -1
  42. css/dist/metabox-921.min.css +0 -1
  43. css/dist/metabox-930-rtl.min.css +1 -0
  44. css/dist/metabox-930.min.css +1 -0
  45. css/dist/{metabox-primary-category-921-rtl.min.css → metabox-primary-category-930-rtl.min.css} +0 -0
  46. css/dist/{metabox-primary-category-921.min.css → metabox-primary-category-930.min.css} +0 -0
  47. css/dist/search-appearance-921-rtl.min.css +0 -1
  48. css/dist/search-appearance-921.min.css +0 -1
  49. css/dist/search-appearance-930-rtl.min.css +1 -0
  50. css/dist/search-appearance-930.min.css +1 -0
  51. css/dist/structured-data-blocks-921.min.css +0 -1
  52. css/dist/{structured-data-blocks-921-rtl.min.css → structured-data-blocks-930-rtl.min.css} +1 -1
  53. css/dist/structured-data-blocks-930.min.css +1 -0
  54. css/dist/{toggle-switch-921-rtl.min.css → toggle-switch-930-rtl.min.css} +1 -1
  55. css/dist/{toggle-switch-921.min.css → toggle-switch-930.min.css} +1 -1
  56. css/dist/{wpseo-dismissible-921-rtl.min.css → wpseo-dismissible-930-rtl.min.css} +0 -0
  57. css/dist/{wpseo-dismissible-921.min.css → wpseo-dismissible-930.min.css} +0 -0
  58. css/dist/yoast-components-921-rtl.min.css +0 -1
  59. css/dist/yoast-components-921.min.css +0 -1
  60. css/dist/yoast-components-930-rtl.min.css +1 -0
  61. css/dist/yoast-components-930.min.css +1 -0
  62. css/dist/yoast-extensions-921-rtl.min.css +0 -1
  63. css/dist/yoast-extensions-921.min.css +0 -1
  64. css/dist/yoast-extensions-930-rtl.min.css +1 -0
  65. css/dist/yoast-extensions-930.min.css +1 -0
  66. css/dist/yst_plugin_tools-921-rtl.min.css +0 -1
  67. css/dist/yst_plugin_tools-921.min.css +0 -1
  68. css/dist/yst_plugin_tools-930-rtl.min.css +1 -0
  69. css/dist/yst_plugin_tools-930.min.css +1 -0
  70. css/dist/{yst_seo_score-921-rtl.min.css → yst_seo_score-930-rtl.min.css} +0 -0
  71. css/dist/{yst_seo_score-921.min.css → yst_seo_score-930.min.css} +0 -0
  72. frontend/class-breadcrumbs.php +34 -5
  73. frontend/class-json-ld.php +26 -3
  74. frontend/class-opengraph-image.php +2 -2
  75. frontend/class-opengraph-oembed.php +1 -0
  76. frontend/class-opengraph.php +4 -0
  77. inc/class-wpseo-admin-bar-menu.php +1 -1
  78. inc/class-wpseo-meta.php +5 -11
  79. inc/class-wpseo-utils.php +1 -1
  80. inc/exceptions/class-invalid-argument-exception.php +1 -1
  81. inc/interface-wpseo-wordpress-integration.php +2 -0
  82. inc/options/class-wpseo-option-wpseo.php +1 -0
  83. inc/sitemaps/class-post-type-sitemap-provider.php +32 -41
  84. js/dist/analysis-921.min.js +0 -7
admin/class-admin-asset-analysis-worker-location.php CHANGED
@@ -32,12 +32,18 @@ final class WPSEO_Admin_Asset_Analysis_Worker_Location implements WPSEO_Admin_As
32
  $flat_version = $asset_manager->flatten_version( WPSEO_VERSION );
33
  }
34
 
 
 
 
 
 
35
  $this->asset_location = WPSEO_Admin_Asset_Manager::create_default_location();
36
- $asset_arguments = array(
37
- 'name' => $name,
38
- 'src' => 'wp-seo-' . $name . '-' . $flat_version,
 
 
39
  );
40
- $this->asset = new WPSEO_Admin_Asset( $asset_arguments );
41
  }
42
 
43
  /**
@@ -58,6 +64,11 @@ final class WPSEO_Admin_Asset_Analysis_Worker_Location implements WPSEO_Admin_As
58
  * @return string The URL of the asset.
59
  */
60
  public function get_url( WPSEO_Admin_Asset $asset, $type ) {
 
 
 
 
 
61
  return $this->asset_location->get_url( $asset, $type );
62
  }
63
  }
32
  $flat_version = $asset_manager->flatten_version( WPSEO_VERSION );
33
  }
34
 
35
+ $analysis_worker = 'wp-seo-' . $name . '-' . $flat_version;
36
+ if ( $name === 'analysis-worker' && WPSEO_Recalibration_Beta::is_enabled() ) {
37
+ $analysis_worker = plugin_dir_url( WPSEO_FILE ) . 'admin/my-yoast-proxy.php?file=research-webworker';
38
+ }
39
+
40
  $this->asset_location = WPSEO_Admin_Asset_Manager::create_default_location();
41
+ $this->asset = new WPSEO_Admin_Asset(
42
+ array(
43
+ 'name' => $name,
44
+ 'src' => $analysis_worker,
45
+ )
46
  );
 
47
  }
48
 
49
  /**
64
  * @return string The URL of the asset.
65
  */
66
  public function get_url( WPSEO_Admin_Asset $asset, $type ) {
67
+ $scheme = wp_parse_url( $asset->get_src(), PHP_URL_SCHEME );
68
+ if ( in_array( $scheme, array( 'http', 'https' ), true ) ) {
69
+ return $asset->get_src();
70
+ }
71
+
72
  return $this->asset_location->get_url( $asset, $type );
73
  }
74
  }
admin/class-admin-asset-manager.php CHANGED
@@ -66,7 +66,7 @@ class WPSEO_Admin_Asset_Manager {
66
  public function register_script( WPSEO_Admin_Asset $script ) {
67
  wp_register_script(
68
  $this->prefix . $script->get_name(),
69
- $this->asset_location->get_url( $script, WPSEO_Admin_Asset::TYPE_JS ),
70
  $script->get_deps(),
71
  $script->get_version(),
72
  $script->is_in_footer()
@@ -81,7 +81,7 @@ class WPSEO_Admin_Asset_Manager {
81
  public function register_style( WPSEO_Admin_Asset $style ) {
82
  wp_register_style(
83
  $this->prefix . $style->get_name(),
84
- $this->asset_location->get_url( $style, WPSEO_Admin_Asset::TYPE_CSS ),
85
  $style->get_deps(),
86
  $style->get_version(),
87
  $style->get_media()
@@ -401,7 +401,6 @@ class WPSEO_Admin_Asset_Manager {
401
  'deps' => array(
402
  'wp-util',
403
  'wp-api',
404
- 'wp-sanitize',
405
  'wp-element',
406
  'wp-i18n',
407
  'wp-data',
@@ -419,7 +418,6 @@ class WPSEO_Admin_Asset_Manager {
419
  'name' => 'term-scraper',
420
  'src' => 'wp-seo-term-scraper-' . $flat_version,
421
  'deps' => array(
422
- 'wp-sanitize',
423
  'wp-element',
424
  'wp-i18n',
425
  'wp-data',
@@ -686,4 +684,21 @@ class WPSEO_Admin_Asset_Manager {
686
  ),
687
  );
688
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
  }
66
  public function register_script( WPSEO_Admin_Asset $script ) {
67
  wp_register_script(
68
  $this->prefix . $script->get_name(),
69
+ $this->get_url( $script, WPSEO_Admin_Asset::TYPE_JS ),
70
  $script->get_deps(),
71
  $script->get_version(),
72
  $script->is_in_footer()
81
  public function register_style( WPSEO_Admin_Asset $style ) {
82
  wp_register_style(
83
  $this->prefix . $style->get_name(),
84
+ $this->get_url( $style, WPSEO_Admin_Asset::TYPE_CSS ),
85
  $style->get_deps(),
86
  $style->get_version(),
87
  $style->get_media()
401
  'deps' => array(
402
  'wp-util',
403
  'wp-api',
 
404
  'wp-element',
405
  'wp-i18n',
406
  'wp-data',
418
  'name' => 'term-scraper',
419
  'src' => 'wp-seo-term-scraper-' . $flat_version,
420
  'deps' => array(
 
421
  'wp-element',
422
  'wp-i18n',
423
  'wp-data',
684
  ),
685
  );
686
  }
687
+
688
+ /**
689
+ * Determines the URL of the asset.
690
+ *
691
+ * @param WPSEO_Admin_Asset $asset The asset to determine the URL for.
692
+ * @param string $type The type of asset. Usually JS or CSS.
693
+ *
694
+ * @return string The URL of the asset.
695
+ */
696
+ protected function get_url( WPSEO_Admin_Asset $asset, $type ) {
697
+ $scheme = wp_parse_url( $asset->get_src(), PHP_URL_SCHEME );
698
+ if ( in_array( $scheme, array( 'http', 'https' ), true ) ) {
699
+ return $asset->get_src();
700
+ }
701
+
702
+ return $this->asset_location->get_url( $asset, $type );
703
+ }
704
  }
admin/class-admin-init.php CHANGED
@@ -92,18 +92,6 @@ class WPSEO_Admin_Init {
92
  $this->asset_manager->enqueue_style( 'dismissible' );
93
  }
94
 
95
- /**
96
- * Helper to verify if the current user has already seen the about page for the current version
97
- *
98
- * @return bool
99
- */
100
- private function seen_about() {
101
- $seen_about_version = substr( get_user_meta( get_current_user_id(), 'wpseo_seen_about_version', true ), 0, 3 );
102
- $last_minor_version = substr( WPSEO_VERSION, 0, 3 );
103
-
104
- return version_compare( $seen_about_version, $last_minor_version, '>=' );
105
- }
106
-
107
  /**
108
  * Notify about the default tagline if the user hasn't changed it
109
  */
@@ -624,7 +612,7 @@ class WPSEO_Admin_Init {
624
  global $wp_filter;
625
 
626
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
627
- return false;
628
  }
629
 
630
  // WordPress hooks that have been deprecated since a Yoast SEO version.
@@ -688,17 +676,6 @@ class WPSEO_Admin_Init {
688
  }
689
  }
690
 
691
- /**
692
- * Check if there is a dismiss notice action.
693
- *
694
- * @param string $notice_name The name of the notice to dismiss.
695
- *
696
- * @return bool
697
- */
698
- private function dismiss_notice( $notice_name ) {
699
- return filter_input( INPUT_GET, $notice_name ) === '1' && wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), $notice_name );
700
- }
701
-
702
  /**
703
  * Check if the permalink uses %postname%
704
  *
@@ -718,8 +695,8 @@ class WPSEO_Admin_Init {
718
  $warning = esc_html__( 'WARNING:', 'wordpress-seo' );
719
  /* translators: %1$s and %2$s expand to <i> items to emphasize the word in the middle. */
720
  $message = esc_html__( 'Changing your permalinks settings can seriously impact your search engine visibility. It should almost %1$s never %2$s be done on a live website.', 'wordpress-seo' );
721
- $link = esc_html__( 'Learn about why permalinks are important for SEO.', 'wordpress-seo' );
722
- $url = WPSEO_Shortlinker::get( 'https://yoa.st/why-permalinks/' );
723
 
724
  echo '<div class="notice notice-warning"><p><strong>' . $warning . '</strong><br>' . sprintf( $message, '<i>', '</i>' ) . '<br><a href="' . $url . '" target="_blank">' . $link . '</a></p></div>';
725
  }
92
  $this->asset_manager->enqueue_style( 'dismissible' );
93
  }
94
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  /**
96
  * Notify about the default tagline if the user hasn't changed it
97
  */
612
  global $wp_filter;
613
 
614
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
615
+ return;
616
  }
617
 
618
  // WordPress hooks that have been deprecated since a Yoast SEO version.
676
  }
677
  }
678
 
 
 
 
 
 
 
 
 
 
 
 
679
  /**
680
  * Check if the permalink uses %postname%
681
  *
695
  $warning = esc_html__( 'WARNING:', 'wordpress-seo' );
696
  /* translators: %1$s and %2$s expand to <i> items to emphasize the word in the middle. */
697
  $message = esc_html__( 'Changing your permalinks settings can seriously impact your search engine visibility. It should almost %1$s never %2$s be done on a live website.', 'wordpress-seo' );
698
+ $link = esc_html__( 'Learn about why permalinks are important for SEO.', 'wordpress-seo' );
699
+ $url = WPSEO_Shortlinker::get( 'https://yoa.st/why-permalinks/' );
700
 
701
  echo '<div class="notice notice-warning"><p><strong>' . $warning . '</strong><br>' . sprintf( $message, '<i>', '</i>' ) . '<br><a href="' . $url . '" target="_blank">' . $link . '</a></p></div>';
702
  }
admin/class-admin-utils.php CHANGED
@@ -67,6 +67,18 @@ class WPSEO_Admin_Utils {
67
  );
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  /* ********************* DEPRECATED METHODS ********************* */
71
 
72
  /**
67
  );
68
  }
69
 
70
+ /**
71
+ * Gets a visually hidden accessible message for links that open in a new browser tab.
72
+ *
73
+ * @return string The visually hidden accessible message.
74
+ */
75
+ public static function get_new_tab_message() {
76
+ return sprintf(
77
+ '<span class="screen-reader-text">%s</span>',
78
+ esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' )
79
+ );
80
+ }
81
+
82
  /* ********************* DEPRECATED METHODS ********************* */
83
 
84
  /**
admin/class-admin.php CHANGED
@@ -90,6 +90,8 @@ class WPSEO_Admin {
90
 
91
  $this->set_upsell_notice();
92
 
 
 
93
  $this->initialize_cornerstone_content();
94
 
95
  if ( WPSEO_Utils::is_plugin_network_active() ) {
@@ -103,7 +105,8 @@ class WPSEO_Admin {
103
  $integrations[] = new WPSEO_Admin_Media_Purge_Notification();
104
  $integrations[] = new WPSEO_Admin_Gutenberg_Compatibility_Notification();
105
  $integrations[] = new WPSEO_Expose_Shortlinks();
106
- $integrations = array_merge( $integrations, $this->initialize_seo_links() );
 
107
 
108
  /** @var WPSEO_WordPress_Integration $integration */
109
  foreach ( $integrations as $integration ) {
@@ -331,14 +334,17 @@ class WPSEO_Admin {
331
 
332
  /**
333
  * Loads the cornerstone filter.
 
 
334
  */
335
  protected function initialize_cornerstone_content() {
336
  if ( ! WPSEO_Options::get( 'enable_cornerstone_content' ) ) {
337
- return;
338
  }
339
 
340
- $cornerstone_filter = new WPSEO_Cornerstone_Filter();
341
- $cornerstone_filter->register_hooks();
 
342
  }
343
 
344
  /**
90
 
91
  $this->set_upsell_notice();
92
 
93
+ $this->check_php_version();
94
+
95
  $this->initialize_cornerstone_content();
96
 
97
  if ( WPSEO_Utils::is_plugin_network_active() ) {
105
  $integrations[] = new WPSEO_Admin_Media_Purge_Notification();
106
  $integrations[] = new WPSEO_Admin_Gutenberg_Compatibility_Notification();
107
  $integrations[] = new WPSEO_Expose_Shortlinks();
108
+ $integrations[] = new WPSEO_Recalibration_Beta();
109
+ $integrations = array_merge( $integrations, $this->initialize_seo_links(), $this->initialize_cornerstone_content() );
110
 
111
  /** @var WPSEO_WordPress_Integration $integration */
112
  foreach ( $integrations as $integration ) {
334
 
335
  /**
336
  * Loads the cornerstone filter.
337
+ *
338
+ * @return WPSEO_WordPress_Integration[] The integrations to initialize.
339
  */
340
  protected function initialize_cornerstone_content() {
341
  if ( ! WPSEO_Options::get( 'enable_cornerstone_content' ) ) {
342
+ return array();
343
  }
344
 
345
+ return array(
346
+ 'cornerstone_filter' => new WPSEO_Cornerstone_Filter(),
347
+ );
348
  }
349
 
350
  /**
admin/class-expose-shortlinks.php CHANGED
@@ -32,6 +32,7 @@ class WPSEO_Expose_Shortlinks implements WPSEO_WordPress_Integration {
32
  'shortlinks.upsell.metabox.additional_button' => 'https://yoa.st/add-keywords-metabox',
33
  'shortlinks.readability_analysis_info' => 'https://yoa.st/readability-analysis',
34
  'shortlinks.activate_premium_info' => 'https://yoa.st/activate-subscription',
 
35
  );
36
 
37
  /**
32
  'shortlinks.upsell.metabox.additional_button' => 'https://yoa.st/add-keywords-metabox',
33
  'shortlinks.readability_analysis_info' => 'https://yoa.st/readability-analysis',
34
  'shortlinks.activate_premium_info' => 'https://yoa.st/activate-subscription',
35
+ 'shortlinks.recalibration_beta_metabox' => 'https://yoa.st/recalibration-beta-metabox',
36
  );
37
 
38
  /**
admin/class-gutenberg-compatibility.php CHANGED
@@ -13,12 +13,12 @@ class WPSEO_Gutenberg_Compatibility {
13
  /**
14
  * The currently released version of Gutenberg.
15
  */
16
- const CURRENT_RELEASE = '4.4.0';
17
 
18
  /**
19
  * The minimally supported version of Gutenberg by the plugin.
20
  */
21
- const MINIMUM_SUPPORTED = '4.4.0';
22
 
23
  /**
24
  * @var string
13
  /**
14
  * The currently released version of Gutenberg.
15
  */
16
+ const CURRENT_RELEASE = '4.7.0';
17
 
18
  /**
19
  * The minimally supported version of Gutenberg by the plugin.
20
  */
21
+ const MINIMUM_SUPPORTED = '4.7.0';
22
 
23
  /**
24
  * @var string
admin/class-premium-upsell-admin-block.php CHANGED
@@ -60,7 +60,7 @@ class WPSEO_Premium_Upsell_Admin_Block {
60
  $dismiss_msg = sprintf( __( 'Dismiss %s upgrade notice', 'wordpress-seo' ), 'Yoast SEO Premium' );
61
 
62
  /* translators: %s expands to Yoast SEO Premium */
63
- $button_text = esc_html( sprintf( __( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) );
64
  $button_text .= '<span class="screen-reader-text">' . esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>' .
65
  '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>';
66
 
60
  $dismiss_msg = sprintf( __( 'Dismiss %s upgrade notice', 'wordpress-seo' ), 'Yoast SEO Premium' );
61
 
62
  /* translators: %s expands to Yoast SEO Premium */
63
+ $button_text = esc_html( sprintf( __( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) );
64
  $button_text .= '<span class="screen-reader-text">' . esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>' .
65
  '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>';
66
 
admin/class-recalibration-beta.php ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ * @since 9.3.0
7
+ */
8
+
9
+ /**
10
+ * Holds the logic for the recalibration beta.
11
+ */
12
+ class WPSEO_Recalibration_Beta implements WPSEO_WordPress_Integration {
13
+
14
+ /**
15
+ * @var string Name of the options.
16
+ */
17
+ protected $option_name = 'recalibration_beta';
18
+
19
+ /**
20
+ * @var string The read more URL.
21
+ */
22
+ protected $read_more_url = 'https://yoa.st/recalibration-beta-explanation';
23
+
24
+ /**
25
+ * Shows the feature toggle.
26
+ *
27
+ * @codeCoverageIgnore Reason: most output is html.
28
+ *
29
+ * @return void
30
+ */
31
+ public function show_feature_toggle() {
32
+ // Temporary disable the toggle.
33
+ return;
34
+
35
+ $values = array(
36
+ 'on' => __( 'On', 'wordpress-seo' ),
37
+ 'off' => __( 'Off', 'wordpress-seo' ),
38
+ );
39
+
40
+ echo '<div class="switch-container">';
41
+ echo '<fieldset id="', esc_attr( $this->option_name ), '" class="fieldset-switch-toggle">';
42
+ echo '<legend><strong>', __( 'Get an even better analysis', 'wordpress-seo' ), '</strong></legend>';
43
+ echo '<p class="clear">';
44
+ printf(
45
+ /* translators: 1: link opening tag, 2: link closing tag, 3: strong opening tag, 4: strong closing tag */
46
+ esc_html__(
47
+ 'We have %1$srecalibrated our analysis%2$s. With the new analysis, we will get even closer to how Google sees your website. It would be %3$sawesome%4$s if you would like to %3$sbeta test this feature%4$s for us!',
48
+ 'wordpress-seo'
49
+ ),
50
+ '<a href="' . esc_url( WPSEO_Shortlinker::get( $this->read_more_url ) ) . '" target="_blank">',
51
+ '</a>',
52
+ '<strong>',
53
+ '</strong>'
54
+ );
55
+ echo '</p>';
56
+
57
+ echo '<div class="switch-toggle switch-candy switch-yoast-seo">';
58
+
59
+ foreach ( $values as $key => $value ) {
60
+ printf(
61
+ '<input type="radio" id="%1$s" name="%2$s" value="%3$s" %4$s /><label for="%1$s">%5$s</label>',
62
+ esc_attr( $this->option_name . '-' . $key ),
63
+ 'wpseo[' . esc_attr( $this->option_name ) . ']',
64
+ esc_attr( $key ),
65
+ checked( $this->get_option_value( self::is_enabled() ), esc_attr( $key ), false ),
66
+ esc_html( $value )
67
+ );
68
+ }
69
+ echo '<a></a></div>';
70
+
71
+ echo '<p class="clear"><br/>';
72
+ esc_html_e(
73
+ 'Simply switch the toggle to "on" and you\'ll be able to use the recalibrated analysis. At the same time, we\'ll add you to our specific mailing list. We\'ll only email you about your experiences with this recalibration!',
74
+ 'wordpress-seo'
75
+ );
76
+ echo '</p>';
77
+ echo '</fieldset><div class="clear"></div></div>' . PHP_EOL . PHP_EOL;
78
+ }
79
+
80
+ /**
81
+ * Registers the hook to catch option change.
82
+ *
83
+ * @codeCoverageIgnore Reason: because it calls a WordPress function.
84
+ *
85
+ * @return void
86
+ */
87
+ public function register_hooks() {
88
+ // Temporary disable the toggle.
89
+ return;
90
+
91
+ add_action( 'update_option_wpseo', array( $this, 'update_option' ), 10, 2 );
92
+
93
+ $notification = new WPSEO_Recalibration_Beta_Notification();
94
+ $notification->register_hooks();
95
+ }
96
+
97
+ /**
98
+ * Compares the logic between old and new option value and send the request.
99
+ *
100
+ * @param mixed $old_value The old option value.
101
+ * @param mixed $new_value The new option value.
102
+ *
103
+ * @return void
104
+ */
105
+ public function update_option( $old_value, $new_value ) {
106
+ $old_option_value = false;
107
+ if ( isset( $old_value[ $this->option_name ] ) ) {
108
+ $old_option_value = $old_value[ $this->option_name ];
109
+ }
110
+
111
+ $new_option_value = false;
112
+ if ( isset( $new_value[ $this->option_name ] ) ) {
113
+ $new_option_value = $new_value[ $this->option_name ];
114
+ }
115
+
116
+ if ( $old_option_value === $new_option_value ) {
117
+ return;
118
+ }
119
+
120
+ if ( $new_option_value === true ) {
121
+ $this->subscribe_newsletter();
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Checks if the recalibration beta has been enabled.
127
+ *
128
+ * @codeCoverageIgnore Reason: It calls a dependency.
129
+ *
130
+ * @return bool True whether the beta has been enabled.
131
+ */
132
+ public static function is_enabled() {
133
+ return WPSEO_Options::get( 'recalibration_beta' );
134
+ }
135
+
136
+ /**
137
+ * Checks if the user has a mailinglist subscription.
138
+ *
139
+ * @codeCoverageIgnore Reason: because it calls a WordPress function.
140
+ *
141
+ * The mailinglist subscription value will set to true when the beta is set
142
+ * to enabled. This value stays true, so it's a good indicator that the user
143
+ * tried the beta.
144
+ *
145
+ * @return bool True whether the user has a subscription.
146
+ */
147
+ public function has_mailinglist_subscription() {
148
+ return (bool) get_option( 'wpseo_recalibration_beta_mailinglist_subscription', false );
149
+ }
150
+
151
+ /**
152
+ * Retrieves the option value based on the current setting.
153
+ *
154
+ * @param bool $is_enabled Is the option enabled.
155
+ *
156
+ * @return string On when is enabled, off when not.
157
+ */
158
+ protected function get_option_value( $is_enabled ) {
159
+ if ( $is_enabled === true ) {
160
+ return 'on';
161
+ }
162
+
163
+ return 'off';
164
+ }
165
+
166
+ /**
167
+ * Subscribes to the newsletter.
168
+ *
169
+ * @return void
170
+ */
171
+ protected function subscribe_newsletter() {
172
+ // Temporary disable the toggle.
173
+ return;
174
+
175
+ if ( $this->has_mailinglist_subscription() ) {
176
+ return;
177
+ }
178
+
179
+ try {
180
+ $this->do_request(
181
+ 'https://my.yoast.com/api/customers/newsletter/recalibration/subscribe',
182
+ array(
183
+ 'email' => get_option( 'admin_email' ),
184
+ 'firstName' => get_option( 'blogname' ),
185
+ 'lastName' => '',
186
+ )
187
+ );
188
+
189
+ $this->set_mailinglist_subscription();
190
+ }
191
+ catch ( Requests_Exception_HTTP $e ) {
192
+ // Intentionally left blank. @todo We should offer this to a logger.
193
+ return;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Performs a request to the given url.
199
+ *
200
+ * @codeCoverageIgnore Reason: because it contains WordPress functions.
201
+ *
202
+ * @param string $url The request url.
203
+ * @param array $body The request body.
204
+ *
205
+ * @return void
206
+ *
207
+ * @throws Requests_Exception_HTTP When request has failed.
208
+ */
209
+ protected function do_request( $url, $body ) {
210
+ $response = wp_remote_post(
211
+ $url,
212
+ array(
213
+ 'body' => $body,
214
+ )
215
+ );
216
+
217
+ if ( is_wp_error( $response ) ) {
218
+ throw new Requests_Exception_HTTP( $response->get_error_message() );
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Sets the mailing list subscription value to true.
224
+ *
225
+ * @codeCoverageIgnore Reason: because it calls a WordPress function.
226
+ *
227
+ * @return void
228
+ */
229
+ protected function set_mailinglist_subscription() {
230
+ update_option( 'wpseo_recalibration_beta_mailinglist_subscription', true );
231
+ }
232
+ }
admin/class-yoast-form.php CHANGED
@@ -630,11 +630,11 @@ class Yoast_Form {
630
 
631
  printf( '<div class="%s">', esc_attr( 'switch-container' . $help_class ) );
632
  echo '<fieldset id="', $var_esc, '" class="fieldset-switch-toggle"><legend>', $label, '</legend>', $help;
 
633
  echo $this->get_disabled_note( $var );
634
  echo '<div class="switch-toggle switch-candy switch-yoast-seo">';
635
 
636
  foreach ( $values as $key => $value ) {
637
- $screen_reader_text = '';
638
  $screen_reader_text_html = '';
639
 
640
  if ( is_array( $value ) ) {
@@ -649,7 +649,7 @@ class Yoast_Form {
649
  '<label for="', $for, '">', esc_html( $value ), $screen_reader_text_html,'</label>';
650
  }
651
 
652
- echo '<a></a></div></fieldset><div class="clear"></div></div>' . "\n\n";
653
  }
654
 
655
  /**
630
 
631
  printf( '<div class="%s">', esc_attr( 'switch-container' . $help_class ) );
632
  echo '<fieldset id="', $var_esc, '" class="fieldset-switch-toggle"><legend>', $label, '</legend>', $help;
633
+
634
  echo $this->get_disabled_note( $var );
635
  echo '<div class="switch-toggle switch-candy switch-yoast-seo">';
636
 
637
  foreach ( $values as $key => $value ) {
 
638
  $screen_reader_text_html = '';
639
 
640
  if ( is_array( $value ) ) {
649
  '<label for="', $for, '">', esc_html( $value ), $screen_reader_text_html,'</label>';
650
  }
651
 
652
+ echo '<a></a></div></fieldset><div class="clear"></div></div>' . PHP_EOL . PHP_EOL;
653
  }
654
 
655
  /**
admin/endpoints/class-endpoint-file-size.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Endpoints
6
+ */
7
+
8
+ /**
9
+ * Represents an implementation of the WPSEO_Endpoint interface to register one or multiple endpoints.
10
+ */
11
+ class WPSEO_Endpoint_File_Size implements WPSEO_Endpoint {
12
+
13
+ const REST_NAMESPACE = 'yoast/v1';
14
+ const ENDPOINT_SINGULAR = 'file_size';
15
+
16
+ const CAPABILITY_RETRIEVE = 'manage_options';
17
+
18
+ /**
19
+ * @var WPSEO_File_Size_Service The service provider.
20
+ */
21
+ private $service;
22
+
23
+ /**
24
+ * Sets the service provider.
25
+ *
26
+ * @param WPSEO_File_Size_Service $service The service provider.
27
+ */
28
+ public function __construct( WPSEO_File_Size_Service $service ) {
29
+ $this->service = $service;
30
+ }
31
+
32
+ /**
33
+ * Registers the routes for the endpoints.
34
+ *
35
+ * @return void
36
+ */
37
+ public function register() {
38
+ register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_SINGULAR, array(
39
+ 'methods' => 'GET',
40
+ 'args' => array(
41
+ 'url' => array(
42
+ 'required' => true,
43
+ 'type' => 'string',
44
+ 'description' => 'The url to retrieve',
45
+ ),
46
+ ),
47
+ 'callback' => array(
48
+ $this->service,
49
+ 'get',
50
+ ),
51
+ 'permission_callback' => array(
52
+ $this,
53
+ 'can_retrieve_data',
54
+ ),
55
+ ) );
56
+ }
57
+
58
+ /**
59
+ * Determines whether or not data can be retrieved for the registered endpoints.
60
+ *
61
+ * @return bool Whether or not data can be retrieved.
62
+ */
63
+ public function can_retrieve_data() {
64
+ return current_user_can( self::CAPABILITY_RETRIEVE );
65
+ }
66
+ }
admin/exceptions/class-file-size-exception.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Exceptions
6
+ */
7
+
8
+ /**
9
+ * Represents named methods for exceptions.
10
+ */
11
+ class WPSEO_File_Size_Exception extends Exception {
12
+
13
+ /**
14
+ * Gets the exception for an externally hosted file.
15
+ *
16
+ * @param string $file_url The file url.
17
+ *
18
+ * @return WPSEO_File_Size_Exception Instance of the exception.
19
+ */
20
+ public static function externally_hosted( $file_url ) {
21
+ $message = sprintf(
22
+ /* translators: %1$s expands to the requested url */
23
+ __( 'Cannot get the size of %1$s because it is hosted externally.', 'wordpress-seo' ),
24
+ $file_url
25
+ );
26
+
27
+ return new self( $message );
28
+ }
29
+
30
+ /**
31
+ * Gets the exception for when a unknown error occurs.
32
+ *
33
+ * @param string $file_url The file url.
34
+ *
35
+ * @return WPSEO_File_Size_Exception Instance of the exception.
36
+ */
37
+ public static function unknown_error( $file_url ) {
38
+ $message = sprintf(
39
+ /* translators: %1$s expands to the requested url */
40
+ __( 'Cannot get the size of %1$s because of unknown reasons.', 'wordpress-seo' ),
41
+ $file_url
42
+ );
43
+
44
+ return new self( $message );
45
+ }
46
+ }
admin/filters/class-cornerstone-filter.php CHANGED
@@ -24,6 +24,7 @@ class WPSEO_Cornerstone_Filter extends WPSEO_Abstract_Post_Filter {
24
  parent::register_hooks();
25
 
26
  add_filter( 'wpseo_cornerstone_post_types', array( 'WPSEO_Post_Type', 'filter_attachment_post_type' ) );
 
27
  }
28
 
29
  /**
@@ -55,6 +56,26 @@ class WPSEO_Cornerstone_Filter extends WPSEO_Abstract_Post_Filter {
55
  return $where;
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * Returns the label for this filter.
60
  *
24
  parent::register_hooks();
25
 
26
  add_filter( 'wpseo_cornerstone_post_types', array( 'WPSEO_Post_Type', 'filter_attachment_post_type' ) );
27
+ add_filter( 'wpseo_cornerstone_post_types', array( $this, 'filter_metabox_disabled' ) );
28
  }
29
 
30
  /**
56
  return $where;
57
  }
58
 
59
+ /**
60
+ * Filters the post types that have the metabox disabled.
61
+ *
62
+ * @param array $post_types The post types to filter.
63
+ *
64
+ * @return array The filtered post types.
65
+ */
66
+ public function filter_metabox_disabled( $post_types ) {
67
+ $filtered_post_types = array();
68
+ foreach ( $post_types as $post_type_key => $post_type ) {
69
+ if ( ! WPSEO_Post_Type::has_metabox_enabled( $post_type_key ) ) {
70
+ continue;
71
+ }
72
+
73
+ $filtered_post_types[ $post_type_key ] = $post_type;
74
+ }
75
+
76
+ return $filtered_post_types;
77
+ }
78
+
79
  /**
80
  * Returns the label for this filter.
81
  *
admin/formatter/class-metabox-formatter.php CHANGED
@@ -67,14 +67,15 @@ class WPSEO_Metabox_Formatter {
67
  'isRtl' => is_rtl(),
68
  'addKeywordUpsell' => $this->get_add_keyword_upsell_translations(),
69
  'wordFormRecognitionActive' => ( WPSEO_Utils::get_language( get_locale() ) === 'en' ),
 
70
 
71
  /**
72
  * Filter to determine if the markers should be enabled or not.
73
  *
74
  * @param bool $showMarkers Should the markers being enabled. Default = true.
75
  */
76
- 'show_markers' => apply_filters( 'wpseo_enable_assessment_markers', true ),
77
- 'publish_box' => array(
78
  'labels' => array(
79
  'content' => array(
80
  'na' => sprintf(
@@ -130,8 +131,8 @@ class WPSEO_Metabox_Formatter {
130
  ),
131
  ),
132
  ),
133
- 'markdownEnabled' => $this->is_markdown_enabled(),
134
- 'analysisHeadingTitle' => __( 'Analysis', 'wordpress-seo' ),
135
  );
136
  }
137
 
67
  'isRtl' => is_rtl(),
68
  'addKeywordUpsell' => $this->get_add_keyword_upsell_translations(),
69
  'wordFormRecognitionActive' => ( WPSEO_Utils::get_language( get_locale() ) === 'en' ),
70
+ 'recalibrationBetaActive' => WPSEO_Recalibration_Beta::is_enabled(),
71
 
72
  /**
73
  * Filter to determine if the markers should be enabled or not.
74
  *
75
  * @param bool $showMarkers Should the markers being enabled. Default = true.
76
  */
77
+ 'show_markers' => apply_filters( 'wpseo_enable_assessment_markers', true ),
78
+ 'publish_box' => array(
79
  'labels' => array(
80
  'content' => array(
81
  'na' => sprintf(
131
  ),
132
  ),
133
  ),
134
+ 'markdownEnabled' => $this->is_markdown_enabled(),
135
+ 'analysisHeadingTitle' => __( 'Analysis', 'wordpress-seo' ),
136
  );
137
  }
138
 
admin/google_search_console/class-gsc-category-filters.php CHANGED
@@ -156,11 +156,11 @@ class WPSEO_GSC_Category_Filters {
156
  )
157
  );
158
 
159
- $class = 'gsc_category';
160
  $aria_current = '';
161
 
162
  if ( $this->category === $category ) {
163
- $class .= ' current';
164
  $aria_current = ' aria-current="page"';
165
  }
166
 
156
  )
157
  );
158
 
159
+ $class = 'gsc_category';
160
  $aria_current = '';
161
 
162
  if ( $this->category === $category ) {
163
+ $class .= ' current';
164
  $aria_current = ' aria-current="page"';
165
  }
166
 
admin/my-yoast-proxy.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * This file acts as a proxy. It will read external files and serves the like they are located locally.
6
+ *
7
+ * @package WPSEO\Admin
8
+ */
9
+
10
+ switch ( filter_input( INPUT_GET, 'file', FILTER_SANITIZE_STRING ) ) {
11
+ case 'research-webworker':
12
+ $my_yoast_url = 'https://my.yoast.com/api/downloads/file/analysis-worker';
13
+ $my_yoast_url_content_type = 'text/javascript; charset=UTF-8';
14
+ break;
15
+ }
16
+
17
+ if ( empty( $my_yoast_url ) ) {
18
+ exit;
19
+ }
20
+
21
+ header( 'Content-Type: ' . $my_yoast_url_content_type );
22
+ header( 'Cache-Control: max-age=86400' );
23
+
24
+ readfile( $my_yoast_url );
25
+
26
+ exit;
admin/notifiers/class-post-type-archive-notification-handler.php CHANGED
@@ -8,14 +8,7 @@
8
  /**
9
  * Represents the logic for showing the post type archive notification.
10
  */
11
- class WPSEO_Post_Type_Archive_Notification_Handler implements WPSEO_Listener, WPSEO_Notification_Handler {
12
-
13
- /**
14
- * The identifier for the notification.
15
- *
16
- * @var string
17
- */
18
- protected $notification_identifier = 'post-type-archive-notification';
19
 
20
  /**
21
  * Defaults for the title option.
@@ -25,70 +18,43 @@ class WPSEO_Post_Type_Archive_Notification_Handler implements WPSEO_Listener, WP
25
  protected $option_defaults = array();
26
 
27
  /**
28
- * Listens to an argument in the request URL and triggers an action.
 
 
29
  *
30
  * @return void
31
  */
32
- public function listen() {
33
- if ( $this->get_listener_value() !== $this->notification_identifier ) {
34
- return;
35
- }
36
-
37
- $this->set_dismissal_state();
38
- $this->redirect_to_dashboard();
39
  }
40
 
41
  /**
42
- * Adds the notification if applicable, otherwise removes it.
43
- *
44
- * @param Yoast_Notification_Center $notification_center The notification center object.
45
  *
46
- * @return void
47
  */
48
- public function handle( Yoast_Notification_Center $notification_center ) {
49
- if ( ! $this->is_applicable() ) {
50
- $notification_center->remove_notification_by_id( 'wpseo-' . $this->notification_identifier );
51
-
52
- return;
53
  }
54
 
55
- $notification = $this->get_notification( $this->get_post_types() );
56
- $notification_center->add_notification( $notification );
57
- }
58
-
59
- /**
60
- * Retrevies the value where listener is listening for.
61
- *
62
- * @return string The listener value.
63
- *
64
- * @codeCoverageIgnore
65
- */
66
- protected function get_listener_value() {
67
- return filter_input( INPUT_GET, 'yoast_dismiss' );
68
- }
69
 
70
- /**
71
- * Redirects the user back to the dashboard.
72
- *
73
- * @return void
74
- *
75
- * @codeCoverageIgnore
76
- */
77
- protected function redirect_to_dashboard() {
78
- wp_safe_redirect( admin_url( 'admin.php?page=wpseo_dashboard' ) );
79
- exit;
80
  }
81
 
82
  /**
83
  * Returns the notification.
84
  *
85
- * @param array $post_types The post types that needs an other check.
86
- *
87
  * @return Yoast_Notification The notification for the notification center.
88
  *
89
  * @codeCoverageIgnore
90
  */
91
- protected function get_notification( array $post_types ) {
 
 
92
  $message = esc_html__(
93
  'We\'ve recently improved the functionality of the Search Appearance settings. Unfortunately, we\'ve discovered that for some edge-cases, saving the settings for specific post type archives might have gone wrong.',
94
  'wordpress-seo'
@@ -124,45 +90,6 @@ class WPSEO_Post_Type_Archive_Notification_Handler implements WPSEO_Listener, WP
124
  return new Yoast_Notification( $message, $notification_options );
125
  }
126
 
127
- /**
128
- * Checks if the noticiation should be shown.
129
- *
130
- * @return bool True when applicable.
131
- */
132
- protected function is_applicable() {
133
- if ( $this->is_notice_dismissed() ) {
134
- return false;
135
- }
136
-
137
- if ( $this->is_new_install() ) {
138
- return false;
139
- }
140
-
141
- return $this->get_post_types() !== array();
142
- }
143
-
144
- /**
145
- * Checks whether the notification has been dismissed.
146
- *
147
- * @return bool True when notification is dismissed.
148
- *
149
- * @codeCoverageIgnore
150
- */
151
- protected function is_notice_dismissed() {
152
- return get_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true ) === '1';
153
- }
154
-
155
- /**
156
- * Dismisses the notification.
157
- *
158
- * @return void
159
- *
160
- * @codeCoverageIgnore
161
- */
162
- protected function set_dismissal_state() {
163
- update_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true );
164
- }
165
-
166
  /**
167
  * Checks if the first activation is done before the release of 7.9.
168
  *
@@ -206,7 +133,7 @@ class WPSEO_Post_Type_Archive_Notification_Handler implements WPSEO_Listener, WP
206
  *
207
  * @codeCoverageIgnore
208
  */
209
- public function filter_woocommerce_product_type( $post_types ) {
210
  if ( WPSEO_Utils::is_woocommerce_active() ) {
211
  unset( $post_types['product'] );
212
  }
8
  /**
9
  * Represents the logic for showing the post type archive notification.
10
  */
11
+ class WPSEO_Post_Type_Archive_Notification_Handler extends WPSEO_Dismissible_Notification {
 
 
 
 
 
 
 
12
 
13
  /**
14
  * Defaults for the title option.
18
  protected $option_defaults = array();
19
 
20
  /**
21
+ * Sets the notification identifier.
22
+ *
23
+ * @codeCoverageIgnore
24
  *
25
  * @return void
26
  */
27
+ public function __construct() {
28
+ $this->notification_identifier = 'post-type-archive-notification';
 
 
 
 
 
29
  }
30
 
31
  /**
32
+ * Checks if the notice should be shown.
 
 
33
  *
34
+ * @return bool True when applicable.
35
  */
36
+ protected function is_applicable() {
37
+ if ( $this->is_notice_dismissed() ) {
38
+ return false;
 
 
39
  }
40
 
41
+ if ( $this->is_new_install() ) {
42
+ return false;
43
+ }
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ return $this->get_post_types() !== array();
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
  /**
49
  * Returns the notification.
50
  *
 
 
51
  * @return Yoast_Notification The notification for the notification center.
52
  *
53
  * @codeCoverageIgnore
54
  */
55
+ protected function get_notification() {
56
+ $post_types = $this->get_post_types();
57
+
58
  $message = esc_html__(
59
  'We\'ve recently improved the functionality of the Search Appearance settings. Unfortunately, we\'ve discovered that for some edge-cases, saving the settings for specific post type archives might have gone wrong.',
60
  'wordpress-seo'
90
  return new Yoast_Notification( $message, $notification_options );
91
  }
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  /**
94
  * Checks if the first activation is done before the release of 7.9.
95
  *
133
  *
134
  * @codeCoverageIgnore
135
  */
136
+ protected function filter_woocommerce_product_type( $post_types ) {
137
  if ( WPSEO_Utils::is_woocommerce_active() ) {
138
  unset( $post_types['product'] );
139
  }
admin/notifiers/class-recalibration-beta.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Notifiers
6
+ */
7
+
8
+ /**
9
+ * Represents the logic for showing recalibration beta notice.
10
+ */
11
+ class WPSEO_Recalibration_Beta_Notification implements WPSEO_WordPress_Integration {
12
+
13
+ /**
14
+ * The name of the notifier.
15
+ *
16
+ * @var string
17
+ */
18
+ protected $notification_identifier = 'recalibration-meta-notification';
19
+
20
+ /**
21
+ * Registers all hooks to WordPress
22
+ *
23
+ * @codeCoverageIgnore
24
+ *
25
+ * @return void
26
+ */
27
+ public function register_hooks() {
28
+ add_action( 'admin_init', array( $this, 'handle_notice' ), 15 );
29
+ }
30
+
31
+ /**
32
+ * Shows the notification when applicable.
33
+ *
34
+ * @return void.
35
+ */
36
+ public function handle_notice() {
37
+ $recalibration_beta = new WPSEO_Recalibration_Beta();
38
+ if ( $this->is_applicable( WPSEO_Recalibration_Beta::is_enabled(), $recalibration_beta->has_mailinglist_subscription() ) ) {
39
+ $this->get_notification_center()->add_notification(
40
+ $this->get_notification()
41
+ );
42
+
43
+ return;
44
+ }
45
+
46
+ $this->get_notification_center()->remove_notification_by_id( 'wpseo-' . $this->notification_identifier );
47
+ }
48
+
49
+ /**
50
+ * Checks if the beta is enabled.
51
+ *
52
+ * @param bool $is_beta_enabled Checks if the beta has been enabled.
53
+ * @param bool $was_ever_enabled Checks if the beta was ever enabled.
54
+ *
55
+ * @return bool Whether the beta is enabled or not.
56
+ */
57
+ protected function is_applicable( $is_beta_enabled, $was_ever_enabled ) {
58
+ if ( $was_ever_enabled ) {
59
+ return false;
60
+ }
61
+
62
+ return ! $is_beta_enabled;
63
+ }
64
+
65
+ /**
66
+ * Returns the notification.
67
+ *
68
+ * @return Yoast_Notification The notification for the notification center.
69
+ *
70
+ * @codeCoverageIgnore
71
+ */
72
+ protected function get_notification() {
73
+ $message = sprintf(
74
+ esc_html__(
75
+ /* translators: 1: link opening tag to the features page, 2: link closing tag, 3: Link to KB article, 4: expands to Yoast SEO */
76
+ 'We\'d love for you to try our new and improved %4$s analysis! Use the toggle on the %1$sFeatures tab%2$s in your %4$s settings. %3$sRead more about the new analysis%2$s.',
77
+ 'wordpress-seo'
78
+ ),
79
+ '<a href="#top#features" onclick="jQuery(\'#features-tab\').click()">',
80
+ '</a>',
81
+ '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/recalibration-beta-notice' ) . '" target="_blank">',
82
+ 'Yoast SEO'
83
+ );
84
+
85
+ $notification_options = array(
86
+ 'type' => Yoast_Notification::WARNING,
87
+ 'id' => 'wpseo-' . $this->notification_identifier,
88
+ 'priority' => 1.0,
89
+ 'capabilities' => 'wpseo_manage_options',
90
+ );
91
+
92
+ return new Yoast_Notification( $message, $notification_options );
93
+ }
94
+
95
+ /**
96
+ * Retrieves an instance of the notification center.
97
+ *
98
+ * @codeCoverageIgnore
99
+ *
100
+ * @return Yoast_Notification_Center Instance of the notification center.
101
+ */
102
+ protected function get_notification_center() {
103
+ return Yoast_Notification_Center::get();
104
+ }
105
+ }
admin/notifiers/dismissible-notification.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Notifiers
6
+ */
7
+
8
+ /**
9
+ * Abstract class representing a dismissible notification.
10
+ */
11
+ abstract class WPSEO_Dismissible_Notification implements WPSEO_Listener, WPSEO_Notification_Handler {
12
+
13
+ /**
14
+ * The identifier for the notification.
15
+ *
16
+ * @var string
17
+ */
18
+ protected $notification_identifier = '';
19
+
20
+ /**
21
+ * Retrieves instance of a notification.
22
+ *
23
+ * @return Yoast_Notification The notification.
24
+ */
25
+ abstract protected function get_notification();
26
+
27
+ /**
28
+ * Listens to an argument in the request URL and triggers an action.
29
+ *
30
+ * @return void
31
+ */
32
+ public function listen() {
33
+ if ( $this->get_listener_value() !== $this->notification_identifier ) {
34
+ return;
35
+ }
36
+
37
+ $this->dismiss();
38
+ }
39
+
40
+ /**
41
+ * Adds the notification if applicable, otherwise removes it.
42
+ *
43
+ * @param Yoast_Notification_Center $notification_center The notification center object.
44
+ *
45
+ * @return void
46
+ */
47
+ public function handle( Yoast_Notification_Center $notification_center ) {
48
+ if ( $this->is_applicable() ) {
49
+ $notification = $this->get_notification();
50
+ $notification_center->add_notification( $notification );
51
+
52
+ return;
53
+ }
54
+
55
+ $notification_center->remove_notification_by_id( 'wpseo-' . $this->notification_identifier );
56
+ }
57
+
58
+ /**
59
+ * Listens to an argument in the request URL and triggers an action.
60
+ *
61
+ * @return void
62
+ */
63
+ protected function dismiss() {
64
+ $this->set_dismissal_state();
65
+ $this->redirect_to_dashboard();
66
+ }
67
+
68
+ /**
69
+ * Checks if a notice is applicable.
70
+ *
71
+ * @return bool Whether a notice should be shown or not.
72
+ */
73
+ protected function is_applicable() {
74
+ return $this->is_notice_dismissed() === false;
75
+ }
76
+
77
+ /**
78
+ * Checks whether the notification has been dismissed.
79
+ *
80
+ * @return bool True when notification is dismissed.
81
+ *
82
+ * @codeCoverageIgnore
83
+ */
84
+ protected function is_notice_dismissed() {
85
+ return get_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true ) === '1';
86
+ }
87
+
88
+ /**
89
+ * Retrieves the value where listener is listening for.
90
+ *
91
+ * @return string The listener value.
92
+ *
93
+ * @codeCoverageIgnore
94
+ */
95
+ protected function get_listener_value() {
96
+ return filter_input( INPUT_GET, 'yoast_dismiss' );
97
+ }
98
+
99
+ /**
100
+ * Dismisses the notification.
101
+ *
102
+ * @return void
103
+ *
104
+ * @codeCoverageIgnore
105
+ */
106
+ protected function set_dismissal_state() {
107
+ update_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true );
108
+ }
109
+
110
+ /**
111
+ * Redirects the user back to the dashboard.
112
+ *
113
+ * @return void
114
+ *
115
+ * @codeCoverageIgnore
116
+ */
117
+ protected function redirect_to_dashboard() {
118
+ wp_safe_redirect( admin_url( 'admin.php?page=wpseo_dashboard' ) );
119
+ exit;
120
+ }
121
+ }
admin/services/class-file-size.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Services
6
+ */
7
+
8
+ /**
9
+ * Represents the file size service.
10
+ */
11
+ class WPSEO_File_Size_Service {
12
+
13
+ /**
14
+ * Retrieves an indexable.
15
+ *
16
+ * @param WP_REST_Request $request The request object.
17
+ *
18
+ * @return WP_REST_Response The response.
19
+ */
20
+ public function get( WP_REST_Request $request ) {
21
+ try {
22
+ $file_url = $this->get_file_url( $request );
23
+
24
+ return new WP_REST_Response(
25
+ array(
26
+ 'type' => 'success',
27
+ 'size_in_bytes' => $this->get_file_size( $file_url ),
28
+ ),
29
+ 404
30
+ );
31
+ }
32
+ catch ( WPSEO_File_Size_Exception $exception ) {
33
+ return new WP_REST_Response(
34
+ array(
35
+ 'type' => 'failure',
36
+ 'response' => $exception->getMessage(),
37
+ ),
38
+ 404
39
+ );
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Retrieves the file url.
45
+ *
46
+ * @param WP_REST_Request $request The request to retrieve file url from.
47
+ *
48
+ * @return string The file url.
49
+ * @throws WPSEO_File_Size_Exception The file is hosted externally.
50
+ */
51
+ protected function get_file_url( WP_REST_Request $request ) {
52
+ $file_url = rawurldecode( $request->get_param( 'url' ) );
53
+
54
+ if ( ! $this->is_externally_hosted( $file_url ) ) {
55
+ return $file_url;
56
+ }
57
+
58
+ throw WPSEO_File_Size_Exception::externally_hosted( $file_url );
59
+ }
60
+
61
+ /**
62
+ * Checks if the file is hosted externally.
63
+ *
64
+ * @param string $file_url The file url.
65
+ *
66
+ * @return bool True if it is hosted externally.
67
+ */
68
+ protected function is_externally_hosted( $file_url ) {
69
+ return wp_parse_url( home_url(), PHP_URL_HOST ) !== wp_parse_url( $file_url, PHP_URL_HOST );
70
+ }
71
+
72
+ /**
73
+ * Returns the file size.
74
+ *
75
+ * @param string $file_url The file url to get the size for.
76
+ *
77
+ * @return int The file size.
78
+ * @throws WPSEO_File_Size_Exception Retrieval of file size went wrong for unknown reasons.
79
+ */
80
+ protected function get_file_size( $file_url ) {
81
+ $file_config = wp_upload_dir();
82
+ $file_url = str_replace( $file_config['baseurl'], '', $file_url );
83
+ $file_size = $this->calculate_file_size( $file_url );
84
+
85
+ if ( ! $file_size ) {
86
+ throw WPSEO_File_Size_Exception::unknown_error( $file_url );
87
+ }
88
+
89
+ return $file_size;
90
+ }
91
+
92
+ /**
93
+ * Calculates the file size using the Utils class.
94
+ *
95
+ * @param string $file_url The file to retrieve the size for.
96
+ *
97
+ * @return int|bool The file size or False if it could not be retrieved.
98
+ */
99
+ protected function calculate_file_size( $file_url ) {
100
+ return WPSEO_Image_Utils::get_file_size(
101
+ array(
102
+ 'path' => $file_url,
103
+ )
104
+ );
105
+ }
106
+
107
+ }
admin/views/sidebar.php CHANGED
@@ -6,17 +6,18 @@
6
  */
7
 
8
  $wpseo_plugin_dir_url = plugin_dir_url( WPSEO_FILE );
 
9
 
10
  ?>
11
  <div class="wpseo_content_cell" id="sidebar-container">
12
- <div id="sidebar">
13
  <div class="wpseo_content_cell_title yoast-sidebar__title">
14
  <?php
15
  /* translators: %1$s expands to Yoast */
16
  printf( esc_html__( '%1$s recommendations for you', 'wordpress-seo' ), 'Yoast' );
17
  ?>
18
  </div>
19
- <div class="yoast-sidebar_section">
20
  <h2>
21
  <?php
22
  /* translators: %1$s expands to the plugin name */
@@ -36,89 +37,101 @@ $wpseo_plugin_dir_url = plugin_dir_url( WPSEO_FILE );
36
  <?php
37
  /* translators: %s is replaced by the plugin name */
38
  printf( esc_html__( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' );
39
- echo '<span class="screen-reader-text">' . __( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>';
40
  echo '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>';
41
  ?>
42
  </a><br>
43
  </div>
44
- <div class="yoast-sidebar_section">
45
  <h2><?php esc_html_e( 'Improve your SEO skills', 'wordpress-seo' ); ?></h2>
46
- <div>
47
- <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/2oi' ); ?>" target="_blank" rel="noopener noreferrer"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/SEO_for_beginners.svg' ); ?>" alt=""></a>
48
  <p>
49
- <strong>
50
- <?php
51
- printf(
52
- /* translators: %1$s and %2$s convert to anchors. */
53
- esc_html__( 'Free: %1$sSEO for Beginners course%2$s', 'wordpress-seo' ),
54
- '<a target="_blank" rel="noopener noreferrer" href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/2oi' ) ) . '">',
55
- '</a>'
56
- );
57
- ?>
58
- </strong><br>
59
  <?php esc_html_e( 'Get quick wins to make your site rank higher in search engines.', 'wordpress-seo' ); ?>
60
  </p>
61
- <br class="clear">
62
  </div>
63
- <div>
64
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jv' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/yoast_seo_for_wp_2.svg' ); ?>" alt=""></a>
65
  <p>
66
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jv' ); ?>"><?php esc_html_e( 'Yoast SEO for WordPress course', 'wordpress-seo' ); ?></a></strong><br>
 
 
 
 
 
 
 
 
 
67
  <?php esc_html_e( 'Don’t waste time figuring out the best settings yourself.', 'wordpress-seo' ); ?>
68
  </p>
69
- <br class="clear">
70
  </div>
71
- <div>
72
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/ju' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/BasicSEO.svg' ); ?>" alt=""></a>
73
  <p>
74
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/ju' ); ?>"><?php esc_html_e( 'Basic SEO course', 'wordpress-seo' ); ?></a></strong><br>
 
 
 
 
75
  <?php esc_html_e( 'Learn practical SEO skills to rank higher in Google.', 'wordpress-seo' ); ?>
76
  </p>
77
- <br class="clear">
78
  </div>
79
  </div>
80
- <div class="yoast-sidebar_section">
81
  <h2><?php esc_html_e( 'Extend Yoast SEO', 'wordpress-seo' ); ?></h2>
82
- <div>
83
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jq' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Local_SEO_Icon.svg' ); ?>" alt=""></a>
84
  <p>
85
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jq' ); ?>">Local SEO</a></strong><br>
 
 
 
 
86
  <?php esc_html_e( 'Be found in Google Maps and local results.', 'wordpress-seo' ); ?>
87
  </p>
88
- <br class="clear">
89
  </div>
90
- <div>
91
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jo' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Video_SEO_Icon.svg' ); ?>" alt=""></a>
92
  <p>
93
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jo' ); ?>">Video SEO</a></strong><br>
 
 
 
 
94
  <?php esc_html_e( 'Be found in Google Video search and enhance your video sharing on social media.', 'wordpress-seo' ); ?>
95
  </p>
96
- <br class="clear">
97
  </div>
98
- <div>
99
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jp' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Woo_SEO_Icon.svg' ); ?>" alt=""></a>
100
  <p>
101
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jp' ); ?>">WooCommerce SEO</a></strong><br>
 
 
 
 
102
  <?php esc_html_e( 'Optimize your shop\'s SEO and sell more products!', 'wordpress-seo' ); ?>
103
  </p>
104
- <br class="clear">
105
  </div>
106
- <div>
107
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jr' ); ?>"><img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/News_SEO_Icon.svg' ); ?>" alt=""></a>
108
  <p>
109
- <strong><a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jr' ); ?>">News SEO</a></strong><br>
 
 
 
 
110
  <?php esc_html_e( 'Optimize your site for Google News.', 'wordpress-seo' ); ?>
111
  </p>
112
- <br class="clear">
113
  </div>
114
  </div>
115
- <div class="yoast-sidebar_section">
116
- <strong><?php esc_html_e( 'Remove these ads?', 'wordpress-seo' ); ?></strong>
117
  <p>
118
- <a target="_blank" rel="noopener noreferrer" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jy' ); ?>">
119
  <?php
120
  /* translators: %s expands to Yoast SEO Premium. */
121
  printf( esc_html__( 'Upgrade to %s »', 'wordpress-seo' ), 'Yoast SEO Premium' );
 
122
  ?>
123
  </a>
124
  </p>
6
  */
7
 
8
  $wpseo_plugin_dir_url = plugin_dir_url( WPSEO_FILE );
9
+ $new_tab_message = WPSEO_Admin_Utils::get_new_tab_message();
10
 
11
  ?>
12
  <div class="wpseo_content_cell" id="sidebar-container">
13
+ <div id="sidebar" class="yoast-sidebar">
14
  <div class="wpseo_content_cell_title yoast-sidebar__title">
15
  <?php
16
  /* translators: %1$s expands to Yoast */
17
  printf( esc_html__( '%1$s recommendations for you', 'wordpress-seo' ), 'Yoast' );
18
  ?>
19
  </div>
20
+ <div class="yoast-sidebar__section">
21
  <h2>
22
  <?php
23
  /* translators: %1$s expands to the plugin name */
37
  <?php
38
  /* translators: %s is replaced by the plugin name */
39
  printf( esc_html__( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' );
40
+ echo $new_tab_message;
41
  echo '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>';
42
  ?>
43
  </a><br>
44
  </div>
45
+ <div class="yoast-sidebar__section">
46
  <h2><?php esc_html_e( 'Improve your SEO skills', 'wordpress-seo' ); ?></h2>
47
+ <div class="wp-clearfix">
 
48
  <p>
49
+ <strong><?php echo esc_html_x( 'Free:', 'course', 'wordpress-seo' ); ?></strong>
50
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/2oi' ); ?>" target="_blank">
51
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/SEO_for_beginners.svg' ); ?>" alt="">
52
+ <strong><?php esc_html_e( 'SEO for Beginners course', 'wordpress-seo' ); ?></strong>
53
+ <?php echo $new_tab_message; ?>
54
+ </a><br>
 
 
 
 
55
  <?php esc_html_e( 'Get quick wins to make your site rank higher in search engines.', 'wordpress-seo' ); ?>
56
  </p>
 
57
  </div>
58
+ <div class="wp-clearfix">
 
59
  <p>
60
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jv' ); ?>" target="_blank">
61
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/yoast_seo_for_wp_2.svg' ); ?>" alt="">
62
+ <strong>
63
+ <?php
64
+ /* translators: %s expands to Yoast SEO */
65
+ printf( esc_html__( '%s for WordPress course', 'wordpress-seo' ), 'Yoast SEO' );
66
+ ?>
67
+ </strong>
68
+ <?php echo $new_tab_message; ?>
69
+ </a><br>
70
  <?php esc_html_e( 'Don’t waste time figuring out the best settings yourself.', 'wordpress-seo' ); ?>
71
  </p>
 
72
  </div>
73
+ <div class="wp-clearfix">
 
74
  <p>
75
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/ju' ); ?>" target="_blank">
76
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/BasicSEO.svg' ); ?>" alt="">
77
+ <strong><?php esc_html_e( 'Basic SEO course', 'wordpress-seo' ); ?></strong>
78
+ <?php echo $new_tab_message; ?>
79
+ </a><br>
80
  <?php esc_html_e( 'Learn practical SEO skills to rank higher in Google.', 'wordpress-seo' ); ?>
81
  </p>
 
82
  </div>
83
  </div>
84
+ <div class="yoast-sidebar__section">
85
  <h2><?php esc_html_e( 'Extend Yoast SEO', 'wordpress-seo' ); ?></h2>
86
+ <div class="wp-clearfix">
 
87
  <p>
88
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jq' ); ?>" target="_blank">
89
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Local_SEO_Icon.svg' ); ?>" alt="">
90
+ <strong>Local SEO</strong>
91
+ <?php echo $new_tab_message; ?>
92
+ </a><br>
93
  <?php esc_html_e( 'Be found in Google Maps and local results.', 'wordpress-seo' ); ?>
94
  </p>
 
95
  </div>
96
+ <div class="wp-clearfix">
 
97
  <p>
98
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jo' ); ?>" target="_blank">
99
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Video_SEO_Icon.svg' ); ?>" alt="">
100
+ <strong>Video SEO</strong>
101
+ <?php echo $new_tab_message; ?>
102
+ </a><br>
103
  <?php esc_html_e( 'Be found in Google Video search and enhance your video sharing on social media.', 'wordpress-seo' ); ?>
104
  </p>
 
105
  </div>
106
+ <div class="wp-clearfix">
 
107
  <p>
108
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jp' ); ?>" target="_blank">
109
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/Woo_SEO_Icon.svg' ); ?>" alt="">
110
+ <strong>WooCommerce SEO</strong>
111
+ <?php echo $new_tab_message; ?>
112
+ </a><br>
113
  <?php esc_html_e( 'Optimize your shop\'s SEO and sell more products!', 'wordpress-seo' ); ?>
114
  </p>
 
115
  </div>
116
+ <div class="wp-clearfix">
 
117
  <p>
118
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jr' ); ?>" target="_blank">
119
+ <img src="<?php echo esc_url( $wpseo_plugin_dir_url . 'images/News_SEO_Icon.svg' ); ?>" alt="">
120
+ <strong>News SEO</strong>
121
+ <?php echo $new_tab_message; ?>
122
+ </a><br>
123
  <?php esc_html_e( 'Optimize your site for Google News.', 'wordpress-seo' ); ?>
124
  </p>
 
125
  </div>
126
  </div>
127
+ <div class="yoast-sidebar__section">
128
+ <strong><?php esc_html_e( 'Remove these ads?', 'wordpress-seo' ) ?></strong>
129
  <p>
130
+ <a href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/jy' ); ?>" target="_blank">
131
  <?php
132
  /* translators: %s expands to Yoast SEO Premium. */
133
  printf( esc_html__( 'Upgrade to %s »', 'wordpress-seo' ), 'Yoast SEO Premium' );
134
+ echo $new_tab_message;
135
  ?>
136
  </a>
137
  </p>
admin/views/tabs/dashboard/features.php CHANGED
@@ -58,6 +58,9 @@ $feature_toggles = Yoast_Feature_Toggles::instance()->get_all();
58
  $feature_help->get_button_html() . $feature_help->get_panel_html()
59
  );
60
  }
 
 
 
61
  ?>
62
  </div>
63
  <?php
58
  $feature_help->get_button_html() . $feature_help->get_panel_html()
59
  );
60
  }
61
+
62
+ $calibration_beta = new WPSEO_Recalibration_Beta();
63
+ $calibration_beta->show_feature_toggle();
64
  ?>
65
  </div>
66
  <?php
css/dist/admin-global-921-rtl.min.css DELETED
@@ -1 +0,0 @@
1
- .wpseo-premium-indicator{display:inline-block;width:1px;height:1px}#adminmenu .wpseo-premium-indicator{margin:-2px 2px -3px 0;color:inherit}.wpseo-premium-indicator svg{display:none;width:auto;height:100%}.yoast-tooltip{position:relative}.yoast-tooltip::after,.yoast-tooltip::before{display:none;position:absolute;opacity:0;pointer-events:none}button.yoast-tooltip{overflow:visible}.yoast-tooltip::after{z-index:1000000;padding:6px 8px 5px;border-radius:3px;color:#fff;background:rgba(0,0,0,.8);text-shadow:none;font:normal normal 11px/1.45454545 Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";text-align:center;white-space:pre;text-decoration:none;letter-spacing:normal;text-transform:none;word-wrap:break-word;content:attr(aria-label);-webkit-font-smoothing:subpixel-antialiased}.yoast-tooltip-alt::after{content:attr(data-label)}.yoast-tooltip::before{z-index:1000001;width:0;height:0;border:5px solid transparent;color:rgba(0,0,0,.8);content:"\00a0"}@keyframes yoast-tooltip-appear{from{opacity:0}to{opacity:1}}.yoast-tooltip:active::after,.yoast-tooltip:active::before,.yoast-tooltip:focus::after,.yoast-tooltip:focus::before,.yoast-tooltip:hover::after,.yoast-tooltip:hover::before{display:inline-block;text-decoration:none;animation-name:yoast-tooltip-appear;animation-duration:.1s;animation-timing-function:ease-in;animation-delay:.4s;animation-fill-mode:forwards}.yoast-tooltip-no-delay:active::after,.yoast-tooltip-no-delay:active::before,.yoast-tooltip-no-delay:focus::after,.yoast-tooltip-no-delay:focus::before,.yoast-tooltip-no-delay:hover::after,.yoast-tooltip-no-delay:hover::before{opacity:1;animation:none}.yoast-tooltip-multiline:active::after,.yoast-tooltip-multiline:focus::after,.yoast-tooltip-multiline:hover::after{display:table-cell}.yoast-tooltip-s::after,.yoast-tooltip-se::after,.yoast-tooltip-sw::after{top:100%;left:50%;margin-top:5px}.yoast-tooltip-s::before