Yoast SEO - Version 15.5

Version Description

Release Date: December 15th, 2020

Out now: Yoast SEO 15.5! You can easily check if you are doing a great job in terms of the readability of your text when writing in Hungarian! Yoast SEO helps you produce a powerful piece of content thats easy to read. It also gives you feedback on what to improve! Read more about whats new in Yoast SEO 15.5 in our release post!

Enhancements:

  • Improves and expands Hungarian language support:
    • Improves keyphrase recognition and internal linking by filtering function words such as 'harmadik', 'titek', and 'valaki'.
    • Improves the transition words assessment.
    • Adds the passive voice assessment.
    • Adds the consecutive sentences assessment to ensure variety in a text.
  • Removes the Open Graph and Twitter author metadata on pages and other non-post post types.
  • Adds breadcrumb Schema to the homepage.

Bugfixes:

  • Fixes a bug where network-wide settings about the Enhanced Slack Sharing feature would not be saved.
  • Fixes a bug where translations on the Search Appearance pages would be missing.
  • Fixes a bug where subsites in a multisite network could have an enabled feature toggle for the Usage tracking feature, even though subsites never send tracking data.
  • Fixes a bug where the Yoast SEO dashboard widget would still fetch data even when the dashboard itself was hidden.
  • Fixes a bug where the TinyMCE editor on term edit pages would not load correctly when using WordPress 5.6.

Other:

  • Removes support for the Facebook App ID, as Facebook has indicated this feature is deprecated on their side.
  • Adds a notification that tells the user to install and activate the Yoast SEO Multilingual plugin when the WPML plugin is installed and activated.
Download this release

Release Info

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

Code changes from version 15.4 to 15.5

Files changed (77) hide show
  1. admin/class-admin-asset-manager.php +11 -14
  2. admin/class-admin-init.php +13 -5
  3. admin/class-config.php +1 -1
  4. admin/class-gutenberg-compatibility.php +2 -2
  5. admin/class-yoast-form.php +29 -1
  6. admin/class-yoast-input-validation.php +0 -5
  7. admin/class-yoast-plugin-conflict.php +20 -3
  8. admin/formatter/class-metabox-formatter.php +40 -34
  9. admin/formatter/class-post-metabox-formatter.php +1 -9
  10. admin/menu/class-admin-menu.php +1 -1
  11. admin/menu/class-base-menu.php +19 -1
  12. admin/menu/class-network-admin-menu.php +1 -1
  13. admin/tracking/class-tracking-settings-data.php +0 -2
  14. admin/views/tabs/social/facebook.php +0 -2
  15. css/dist/{admin-global-1540-rtl.css → admin-global-1550-rtl.css} +0 -0
  16. css/dist/{admin-global-1540.css → admin-global-1550.css} +0 -0
  17. css/dist/{adminbar-1540-rtl.css → adminbar-1550-rtl.css} +0 -0
  18. css/dist/{adminbar-1540.css → adminbar-1550.css} +0 -0
  19. css/dist/{alerts-1540-rtl.css → alerts-1550-rtl.css} +0 -0
  20. css/dist/{alerts-1540.css → alerts-1550.css} +0 -0
  21. css/dist/{dashboard-1540-rtl.css → dashboard-1550-rtl.css} +0 -0
  22. css/dist/{dashboard-1540.css → dashboard-1550.css} +0 -0
  23. css/dist/{edit-page-1540-rtl.css → edit-page-1550-rtl.css} +0 -0
  24. css/dist/{edit-page-1540.css → edit-page-1550.css} +0 -0
  25. css/dist/{elementor-1540-rtl.css → elementor-1550-rtl.css} +0 -0
  26. css/dist/{elementor-1540.css → elementor-1550.css} +0 -0
  27. css/dist/{featured-image-1540-rtl.css → featured-image-1550-rtl.css} +0 -0
  28. css/dist/{featured-image-1540.css → featured-image-1550.css} +0 -0
  29. css/dist/{filter-explanation-1540-rtl.css → filter-explanation-1550-rtl.css} +0 -0
  30. css/dist/{filter-explanation-1540.css → filter-explanation-1550.css} +0 -0
  31. css/dist/{icons-1540-rtl.css → icons-1550-rtl.css} +0 -0
  32. css/dist/{icons-1540.css → icons-1550.css} +0 -0
  33. css/dist/{inside-editor-1540-rtl.css → inside-editor-1550-rtl.css} +0 -0
  34. css/dist/{inside-editor-1540.css → inside-editor-1550.css} +0 -0
  35. css/dist/{metabox-1540-rtl.css → metabox-1550-rtl.css} +0 -0
  36. css/dist/{metabox-1540.css → metabox-1550.css} +0 -0
  37. css/dist/{metabox-primary-category-1540-rtl.css → metabox-primary-category-1550-rtl.css} +0 -0
  38. css/dist/{metabox-primary-category-1540.css → metabox-primary-category-1550.css} +0 -0
  39. css/dist/{modal-1540-rtl.css → modal-1550-rtl.css} +0 -0
  40. css/dist/{modal-1540.css → modal-1550.css} +0 -0
  41. css/dist/{monorepo-1540-rtl.css → monorepo-1550-rtl.css} +0 -0
  42. css/dist/{monorepo-1540.css → monorepo-1550.css} +0 -0
  43. css/dist/{notifications-1540-rtl.css → notifications-1550-rtl.css} +0 -0
  44. css/dist/{notifications-1540.css → notifications-1550.css} +0 -0
  45. css/dist/{schema-blocks-1540-rtl.css → schema-blocks-1550-rtl.css} +0 -0
  46. css/dist/{schema-blocks-1540.css → schema-blocks-1550.css} +0 -0
  47. css/dist/{score_icon-1540-rtl.css → score_icon-1550-rtl.css} +0 -0
  48. css/dist/{score_icon-1540.css → score_icon-1550.css} +0 -0
  49. css/dist/{search-appearance-1540-rtl.css → search-appearance-1550-rtl.css} +0 -0
  50. css/dist/{search-appearance-1540.css → search-appearance-1550.css} +0 -0
  51. css/dist/{structured-data-blocks-1540-rtl.css → structured-data-blocks-1550-rtl.css} +0 -0
  52. css/dist/{structured-data-blocks-1540.css → structured-data-blocks-1550.css} +0 -0
  53. css/dist/{toggle-switch-1540-rtl.css → toggle-switch-1550-rtl.css} +0 -0
  54. css/dist/{toggle-switch-1540.css → toggle-switch-1550.css} +0 -0
  55. css/dist/{wpseo-dismissible-1540-rtl.css → wpseo-dismissible-1550-rtl.css} +0 -0
  56. css/dist/{wpseo-dismissible-1540.css → wpseo-dismissible-1550.css} +0 -0
  57. css/dist/{yoast-components-1540-rtl.css → yoast-components-1550-rtl.css} +0 -0
  58. css/dist/{yoast-components-1540.css → yoast-components-1550.css} +0 -0
  59. css/dist/{yoast-extensions-1540-rtl.css → yoast-extensions-1550-rtl.css} +0 -0
  60. css/dist/{yoast-extensions-1540.css → yoast-extensions-1550.css} +0 -0
  61. css/dist/{yst_plugin_tools-1540-rtl.css → yst_plugin_tools-1550-rtl.css} +0 -0
  62. css/dist/{yst_plugin_tools-1540.css → yst_plugin_tools-1550.css} +0 -0
  63. css/dist/{yst_seo_score-1540-rtl.css → yst_seo_score-1550-rtl.css} +0 -0
  64. css/dist/{yst_seo_score-1540.css → yst_seo_score-1550.css} +0 -0
  65. inc/class-upgrade.php +17 -1
  66. inc/class-wpseo-replace-vars.php +15 -16
  67. inc/class-wpseo-utils.php +215 -198
  68. inc/date-helper.php +4 -24
  69. inc/options/class-wpseo-option-ms.php +1 -0
  70. inc/options/class-wpseo-option-social.php +0 -7
  71. inc/options/class-wpseo-option-wpseo.php +1 -0
  72. inc/options/class-wpseo-option.php +11 -3
  73. inc/sitemaps/class-author-sitemap-provider.php +1 -15
  74. inc/sitemaps/class-sitemaps-renderer.php +2 -10
  75. inc/sitemaps/class-sitemaps.php +2 -10
  76. js/dist/{admin-global-1540.js → admin-global-1550.js} +0 -0
  77. js/dist/analysis-1540.js +0 -21
admin/class-admin-asset-manager.php CHANGED
@@ -178,6 +178,17 @@ class WPSEO_Admin_Asset_Manager {
178
  return new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
179
  }
