Yoast SEO - Version 13.2

Version Description

Release Date: March 3rd, 2020

In Yoast SEO 13.2, youll find a number of checks moved to the WordPress Site Health tool. Site Health was introduced in WordPress 5.2 as a way to help site owners and managers get a sense of how their site is doing, technically speaking. Find out more about these changes in our 13.2 release post!

Enhancements:

  • Adds the capability to view Site Health to the SEO Manager role.
  • Adds a cURL minimal version check to Site Health.
  • Moves the "The postname is present in your permalink"-notification from the SEO Dashboard to Site Health.
  • Moves the "You are using the default WordPress tagline"-notification from the SEO Dashboard to Site Health.
  • Moves the "Your site is indexable"-notification and widget from the SEO dashboard to Site Health.
  • Improves the usability of the "Your site is indexable" Site Health check.
  • Adds error handling for the "Your site is indexable" status request.
  • Adds an ID to the FAQ sections in the Schema output.

Bugfixes:

  • Removes the "Check headers"-tool from the Yoast Admin bar menu, as it is no longer available.
Download this release

Release Info

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

Code changes from version 13.1 to 13.2

Files changed (77) hide show
  1. admin/ajax.php +15 -19
  2. admin/capabilities/class-register-capabilities.php +2 -1
  3. admin/class-admin-init.php +52 -80
  4. admin/class-admin.php +0 -1
  5. admin/class-gutenberg-compatibility.php +2 -2
  6. admin/class-yoast-dashboard-widget.php +0 -10
  7. admin/onpage/class-onpage-request.php +0 -70
  8. admin/onpage/class-onpage.php +0 -298
  9. admin/onpage/class-ryte-service.php +0 -112
  10. admin/{onpage/class-onpage-option.php → ryte/class-ryte-option.php} +21 -13
  11. admin/ryte/class-ryte-request.php +104 -0
  12. admin/ryte/class-ryte.php +227 -0
  13. admin/tracking/class-tracking-settings-data.php +1 -1
  14. admin/views/class-yoast-feature-toggles.php +1 -1
  15. css/dist/{admin-global-1310-rtl.css → admin-global-1320-rtl.css} +0 -0
  16. css/dist/{admin-global-1310.css → admin-global-1320.css} +0 -0
  17. css/dist/{adminbar-1310-rtl.css → adminbar-1320-rtl.css} +0 -0
  18. css/dist/{adminbar-1310.css → adminbar-1320.css} +0 -0
  19. css/dist/{alerts-1310-rtl.css → alerts-1320-rtl.css} +0 -0
  20. css/dist/{alerts-1310.css → alerts-1320.css} +0 -0
  21. css/dist/{dashboard-1310-rtl.css → dashboard-1320-rtl.css} +0 -0
  22. css/dist/{dashboard-1310.css → dashboard-1320.css} +0 -0
  23. css/dist/{edit-page-1310-rtl.css → edit-page-1320-rtl.css} +0 -0
  24. css/dist/{edit-page-1310.css → edit-page-1320.css} +0 -0
  25. css/dist/{featured-image-1310-rtl.css → featured-image-1320-rtl.css} +0 -0
  26. css/dist/{featured-image-1310.css → featured-image-1320.css} +0 -0
  27. css/dist/{filter-explanation-1310-rtl.css → filter-explanation-1320-rtl.css} +0 -0
  28. css/dist/{filter-explanation-1310.css → filter-explanation-1320.css} +0 -0
  29. css/dist/{inside-editor-1310-rtl.css → inside-editor-1320-rtl.css} +0 -0
  30. css/dist/{inside-editor-1310.css → inside-editor-1320.css} +0 -0
  31. css/dist/{metabox-1310-rtl.css → metabox-1320-rtl.css} +0 -0
  32. css/dist/{metabox-1310.css → metabox-1320.css} +0 -0
  33. css/dist/{metabox-primary-category-1310-rtl.css → metabox-primary-category-1320-rtl.css} +0 -0
  34. css/dist/{metabox-primary-category-1310.css → metabox-primary-category-1320.css} +0 -0
  35. css/dist/{search-appearance-1310-rtl.css → search-appearance-1320-rtl.css} +0 -0
  36. css/dist/{search-appearance-1310.css → search-appearance-1320.css} +0 -0
  37. css/dist/{structured-data-blocks-1310-rtl.css → structured-data-blocks-1320-rtl.css} +0 -0
  38. css/dist/{structured-data-blocks-1310.css → structured-data-blocks-1320.css} +0 -0
  39. css/dist/{toggle-switch-1310-rtl.css → toggle-switch-1320-rtl.css} +0 -0
  40. css/dist/{toggle-switch-1310.css → toggle-switch-1320.css} +0 -0
  41. css/dist/{wpseo-dismissible-1310-rtl.css → wpseo-dismissible-1320-rtl.css} +0 -0
  42. css/dist/{wpseo-dismissible-1310.css → wpseo-dismissible-1320.css} +0 -0
  43. css/dist/{yoast-components-1310-rtl.css → yoast-components-1320-rtl.css} +0 -0
  44. css/dist/{yoast-components-1310.css → yoast-components-1320.css} +0 -0
  45. css/dist/{yoast-extensions-1310-rtl.css → yoast-extensions-1320-rtl.css} +0 -0
  46. css/dist/{yoast-extensions-1310.css → yoast-extensions-1320.css} +0 -0
  47. css/dist/{yst_plugin_tools-1310-rtl.css → yst_plugin_tools-1320-rtl.css} +0 -0
  48. css/dist/{yst_plugin_tools-1310.css → yst_plugin_tools-1320.css} +0 -0
  49. css/dist/{yst_seo_score-1310-rtl.css → yst_seo_score-1320-rtl.css} +0 -0
  50. css/dist/{yst_seo_score-1310.css → yst_seo_score-1320.css} +0 -0
  51. {admin → deprecated/admin}/ajax/class-yoast-onpage-ajax.php +14 -12
  52. {admin → deprecated/admin}/endpoints/class-endpoint-ryte.php +14 -19
  53. deprecated/admin/onpage/class-onpage-option.php +29 -0
  54. deprecated/admin/onpage/class-onpage-request.php +30 -0
  55. deprecated/admin/onpage/class-onpage.php +41 -0
  56. deprecated/admin/onpage/class-ryte-service.php +38 -0
  57. inc/class-addon-manager.php +11 -11
  58. inc/class-upgrade.php +37 -0
  59. inc/class-wpseo-admin-bar-menu.php +0 -5
  60. inc/health-check-curl-version.php +133 -0
  61. inc/health-check-default-tagline.php +67 -0
  62. inc/health-check-page-comments.php +1 -3
  63. inc/health-check-postname-permalink.php +59 -0
  64. inc/health-check-ryte.php +230 -0
  65. inc/health-check.php +1 -0
  66. inc/options/class-wpseo-option-ms.php +1 -1
  67. inc/options/class-wpseo-option-wpseo.php +2 -2
  68. js/dist/{analysis-1310.js → analysis-1320.js} +0 -0
  69. js/dist/{babel-polyfill-1310.js → babel-polyfill-1320.js} +0 -0
  70. js/dist/{commons-1310.js → commons-1320.js} +0 -0
  71. js/dist/{components-1310.js → components-1320.js} +0 -0
  72. js/dist/{configuration-wizard-1310.js → configuration-wizard-1320.js} +2 -2
  73. js/dist/{help-scout-beacon-1310.js → help-scout-beacon-1320.js} +0 -0
  74. js/dist/{jed-1310.js → jed-1320.js} +2 -2
  75. js/dist/{redux-1310.js → redux-1320.js} +1 -1
  76. js/dist/{search-appearance-1310.js → search-appearance-1320.js} +1 -1
  77. js/dist/{styled-components-1310.js → styled-components-1320.js} +2 -2