180
 
 
 
 
 
 
 
 
 
 
 
 
181
  /**
182
  * Returns the scripts that need to be registered.
183
  *
@@ -210,20 +221,6 @@ class WPSEO_Admin_Asset_Manager {
210
  'wp-polyfill',
211
  ],
212
  ],
213
- [
214
- 'name' => 'search-appearance',
215
- 'src' => 'search-appearance-' . $flat_version,
216
- 'deps' => [
217
- 'lodash',
218
- 'wp-api',
219
- 'wp-element',
220
- 'wp-i18n',
221
- self::PREFIX . 'styled-components',
222
- self::PREFIX . 'yoast-components',
223
- self::PREFIX . 'replacement-variable-editor',
224
- self::PREFIX . 'commons',
225
- ],
226
- ],
227
  [
228
  'name' => 'schema-blocks',
229
  'src' => 'schema-blocks-' . $flat_version,
178
  return new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
179
  }
180
 
181
+ /**
182
+ * Checks if the given script is enqueued.
183
+ *
184
+ * @param string $script The script to check.
185
+ *
186
+ * @return bool True when the script is enqueued.
187
+ */
188
+ public function is_script_enqueued( $script ) {
189
+ return \wp_script_is( $this->prefix . $script );
190
+ }
191
+
192
  /**
193
  * Returns the scripts that need to be registered.
194
  *
221
  'wp-polyfill',
222
  ],
223
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  [
225
  'name' => 'schema-blocks',
226
  'src' => 'schema-blocks-' . $flat_version,
admin/class-admin-init.php CHANGED
@@ -435,26 +435,34 @@ class WPSEO_Admin_Init {
435
  'version' => '9.4',
436
  'alternative' => null,
437
  ],
438
- 'wpseo_opengraph' => [
439
  'version' => '14.0',
440
  'alternative' => null,
441
  ],
442
- 'wpseo_twitter' => [
443
  'version' => '14.0',
444
  'alternative' => null,
445
  ],
446
- 'wpseo_twitter_taxonomy_image' => [
447
  'version' => '14.0',
448
  'alternative' => null,
449
  ],
450
- 'wpseo_twitter_metatag_key' => [
451
  'version' => '14.0',
452
  'alternative' => null,
453
  ],
454
- 'wp_seo_get_bc_ancestors' => [
455
  'version' => '14.0',
456
  'alternative' => 'wpseo_breadcrumb_links',
457
  ],
 
 
 
 
 
 
 
 
458
  ];
459
 
460
  // Determine which filters have been registered.
435
  'version' => '9.4',
436
  'alternative' => null,
437
  ],
438
+ 'wpseo_opengraph' => [
439
  'version' => '14.0',
440
  'alternative' => null,
441
  ],
442
+ 'wpseo_twitter' => [
443
  'version' => '14.0',
444
  'alternative' => null,
445
  ],
446
+ 'wpseo_twitter_taxonomy_image' => [
447
  'version' => '14.0',
448
  'alternative' => null,
449
  ],
450
+ 'wpseo_twitter_metatag_key' => [
451
  'version' => '14.0',
452
  'alternative' => null,
453
  ],
454
+ 'wp_seo_get_bc_ancestors' => [
455
  'version' => '14.0',
456
  'alternative' => 'wpseo_breadcrumb_links',
457
  ],
458
+ 'validate_facebook_app_id_api_response_code' => [
459
+ 'version' => '15.5',
460
+ 'alternative' => null,
461
+ ],
462
+ 'validate_facebook_app_id_api_response_body' => [
463
+ 'version' => '15.5',
464
+ 'alternative' => null,
465
+ ],
466
  ];
467
 
468
  // Determine which filters have been registered.
admin/class-config.php CHANGED
@@ -108,7 +108,7 @@ class WPSEO_Admin_Pages {
108
  remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
109
 
110
  $yoast_components_l10n = new WPSEO_Admin_Asset_Yoast_Components_L10n();
111
- $yoast_components_l10n->localize_script( 'search-appearance' );
112
  }
113
 
114
  if ( in_array( $page, [ 'wpseo_social', WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_titles' ], true ) ) {
108
  remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
109
 
110
  $yoast_components_l10n = new WPSEO_Admin_Asset_Yoast_Components_L10n();
111
+ $yoast_components_l10n->localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'settings' );
112
  }
113
 
114
  if ( in_array( $page, [ 'wpseo_social', WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_titles' ], true ) ) {
admin/class-gutenberg-compatibility.php CHANGED
@@ -15,14 +15,14 @@ class WPSEO_Gutenberg_Compatibility {
15
  *
16
  * @var string
17
  */
18
- const CURRENT_RELEASE = '9.4.1';
19
 
20
  /**
21
  * The minimally supported version of Gutenberg by the plugin.
22
  *
23
  * @var string
24
  */
25
- const MINIMUM_SUPPORTED = '9.4.1';
26
 
27
  /**
28
  * Holds the current version.
15
  *
16
  * @var string
17
  */
18
+ const CURRENT_RELEASE = '9.5.0';
19
 
20
  /**
21
  * The minimally supported version of Gutenberg by the plugin.
22
  *
23
  * @var string
24
  */
25
+ const MINIMUM_SUPPORTED = '9.5.0';
26
 
27
  /**
28
  * Holds the current version.
admin/class-yoast-form.php CHANGED
@@ -760,6 +760,11 @@ class Yoast_Form {
760
  * @return mixed|null The retrieved value.
761
  */
762
  protected function get_field_value( $field_name, $default_value = null ) {
 
 
 
 
 
763
  return WPSEO_Options::get( $field_name, $default_value );
764
  }
765
 
@@ -775,6 +780,11 @@ class Yoast_Form {
775
  return false;
776
  }
777
 
 
 
 
 
 
778
  return $this->option_instance->is_disabled( $var );
779
  }
780
 
@@ -790,6 +800,24 @@ class Yoast_Form {
790
  return '';
791
  }
792
 
793
- return '<p class="disabled-note">' . esc_html__( 'This feature has been disabled by the network admin.', 'wordpress-seo' ) . '</p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
794
  }
795
  }
760
  * @return mixed|null The retrieved value.
761
  */
762
  protected function get_field_value( $field_name, $default_value = null ) {
763
+ // On multisite subsites, the Usage tracking feature should always be set to Off.
764
+ if ( $this->is_tracking_on_subsite( $field_name ) ) {
765
+ return false;
766
+ }
767
+
768
  return WPSEO_Options::get( $field_name, $default_value );
769
  }
770
 
780
  return false;
781
  }
782
 
783
+ // Disable the Usage tracking feature for multisite subsites.
784
+ if ( $this->is_tracking_on_subsite( $var ) ) {
785
+ return true;
786
+ }
787
+
788
  return $this->option_instance->is_disabled( $var );
789
  }
790
 
800
  return '';
801
  }
802
 
803
+ $disabled_message = esc_html__( 'This feature has been disabled by the network admin.', 'wordpress-seo' );
804
+
805
+ // The explanation to show when disabling the Usage tracking feature for multisite subsites.
806
+ if ( $this->is_tracking_on_subsite( $var ) ) {
807
+ $disabled_message = esc_html__( 'This feature has been disabled since subsites never send tracking data.', 'wordpress-seo' );
808
+ }
809
+ return '<p class="disabled-note">' . $disabled_message . '</p>';
810
+ }
811
+
812
+ /**
813
+ * Determines whether we are dealing with the Usage tracking feature on a multisite subsite.
814
+ * This feature requires specific behavior for the toggle switch.
815
+ *
816
+ * @param string $feature_setting The feature setting.
817
+ *
818
+ * @return boolean True if we are dealing with the Usage tracking feature on a multisite subsite.
819
+ */
820
+ protected function is_tracking_on_subsite( $feature_setting ) {
821
+ return ( $feature_setting === 'tracking' && ! is_network_admin() && ! is_main_site() );
822
  }
823
  }
admin/class-yoast-input-validation.php CHANGED
@@ -112,11 +112,6 @@ class Yoast_Input_Validation {
112
  esc_html__( 'Please check the format of the Facebook Page URL you entered. %s', 'wordpress-seo' ),
113
  self::get_dirty_value_message( 'facebook_site' )
114
  ),
115
- 'fbadminapp' => sprintf(
116
- /* translators: %s: additional message with the submitted invalid value */
117
- esc_html__( 'The Facebook App ID you entered doesn\'t exist. %s', 'wordpress-seo' ),
118
- self::get_dirty_value_message( 'fbadminapp' )
119
- ),
120
  'googleverify' => sprintf(
121
  /* translators: %s: additional message with the submitted invalid value */
122
  esc_html__( 'Google verification codes can only contain letters, numbers, hyphens, and underscores. %s', 'wordpress-seo' ),
112
  esc_html__( 'Please check the format of the Facebook Page URL you entered. %s', 'wordpress-seo' ),
113
  self::get_dirty_value_message( 'facebook_site' )
114
  ),
 
 
 
 
 
115
  'googleverify' => sprintf(
116
  /* translators: %s: additional message with the submitted invalid value */
117
  esc_html__( 'Google verification codes can only contain letters, numbers, hyphens, and underscores. %s', 'wordpress-seo' ),
admin/class-yoast-plugin-conflict.php CHANGED
@@ -126,7 +126,7 @@ class Yoast_Plugin_Conflict {
126
 
127
  $plugin_names = [];
128
  foreach ( $plugins as $plugin ) {
129
- $name = WPSEO_Utils::get_plugin_name( $plugin );
130
  if ( ! empty( $name ) ) {
131
  $plugin_names[] = '<em>' . $name . '</em>';
132
  }
@@ -195,7 +195,7 @@ class Yoast_Plugin_Conflict {
195
 
196
  foreach ( $this->active_plugins[ $plugin_section ] as $plugin_file ) {
197
 
198
- $plugin_name = WPSEO_Utils::get_plugin_name( $plugin_file );
199
 
200
  $error_message = '';
201
  /* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */
@@ -203,7 +203,7 @@ class Yoast_Plugin_Conflict {
203
  $error_message .= '<p>' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '</p>';
204
 
205
  /* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */
206
- $error_message .= '<a class="button button-primary" href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $plugin_file . '&amp;plugin_status=all', 'deactivate-plugin_' . $plugin_file ) . '">' . sprintf( __( 'Deactivate %s', 'wordpress-seo' ), WPSEO_Utils::get_plugin_name( $plugin_file ) ) . '</a> ';
207
 
208
  $identifier = $this->get_notification_identifier( $plugin_file );
209
 
@@ -306,6 +306,23 @@ class Yoast_Plugin_Conflict {
306
  }
307
  }
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  /**
310
  * When being in the deactivation process the currently deactivated plugin has to be removed.
311
  */
126
 
127
  $plugin_names = [];
128
  foreach ( $plugins as $plugin ) {
129
+ $name = $this->get_plugin_name( $plugin );
130
  if ( ! empty( $name ) ) {
131
  $plugin_names[] = '<em>' . $name . '</em>';
132
  }
195
 
196
  foreach ( $this->active_plugins[ $plugin_section ] as $plugin_file ) {
197
 
198
+ $plugin_name = $this->get_plugin_name( $plugin_file );
199
 
200
  $error_message = '';
201
  /* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */
203
  $error_message .= '<p>' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '</p>';
204
 
205
  /* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */
206
+ $error_message .= '<a class="button button-primary" href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $plugin_file . '&amp;plugin_status=all', 'deactivate-plugin_' . $plugin_file ) . '">' . sprintf( __( 'Deactivate %s', 'wordpress-seo' ), $this->get_plugin_name( $plugin_file ) ) . '</a> ';
207
 
208
  $identifier = $this->get_notification_identifier( $plugin_file );
209
 
306
  }
307
  }
308
 
309
+ /**
310
+ * Get plugin name from file.
311
+ *
312
+ * @param string $plugin Plugin path relative to plugins directory.
313
+ *
314
+ * @return string|bool Plugin name or false when no name is set.
315
+ */
316
+ protected function get_plugin_name( $plugin ) {
317
+ $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
318
+
319
+ if ( $plugin_details['Name'] !== '' ) {
320
+ return $plugin_details['Name'];
321
+ }
322
+
323
+ return false;
324
+ }
325
+
326
  /**
327
  * When being in the deactivation process the currently deactivated plugin has to be removed.
328
  */
admin/formatter/class-metabox-formatter.php CHANGED
@@ -77,7 +77,6 @@ class WPSEO_Metabox_Formatter {
77
  'intl' => $this->get_content_analysis_component_translations(),
78
  'isRtl' => is_rtl(),
79
  'isPremium' => WPSEO_Utils::is_yoast_seo_premium(),
80
- 'addKeywordUpsell' => $this->get_add_keyword_upsell_translations(),
81
  'wordFormRecognitionActive' => YoastSEO()->helpers->language->is_word_form_recognition_active( WPSEO_Language_Utils::get_language( get_locale() ) ),
82
  'siteIconUrl' => get_site_icon_url(),
83
  'countryCode' => WPSEO_Options::get( 'semrush_country_code', false ),
@@ -198,39 +197,6 @@ class WPSEO_Metabox_Formatter {
198
  ];
199
  }
200
 
201
- /**
202
- * Returns the translations for the Add Keyword modal.
203
- *
204
- * These strings are not escaped because they're meant to be used with React
205
- * which already takes care of that. If used in PHP, they should be escaped.
206
- *
207
- * @return array Translated text strings for the Add Keyword modal.
208
- */
209
- public function get_add_keyword_upsell_translations() {
210
- return [
211
- 'title' => __( 'Would you like to add more than one keyphrase?', 'wordpress-seo' ),
212
- 'intro' => sprintf(
213
- /* translators: %s expands to a 'Yoast SEO Premium' text linked to the yoast.com website. */
214
- __( 'Great news: you can, with %s!', 'wordpress-seo' ),
215
- '{{link}}Yoast SEO Premium{{/link}}'
216
- ),
217
- 'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
218
- 'other' => sprintf(
219
- /* translators: %s expands to 'Yoast SEO Premium'. */
220
- __( 'Other benefits of %s for you:', 'wordpress-seo' ),
221
- 'Yoast SEO Premium'
222
- ),
223
- 'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/add-keywords-popup' ),
224
- 'buy' => sprintf(
225
- /* translators: %s expands to 'Yoast SEO Premium'. */
226
- __( 'Get %s', 'wordpress-seo' ),
227
- 'Yoast SEO Premium'
228
- ),
229
- 'small' => __( '1 year free support and updates included!', 'wordpress-seo' ),
230
- 'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
231
- ];
232
- }
233
-
234
  /**
235
  * Returns Jed compatible YoastSEO.js translations.
236
  *
@@ -301,4 +267,44 @@ class WPSEO_Metabox_Formatter {
301
 
302
  return $semrush_client->has_valid_tokens();
303
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  }
77
  'intl' => $this->get_content_analysis_component_translations(),
78
  'isRtl' => is_rtl(),
79
  'isPremium' => WPSEO_Utils::is_yoast_seo_premium(),
 
80
  'wordFormRecognitionActive' => YoastSEO()->helpers->language->is_word_form_recognition_active( WPSEO_Language_Utils::get_language( get_locale() ) ),
81
  'siteIconUrl' => get_site_icon_url(),
82
  'countryCode' => WPSEO_Options::get( 'semrush_country_code', false ),
197
  ];
198
  }
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  /**
201
  * Returns Jed compatible YoastSEO.js translations.
202
  *
267
 
268
  return $semrush_client->has_valid_tokens();
269
  }
270
+
271
+ /* ********************* DEPRECATED METHODS ********************* */
272
+
273
+ /**
274
+ * Returns the translations for the Add Keyword modal.
275
+ *
276
+ * These strings are not escaped because they're meant to be used with React
277
+ * which already takes care of that. If used in PHP, they should be escaped.
278
+ *
279
+ * @deprecated 15.5
280
+ * @codeCoverageIgnore
281
+ *
282
+ * @return array Translated text strings for the Add Keyword modal.
283
+ */
284
+ public function get_add_keyword_upsell_translations() {
285
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
286
+
287
+ return [
288
+ 'title' => __( 'Would you like to add more than one keyphrase?', 'wordpress-seo' ),
289
+ 'intro' => sprintf(
290
+ /* translators: %s expands to a 'Yoast SEO Premium' text linked to the yoast.com website. */
291
+ __( 'Great news: you can, with %s!', 'wordpress-seo' ),
292
+ '{{link}}Yoast SEO Premium{{/link}}'
293
+ ),
294
+ 'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
295
+ 'other' => sprintf(
296
+ /* translators: %s expands to 'Yoast SEO Premium'. */
297
+ __( 'Other benefits of %s for you:', 'wordpress-seo' ),
298
+ 'Yoast SEO Premium'
299
+ ),
300
+ 'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/add-keywords-popup' ),
301
+ 'buy' => sprintf(
302
+ /* translators: %s expands to 'Yoast SEO Premium'. */
303
+ __( 'Get %s', 'wordpress-seo' ),
304
+ 'Yoast SEO Premium'
305
+ ),
306
+ 'small' => __( '1 year free support and updates included!', 'wordpress-seo' ),
307
+ 'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
308
+ ];
309
+ }
310
  }
admin/formatter/class-post-metabox-formatter.php CHANGED
@@ -24,13 +24,6 @@ class WPSEO_Post_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface
24
  */
25
  private $permalink;
26
 
27
- /**
28
- * The date helper.
29
- *
30
- * @var WPSEO_Date_Helper
31
- */
32
- protected $date;
33
-
34
  /**
35
  * Constructor.
36
  *
@@ -41,7 +34,6 @@ class WPSEO_Post_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface
41
  public function __construct( $post, array $options, $structure ) {
42
  $this->post = $post;
43
  $this->permalink = $structure;
44
- $this->date = new WPSEO_Date_Helper();
45
  }
46
 
47
  /**
@@ -227,6 +219,6 @@ class WPSEO_Post_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface
227
  * @return string
228
  */
229
  private function get_metadesc_date() {
230
- return $this->date->format_translated( $this->post->post_date, 'M j, Y' );
231
  }
232
  }
24
  */
25
  private $permalink;
26
 
 
 
 
 
 
 
 
27
  /**
28
  * Constructor.
29
  *
34
  public function __construct( $post, array $options, $structure ) {
35
  $this->post = $post;
36
  $this->permalink = $structure;
 
37
  }
38
 
39
  /**
219
  * @return string
220
  */
221
  private function get_metadesc_date() {
222
+ return YoastSEO()->helpers->date->format_translated( $this->post->post_date, 'M j, Y' );
223
  }
224
  }
admin/menu/class-admin-menu.php CHANGED
@@ -39,7 +39,7 @@ class WPSEO_Admin_Menu extends WPSEO_Base_Menu {
39
  $this->get_manage_capability(),
40
  $this->get_page_identifier(),
41
  $this->get_admin_page_callback(),
42
- WPSEO_Utils::get_icon_svg(),
43
  '99.31337'
44
  );
45
 
39
  $this->get_manage_capability(),
40
  $this->get_page_identifier(),
41
  $this->get_admin_page_callback(),
42
+ $this->get_icon_svg(),
43
  '99.31337'
44
  );
45
 
admin/menu/class-base-menu.php CHANGED
@@ -136,7 +136,7 @@ abstract class WPSEO_Base_Menu implements WPSEO_WordPress_Integration {
136
  $submenu_page[3],
137
  $submenu_page[4],
138
  $submenu_page[5],
139
- WPSEO_Utils::get_icon_svg(),
140
  '99.31337'
141
  );
142
 
@@ -269,4 +269,22 @@ abstract class WPSEO_Base_Menu implements WPSEO_WordPress_Integration {
269
 
270
  return $title;
271
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  }
136
  $submenu_page[3],
137
  $submenu_page[4],
138
  $submenu_page[5],
139
+ $this->get_icon_svg(),
140
  '99.31337'
141
  );
142
 
269
 
270
  return $title;
271
  }
272
+
273
+ /**
274
+ * Returns a base64 URL for the svg for use in the menu.
275
+ *
276
+ * @param bool $base64 Whether or not to return base64'd output.
277
+ *
278
+ * @return string SVG icon.
279
+ */
280
+ public function get_icon_svg( $base64 = true ) {
281
+ $svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>';
282
+
283
+ if ( $base64 ) {
284
+ //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- This encoding is intended.
285
+ return 'data:image/svg+xml;base64,' . base64_encode( $svg );
286
+ }
287
+
288
+ return $svg;
289
+ }
290
  }
admin/menu/class-network-admin-menu.php CHANGED
@@ -36,7 +36,7 @@ class WPSEO_Network_Admin_Menu extends WPSEO_Base_Menu {
36
  $this->get_manage_capability(),
37
  $this->get_page_identifier(),
38
  [ $this, 'network_config_page' ],
39
- WPSEO_Utils::get_icon_svg()
40
  );
41
 
42
  $submenu_pages = $this->get_submenu_pages();
36
  $this->get_manage_capability(),
37
  $this->get_page_identifier(),
38
  [ $this, 'network_config_page' ],
39
+ $this->get_icon_svg()
40
  );
41
 
42
  $submenu_pages = $this->get_submenu_pages();
admin/tracking/class-tracking-settings-data.php CHANGED
@@ -45,7 +45,6 @@ class WPSEO_Tracking_Settings_Data implements WPSEO_Collection {
45
  'twitter_site',
46
  'youtube_url',
47
  'wikipedia_url',
48
- 'fbadminapp',
49
  'semrush_tokens',
50
  'zapier_api_key',
51
  ];
@@ -157,7 +156,6 @@ class WPSEO_Tracking_Settings_Data implements WPSEO_Collection {
157
  'twitter_card_type',
158
  'youtube_url',
159
  'wikipedia_url',
160
- 'fbadminapp',
161
  'indexables_indexing_completed',
162
  'semrush_integration_active',
163
  'semrush_tokens',
45
  'twitter_site',
46
  'youtube_url',
47
  'wikipedia_url',
 
48
  'semrush_tokens',
49
  'zapier_api_key',
50
  ];
156
  'twitter_card_type',
157
  'youtube_url',
158
  'wikipedia_url',
 
159
  'indexables_indexing_completed',
160
  'semrush_integration_active',
161
  'semrush_tokens',