admin/ajax.php CHANGED
@@ -66,23 +66,6 @@ function wpseo_set_ignore() {
66
 
67
  add_action( 'wp_ajax_wpseo_set_ignore', 'wpseo_set_ignore' );
68
 
69
- /**
70
- * Hides the default tagline notice for a specific user.
71
- */
72
- function wpseo_dismiss_tagline_notice() {
73
- if ( ! current_user_can( 'manage_options' ) ) {
74
- die( '-1' );
75
- }
76
-
77
- check_ajax_referer( 'wpseo-dismiss-tagline-notice' );
78
-
79
- update_user_meta( get_current_user_id(), 'wpseo_seen_tagline_notice', 'seen' );
80
-
81
- die( '1' );
82
- }
83
-
84
- add_action( 'wp_ajax_wpseo_dismiss_tagline_notice', 'wpseo_dismiss_tagline_notice' );
85
-
86
  /**
87
  * Save an individual SEO title from the Bulk Editor.
88
  */
@@ -331,8 +314,6 @@ wpseo_register_ajax_integrations();
331
  // SEO Score Recalculations.
332
  new WPSEO_Recalculate_Scores_Ajax();
333
 
334
- new Yoast_OnPage_Ajax();
335
-
336
  new WPSEO_Shortcode_Filter();
337
 
338
  new WPSEO_Taxonomy_Columns();
@@ -401,3 +382,18 @@ function wpseo_ajax_replace_vars() {
401
  echo wpseo_replace_vars( stripslashes( filter_input( INPUT_POST, 'string' ) ), $post, $omit );
402
  die;
403
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  add_action( 'wp_ajax_wpseo_set_ignore', 'wpseo_set_ignore' );
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  /**
70
  * Save an individual SEO title from the Bulk Editor.
71
  */
314
  // SEO Score Recalculations.
315
  new WPSEO_Recalculate_Scores_Ajax();
316
 
 
 
317
  new WPSEO_Shortcode_Filter();
318
 
319
  new WPSEO_Taxonomy_Columns();
382
  echo wpseo_replace_vars( stripslashes( filter_input( INPUT_POST, 'string' ) ), $post, $omit );
383
  die;
384
  }
385
+
386
+ /**
387
+ * Hides the default tagline notice for a specific user.
388
+ *
389
+ * @deprecated 13.2
390
+ * @codeCoverageIgnore
391
+ */
392
+ function wpseo_dismiss_tagline_notice() {
393
+ if ( ! current_user_can( 'manage_options' ) ) {
394
+ die( '-1' );
395
+ }
396
+
397
+ _deprecated_function( __FUNCTION__, 'WPSEO 13.2', 'This method is deprecated.' );
398
+ wpseo_ajax_json_echo_die( '' );
399
+ }
admin/capabilities/class-register-capabilities.php CHANGED
@@ -35,10 +35,11 @@ class WPSEO_Register_Capabilities implements WPSEO_WordPress_Integration {
35
  $manager->register( 'wpseo_edit_advanced_metadata', [ 'wpseo_editor', 'wpseo_manager' ] );
36
 
37
  $manager->register( 'wpseo_manage_options', [ 'administrator', 'wpseo_manager' ] );
 
38
  }
39
 
40
  /**
41
- * Revokes the 'wpseo_manage_options' capability from administrator users if it should only
42
  * only be granted to network administrators.
43
  *
44
  * @param array $allcaps An array of all the user's capabilities.
35
  $manager->register( 'wpseo_edit_advanced_metadata', [ 'wpseo_editor', 'wpseo_manager' ] );
36
 
37
  $manager->register( 'wpseo_manage_options', [ 'administrator', 'wpseo_manager' ] );
38
+ $manager->register( 'view_site_health_checks', [ 'wpseo_manager' ] );
39
  }
40
 
41
  /**
42
+ * Revokes the 'wpseo_manage_options' capability from administrator users if it should
43
  * only be granted to network administrators.
44
  *
45
  * @param array $allcaps An array of all the user's capabilities.
admin/class-admin-init.php CHANGED
@@ -35,9 +35,7 @@ class WPSEO_Admin_Init {
35
  $this->asset_manager = new WPSEO_Admin_Asset_Manager();
36
 
37
  add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_dismissible' ] );
38
- add_action( 'admin_init', [ $this, 'tagline_notice' ], 15 );
39
  add_action( 'admin_init', [ $this, 'blog_public_notice' ], 15 );
40
- add_action( 'admin_init', [ $this, 'permalink_notice' ], 15 );
41
  add_action( 'admin_init', [ $this, 'yoast_plugin_suggestions_notification' ], 15 );
42
  add_action( 'admin_init', [ $this, 'recalculate_notice' ], 15 );
43
  add_action( 'admin_init', [ $this, 'unsupported_php_notice' ], 15 );
@@ -47,8 +45,17 @@ class WPSEO_Admin_Init {
47
  add_action( 'admin_init', [ $this, 'handle_notifications' ], 15 );
48
  add_action( 'admin_notices', [ $this, 'permalink_settings_notice' ] );
49
 
50
- $page_comments = new WPSEO_Health_Check_Page_Comments();
51
- $page_comments->register_test();
 
 
 
 
 
 
 
 
 
52
 
53
  $listeners = [];
54
  $listeners[] = new WPSEO_Post_Type_Archive_Notification_Handler();
@@ -97,34 +104,6 @@ class WPSEO_Admin_Init {
97
  $this->asset_manager->enqueue_style( 'dismissible' );
98
  }
99
 
100
- /**
101
- * Notify about the default tagline if the user hasn't changed it.
102
- */
103
- public function tagline_notice() {
104
- $query_args = [
105
- 'autofocus[control]' => 'blogdescription',
106
- ];
107
- $customize_url = add_query_arg( $query_args, wp_customize_url() );
108
-
109
- $info_message = sprintf(
110
- /* translators: 1: link open tag; 2: link close tag. */
111
- __( 'You still have the default WordPress tagline, even an empty one is probably better. %1$sYou can fix this in the customizer%2$s.', 'wordpress-seo' ),
112
- '<a href="' . esc_attr( $customize_url ) . '">',
113
- '</a>'
114
- );
115
-
116
- $notification_options = [
117
- 'type' => Yoast_Notification::ERROR,
118
- 'id' => 'wpseo-dismiss-tagline-notice',
119
- 'capabilities' => 'wpseo_manage_options',
120
- ];
121
-
122
- $tagline_notification = new Yoast_Notification( $info_message, $notification_options );
123
-
124
- $notification_center = Yoast_Notification_Center::get();
125
- $notification_center->remove_notification( $tagline_notification );
126
- }
127
-
128
  /**
129
  * Add an alert if the blog is not publicly visible.
130
  */
@@ -156,54 +135,6 @@ class WPSEO_Admin_Init {
156
  }
157
  }
158
 