admin/views/tabs/social/facebook.php CHANGED
@@ -27,8 +27,6 @@ $yform->light_switch( 'opengraph', __( 'Add Open Graph meta data', 'wordpress-se
27
  <div id="wpseo-opengraph-settings" style="display: none;">
28
  <?php
29
 
30
- $yform->textinput( 'fbadminapp', __( 'Facebook App ID', 'wordpress-seo' ) );
31
-
32
  if ( get_option( 'show_on_front' ) === 'posts' ) {
33
  $social_facebook_frontpage_help = new WPSEO_Admin_Help_Panel(
34
  'social-facebook-frontpage',
27
  <div id="wpseo-opengraph-settings" style="display: none;">
28
  <?php
29
 
 
 
30
  if ( get_option( 'show_on_front' ) === 'posts' ) {
31
  $social_facebook_frontpage_help = new WPSEO_Admin_Help_Panel(
32
  'social-facebook-frontpage',
css/dist/{admin-global-1540-rtl.css → admin-global-1550-rtl.css} RENAMED
File without changes
css/dist/{admin-global-1540.css → admin-global-1550.css} RENAMED
File without changes
css/dist/{adminbar-1540-rtl.css → adminbar-1550-rtl.css} RENAMED
File without changes
css/dist/{adminbar-1540.css → adminbar-1550.css} RENAMED
File without changes
css/dist/{alerts-1540-rtl.css → alerts-1550-rtl.css} RENAMED
File without changes
css/dist/{alerts-1540.css → alerts-1550.css} RENAMED
File without changes
css/dist/{dashboard-1540-rtl.css → dashboard-1550-rtl.css} RENAMED
File without changes
css/dist/{dashboard-1540.css → dashboard-1550.css} RENAMED
File without changes
css/dist/{edit-page-1540-rtl.css → edit-page-1550-rtl.css} RENAMED
File without changes
css/dist/{edit-page-1540.css → edit-page-1550.css} RENAMED
File without changes
css/dist/{elementor-1540-rtl.css → elementor-1550-rtl.css} RENAMED
File without changes
css/dist/{elementor-1540.css → elementor-1550.css} RENAMED
File without changes
css/dist/{featured-image-1540-rtl.css → featured-image-1550-rtl.css} RENAMED
File without changes
css/dist/{featured-image-1540.css → featured-image-1550.css} RENAMED
File without changes
css/dist/{filter-explanation-1540-rtl.css → filter-explanation-1550-rtl.css} RENAMED
File without changes
css/dist/{filter-explanation-1540.css → filter-explanation-1550.css} RENAMED
File without changes
css/dist/{icons-1540-rtl.css → icons-1550-rtl.css} RENAMED
File without changes
css/dist/{icons-1540.css → icons-1550.css} RENAMED
File without changes
css/dist/{inside-editor-1540-rtl.css → inside-editor-1550-rtl.css} RENAMED
File without changes
css/dist/{inside-editor-1540.css → inside-editor-1550.css} RENAMED
File without changes
css/dist/{metabox-1540-rtl.css → metabox-1550-rtl.css} RENAMED
File without changes
css/dist/{metabox-1540.css → metabox-1550.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1540-rtl.css → metabox-primary-category-1550-rtl.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1540.css → metabox-primary-category-1550.css} RENAMED
File without changes
css/dist/{modal-1540-rtl.css → modal-1550-rtl.css} RENAMED
File without changes
css/dist/{modal-1540.css → modal-1550.css} RENAMED
File without changes
css/dist/{monorepo-1540-rtl.css → monorepo-1550-rtl.css} RENAMED
File without changes
css/dist/{monorepo-1540.css → monorepo-1550.css} RENAMED
File without changes
css/dist/{notifications-1540-rtl.css → notifications-1550-rtl.css} RENAMED
File without changes
css/dist/{notifications-1540.css → notifications-1550.css} RENAMED
File without changes
css/dist/{schema-blocks-1540-rtl.css → schema-blocks-1550-rtl.css} RENAMED
File without changes
css/dist/{schema-blocks-1540.css → schema-blocks-1550.css} RENAMED
File without changes
css/dist/{score_icon-1540-rtl.css → score_icon-1550-rtl.css} RENAMED
File without changes
css/dist/{score_icon-1540.css → score_icon-1550.css} RENAMED
File without changes
css/dist/{search-appearance-1540-rtl.css → search-appearance-1550-rtl.css} RENAMED
File without changes
css/dist/{search-appearance-1540.css → search-appearance-1550.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1540-rtl.css → structured-data-blocks-1550-rtl.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1540.css → structured-data-blocks-1550.css} RENAMED
File without changes
css/dist/{toggle-switch-1540-rtl.css → toggle-switch-1550-rtl.css} RENAMED
File without changes
css/dist/{toggle-switch-1540.css → toggle-switch-1550.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1540-rtl.css → wpseo-dismissible-1550-rtl.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1540.css → wpseo-dismissible-1550.css} RENAMED
File without changes
css/dist/{yoast-components-1540-rtl.css → yoast-components-1550-rtl.css} RENAMED
File without changes
css/dist/{yoast-components-1540.css → yoast-components-1550.css} RENAMED
File without changes
css/dist/{yoast-extensions-1540-rtl.css → yoast-extensions-1550-rtl.css} RENAMED
File without changes
css/dist/{yoast-extensions-1540.css → yoast-extensions-1550.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1540-rtl.css → yst_plugin_tools-1550-rtl.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1540.css → yst_plugin_tools-1550.css} RENAMED
File without changes
css/dist/{yst_seo_score-1540-rtl.css → yst_seo_score-1550-rtl.css} RENAMED
File without changes
css/dist/{yst_seo_score-1540.css → yst_seo_score-1550.css} RENAMED
File without changes
inc/class-upgrade.php CHANGED
@@ -68,6 +68,7 @@ class WPSEO_Upgrade {
68
  '14.9-RC0' => 'upgrade_149',
69
  '15.1-RC0' => 'upgrade_151',
70
  '15.3-RC0' => 'upgrade_153',
 
71
  ];
72
 
73
  array_walk( $routines, [ $this, 'run_upgrade_routine' ], $version );
@@ -770,6 +771,21 @@ class WPSEO_Upgrade {
770
  WPSEO_Options::set( 'indexables_indexing_completed', $indexables_indexing_completed_value );
771
  }
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  /**
774
  * Sets the home_url option for the 15.1 upgrade routine.
775
  *
@@ -825,7 +841,7 @@ class WPSEO_Upgrade {
825
  // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Reason: Is it prepared already.
826
  $query = $wpdb->prepare(
827
  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: Too hard to fix.
828
- "DELETE FROM $indexable_table
829
  WHERE object_type = 'term'
830
  AND object_sub_type IN ("
831
  . \implode( ', ', \array_fill( 0, \count( $private_taxonomies ), '%s' ) )
68
  '14.9-RC0' => 'upgrade_149',
69
  '15.1-RC0' => 'upgrade_151',
70
  '15.3-RC0' => 'upgrade_153',
71
+ '15.5-RC0' => 'upgrade_155',
72
  ];
73
 
74
  array_walk( $routines, [ $this, 'run_upgrade_routine' ], $version );
771
  WPSEO_Options::set( 'indexables_indexing_completed', $indexables_indexing_completed_value );
772
  }
773
 
774
+ /**
775
+ * Performs the 15.5 upgrade.
776
+ *
777
+ * @return void
778
+ */
779
+ private function upgrade_155() {
780
+ // Unset the fbadminapp value in the wpseo_social option.
781
+ $wpseo_social_option = get_option( 'wpseo_social' );
782
+
783
+ if ( isset( $wpseo_social_option['fbadminapp'] ) ) {
784
+ unset( $wpseo_social_option['fbadminapp'] );
785
+ update_option( 'wpseo_social', $wpseo_social_option );
786
+ }
787
+ }
788
+
789
  /**
790
  * Sets the home_url option for the 15.1 upgrade routine.
791
  *
841
  // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Reason: Is it prepared already.
842
  $query = $wpdb->prepare(
843
  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: Too hard to fix.
844
+ "DELETE FROM $indexable_table
845
  WHERE object_type = 'term'
846
  AND object_sub_type IN ("
847
  . \implode( ', ', \array_fill( 0, \count( $private_taxonomies ), '%s' ) )
inc/class-wpseo-replace-vars.php CHANGED
@@ -47,13 +47,6 @@ class WPSEO_Replace_Vars {
47
  */
48
  protected $args;
49
 
50
- /**
51
- * The date helper.
52
- *
53
- * @var WPSEO_Date_Helper
54
- */
55
- protected $date;
56
-
57
  /**
58
  * Help texts for use in WPSEO -> Search appearance tabs.
59
  *
@@ -68,13 +61,6 @@ class WPSEO_Replace_Vars {
68
  */
69
  protected static $external_replacements = [];
70
 
71
- /**
72
- * Constructor.
73
- */
74
- public function __construct() {
75
- $this->date = new WPSEO_Date_Helper();
76
- }
77
-
78
  /**
79
  * Setup the help texts and external replacements as statics so they will be available to all instances.
80
  */
@@ -225,6 +211,19 @@ class WPSEO_Replace_Vars {
225
  return $string;
226
  }
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  /**
229
  * Retrieve the replacements for the variables found.
230
  *
@@ -319,7 +318,7 @@ class WPSEO_Replace_Vars {
319
 
320
  if ( $this->args->post_date !== '' ) {
321
  // Returns a string.
322
- $replacement = $this->date->format_translated( $this->args->post_date, get_option( 'date_format' ) );
323
  }
324
  else {
325
  if ( get_query_var( 'day' ) && get_query_var( 'day' ) !== '' ) {
@@ -926,7 +925,7 @@ class WPSEO_Replace_Vars {
926
  $replacement = null;
927
 
928
  if ( ! empty( $this->args->post_modified ) ) {
929
- $replacement = $this->date->format_translated( $this->args->post_modified, get_option( 'date_format' ) );
930
  }
931
 
932
  return $replacement;
47
  */
48
  protected $args;
49
 
 
 
 
 
 
 
 
50
  /**
51
  * Help texts for use in WPSEO -> Search appearance tabs.
52
  *
61
  */
62
  protected static $external_replacements = [];
63
 
 
 
 
 
 
 
 
64
  /**
65
  * Setup the help texts and external replacements as statics so they will be available to all instances.
66
  */
211
  return $string;
212
  }
213
 
214
+ /**
215
+ * Checks whether the given replacement variable has already been registered or not.
216
+ *
217
+ * @param string $replacement_variable The replacement variable to check, including the variable delimiter (e.g. `%%var%%`).
218
+ *
219
+ * @return bool `true` if the replacement variable has already been registered.
220
+ */
221
+ public function has_been_registered( $replacement_variable ) {
222
+ $replacement_variable = self::remove_var_delimiter( $replacement_variable );
223
+
224
+ return isset( self::$external_replacements[ $replacement_variable ] );
225
+ }
226
+
227
  /**
228
  * Retrieve the replacements for the variables found.
229
  *
318
 
319
  if ( $this->args->post_date !== '' ) {
320
  // Returns a string.
321
+ $replacement = YoastSEO()->helpers->date->format_translated( $this->args->post_date, get_option( 'date_format' ) );
322
  }
323
  else {
324
  if ( get_query_var( 'day' ) && get_query_var( 'day' ) !== '' ) {
925
  $replacement = null;
926
 
927
  if ( ! empty( $this->args->post_modified ) ) {
928
+ $replacement = YoastSEO()->helpers->date->format_translated( $this->args->post_modified, get_option( 'date_format' ) );
929
  }
930
 
931
  return $replacement;
inc/class-wpseo-utils.php CHANGED
@@ -21,28 +21,6 @@ class WPSEO_Utils {
21
  */
22
  public static $has_filters;
23
 
24
- /**
25
- * Check whether the current user is allowed to access the configuration.
26
- *
27
- * @since 1.8.0
28
- *
29
- * @return boolean
30
- */
31
- public static function grant_access() {
32
- // @todo Add deprecation warning.
33
- if ( ! is_multisite() ) {
34
- return true;
35
- }
36
-
37
- $options = get_site_option( 'wpseo_ms' );
38
-
39
- if ( empty( $options['access'] ) || $options['access'] === 'admin' ) {
40
- return current_user_can( 'wpseo_manage_options' );
41
- }
42
-
43
- return is_super_admin();
44
- }
45
-
46
  /**
47
  * Check whether file editing is allowed for the .htaccess and robots.txt files.
48
  *
@@ -116,24 +94,6 @@ class WPSEO_Utils {
116
  return YoastSEO()->helpers->url->is_relative( $url );
117
  }
118
 
119
- /**
120
- * List all the available user roles.
121
- *
122
- * @since 1.8.0
123
- * @deprecated 15.0
124
- * @codeCoverageIgnore
125
- *
126
- * @return array $roles
127
- */
128
- public static function get_roles() {
129
- _deprecated_function( __METHOD__, '15.0', 'wp_roles()->get_names()' );
130
- $wp_roles = wp_roles();
131
-
132
- $roles = $wp_roles->get_names();
133
-
134
- return $roles;
135
- }
136
-
137
  /**
138
  * Recursively trim whitespace round a string value or of string values within an array.
139
  * Only trims strings to avoid typecasting a variable (to string).
@@ -650,13 +610,7 @@ class WPSEO_Utils {
650
  * @return bool
651
  */
652
  public static function is_valid_datetime( $datetime ) {
653
- static $date_helper;
654
-
655
- if ( ! $date_helper ) {
656
- $date_helper = new WPSEO_Date_Helper();
657
- }
658
-
659
- return $date_helper->is_valid_datetime( $datetime );
660
  }
661
 
662
  /**
@@ -691,25 +645,6 @@ class WPSEO_Utils {
691
  return apply_filters( 'wpseo_format_admin_url', $formatted_url );
692
  }
693
 
694
- /**
695
- * Get plugin name from file.
696
- *
697
- * @since 2.3.3
698
- *
699
- * @param string $plugin Plugin path relative to plugins directory.
700
- *
701
- * @return string|bool
702
- */
703
- public static function get_plugin_name( $plugin ) {
704
- $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
705
-
706
- if ( $plugin_details['Name'] !== '' ) {
707
- return $plugin_details['Name'];
708
- }
709
-
710
- return false;
711
- }
712
-
713
  /**
714
  * Retrieves the sitename.
715
  *
@@ -810,77 +745,6 @@ class WPSEO_Utils {
810
  return YoastSEO()->helpers->url->home( $path, $scheme );
811
  }
812
 
813
- /**
814
- * Returns a base64 URL for the svg for use in the menu.
815
- *
816
- * @since 3.3.0
817
- *
818
- * @param bool $base64 Whether or not to return base64'd output.
819
- *
820
- * @return string
821
- */
822
- public static function get_icon_svg( $base64 = true ) {
823
- $svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>';
824
-
825
- if ( $base64 ) {
826
- return 'data:image/svg+xml;base64,' . base64_encode( $svg );
827
- }
828
-
829
- return $svg;
830
- }
831
-
832
- /**
833
- * Returns the SVG for the traffic light in the metabox.
834
- *
835
- * @return string
836
- */
837
- public static function traffic_light_svg() {
838
- return <<<'SVG'
839
- <svg class="yst-traffic-light init" version="1.1" xmlns="http://www.w3.org/2000/svg"
840
- role="img" aria-hidden="true" focusable="false"
841
- x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
842
- <g id="BG_1_">
843
- </g>
844
- <g id="traffic_light">
845
- <g>
846
- <g>
847
- <g>
848
- <path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
849
- M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
850
- c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
851
- </g>
852
- <g class="traffic-light-color traffic-light-red">
853
- <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
854
- <ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
855
- <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
856
- </g>
857
- <g class="traffic-light-color traffic-light-orange">
858
- <ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
859
- <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
860
- <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
861
- </g>
862
- <g class="traffic-light-color traffic-light-green">
863
- <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
864
- <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
865
- <ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
866
- </g>
867
- <g class="traffic-light-color traffic-light-empty">
868
- <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
869
- <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
870
- <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
871
- </g>
872
- <g class="traffic-light-color traffic-light-init">
873
- <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
874
- <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
875
- <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
876
- </g>
877
- </g>
878
- </g>
879
- </g>
880
- </svg>
881
- SVG;
882
- }
883
-
884
  /**
885
  * Checks if the WP-REST-API is available.
886
  *
@@ -1073,15 +937,6 @@ SVG;
1073
  return 'ERROR';
1074
  }
1075
 
1076
- /**
1077
- * Returns the unfiltered home URL.
1078
- *
1079
- * In case WPML is installed, returns the original home_url and not the WPML version.
1080
- * In case of a multisite setup we return the network_home_url.
1081
- *
1082
- * @return string The home url.
1083
- */
1084
-
1085
  /**
1086
  * Returns the unfiltered home URL.
1087
  *
@@ -1107,20 +962,6 @@ SVG;
1107
  return home_url();
1108
  }
1109
 
1110
- /**
1111
- * Checks if the current installation supports MyYoast access tokens.
1112
- *
1113
- * @codeCoverageIgnore
1114
- *
1115
- * @return bool True if access_tokens are supported.
1116
- *
1117
- * @deprecated 15.0
1118
- */
1119
- public static function has_access_token_support() {
1120
- _deprecated_function( __METHOD__, 'WPSEO 15.0' );
1121
- return false;
1122
- }
1123
-
1124
  /**
1125
  * Prepares data for outputting as JSON.
1126
  *
@@ -1146,44 +987,6 @@ SVG;
1146
  return wp_json_encode( $data, $flags );
1147
  }
1148
 
1149
- /**
1150
- * Output a Schema blob.
1151
- *
1152
- * @param array $graph The Schema graph array to output.
1153
- * @param string $class The (optional) class to add to the script tag.
1154
- *
1155
- * @return bool
1156
- */
1157
- public static function schema_output( $graph, $class = 'yoast-schema-graph' ) {
1158
- if ( ! is_array( $graph ) || empty( $graph ) ) {
1159
- return false;
1160
- }
1161
-
1162
- // phpcs:ignore WordPress.Security.EscapeOutput -- Escaping happens in WPSEO_Utils::schema_tag, which should be whitelisted.
1163
- echo self::schema_tag( $graph, $class );
1164
- return true;
1165
- }
1166
-
1167
- /**
1168
- * Returns a script tag with Schema blob.
1169
- *
1170
- * @param array $graph The Schema graph array to output.
1171
- * @param string $class The (optional) class to add to the script tag.
1172
- *
1173
- * @return false|string A schema blob with script tags.
1174
- */
1175
- public static function schema_tag( $graph, $class = 'yoast-schema-graph' ) {
1176
- if ( ! is_array( $graph ) || empty( $graph ) ) {
1177
- return false;
1178
- }
1179
-
1180
- $output = [
1181
- '@context' => 'https://schema.org',
1182
- '@graph' => $graph,
1183
- ];
1184
- return "<script type='application/ld+json' class='" . esc_attr( $class ) . "'>" . self::format_json_encode( $output ) . '</script>' . "\n";
1185
- }
1186
-
1187
  /**
1188
  * Extends the allowed post tags with accessibility-related attributes.
1189
  *
@@ -1350,6 +1153,40 @@ SVG;
1350
  return $enabled_features;
1351
  }
1352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1353
  /**
1354
  * Standardize whitespace in a string.
1355
  *
@@ -1430,4 +1267,184 @@ SVG;
1430
 
1431
  return YoastSEO()->helpers->woocommerce->is_active();
1432
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1433
  }
21
  */
22
  public static $has_filters;
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * Check whether file editing is allowed for the .htaccess and robots.txt files.
26
  *
94
  return YoastSEO()->helpers->url->is_relative( $url );
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  /**
98
  * Recursively trim whitespace round a string value or of string values within an array.
99
  * Only trims strings to avoid typecasting a variable (to string).
610
  * @return bool
611
  */
612
  public static function is_valid_datetime( $datetime ) {
613
+ return YoastSEO()->helpers->date->is_valid_datetime( $datetime );
 
 
 
 
 
 
614
  }
615
 
616
  /**
645
  return apply_filters( 'wpseo_format_admin_url', $formatted_url );
646
  }
647
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
  /**
649
  * Retrieves the sitename.
650
  *
745
  return YoastSEO()->helpers->url->home( $path, $scheme );
746
  }
747
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
748
  /**
749
  * Checks if the WP-REST-API is available.
750
  *
937
  return 'ERROR';
938
  }
939
 
 
 
 
 
 
 
 
 
 
940
  /**
941
  * Returns the unfiltered home URL.
942
  *
962
  return home_url();
963
  }
964
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
965
  /**
966
  * Prepares data for outputting as JSON.
967
  *
987
  return wp_json_encode( $data, $flags );
988
  }
989
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990
  /**
991
  * Extends the allowed post tags with accessibility-related attributes.
992
  *
1153
  return $enabled_features;
1154
  }
1155
 
1156
+ /* ********************* DEPRECATED METHODS ********************* */
1157
+
1158
+ /**
1159
+ * List all the available user roles.
1160
+ *
1161
+ * @since 1.8.0
1162
+ * @deprecated 15.0
1163
+ * @codeCoverageIgnore
1164
+ *
1165
+ * @return array $roles
1166
+ */
1167
+ public static function get_roles() {
1168
+ _deprecated_function( __METHOD__, '15.0', 'wp_roles()->get_names()' );
1169
+ $wp_roles = wp_roles();
1170
+
1171
+ $roles = $wp_roles->get_names();
1172
+
1173
+ return $roles;
1174
+ }
1175
+
1176
+ /**
1177
+ * Checks if the current installation supports MyYoast access tokens.
1178
+ *
1179
+ * @codeCoverageIgnore
1180
+ *
1181
+ * @return bool True if access_tokens are supported.
1182
+ *
1183
+ * @deprecated 15.0
1184
+ */
1185
+ public static function has_access_token_support() {
1186
+ _deprecated_function( __METHOD__, 'WPSEO 15.0' );
1187
+ return false;
1188
+ }
1189
+
1190
  /**
1191
  * Standardize whitespace in a string.
1192
  *
1267
 
1268
  return YoastSEO()->helpers->woocommerce->is_active();
1269
  }
1270
+
1271
+ /**
1272
+ * Outputs a Schema blob.
1273
+ *
1274
+ * @deprecated 15.5
1275
+ * @codeCoverageIgnore
1276
+ *
1277
+ * @param array $graph The Schema graph array to output.
1278
+ * @param string $class The (optional) class to add to the script tag.
1279
+ *
1280
+ * @return bool
1281
+ */
1282
+ public static function schema_output( $graph, $class = 'yoast-schema-graph' ) {
1283
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1284
+
1285
+ if ( ! is_array( $graph ) || empty( $graph ) ) {
1286
+ return false;
1287
+ }
1288
+
1289
+ // phpcs:ignore WordPress.Security.EscapeOutput -- Escaping happens in WPSEO_Utils::schema_tag, which should be whitelisted.
1290
+ echo self::schema_tag( $graph, $class );
1291
+ return true;
1292
+ }
1293
+
1294
+ /**
1295
+ * Returns a script tag with Schema blob.
1296
+ *
1297
+ * @deprecated 15.5
1298
+ * @codeCoverageIgnore
1299
+ *
1300
+ * @param array $graph The Schema graph array to output.
1301
+ * @param string $class The (optional) class to add to the script tag.
1302
+ *
1303
+ * @return false|string A schema blob with script tags.
1304
+ */
1305
+ public static function schema_tag( $graph, $class = 'yoast-schema-graph' ) {
1306
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1307
+
1308
+ if ( ! is_array( $graph ) || empty( $graph ) ) {
1309
+ return false;
1310
+ }
1311
+
1312
+ $output = [
1313
+ '@context' => 'https://schema.org',
1314
+ '@graph' => $graph,
1315
+ ];
1316
+ return "<script type='application/ld+json' class='" . esc_attr( $class ) . "'>" . self::format_json_encode( $output ) . '</script>' . "\n";
1317
+ }
1318
+
1319
+ /**
1320
+ * Returns the SVG for the traffic light in the metabox.
1321
+ *
1322
+ * @deprecated 15.5
1323
+ * @codeCoverageIgnore
1324
+ *
1325
+ * @return string
1326
+ */
1327
+ public static function traffic_light_svg() {
1328
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1329
+
1330
+ return <<<'SVG'
1331
+ <svg class="yst-traffic-light init" version="1.1" xmlns="http://www.w3.org/2000/svg"
1332
+ role="img" aria-hidden="true" focusable="false"
1333
+ x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
1334
+ <g id="BG_1_">
1335
+ </g>
1336
+ <g id="traffic_light">
1337
+ <g>
1338
+ <g>
1339
+ <g>
1340
+ <path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
1341
+ M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
1342
+ c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
1343
+ </g>
1344
+ <g class="traffic-light-color traffic-light-red">
1345
+ <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
1346
+ <ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
1347
+ <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
1348
+ </g>
1349
+ <g class="traffic-light-color traffic-light-orange">
1350
+ <ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
1351
+ <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
1352
+ <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
1353
+ </g>
1354
+ <g class="traffic-light-color traffic-light-green">
1355
+ <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
1356
+ <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
1357
+ <ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
1358
+ </g>
1359
+ <g class="traffic-light-color traffic-light-empty">
1360
+ <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
1361
+ <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
1362
+ <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
1363
+ </g>
1364
+ <g class="traffic-light-color traffic-light-init">
1365
+ <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
1366
+ <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
1367
+ <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
1368
+ </g>
1369
+ </g>
1370
+ </g>
1371
+ </g>
1372
+ </svg>
1373
+ SVG;
1374
+ }
1375
+
1376
+ /**
1377
+ * Gets the plugin name from file.
1378
+ *
1379
+ * @deprecated 15.5
1380
+ * @codeCoverageIgnore
1381
+ *
1382
+ * @since 2.3.3
1383
+ *
1384
+ * @param string $plugin Plugin path relative to plugins directory.
1385
+ *
1386
+ * @return string|bool
1387
+ */
1388
+ public static function get_plugin_name( $plugin ) {
1389
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1390
+
1391
+ $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
1392
+
1393
+ if ( $plugin_details['Name'] !== '' ) {
1394
+ return $plugin_details['Name'];
1395
+ }
1396
+
1397
+ return false;
1398
+ }
1399
+
1400
+ /**
1401
+ * Returns a base64 URL for the svg for use in the menu.
1402
+ *
1403
+ * @deprecated 15.5
1404
+ * @codeCoverageIgnore
1405
+ *
1406
+ * @since 3.3.0
1407
+ *
1408
+ * @param bool $base64 Whether or not to return base64'd output.
1409
+ *
1410
+ * @return string
1411
+ */
1412
+ public static function get_icon_svg( $base64 = true ) {
1413
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1414
+
1415
+ $svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>';
1416
+
1417
+ if ( $base64 ) {
1418
+ //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- This encoding is intended.
1419
+ return 'data:image/svg+xml;base64,' . base64_encode( $svg );
1420
+ }
1421
+
1422
+ return $svg;
1423
+ }
1424
+
1425
+ /**
1426
+ * Checks whether the current user is allowed to access the configuration.
1427
+ *
1428
+ * @deprecated 15.5
1429
+ * @codeCoverageIgnore
1430
+ *
1431
+ * @since 1.8.0
1432
+ *
1433
+ * @return boolean
1434
+ */
1435
+ public static function grant_access() {
1436
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
1437
+
1438
+ if ( ! is_multisite() ) {
1439
+ return true;
1440
+ }
1441
+
1442
+ $options = get_site_option( 'wpseo_ms' );
1443
+
1444
+ if ( empty( $options['access'] ) || $options['access'] === 'admin' ) {
1445
+ return current_user_can( 'wpseo_manage_options' );
1446
+ }
1447
+
1448
+ return is_super_admin();
1449
+ }
1450
  }
inc/date-helper.php CHANGED
@@ -21,13 +21,7 @@ class WPSEO_Date_Helper {
21
  * @return string The formatted date.
22
  */
23
  public function format( $date, $format = DATE_W3C ) {
24
- $immutable_date = date_create_immutable_from_format( 'Y-m-d H:i:s', $date, new DateTimeZone( 'UTC' ) );
25
-
26
- if ( ! $immutable_date ) {
27
- return $date;
28
- }
29
-
30
- return $immutable_date->format( $format );
31
  }
32
 
33
  /**
@@ -39,13 +33,7 @@ class WPSEO_Date_Helper {
39
  * @return string The formatted date.
40
  */
41
  public function format_timestamp( $timestamp, $format = DATE_W3C ) {
42
- $immutable_date = date_create_immutable_from_format( 'U', $timestamp, new DateTimeZone( 'UTC' ) );
43
-
44
- if ( ! $immutable_date ) {
45
- return $timestamp;
46
- }
47
-
48
- return $immutable_date->format( $format );
49
  }
50
 
51
  /**
@@ -57,7 +45,7 @@ class WPSEO_Date_Helper {
57
  * @return string The formatted and translated date.
58
  */
59
  public function format_translated( $date, $format = DATE_W3C ) {
60
- return date_i18n( $format, $this->format( $date, 'U' ) );
61
  }
62
 
63
  /**
@@ -68,14 +56,6 @@ class WPSEO_Date_Helper {
68
  * @return bool True when datatime is valid.
69
  */
70
  public function is_valid_datetime( $datetime ) {
71
- if ( substr( $datetime, 0, 1 ) === '-' ) {
72
- return false;
73
- }
74
-
75
- try {
76
- return new DateTime( $datetime ) !== false;
77
- } catch ( Exception $exception ) {
78
- return false;
79
- }
80
  }
81
  }
21
  * @return string The formatted date.
22
  */
23
  public function format( $date, $format = DATE_W3C ) {
24
+ return YoastSEO()->helpers->date->format( $date, $format );
 
 
 
 
 
 
25
  }
26
 
27
  /**
33
  * @return string The formatted date.
34
  */
35
  public function format_timestamp( $timestamp, $format = DATE_W3C ) {
36
+ return YoastSEO()->helpers->date->format_timestamp( $timestamp, $format );
 
 
 
 
 
 
37
  }
38
 
39
  /**
45
  * @return string The formatted and translated date.
46
  */
47
  public function format_translated( $date, $format = DATE_W3C ) {
48
+ return YoastSEO()->helpers->date->format_translated( $date, $format );
49
  }
50
 
51
  /**
56
  * @return bool True when datatime is valid.
57
  */
58
  public function is_valid_datetime( $datetime ) {
59
+ return YoastSEO()->helpers->date->is_valid_datetime( $datetime );
 
 
 
 
 
 
 
 
60
  }
61
  }
inc/options/class-wpseo-option-ms.php CHANGED
@@ -95,6 +95,7 @@ class WPSEO_Option_MS extends WPSEO_Option {
95
  "{$allow_prefix}enable_text_link_counter" => true,
96
  "{$allow_prefix}enable_headless_rest_endpoints" => true,
97
  "{$allow_prefix}tracking" => true,
 
98
  "{$allow_prefix}semrush_integration_active" => true,
99
  "{$allow_prefix}zapier_integration_active" => true,
100
  ];
95
  "{$allow_prefix}enable_text_link_counter" => true,
96
  "{$allow_prefix}enable_headless_rest_endpoints" => true,
97
  "{$allow_prefix}tracking" => true,
98
+ "{$allow_prefix}enable_enhanced_slack_sharing" => true,
99
  "{$allow_prefix}semrush_integration_active" => true,
100
  "{$allow_prefix}zapier_integration_active" => true,
101
  ];
inc/options/class-wpseo-option-social.php CHANGED
@@ -44,8 +44,6 @@ class WPSEO_Option_Social extends WPSEO_Option {
44
  'twitter_card_type' => 'summary_large_image',
45
  'youtube_url' => '',
46
  'wikipedia_url' => '',
47
- // Form field, but not always available.
48
- 'fbadminapp' => '', // Facebook app ID.
49
  ];
50
 
51
  /**
@@ -56,7 +54,6 @@ class WPSEO_Option_Social extends WPSEO_Option {
56
  public $ms_exclude = [
57
  /* Privacy. */
58
  'pinterestverify',
59
- 'fbadminapp',
60
  ];
61
 
62
  /**
@@ -218,10 +215,6 @@ class WPSEO_Option_Social extends WPSEO_Option {
218
  case 'twitter':
219
  $clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : false );
220
  break;
221
-
222
- case 'fbadminapp':
223
- $this->validate_facebook_app_id( $key, $dirty, $old, $clean );
224
- break;
225
  }
226
  }
227
 
44
  'twitter_card_type' => 'summary_large_image',
45
  'youtube_url' => '',
46
  'wikipedia_url' => '',
 
 
47
  ];
48
 
49
  /**
54
  public $ms_exclude = [
55
  /* Privacy. */
56
  'pinterestverify',
 
57
  ];
58
 
59
  /**
215
  case 'twitter':
216
  $clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : false );
217
  break;
 
 
 
 
218
  }
219
  }
220
 
inc/options/class-wpseo-option-wpseo.php CHANGED
@@ -402,6 +402,7 @@ class WPSEO_Option_Wpseo extends WPSEO_Option {
402
  'enable_text_link_counter' => false,
403
  'enable_headless_rest_endpoints' => false,
404
  'tracking' => false,
 
405
  'semrush_integration_active' => false,
406
  'zapier_integration_active' => false,
407
  ];
402
  'enable_text_link_counter' => false,
403
  'enable_headless_rest_endpoints' => false,
404
  'tracking' => false,
405
+ 'enable_enhanced_slack_sharing' => false,
406
  'semrush_integration_active' => false,
407
  'zapier_integration_active' => false,
408
  ];
inc/options/class-wpseo-option.php CHANGED
@@ -426,29 +426,37 @@ abstract class WPSEO_Option {
426
  /**
427
  * Validates a Facebook App ID.
428
  *
 
 
 
429
  * @param string $key Key to check, in this case: the Facebook App ID field name.
430
  * @param array $dirty Dirty data with the new values.
431
  * @param array $old Old data.
432
  * @param array $clean Clean data by reference, normally the default values.
433
  */