159
- /**
160
- * Returns whether or not the site has the default tagline.
161
- *
162
- * @return bool
163
- */
164
- public function has_default_tagline() {
165
- $blog_description = get_bloginfo( 'description' );
166
- $default_blog_description = 'Just another WordPress site';
167
-
168
- // We are checking against the WordPress internal translation.
169
- // @codingStandardsIgnoreLine
170
- $translated_blog_description = __( 'Just another WordPress site', 'default' );
171
-
172
- return $translated_blog_description === $blog_description || $default_blog_description === $blog_description;
173
- }
174
-
175
- /**
176
- * Show alert when the permalink doesn't contain %postname%.
177
- */
178
- public function permalink_notice() {
179
-
180
- $info_message = __( 'You do not have your postname in the URL of your posts and pages, it is highly recommended that you do. Consider setting your permalink structure to <strong>/%postname%/</strong>.', 'wordpress-seo' );
181
- $info_message .= '<br/>';
182
- $info_message .= sprintf(
183
- /* translators: %1$s resolves to the starting tag of the link to the permalink settings page, %2$s resolves to the closing tag of the link */
184
- __( 'You can fix this on the %1$sPermalink settings page%2$s.', 'wordpress-seo' ),
185
- '<a href="' . admin_url( 'options-permalink.php' ) . '">',
186
- '</a>'
187
- );
188
-
189
- $notification_options = [
190
- 'type' => Yoast_Notification::WARNING,
191
- 'id' => 'wpseo-dismiss-permalink-notice',
192
- 'capabilities' => 'wpseo_manage_options',
193
- 'priority' => 0.8,
194
- ];
195
-
196
- $notification = new Yoast_Notification( $info_message, $notification_options );
197
-
198
- $notification_center = Yoast_Notification_Center::get();
199
- if ( ! $this->has_postname_in_permalink() ) {
200
- $notification_center->add_notification( $notification );
201
- }
202
- else {
203
- $notification_center->remove_notification( $notification );
204
- }
205
- }
206
-
207
  /**
208
  * Determines whether a suggested plugins notification needs to be displayed.
209
  *
@@ -621,4 +552,45 @@ class WPSEO_Admin_Init {
621
 
622
  return get_option( 'page_comments' ) === '1';
623
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
  }
35
  $this->asset_manager = new WPSEO_Admin_Asset_Manager();
36
 
37
  add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_dismissible' ] );
 
38
  add_action( 'admin_init', [ $this, 'blog_public_notice' ], 15 );
 
39
  add_action( 'admin_init', [ $this, 'yoast_plugin_suggestions_notification' ], 15 );
40
  add_action( 'admin_init', [ $this, 'recalculate_notice' ], 15 );
41
  add_action( 'admin_init', [ $this, 'unsupported_php_notice' ], 15 );
45
  add_action( 'admin_init', [ $this, 'handle_notifications' ], 15 );
46
  add_action( 'admin_notices', [ $this, 'permalink_settings_notice' ] );
47
 
48
+ $health_checks = [
49
+ new WPSEO_Health_Check_Page_Comments(),
50
+ new WPSEO_Health_Check_Ryte(),
51
+ new WPSEO_Health_Check_Default_Tagline(),
52
+ new WPSEO_Health_Check_Postname_Permalink(),
53
+ new WPSEO_Health_Check_Curl_Version(),
54
+ ];
55
+
56
+ foreach ( $health_checks as $health_check ) {
57
+ $health_check->register_test();
58
+ }
59
 
60
  $listeners = [];
61
  $listeners[] = new WPSEO_Post_Type_Archive_Notification_Handler();
104
  $this->asset_manager->enqueue_style( 'dismissible' );
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * Add an alert if the blog is not publicly visible.
109
  */
135
  }
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  /**
139
  * Determines whether a suggested plugins notification needs to be displayed.
140
  *
552
 
553
  return get_option( 'page_comments' ) === '1';
554
  }
555
+
556
+ /**
557
+ * Notify about the default tagline if the user hasn't changed it.
558
+ *
559
+ * @deprecated 13.2
560
+ * @codeCoverageIgnore
561
+ */
562
+ public function tagline_notice() {
563
+ _deprecated_function( __METHOD__, 'WPSEO 13.2' );
564
+ }
565
+
566
+ /**
567
+ * Returns whether or not the site has the default tagline.
568
+ *
569
+ * @deprecated 13.2
570
+ * @codeCoverageIgnore
571
+ *
572
+ * @return bool
573
+ */
574
+ public function has_default_tagline() {
575
+ _deprecated_function( __METHOD__, 'WPSEO 13.2' );
576
+
577
+ $blog_description = get_bloginfo( 'description' );
578
+ $default_blog_description = 'Just another WordPress site';
579
+
580
+ // We are checking against the WordPress internal translation.
581
+ // @codingStandardsIgnoreLine
582
+ $translated_blog_description = __( 'Just another WordPress site', 'default' );
583
+
584
+ return $translated_blog_description === $blog_description || $default_blog_description === $blog_description;
585
+ }
586
+
587
+ /**
588
+ * Shows an alert when the permalink doesn't contain %postname%.
589
+ *
590
+ * @deprecated 13.2
591
+ * @codeCoverageIgnore
592
+ */
593
+ public function permalink_notice() {
594
+ _deprecated_function( __METHOD__, 'WPSEO 13.2' );
595
+ }
596
  }
admin/class-admin.php CHANGED
@@ -315,7 +315,6 @@ class WPSEO_Admin {
315
  'HelpScout beacon'
316
  ),
317
  'dismiss_about_url' => $this->get_dismiss_url( 'wpseo-dismiss-about' ),
318
- 'dismiss_tagline_url' => $this->get_dismiss_url( 'wpseo-dismiss-tagline-notice' ),
319
  /* translators: %s: expends to Yoast SEO */
320
  'help_video_iframe_title' => sprintf( __( '%s video tutorial', 'wordpress-seo' ), 'Yoast SEO' ),
321
  'scrollable_table_hint' => __( 'Scroll to see the table content.', 'wordpress-seo' ),
315
  'HelpScout beacon'
316
  ),
317
  'dismiss_about_url' => $this->get_dismiss_url( 'wpseo-dismiss-about' ),
 
318
  /* translators: %s: expends to Yoast SEO */
319
  'help_video_iframe_title' => sprintf( __( '%s video tutorial', 'wordpress-seo' ), 'Yoast SEO' ),
320
  'scrollable_table_hint' => __( 'Scroll to see the table content.', 'wordpress-seo' ),
admin/class-gutenberg-compatibility.php CHANGED
@@ -15,14 +15,14 @@ class WPSEO_Gutenberg_Compatibility {
15
  *
16
  * @var string
17
  */
18
- const CURRENT_RELEASE = '7.2.0';
19
 
20
  /**
21
  * The minimally supported version of Gutenberg by the plugin.
22
  *
23
  * @var string
24
  */
25
- const MINIMUM_SUPPORTED = '7.2.0';
26
 
27
  /**
28
  * Holds the current version.
15
  *
16
  * @var string
17
  */
18
+ const CURRENT_RELEASE = '7.5.0';
19
 
20
  /**
21
  * The minimally supported version of Gutenberg by the plugin.
22
  *
23
  * @var string
24
  */
25
+ const MINIMUM_SUPPORTED = '7.5.0';
26
 
27
  /**
28
  * Holds the current version.
admin/class-yoast-dashboard-widget.php CHANGED
@@ -124,16 +124,6 @@ class Yoast_Dashboard_Widget implements WPSEO_WordPress_Integration {
124
  'Yoast.com'
125
  ),
126
  'feed_footer' => __( 'Read more like this on our SEO blog', 'wordpress-seo' ),
127
- 'ryte_header' => sprintf(
128
- /* translators: %1$s expands to Ryte. */
129
- __( 'Indexability check by %1$s', 'wordpress-seo' ),
130
- 'Ryte'
131
- ),
132
- 'ryteEnabled' => ( WPSEO_Options::get( 'onpage_indexability' ) === true ),
133
- 'ryte_fetch' => __( 'Fetch the current status', 'wordpress-seo' ),
134
- 'ryte_analyze' => __( 'Analyze entire site', 'wordpress-seo' ),
135
- 'ryte_fetch_url' => esc_attr( add_query_arg( 'wpseo-redo-onpage', '1' ) ) . '#wpseo-dashboard-overview',
136
- 'ryte_landing_url' => WPSEO_Shortlinker::get( 'https://yoa.st/rytelp' ),
137
  'wp_version' => substr( $GLOBALS['wp_version'], 0, 3 ) . '-' . ( is_plugin_active( 'classic-editor/classic-editor.php' ) ? '1' : '0' ),
138
  'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
139
  ];
124
  'Yoast.com'
125
  ),
126
  'feed_footer' => __( 'Read more like this on our SEO blog', 'wordpress-seo' ),
 
 
 
 
 
 
 
 
 
 
127
  'wp_version' => substr( $GLOBALS['wp_version'], 0, 3 ) . '-' . ( is_plugin_active( 'classic-editor/classic-editor.php' ) ? '1' : '0' ),
128
  'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
129
  ];