434
  public function validate_facebook_app_id( $key, $dirty, $old, &$clean ) {
 
 
435
  if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
436
  $url = 'https://graph.facebook.com/' . $dirty[ $key ];
437
 
438
  $response = wp_remote_get( $url );
439
- // These filters are used in the tests.
440
  /**
441
  * Filter: 'validate_facebook_app_id_api_response_code' - Allows to filter the Faceboook API response code.
442
  *
 
 
443
  * @api int $response_code The Facebook API response header code.
444
  */
445
- $response_code = apply_filters( 'validate_facebook_app_id_api_response_code', wp_remote_retrieve_response_code( $response ) );
446
  /**
447
  * Filter: 'validate_facebook_app_id_api_response_body' - Allows to filter the Faceboook API response body.
448
  *
 
 
449
  * @api string $response_body The Facebook API JSON response body.
450
  */
451
- $response_body = apply_filters( 'validate_facebook_app_id_api_response_body', wp_remote_retrieve_body( $response ) );
452
  $response_object = json_decode( $response_body );
453
 
454
  /*
426
  /**
427
  * Validates a Facebook App ID.
428
  *
429
+ * @deprecated 15.5
430
+ * @codeCoverageIgnore
431
+ *
432
  * @param string $key Key to check, in this case: the Facebook App ID field name.
433
  * @param array $dirty Dirty data with the new values.
434
  * @param array $old Old data.
435
  * @param array $clean Clean data by reference, normally the default values.
436
  */
437
  public function validate_facebook_app_id( $key, $dirty, $old, &$clean ) {
438
+ _deprecated_function( __METHOD__, 'WPSEO 15.5' );
439
+
440
  if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
441
  $url = 'https://graph.facebook.com/' . $dirty[ $key ];
442
 
443
  $response = wp_remote_get( $url );
 
444
  /**
445
  * Filter: 'validate_facebook_app_id_api_response_code' - Allows to filter the Faceboook API response code.
446
  *
447
+ * @deprecated 15.5
448
+ *
449
  * @api int $response_code The Facebook API response header code.
450
  */
451
+ $response_code = apply_filters_deprecated( 'validate_facebook_app_id_api_response_code', wp_remote_retrieve_response_code( $response ), 'WPSEO 15.5' );
452
  /**
453
  * Filter: 'validate_facebook_app_id_api_response_body' - Allows to filter the Faceboook API response body.
454
  *
455
+ * @deprecated 15.5
456
+ *
457
  * @api string $response_body The Facebook API JSON response body.
458
  */
459
+ $response_body = apply_filters_deprecated( 'validate_facebook_app_id_api_response_body', wp_remote_retrieve_body( $response ), 'WPSEO 15.5' );
460
  $response_object = json_decode( $response_body );
461
 
462
  /*
inc/sitemaps/class-author-sitemap-provider.php CHANGED
@@ -12,20 +12,6 @@ use Yoast\WP\SEO\Helpers\Author_Archive_Helper;
12
  */
13
  class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider {
14
 
15
- /**
16
- * The date helper.
17
- *
18
- * @var WPSEO_Date_Helper
19
- */
20
- protected $date;
21
-
22
- /**
23
- * WPSEO_Author_Sitemap_Provider constructor.
24
- */
25
- public function __construct() {
26
- $this->date = new WPSEO_Date_Helper();
27
- }
28
-
29
  /**
30
  * Check if provider supports given item type.
31
  *
@@ -91,7 +77,7 @@ class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider {
91
  $user = get_user_by( 'id', $user_id );
92
  $index[] = [
93
  'loc' => WPSEO_Sitemaps_Router::get_base_url( 'author-sitemap' . $page . '.xml' ),
94
- 'lastmod' => ( $user->_yoast_wpseo_profile_updated ) ? $this->date->format_timestamp( $user->_yoast_wpseo_profile_updated ) : null,
95
  ];
96
 
97
  ++$page;
12
  */
13
  class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider {
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  /**
16
  * Check if provider supports given item type.
17
  *
77
  $user = get_user_by( 'id', $user_id );
78
  $index[] = [
79
  'loc' => WPSEO_Sitemaps_Router::get_base_url( 'author-sitemap' . $page . '.xml' ),
80
+ 'lastmod' => ( $user->_yoast_wpseo_profile_updated ) ? YoastSEO()->helpers->date->format_timestamp( $user->_yoast_wpseo_profile_updated ) : null,
81
  ];
82
 
83
  ++$page;
inc/sitemaps/class-sitemaps-renderer.php CHANGED
@@ -38,13 +38,6 @@ class WPSEO_Sitemaps_Renderer {
38
  */
39
  protected $needs_conversion = false;
40
 
41
- /**
42
- * The date helper.
43
- *
44
- * @var WPSEO_Date_Helper
45
- */
46
- protected $date;
47
-
48
  /**
49
  * Set up object properties.
50
  */
@@ -53,7 +46,6 @@ class WPSEO_Sitemaps_Renderer {
53
  $this->stylesheet = '<?xml-stylesheet type="text/xsl" href="' . esc_url( $stylesheet_url ) . '"?>';
54
  $this->charset = get_bloginfo( 'charset' );
55
  $this->output_charset = $this->charset;
56
- $this->date = new WPSEO_Date_Helper();
57
 
58
  if (
59
  $this->charset !== 'UTF-8'
@@ -192,7 +184,7 @@ class WPSEO_Sitemaps_Renderer {
192
  $date = null;
193
 
194
  if ( ! empty( $url['lastmod'] ) ) {
195
- $date = $this->date->format( $url['lastmod'] );
196
  }
197
 
198
  $url['loc'] = htmlspecialchars( $url['loc'], ENT_COMPAT, $this->output_charset, false );
@@ -221,7 +213,7 @@ class WPSEO_Sitemaps_Renderer {
221
 
222
  if ( ! empty( $url['mod'] ) ) {
223
  // Create a DateTime object date in the correct timezone.
224
- $date = $this->date->format( $url['mod'] );
225
  }
226
 
227
  $url['loc'] = htmlspecialchars( $url['loc'], ENT_COMPAT, $this->output_charset, false );
38
  */
39
  protected $needs_conversion = false;
40
 
 
 
 
 
 
 
 
41
  /**
42
  * Set up object properties.
43
  */
46
  $this->stylesheet = '<?xml-stylesheet type="text/xsl" href="' . esc_url( $stylesheet_url ) . '"?>';
47
  $this->charset = get_bloginfo( 'charset' );
48
  $this->output_charset = $this->charset;
 
49
 
50
  if (
51
  $this->charset !== 'UTF-8'
184
  $date = null;
185
 
186
  if ( ! empty( $url['lastmod'] ) ) {
187
+ $date = YoastSEO()->helpers->date->format( $url['lastmod'] );
188
  }
189
 
190
  $url['loc'] = htmlspecialchars( $url['loc'], ENT_COMPAT, $this->output_charset, false );
213
 
214
  if ( ! empty( $url['mod'] ) ) {
215
  // Create a DateTime object date in the correct timezone.
216
+ $date = YoastSEO()->helpers->date->format( $url['mod'] );
217
  }
218
 
219
  $url['loc'] = htmlspecialchars( $url['loc'], ENT_COMPAT, $this->output_charset, false );
inc/sitemaps/class-sitemaps.php CHANGED
@@ -91,13 +91,6 @@ class WPSEO_Sitemaps {
91
  */
92
  public $providers;
93
 
94
- /**
95
- * The date helper.
96
- *
97
- * @var WPSEO_Date_Helper
98
- */
99
- protected $date;
100
-
101
  /**
102
  * Class constructor.
103
  */
@@ -112,7 +105,6 @@ class WPSEO_Sitemaps {
112
  $this->router = new WPSEO_Sitemaps_Router();
113
  $this->renderer = new WPSEO_Sitemaps_Renderer();
114
  $this->cache = new WPSEO_Sitemaps_Cache();
115
- $this->date = new WPSEO_Date_Helper();
116
 
117
  if ( ! empty( $_SERVER['SERVER_PROTOCOL'] ) ) {
118
  $this->http_protocol = sanitize_text_field( wp_unslash( $_SERVER['SERVER_PROTOCOL'] ) );
@@ -440,7 +432,7 @@ class WPSEO_Sitemaps {
440
  $expires = YEAR_IN_SECONDS;
441
  header( 'Pragma: public' );
442
  header( 'Cache-Control: max-age=' . $expires );
443
- header( 'Expires: ' . $this->date->format_timestamp( ( time() + $expires ), 'D, d M Y H:i:s' ) . ' GMT' );
444
 
445
  // Don't use WP_Filesystem() here because that's not initialized yet. See https://yoast.atlassian.net/browse/QAK-2043.
446
  readfile( WPSEO_PATH . 'css/main-sitemap.xsl' );
@@ -538,7 +530,7 @@ class WPSEO_Sitemaps {
538
  * @return string
539
  */
540
  public function get_last_modified( $post_types ) {
541
- return $this->date->format( self::get_last_modified_gmt( $post_types ) );
542
  }
543
 
544
  /**
91
  */
92
  public $providers;
93
 
 
 
 
 
 
 
 
94
  /**
95
  * Class constructor.
96
  */
105
  $this->router = new WPSEO_Sitemaps_Router();
106
  $this->renderer = new WPSEO_Sitemaps_Renderer();
107
  $this->cache = new WPSEO_Sitemaps_Cache();
 
108
 
109
  if ( ! empty( $_SERVER['SERVER_PROTOCOL'] ) ) {
110
  $this->http_protocol = sanitize_text_field( wp_unslash( $_SERVER['SERVER_PROTOCOL'] ) );
432
  $expires = YEAR_IN_SECONDS;
433
  header( 'Pragma: public' );
434
  header( 'Cache-Control: max-age=' . $expires );
435
+ header( 'Expires: ' . YoastSEO()->helpers->date->format_timestamp( ( time() + $expires ), 'D, d M Y H:i:s' ) . ' GMT' );
436
 
437
  // Don't use WP_Filesystem() here because that's not initialized yet. See https://yoast.atlassian.net/browse/QAK-2043.
438
  readfile( WPSEO_PATH . 'css/main-sitemap.xsl' );
530
  * @return string
531
  */
532
  public function get_last_modified( $post_types ) {
533
+ return YoastSEO()->helpers->date->format( self::get_last_modified_gmt( $post_types ) );
534
  }
535
 
536
  /**
js/dist/{admin-global-1540.js → admin-global-1550.js} RENAMED
File without changes
js/dist/analysis-1540.js DELETED
@@ -1,21 +0,0 @@
1
- !function(a){var e={};function t(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return a[i].call(n.exports,n,n.exports,t),n.l=!0,n.exports}t.m=a,t.c=e,t.d=function(a,e,i){t.o(a,e)||Object.defineProperty(a,e,{enumerable:!0,get:i})},t.r=function(a){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})},t.t=function(a,e){if(1&e&&(a=t(a)),8&e)return a;if(4&e&&"object"==typeof a&&a&&a.__esModule)return a;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:a}),2&e&&"string"!=typeof a)for(var n in a)t.d(i,n,function(e){return a[e]}.bind(null,n));return i},t.n=function(a){var e=a&&a.__esModule?function(){return a.default}:function(){return a};return t.d(e,"a",e),e},t.o=function(a,e){return Object.prototype.hasOwnProperty.call(a,e)},t.p="",t(t.s=558)}([,function(a,e,t){"use strict";t.r(e);var i=t(12),n=t(3),o="[object Symbol]";var r=function(a){return"symbol"==typeof a||Object(n.a)(a)&&Object(i.a)(a)==o},s=NaN;var d=function(a){return"number"==typeof a?a:r(a)?s:+a},l=t(15);var c=function(a,e){for(var t=-1,i=null==a?0:a.length,n=Array(i);++t<i;)n[t]=e(a[t],t,a);return n},u=t(2),p=1/0,z=l.a?l.a.prototype:void 0,m=z?z.toString:void 0;var g=function a(e){if("string"==typeof e)return e;if(Object(u.a)(e))return c(e,a)+"";if(r(e))return m?m.call(e):"";var t=e+"";return"0"==t&&1/e==-p?"-0":t};var f=function(a,e){return function(t,i){var n;if(void 0===t&&void 0===i)return e;if(void 0!==t&&(n=t),void 0!==i){if(void 0===n)return i;"string"==typeof t||"string"==typeof i?(t=g(t),i=g(i)):(t=d(t),i=d(i)),n=a(t,i)}return n}},w=f(function(a,e){return a+e},0);var y=function(a){var e=typeof a;return null!=a&&("object"==e||"function"==e)},b=NaN,h=/^\s+|\s+$/g,v=/^[-+]0x[0-9a-f]+$/i,k=/^0b[01]+$/i,j=/^0o[0-7]+$/i,_=parseInt;var x=function(a){if("number"==typeof a)return a;if(r(a))return b;if(y(a)){var e="function"==typeof a.valueOf?a.valueOf():a;a=y(e)?e+"":e}if("string"!=typeof a)return 0===a?a:+a;a=a.replace(h,"");var t=k.test(a);return t||j.test(a)?_(a.slice(2),t?2:8):v.test(a)?b:+a},q=1/0,E=1.7976931348623157e308;var A=function(a){return a?(a=x(a))===q||a===-q?(a<0?-1:1)*E:a==a?a:0:0===a?a:0};var S=function(a){var e=A(a),t=e%1;return e==e?t?e-t:e:0},O="Expected a function";var T=function(a,e){if("function"!=typeof e)throw new TypeError(O);return a=S(a),function(){if(--a<1)return e.apply(this,arguments)}};var M=function(a){return a},P="[object AsyncFunction]",R="[object Function]",C="[object GeneratorFunction]",D="[object Proxy]";var F=function(a){if(!y(a))return!1;var e=Object(i.a)(a);return e==R||e==C||e==P||e==D},B=t(8),L=B.a["__core-js_shared__"],I=function(){var a=/[^.]+$/.exec(L&&L.keys&&L.keys.IE_PROTO||"");return a?"Symbol(src)_1."+a:""}();var $=function(a){return!!I&&I in a},W=Function.prototype.toString;var N=function(a){if(null!=a){try{return W.call(a)}catch(a){}try{return a+""}catch(a){}}return""},U=/^\[object .+?Constructor\]$/,K=Function.prototype,H=Object.prototype,V=K.toString,Y=H.hasOwnProperty,G=RegExp("^"+V.call(Y).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");var Z=function(a){return!(!y(a)||$(a))&&(F(a)?G:U).test(N(a))};var J=function(a,e){return null==a?void 0:a[e]};var Q=function(a,e){var t=J(a,e);return Z(t)?t:void 0},X=Q(B.a,"WeakMap"),aa=X&&new X,ea=aa?function(a,e){return aa.set(a,e),a}:M,ta=Object.create,ia=function(){function a(){}return function(e){if(!y(e))return{};if(ta)return ta(e);a.prototype=e;var t=new a;return a.prototype=void 0,t}}();var na=function(a){return function(){var e=arguments;switch(e.length){case 0:return new a;case 1:return new a(e[0]);case 2:return new a(e[0],e[1]);case 3:return new a(e[0],e[1],e[2]);case 4:return new a(e[0],e[1],e[2],e[3]);case 5:return new a(e[0],e[1],e[2],e[3],e[4]);case 6:return new a(e[0],e[1],e[2],e[3],e[4],e[5]);case 7:return new a(e[0],e[1],e[2],e[3],e[4],e[5],e[6])}var t=ia(a.prototype),i=a.apply(t,e);return y(i)?i:t}},oa=1;var ra=function(a,e,t){var i=e&oa,n=na(a);return function e(){return(this&&this!==B.a&&this instanceof e?n:a).apply(i?t:this,arguments)}};var sa=function(a,e,t){switch(t.length){case 0:return a.call(e);case 1:return a.call(e,t[0]);case 2:return a.call(e,t[0],t[1]);case 3:return a.call(e,t[0],t[1],t[2])}return a.apply(e,t)},da=Math.max;var la=function(a,e,t,i){for(var n=-1,o=a.length,r=t.length,s=-1,d=e.length,l=da(o-r,0),c=Array(d+l),u=!i;++s<d;)c[s]=e[s];for(;++n<r;)(u||n<o)&&(c[t[n]]=a[n]);for(;l--;)c[s++]=a[n++];return c},ca=Math.max;var ua=function(a,e,t,i){for(var n=-1,o=a.length,r=-1,s=t.length,d=-1,l=e.length,c=ca(o-s,0),u=Array(c+l),p=!i;++n<c;)u[n]=a[n];for(var z=n;++d<l;)u[z+d]=e[d];for(;++r<s;)(p||n<o)&&(u[z+t[r]]=a[n++]);return u};var pa=function(a,e){for(var t=a.length,i=0;t--;)a[t]===e&&++i;return i};var za=function(){},ma=4294967295;function ga(a){this.__wrapped__=a,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=ma,this.__views__=[]}ga.prototype=ia(za.prototype),ga.prototype.constructor=ga;var fa=ga;var wa=function(){},ya=aa?function(a){return aa.get(a)}:wa,ba={},ha=Object.prototype.hasOwnProperty;var va=function(a){for(var e=a.name+"",t=ba[e],i=ha.call(ba,e)?t.length:0;i--;){var n=t[i],o=n.func;if(null==o||o==a)return n.name}return e};function ka(a,e){this.__wrapped__=a,this.__actions__=[],this.__chain__=!!e,this.__index__=0,this.__values__=void 0}ka.prototype=ia(za.prototype),ka.prototype.constructor=ka;var ja=ka;var _a=function(a,e){var t=-1,i=a.length;for(e||(e=Array(i));++t<i;)e[t]=a[t];return e};var xa=function(a){if(a instanceof fa)return a.clone();var e=new ja(a.__wrapped__,a.__chain__);return e.__actions__=_a(a.__actions__),e.__index__=a.__index__,e.__values__=a.__values__,e},qa=Object.prototype.hasOwnProperty;function Ea(a){if(Object(n.a)(a)&&!Object(u.a)(a)&&!(a instanceof fa)){if(a instanceof ja)return a;if(qa.call(a,"__wrapped__"))return xa(a)}return new ja(a)}Ea.prototype=za.prototype,Ea.prototype.constructor=Ea;var Aa=Ea;var Sa=function(a){var e=va(a),t=Aa[e];if("function"!=typeof t||!(e in fa.prototype))return!1;if(a===t)return!0;var i=ya(t);return!!i&&a===i[0]},Oa=800,Ta=16,Ma=Date.now;var Pa=function(a){var e=0,t=0;return function(){var i=Ma(),n=Ta-(i-t);if(t=i,n>0){if(++e>=Oa)return arguments[0]}else e=0;return a.apply(void 0,arguments)}},Ra=Pa(ea),Ca=/\{\n\/\* \[wrapped with (.+)\] \*/,Da=/,? & /;var Fa=function(a){var e=a.match(Ca);return e?e[1].split(Da):[]},Ba=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/;var La=function(a,e){var t=e.length;if(!t)return a;var i=t-1;return e[i]=(t>1?"& ":"")+e[i],e=e.join(t>2?", ":" "),a.replace(Ba,"{\n/* [wrapped with "+e+"] */\n")};var Ia=function(a){return function(){return a}},$a=function(){try{var a=Q(Object,"defineProperty");return a({},"",{}),a}catch(a){}}(),Wa=Pa($a?function(a,e){return $a(a,"toString",{configurable:!0,enumerable:!1,value:Ia(e),writable:!0})}:M);var Na=function(a,e){for(var t=-1,i=null==a?0:a.length;++t<i&&!1!==e(a[t],t,a););return a};var Ua=function(a,e,t,i){for(var n=a.length,o=t+(i?1:-1);i?o--:++o<n;)if(e(a[o],o,a))return o;return-1};var Ka=function(a){return a!=a};var Ha=function(a,e,t){for(var i=t-1,n=a.length;++i<n;)if(a[i]===e)return i;return-1};var Va=function(a,e,t){return e==e?Ha(a,e,t):Ua(a,Ka,t)};var Ya=function(a,e){return!(null==a||!a.length)&&Va(a,e,0)>-1},Ga=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]];var Za=function(a,e){return Na(Ga,function(t){var i="_."+t[0];e&t[