admin/onpage/class-onpage-request.php DELETED
@@ -1,70 +0,0 @@
1
- <?php
2
- /**
3
- * WPSEO plugin file.
4
- *
5
- * @package WPSEO\Admin
6
- */
7
-
8
- /**
9
- * This class will fetch a new status from Ryte and if it's necessary it will
10
- * notify the site admin by email and remove the current meta value to hide the
11
- * notice for all admin users.
12
- */
13
- class WPSEO_OnPage_Request {
14
-
15
- /**
16
- * The endpoint where the request will be send to.
17
- *
18
- * @var string
19
- */
20
- private $onpage_endpoint = 'https://indexability.yoast.onpage.org/';
21
-
22
- /**
23
- * Doing the remote get and returns the body.
24
- *
25
- * @param string $target_url The home url.
26
- * @param array $parameters Array of extra parameters to send to Ryte.
27
- *
28
- * @return array
29
- * @throws Exception The error message that can be used to show to the user.
30
- */
31
- protected function get_remote( $target_url, $parameters = [] ) {
32
- $defaults = [
33
- 'url' => $target_url,
34
- 'wp_version' => $GLOBALS['wp_version'],
35
- 'yseo_version' => WPSEO_VERSION,
36
- ];
37
- $parameters = array_merge( $defaults, $parameters );
38
-
39
- $url = add_query_arg( $parameters, $this->onpage_endpoint );
40
-
41
- $response = wp_remote_get( $url );
42
- $response_code = wp_remote_retrieve_response_code( $response );
43
-
44
- // When the request is successful, the response code will be 200.
45
- if ( $response_code === 200 ) {
46
- $response_body = wp_remote_retrieve_body( $response );
47
-
48
- return json_decode( $response_body, true );
49
- }
50
- }
51
-
52
- /**
53
- * Sending a request to Ryte to check if the $home_url is indexable.
54
- *
55
- * @param string $target_url The URL that will be send to the API.
56
- * @param array $parameters Array of extra parameters to send to Ryte.
57
- *
58
- * @return array
59
- */
60
- public function do_request( $target_url, $parameters = [] ) {
61
- $json_body = $this->get_remote( $target_url, $parameters );
62
-
63
- // Ryte recognized a redirect, fetch the data of that URL by calling this method with the value from Ryte.
64
- if ( ! empty( $json_body['passes_juice_to'] ) ) {
65
- return $this->do_request( $json_body['passes_juice_to'], $parameters );
66
- }
67
-
68
- return $json_body;
69
- }
70
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/onpage/class-onpage.php DELETED
@@ -1,298 +0,0 @@
1
- <?php
2
- /**
3
- * WPSEO plugin file.
4
- *
5
- * @package WPSEO\Admin
6
- */
7
-
8
- /**
9
- * Handles the request for getting the Ryte status.
10
- */
11
- class WPSEO_OnPage implements WPSEO_WordPress_Integration {
12
-
13
- /**
14
- * The name of the user meta key for storing the dismissed status.
15
- *
16
- * @var string
17
- */
18
- const USER_META_KEY = 'wpseo_dismiss_onpage';
19
-
20
- /**
21
- * Is the request started by pressing the fetch button.
22
- *
23
- * @var bool
24
- */
25
- private $is_manual_request = false;
26
-
27
- /**
28
- * Constructs the object.
29
- */
30
- public function __construct() {
31
- $this->catch_redo_listener();
32
- }
33
-
34
- /**
35
- * Sets up the hooks.
36
- *
37
- * @return void
38
- */
39
- public function register_hooks() {
40
- // Adds admin notice if necessary.
41
- add_filter( 'admin_init', [ $this, 'show_notice' ] );
42
-
43
- if ( ! self::is_active() ) {
44
- return;
45
- }
46
-
47
- // Adds weekly schedule to the cron job schedules.
48
- add_filter( 'cron_schedules', [ $this, 'add_weekly_schedule' ] );
49
-
50
- // Sets the action for the Ryte fetch.
51
- add_action( 'wpseo_onpage_fetch', [ $this, 'fetch_from_onpage' ] );
52
- }
53
-
54
- /**
55
- * Shows a notice when the website is not indexable.
56
- *
57
- * @return void
58
- */
59
- public function show_notice() {
60
- $notification = $this->get_indexability_notification();
61
- $notification_center = Yoast_Notification_Center::get();
62
-
63
- if ( $this->should_show_notice() ) {
64
- $notification_center->add_notification( $notification );
65
-
66
- return;
67
- }
68
-
69
- $notification_center->remove_notification( $notification );
70
- }
71
-
72
- /**
73
- * Determines if we can use the functionality.
74
- *
75
- * @return bool True if this functionality can be used.
76
- */
77
- public static function is_active() {
78
- if ( wp_doing_ajax() ) {
79
- return false;
80
- }
81
-
82
- if ( ! WPSEO_Options::get( 'onpage_indexability' ) ) {
83
- return false;
84
- }
85
-
86
- return true;
87
- }
88
-
89
- /**
90
- * Hooks to run on plugin activation.
91
- */
92
- public function activate_hooks() {
93
- if ( $this->get_option()->is_enabled() ) {
94
- $this->schedule_cron();
95
-
96
- return;
97
- }
98
-
99
- $this->unschedule_cron();
100
- }
101
-
102
- /**
103
- * Adds a weekly cron schedule.
104
- *
105
- * @param array $schedules Currently scheduled items.
106
- *
107
- * @return array Enriched list of schedules.
108
- */
109
- public function add_weekly_schedule( $schedules ) {
110
- if ( ! is_array( $schedules ) ) {
111
- $schedules = [];
112
- }
113
-
114
- $schedules['weekly'] = [
115
- 'interval' => WEEK_IN_SECONDS,
116
- 'display' => __( 'Once Weekly', 'wordpress-seo' ),
117
- ];
118
-
119
- return $schedules;
120
- }
121
-
122
- /**
123
- * Fetches the data from Ryte.
124
- *
125
- * @return bool True if this has been run.
126
- */
127
- public function fetch_from_onpage() {
128
- $onpage_option = $this->get_option();
129
- if ( ! $onpage_option->should_be_fetched() ) {
130
- return false;
131
- }
132
-
133
- $new_status = $this->request_indexability();
134
- if ( $new_status === false ) {
135
- return false;
136
- }
137
-
138
- // Updates the timestamp in the option.
139
- $onpage_option->set_last_fetch( time() );
140
-
141
- // The currently indexability status.
142
- $old_status = $onpage_option->get_status();
143
-
144
- $onpage_option->set_status( $new_status );
145
- $onpage_option->save_option();
146
-
147
- // Check if the status has been changed.
148
- if ( $old_status !== $new_status && $new_status !== WPSEO_OnPage_Option::CANNOT_FETCH ) {
149
- $this->notify_admins();
150
- }
151
-
152
- return true;
153
- }
154
-
155
- /**
156
- * Retrieves the option to use.
157
- *
158
- * @return WPSEO_OnPage_Option The option.
159
- */
160
- protected function get_option() {
161
- return new WPSEO_OnPage_Option();
162
- }
163
-
164
- /**
165
- * Builds the indexability notification.
166
- *
167
- * @return Yoast_Notification The notification.
168
- */
169
- private function get_indexability_notification() {
170
- $notice = sprintf(
171
- /* translators: 1: opens a link to a related knowledge base article. 2: closes the link */
172
- __( '%1$sYour homepage cannot be indexed by search engines%2$s. This is very bad for SEO and should be fixed.', 'wordpress-seo' ),
173
- '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpageindexerror' ) . '" target="_blank">',
174
- '</a>'
175
- );
176
-
177
- return new Yoast_Notification(
178
- $notice,
179
- [
180
- 'type' => Yoast_Notification::ERROR,
181
- 'id' => 'wpseo-dismiss-onpageorg',
182
- 'capabilities' => 'wpseo_manage_options',
183
- ]
184
- );
185
- }
186
-
187
- /**
188
- * Sends a request to Ryte to get the indexability.
189
- *
190
- * @return int|bool The indexability value.
191
- */
192
- protected function request_indexability() {
193
- $parameters = [];
194
- if ( $this->wordfence_protection_enabled() ) {
195
- $parameters['wf_strict'] = 1;
196
- }
197
-
198
- $request = new WPSEO_OnPage_Request();
199
- $response = $request->do_request( get_option( 'home' ), $parameters );
200
-
201
- if ( isset( $response['is_indexable'] ) ) {
202
- return (int) $response['is_indexable'];
203
- }
204
-
205
- return WPSEO_OnPage_Option::CANNOT_FETCH;
206
- }
207
-
208
- /**
209
- * Should the notice being given?
210
- *
211
- * @return bool True if a notice should be shown.
212
- */
213
- protected function should_show_notice() {
214
- if ( ! $this->get_option()->is_enabled() ) {
215
- return false;
216
- }
217
-
218
- // If development mode is on or the blog is not public, just don't show this notice.
219
- if ( WPSEO_Utils::is_development_mode() || ( get_option( 'blog_public' ) === '0' ) ) {
220
- return false;
221
- }
222
-
223
- return $this->get_option()->get_status() === WPSEO_OnPage_Option::IS_NOT_INDEXABLE;
224
- }
225
-
226
- /**
227
- * Notifies the admins.
228
- *
229
- * @return void
230
- */
231
- protected function notify_admins() {
232
- /*
233
- * Let's start showing the notices to all admins by removing the hide-notice meta data for each admin resulting
234
- * in popping up the notice again.
235
- */
236
- delete_metadata( 'user', 0, self::USER_META_KEY, '', true );
237
- }
238
-
239
- /**
240
- * Schedules the cronjob to get the new indexibility status.
241
- *
242
- * @return void
243
- */
244
- private function schedule_cron() {
245
- if ( wp_next_scheduled( 'wpseo_onpage_fetch' ) ) {
246
- return;
247
- }
248
-
249
- wp_schedule_event( time(), 'weekly', 'wpseo_onpage_fetch' );
250
- }
251
-
252
- /**
253
- * Unschedules the cronjob to get the new indexibility status.
254
- *
255
- * @return void
256
- */
257
- private function unschedule_cron() {
258
- if ( ! wp_next_scheduled( 'wpseo_onpage_fetch' ) ) {
259
- return;
260
- }
261
-
262
- wp_clear_scheduled_hook( 'wpseo_onpage_fetch' );
263
- }
264
-
265
- /**
266
- * Redo the fetch request for Ryte.
267
- *
268
- * @return void
269
- */
270
- private function catch_redo_listener() {
271
- if ( ! self::is_active() ) {
272
- return;
273
- }
274
-
275
- if ( filter_input( INPUT_GET, 'wpseo-redo-onpage' ) === '1' ) {
276
- $this->is_manual_request = true;
277
-
278
- add_action( 'admin_init', [ $this, 'fetch_from_onpage' ] );
279
- }
280
- }
281
-
282
- /**
283
- * Checks if WordFence protects the site against 'fake' Google crawlers.
284
- *
285
- * @return boolean True if WordFence protects the site.
286
- */
287
- private function wordfence_protection_enabled() {
288
- if ( ! class_exists( 'wfConfig' ) ) {
289
- return false;
290
- }
291
-
292
- if ( ! method_exists( 'wfConfig', 'get' ) ) {
293
- return false;
294
- }
295
-
296
- return (bool) wfConfig::get( 'blockFakeBots' );
297
- }
298
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/onpage/class-ryte-service.php DELETED
@@ -1,112 +0,0 @@
1
- <?php
2
- /**
3
- * WPSEO plugin file.
4
- *
5
- * @package WPSEO\Admin\OnPage
6
- */
7
-
8
- /**
9
- * Represents the service to be used by the WPSEO_Endpoint_Ryte endpoint.
10
- */
11
- class WPSEO_Ryte_Service {
12
-
13
- /**
14
- * This class handles the data for the option where the Ryte data is stored.
15
- *
16
- * @var WPSEO_OnPage_Option
17
- */
18
- protected $option;
19
-
20
- /**
21
- * Constructs the WPSEO_Ryte_Service class.
22
- *
23
- * @param WPSEO_OnPage_Option $option The option to retrieve data from.
24
- */
25
- public function __construct( WPSEO_OnPage_Option $option ) {
26
- $this->option = $option;
27
- }
28
-
29
- /**
30
- * Fetches statistics via REST request.
31
- *
32
- * @return WP_REST_Response The response object.
33
- */
34
- public function get_statistics() {
35
- // Switch to the user locale with fallback to the site locale.
36
- switch_to_locale( WPSEO_Language_Utils::get_user_locale() );
37
-
38
- $result = false;
39
-
40
- if ( $this->option->is_enabled() ) {
41
- $result = $this->get_score( $this->option->get_status(), $this->option->should_be_fetched() );
42
- }
43
-
44
- return new WP_REST_Response( [ 'ryte' => $result ] );
45
- }
46
-
47
- /**
48
- * Returns an the results of the Ryte option based on the passed status.
49
- *
50
- * @param string $status The option's status.
51
- * @param bool $fetch Whether or not the data should be fetched.
52
- *
53
- * @return array The results, contains a score and label.
54
- */
55
- private function get_score( $status, $fetch = false ) {
56
- if ( $status === WPSEO_OnPage_Option::IS_INDEXABLE ) {
57
- return [
58
- 'score' => 'good',
59
- 'label' => __( 'Your homepage can be indexed by search engines.', 'wordpress-seo' ),
60
- 'can_fetch' => $fetch,
61
- ];
62
- }
63
-
64
- if ( $status === WPSEO_OnPage_Option::IS_NOT_INDEXABLE ) {
65
- /* translators: %1$s: opens a link to a related knowledge base article. %2$s: closes the link. */
66
- $label = __( '%1$sYour homepage cannot be indexed by search engines%2$s. This is very bad for SEO and should be fixed.', 'wordpress-seo' );
67
- $label = sprintf(
68
- $label,
69
- '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpageindexerror' ) . '" target="_blank">',
70
- '</a>'
71
- );
72
-
73
- return [
74
- 'score' => 'bad',
75
- 'label' => $label,
76
- 'can_fetch' => $fetch,
77
- ];
78
- }
79
-
80
- if ( $status === WPSEO_OnPage_Option::CANNOT_FETCH ) {
81
- /* translators: %1$s: opens a link to a related knowledge base article, %2$s: expands to Yoast SEO, %3$s: closes the link, %4$s: expands to Ryte. */
82
- $label = __( '%1$s%2$s has not been able to fetch your site\'s indexability status%3$s from %4$s', 'wordpress-seo' );
83
- $label = sprintf(
84
- $label,
85
- '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpagerequestfailed' ) . '" target="_blank">',
86
- 'Yoast SEO',
87
- '</a>',
88
- 'Ryte'
89
- );
90
-
91
- return [
92
- 'score' => 'na',
93
- 'label' => $label,
94
- 'can_fetch' => $fetch,
95
- ];
96
- }
97
-
98
- if ( $status === WPSEO_OnPage_Option::NOT_FETCHED ) {
99
- /* translators: %1$s: expands to Yoast SEO, %2$s: expands to Ryte. */
100
- $label = __( '%1$s has not fetched your site\'s indexability status yet from %2$s', 'wordpress-seo' );
101
- $label = sprintf( $label, 'Yoast SEO', 'Ryte' );
102
-
103
- return [
104
- 'score' => 'na',
105
- 'label' => esc_html( $label ),
106
- 'can_fetch' => $fetch,
107
- ];
108
- }
109
-
110
- return [];
111
- }
112
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/{onpage/class-onpage-option.php → ryte/class-ryte-option.php} RENAMED
@@ -8,7 +8,7 @@
8
  /**
9
  * This class handles the data for the option where the Ryte data is stored.
10
  */
11
- class WPSEO_OnPage_Option {
12
 
13
  /**
14
  * Indicates the data is not fetched.
@@ -43,7 +43,7 @@ class WPSEO_OnPage_Option {
43
  *
44
  * @var string
45
  */
46
- const OPTION_NAME = 'wpseo_onpage';
47
 
48
  /**
49
  * The key of the status in the option.
@@ -71,13 +71,13 @@ class WPSEO_OnPage_Option {
71
  *
72
  * @var array
73
  */
74
- private $onpage_option;
75
 
76
  /**
77
  * Setting the object by setting the properties.
78
  */
79
  public function __construct() {
80
- $this->onpage_option = $this->get_option();
81
  }
82
 
83
  /**
@@ -86,8 +86,8 @@ class WPSEO_OnPage_Option {
86
  * @return string
87
  */
88
  public function get_status() {
89
- if ( array_key_exists( self::STATUS, $this->onpage_option ) ) {
90
- return $this->onpage_option[ self::STATUS ];
91
  }
92
 
93
  return self::CANNOT_FETCH;
@@ -99,7 +99,7 @@ class WPSEO_OnPage_Option {
99
  * @param string $status The status to save.
100
  */
101
  public function set_status( $status ) {
102
- $this->onpage_option[ self::STATUS ] = $status;
103
  }
104
 
105
  /**
@@ -108,23 +108,31 @@ class WPSEO_OnPage_Option {
108
  * @param integer $timestamp Timestamp with the new value.
109
  */
110
  public function set_last_fetch( $timestamp ) {
111
- $this->onpage_option[ self::LAST_FETCH ] = $timestamp;
112
  }
113
 
114
  /**
115
- * Check if the last fetch is within the time of 60 minutes.
116
  *
117
- * @return bool
 
 
 
 
118
  */
119
  public function should_be_fetched() {
120
- return ( ( time() - $this->onpage_option[ self::LAST_FETCH ] ) > self::FETCH_LIMIT );
 
 
 
 
121
  }
122
 
123
  /**
124
  * Saving the option with the current data.
125
  */
126
  public function save_option() {
127
- update_option( self::OPTION_NAME, $this->onpage_option );
128
  }
129
 
130
  /**
@@ -133,7 +141,7 @@ class WPSEO_OnPage_Option {
133
  * @return bool
134
  */
135
  public function is_enabled() {
136
- return WPSEO_Options::get( 'onpage_indexability' );
137
  }
138
 
139
  /**
8
  /**
9
  * This class handles the data for the option where the Ryte data is stored.
10
  */
11
+ class WPSEO_Ryte_Option {
12
 
13
  /**
14
  * Indicates the data is not fetched.
43
  *
44
  * @var string
45
  */
46
+ const OPTION_NAME = 'wpseo_ryte';
47
 
48
  /**
49
  * The key of the status in the option.
71
  *
72
  * @var array
73
  */
74
+ private $ryte_option;
75
 
76
  /**
77
  * Setting the object by setting the properties.
78
  */
79
  public function __construct() {
80
+ $this->ryte_option = $this->get_option();
81
  }
82
 
83
  /**
86
  * @return string
87
  */
88
  public function get_status() {
89
+ if ( array_key_exists( self::STATUS, $this->ryte_option ) ) {
90
+ return $this->ryte_option[ self::STATUS ];
91
  }
92
 
93
  return self::CANNOT_FETCH;
99
  * @param string $status The status to save.
100
  */
101
  public function set_status( $status ) {
102
+ $this->ryte_option[ self::STATUS ] = $status;
103
  }
104
 
105
  /**
108
  * @param integer $timestamp Timestamp with the new value.
109
  */
110
  public function set_last_fetch( $timestamp ) {
111
+ $this->ryte_option[ self::LAST_FETCH ] = $timestamp;
112
  }
113
 
114
  /**
115
+ * Determines whether the indexability status should be fetched.
116
  *
117
+ * If LAST_FETCH isn't set, we assume the indexability status hasn't been fetched
118
+ * yet and return true. Then, we check whether the last fetch is within the
119
+ * FETCH_LIMIT time interval (15 seconds) to avoid too many consecutive API calls.
120
+ *
121
+ * @return bool Whether the indexability status should be fetched.
122
  */
123
  public function should_be_fetched() {
124
+ if ( ! isset( $this->ryte_option[ self::LAST_FETCH ] ) ) {
125
+ return true;
126
+ }
127
+
128
+ return ( ( time() - $this->ryte_option[ self::LAST_FETCH ] ) > self::FETCH_LIMIT );
129
  }
130
 
131
  /**
132
  * Saving the option with the current data.
133
  */
134
  public function save_option() {
135
+ update_option( self::OPTION_NAME, $this->ryte_option );
136
  }
137
 
138
  /**
141
  * @return bool
142
  */
143
  public function is_enabled() {
144
+ return WPSEO_Options::get( 'ryte_indexability' );
145
  }
146
 
147
  /**
admin/ryte/class-ryte-request.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * This class will fetch a new status from Ryte and if it's necessary it will
10
+ * notify the site admin by email and remove the current meta value to hide the
11
+ * notice for all admin users.
12
+ */
13
+ class WPSEO_Ryte_Request {
14
+
15
+ /**
16
+ * The endpoint where the request will be send to.
17
+ *
18
+ * @var string
19
+ */
20
+ private $ryte_endpoint = 'https://indexability.yoast.onpage.org/';
21
+
22
+ /**
23
+ * Gets the response from the Ryte API and returns the body.
24
+ *
25
+ * @param string $target_url The URL to check indexability for.
26
+ * @param array $parameters Array of extra parameters to send to the Ryte API.
27
+ *
28
+ * @return array The successful response or the error details.
29
+ */
30
+ protected function get_remote( $target_url, $parameters = [] ) {
31
+ $defaults = [
32
+ 'url' => $target_url,
33
+ 'wp_version' => $GLOBALS['wp_version'],
34
+ 'yseo_version' => WPSEO_VERSION,
35
+ ];
36
+
37
+ $parameters = array_merge( $defaults, $parameters );
38
+ $url = add_query_arg( $parameters, $this->ryte_endpoint );
39
+ $response = wp_remote_get( $url );
40
+
41
+ return $this->process_response( $response );
42
+ }
43
+
44
+ /**
45
+ * Sends a request to the Ryte API to check whether a URL is indexable.
46
+ *
47
+ * @param string $target_url The URL to check indexability for.
48
+ * @param array $parameters Array of extra parameters to send to the Ryte API.
49
+ *
50
+ * @return array
51
+ */
52
+ public function do_request( $target_url, $parameters = [] ) {
53
+ $json_body = $this->get_remote( $target_url, $parameters );
54
+
55
+ // Ryte recognized a redirect, fetch the data of that URL by calling this method with the value from Ryte.
56
+ if ( ! empty( $json_body['passes_juice_to'] ) ) {
57
+ return $this->do_request( $json_body['passes_juice_to'], $parameters );
58
+ }
59
+
60
+ return $json_body;
61
+ }
62
+
63
+ /**
64
+ * Processes the given Ryte response.
65
+ *
66
+ * @param array|WP_Error $response The response or WP_Error to process.
67
+ *
68
+ * @return array The response body or the error detaiils on failure.
69
+ */
70
+ protected function process_response( $response ) {
71
+ // Most of the potential errors are WP_Error(s).
72
+ if ( is_wp_error( $response ) ) {
73
+ return [
74
+ 'is_error' => true,
75
+ 'raw_error_code' => '',
76
+ // WP_Error codes aren't that helpful for users, let's display them in a less prominent way.
77
+ 'wp_error_code' => '(' . $response->get_error_code() . ')',
78
+ 'message' => $response->get_error_message(),
79
+ ];
80
+ }
81
+
82
+ /*
83
+ * As of February 2020 the Ryte API returns an error 500 for non-reachable
84
+ * sites. There's also the need to handle any potential raw HTTP error.
85
+ */
86
+ if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
87
+ // Not all HTTP errors may have a response message. Let's provide a default one.
88
+ $response_message = wp_remote_retrieve_response_message( $response );
89
+ $message = ( $response_message ) ? $response_message : __( 'The request to Ryte returned an error.', 'wordpress-seo' );
90
+
91
+ return [
92
+ 'is_error' => true,
93
+ 'raw_error_code' => wp_remote_retrieve_response_code( $response ),
94
+ 'wp_error_code' => '',
95
+ 'message' => $message,
96
+ ];
97
+ }
98
+
99
+ // When the request is successful, the response code will be 200.
100
+ $response_body = wp_remote_retrieve_body( $response );
101
+
102
+ return json_decode( $response_body, true );
103
+ }
104
+ }
admin/ryte/class-ryte.php ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * Handles the request for getting the Ryte status.
10
+ */
11
+ class WPSEO_Ryte implements WPSEO_WordPress_Integration {
12
+
13
+ /**
14
+ * Is the request started by pressing the fetch button.
15
+ *
16
+ * @var boolean
17
+ */
18
+ private $is_manual_request = false;
19
+
20
+ /**
21
+ * Holds the Ryte API response.
22
+ *
23
+ * @var array
24
+ */
25
+ private $ryte_response = null;
26
+
27
+ /**
28
+ * Constructs the object.
29
+ */
30
+ public function __construct() {
31
+ $this->maybe_add_weekly_schedule();
32
+ }
33
+
34
+ /**
35
+ * Sets up the hooks.
36
+ *
37
+ * @return void
38
+ */
39
+ public function register_hooks() {
40
+ if ( ! self::is_active() ) {
41
+ return;
42
+ }
43
+
44
+ // Sets the action for the Ryte fetch cron job.
45
+ add_action( 'wpseo_ryte_fetch', [ $this, 'fetch_from_ryte' ] );
46
+ }
47
+
48
+ /**
49
+ * Determines if we can use the functionality.
50
+ *
51
+ * @return bool True if this functionality can be used.
52
+ */
53
+ public static function is_active() {
54
+ if ( wp_doing_ajax() ) {
55
+ return false;
56
+ }
57
+
58
+ if ( ! WPSEO_Options::get( 'ryte_indexability' ) ) {
59
+ return false;
60
+ }
61
+
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Hooks to run on plugin activation.
67
+ */
68
+ public function activate_hooks() {
69
+ if ( $this->get_option()->is_enabled() ) {
70
+ $this->schedule_cron();
71
+
72
+ return;
73
+ }
74
+
75
+ $this->unschedule_cron();
76
+ }
77
+
78
+ /**
79
+ * Determines whether to add a custom cron weekly schedule.
80
+ *
81
+ * @return void
82
+ */
83
+ public function maybe_add_weekly_schedule() {
84
+ $schedules = wp_get_schedules();
85
+
86
+ /*
87
+ * Starting with version 5.4, WordPress does have a default weekly cron
88
+ * schedule. See https://core.trac.wordpress.org/changeset/47062.
89
+ * We need to add a custom one only if the default one doesn't exist.
90
+ */
91
+ if ( isset( $schedules['weekly'] ) ) {
92
+ return;
93
+ }
94
+
95
+ // If there's no default cron weekly schedule, add a custom one.
96
+ add_filter( 'cron_schedules', [ $this, 'add_weekly_schedule' ] );
97
+ }
98
+
99
+ /**
100
+ * Adds a custom weekly cron schedule.
101
+ *
102
+ * @param array $schedules The existing custom cron schedules.
103
+ *
104
+ * @return array Enriched list of custom cron schedules.
105
+ */
106
+ public function add_weekly_schedule( $schedules ) {
107
+ if ( ! is_array( $schedules ) ) {
108
+ $schedules = [];
109
+ }
110
+
111
+ $schedules['weekly'] = [
112
+ 'interval' => WEEK_IN_SECONDS,
113
+ 'display' => __( 'Once Weekly', 'wordpress-seo' ),
114
+ ];
115
+
116
+ return $schedules;
117
+ }
118
+
119
+ /**
120
+ * Fetches the data from Ryte.
121
+ *
122
+ * @return bool Whether the request ran.
123
+ */
124
+ public function fetch_from_ryte() {
125
+ $ryte_option = $this->get_option();
126
+ if ( ! $ryte_option->should_be_fetched() ) {
127
+ return false;
128
+ }
129
+
130
+ $new_status = $this->request_indexability();
131
+
132
+ // Updates the timestamp in the option.
133
+ $ryte_option->set_last_fetch( time() );
134
+
135
+ $ryte_option->set_status( $new_status );
136
+ $ryte_option->save_option();
137
+
138
+ return true;
139
+ }
140
+
141
+ /**
142
+ * Retrieves the option to use.
143
+ *
144
+ * @return WPSEO_Ryte_Option The option.
145
+ */
146
+ protected function get_option() {
147
+ return new WPSEO_Ryte_Option();
148
+ }
149
+
150
+ /**
151
+ * Sends a request to Ryte to get the indexability status.
152
+ *
153
+ * @return int The indexability status value.
154
+ */
155
+ protected function request_indexability() {
156
+ $parameters = [];
157
+ if ( $this->wordfence_protection_enabled() ) {
158
+ $parameters['wf_strict'] = 1;
159
+ }
160
+
161
+ $request = new WPSEO_Ryte_Request();
162
+ $response = $request->do_request( get_option( 'home' ), $parameters );
163
+
164
+ // Populate the ryte_response property.
165
+ $this->ryte_response = $response;
166
+
167
+ // It's a valid Ryte response because the array contains an `is_indexable` element.
168
+ if ( isset( $response['is_indexable'] ) ) {
169
+ return (int) $response['is_indexable'];
170
+ }
171
+
172
+ // It's not a valid Ryte response.
173
+ return WPSEO_Ryte_Option::CANNOT_FETCH;
174
+ }
175
+
176
+ /**
177
+ * Schedules the cronjob to get the new indexability status.
178
+ *
179
+ * @return void
180
+ */
181
+ private function schedule_cron() {
182
+ if ( wp_next_scheduled( 'wpseo_ryte_fetch' ) ) {
183
+ return;
184
+ }
185
+
186
+ wp_schedule_event( time(), 'weekly', 'wpseo_ryte_fetch' );
187
+ }
188
+
189
+ /**
190
+ * Unschedules the cronjob to get the new indexability status.
191
+ *
192
+ * @return void
193
+ */
194
+ private function unschedule_cron() {
195
+ if ( ! wp_next_scheduled( 'wpseo_ryte_fetch' ) ) {
196
+ return;
197
+ }
198
+
199
+ wp_clear_scheduled_hook( 'wpseo_ryte_fetch' );
200
+ }
201
+
202
+ /**
203
+ * Checks if WordFence protects the site against 'fake' Google crawlers.
204
+ *
205
+ * @return boolean True if WordFence protects the site.
206
+ */
207
+ private function wordfence_protection_enabled() {
208
+ if ( ! class_exists( 'wfConfig' ) ) {
209
+ return false;
210
+ }
211
+
212
+ if ( ! method_exists( 'wfConfig', 'get' ) ) {
213
+ return false;
214
+ }
215
+
216
+ return (bool) wfConfig::get( 'blockFakeBots' );
217
+ }
218
+
219
+ /**
220
+ * Retrieves the Ryte API response property.
221
+ *
222
+ * @return array|WP_Error The response or WP_Error on failure.
223
+ */
224
+ public function get_response() {
225
+ return $this->ryte_response;
226
+ }
227
+ }
admin/tracking/class-tracking-settings-data.php CHANGED
@@ -57,7 +57,7 @@ class WPSEO_Tracking_Settings_Data implements WPSEO_Collection {
57
  'ms_defaults_set',
58
  'version',
59
  'disableadvanced_meta',
60
- 'onpage_indexability',
61
  'baiduverify',
62
  'googleverify',
63
  'msverify',
57
  'ms_defaults_set',
58
  'version',
59
  'disableadvanced_meta',
60
+ 'ryte_indexability',
61
  'baiduverify',
62
  'googleverify',
63
  'msverify',
admin/views/class-yoast-feature-toggles.php CHANGED
@@ -113,7 +113,7 @@ class Yoast_Feature_Toggles {
113
  (object) [
114
  /* translators: %s: Ryte */
115
  'name' => sprintf( __( '%s integration', 'wordpress-seo' ), 'Ryte' ),
116
- 'setting' => 'onpage_indexability',
117
  'label' => sprintf(
118
  /* translators: 1: Ryte, 2: Yoast SEO */
119
  __( '%1$s will check weekly if your site is still indexable by search engines and %2$s will notify you when this is not the case.', 'wordpress-seo' ),
113
  (object) [
114
  /* translators: %s: Ryte */
115
  'name' => sprintf( __( '%s integration', 'wordpress-seo' ), 'Ryte' ),
116
+ 'setting' => 'ryte_indexability',
117
  'label' => sprintf(
118
  /* translators: 1: Ryte, 2: Yoast SEO */
119
  __( '%1$s will check weekly if your site is still indexable by search engines and %2$s will notify you when this is not the case.', 'wordpress-seo' ),
css/dist/{admin-global-1310-rtl.css → admin-global-1320-rtl.css} RENAMED
File without changes
css/dist/{admin-global-1310.css → admin-global-1320.css} RENAMED
File without changes
css/dist/{adminbar-1310-rtl.css → adminbar-1320-rtl.css} RENAMED
File without changes
css/dist/{adminbar-1310.css → adminbar-1320.css} RENAMED
File without changes
css/dist/{alerts-1310-rtl.css → alerts-1320-rtl.css} RENAMED
File without changes
css/dist/{alerts-1310.css → alerts-1320.css} RENAMED
File without changes
css/dist/{dashboard-1310-rtl.css → dashboard-1320-rtl.css} RENAMED
File without changes
css/dist/{dashboard-1310.css → dashboard-1320.css} RENAMED
File without changes
css/dist/{edit-page-1310-rtl.css → edit-page-1320-rtl.css} RENAMED
File without changes
css/dist/{edit-page-1310.css → edit-page-1320.css} RENAMED
File without changes
css/dist/{featured-image-1310-rtl.css → featured-image-1320-rtl.css} RENAMED
File without changes
css/dist/{featured-image-1310.css → featured-image-1320.css} RENAMED
File without changes
css/dist/{filter-explanation-1310-rtl.css → filter-explanation-1320-rtl.css} RENAMED
File without changes
css/dist/{filter-explanation-1310.css → filter-explanation-1320.css} RENAMED
File without changes
css/dist/{inside-editor-1310-rtl.css → inside-editor-1320-rtl.css} RENAMED
File without changes
css/dist/{inside-editor-1310.css → inside-editor-1320.css} RENAMED
File without changes
css/dist/{metabox-1310-rtl.css → metabox-1320-rtl.css} RENAMED
File without changes
css/dist/{metabox-1310.css → metabox-1320.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1310-rtl.css → metabox-primary-category-1320-rtl.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1310.css → metabox-primary-category-1320.css} RENAMED
File without changes
css/dist/{search-appearance-1310-rtl.css → search-appearance-1320-rtl.css} RENAMED
File without changes
css/dist/{search-appearance-1310.css → search-appearance-1320.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1310-rtl.css → structured-data-blocks-1320-rtl.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1310.css → structured-data-blocks-1320.css} RENAMED
File without changes
css/dist/{toggle-switch-1310-rtl.css → toggle-switch-1320-rtl.css} RENAMED
File without changes
css/dist/{toggle-switch-1310.css → toggle-switch-1320.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1310-rtl.css → wpseo-dismissible-1320-rtl.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1310.css → wpseo-dismissible-1320.css} RENAMED
File without changes
css/dist/{yoast-components-1310-rtl.css → yoast-components-1320-rtl.css} RENAMED
File without changes
css/dist/{yoast-components-1310.css → yoast-components-1320.css} RENAMED
File without changes
css/dist/{yoast-extensions-1310-rtl.css → yoast-extensions-1320-rtl.css} RENAMED
File without changes
css/dist/{yoast-extensions-1310.css → yoast-extensions-1320.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1310-rtl.css → yst_plugin_tools-1320-rtl.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1310.css → yst_plugin_tools-1320.css} RENAMED
File without changes
css/dist/{yst_seo_score-1310-rtl.css → yst_seo_score-1320-rtl.css} RENAMED
File without changes
css/dist/{yst_seo_score-1310.css → yst_seo_score-1320.css} RENAMED
File without changes
{admin → deprecated/admin}/ajax/class-yoast-onpage-ajax.php RENAMED
@@ -5,36 +5,38 @@
5
  * @package WPSEO\Admin\Ajax
6
  */
7
 
 
 
 
8
  /**
9
  * Class Yoast_OnPage_Ajax.
10
  *
11
  * This class will catch the request to dismiss the Ryte notice and will store
12
  * the dismiss status as an user meta in the database.
 
 
 
13
  */
14
  class Yoast_OnPage_Ajax {
15
 
16
  /**
17
  * Initialize the hooks for the AJAX request.
 
 
 
18
  */
19
  public function __construct() {
20
- add_action( 'wp_ajax_wpseo_dismiss_onpageorg', [ $this, 'dismiss_notice' ] );
21
  }
22
 
23
  /**
24
  * Handles the dismiss notice request.
 
 
 
25
  */
26
  public function dismiss_notice() {
27
- check_ajax_referer( 'wpseo-dismiss-onpageorg' );
28
-
29
- $this->save_dismissed();
30
-
31
- wp_die( 'true' );
32
  }
33
 
34
- /**
35
- * Storing the dismissed value as an user option in the database.
36
- */
37
- private function save_dismissed() {
38
- update_user_meta( get_current_user_id(), WPSEO_OnPage::USER_META_KEY, 1 );
39
- }
40
  }
5
  * @package WPSEO\Admin\Ajax
6
  */
7
 
8
+ // Mark this file as deprecated.
9
+ _deprecated_file( __FILE__, 'WPSEO 13.2' );
10
+
11
  /**
12
  * Class Yoast_OnPage_Ajax.
13
  *
14
  * This class will catch the request to dismiss the Ryte notice and will store
15
  * the dismiss status as an user meta in the database.
16
+ *
17
+ * @deprecated 13.2
18
+ * @codeCoverageIgnore
19
  */
20
  class Yoast_OnPage_Ajax {
21
 
22
  /**
23
  * Initialize the hooks for the AJAX request.
24
+ *
25
+ * @deprecated 13.2
26
+ * @codeCoverageIgnore
27
  */
28
  public function __construct() {
29
+ _deprecated_function( __METHOD__, 'WPSEO 13.2' );
30
  }
31
 
32
  /**
33
  * Handles the dismiss notice request.
34
+ *
35
+ * @deprecated 13.2
36
+ * @codeCoverageIgnore
37
  */
38
  public function dismiss_notice() {
39
+ _deprecated_function( __METHOD__, 'WPSEO 13.2' );
 
 
 
 
40
  }
41
 
 
 
 
 
 
 
42
  }
{admin → deprecated/admin}/endpoints/class-endpoint-ryte.php RENAMED
@@ -7,6 +7,9 @@
7
 
8
  /**
9
  * Represents an implementation of the WPSEO_Endpoint interface to register one or multiple endpoints.
 
 
 
10
  */
11
  class WPSEO_Endpoint_Ryte implements WPSEO_Endpoint {
12
 
@@ -31,41 +34,33 @@ class WPSEO_Endpoint_Ryte implements WPSEO_Endpoint {
31
  */
32
  const CAPABILITY_RETRIEVE = 'manage_options';
33
 
34
- /**
35
- * Service to use.
36
- *
37
- * @var WPSEO_Ryte_Service
38
- */
39
- protected $service;
40
-
41
  /**
42
  * Constructs the WPSEO_Endpoint_Ryte class and sets the service to use.
43
  *
44
- * @param WPSEO_Ryte_Service $service Service to use.
 
45
  */
46
- public function __construct( WPSEO_Ryte_Service $service ) {
47
- $this->service = $service;
48
  }
49
 
50
  /**
51
  * Registers the REST routes that are available on the endpoint.
 
 
 
52
  */
53
  public function register() {
54
-