The SEO Framework - Version 4.1.2

Version Description

In this minor update, we ensured compatibility with PHP 8 and WP 5.6. TSF now also fully integrates with WordPress Core Sitemaps, which you can configure via the SEO settings. If you decide to keep using TSF's optimized sitemap, you can now enjoy prerendering, DoS protection, and full Polylang integration. Lastly, you'll find various accessibility improvements, and we fixed about a dozen bugs.

We included a single-line self-destructing notification about our Cyber Monday sale. We hope you'll opt to support our continuous efforts. But we're also kindly asking you to understand we must promote our premium extensions to make TSF possible. We are apprehensive about any embedded advertising and self-promotion in the plugin, so we must rely on notifications until a better system is available in WordPress.

Download this release

Release Info

Developer Cybr
Plugin Icon 128x128 The SEO Framework
Version 4.1.2
Comparing to
See all releases

Code changes from version 4.1.1 to 4.1.2

Files changed (72) hide show
  1. autodescription.php +3 -3
  2. bootstrap/load.php +0 -8
  3. bootstrap/upgrade.php +17 -4
  4. inc/classes/admin-init.class.php +5 -13
  5. inc/classes/admin-pages.class.php +45 -29
  6. inc/classes/bridges/feed.class.php +0 -1
  7. inc/classes/bridges/listedit.class.php +2 -2
  8. inc/classes/bridges/listtable.class.php +0 -1
  9. inc/classes/bridges/ping.class.php +71 -10
  10. inc/classes/bridges/postsettings.class.php +0 -1
  11. inc/classes/bridges/scripts.class.php +4 -4
  12. inc/classes/bridges/seosettings.class.php +0 -1
  13. inc/classes/bridges/sitemap.class.php +167 -11
  14. inc/classes/bridges/termsettings.class.php +0 -1
  15. inc/classes/builders/coresitemaps/index.php +7 -0
  16. inc/classes/builders/coresitemaps/main.class.php +116 -0
  17. inc/classes/builders/coresitemaps/posts.class.php +185 -0
  18. inc/classes/builders/coresitemaps/taxonomies.class.php +127 -0
  19. inc/classes/builders/images.class.php +0 -1
  20. inc/classes/builders/scripts.class.php +18 -3
  21. inc/classes/builders/seobar.class.php +0 -1
  22. inc/classes/builders/sitemap-base.class.php +108 -4
  23. inc/classes/builders/sitemap.class.php +21 -2
  24. inc/classes/cache.class.php +10 -2
  25. inc/classes/core.class.php +3 -3
  26. inc/classes/debug.class.php +1 -3
  27. inc/classes/detect.class.php +29 -0
  28. inc/classes/generate-image.class.php +7 -4
  29. inc/classes/generate-ldjson.class.php +10 -2
  30. inc/classes/generate-title.class.php +140 -22
  31. inc/classes/generate-url.class.php +23 -8
  32. inc/classes/generate.class.php +15 -21
  33. inc/classes/init.class.php +39 -6
  34. inc/classes/interpreters/seobar.class.php +0 -1
  35. inc/classes/post-data.class.php +2 -3
  36. inc/classes/query.class.php +3 -3
  37. inc/classes/render.class.php +18 -8
  38. inc/classes/sanitize.class.php +2 -6
  39. inc/classes/site-options.class.php +12 -9
  40. inc/classes/term-data.class.php +2 -0
  41. inc/classes/user-data.class.php +2 -2
  42. inc/compat/plugin-polylang.php +150 -34
  43. inc/compat/plugin-wpml.php +2 -0
  44. inc/functions/upgrade-suggestion.php +60 -0
  45. inc/traits/core/overload.trait.php +0 -136
  46. inc/views/admin/metaboxes/general-metabox.php +1 -1
  47. inc/views/admin/metaboxes/sitemaps-metabox.php +83 -43
  48. inc/views/admin/metaboxes/title-metabox.php +28 -1
  49. inc/views/edit/seo-settings-singular.php +15 -2
  50. inc/views/edit/seo-settings-tt.php +6 -1
  51. inc/views/notice/persistent.php +1 -1
  52. inc/views/sitemap/xml-sitemap.php +8 -28
  53. language/autodescription.pot +218 -204
  54. lib/css/tsf.css +11 -17
  55. lib/css/tsf.min.css +1 -1
  56. lib/css/tsfc.css +1 -1
  57. lib/js/ays.js +2 -2
  58. lib/js/description.js +10 -2
  59. lib/js/description.min.js +1 -1
  60. lib/js/le.js +11 -11
  61. lib/js/le.min.js +1 -1
  62. lib/js/media.js +70 -55
  63. lib/js/media.min.js +1 -1
  64. lib/js/post.js +210 -20
  65. lib/js/post.min.js +1 -1
  66. lib/js/pt-gb.js +1 -1
  67. lib/js/term.js +57 -9
  68. lib/js/term.min.js +1 -1
  69. lib/js/tsf.js +82 -45
  70. lib/js/tsf.min.js +1 -1
  71. lib/js/tt.js +6 -6
  72. readme.txt +11 -3
autodescription.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: The SEO Framework
4
  * Plugin URI: https://theseoframework.com/
5
  * Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website.
6
- * Version: 4.1.1
7
  * Author: The SEO Framework Team
8
  * Author URI: https://theseoframework.com/
9
  * License: GPLv3
@@ -46,7 +46,7 @@ defined( 'ABSPATH' ) or die;
46
  *
47
  * @since 2.3.5
48
  */
49
- define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.1' );
50
 
51
  /**
52
  * The plugin Database version.
@@ -55,7 +55,7 @@ define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.1' );
55
  *
56
  * @since 2.7.0
57
  */
58
- define( 'THE_SEO_FRAMEWORK_DB_VERSION', '4110' );
59
 
60
  /**
61
  * The plugin file, absolute unix path.
3
  * Plugin Name: The SEO Framework
4
  * Plugin URI: https://theseoframework.com/
5
  * Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website.
6
+ * Version: 4.1.2
7
  * Author: The SEO Framework Team
8
  * Author URI: https://theseoframework.com/
9
  * License: GPLv3
46
  *
47
  * @since 2.3.5
48
  */
49
+ define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.2' );
50
 
51
  /**
52
  * The plugin Database version.
55
  *
56
  * @since 2.7.0
57
  */
58
+ define( 'THE_SEO_FRAMEWORK_DB_VERSION', '4120' );
59
 
60
  /**
61
  * The plugin file, absolute unix path.
bootstrap/load.php CHANGED
@@ -43,14 +43,6 @@ function _init_locale() {
43
  );
44
  }
45
 
46
- /**
47
- * Loads base overloading trait-collection.
48
- *
49
- * For now, other traits must be loaded via this function.
50
- * However, we might deprecate this method in favor of an autoloader.
51
- */
52
- _load_trait( 'core/overload' );
53
-
54
  \add_action( 'plugins_loaded', __NAMESPACE__ . '\\_init_tsf', 5 );
55
  /**
56
  * Loads and memoizes `\The_SEO_Framework\Load` class.
43
  );
44
  }
45
 
 
 
 
 
 
 
 
 
46
  \add_action( 'plugins_loaded', __NAMESPACE__ . '\\_init_tsf', 5 );
47
  /**
48
  * Loads and memoizes `\The_SEO_Framework\Load` class.
bootstrap/upgrade.php CHANGED
@@ -84,6 +84,7 @@ function _previous_db_version() {
84
  * Each called function will upgrade the version by its iteration.
85
  *
86
  * @TODO run this upgrader in a separate thread (e.g. via cron)? And store all notices as persistent?
 
87
  *
88
  * @since 2.7.0
89
  * @since 2.9.4 No longer tests WP version. This file won't be loaded anyway if rendered incompatible.
@@ -215,7 +216,7 @@ function _upgrade( $previous_version ) {
215
  //? This means no data may be erased for at least 1 major version, or 1 year, whichever is later.
216
  //? We must manually delete settings that are no longer used; we merge them otherwise.
217
  //? When a user upgrades beyond this scope, they aren't expected to roll back.
218
- $versions = [ '1', '2701', '2802', '2900', '3001', '3103', '3300', '4051', '4103', '4110' ];
219
 
220
  foreach ( $versions as $_version ) {
221
  if ( $current_version < $_version ) {
@@ -399,6 +400,7 @@ function _prepare_downgrade_notice( $previous_version, $current_version ) {
399
  * @since 4.0.0
400
  * @since 4.1.0 1. Moved admin notice user capability check here.
401
  * 2. Now registers persistent notice for the update version.
 
402
  * @TODO Add browser cache flush notice? Or set a pragma/cache-control header?
403
  * Users that remove query strings (thanks to YSlow) are to blame, though.
404
  * The authors of the plugin that allowed this to happen are even more to blame.
@@ -437,7 +439,7 @@ function _prepare_upgrade_notice( $previous_version, $current_version ) {
437
  'timeout' => DAY_IN_SECONDS,
438
  ]
439
  );
440
- } elseif ( $current_version ) { // User successfully installed.
441
  if ( \current_user_can( 'update_plugins' ) ) {
442
  \add_action( 'admin_notices', __NAMESPACE__ . '\\_do_install_notice' );
443
  }
@@ -781,8 +783,6 @@ function _do_upgrade_4103() {
781
  'noarchive' => [],
782
  ];
783
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
784
- $_option = $tsf->get_robots_taxonomy_option_id( $r );
785
-
786
  $_value = $_new_pt_option_defaults[ $r ];
787
 
788
  $_category_option = (int) (bool) $tsf->get_option( "category_$r", false );
@@ -815,3 +815,16 @@ function _do_upgrade_4110() {
815
  $tsf->update_option( 'oembed_use_social_image', 0 ); // Defaults to 1 for new sites!
816
  }
817
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  * Each called function will upgrade the version by its iteration.
85
  *
86
  * @TODO run this upgrader in a separate thread (e.g. via cron)? And store all notices as persistent?
87
+ * TODO Add a notice that the upgrader is still running (and clear it once the upgrade is completed--preferably before the user can see it!)
88
  *
89
  * @since 2.7.0
90
  * @since 2.9.4 No longer tests WP version. This file won't be loaded anyway if rendered incompatible.
216
  //? This means no data may be erased for at least 1 major version, or 1 year, whichever is later.
217
  //? We must manually delete settings that are no longer used; we merge them otherwise.
218
  //? When a user upgrades beyond this scope, they aren't expected to roll back.
219
+ $versions = [ '1', '2701', '2802', '2900', '3001', '3103', '3300', '4051', '4103', '4110', '4120' ];
220
 
221
  foreach ( $versions as $_version ) {
222
  if ( $current_version < $_version ) {
400
  * @since 4.0.0
401
  * @since 4.1.0 1. Moved admin notice user capability check here.
402
  * 2. Now registers persistent notice for the update version.
403
+ * @since 4.1.2 No longer can accidentally show the install notice after stale upgrade.
404
  * @TODO Add browser cache flush notice? Or set a pragma/cache-control header?
405
  * Users that remove query strings (thanks to YSlow) are to blame, though.
406
  * The authors of the plugin that allowed this to happen are even more to blame.
439
  'timeout' => DAY_IN_SECONDS,
440
  ]
441
  );
442
+ } elseif ( ! $previous_version && $current_version ) { // User successfully installed.
443
  if ( \current_user_can( 'update_plugins' ) ) {
444
  \add_action( 'admin_notices', __NAMESPACE__ . '\\_do_install_notice' );
445
  }
783
  'noarchive' => [],
784
  ];
785
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
 
 
786
  $_value = $_new_pt_option_defaults[ $r ];
787
 
788
  $_category_option = (int) (bool) $tsf->get_option( "category_$r", false );
815
  $tsf->update_option( 'oembed_use_social_image', 0 ); // Defaults to 1 for new sites!
816
  }
817
  }
818
+
819
+ /**
820
+ * Registers the `ping_use_cron_prerender` option, boolean.
821
+ *
822
+ * @since 4.1.2
823
+ */
824
+ function _do_upgrade_4120() {
825
+ if ( \get_option( 'the_seo_framework_initial_db_version' ) < '4120' ) {
826
+ $tsf = \the_seo_framework();
827
+
828
+ $tsf->update_option( 'ping_use_cron_prerender', 0 );
829
+ }
830
+ }
inc/classes/admin-init.class.php CHANGED
@@ -62,12 +62,13 @@ class Admin_Init extends Init {
62
  * Adds post states for the post/page edit.php query.
63
  *
64
  * @since 4.0.0
 
65
  *
66
  * @param array $states The current post states array
67
- * @param \WP_Post $post The Post Object.
68
  * @return array Adjusted $states
69
  */
70
- public function _add_post_state( $states = [], $post ) {
71
 
72
  $post_id = isset( $post->ID ) ? $post->ID : false;
73
 
@@ -91,6 +92,7 @@ class Admin_Init extends Init {
91
  * @since 3.1.0
92
  * @since 4.0.0 Now discerns autoloading between taxonomies and singular types.
93
  * @since 4.1.0 Now invokes autoloading when persistent scripts are enqueued (regardless of validity).
 
94
  * @access private
95
  *
96
  * @param string|null $hook The current page hook.
@@ -121,16 +123,6 @@ class Admin_Init extends Init {
121
  'edit-tags.php',
122
  'term.php',
123
  ];
124
-
125
- if ( ! $this->get_option( 'display_seo_bar_tables' ) ) {
126
- $enqueue_hooks = array_diff(
127
- $enqueue_hooks,
128
- [
129
- 'edit.php',
130
- 'edit-tags.php',
131
- ]
132
- );
133
- }
134
  }
135
 
136
  if ( \in_array( $hook, $enqueue_hooks, true ) )
@@ -861,7 +853,7 @@ class Admin_Init extends Init {
861
  $parent_url = \wp_get_attachment_url( $attachment_id );
862
  $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
863
 
864
- // phpcs:ignore, WordPress.PHP.NoSilencedErrors -- Feature may be disabled.
865
  $size = @getimagesize( $cropped );
866
  $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
867
 
62
  * Adds post states for the post/page edit.php query.
63
  *
64
  * @since 4.0.0
65
+ * @access private
66
  *
67
  * @param array $states The current post states array
68
+ * @param \WP_Post $post The Post Object.
69
  * @return array Adjusted $states
70
  */
71
+ public function _add_post_state( $states = [], $post = null ) {
72
 
73
  $post_id = isset( $post->ID ) ? $post->ID : false;
74
 
92
  * @since 3.1.0
93
  * @since 4.0.0 Now discerns autoloading between taxonomies and singular types.
94
  * @since 4.1.0 Now invokes autoloading when persistent scripts are enqueued (regardless of validity).
95
+ * @since 4.1.2 Now autoenqueues on edit.php and edit-tags.php regardless of SEO Bar output (for quick/bulk-edit support).
96
  * @access private
97
  *
98
  * @param string|null $hook The current page hook.
123
  'edit-tags.php',
124
  'term.php',
125
  ];
 
 
 
 
 
 
 
 
 
 
126
  }
127
 
128
  if ( \in_array( $hook, $enqueue_hooks, true ) )
853
  $parent_url = \wp_get_attachment_url( $attachment_id );
854
  $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
855
 
856
+ // phpcs:ignore, WordPress.PHP.NoSilencedErrors -- Feature may be disabled; should not cause fatal errors.
857
  $size = @getimagesize( $cropped );
858
  $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
859
 
inc/classes/admin-pages.class.php CHANGED
@@ -248,6 +248,7 @@ class Admin_Pages extends Profile {
248
  * @since 2.6.0 Refactored.
249
  * @since 3.1.0 Now prefixes the IDs.
250
  * @since 4.0.0 Deprecated third parameter, silently.
 
251
  *
252
  * @param string $id The nav-tab ID
253
  * @param array $tabs The tab content {
@@ -271,6 +272,7 @@ class Admin_Pages extends Profile {
271
  * @since 2.9.0
272
  * @since 3.0.0 Converted to view.
273
  * @since 4.0.0 Deprecated third parameter, silently.
 
274
  *
275
  * @param string $id The nav-tab ID
276
  * @param array $tabs The tab content {
@@ -284,7 +286,7 @@ class Admin_Pages extends Profile {
284
  * @param null $_depr Deprecated.
285
  * @param bool $use_tabs Whether to output tabs, only works when $tabs count is greater than 1.
286
  */
287
- public static function inpost_flex_nav_tab_wrapper( $id, $tabs = [], $_depr = null, $use_tabs = true ) {
288
  Bridges\PostSettings::_flex_nav_tab_wrapper( $id, $tabs, $use_tabs );
289
  }
290
 
@@ -355,34 +357,40 @@ class Admin_Pages extends Profile {
355
  * @since 4.0.3 1. Keyboard navigation is now supported on the dismiss icon.
356
  * 2. The info notice type is now supported.
357
  * @since 4.1.0 Now semantically wraps the content with HTML.
358
- * @TODO deprecate--use the more reliable and secure persistent notices registry instead.
 
 
 
 
 
359
  * @see register_dismissible_persistent_notice()
360
  *
361
  * @param string $message The notice message. Expected to be escaped if $escape is false.
362
  * When the message contains HTML, it must start with a <p> tag,
363
  * or it will be added for you--regardless of proper semantics.
364
- * @param string $type The notice type : 'updated', 'error', 'warning'. Expected to be escaped.
365
- * @param bool $icon Whether to add an accessibility icon.
366
  * @param bool $escape Whether to escape the whole output.
 
367
  * @return string The dismissible error notice.
368
  */
369
- public function generate_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true ) {
370
 
371
- // Don't check for strlen. '0' is a useless message, anyway.
372
- if ( ! $message ) return '';
373
-
374
- // Make sure the scripts are loaded.
375
- $this->init_admin_scripts();
376
- Builders\Scripts::enqueue();
377
 
378
  if ( \in_array( $type, [ 'warning', 'info' ], true ) )
379
  $type = "notice-$type";
380
 
381
  return vsprintf(
382
- '<div class="notice %s tsf-notice %s">%s%s</div>',
383
  [
384
  \esc_attr( $type ),
385
  ( $icon ? 'tsf-show-icon' : '' ),
 
386
  sprintf(
387
  ( ! $escape && 0 === strpos( $message, '<p' ) ? '%s' : '<p>%s</p>' ),
388
  ( $escape ? \esc_html( $message ) : $message )
@@ -399,17 +407,20 @@ class Admin_Pages extends Profile {
399
  * Echos generated dismissible notice.
400
  *
401
  * @since 2.7.0
402
- * @TODO deprecate--use the more reliable and secure persistent notices registry instead.
 
 
403
  * @see register_dismissible_persistent_notice()
404
  *
405
  * @param string $message The notice message. Expected to be escaped if $escape is false.
406
- * @param string $type The notice type : 'updated', 'error', 'warning'. Expected to be escaped.
407
  * @param bool $icon Whether to add an accessibility icon.
408
  * @param bool $escape Whether to escape the whole output.
 
409
  */
410
- public function do_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true ) {
411
  // phpcs:ignore, WordPress.Security.EscapeOutput -- use $escape
412
- echo $this->generate_dismissible_notice( $message, $type, $icon, $escape );
413
  }
414
 
415
  /**
@@ -1043,6 +1054,7 @@ class Admin_Pages extends Profile {
1043
  * @since 2.8.0
1044
  * @since 3.1.0 No longer prepares media l10n data.
1045
  * @since 4.0.0 Now adds a media preview dispenser.
 
1046
  *
1047
  * @param string $input_id Required. The HTML input id to pass URL into.
1048
  * @return string The image uploader button.
@@ -1059,7 +1071,7 @@ class Admin_Pages extends Profile {
1059
  %s>%s</button>',
1060
  [
1061
  \esc_url( \get_upload_iframe_src( 'image', $this->get_the_real_ID() ) ),
1062
- \esc_attr_x( 'Select social image', 'Button hover', 'autodescription' ),
1063
  $s_input_id,
1064
  $this->make_data_attributes( [
1065
  'inputId' => $s_input_id,
@@ -1074,7 +1086,7 @@ class Admin_Pages extends Profile {
1074
  ]
1075
  );
1076
 
1077
- $content .= vsprintf(
1078
  '<span class="tsf-tooltip-wrap"><span id="%1$s-preview" class="tsf-image-preview tsf-tooltip-item dashicons dashicons-format-image" data-for="%1$s" tabindex=0></span></span>',
1079
  $s_input_id
1080
  );
@@ -1120,7 +1132,7 @@ class Admin_Pages extends Profile {
1120
  ]
1121
  );
1122
 
1123
- $content .= vsprintf(
1124
  '<span class="tsf-tooltip-wrap"><span id="%1$s-preview" class="tsf-image-preview tsf-tooltip-item dashicons dashicons-format-image" data-for="%1$s" tabindex=0></span></span>',
1125
  $s_input_id
1126
  );
@@ -1135,17 +1147,19 @@ class Admin_Pages extends Profile {
1135
  *
1136
  * @since 3.0.4
1137
  * @since 4.1.0 Now only outputs the legacy reference and noadditions reference.
 
1138
  * @ignore
1139
  * @todo deprecate
1140
  */
1141
  public function output_js_title_elements() {
1142
- echo '<span data-ignore-me=legacy id=tsf-title-reference class="tsf-title-reference hidden" data-do-not-use=legacy></span>';
1143
  }
1144
 
1145
  /**
1146
  * Outputs reference description HTML elements for JavaScript for a specific ID.
1147
  *
1148
  * @since 4.1.0
 
1149
  *
1150
  * @param string $id The input ID.
1151
  * @param array $data The input data.
@@ -1155,12 +1169,12 @@ class Admin_Pages extends Profile {
1155
  implode(
1156
  '',
1157
  [
1158
- '<span id="tsf-title-reference_%1$s" class="tsf-title-reference hidden" data-for="%1$s"></span>',
1159
- '<span id="tsf-title-noadditions-reference_%1$s" class="tsf-title-noadditions-reference hidden" data-for="%1$s"></span>',
1160
- '<span id="tsf-title-offset_%1$s" class="tsf-title-offset hide-if-no-tsf-js" data-for="%1$s"></span>',
1161
- '<span id="tsf-title-placeholder-additions_%1$s" class="tsf-title-placeholder-additions hide-if-no-tsf-js" data-for="%1$s"></span>',
1162
- '<span id="tsf-title-placeholder-prefix_%1$s" class="tsf-title-placeholder-prefix hide-if-no-tsf-js" data-for="%1$s"></span>',
1163
- '<span id="tsf-title-data_%1$s" class=hidden data-for="%1$s" %2$s></span>',
1164
  ]
1165
  ),
1166
  \esc_attr( $id ),
@@ -1175,17 +1189,19 @@ class Admin_Pages extends Profile {
1175
  * Do not use. Legacy item output for backward compatibility.
1176
  *
1177
  * @since 3.0.4
 
1178
  * @ignore
1179
  * @todo deprecate
1180
  */
1181
  public function output_js_description_elements() {
1182
- echo '<span data-ignore-me=legacy id=tsf-description-reference class="tsf-description-reference hidden" data-do-not-use=legacy></span>';
1183
  }
1184
 
1185
  /**
1186
  * Outputs reference description HTML elements for JavaScript for a specific ID.
1187
  *
1188
  * @since 4.1.0
 
1189
  *
1190
  * @param string $id The description input ID.
1191
  * @param array $data The input data.
@@ -1195,8 +1211,8 @@ class Admin_Pages extends Profile {
1195
  implode(
1196
  '',
1197
  [
1198
- '<span id="tsf-description-reference_%1$s" class=hidden data-for="%1$s" ></span>',
1199
- '<span id="tsf-description-data_%1$s" class=hidden data-for="%1$s" %2$s ></span>',
1200
  ]
1201
  ),
1202
  \esc_attr( $id ),
248
  * @since 2.6.0 Refactored.
249
  * @since 3.1.0 Now prefixes the IDs.
250
  * @since 4.0.0 Deprecated third parameter, silently.
251
+ * @TODO is this even used??? See inc\views\edit\seo-settings-singular.php. Deprecate me?
252
  *
253
  * @param string $id The nav-tab ID
254
  * @param array $tabs The tab content {
272
  * @since 2.9.0
273
  * @since 3.0.0 Converted to view.
274
  * @since 4.0.0 Deprecated third parameter, silently.
275
+ * @TODO is this even used??? See inc\views\edit\seo-settings-singular.php. Deprecate me?
276
  *
277
  * @param string $id The nav-tab ID
278
  * @param array $tabs The tab content {
286
  * @param null $_depr Deprecated.
287
  * @param bool $use_tabs Whether to output tabs, only works when $tabs count is greater than 1.
288
  */
289
+ public function inpost_flex_nav_tab_wrapper( $id, $tabs = [], $_depr = null, $use_tabs = true ) {
290
  Bridges\PostSettings::_flex_nav_tab_wrapper( $id, $tabs, $use_tabs );
291
  }
292
 
357
  * @since 4.0.3 1. Keyboard navigation is now supported on the dismiss icon.
358
  * 2. The info notice type is now supported.
359
  * @since 4.1.0 Now semantically wraps the content with HTML.
360
+ * @since 4.1.2 1. No longer invokes the script loader during AJAX-requests.
361
+ * 2. Now accepts empty messages, so that AJAX-invoked generators can grab a notice wrapper.
362
+ * 3. Added the inline parameter.
363
+ * 4. Now enqueues scripts in the footer, so templates won't spam the header.
364
+ * @TODO deprecate -- Use the more reliable and secure persistent notices registry instead...
365
+ * Then again, this allows for AJAX-generated notices.
366
  * @see register_dismissible_persistent_notice()
367
  *
368
  * @param string $message The notice message. Expected to be escaped if $escape is false.
369
  * When the message contains HTML, it must start with a <p> tag,
370
  * or it will be added for you--regardless of proper semantics.
371
+ * @param string $type The notice type : 'updated', 'error', 'warning', 'info'. Expected to be escaped.
372
+ * @param bool $icon Whether to add an accessibility icon.
373
  * @param bool $escape Whether to escape the whole output.
374
+ * @param bool $inline Whether WordPress should be allowed to move it.
375
  * @return string The dismissible error notice.
376
  */
377
+ public function generate_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true, $inline = false ) {
378
 
379
+ if ( ! \wp_doing_ajax() ) {
380
+ // Make sure the scripts are loaded.
381
+ $this->init_admin_scripts();
382
+ \The_SEO_Framework\Builders\Scripts::footer_enqueue();
383
+ }
 
384
 
385
  if ( \in_array( $type, [ 'warning', 'info' ], true ) )
386
  $type = "notice-$type";
387
 
388
  return vsprintf(
389
+ '<div class="notice %s tsf-notice %s %s">%s%s</div>',
390
  [
391
  \esc_attr( $type ),
392
  ( $icon ? 'tsf-show-icon' : '' ),
393
+ ( $inline ? 'inline' : '' ),
394
  sprintf(
395
  ( ! $escape && 0 === strpos( $message, '<p' ) ? '%s' : '<p>%s</p>' ),
396
  ( $escape ? \esc_html( $message ) : $message )
407
  * Echos generated dismissible notice.
408
  *
409
  * @since 2.7.0
410
+ * @since 4.1.2 Added the $inline parameter.
411
+ * @TODO deprecate -- Use the more reliable and secure persistent notices registry instead...
412
+ * Then again, this allows for AJAX-generated notices.
413
  * @see register_dismissible_persistent_notice()
414
  *
415
  * @param string $message The notice message. Expected to be escaped if $escape is false.
416
+ * @param string $type The notice type : 'updated', 'error', 'warning', 'info'. Expected to be escaped.
417
  * @param bool $icon Whether to add an accessibility icon.
418
  * @param bool $escape Whether to escape the whole output.
419
+ * @param bool $inline Whether WordPress should be allowed to move it.
420
  */
421
+ public function do_dismissible_notice( $message = '', $type = 'updated', $icon = true, $escape = true, $inline = false ) {
422
  // phpcs:ignore, WordPress.Security.EscapeOutput -- use $escape
423
+ echo $this->generate_dismissible_notice( $message, $type, $icon, $escape, $inline );
424
  }
425
 
426
  /**
1054
  * @since 2.8.0
1055
  * @since 3.1.0 No longer prepares media l10n data.
1056
  * @since 4.0.0 Now adds a media preview dispenser.
1057
+ * @since 4.1.2 No longer adds a redundant title to the selection button.
1058
  *
1059
  * @param string $input_id Required. The HTML input id to pass URL into.
1060
  * @return string The image uploader button.
1071
  %s>%s</button>',
1072
  [
1073
  \esc_url( \get_upload_iframe_src( 'image', $this->get_the_real_ID() ) ),
1074
+ '', // redundant
1075
  $s_input_id,
1076
  $this->make_data_attributes( [
1077
  'inputId' => $s_input_id,
1086
  ]
1087
  );
1088
 
1089
+ $content .= sprintf(
1090
  '<span class="tsf-tooltip-wrap"><span id="%1$s-preview" class="tsf-image-preview tsf-tooltip-item dashicons dashicons-format-image" data-for="%1$s" tabindex=0></span></span>',
1091
  $s_input_id
1092
  );
1132
  ]
1133
  );
1134
 
1135
+ $content .= sprintf(
1136
  '<span class="tsf-tooltip-wrap"><span id="%1$s-preview" class="tsf-image-preview tsf-tooltip-item dashicons dashicons-format-image" data-for="%1$s" tabindex=0></span></span>',
1137
  $s_input_id
1138
  );
1147
  *
1148
  * @since 3.0.4
1149
  * @since 4.1.0 Now only outputs the legacy reference and noadditions reference.
1150
+ * @since 4.1.2 Now prevents wp-emoji.js parsing the reference.
1151
  * @ignore
1152
  * @todo deprecate
1153
  */
1154
  public function output_js_title_elements() {
1155
+ echo '<span data-ignore-me=legacy id=tsf-title-reference class="tsf-title-reference wp-exclude-emoji hidden" data-do-not-use=legacy></span>';
1156
  }
1157
 
1158
  /**
1159
  * Outputs reference description HTML elements for JavaScript for a specific ID.
1160
  *
1161
  * @since 4.1.0
1162
+ * @since 4.1.2 Now prevents wp-emoji.js parsing the references and data.
1163
  *
1164
  * @param string $id The input ID.
1165
  * @param array $data The input data.
1169
  implode(
1170
  '',
1171
  [
1172
+ '<span id="tsf-title-reference_%1$s" class="tsf-title-reference wp-exclude-emoji hidden" data-for="%1$s"></span>',
1173
+ '<span id="tsf-title-noadditions-reference_%1$s" class="tsf-title-noadditions-reference wp-exclude-emoji hidden" data-for="%1$s"></span>',
1174
+ '<span id="tsf-title-offset_%1$s" class="tsf-title-offset wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
1175
+ '<span id="tsf-title-placeholder-additions_%1$s" class="tsf-title-placeholder-additions wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
1176
+ '<span id="tsf-title-placeholder-prefix_%1$s" class="tsf-title-placeholder-prefix wp-exclude-emoji hide-if-no-tsf-js" data-for="%1$s"></span>',
1177
+ '<span id="tsf-title-data_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" %2$s></span>',
1178
  ]
1179
  ),
1180
  \esc_attr( $id ),
1189
  * Do not use. Legacy item output for backward compatibility.
1190
  *
1191
  * @since 3.0.4
1192
+ * @since 4.1.2 Now prevents wp-emoji.js parsing the reference.
1193
  * @ignore
1194
  * @todo deprecate
1195
  */
1196
  public function output_js_description_elements() {
1197
+ echo '<span data-ignore-me=legacy id=tsf-description-reference class="tsf-description-reference wp-exclude-emoji hidden" data-do-not-use=legacy></span>';
1198
  }
1199
 
1200
  /**
1201
  * Outputs reference description HTML elements for JavaScript for a specific ID.
1202
  *
1203
  * @since 4.1.0
1204
+ * @since 4.1.2 Now prevents wp-emoji.js parsing the references and data.
1205
  *
1206
  * @param string $id The description input ID.
1207
  * @param array $data The input data.
1211
  implode(
1212
  '',
1213
  [
1214
+ '<span id="tsf-description-reference_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" ></span>',
1215
+ '<span id="tsf-description-data_%1$s" class="hidden wp-exclude-emoji" data-for="%1$s" %2$s ></span>',
1216
  ]
1217
  ),
1218
  \esc_attr( $id ),
inc/classes/bridges/feed.class.php CHANGED
@@ -45,7 +45,6 @@ $_load_feed_class = function() {
45
  * @final Can't be extended.
46
  */
47
  final class Feed {
48
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
49
 
50
  /**
51
  * @since 4.1.0
45
  * @final Can't be extended.
46
  */
47
  final class Feed {
 
48
 
49
  /**
50
  * @since 4.1.0
inc/classes/bridges/listedit.class.php CHANGED
@@ -172,7 +172,7 @@ final class ListEdit extends ListTable {
172
 
173
  $r_defaults = $tsf->robots_meta(
174
  $query,
175
- \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS | \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
176
  );
177
 
178
  $meta = $tsf->get_post_meta( $post_id );
@@ -321,7 +321,7 @@ final class ListEdit extends ListTable {
321
 
322
  $r_defaults = $tsf->robots_meta(
323
  $query,
324
- \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS | \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
325
  );
326
 
327
  $meta = $tsf->get_term_meta( $term_id );
172
 
173
  $r_defaults = $tsf->robots_meta(
174
  $query,
175
+ \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
176
  );
177
 
178
  $meta = $tsf->get_post_meta( $post_id );
321
 
322
  $r_defaults = $tsf->robots_meta(
323
  $query,
324
+ \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
325
  );
326
 
327
  $meta = $tsf->get_term_meta( $term_id );
inc/classes/bridges/listtable.class.php CHANGED
@@ -41,7 +41,6 @@ namespace The_SEO_Framework\Bridges;
41
  * @abstract
42
  */
43
  abstract class ListTable {
44
- use \The_SEO_Framework\Traits\Enclose_Core_Final;
45
 
46
  /**
47
  * @since 4.0.0
41
  * @abstract
42
  */
43
  abstract class ListTable {
 
44
 
45
  /**
46
  * @since 4.0.0
inc/classes/bridges/ping.class.php CHANGED
@@ -34,7 +34,6 @@ namespace The_SEO_Framework\Bridges;
34
  * @final Can't be extended.
35
  */
36
  final class Ping {
37
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
38
 
39
  /**
40
  * The constructor, can't be initialized.
@@ -46,11 +45,52 @@ final class Ping {
46
  *
47
  * @since 4.0.0
48
  * @since 4.1.0 Now returns whether the cron engagement was successful.
 
 
49
  *
50
  * @return bool True on success, false on failure.
51
  */
52
  public static function engage_pinging_cron() {
53
- return \wp_schedule_single_event( time() + 30, 'tsf_sitemap_cron_hook' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
 
56
  /**
@@ -74,9 +114,14 @@ final class Ping {
74
 
75
  if ( $tsf->get_option( 'site_noindex' ) || ! $tsf->is_blog_public() ) return;
76
 
 
 
 
 
 
77
  $transient = $tsf->generate_cache_key( 0, '', 'ping' );
78
 
79
- // NOTE: Use legacy get_transient to bypass TSF's transient filters and prevent ping spam.
80
  if ( false === \get_transient( $transient ) ) {
81
  /**
82
  * @since 4.1.1
@@ -102,7 +147,7 @@ final class Ping {
102
  */
103
  $expiration = (int) \apply_filters( 'the_seo_framework_sitemap_throttle_s', HOUR_IN_SECONDS );
104
 
105
- // @NOTE: Using legacy set_transient to bypass TSF's transient filters and prevent ping spam.
106
  \set_transient( $transient, 1, $expiration );
107
  }
108
  }
@@ -114,12 +159,20 @@ final class Ping {
114
  * @since 3.1.0 Updated ping URL. Old one still worked, too.
115
  * @since 4.0.0 Moved to \The_SEO_Framework\Bridges\Ping
116
  * @since 4.0.3 Google now redirects to HTTPS. Updated URL scheme to accomodate.
 
117
  * @link https://support.google.com/webmasters/answer/6065812?hl=en
118
  */
119
  public static function ping_google() {
120
- $pingurl = 'https://www.google.com/ping?sitemap=' . rawurlencode(
121
- \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url()
122
- );
 
 
 
 
 
 
 
123
  \wp_safe_remote_get( $pingurl, [ 'timeout' => 3 ] );
124
  }
125
 
@@ -130,12 +183,20 @@ final class Ping {
130
  * @since 3.2.3 Updated ping URL. Old one still worked, too.
131
  * @since 4.0.0 Moved to \The_SEO_Framework\Bridges\Ping
132
  * @since 4.0.3 Bing now redirects to HTTPS. Updated URL scheme to accomodate.
 
133
  * @link https://www.bing.com/webmaster/help/how-to-submit-sitemaps-82a15bd4
134
  */
135
  public static function ping_bing() {
136
- $pingurl = 'https://www.bing.com/ping?sitemap=' . rawurlencode(
137
- \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url()
138
- );
 
 
 
 
 
 
 
139
  \wp_safe_remote_get( $pingurl, [ 'timeout' => 3 ] );
140
  }
141
  }
34
  * @final Can't be extended.
35
  */
36
  final class Ping {
 
37
 
38
  /**
39
  * The constructor, can't be initialized.
45
  *
46
  * @since 4.0.0
47
  * @since 4.1.0 Now returns whether the cron engagement was successful.
48
+ * @since 4.1.2 Now registers before and after cron hooks. They should run subsequential when successful.
49
+ * @see static::engage_pinging_retry_cron()
50
  *
51
  * @return bool True on success, false on failure.
52
  */
53
  public static function engage_pinging_cron() {
54
+
55
+ $when = time() + 28;
56
+
57
+ // Because WordPress sorts the actions, we can't be sure if they're scrambled. Therefore: skew timing.
58
+ // Note that when WP_CRON_LOCK_TIMEOUT expires, the subsequent actions will run, regardless if previous was successful.
59
+ return \wp_schedule_single_event( ++$when, 'tsf_sitemap_cron_hook_before' )
60
+ && \wp_schedule_single_event( ++$when, 'tsf_sitemap_cron_hook' )
61
+ && \wp_schedule_single_event( ++$when, 'tsf_sitemap_cron_hook_after' );
62
+ }
63
+
64
+ /**
65
+ * Retries a cronjob-based ping, via another hook.
66
+ *
67
+ * @since 4.1.2
68
+ * @uses \WP_CRON_LOCK_TIMEOUT, default 60 (seconds).
69
+ *
70
+ * @param array $args Optional. Array containing each separate argument to pass to the hook's callback function.
71
+ * @return bool True on success, false on failure.
72
+ */
73
+ public static function engage_pinging_retry_cron( $args = [] ) {
74
+
75
+ $when = (int) ( time() + min( \WP_CRON_LOCK_TIMEOUT, 60 ) + 1 );
76
+
77
+ return \wp_schedule_single_event( $when, 'tsf_sitemap_cron_hook_retry', [ $args ] );
78
+ }
79
+
80
+ /**
81
+ * Retries pinging the search engines.
82
+ *
83
+ * @since 4.1.2
84
+ * @see static::engage_pinging_retry_cron()
85
+ * @uses static::ping_search_engines()
86
+ *
87
+ * @param array $args Array from ping hook.
88
+ */
89
+ public static function retry_ping_search_engines( $args = [] ) {
90
+
91
+ if ( empty( $args['id'] ) || 'base' !== $args['id'] ) return;
92
+
93
+ static::ping_search_engines();
94
  }
95
 
96
  /**
114
 
115
  if ( $tsf->get_option( 'site_noindex' ) || ! $tsf->is_blog_public() ) return;
116
 
117
+ // Check for sitemap lock. If TSF's default sitemap isn't used, this should return false.
118
+ if ( \The_SEO_Framework\Bridges\Sitemap::get_instance()->is_sitemap_locked() ) {
119
+ static::engage_pinging_retry_cron( [ 'id' => 'base' ] );
120
+ return;
121
+ }
122
  $transient = $tsf->generate_cache_key( 0, '', 'ping' );
123
 
124
+ // Uses legacy get_transient to bypass TSF's transient filters and prevent ping spam.
125
  if ( false === \get_transient( $transient ) ) {
126
  /**
127
  * @since 4.1.1
147
  */
148
  $expiration = (int) \apply_filters( 'the_seo_framework_sitemap_throttle_s', HOUR_IN_SECONDS );
149
 
150
+ // Uses legacy set_transient to bypass TSF's transient filters and prevent ping spam.
151
  \set_transient( $transient, 1, $expiration );
152
  }
153
  }
159
  * @since 3.1.0 Updated ping URL. Old one still worked, too.
160
  * @since 4.0.0 Moved to \The_SEO_Framework\Bridges\Ping
161
  * @since 4.0.3 Google now redirects to HTTPS. Updated URL scheme to accomodate.
162
+ * @since 4.1.2 Now fetches WP Sitemaps' index URL when it's enabled.
163
  * @link https://support.google.com/webmasters/answer/6065812?hl=en
164
  */
165
  public static function ping_google() {
166
+
167
+ if ( \the_seo_framework()->use_core_sitemaps() ) {
168
+ $url = \get_sitemap_url( 'index' );
169
+ } else {
170
+ $url = \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url();
171
+ }
172
+
173
+ if ( ! $url ) return;
174
+
175
+ $pingurl = 'https://www.google.com/ping?sitemap=' . rawurlencode( $url );
176
  \wp_safe_remote_get( $pingurl, [ 'timeout' => 3 ] );
177
  }
178
 
183
  * @since 3.2.3 Updated ping URL. Old one still worked, too.
184
  * @since 4.0.0 Moved to \The_SEO_Framework\Bridges\Ping
185
  * @since 4.0.3 Bing now redirects to HTTPS. Updated URL scheme to accomodate.
186
+ * @since 4.1.2 Now fetches WP Sitemaps' index URL when it's enabled.
187
  * @link https://www.bing.com/webmaster/help/how-to-submit-sitemaps-82a15bd4
188
  */
189
  public static function ping_bing() {
190
+
191
+ if ( \the_seo_framework()->use_core_sitemaps() ) {
192
+ $url = \get_sitemap_url( 'index' );
193
+ } else {
194
+ $url = \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url();
195
+ }
196
+
197
+ if ( ! $url ) return;
198
+
199
+ $pingurl = 'https://www.bing.com/ping?sitemap=' . rawurlencode( $url );
200
  \wp_safe_remote_get( $pingurl, [ 'timeout' => 3 ] );
201
  }
202
  }
inc/classes/bridges/postsettings.class.php CHANGED
@@ -36,7 +36,6 @@ namespace The_SEO_Framework\Bridges;
36
  * @final Can't be extended.
37
  */
38
  final class PostSettings {
39
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
40
 
41
  /**
42
  * Registers the meta box for the Post edit screens.
36
  * @final Can't be extended.
37
  */
38
  final class PostSettings {
 
39
 
40
  /**
41
  * Registers the meta box for the Post edit screens.
inc/classes/bridges/scripts.class.php CHANGED
@@ -52,7 +52,6 @@ $_load_scripts_class = function() {
52
  * @final Can't be extended.
53
  */
54
  final class Scripts {
55
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
56
 
57
  /**
58
  * @since 4.0.0
@@ -595,6 +594,7 @@ final class Scripts {
595
  * Returns Media scripts params.
596
  *
597
  * @since 4.0.0
 
598
  *
599
  * @return array The script params.
600
  */
@@ -613,10 +613,10 @@ final class Scripts {
613
  'labels' => [
614
  'social' => [
615
  'imgSelect' => \esc_attr__( 'Select Image', 'autodescription' ),
616
- 'imgSelectTitle' => \esc_attr_x( 'Select social image', 'Button hover', 'autodescription' ),
617
  'imgChange' => \esc_attr__( 'Change Image', 'autodescription' ),
618
  'imgRemove' => \esc_attr__( 'Remove Image', 'autodescription' ),
619
- 'imgRemoveTitle' => \esc_attr__( 'Remove selected social image', 'autodescription' ),
620
  'imgFrameTitle' => \esc_attr_x( 'Select Social Image', 'Frame title', 'autodescription' ),
621
  'imgFrameButton' => \esc_attr__( 'Use this image', 'autodescription' ),
622
  ],
@@ -625,7 +625,7 @@ final class Scripts {
625
  'imgSelectTitle' => '',
626
  'imgChange' => \esc_attr__( 'Change Logo', 'autodescription' ),
627
  'imgRemove' => \esc_attr__( 'Remove Logo', 'autodescription' ),
628
- 'imgRemoveTitle' => \esc_attr__( 'Unset selected logo', 'autodescription' ),
629
  'imgFrameTitle' => \esc_attr_x( 'Select Logo', 'Frame title', 'autodescription' ),
630
  'imgFrameButton' => \esc_attr__( 'Use this image', 'autodescription' ),
631
  ],
52
  * @final Can't be extended.
53
  */
54
  final class Scripts {
 
55
 
56
  /**
57
  * @since 4.0.0
594
  * Returns Media scripts params.
595
  *
596
  * @since 4.0.0
597
+ * @since 4.1.2 Removed redundant button titles.
598
  *
599
  * @return array The script params.
600
  */
613
  'labels' => [
614
  'social' => [
615
  'imgSelect' => \esc_attr__( 'Select Image', 'autodescription' ),
616
+ 'imgSelectTitle' => '',
617
  'imgChange' => \esc_attr__( 'Change Image', 'autodescription' ),
618
  'imgRemove' => \esc_attr__( 'Remove Image', 'autodescription' ),
619
+ 'imgRemoveTitle' => '',
620
  'imgFrameTitle' => \esc_attr_x( 'Select Social Image', 'Frame title', 'autodescription' ),
621
  'imgFrameButton' => \esc_attr__( 'Use this image', 'autodescription' ),
622
  ],
625
  'imgSelectTitle' => '',
626
  'imgChange' => \esc_attr__( 'Change Logo', 'autodescription' ),
627
  'imgRemove' => \esc_attr__( 'Remove Logo', 'autodescription' ),
628
+ 'imgRemoveTitle' => '',
629
  'imgFrameTitle' => \esc_attr_x( 'Select Logo', 'Frame title', 'autodescription' ),
630
  'imgFrameButton' => \esc_attr__( 'Use this image', 'autodescription' ),
631
  ],
inc/classes/bridges/seosettings.class.php CHANGED
@@ -36,7 +36,6 @@ namespace The_SEO_Framework\Bridges;
36
  * @final Can't be extended.
37
  */
38
  final class SeoSettings {
39
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
40
 
41
  /**
42
  * Registers meta boxes on the Site SEO Settings page.
36
  * @final Can't be extended.
37
  */
38
  final class SeoSettings {
 
39
 
40
  /**
41
  * Registers meta boxes on the Site SEO Settings page.
inc/classes/bridges/sitemap.class.php CHANGED
@@ -45,7 +45,6 @@ $_load_sitemap_class = function() {
45
  * @final Can't be extended.
46
  */
47
  final class Sitemap {
48
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
49
 
50
  /**
51
  * @since 4.0.0
@@ -152,6 +151,8 @@ final class Sitemap {
152
  * Returns the expected sitemap endpoint for the given ID.
153
  *
154
  * @since 4.0.0
 
 
155
  * @global \WP_Rewrite $wp_rewrite
156
  *
157
  * @param string $id The base ID. Default 'base'.
@@ -168,14 +169,27 @@ final class Sitemap {
168
  $scheme = static::$tsf->get_preferred_scheme();
169
  $prefix = $this->get_sitemap_path_prefix();
170
 
 
 
 
 
 
 
 
 
171
  if ( $wp_rewrite->using_index_permalinks() ) {
172
- $url = \home_url( "/index.php$prefix{$list[ $id ]['endpoint']}", $scheme );
173
  } elseif ( $wp_rewrite->using_permalinks() ) {
174
- $url = \home_url( "$prefix{$list[ $id ]['endpoint']}", $scheme );
175
  } else {
176
- $url = \home_url( "$prefix?tsf-sitemap=$id", $scheme );
177
  }
178
 
 
 
 
 
 
179
  return \esc_url_raw( $url );
180
  }
181
 
@@ -195,6 +209,7 @@ final class Sitemap {
195
  * @link Example: https://github.com/sybrew/tsf-term-sitemap
196
  * @param array $list The endpoints: {
197
  * 'id' => array: {
 
198
  * 'endpoint' => string The expected "pretty" endpoint, meant for administrative display.
199
  * 'epregex' => string The endpoint regex, following the home path regex.
200
  * N.B. Be wary of case sensitivity. Append the i-flag.
@@ -213,12 +228,14 @@ final class Sitemap {
213
  'the_seo_framework_sitemap_endpoint_list',
214
  [
215
  'base' => [
 
216
  'endpoint' => 'sitemap.xml',
217
  'regex' => '/^sitemap\.xml/i',
218
  'callback' => static::class . '::output_base_sitemap',
219
  'robots' => true,
220
  ],
221
  'index' => [
 
222
  'endpoint' => 'sitemap_index.xml',
223
  'regex' => '/^sitemap_index\.xml/i',
224
  'callback' => static::class . '::output_base_sitemap',
@@ -234,6 +251,115 @@ final class Sitemap {
234
  );
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  /**
238
  * Outputs sitemap.xml 'file' and header.
239
  *
@@ -243,8 +369,18 @@ final class Sitemap {
243
  * 3. Now overrides other header tags.
244
  * @since 4.0.0 1. Moved to \The_SEO_Framework\Bridges\Sitemap
245
  * 2. Renamed from `output_sitemap()`
 
 
 
246
  */
247
- public function output_base_sitemap() {
 
 
 
 
 
 
 
248
 
249
  // Remove output, if any.
250
  static::$tsf->clean_response_header();
@@ -255,7 +391,7 @@ final class Sitemap {
255
  }
256
 
257
  // Fetch sitemap content and add trailing line. Already escaped internally.
258
- static::$tsf->get_view( 'sitemap/xml-sitemap' );
259
  echo "\n";
260
 
261
  // We're done now.
@@ -271,8 +407,9 @@ final class Sitemap {
271
  * 3. Now overrides other header tags.
272
  * @since 4.0.0 1. Moved to \The_SEO_Framework\Bridges\Sitemap
273
  * 2. Renamed from `output_sitemap_xsl_stylesheet()`
 
274
  */
275
- public function output_stylesheet() {
276
 
277
  static::$tsf->clean_response_header();
278
 
@@ -352,6 +489,25 @@ final class Sitemap {
352
  echo '</urlset>';
353
  }
354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  /**
356
  * Returns the sitemap path prefix.
357
  * Useful when the prefix path is non-standard, like notoriously in Polylang.
@@ -435,19 +591,19 @@ final class Sitemap {
435
  private function get_sitemap_base_path_info() {
436
  global $wp_rewrite;
437
 
438
- $home_path = rtrim( parse_url( \get_home_url(), PHP_URL_PATH ), '/' );
439
  $prefix = $this->get_sitemap_path_prefix();
440
 
441
  $use_query_var = false;
442
 
443
  if ( $wp_rewrite->using_index_permalinks() ) {
444
- $path = "$home_path/index.php$prefix";
445
  } elseif ( $wp_rewrite->using_permalinks() ) {
446
- $path = "$home_path$prefix";
447
  } else {
448
  // Yes, we know. This is not really checking for standardized query-variables.
449
  // It's straightforward and doesn't mess with the rest of the site, however.
450
- $path = "$home_path$prefix?tsf-sitemap=";
451
 
452
  $use_query_var = true;
453
  }
45
  * @final Can't be extended.
46
  */
47
  final class Sitemap {
 
48
 
49
  /**
50
  * @since 4.0.0
151
  * Returns the expected sitemap endpoint for the given ID.
152
  *
153
  * @since 4.0.0
154
+ * @since 4.1.2 No longer passes the path to the home_url() function because
155
+ * Polylang is being astonishingly asinine.
156
  * @global \WP_Rewrite $wp_rewrite
157
  *
158
  * @param string $id The base ID. Default 'base'.
169
  $scheme = static::$tsf->get_preferred_scheme();
170
  $prefix = $this->get_sitemap_path_prefix();
171
 
172
+ $home_url = \home_url( '/', $scheme );
173
+
174
+ // Other plugins may append a query (such as translations).
175
+ $home_query = parse_url( $home_url, PHP_URL_QUERY );
176
+ // Remove query from URL when found. Add back later.
177
+ if ( $home_query )
178
+ $home_url = static::$tsf->s_url( $home_url );
179
+
180
  if ( $wp_rewrite->using_index_permalinks() ) {
181
+ $path = "/index.php$prefix{$list[ $id ]['endpoint']}";
182
  } elseif ( $wp_rewrite->using_permalinks() ) {
183
+ $path = "$prefix{$list[ $id ]['endpoint']}";
184
  } else {
185
+ $path = "$prefix?tsf-sitemap=$id";
186
  }
187
 
188
+ $url = \trailingslashit( $home_url ) . ltrim( $path, '/' );
189
+
190
+ if ( $home_query )
191
+ $url = static::$tsf->append_php_query( $url, $home_query );
192
+
193
  return \esc_url_raw( $url );
194
  }
195
 
209
  * @link Example: https://github.com/sybrew/tsf-term-sitemap
210
  * @param array $list The endpoints: {
211
  * 'id' => array: {
212
+ * 'cache_id' => string Optional. The cache key to use for locking. Defaults to index 'id'.
213
  * 'endpoint' => string The expected "pretty" endpoint, meant for administrative display.
214
  * 'epregex' => string The endpoint regex, following the home path regex.
215
  * N.B. Be wary of case sensitivity. Append the i-flag.
228
  'the_seo_framework_sitemap_endpoint_list',
229
  [
230
  'base' => [
231
+ 'lock_id' => 'base',
232
  'endpoint' => 'sitemap.xml',
233
  'regex' => '/^sitemap\.xml/i',
234
  'callback' => static::class . '::output_base_sitemap',
235
  'robots' => true,
236
  ],
237
  'index' => [
238
+ 'lock_id' => 'base',
239
  'endpoint' => 'sitemap_index.xml',
240
  'regex' => '/^sitemap_index\.xml/i',
241
  'callback' => static::class . '::output_base_sitemap',
251
  );
252
  }
253
 
254
+ /**
255
+ * Tells whether sitemap caching is enabled by user.
256
+ *
257
+ * @since 4.1.2
258
+ *
259
+ * @return bool
260
+ */
261
+ public function sitemap_cache_enabled() {
262
+ return (bool) static::$tsf->get_option( 'cache_sitemap' );
263
+ }
264
+
265
+ /**
266
+ * Outputs a '503: Service Unavailable' header and no-cache headers.
267
+ *
268
+ * @since 4.1.2
269
+ * TODO consider instead of sending me, output the previous sitemap from cache, instead? Spaghetti.
270
+ *
271
+ * @param int $timeout How many seconds the user has to wait. Optional. Leave 0 to send a generic message.
272
+ */
273
+ public function output_locked_header( $timeout = 0 ) {
274
+ static::$tsf->clean_response_header();
275
+ \status_header( 503 );
276
+ \nocache_headers();
277
+ if ( $timeout ) {
278
+ printf(
279
+ 'Sitemap is locked for %d seconds. Try again later.',
280
+ (int) ( $timeout - time() )
281
+ );
282
+ } else {
283
+ echo 'Sitemap is locked temporarily. Try again later.';
284
+ }
285
+ echo PHP_EOL;
286
+ exit;
287
+ }
288
+
289
+ /**
290
+ * Returns the sitemap's lock cache ID.
291
+ *
292
+ * @since 4.1.2
293
+ *
294
+ * @param string|false $sitemap_id The sitemap ID to test. False when key is invalid.
295
+ */
296
+ public function get_lock_key( $sitemap_id = 'base' ) {
297
+
298
+ $ep_list = $this->get_sitemap_endpoint_list();
299
+
300
+ if ( ! isset( $ep_list[ $sitemap_id ] ) ) return false;
301
+
302
+ $lock_id = isset( $ep_list[ $sitemap_id ]['lock_id'] ) ? $ep_list[ $sitemap_id ]['lock_id'] : $sitemap_id;
303
+
304
+ return static::$tsf->generate_cache_key( 0, '', 'sitemap_lock' ) . "_{$lock_id}";
305
+ }
306
+
307
+ /**
308
+ * Locks a sitemap for the current blog & locale and $sitemap_id, preferably
309
+ * at least as long as PHP is allowed to run.
310
+ *
311
+ * @since 4.1.2
312
+ *
313
+ * @param string $sitemap_id The sitemap ID.
314
+ * @return bool True on succes, false on failure.
315
+ */
316
+ public function lock_sitemap( $sitemap_id = 'base' ) {
317
+
318
+ $lock_key = $this->get_lock_key( $sitemap_id );
319
+ if ( ! $lock_key ) return false;
320
+
321
+ // This is rather at most as PHP will run. However, 3 minutes to generate a sitemap is already ludicrous.
322
+ $timeout = (int) min( ini_get( 'max_execution_time' ), 3 * MINUTE_IN_SECONDS );
323
+
324
+ return \set_transient(
325
+ $lock_key,
326
+ time() + $timeout,
327
+ $timeout
328
+ );
329
+ }
330
+
331
+ /**
332
+ * Unlocks a sitemap for the current blog & locale and $sitemap_id.
333
+ *
334
+ * @since 4.1.2
335
+ *
336
+ * @param string $sitemap_id The sitemap ID.
337
+ * @return bool True on succes, false on failure.
338
+ */
339
+ public function unlock_sitemap( $sitemap_id = 'base' ) {
340
+
341
+ $lock_key = $this->get_lock_key( $sitemap_id );
342
+ if ( ! $lock_key ) return false;
343
+
344
+ return \delete_transient( $lock_key );
345
+ }
346
+
347
+ /**
348
+ * Tells whether a sitemap is locked for the current blog & locale and $sitemap_id.
349
+ *
350
+ * @since 4.1.2
351
+ *
352
+ * @param string $sitemap_id The sitemap ID.
353
+ * @return bool|int False if not locked, the lock UNIX release time otherwise.
354
+ */
355
+ public function is_sitemap_locked( $sitemap_id = 'base' ) {
356
+
357
+ $lock_key = $this->get_lock_key( $sitemap_id );
358
+ if ( ! $lock_key ) return false;
359
+
360
+ return \get_transient( $lock_key );
361
+ }
362
+
363
  /**
364
  * Outputs sitemap.xml 'file' and header.
365
  *
369
  * 3. Now overrides other header tags.
370
  * @since 4.0.0 1. Moved to \The_SEO_Framework\Bridges\Sitemap
371
  * 2. Renamed from `output_sitemap()`
372
+ * @since 4.1.2 Is now static.
373
+ *
374
+ * @param string $sitemap_id The sitemap ID.
375
  */
376
+ public static function output_base_sitemap( $sitemap_id = 'base' ) {
377
+
378
+ $locked_timeout = static::get_instance()->is_sitemap_locked( $sitemap_id );
379
+
380
+ if ( false !== $locked_timeout ) {
381
+ static::get_instance()->output_locked_header( $locked_timeout );
382
+ exit;
383
+ }
384
 
385
  // Remove output, if any.
386
  static::$tsf->clean_response_header();
391
  }
392
 
393
  // Fetch sitemap content and add trailing line. Already escaped internally.
394
+ static::$tsf->get_view( 'sitemap/xml-sitemap', compact( 'sitemap_id' ) );
395
  echo "\n";
396
 
397
  // We're done now.
407
  * 3. Now overrides other header tags.
408
  * @since 4.0.0 1. Moved to \The_SEO_Framework\Bridges\Sitemap
409
  * 2. Renamed from `output_sitemap_xsl_stylesheet()`
410
+ * @since 4.1.2 Is now static.
411
  */
412
+ public static function output_stylesheet() {
413
 
414
  static::$tsf->clean_response_header();
415
 
489
  echo '</urlset>';
490
  }
491
 
492
+ /**
493
+ * Returns the sitemap base path.
494
+ * Useful when the path is non-standard, like notoriously in Polylang.
495
+ *
496
+ * @since 4.1.2
497
+ *
498
+ * @return string The path.
499
+ */
500
+ private function get_sitemap_base_path() {
501
+ /**
502
+ * @since 4.1.2
503
+ * @param string $path The home path.
504
+ */
505
+ return \apply_filters(
506
+ 'the_seo_framework_sitemap_base_path',
507
+ rtrim( parse_url( \get_home_url(), PHP_URL_PATH ), '/' )
508
+ );
509
+ }
510
+
511
  /**
512
  * Returns the sitemap path prefix.
513
  * Useful when the prefix path is non-standard, like notoriously in Polylang.
591
  private function get_sitemap_base_path_info() {
592
  global $wp_rewrite;
593
 
594
+ $base_path = $this->get_sitemap_base_path();
595
  $prefix = $this->get_sitemap_path_prefix();
596
 
597
  $use_query_var = false;
598
 
599
  if ( $wp_rewrite->using_index_permalinks() ) {
600
+ $path = "$base_path/index.php$prefix";
601
  } elseif ( $wp_rewrite->using_permalinks() ) {
602
+ $path = "$base_path$prefix";
603
  } else {
604
  // Yes, we know. This is not really checking for standardized query-variables.
605
  // It's straightforward and doesn't mess with the rest of the site, however.
606
+ $path = "$base_path$prefix?tsf-sitemap=";
607
 
608
  $use_query_var = true;
609
  }
inc/classes/bridges/termsettings.class.php CHANGED
@@ -34,7 +34,6 @@ namespace The_SEO_Framework\Bridges;
34
  * @final Can't be extended.
35
  */
36
  final class TermSettings {
37
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
38
 
39
  /**
40
  * Prepares the setting fields.
34
  * @final Can't be extended.
35
  */
36
  final class TermSettings {
 
37
 
38
  /**
39
  * Prepares the setting fields.
inc/classes/builders/coresitemaps/index.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The great questions of the day will not be settled by means of speeches and
4
+ * majority descisions, but by iron and blood.
5
+ *
6
+ * - Otto von Bismarck
7
+ */
inc/classes/builders/coresitemaps/main.class.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Builders\Core_Sitemaps
4
+ * @subpackage The_SEO_Framework\Classes\Builders\Sitemap
5
+ */
6
+
7
+ namespace The_SEO_Framework\Builders\CoreSitemaps;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
12
+ *
13
+ * This program is free software: you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License version 3 as published
15
+ * by the Free Software Foundation.
16
+ *
17
+ * This program is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ */
25
+
26
+ \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
27
+
28
+ /**
29
+ * Augments the WordPress Core sitemap.
30
+ *
31
+ * @since 4.1.2
32
+ *
33
+ * @access private
34
+ */
35
+ class Main extends \The_SEO_Framework\Builders\Sitemap {
36
+
37
+ /**
38
+ * @since 4.1.2
39
+ * @var \The_SEO_Framework\Builders\Sitemap
40
+ */
41
+ private static $instance;
42
+
43
+ /**
44
+ * Returns this instance.
45
+ *
46
+ * @since 4.1.2
47
+ *
48
+ * @return \The_SEO_Framework\Builders\Sitemap $instance
49
+ */
50
+ public static function get_instance() {
51
+ return static::$instance ?: ( static::$instance = new static );
52
+ }
53
+
54
+ /**
55
+ * Generate sitemap.xml content.
56
+ *
57
+ * @since 4.1.2
58
+ * @abstract
59
+ * @ignore
60
+ *
61
+ * @return string The sitemap content.
62
+ */
63
+ public function build_sitemap() {
64
+ return '';
65
+ }
66
+
67
+ /**
68
+ * Filters Core sitemap provider.
69
+ *
70
+ * @since 4.1.2
71
+ * @access private
72
+ *
73
+ * @param \WP_Sitemaps_Provider $provider Instance of a \WP_Sitemaps_Provider.
74
+ * @param string $name Name of the sitemap provider.
75
+ * @return \WP_Sitemaps_Provider|null The original or augmented instance of a \WP_Sitemaps_Provider.
76
+ * null if the provider is disabled.
77
+ */
78
+ public static function _filter_add_provider( $provider, $name ) {
79
+
80
+ if ( ! $provider instanceof \WP_Sitemaps_Provider ) {
81
+ return $provider;
82
+ }
83
+
84
+ switch ( $name ) {
85
+ case 'posts':
86
+ $provider = new Posts;
87
+ break;
88
+ case 'taxonomies':
89
+ $provider = new Taxonomies;
90
+ break;
91
+ case 'users':
92
+ // This option is not reversible through means other than filters.
93
+ // static::$tsf isn't set, because static doesn't require instantiation here.
94
+ if ( \the_seo_framework()->get_option( 'author_noindex' ) )
95
+ $provider = null;
96
+ break;
97
+
98
+ default:
99
+ break;
100
+ }
101
+
102
+ return $provider;
103
+ }
104
+
105
+ /**
106
+ * Filters Core sitemap query limit.
107
+ *
108
+ * @since 4.1.2
109
+ * @access private
110
+ *
111
+ * @return string The sitemap query limit.
112
+ */
113
+ public static function _filter_max_urls() {
114
+ return static::get_instance()->get_sitemap_post_limit();
115
+ }
116
+ }
inc/classes/builders/coresitemaps/posts.class.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Builders\Core_Sitemaps
4
+ * @subpackage WordPress\Sitemaps
5
+ */
6
+
7
+ namespace The_SEO_Framework\Builders\CoreSitemaps;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
12
+ *
13
+ * This program is free software: you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License version 3 as published
15
+ * by the Free Software Foundation.
16
+ *
17
+ * This program is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ */
25
+
26
+ \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
27
+
28
+ /**
29
+ * Augments the WordPress Core 'posts' sitemap.
30
+ *
31
+ * @since 4.1.2
32
+ *
33
+ * @access private
34
+ */
35
+ class Posts extends \WP_Sitemaps_Posts {
36
+
37
+ /**
38
+ * Gets a URL list for a post type sitemap.
39
+ *
40
+ * Copied from parent and augmented slightly to return
41
+ *
42
+ * @since 4.1.2
43
+ * @source \WP_Sitemaps_Posts\get_url_list()
44
+ * @TEMP https://wordpress.slack.com/archives/CTKTGNJJW/p1604995479019700
45
+ * @link <https://core.trac.wordpress.org/ticket/51860>
46
+ *
47
+ * @param int $page_num Page of results.
48
+ * @param string $post_type Optional. Post type name. Default empty.
49
+ * @return array Array of URLs for a sitemap.
50
+ */
51
+ public function get_url_list( $page_num, $post_type = '' ) {
52
+ // Bail early if the queried post type is not supported.
53
+ $supported_types = $this->get_object_subtypes();
54
+
55
+ if ( ! isset( $supported_types[ $post_type ] ) ) {
56
+ return [];
57
+ }
58
+
59
+ /**
60
+ * Filters the posts URL list before it is generated.
61
+ *
62
+ * Passing a non-null value will effectively short-circuit the generation,
63
+ * returning that value instead.
64
+ *
65
+ * @since WP Core 5.5.0
66
+ *
67
+ * @param array $url_list The URL list. Default null.
68
+ * @param string $post_type Post type name.
69
+ * @param int $page_num Page of results.
70
+ */
71
+ $url_list = \apply_filters(
72
+ 'wp_sitemaps_posts_pre_url_list',
73
+ null,
74
+ $post_type,
75
+ $page_num
76
+ );
77
+
78
+ if ( null !== $url_list ) {
79
+ return $url_list;
80
+ }
81
+
82
+ $args = $this->get_posts_query_args( $post_type );
83
+ $args['paged'] = $page_num;
84
+
85
+ $query = new \WP_Query( $args );
86
+
87
+ $url_list = [];
88
+
89
+ /**
90
+ * @augmented This differs from the inherented.
91
+ */
92
+ $tsf = \the_seo_framework();
93
+ $main = Main::get_instance();
94
+ $show_modified = (bool) $tsf->get_option( 'sitemaps_modified' );
95
+ $timestamp_format = $tsf->get_timestamp_format();
96
+
97
+ /*
98
+ * Add a URL for the homepage in the pages sitemap.
99
+ * Shows only on the first page if the reading settings are set to display latest posts.
100
+ */
101
+ if ( 'page' === $post_type && 1 === $page_num && 'posts' === \get_option( 'show_on_front' ) ) {
102
+ /**
103
+ * @augmented This if-statement prevents including the homepage as blog in the sitemap when conditions apply.
104
+ */
105
+ if ( $main->is_post_included_in_sitemap( 0 ) ) {
106
+ // Extract the data needed for home URL to add to the array.
107
+ $sitemap_entry = [
108
+ 'loc' => \home_url( '/' ),
109
+ ];
110
+
111
+ /**
112
+ * @augmented Adds lastmod to sitemap entry.
113
+ */
114
+ if ( $show_modified ) {
115
+ $latests_posts = \wp_get_recent_posts(
116
+ [
117
+ 'numberposts' => 1,
118
+ 'post_type' => 'post',
119
+ 'post_status' => 'publish',
120
+ 'has_password' => false,
121
+ 'orderby' => 'post_date',
122
+ 'order' => 'DESC',
123
+ 'offset' => 0,
124
+ ],
125
+ \OBJECT
126
+ );
127
+
128
+ $lastmod = isset( $latests_posts[0]->post_date_gmt ) ? $latests_posts[0]->post_date_gmt : '0000-00-00 00:00:00';
129
+
130
+ /**
131
+ * @since 4.1.1
132
+ * @param string $lastmod The lastmod time in SQL notation (`Y-m-d H:i:s`). Expected to explicitly follow that format!
133
+ */
134
+ $sitemap_entry['lastmod'] = (string) \apply_filters( 'the_seo_framework_sitemap_blog_lastmod', $lastmod );
135
+ }
136
+
137
+ /**
138
+ * Filters the sitemap entry for the home page when the 'show_on_front' option equals 'posts'.
139
+ *
140
+ * @since WP Core 5.5.0
141
+ *
142
+ * @param array $sitemap_entry Sitemap entry for the home page.
143
+ */
144
+ $sitemap_entry = \apply_filters( 'wp_sitemaps_posts_show_on_front_entry', $sitemap_entry );
145
+ $url_list[] = $sitemap_entry;
146
+ }
147
+ }
148
+
149
+ foreach ( $query->posts as $post ) {
150
+ /**
151
+ * @augmented This if-statement prevents including the post in the sitemap when conditions apply.
152
+ */
153
+ if ( ! $main->is_post_included_in_sitemap( $post->ID ) )
154
+ continue;
155
+
156
+ $sitemap_entry = [
157
+ 'loc' => \get_permalink( $post ),
158
+ ];
159
+
160
+ /**
161
+ * @augmented Adds lastmod to sitemap entry.
162
+ */
163
+ if ( $show_modified ) {
164
+ $lastmod = isset( $post->post_modified_gmt ) ? $post->post_modified_gmt : false;
165
+
166
+ if ( $lastmod && '0000-00-00 00:00:00' !== $lastmod )
167
+ $sitemap_entry['lastmod'] = $tsf->gmt2date( $timestamp_format, $lastmod );
168
+ }
169
+
170
+ /**
171
+ * Filters the sitemap entry for an individual post.
172
+ *
173
+ * @since WP Core 5.5.0
174
+ *
175
+ * @param array $sitemap_entry Sitemap entry for the post.
176
+ * @param WP_Post $post Post object.
177
+ * @param string $post_type Name of the post_type.
178
+ */
179
+ $sitemap_entry = \apply_filters( 'wp_sitemaps_posts_entry', $sitemap_entry, $post, $post_type );
180
+ $url_list[] = $sitemap_entry;
181
+ }
182
+
183
+ return $url_list;
184
+ }
185
+ }
inc/classes/builders/coresitemaps/taxonomies.class.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Builders\Core_Sitemaps
4
+ * @subpackage WordPress\Sitemaps
5
+ */
6
+
7
+ namespace The_SEO_Framework\Builders\CoreSitemaps;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
12
+ *
13
+ * This program is free software: you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License version 3 as published
15
+ * by the Free Software Foundation.
16
+ *
17
+ * This program is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24
+ */
25
+
26
+ \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
27
+
28
+ /**
29
+ * Augments the WordPress Core 'taxonomies' sitemap.
30
+ *
31
+ * @since 4.1.2
32
+ *
33
+ * @access private
34
+ */
35
+ class Taxonomies extends \WP_Sitemaps_Taxonomies {
36
+
37
+ /**
38
+ * Gets a URL list for a taxonomy sitemap.
39
+ *
40
+ * @since 4.1.2
41
+ * @source \WP_Sitemaps_Taxonomies\get_url_list()
42
+ * @TEMP https://wordpress.slack.com/archives/CTKTGNJJW/p1604995479019700
43
+ * @link <https://core.trac.wordpress.org/ticket/51860>
44
+ *
45
+ * @param int $page_num Page of results.
46
+ * @param string $taxonomy Optional. Taxonomy name. Default empty.
47
+ * @return array Array of URLs for a sitemap.
48
+ */
49
+ public function get_url_list( $page_num, $taxonomy = '' ) {
50
+
51
+ $supported_types = $this->get_object_subtypes();
52
+
53
+ // Bail early if the queried taxonomy is not supported.
54
+ if ( ! isset( $supported_types[ $taxonomy ] ) ) {
55
+ return [];
56
+ }
57
+
58
+ /**
59
+ * Filters the taxonomies URL list before it is generated.
60
+ *
61
+ * Passing a non-null value will effectively short-circuit the generation,
62
+ * returning that value instead.
63
+ *
64
+ * @since WP Core 5.5.0
65
+ *
66
+ * @param array $url_list The URL list. Default null.
67
+ * @param string $taxonomy Taxonomy name.
68
+ * @param int $page_num Page of results.
69
+ */
70
+ $url_list = \apply_filters(
71
+ 'wp_sitemaps_taxonomies_pre_url_list',
72
+ null,
73
+ $taxonomy,
74
+ $page_num
75
+ );
76
+
77
+ if ( null !== $url_list ) {
78
+ return $url_list;
79
+ }
80
+
81
+ $url_list = [];
82
+
83
+ $main = Main::get_instance();
84
+
85
+ // Offset by how many terms should be included in previous pages.
86
+ $offset = ( $page_num - 1 ) * \wp_sitemaps_get_max_urls( $this->object_type );
87
+
88
+ $args = $this->get_taxonomies_query_args( $taxonomy );
89
+ $args['offset'] = $offset;
90
+
91
+ $taxonomy_terms = new \WP_Term_Query( $args );
92
+
93
+ if ( ! empty( $taxonomy_terms->terms ) ) {
94
+ foreach ( $taxonomy_terms->terms as $term ) {
95
+ /**
96
+ * @augmented This if-statement prevents including the term in the sitemap when conditions apply.
97
+ */
98
+ if ( ! $main->is_term_included_in_sitemap( $term, $taxonomy ) )
99
+ continue;
100
+
101
+ $term_link = \get_term_link( $term, $taxonomy );
102
+
103
+ if ( \is_wp_error( $term_link ) ) {
104
+ continue;
105
+ }
106
+
107
+ $sitemap_entry = [
108
+ 'loc' => $term_link,
109
+ ];
110
+
111
+ /**
112
+ * Filters the sitemap entry for an individual term.
113
+ *
114
+ * @since WP Core 5.5.0
115
+ *
116
+ * @param array $sitemap_entry Sitemap entry for the term.
117
+ * @param WP_Term $term Term object.
118
+ * @param string $taxonomy Taxonomy name.
119
+ */
120
+ $sitemap_entry = \apply_filters( 'wp_sitemaps_taxonomies_entry', $sitemap_entry, $term, $taxonomy );
121
+ $url_list[] = $sitemap_entry;
122
+ }
123
+ }
124
+
125
+ return $url_list;
126
+ }
127
+ }
inc/classes/builders/images.class.php CHANGED
@@ -31,7 +31,6 @@ namespace The_SEO_Framework\Builders;
31
  * @since 4.0.0
32
  */
33
  final class Images {
34
- use \The_SEO_Framework\Traits\Enclose_Core_Final;
35
 
36
  /**
37
  * The constructor. Or rather, the lack thereof.
31
  * @since 4.0.0
32
  */
33
  final class Images {
 
34
 
35
  /**
36
  * The constructor. Or rather, the lack thereof.
inc/classes/builders/scripts.class.php CHANGED
@@ -53,7 +53,6 @@ $_load_scripts_class = function() {
53
  * @final Can't be extended.
54
  */
55
  final class Scripts {
56
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
57
 
58
  /**
59
  * Codes to maintain the internal state of the scripts. This state might not reflect
@@ -198,6 +197,20 @@ final class Scripts {
198
  static::$instance->_output_templates();
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  /**
202
  * Registers script to be enqueued. Can register multiple scripts at once.
203
  *
@@ -449,7 +462,6 @@ final class Scripts {
449
  return $out;
450
  }
451
 
452
-
453
  /**
454
  * Concatenates inline JS.
455
  *
@@ -563,6 +575,7 @@ final class Scripts {
563
  *
564
  * @since 3.1.0
565
  * @since 3.2.2 Now clears outputted templates, so to prevent duplications.
 
566
  * @see $this->forward_known_scripts()
567
  * @see $this->register_template()
568
  * @see $this->autoload_scripts()
@@ -572,9 +585,11 @@ final class Scripts {
572
  public function _output_templates() {
573
  foreach ( static::$templates as $id => $templates ) {
574
  if ( \wp_script_is( $id, 'enqueued' ) ) { // This list retains scripts after they're outputted.
 
 
 
575
  foreach ( $templates as $t )
576
  $this->output_view( $t[0], $t[1] );
577
- unset( static::$templates[ $id ] );
578
  }
579
  }
580
  }
53
  * @final Can't be extended.
54
  */
55
  final class Scripts {
 
56
 
57
  /**
58
  * Codes to maintain the internal state of the scripts. This state might not reflect
197
  static::$instance->_output_templates();
198
  }
199
 
200
+ /**
201
+ * Enqueues all known registeres scripts, styles, and templates,
202
+ * in the footer, right before WordPress's last script-outputting call.
203
+ *
204
+ * @since 4.1.2
205
+ * @see ABSPATH.wp-admin/admin-footer.php
206
+ */
207
+ public static function footer_enqueue() {
208
+
209
+ if ( \The_SEO_Framework\_has_run( __METHOD__ ) ) return;
210
+
211
+ \add_action( 'admin_footer', [ static::class, 'enqueue' ], 998 );
212
+ }
213
+
214
  /**
215
  * Registers script to be enqueued. Can register multiple scripts at once.
216
  *
462
  return $out;
463
  }
464
 
 
465
  /**
466
  * Concatenates inline JS.
467
  *
575
  *
576
  * @since 3.1.0
577
  * @since 3.2.2 Now clears outputted templates, so to prevent duplications.
578
+ * @since 4.1.2 Now clears templates right before outputting them, so to prevent a plausible infinite loop.
579
  * @see $this->forward_known_scripts()
580
  * @see $this->register_template()
581
  * @see $this->autoload_scripts()
585
  public function _output_templates() {
586
  foreach ( static::$templates as $id => $templates ) {
587
  if ( \wp_script_is( $id, 'enqueued' ) ) { // This list retains scripts after they're outputted.
588
+ // Unset template before the loop, to prevent an infinite loop.
589
+ unset( static::$templates[ $id ] );
590
+
591
  foreach ( $templates as $t )
592
  $this->output_view( $t[0], $t[1] );
 
593
  }
594
  }
595
  }
inc/classes/builders/seobar.class.php CHANGED
@@ -40,7 +40,6 @@ namespace The_SEO_Framework\Builders;
40
  * @see \The_SEO_Framework\Interpreters\SeoBar
41
  */
42
  abstract class SeoBar {
43
- use \The_SEO_Framework\Traits\Enclose_Core_Final;
44
 
45
  /**
46
  * @since 4.0.0
40
  * @see \The_SEO_Framework\Interpreters\SeoBar
41
  */
42
  abstract class SeoBar {
 
43
 
44
  /**
45
  * @since 4.0.0
inc/classes/builders/sitemap-base.class.php CHANGED
@@ -34,6 +34,105 @@ namespace The_SEO_Framework\Builders;
34
  */
35
  class Sitemap_Base extends Sitemap {
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  /**
38
  * Generate sitemap.xml content.
39
  *
@@ -69,8 +168,13 @@ class Sitemap_Base extends Sitemap {
69
  $content .= sprintf(
70
  '<!-- %s -->',
71
  sprintf(
72
- /* translators: %s = timestamp */
73
- \esc_html__( 'Sitemap is generated on %s', 'autodescription' ),
 
 
 
 
 
74
  \current_time( 'Y-m-d H:i:s \G\M\T' )
75
  )
76
  ) . "\n";
@@ -306,7 +410,7 @@ class Sitemap_Base extends Sitemap {
306
  'order' => 'DESC',
307
  'offset' => 0,
308
  ],
309
- OBJECT
310
  );
311
  $latest_post = isset( $latests_posts[0] ) ? $latests_posts[0] : null;
312
  $_publish_post = isset( $latest_post->post_date_gmt ) ? $latest_post->post_date_gmt : '0000-00-00 00:00:00';
@@ -352,7 +456,7 @@ class Sitemap_Base extends Sitemap {
352
  'order' => 'DESC',
353
  'offset' => 0,
354
  ],
355
- OBJECT
356
  );
357
 
358
  $_values['lastmod'] = isset( $latests_posts[0]->post_date_gmt ) ? $latests_posts[0]->post_date_gmt : '0000-00-00 00:00:00';
34
  */
35
  class Sitemap_Base extends Sitemap {
36
 
37
+ /**
38
+ * @since 4.1.2
39
+ * @var bool
40
+ */
41
+ public $base_is_regenerated = false;
42
+
43
+ /**
44
+ * @since 4.1.2
45
+ * @var bool
46
+ */
47
+ public $base_is_prerendering = false;
48
+
49
+ /**
50
+ * Returns the base sitemap's storage transient name.
51
+ *
52
+ * @since 4.1.2
53
+ *
54
+ * @return string.
55
+ */
56
+ public function base_get_sitemap_store_key() {
57
+ return static::$tsf->get_sitemap_transient_name();
58
+ }
59
+
60
+ /**
61
+ * Generates the sitemap, and stores the generated content in the database.
62
+ *
63
+ * Note that this will work sporadically with translation plugins; however,
64
+ * it will not conflict, since a unique caching key is generated for each language.
65
+ * TODO consider expanding this feature for multilingual sites?
66
+ *
67
+ * @since 4.1.2
68
+ */
69
+ public function prerender_sitemap() {
70
+
71
+ $bridge = \The_SEO_Framework\Bridges\Sitemap::get_instance();
72
+
73
+ if ( ! $bridge->sitemap_cache_enabled() ) return;
74
+
75
+ // Don't prerender if the sitemap is already generated.
76
+ if ( false !== static::$tsf->get_transient( $this->base_get_sitemap_store_key() ) ) return;
77
+
78
+ set_time_limit( (int) max( ini_get( 'max_execution_time' ), 3 * MINUTE_IN_SECONDS ) );
79
+
80
+ // Somehow, the 'base' key is unavailable, the database failed, or a lock is already in place. Either way, bail.
81
+ if ( ! $bridge->lock_sitemap( 'base' ) ) return;
82
+
83
+ $this->prepare_generation();
84
+ $this->base_is_prerendering = true;
85
+
86
+ static::$tsf->set_transient(
87
+ $this->base_get_sitemap_store_key(),
88
+ $this->build_sitemap(),
89
+ WEEK_IN_SECONDS
90
+ );
91
+
92
+ $bridge->unlock_sitemap( 'base' );
93
+
94
+ $this->shutdown_generation();
95
+ $this->base_is_regenerated = true;
96
+ }
97
+
98
+ /**
99
+ * Returns the generated sitemap. Also stores it in the database when caching is enabled.
100
+ *
101
+ * @since 4.1.2
102
+ * @abstract
103
+ *
104
+ * @param string $sitemap_id The sitemap ID. Expected either 'base' or 'index'--or otherwise overwriten via the API.
105
+ * @return string The sitemap content.
106
+ */
107
+ public function generate_sitemap( $sitemap_id = 'base' ) {
108
+
109
+ $bridge = \The_SEO_Framework\Bridges\Sitemap::get_instance();
110
+ $_caching_enabled = $bridge->sitemap_cache_enabled();
111
+
112
+ $sitemap_content = $_caching_enabled ? static::$tsf->get_transient( $this->base_get_sitemap_store_key() ) : false;
113
+
114
+ if ( false === $sitemap_content ) {
115
+ $this->prepare_generation();
116
+ $_caching_enabled && $bridge->lock_sitemap( $sitemap_id );
117
+
118
+ $sitemap_content = $this->build_sitemap();
119
+
120
+ $this->shutdown_generation();
121
+ $this->base_is_regenerated = true;
122
+
123
+ if ( $_caching_enabled ) {
124
+ static::$tsf->set_transient(
125
+ $this->base_get_sitemap_store_key(),
126
+ $sitemap_content,
127
+ WEEK_IN_SECONDS
128
+ );
129
+ $bridge->unlock_sitemap( $sitemap_id );
130
+ }
131
+ }
132
+
133
+ return $sitemap_content;
134
+ }
135
+
136
  /**
137
  * Generate sitemap.xml content.
138
  *
168
  $content .= sprintf(
169
  '<!-- %s -->',
170
  sprintf(
171
+ (
172
+ $this->base_is_prerendering
173
+ /* translators: %s = timestamp */
174
+ ? \esc_html__( 'Sitemap is prerendered on %s', 'autodescription' )
175
+ /* translators: %s = timestamp */
176
+ : \esc_html__( 'Sitemap is generated on %s', 'autodescription' )
177
+ ),
178
  \current_time( 'Y-m-d H:i:s \G\M\T' )
179
  )
180
  ) . "\n";
410
  'order' => 'DESC',
411
  'offset' => 0,
412
  ],
413
+ \OBJECT
414
  );
415
  $latest_post = isset( $latests_posts[0] ) ? $latests_posts[0] : null;
416
  $_publish_post = isset( $latest_post->post_date_gmt ) ? $latest_post->post_date_gmt : '0000-00-00 00:00:00';
456
  'order' => 'DESC',
457
  'offset' => 0,
458
  ],
459
+ \OBJECT
460
  );
461
 
462
  $_values['lastmod'] = isset( $latests_posts[0]->post_date_gmt ) ? $latests_posts[0]->post_date_gmt : '0000-00-00 00:00:00';
inc/classes/builders/sitemap.class.php CHANGED
@@ -34,7 +34,6 @@ namespace The_SEO_Framework\Builders;
34
  * @access public
35
  */
36
  abstract class Sitemap {
37
- use \The_SEO_Framework\Traits\Enclose_Core_Final;
38
 
39
  /**
40
  * @var null|\The_SEO_Framework\Load
@@ -82,6 +81,27 @@ abstract class Sitemap {
82
  static::$tsf->reset_timezone();
83
  }
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  /**
86
  * Returns the sitemap content.
87
  *
@@ -188,7 +208,6 @@ abstract class Sitemap {
188
  if ( null === $excluded ) {
189
  /**
190
  * @since 4.0.0
191
- * @ignore NOT USED INTERNALLY!
192
  * @param array $excluded Sequential list of excluded IDs: [ int ...term_id ]
193
  */
194
  $excluded = (array) \apply_filters( 'the_seo_framework_sitemap_exclude_term_ids', [] );
34
  * @access public
35
  */
36
  abstract class Sitemap {
 
37
 
38
  /**
39
  * @var null|\The_SEO_Framework\Load
81
  static::$tsf->reset_timezone();
82
  }
83
 
84
+ /**
85
+ * Generates and returns the sitemap content.
86
+ * We recommend you overwriting this method to include caching.
87
+ *
88
+ * @since 4.1.2
89
+ * @abstract
90
+ * TODO consider adding ...$args?
91
+ *
92
+ * @return string The sitemap content.
93
+ */
94
+ public function generate_sitemap() {
95
+
96
+ $this->prepare_generation();
97
+
98
+ $sitemap = $this->build_sitemap();
99
+
100
+ $this->shutdown_generation();
101
+
102
+ return $sitemap;
103
+ }
104
+
105
  /**
106
  * Returns the sitemap content.
107
  *
208
  if ( null === $excluded ) {
209
  /**
210
  * @since 4.0.0
 
211
  * @param array $excluded Sequential list of excluded IDs: [ int ...term_id ]
212
  */
213
  $excluded = (array) \apply_filters( 'the_seo_framework_sitemap_exclude_term_ids', [] );
inc/classes/cache.class.php CHANGED
@@ -337,6 +337,8 @@ class Cache extends Site_Options {
337
  * or transients have been disabled through a constant, then the transient
338
  * will be false.
339
  *
 
 
340
  * @since 2.6.0
341
  * @uses $this->the_seo_framework_use_transients
342
  *
@@ -616,6 +618,7 @@ class Cache extends Site_Options {
616
  *
617
  * @since 2.9.1
618
  * @since 2.9.2 Now returns false when an incorrect $type is supplied.
 
619
  * @see $this->generate_cache_key().
620
  * @see $this->generate_cache_key_by_query() to get cache key from the query.
621
  *
@@ -642,6 +645,8 @@ class Cache extends Site_Options {
642
  return $this->add_cache_key_suffix( $this->generate_taxonomical_cache_key( $page_id, $taxonomy ) );
643
  case 'ping':
644
  return $this->add_cache_key_suffix( 'tsf_throttle_ping' );
 
 
645
  default:
646
  $this->_doing_it_wrong( __METHOD__, 'Third parameter must be a known type.', '2.6.5' );
647
  return $this->add_cache_key_suffix( \esc_sql( $type . '_' . $page_id . '_' . $taxonomy ) );
@@ -829,16 +834,19 @@ class Cache extends Site_Options {
829
  $transient = $this->get_sitemap_transient_name();
830
  $transient and \delete_transient( $transient );
831
 
832
- $ping_use_cron = $this->get_option( 'ping_use_cron' );
 
833
 
834
  /**
835
  * @since 4.1.1
 
836
  * @param array $params Any useful environment parameters.
837
  */
838
  \do_action(
839
  'the_seo_framework_sitemap_transient_cleared',
840
  [
841
- 'ping_use_cron' => $ping_use_cron,
 
842
  ]
843
  );
844
 
337
  * or transients have been disabled through a constant, then the transient
338
  * will be false.
339
  *
340
+ * N.B. not all transient settings make use of this function, bypassing the constant check.
341
+ *
342
  * @since 2.6.0
343
  * @uses $this->the_seo_framework_use_transients
344
  *
618
  *
619
  * @since 2.9.1
620
  * @since 2.9.2 Now returns false when an incorrect $type is supplied.
621
+ * @since 4.1.2 Now accepts $type 'sitemap_lock'.
622
  * @see $this->generate_cache_key().
623
  * @see $this->generate_cache_key_by_query() to get cache key from the query.
624
  *
645
  return $this->add_cache_key_suffix( $this->generate_taxonomical_cache_key( $page_id, $taxonomy ) );
646
  case 'ping':
647
  return $this->add_cache_key_suffix( 'tsf_throttle_ping' );
648
+ case 'sitemap_lock':
649
+ return $this->add_cache_key_suffix( 'tsf_sitemap_lock' );
650
  default:
651
  $this->_doing_it_wrong( __METHOD__, 'Third parameter must be a known type.', '2.6.5' );
652
  return $this->add_cache_key_suffix( \esc_sql( $type . '_' . $page_id . '_' . $taxonomy ) );
834
  $transient = $this->get_sitemap_transient_name();
835
  $transient and \delete_transient( $transient );
836
 
837
+ $ping_use_cron = $this->get_option( 'ping_use_cron' );
838
+ $ping_use_cron_prerender = $this->get_option( 'ping_use_cron_prerender' );
839
 
840
  /**
841
  * @since 4.1.1
842
+ * @since 4.1.2 Added index `ping_use_cron_prerender` to the first parameter.
843
  * @param array $params Any useful environment parameters.
844
  */
845
  \do_action(
846
  'the_seo_framework_sitemap_transient_cleared',
847
  [
848
+ 'ping_use_cron' => $ping_use_cron,
849
+ 'ping_use_cron_prerender' => $ping_use_cron_prerender,
850
  ]
851
  );
852
 
inc/classes/core.class.php CHANGED
@@ -33,7 +33,6 @@ namespace The_SEO_Framework;
33
  * @since 2.8.0
34
  */
35
  class Core {
36
- use Traits\Enclose_Core_Final;
37
 
38
  /**
39
  * Tells if this plugin is loaded.
@@ -637,11 +636,12 @@ class Core {
637
  *
638
  * @param string $string The string to test and maybe trim
639
  * @param int $over The character limit. Must be over 0 to have effect.
640
- * If 1 is given, the returned string length will be 3.
641
- * If 2 is given, the returned string will only consist of the hellip.
642
  * @return string
643
  */
644
  public function hellip_if_over( $string, $over = 0 ) {
 
645
  if ( $over > 0 && \strlen( $string ) > $over ) {
646
  $string = substr( $string, 0, abs( $over - 2 ) ) . ' &hellip;';
647
  }
33
  * @since 2.8.0
34
  */
35
  class Core {
 
36
 
37
  /**
38
  * Tells if this plugin is loaded.
636
  *
637
  * @param string $string The string to test and maybe trim
638
  * @param int $over The character limit. Must be over 0 to have effect.
639
+ * Bug: If 1 is given, the returned string length will be 3.
640
+ * Bug: If 2 is given, the returned string will only consist of the hellip.
641
  * @return string
642
  */
643
  public function hellip_if_over( $string, $over = 0 ) {
644
+
645
  if ( $over > 0 && \strlen( $string ) > $over ) {
646
  $string = substr( $string, 0, abs( $over - 2 ) ) . ' &hellip;';
647
  }
inc/classes/debug.class.php CHANGED
@@ -36,7 +36,6 @@ namespace The_SEO_Framework;
36
  * @since 4.0.0 No longer implements an interface. It's implied.
37
  */
38
  final class Debug {
39
- use Traits\Enclose_Stray_Private;
40
 
41
  /**
42
  * @since 2.8.0
@@ -292,8 +291,7 @@ final class Debug {
292
  */
293
  protected function get_error( $type = null ) {
294
 
295
- // phpcs:ignore, WordPress.PHP.NoSilencedErrors -- Feature may be disabled.
296
- $backtrace = @debug_backtrace();
297
 
298
  if ( ! $backtrace ) return [];
299
 
36
  * @since 4.0.0 No longer implements an interface. It's implied.
37
  */
38
  final class Debug {
 
39
 
40
  /**
41
  * @since 2.8.0
291
  */
292
  protected function get_error( $type = null ) {
293
 
294
+ $backtrace = debug_backtrace();
 
295
 
296
  if ( ! $backtrace ) return [];
297
 
inc/classes/detect.class.php CHANGED
@@ -540,6 +540,33 @@ class Detect extends Render {
540
  return $detected = (bool) $detected;
541
  }
542
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
  /**
544
  * Detects presence of a page builder.
545
  * Memoizes the return value.
@@ -629,6 +656,7 @@ class Detect extends Render {
629
  * @since 3.1.0 Removed Jetpack's sitemap check -- it's no longer valid.
630
  * @since 4.0.0 : 1. Now uses has_robots_txt()
631
  * : 2. Now uses the get_robots_txt_url() to determine validity.
 
632
  *
633
  * @param bool $check_option Whether to check for sitemap option.
634
  * @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
@@ -731,6 +759,7 @@ class Detect extends Render {
731
  *
732
  * @since 2.2.5
733
  * @since 3.1.0 Removed caching
 
734
  *
735
  * @param string|array required $features The features to check for.
736
  * @return bool theme support.
540
  return $detected = (bool) $detected;
541
  }
542
 
543
+ /**
544
+ * Tells whether WP 5.5 Core Sitemaps are used.
545
+ * Memoizes the return value.
546
+ *
547
+ * @since 4.1.2
548
+ *
549
+ * @return bool
550
+ */
551
+ public function use_core_sitemaps() {
552
+ static $use;
553
+
554
+ if ( isset( $use ) ) return $use;
555
+
556
+ if ( $this->get_option( 'sitemaps_output' ) )
557
+ return $use = false;
558
+
559
+ if ( \function_exists( '\\wp_sitemaps_get_server' ) ) {
560
+ $wp_sitemaps_server = \wp_sitemaps_get_server();
561
+
562
+ return $use =
563
+ method_exists( $wp_sitemaps_server, 'sitemaps_enabled' )
564
+ && $wp_sitemaps_server->sitemaps_enabled();
565
+ }
566
+
567
+ return $use = false;
568
+ }
569
+
570
  /**
571
  * Detects presence of a page builder.
572
  * Memoizes the return value.
656
  * @since 3.1.0 Removed Jetpack's sitemap check -- it's no longer valid.
657
  * @since 4.0.0 : 1. Now uses has_robots_txt()
658
  * : 2. Now uses the get_robots_txt_url() to determine validity.
659
+ * FIXME This method also checks for file existence (and location...), but is only used when the file definitely doesn't exist.
660
  *
661
  * @param bool $check_option Whether to check for sitemap option.
662
  * @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
759
  *
760
  * @since 2.2.5
761
  * @since 3.1.0 Removed caching
762
+ * @TODO deprecate me.
763
  *
764
  * @param string|array required $features The features to check for.
765
  * @return bool theme support.
inc/classes/generate-image.class.php CHANGED
@@ -40,6 +40,9 @@ class Generate_Image extends Generate_Url {
40
  * Memoizes the return value.
41
  *
42
  * @since 4.0.0
 
 
 
43
  *
44
  * @return array The image details array, sequential: int => {
45
  * string url: The image URL,
@@ -49,9 +52,9 @@ class Generate_Image extends Generate_Url {
49
  * string alt: The image alt tag,
50
  * }
51
  */
52
- public function get_image_details_from_cache() {
53
- static $cache;
54
- return isset( $cache ) ? $cache : $cache = $this->get_image_details();
55
  }
56
 
57
  /**
@@ -436,7 +439,7 @@ class Generate_Image extends Generate_Url {
436
  * @param array $cbs The callbacks to parse. Ideally be generators, so we can halt early.
437
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
438
  * Leave null to autodetermine query.
439
- * @param sring $size The image size to use.
440
  * @param bool $single Whether to fetch one image, or multiple.
441
  * @return array The image details array, sequential: int => {
442
  * string url: The image URL,
40
  * Memoizes the return value.
41
  *
42
  * @since 4.0.0
43
+ * @since 4.1.2 Added a $single parameter, which helps reduce processing power required.
44
+ * This parameter might get deprecated when we start supporting PHP 7.1+ only.
45
+ * TODO yield from and memoize deeper? Iterators calling this method currently do not affect the generators.
46
  *
47
  * @return array The image details array, sequential: int => {
48
  * string url: The image URL,
52
  * string alt: The image alt tag,
53
  * }
54
  */
55
+ public function get_image_details_from_cache( $single = false ) {
56
+ static $cache = [];
57
+ return isset( $cache[ $single ] ) ? $cache[ $single ] : $cache[ $single ] = $this->get_image_details( null, $single );
58
  }
59
 
60
  /**
439
  * @param array $cbs The callbacks to parse. Ideally be generators, so we can halt early.
440
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
441
  * Leave null to autodetermine query.
442
+ * @param string $size The image size to use.
443
  * @param bool $single Whether to fetch one image, or multiple.
444
  * @return array The image details array, sequential: int => {
445
  * string url: The image URL,
inc/classes/generate-ldjson.class.php CHANGED
@@ -165,6 +165,7 @@ class Generate_Ldjson extends Generate_Image {
165
  *
166
  * @since 2.9.3
167
  * @since 3.0.0 This whole functions now only listens to the searchbox option.
 
168
  *
169
  * @return string escaped LD+JSON Search and Sitename script.
170
  */
@@ -191,6 +192,7 @@ class Generate_Ldjson extends Generate_Image {
191
  ];
192
 
193
  //= The searchbox part.
 
194
  $action_name = 'search_term_string';
195
  $search_link = $this->pretty_permalinks ? \trailingslashit( \get_search_link() ) : \get_search_link();
196
  /**
@@ -199,10 +201,16 @@ class Generate_Ldjson extends Generate_Image {
199
  */
200
  $search_url = (string) \apply_filters( 'the_seo_framework_ld_json_search_url', $search_link );
201
 
 
 
 
 
 
202
  $data += [
203
  'potentialAction' => [
204
  '@type' => 'SearchAction',
205
- 'target' => sprintf( '%s{%s}', \esc_url( $search_url ), $action_name ),
 
206
  'query-input' => sprintf( 'required name=%s', $action_name ),
207
  ],
208
  ];
@@ -326,7 +334,7 @@ class Generate_Ldjson extends Generate_Image {
326
  *
327
  * @since 4.0.0
328
  * @uses $this->get_image_details()
329
- * @ignore Not used internally, only externally.
330
  *
331
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
332
  * Leave null to autodetermine query.
165
  *
166
  * @since 2.9.3
167
  * @since 3.0.0 This whole functions now only listens to the searchbox option.
168
+ * @since 4.1.2 Now properly slashes the search URL.
169
  *
170
  * @return string escaped LD+JSON Search and Sitename script.
171
  */
192
  ];
193
 
194
  //= The searchbox part.
195
+ $pattern = '%s{%s}';
196
  $action_name = 'search_term_string';
197
  $search_link = $this->pretty_permalinks ? \trailingslashit( \get_search_link() ) : \get_search_link();
198
  /**
201
  */
202
  $search_url = (string) \apply_filters( 'the_seo_framework_ld_json_search_url', $search_link );
203
 
204
+ if ( ! empty( $GLOBALS['wp_rewrite']->get_search_permastruct() ) ) {
205
+ $pattern = \user_trailingslashit( '%s{%s}', 'search' );
206
+ $search_url = \trailingslashit( $search_url );
207
+ }
208
+
209
  $data += [
210
  'potentialAction' => [
211
  '@type' => 'SearchAction',
212
+ // not properly sanitized; however, search_term_string is inert.
213
+ 'target' => sprintf( $pattern, \esc_url( $search_url ), $action_name ),
214
  'query-input' => sprintf( 'required name=%s', $action_name ),
215
  ],
216
  ];
334
  *
335
  * @since 4.0.0
336
  * @uses $this->get_image_details()
337
+ * @api Not used internally, only externally.
338
  *
339
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
340
  * Leave null to autodetermine query.
inc/classes/generate-title.class.php CHANGED
@@ -737,6 +737,25 @@ class Generate_Title extends Generate_Description {
737
  return \get_bloginfo( 'name', 'raw' );
738
  }
739
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
  /**
741
  * Returns the archive title. Also works in admin.
742
  *
@@ -748,6 +767,7 @@ class Generate_Title extends Generate_Description {
748
  * @since 4.0.5 1: Now no longer uses `get_the_author()` to fetch the author's display name,
749
  * but uses the provided term object instead.
750
  * 2: The first parameter now accepts `\WP_User` objects.
 
751
  *
752
  * @param \WP_Term|\WP_User|\WP_Error|null $term The Term object or error. Leave null to autodetermine query.
753
  * @return string The generated archive title, not escaped.
@@ -778,53 +798,128 @@ class Generate_Title extends Generate_Description {
778
  $_tax = isset( $term->taxonomy ) ? $term->taxonomy : '';
779
  $use_prefix = $this->use_generated_archive_prefix( $term );
780
 
 
 
 
781
  if ( ! $_query ) {
782
  if ( $_tax ) {
783
  if ( 'category' === $_tax ) {
784
  $title = $this->get_generated_single_term_title( $term );
785
- /* translators: Category archive title. 1: Category name */
786
- $title = $use_prefix ? sprintf( \__( 'Category: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
787
  } elseif ( 'post_tag' === $_tax ) {
788
  $title = $this->get_generated_single_term_title( $term );
789
- /* translators: Tag archive title. 1: Tag name */
790
- $title = $use_prefix ? sprintf( \__( 'Tag: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
791
  } else {
792
  $title = $this->get_generated_single_term_title( $term );
793
  $title = $use_prefix ? $this->prepend_tax_label_prefix( $title, $_tax ) : $title;
794
  }
795
  } elseif ( $term instanceof \WP_User && isset( $term->display_name ) ) {
796
  $title = $term->display_name;
797
- /* translators: Author archive title. 1: Author name */
798
- $title = $use_prefix ? sprintf( \__( 'Author: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
799
  } else {
800
  $title = \__( 'Archives', 'default' );
801
  }
802
  } else {
803
  if ( $this->is_category() ) {
804
  $title = $this->get_generated_single_term_title( $term );
805
- /* translators: Category archive title. 1: Category name */
806
- $title = $use_prefix ? sprintf( \__( 'Category: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
807
  } elseif ( $this->is_tag() ) {
808
  $title = $this->get_generated_single_term_title( $term );
809
- /* translators: Tag archive title. 1: Tag name */
810
- $title = $use_prefix ? sprintf( \__( 'Tag: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
811
  } elseif ( $this->is_author() ) {
812
  $title = isset( $term->display_name ) ? $term->display_name : '';
813
- /* translators: Author archive title. 1: Author name */
814
- $title = $use_prefix ? sprintf( \__( 'Author: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
815
  } elseif ( $this->is_date() ) {
816
  if ( $this->is_year() ) {
817
  $title = \get_the_date( \_x( 'Y', 'yearly archives date format', 'default' ) );
818
- /* translators: Yearly archive title. 1: Year */
819
- $title = $use_prefix ? sprintf( \__( 'Year: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
820
  } elseif ( $this->is_month() ) {
821
  $title = \get_the_date( \_x( 'F Y', 'monthly archives date format', 'default' ) );
822
- /* translators: Monthly archive title. 1: Month name and year */
823
- $title = $use_prefix ? sprintf( \__( 'Month: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
824
  } elseif ( $this->is_day() ) {
825
  $title = \get_the_date( \_x( 'F j, Y', 'daily archives date format', 'default' ) );
826
- /* translators: Daily archive title. 1: Date */
827
- $title = $use_prefix ? sprintf( \__( 'Day: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
828
  }
829
  } elseif ( \is_tax( 'post_format' ) ) {
830
  if ( \is_tax( 'post_format', 'post-format-aside' ) ) {
@@ -848,8 +943,16 @@ class Generate_Title extends Generate_Description {
848
  }
849
  } elseif ( \is_post_type_archive() ) {
850
  $title = $this->get_generated_post_type_archive_title() ?: $this->get_tax_type_label( $_tax, false );
851
- /* translators: Post type archive title. 1: Post type name */
852
- $title = $use_prefix ? sprintf( \__( 'Archives: %s', 'default' ), $title ) : $title;
 
 
 
 
 
 
 
 
853
  } elseif ( $this->is_tax() ) {
854
  $title = $this->get_generated_single_term_title( $term );
855
  $title = $use_prefix ? $this->prepend_tax_label_prefix( $title, $_tax ) : $title;
@@ -1257,6 +1360,7 @@ class Generate_Title extends Generate_Description {
1257
  * Prepends the taxonomy label to the title.
1258
  *
1259
  * @since 4.1.0
 
1260
  *
1261
  * @param string $title The title to prepend taxonomy label to.
1262
  * @param string $taxonomy The taxonomy to get label from.
@@ -1267,8 +1371,22 @@ class Generate_Title extends Generate_Description {
1267
  $prefix = $this->get_tax_type_label( $taxonomy ) ?: '';
1268
 
1269
  if ( $prefix ) {
1270
- /* translators: Taxonomy term archive title. 1: Taxonomy singular name, 2: Current taxonomy term */
1271
- $title = sprintf( \_x( '%1$s: %2$s', 'tax prefix: title', 'autodescription' ), $prefix, $title );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1272
  }
1273
 
1274
  return $title;
737
  return \get_bloginfo( 'name', 'raw' );
738
  }
739
 
740
+ /**
741
+ * Combobulates archive title prefixes for WP 5.5+.
742
+ *
743
+ * @since 4.1.2
744
+ * @TEMP
745
+ *
746
+ * @param string $prefix The archive prefix.
747
+ * @param string $title The archive title.
748
+ * @return string The archive title.
749
+ */
750
+ protected function _combobulate_wp550_archive_title( $prefix, $title ) {
751
+ return sprintf(
752
+ /* translators: 1: Title prefix. 2: Title. */
753
+ \_x( '%1$s %2$s', 'archive title', 'default' ),
754
+ $prefix,
755
+ $title
756
+ );
757
+ }
758
+
759
  /**
760
  * Returns the archive title. Also works in admin.
761
  *
767
  * @since 4.0.5 1: Now no longer uses `get_the_author()` to fetch the author's display name,
768
  * but uses the provided term object instead.
769
  * 2: The first parameter now accepts `\WP_User` objects.
770
+ * @since 4.1.2 Now supports WP 5.5 archive titles.
771
  *
772
  * @param \WP_Term|\WP_User|\WP_Error|null $term The Term object or error. Leave null to autodetermine query.
773
  * @return string The generated archive title, not escaped.
798
  $_tax = isset( $term->taxonomy ) ? $term->taxonomy : '';
799
  $use_prefix = $this->use_generated_archive_prefix( $term );
800
 
801
+ // TEMP hack. Let's clean this up later.
802
+ $use_new_string_prefixes = $use_prefix && version_compare( \get_bloginfo( 'version' ), '5.5', '>=' );
803
+
804
  if ( ! $_query ) {
805
  if ( $_tax ) {
806
  if ( 'category' === $_tax ) {
807
  $title = $this->get_generated_single_term_title( $term );
808
+
809
+ if ( $use_new_string_prefixes ) {
810
+ $title = $this->_combobulate_wp550_archive_title(
811
+ \_x( 'Category:', 'category archive title prefix', 'default' ),
812
+ $title
813
+ );
814
+ } else {
815
+ /* translators: Category archive title. 1: Category name */
816
+ $title = $use_prefix ? sprintf( \__( 'Category: %s', 'default' ), $title ) : $title;
817
+ }
818
  } elseif ( 'post_tag' === $_tax ) {
819
  $title = $this->get_generated_single_term_title( $term );
820
+
821
+ if ( $use_new_string_prefixes ) {
822
+ $title = $this->_combobulate_wp550_archive_title(
823
+ \_x( 'Tag:', 'tag archive title prefix', 'default' ),
824
+ $title
825
+ );
826
+ } else {
827
+ /* translators: Tag archive title. 1: Tag name */
828
+ $title = $use_prefix ? sprintf( \__( 'Tag: %s', 'default' ), $title ) : $title;
829
+ }
830
  } else {
831
  $title = $this->get_generated_single_term_title( $term );
832
  $title = $use_prefix ? $this->prepend_tax_label_prefix( $title, $_tax ) : $title;
833
  }
834
  } elseif ( $term instanceof \WP_User && isset( $term->display_name ) ) {
835
  $title = $term->display_name;
836
+
837
+ if ( $use_new_string_prefixes ) {
838
+ $title = $this->_combobulate_wp550_archive_title(
839
+ \_x( 'Author:', 'author archive title prefix', 'default' ),
840
+ $title
841
+ );
842
+ } else {
843
+ /* translators: Author archive title. 1: Author name */
844
+ $title = $use_prefix ? sprintf( \__( 'Author: %s', 'default' ), $title ) : $title;
845
+ }
846
  } else {
847
  $title = \__( 'Archives', 'default' );
848
  }
849
  } else {
850
  if ( $this->is_category() ) {
851
  $title = $this->get_generated_single_term_title( $term );
852
+
853
+ if ( $use_new_string_prefixes ) {
854
+ $title = $this->_combobulate_wp550_archive_title(
855
+ \_x( 'Category:', 'category archive title prefix', 'default' ),
856
+ $title
857
+ );
858
+ } else {
859
+ /* translators: Category archive title. 1: Category name */
860
+ $title = $use_prefix ? sprintf( \__( 'Category: %s', 'default' ), $title ) : $title;
861
+ }
862
  } elseif ( $this->is_tag() ) {
863
  $title = $this->get_generated_single_term_title( $term );
864
+
865
+ if ( $use_new_string_prefixes ) {
866
+ $title = $this->_combobulate_wp550_archive_title(
867
+ \_x( 'Tag:', 'tag archive title prefix', 'default' ),
868
+ $title
869
+ );
870
+ } else {
871
+ /* translators: Tag archive title. 1: Tag name */
872
+ $title = $use_prefix ? sprintf( \__( 'Tag: %s', 'default' ), $title ) : $title;
873
+ }
874
  } elseif ( $this->is_author() ) {
875
  $title = isset( $term->display_name ) ? $term->display_name : '';
876
+
877
+ if ( $use_new_string_prefixes ) {
878
+ $title = $this->_combobulate_wp550_archive_title(
879
+ \_x( 'Author:', 'author archive title prefix', 'default' ),
880
+ $title
881
+ );
882
+ } else {
883
+ /* translators: Author archive title. 1: Author name */
884
+ $title = $use_prefix ? sprintf( \__( 'Author: %s', 'default' ), $title ) : $title;
885
+ }
886
  } elseif ( $this->is_date() ) {
887
  if ( $this->is_year() ) {
888
  $title = \get_the_date( \_x( 'Y', 'yearly archives date format', 'default' ) );
889
+
890
+ if ( $use_new_string_prefixes ) {
891
+ $title = $this->_combobulate_wp550_archive_title(
892
+ \_x( 'Year:', 'date archive title prefix', 'default' ),
893
+ $title
894
+ );
895
+ } else {
896
+ /* translators: Yearly archive title. 1: Year */
897
+ $title = $use_prefix ? sprintf( \__( 'Year: %s', 'default' ), $title ) : $title;
898
+ }
899
  } elseif ( $this->is_month() ) {
900
  $title = \get_the_date( \_x( 'F Y', 'monthly archives date format', 'default' ) );
901
+
902
+ if ( $use_new_string_prefixes ) {
903
+ $title = $this->_combobulate_wp550_archive_title(
904
+ \_x( 'Month:', 'date archive title prefix', 'default' ),
905
+ $title
906
+ );
907
+ } else {
908
+ /* translators: Monthly archive title. 1: Month name and year */
909
+ $title = $use_prefix ? sprintf( \__( 'Month: %s', 'default' ), $title ) : $title;
910
+ }
911
  } elseif ( $this->is_day() ) {
912
  $title = \get_the_date( \_x( 'F j, Y', 'daily archives date format', 'default' ) );
913
+
914
+ if ( $use_new_string_prefixes ) {
915
+ $title = $this->_combobulate_wp550_archive_title(
916
+ \_x( 'Day:', 'date archive title prefix', 'default' ),
917
+ $title
918
+ );
919
+ } else {
920
+ /* translators: Daily archive title. 1: Date */
921
+ $title = $use_prefix ? sprintf( \__( 'Day: %s', 'default' ), $title ) : $title;
922
+ }
923
  }
924
  } elseif ( \is_tax( 'post_format' ) ) {
925
  if ( \is_tax( 'post_format', 'post-format-aside' ) ) {
943
  }
944
  } elseif ( \is_post_type_archive() ) {
945
  $title = $this->get_generated_post_type_archive_title() ?: $this->get_tax_type_label( $_tax, false );
946
+
947
+ if ( $use_new_string_prefixes ) {
948
+ $title = $this->_combobulate_wp550_archive_title(
949
+ \_x( 'Archives:', 'post type archive title prefix', 'default' ),
950
+ $title
951
+ );
952
+ } else {
953
+ /* translators: Post type archive title. 1: Post type name */
954
+ $title = $use_prefix ? sprintf( \__( 'Archives: %s', 'default' ), $title ) : $title;
955
+ }
956
  } elseif ( $this->is_tax() ) {
957
  $title = $this->get_generated_single_term_title( $term );
958
  $title = $use_prefix ? $this->prepend_tax_label_prefix( $title, $_tax ) : $title;
1360
  * Prepends the taxonomy label to the title.
1361
  *
1362
  * @since 4.1.0
1363
+ * @since 4.1.2 Now supports WP 5.5 archive titles.
1364
  *
1365
  * @param string $title The title to prepend taxonomy label to.
1366
  * @param string $taxonomy The taxonomy to get label from.
1371
  $prefix = $this->get_tax_type_label( $taxonomy ) ?: '';
1372
 
1373
  if ( $prefix ) {
1374
+ $use_new_string_prefixes = version_compare( \get_bloginfo( 'version' ), '5.5', '>=' );
1375
+
1376
+ if ( $use_new_string_prefixes ) {
1377
+ $title = $this->_combobulate_wp550_archive_title(
1378
+ /* translators: %s: Taxonomy singular name. */
1379
+ sprintf( \_x( '%s:', 'taxonomy term archive title prefix', 'default' ), $prefix ),
1380
+ $title
1381
+ );
1382
+ } else {
1383
+ $title = sprintf(
1384
+ /* translators: Taxonomy term archive title. 1: Taxonomy singular name, 2: Current taxonomy term. */
1385
+ \__( '%1$s: %2$s', 'default' ),
1386
+ $prefix,
1387
+ $title
1388
+ );
1389
+ }
1390
  }
1391
 
1392
  return $title;
inc/classes/generate-url.class.php CHANGED
@@ -727,6 +727,7 @@ class Generate_Url extends Generate_Title {
727
  * 3. The third parameter is now changed to $use_base, from the archive pagination number.
728
  * 4. Now supports pretty permalinks with query parameters.
729
  * 5. Is now public.
 
730
  *
731
  * @param string $url The fully qualified URL to remove pagination from.
732
  * @param int|null $page The page number to remove. If null, it will get number from query.
@@ -748,6 +749,8 @@ class Generate_Url extends Generate_Title {
748
  $_page = isset( $page ) ? $page : max( $this->paged(), $this->page() );
749
 
750
  if ( $_page > 1 ) {
 
 
751
  $_use_base = isset( $use_base ) ? $use_base
752
  : $this->is_archive() || $this->is_real_front_page() || $this->is_singular_archive();
753
 
@@ -757,22 +760,25 @@ class Generate_Url extends Generate_Title {
757
  $find = '/' . $_page . $user_slash;
758
  }
759
 
760
- $_query = parse_url( $url, PHP_URL_QUERY );
761
  // Remove queries, add them back later.
762
  if ( $_query )
763
- $url = $this->s_url( $url );
764
 
765
- $pos = strrpos( $url, $find );
766
  // Defensive programming, only remove if $find matches the stack length, without query arguments.
767
- $continue = $pos && $pos + \strlen( $find ) === \strlen( $url );
768
 
769
  if ( $continue ) {
770
- $url = substr( $url, 0, $pos );
771
- $url = \user_trailingslashit( $url );
772
 
 
773
  if ( $_query )
774
- $url = $this->append_php_query( $url, $_query );
775
  }
 
 
776
  }
777
  } else {
778
  $url = \remove_query_arg( [ 'page', 'paged', 'cpage' ], $url );
@@ -886,6 +892,7 @@ class Generate_Url extends Generate_Title {
886
  * 3. Removed second parameter. It was only a source of bugs.
887
  * 4. Removed WordPress Core `get_pagenum_link` filter.
888
  * @uses $this->get_paged_urls();
 
889
  *
890
  * @param string $next_prev Whether to get the previous or next page link.
891
  * Accepts 'prev' and 'next'.
@@ -902,6 +909,8 @@ class Generate_Url extends Generate_Title {
902
  * @since 3.2.4 1. Now correctly removes the pagination base from singular URLs.
903
  * 2. Now returns no URLs when a custom canonical URL is set.
904
  * @since 4.1.0 Removed memoization.
 
 
905
  *
906
  * @return array Escaped site Pagination URLs: {
907
  * string 'prev'
@@ -910,8 +919,11 @@ class Generate_Url extends Generate_Title {
910
  */
911
  public function get_paged_urls() {
912
 
 
 
 
 
913
  $prev = $next = '';
914
- $_run = false;
915
 
916
  if ( $this->has_custom_canonical_url() ) goto end;
917
 
@@ -939,6 +951,9 @@ class Generate_Url extends Generate_Title {
939
  }
940
  // phpcs:enable, WordPress.WhiteSpace.PrecisionAlignment
941
 
 
 
 
942
  $canonical = $this->remove_pagination_from_url( $this->get_current_canonical_url() );
943
 
944
  // If this page is not the last, create a next-URL.
727
  * 3. The third parameter is now changed to $use_base, from the archive pagination number.
728
  * 4. Now supports pretty permalinks with query parameters.
729
  * 5. Is now public.
730
+ * @since 4.1.2 Now correctly reappends query when pagination isn't removed.
731
  *
732
  * @param string $url The fully qualified URL to remove pagination from.
733
  * @param int|null $page The page number to remove. If null, it will get number from query.
749
  $_page = isset( $page ) ? $page : max( $this->paged(), $this->page() );
750
 
751
  if ( $_page > 1 ) {
752
+ $_url = $url;
753
+
754
  $_use_base = isset( $use_base ) ? $use_base
755
  : $this->is_archive() || $this->is_real_front_page() || $this->is_singular_archive();
756
 
760
  $find = '/' . $_page . $user_slash;
761
  }
762
 
763
+ $_query = parse_url( $_url, PHP_URL_QUERY );
764
  // Remove queries, add them back later.
765
  if ( $_query )
766
+ $_url = $this->s_url( $_url );
767
 
768
+ $pos = strrpos( $_url, $find );
769
  // Defensive programming, only remove if $find matches the stack length, without query arguments.
770
+ $continue = $pos && $pos + \strlen( $find ) === \strlen( $_url );
771
 
772
  if ( $continue ) {
773
+ $_url = substr( $_url, 0, $pos );
774
+ $_url = \user_trailingslashit( $_url );
775
 
776
+ // Add back the query.
777
  if ( $_query )
778
+ $_url = $this->append_php_query( $_url, $_query );
779
  }
780
+
781
+ $url = $_url;
782
  }
783
  } else {
784
  $url = \remove_query_arg( [ 'page', 'paged', 'cpage' ], $url );
892
  * 3. Removed second parameter. It was only a source of bugs.
893
  * 4. Removed WordPress Core `get_pagenum_link` filter.
894
  * @uses $this->get_paged_urls();
895
+ * @api Not used internally.
896
  *
897
  * @param string $next_prev Whether to get the previous or next page link.
898
  * Accepts 'prev' and 'next'.
909
  * @since 3.2.4 1. Now correctly removes the pagination base from singular URLs.
910
  * 2. Now returns no URLs when a custom canonical URL is set.
911
  * @since 4.1.0 Removed memoization.
912
+ * @since 4.1.2 1. Added back memoization.
913
+ * 2. Reduced needless canonical URL generation when it wouldn't be processed anyway.
914
  *
915
  * @return array Escaped site Pagination URLs: {
916
  * string 'prev'
919
  */
920
  public function get_paged_urls() {
921
 
922
+ static $prev, $next;
923
+
924
+ if ( isset( $prev, $next ) ) goto end;
925
+
926
  $prev = $next = '';
 
927
 
928
  if ( $this->has_custom_canonical_url() ) goto end;
929
 
951
  }
952
  // phpcs:enable, WordPress.WhiteSpace.PrecisionAlignment
953
 
954
+ // See if-statements below.
955
+ if ( ! ( $page + 1 <= $_numpages || $page > 1 ) ) goto end;
956
+
957
  $canonical = $this->remove_pagination_from_url( $this->get_current_canonical_url() );
958
 
959
  // If this page is not the last, create a next-URL.
inc/classes/generate.class.php CHANGED
@@ -238,6 +238,8 @@ class Generate extends User_Data {
238
  * because page builders and templates can and will take over.
239
  *
240
  * Don't use empty(), null is regarded as indexable.
 
 
241
  */
242
  if ( isset( $wp_query->post_count ) && ! $wp_query->post_count )
243
  $noindex = true;
@@ -274,15 +276,13 @@ class Generate extends User_Data {
274
  // Store values from each post type bound to the taxonomy.
275
  foreach ( $this->get_post_types_from_taxonomy() as $post_type ) {
276
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
277
- // SECURITY: Put in array to circumvent GLOBALS injection.
278
- // FIXME: Shouldn't we check for $$r before? Saves processing power.
279
- $_post_type_meta[ $r ][] = $this->is_post_type_robots_set( $r, $post_type );
280
  }
281
  }
282
  // Only enable if all post types have the value ticked.
283
- foreach ( $_post_type_meta as $_type => $_values ) {
284
- // FIXME: Isn't $$_type just $$r?
285
- $$_type = $$_type || ! \in_array( false, $_values, true );
286
  }
287
 
288
  $taxonomy = $this->get_current_taxonomy();
@@ -322,9 +322,7 @@ class Generate extends User_Data {
322
 
323
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
324
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
325
- if ( $this->is_protected( $this->get_the_real_ID() ) ) {
326
- $noindex = true;
327
- }
328
  endif;
329
 
330
  /**
@@ -412,12 +410,12 @@ class Generate extends User_Data {
412
  foreach ( $this->get_post_types_from_taxonomy( $args['taxonomy'] ) as $post_type ) {
413
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
414
  // SECURITY: Put in array to circumvent GLOBALS injection.
415
- $_post_type_meta[ $r ][] = $this->is_post_type_robots_set( $r, $post_type );
416
  }
417
  }
418
  // Only enable if all post types have the value ticked.
419
- foreach ( $_post_type_meta as $_type => $_values ) {
420
- $$_type = $$_type || ! \in_array( false, $_values, true );
421
  }
422
 
423
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
@@ -455,9 +453,7 @@ class Generate extends User_Data {
455
 
456
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
457
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
458
- if ( $this->is_protected( $args['id'] ) ) {
459
- $noindex = true;
460
- }
461
  endif;
462
  }
463
 
@@ -508,11 +504,11 @@ class Generate extends User_Data {
508
  // Store values from each post type bound to the taxonomy.
509
  foreach ( $this->get_post_types_from_taxonomy( $args['taxonomy'] ) as $post_type ) {
510
  // SECURITY: Put in array to circumvent GLOBALS injection.
511
- $_post_type_meta['noindex'][] = $this->is_post_type_robots_set( 'noindex', $post_type );
512
  }
513
  // Only enable if all post types have the value ticked.
514
- foreach ( $_post_type_meta as $_type => $_values ) {
515
- $$_type = $$_type || ! \in_array( false, $_values, true );
516
  }
517
 
518
  $noindex = $noindex || $this->is_taxonomy_robots_set( 'noindex', $args['taxonomy'] );
@@ -539,9 +535,7 @@ class Generate extends User_Data {
539
 
540
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
541
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
542
- if ( $this->is_protected( $args['id'] ) ) {
543
- $noindex = true;
544
- }
545
  endif;
546
  }
547
 
238
  * because page builders and templates can and will take over.
239
  *
240
  * Don't use empty(), null is regarded as indexable.
241
+ *
242
+ * TODO Consider toggling this for author archives?
243
  */
244
  if ( isset( $wp_query->post_count ) && ! $wp_query->post_count )
245
  $noindex = true;
276
  // Store values from each post type bound to the taxonomy.
277
  foreach ( $this->get_post_types_from_taxonomy() as $post_type ) {
278
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
279
+ // SECURITY: Put in array to circumvent GLOBALS injection in sequenting loop.
280
+ $_post_type_meta[ $r ][] = $$r || $this->is_post_type_robots_set( $r, $post_type );
 
281
  }
282
  }
283
  // Only enable if all post types have the value ticked.
284
+ foreach ( $_post_type_meta as $r => $_values ) {
285
+ $$r = $$r || ! \in_array( false, $_values, true );
 
286
  }
287
 
288
  $taxonomy = $this->get_current_taxonomy();
322
 
323
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
324
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
325
+ $noindex = $noindex || $this->is_protected( $this->get_the_real_ID() );
 
 
326
  endif;
327
 
328
  /**
410
  foreach ( $this->get_post_types_from_taxonomy( $args['taxonomy'] ) as $post_type ) {
411
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
412
  // SECURITY: Put in array to circumvent GLOBALS injection.
413
+ $_post_type_meta[ $r ][] = $$r || $this->is_post_type_robots_set( $r, $post_type );
414
  }
415
  }
416
  // Only enable if all post types have the value ticked.
417
+ foreach ( $_post_type_meta as $r => $_values ) {
418
+ $$r = $$r || ! \in_array( false, $_values, true );
419
  }
420
 
421
  foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
453
 
454
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
455
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
456
+ $noindex = $noindex || $this->is_protected( $args['id'] );
 
 
457
  endif;
458
  }
459
 
504
  // Store values from each post type bound to the taxonomy.
505
  foreach ( $this->get_post_types_from_taxonomy( $args['taxonomy'] ) as $post_type ) {
506
  // SECURITY: Put in array to circumvent GLOBALS injection.
507
+ $_post_type_meta['noindex'][] = $noindex || $this->is_post_type_robots_set( 'noindex', $post_type );
508
  }
509
  // Only enable if all post types have the value ticked.
510
+ foreach ( $_post_type_meta as $r => $_values ) {
511
+ $$r = $$r || ! \in_array( false, $_values, true );
512
  }
513
 
514
  $noindex = $noindex || $this->is_taxonomy_robots_set( 'noindex', $args['taxonomy'] );
535
 
536
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
537
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
538
+ $noindex = $noindex || $this->is_protected( $args['id'] );
 
 
539
  endif;
540
  }
541
 
inc/classes/init.class.php CHANGED
@@ -101,14 +101,22 @@ class Init extends Query {
101
  * Initializes cron actions.
102
  *
103
  * @since 2.8.0
 
 
104
  */
105
  public function init_cron_actions() {
106
- // Init post update/delete caching actions.
107
  $this->init_post_cache_actions();
108
 
109
  // Ping searchengines.
110
- if ( $this->get_option( 'ping_use_cron' ) )
 
 
 
 
111
  \add_action( 'tsf_sitemap_cron_hook', Bridges\Ping::class . '::ping_search_engines' );
 
 
112
  }
113
 
114
  /**
@@ -230,6 +238,9 @@ class Init extends Query {
230
  // We can use action `set_404` when we support WP 5.5+...?
231
  \add_action( 'template_redirect', [ $this, '_init_sitemap' ], 1 );
232
  \add_filter( 'wp_sitemaps_enabled', '__return_false' );
 
 
 
233
  }
234
 
235
  // Initialize 301 redirects.
@@ -531,7 +542,14 @@ class Init extends Query {
531
  $url = $this->get_term_meta_item( 'redirect' ) ?: '';
532
  }
533
 
534
- $url and $this->do_redirect( $url );
 
 
 
 
 
 
 
535
  }
536
 
537
  /**
@@ -593,6 +611,20 @@ class Init extends Query {
593
  Bridges\Sitemap::get_instance()->_init();
594
  }
595
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  /**
597
  * Prepares feed modifications.
598
  *
@@ -620,6 +652,7 @@ class Init extends Query {
620
  * 3. No longer shortcircuits on non-public sites.
621
  * 4. Now marked as private. Will be renamed to `_robots_txt()` in the future.
622
  * @since 4.1.0 Now adds the WordPress Core sitemap URL.
 
623
  * @uses robots_txt filter located at WP core
624
  * @access private
625
  * @TODO extrapolate the contents without a warning to get_robots_txt(). Forward filter to it.
@@ -679,10 +712,10 @@ class Init extends Query {
679
  }
680
  }
681
  $output .= "\r\n";
682
- } elseif ( $this->get_option( 'sitemaps_robots' ) && ! $this->detect_sitemap_plugin() ) {
683
- if ( \function_exists( '\\wp_sitemaps_get_server' ) ) {
684
  $wp_sitemaps_server = \wp_sitemaps_get_server();
685
- if ( $wp_sitemaps_server && method_exists( $wp_sitemaps_server, 'add_robots' ) ) {
686
  // This method augments the output--it doesn't overwrite it.
687
  $output = \wp_sitemaps_get_server()->add_robots( $output, $public );
688
  }
101
  * Initializes cron actions.
102
  *
103
  * @since 2.8.0
104
+ * @since 4.1.2 1. Added hook for sitemap prerender.
105
+ * 2. Added hook for ping retry.
106
  */
107
  public function init_cron_actions() {
108
+ // Init post update/delete caching actions which may occur during cronjobs.
109
  $this->init_post_cache_actions();
110
 
111
  // Ping searchengines.
112
+ if ( $this->get_option( 'ping_use_cron' ) ) {
113
+ if ( $this->get_option( 'sitemaps_output' ) && $this->get_option( 'ping_use_cron_prerender' ) ) {
114
+ \add_action( 'tsf_sitemap_cron_hook_before', [ new Builders\Sitemap_Base, 'prerender_sitemap' ] );
115
+ }
116
+
117
  \add_action( 'tsf_sitemap_cron_hook', Bridges\Ping::class . '::ping_search_engines' );
118
+ \add_action( 'tsf_sitemap_cron_hook_retry', Bridges\Ping::class . '::retry_ping_search_engines' );
119
+ }
120
  }
121
 
122
  /**
238
  // We can use action `set_404` when we support WP 5.5+...?
239
  \add_action( 'template_redirect', [ $this, '_init_sitemap' ], 1 );
240
  \add_filter( 'wp_sitemaps_enabled', '__return_false' );
241
+ } else {
242
+ // Augment Core sitemaps. Can't hook into `wp_sitemaps_init` as we're augmenting the providers before that.
243
+ $this->_init_core_sitemap();
244
  }
245
 
246
  // Initialize 301 redirects.
542
  $url = $this->get_term_meta_item( 'redirect' ) ?: '';
543
  }
544
 
545
+ if ( $url ) {
546
+ /**
547
+ * @since 4.1.2
548
+ * @param string $url The URL we're redirecting to.
549
+ */
550
+ \do_action( 'the_seo_framework_before_redirect', $url );
551
+ $this->do_redirect( $url );
552
+ }
553
  }
554
 
555
  /**
611
  Bridges\Sitemap::get_instance()->_init();
612
  }
613
 
614
+ /**
615
+ * Prepares core sitemap output.
616
+ *
617
+ * @since 4.1.2
618
+ * @access private
619
+ */
620
+ public function _init_core_sitemap() {
621
+ // It's not a bridge, don't treat it like one: Submit hooks here. Clean me up?
622
+ $builder_class = Builders\CoreSitemaps\Main::class;
623
+
624
+ \add_filter( 'wp_sitemaps_add_provider', "{$builder_class}::_filter_add_provider", 9, 2 );
625
+ \add_filter( 'wp_sitemaps_max_urls', "{$builder_class}::_filter_max_urls", 9 );
626
+ }
627
+
628
  /**
629
  * Prepares feed modifications.
630
  *
652
  * 3. No longer shortcircuits on non-public sites.
653
  * 4. Now marked as private. Will be renamed to `_robots_txt()` in the future.
654
  * @since 4.1.0 Now adds the WordPress Core sitemap URL.
655
+ * @since 4.1.2 Now only adds the WP Core sitemap URL when the provider tells us it's enabled.
656
  * @uses robots_txt filter located at WP core
657
  * @access private
658
  * @TODO extrapolate the contents without a warning to get_robots_txt(). Forward filter to it.
712
  }
713
  }
714
  $output .= "\r\n";
715
+ } elseif ( $this->get_option( 'sitemaps_robots' ) && ! $this->detect_sitemap_plugin() ) { // detect_sitemap_plugin() temp backward compat.
716
+ if ( $this->use_core_sitemaps() ) {
717
  $wp_sitemaps_server = \wp_sitemaps_get_server();
718
+ if ( method_exists( $wp_sitemaps_server, 'add_robots' ) ) {
719
  // This method augments the output--it doesn't overwrite it.
720
  $output = \wp_sitemaps_get_server()->add_robots( $output, $public );
721
  }
inc/classes/interpreters/seobar.class.php CHANGED
@@ -35,7 +35,6 @@ namespace The_SEO_Framework\Interpreters;
35
  * Note that you can't instance this class. Only static methods and properties are accessible.
36
  */
37
  final class SeoBar {
38
- use \The_SEO_Framework\Traits\Enclose_Stray_Private;
39
 
40
  const STATE_UNDEFINED = 0b0000;
41
  const STATE_UNKNOWN = 0b0001;
35
  * Note that you can't instance this class. Only static methods and properties are accessible.
36
  */
37
  final class SeoBar {
 
38
 
39
  const STATE_UNDEFINED = 0b0000;
40
  const STATE_UNKNOWN = 0b0001;
inc/classes/post-data.class.php CHANGED
@@ -462,12 +462,11 @@ class Post_Data extends Detect {
462
 
463
  // This is sent via GET. Keep using $_REQUEST for future-compatibility.
464
  foreach ( (array) $_REQUEST['autodescription-bulk'] as $key => $value ) :
465
- if ( 'nochange' === $value ) continue;
466
-
467
  switch ( $key ) :
468
  case 'noindex':
469
  case 'nofollow':
470
  case 'noarchive':
 
471
  $new_data[ "_genesis_$key" ] = $value;
472
  break;
473
 
@@ -548,7 +547,7 @@ class Post_Data extends Detect {
548
  }
549
 
550
  /**
551
- * Fetch latest public post ID.
552
  * Memoizes the return value.
553
  *
554
  * @since 2.4.3
462
 
463
  // This is sent via GET. Keep using $_REQUEST for future-compatibility.
464
  foreach ( (array) $_REQUEST['autodescription-bulk'] as $key => $value ) :
 
 
465
  switch ( $key ) :
466
  case 'noindex':
467
  case 'nofollow':
468
  case 'noarchive':
469
+ if ( 'nochange' === $value ) continue 2;
470
  $new_data[ "_genesis_$key" ] = $value;
471
  break;
472
 
547
  }
548
 
549
  /**
550
+ * Fetch latest public post/page ID.
551
  * Memoizes the return value.
552
  *
553
  * @since 2.4.3
inc/classes/query.class.php CHANGED
@@ -90,7 +90,7 @@ class Query extends Core {
90
 
91
  $message = "You've initiated a method that uses queries too early.";
92
 
93
- $trace = @debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, 4 );
94
  if ( ! empty( $trace[3] ) ) {
95
  $message .= ' - In file: ' . $trace[3]['file'];
96
  $message .= ' - On line: ' . $trace[3]['line'];
@@ -102,7 +102,7 @@ class Query extends Core {
102
  $depth = 10;
103
  static $_more = true;
104
  if ( $_more ) {
105
- error_log( var_export( @debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $depth ), true ) );
106
  $_more = false;
107
  }
108
  }
@@ -814,7 +814,7 @@ class Query extends Core {
814
  *
815
  * @since 2.6.0
816
  * @since 4.0.0 Now tests for post type, which is more reliable.
817
- * @ignore not used internally, polar opposite of is_single().
818
  * @uses $this->is_singular()
819
  *
820
  * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
90
 
91
  $message = "You've initiated a method that uses queries too early.";
92
 
93
+ $trace = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, 4 );
94
  if ( ! empty( $trace[3] ) ) {
95
  $message .= ' - In file: ' . $trace[3]['file'];
96
  $message .= ' - On line: ' . $trace[3]['line'];
102
  $depth = 10;
103
  static $_more = true;
104
  if ( $_more ) {
105
+ error_log( var_export( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $depth ), true ) );
106
  $_more = false;
107
  }
108
  }
814
  *
815
  * @since 2.6.0
816
  * @since 4.0.0 Now tests for post type, which is more reliable.
817
+ * @api not used internally, polar opposite of is_single().
818
  * @uses $this->is_singular()
819
  *
820
  * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
inc/classes/render.class.php CHANGED
@@ -104,6 +104,9 @@ class Render extends Admin_Init {
104
  * @since 2.2.2
105
  * @since 2.7.0 $get_id parameter has been added.
106
  * @since 4.0.0 Now uses the new image generator.
 
 
 
107
  *
108
  * @return string The image URL.
109
  */
@@ -111,7 +114,7 @@ class Render extends Admin_Init {
111
 
112
  $url = '';
113
 
114
- foreach ( $this->get_image_details_from_cache() as $image ) {
115
  $url = $image['url'];
116
  if ( $url ) break;
117
  }
@@ -291,8 +294,10 @@ class Render extends Admin_Init {
291
  * Renders Open Graph image meta tag.
292
  *
293
  * @since 1.3.0
294
- * @since 2.6.0 : Added WooCommerce gallery images.
295
- * @since 2.7.0 : Added image dimensions if found.
 
 
296
  *
297
  * @return string The Open Graph image meta tag.
298
  */
@@ -304,7 +309,7 @@ class Render extends Admin_Init {
304
 
305
  $multi = (bool) $this->get_option( 'multi_og_image' );
306
 
307
- foreach ( $this->get_image_details_from_cache() as $image ) {
308
  $output .= '<meta property="og:image" content="' . \esc_attr( $image['url'] ) . '" />' . "\r\n";
309
 
310
  if ( $image['height'] && $image['width'] ) {
@@ -542,6 +547,9 @@ class Render extends Admin_Init {
542
  * Renders Twitter Image meta tag.
543
  *
544
  * @since 2.2.2
 
 
 
545
  *
546
  * @return string The Twitter Image meta tag.
547
  */
@@ -551,7 +559,7 @@ class Render extends Admin_Init {
551
 
552
  $output = '';
553
 
554
- foreach ( $this->get_image_details_from_cache() as $image ) {
555
  $output .= '<meta name="twitter:image" content="' . \esc_attr( $image['url'] ) . '" />' . "\r\n";
556
 
557
  if ( $image['height'] && $image['width'] ) {
@@ -1072,7 +1080,7 @@ class Render extends Admin_Init {
1072
  * Renders Prev/Next Paged URL meta tags.
1073
  *
1074
  * @since 2.2.2
1075
- * @uses $this->get_paged_url()
1076
  *
1077
  * @return string The Prev/Next Paged URL meta tags.
1078
  */
@@ -1080,6 +1088,8 @@ class Render extends Admin_Init {
1080
 
1081
  $id = $this->get_the_real_ID();
1082
 
 
 
1083
  /**
1084
  * @since 2.6.0
1085
  * @param string $next The next-page URL.
@@ -1088,7 +1098,7 @@ class Render extends Admin_Init {
1088
  $next = (string) \apply_filters_ref_array(
1089
  'the_seo_framework_paged_url_output_next',
1090
  [
1091
- $this->get_paged_url( 'next' ),
1092
  $id,
1093
  ]
1094
  );
@@ -1101,7 +1111,7 @@ class Render extends Admin_Init {
1101
  $prev = (string) \apply_filters_ref_array(
1102
  'the_seo_framework_paged_url_output_prev',
1103
  [
1104
- $this->get_paged_url( 'prev' ),
1105
  $id,
1106
  ]
1107
  );
104
  * @since 2.2.2
105
  * @since 2.7.0 $get_id parameter has been added.
106
  * @since 4.0.0 Now uses the new image generator.
107
+ * @since 4.1.2 Now forwards the `multi_og_image` option to the generator. Although
108
+ * it'll always use just one image, we read this option so we'll only
109
+ * use a single cache instance internally with the generator.
110
  *
111
  * @return string The image URL.
112
  */
114
 
115
  $url = '';
116
 
117
+ foreach ( $this->get_image_details_from_cache( ! $this->get_option( 'multi_og_image' ) ) as $image ) {
118
  $url = $image['url'];
119
  if ( $url ) break;
120
  }
294
  * Renders Open Graph image meta tag.
295
  *
296
  * @since 1.3.0
297
+ * @since 2.6.0 Added WooCommerce gallery images.
298
+ * @since 2.7.0 Added image dimensions if found.
299
+ * @since 4.1.2 Now forwards the `multi_og_image` option to the generator to
300
+ * reduce processing power.
301
  *
302
  * @return string The Open Graph image meta tag.
303
  */
309
 
310
  $multi = (bool) $this->get_option( 'multi_og_image' );
311
 
312
+ foreach ( $this->get_image_details_from_cache( ! $multi ) as $image ) {
313
  $output .= '<meta property="og:image" content="' . \esc_attr( $image['url'] ) . '" />' . "\r\n";
314
 
315
  if ( $image['height'] && $image['width'] ) {
547
  * Renders Twitter Image meta tag.
548
  *
549
  * @since 2.2.2
550
+ * @since 4.1.2 Now forwards the `multi_og_image` option to the generator. Although
551
+ * it'll always use just one image, we read this option so we'll only
552
+ * use a single cache instance internally with the generator.
553
  *
554
  * @return string The Twitter Image meta tag.
555
  */
559
 
560
  $output = '';
561
 
562
+ foreach ( $this->get_image_details_from_cache( ! $this->get_option( 'multi_og_image' ) ) as $image ) {
563
  $output .= '<meta name="twitter:image" content="' . \esc_attr( $image['url'] ) . '" />' . "\r\n";
564
 
565
  if ( $image['height'] && $image['width'] ) {
1080
  * Renders Prev/Next Paged URL meta tags.
1081
  *
1082
  * @since 2.2.2
1083
+ * @uses $this->get_paged_urls()
1084
  *
1085
  * @return string The Prev/Next Paged URL meta tags.
1086
  */
1088
 
1089
  $id = $this->get_the_real_ID();
1090
 
1091
+ $paged_urls = $this->get_paged_urls();
1092
+
1093
  /**
1094
  * @since 2.6.0
1095
  * @param string $next The next-page URL.
1098
  $next = (string) \apply_filters_ref_array(
1099
  'the_seo_framework_paged_url_output_next',
1100
  [
1101
+ $paged_urls['next'],
1102
  $id,
1103
  ]
1104
  );
1111
  $prev = (string) \apply_filters_ref_array(
1112
  'the_seo_framework_paged_url_output_prev',
1113
  [
1114
+ $paged_urls['prev'],
1115
  $id,
1116
  ]
1117
  );
inc/classes/sanitize.class.php CHANGED
@@ -330,6 +330,7 @@ class Sanitize extends Admin_Pages {
330
  'ping_use_cron',
331
  'ping_google',
332
  'ping_bing',
 
333
 
334
  'excerpt_the_feed',
335
  'source_the_feed',
@@ -799,11 +800,6 @@ class Sanitize extends Admin_Pages {
799
 
800
  case '_genesis_canonical_uri':
801
  case '_social_image_url':
802
- /**
803
- * Remove unwanted query parameters. They're allowed by Google, but very much rather not.
804
- * Also, they will only cause bugs.
805
- * Query parameters are also only used when no pretty permalinks are used. Which is bad.
806
- */
807
  $value = $this->s_url_query( $value );
808
  continue 2;
809
 
@@ -2040,7 +2036,7 @@ class Sanitize extends Admin_Pages {
2040
 
2041
  $cleaned_details = [];
2042
 
2043
- // Failsafe. Convert associative detailts to a multidimensional sequential array.
2044
  if ( isset( $details_array['url'] ) )
2045
  $details_array = [ $details_array ];
2046
 
330
  'ping_use_cron',
331
  'ping_google',
332
  'ping_bing',
333
+ 'ping_use_cron_prerender',
334
 
335
  'excerpt_the_feed',
336
  'source_the_feed',
800
 
801
  case '_genesis_canonical_uri':
802
  case '_social_image_url':
 
 
 
 
 
803
  $value = $this->s_url_query( $value );
804
  continue 2;
805
 
2036
 
2037
  $cleaned_details = [];
2038
 
2039
+ // Failsafe. Convert associative details to a multidimensional sequential array.
2040
  if ( isset( $details_array['url'] ) )
2041
  $details_array = [ $details_array ];
2042
 
inc/classes/site-options.class.php CHANGED
@@ -206,7 +206,7 @@ class Site_Options extends Sanitize {
206
  'social_title_rem_additions' => 1, // Remove social title additions
207
 
208
  // Social image settings.
209
- 'multi_og_image' => 1, // Allow multiple images to be generated
210
 
211
  // Theme color settings.
212
  'theme_color' => '', // Theme color metatag, default none
@@ -254,9 +254,10 @@ class Site_Options extends Sanitize {
254
 
255
  'sitemaps_robots' => 1, // Add sitemap location to robots.txt
256
 
257
- 'ping_use_cron' => 1, // Ping using CRON.
258
- 'ping_google' => 1, // Ping Google
259
- 'ping_bing' => 1, // Ping Bing
 
260
 
261
  'sitemap_styles' => 1, // Whether to style the sitemap
262
  'sitemap_logo' => 1, // Whether to add logo to sitemap
@@ -285,6 +286,7 @@ class Site_Options extends Sanitize {
285
  * @since 2.9.0 Removed all non-warned settings.
286
  * @since 3.1.0 Now applies the "the_seo_framework_warned_site_options" filter.
287
  * @since 4.1.0 Added robots' post type setting warnings.
 
288
  *
289
  * @return array $options.
290
  */
@@ -304,11 +306,11 @@ class Site_Options extends Sanitize {
304
  return (array) \apply_filters(
305
  'the_seo_framework_warned_site_options',
306
  [
307
- 'title_rem_additions' => 1, // Title remove additions.
308
- 'site_noindex' => 1, // Site Page robots noindex
309
- 'site_nofollow' => 1, // Site Page robots nofollow
310
- 'homepage_noindex' => 1, // Homepage robots noindex
311
- 'homepage_nofollow' => 1, // Homepage robots noarchive
312
  $this->get_robots_post_type_option_id( 'noindex' ) => [
313
  'post' => 1,
314
  'page' => 1,
@@ -317,6 +319,7 @@ class Site_Options extends Sanitize {
317
  'post' => 1,
318
  'page' => 1,
319
  ],
 
320
  ]
321
  );
322
  }
206
  'social_title_rem_additions' => 1, // Remove social title additions
207
 
208
  // Social image settings.
209
+ 'multi_og_image' => 0, // Allow multiple images to be generated
210
 
211
  // Theme color settings.
212
  'theme_color' => '', // Theme color metatag, default none
254
 
255
  'sitemaps_robots' => 1, // Add sitemap location to robots.txt
256
 
257
+ 'ping_use_cron' => 1, // Ping using cron
258
+ 'ping_google' => 1, // Ping Google
259
+ 'ping_bing' => 1, // Ping Bing
260
+ 'ping_use_cron_prerender' => 0, // Sitemap cron-ping prerender
261
 
262
  'sitemap_styles' => 1, // Whether to style the sitemap
263
  'sitemap_logo' => 1, // Whether to add logo to sitemap
286
  * @since 2.9.0 Removed all non-warned settings.
287
  * @since 3.1.0 Now applies the "the_seo_framework_warned_site_options" filter.
288
  * @since 4.1.0 Added robots' post type setting warnings.
289
+ * @since 4.1.2 Added `ping_use_cron_prerender`.
290
  *
291
  * @return array $options.
292
  */
306
  return (array) \apply_filters(
307
  'the_seo_framework_warned_site_options',
308
  [
309
+ 'title_rem_additions' => 1, // Title remove additions.
310
+ 'site_noindex' => 1, // Site Page robots noindex.
311
+ 'site_nofollow' => 1, // Site Page robots nofollow.
312
+ 'homepage_noindex' => 1, // Homepage robots noindex.
313
+ 'homepage_nofollow' => 1, // Homepage robots noarchive.
314
  $this->get_robots_post_type_option_id( 'noindex' ) => [
315
  'post' => 1,
316
  'page' => 1,
319
  'post' => 1,
320
  'page' => 1,
321
  ],
322
+ 'ping_use_cron_prerender' => 1, // Sitemap cron-ping prerender.
323
  ]
324
  );
325
  }
inc/classes/term-data.class.php CHANGED
@@ -298,6 +298,7 @@ class Term_Data extends Post_Data {
298
 
299
  $data = (array) $_POST['autodescription-meta'];
300
 
 
301
  $this->save_term_meta( $term->term_id, $tt_id, $taxonomy, $data );
302
  }
303
 
@@ -332,6 +333,7 @@ class Term_Data extends Post_Data {
332
  (array) $_POST['autodescription-quick']
333
  );
334
 
 
335
  $this->save_term_meta( $term->term_id, $tt_id, $taxonomy, $data );
336
  }
337
 
298
 
299
  $data = (array) $_POST['autodescription-meta'];
300
 
301
+ // Trim, sanitize, and save the metadata.
302
  $this->save_term_meta( $term->term_id, $tt_id, $taxonomy, $data );
303
  }
304
 
333
  (array) $_POST['autodescription-quick']
334
  );
335
 
336
+ // Trim, sanitize, and save the metadata.
337
  $this->save_term_meta( $term->term_id, $tt_id, $taxonomy, $data );
338
  }
339
 
inc/classes/user-data.class.php CHANGED
@@ -169,7 +169,7 @@ class User_Data extends Term_Data {
169
  * @param mixed $default The default value to return when the data doesn't exist.
170
  * @return mixed The metadata value.
171
  */
172
- public function get_user_option( $user_id = 0, $option, $default = null ) {
173
 
174
  if ( ! $option )
175
  return $default;
@@ -210,7 +210,7 @@ class User_Data extends Term_Data {
210
  * @param mixed $value The escaped option value.
211
  * @return bool True on success. False on failure.
212
  */
213
- public function update_user_option( $user_id = 0, $option, $value ) {
214
 
215
  if ( ! $option )
216
  return false;
169
  * @param mixed $default The default value to return when the data doesn't exist.
170
  * @return mixed The metadata value.
171
  */
172
+ public function get_user_option( $user_id = 0, $option = '', $default = null ) {
173
 
174
  if ( ! $option )
175
  return $default;
210
  * @param mixed $value The escaped option value.
211
  * @return bool True on success. False on failure.
212
  */
213
+ public function update_user_option( $user_id = 0, $option = '', $value = '' ) {
214
 
215
  if ( ! $option )
216
  return false;
inc/compat/plugin-polylang.php CHANGED
@@ -8,6 +8,147 @@ namespace The_SEO_Framework;
8
 
9
  \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and \the_seo_framework()->_verify_include_secret( $_secret ) or die;
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  /**
12
  * Warns homepage global title and description about receiving input.
13
  *
@@ -37,8 +178,7 @@ function pll__( $string ) {
37
  return $string;
38
  }
39
 
40
- \add_filter( 'pll_home_url_white_list', __NAMESPACE__ . '\\_allowlist_tsf_urls' );
41
- \add_filter( 'pll_home_url_black_list', __NAMESPACE__ . '\\_blocklist_tsf_sitemap_styles' );
42
  /**
43
  * Accompany the most broken and asinine idea in WordPress's history.
44
  * Adds The SEO Framework's files to their allowlist of autoincompatible doom.
@@ -49,11 +189,12 @@ function pll__( $string ) {
49
  * @param array $allowlist The wildcard file parts that are allowlisted.
50
  * @return array
51
  */
52
- function _allowlist_tsf_urls( $allowlist ) {
53
  $allowlist[] = [ 'file' => 'autodescription/inc' ];
54
  return $allowlist;
55
  }
56
 
 
57
  /**
58
  * Accompany the most broken and asinine idea in WordPress's history.
59
  * ...and stop messing with the rewrite system while doing so.
@@ -61,53 +202,28 @@ function _allowlist_tsf_urls( $allowlist ) {
61
  *
62
  * @since 3.2.4
63
  * @since 4.1.0 Renamed function and parameters to something racially neutral.
 
64
  *
65
  * @param array $blocklist The wildcard file parts that are blocklisted.
66
  * @return array
67
  */
68
- function _blocklist_tsf_sitemap_styles( $blocklist ) {
69
- // y u no recurse
70
- $blocklist[] = [ 'function' => 'get_expected_sitemap_endpoint_url' ];
71
- $blocklist[] = [ 'function' => 'get_sitemap_base_path_info' ];
72
  $blocklist[] = [ 'file' => 'autodescription/inc/compat/plugin-polylang.php' ];
73
  return $blocklist;
74
  }
75
 
76
- \add_filter( 'the_seo_framework_sitemap_path_prefix', __NAMESPACE__ . '\\_fix_sitemap_prefix', 9 );
77
- /**
78
- * Fixes the sitemap prefix, because setting the home URL globally requires only one filter.
79
- * This will mess up translating with query-vars, though... FIXME?
80
- *
81
- * @since 4.0.0
82
- * @param string $prefix The path prefix. Ideally appended with a slash.
83
- * Recommended return value: "$prefix$custompath/"
84
- * @return string New prefix.
85
- */
86
- function _fix_sitemap_prefix( $prefix = '' ) {
87
-
88
- if ( \function_exists( '\\pll_home_url' ) ) {
89
- $home_url = \home_url();
90
- $ruined_home_url = \pll_home_url();
91
-
92
- $path = trim( substr_replace( $ruined_home_url, '', 0, \strlen( $home_url ) ), '/' );
93
-
94
- return $path ? "$prefix$path/" : $prefix;
95
- }
96
-
97
- return $prefix;
98
- }
99
-
100
- \add_filter( 'the_seo_framework_rel_canonical_output', __NAMESPACE__ . '\\_fix_home_url', 10, 2 );
101
- \add_filter( 'the_seo_framework_ogurl_output', __NAMESPACE__ . '\\_fix_home_url', 10, 2 );
102
  /**
103
  * Adds a trailing slash to whatever's deemed as the homepage URL.
104
  * This fixes user_trailingslashit() issues.
105
  *
106
  * @since 3.2.4
 
107
  * @param string $url The url to fix.
108
  * @param int $id The page or term ID.
109
  */
110
- function _fix_home_url( $url, $id ) {
111
  return \the_seo_framework()->is_front_page_by_ID( $id ) && \get_option( 'permalink_structure' ) ? \trailingslashit( $url ) : $url;
112
  }
113
 
8
 
9
  \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and \the_seo_framework()->_verify_include_secret( $_secret ) or die;
10
 
11
+ \add_filter( 'the_seo_framework_sitemap_base_path', __NAMESPACE__ . '\\_polylang_fix_sitemap_base_bath' );
12
+ /**
13
+ * Sets the correct Polylang base path language when using cookie-based language settings.
14
+ * This resolves an issue where the sitemap would otherwise return a 404 error after a
15
+ * cookie has been set.
16
+ *
17
+ * @since 4.1.2
18
+ * @access private
19
+ *
20
+ * @param string $path The home path.
21
+ * @return string Polylang-aware home path.
22
+ */
23
+ function _polylang_fix_sitemap_base_bath( $path ) {
24
+
25
+ $_options = \get_option( 'polylang' );
26
+
27
+ if ( isset( $_options['force_lang'] ) ) {
28
+ switch ( $_options['force_lang'] ) {
29
+ case 0:
30
+ // Polylang determines language sporadically from content: can't be trusted.
31
+ // NOTE: Thanks to '_polylang_blocklist_tsf_urls', this yields a different value albeit the same code.
32
+ // That's Polylang for you: can't trust your own code.
33
+ $path = rtrim( parse_url( \get_home_url(), PHP_URL_PATH ), '/' );
34
+ break;
35
+ default:
36
+ // Polylang can differentiate languages by (sub)domain/directory name early. No need to interfere.
37
+ break;
38
+ }
39
+ }
40
+
41
+ return $path;
42
+ }
43
+
44
+ \add_filter( 'the_seo_framework_sitemap_path_prefix', __NAMESPACE__ . '\\_polylang_fix_sitemap_prefix', 99 );
45
+ /**
46
+ * Fixes the sitemap prefix, because setting the home URL globally requires only one filter.
47
+ *
48
+ * @since 4.0.0
49
+ * @since 4.1.2 1. Now always defaults to the WP home URL. So, now supports other translation paths well,
50
+ * other than solely query-strings.
51
+ * 2. Prefixed function name with _polylang.
52
+ * @param string $prefix The path prefix. Ideally appended with a slash.
53
+ * Recommended return value: "$prefix$custompath/"
54
+ * @return string Polylang-aware prefix.
55
+ */
56
+ function _polylang_fix_sitemap_prefix( $prefix = '' ) {
57
+ $path = parse_url( \home_url(), PHP_URL_PATH );
58
+ return \trailingslashit( "$prefix$path" );
59
+ }
60
+
61
+ \add_action( 'the_seo_framework_sitemap_header', __NAMESPACE__ . '\\_polylang_set_sitemap_language' );
62
+ /**
63
+ * Sets the correct Polylang query language for the sitemap based on the 'lang' GET parameter.
64
+ *
65
+ * When the user supplies a correct 'lang' query parameter, they can overwrite our testing for force_lang settings.
66
+ * This is a fallback solution because we get endless support requests for Polylang, and we wish that plugin would be
67
+ * rewritten from scratch.
68
+ *
69
+ * @since 4.1.2
70
+ * @access private
71
+ */
72
+ function _polylang_set_sitemap_language() {
73
+
74
+ if ( ! \function_exists( 'PLL' ) ) return;
75
+ if ( ! ( \PLL() instanceof \PLL_Frontend ) ) return;
76
+
77
+ // phpcs:ignore, WordPress.Security.NonceVerification.Recommended -- Arbitrary input expected.
78
+ $lang = isset( $_GET['lang'] ) ? $_GET['lang'] : '';
79
+
80
+ // Language codes are user-definable: copy Polylang's filtering.
81
+ // The preg_match's source: \PLL_Admin_Model::validate_lang();
82
+ if ( ! \is_string( $lang ) || ! \strlen( $lang ) || ! preg_match( '#^[a-z_-]+$#', $lang ) ) {
83
+ $_options = \get_option( 'polylang' );
84
+ if ( isset( $_options['force_lang'] ) ) {
85
+ switch ( $_options['force_lang'] ) {
86
+ case 0:
87
+ // Polylang determines language sporadically from content: can't be trusted. Overwrite.
88
+ $lang = \pll_default_language();
89
+ break;
90
+ default:
91
+ // Polylang can differentiate languages by (sub)domain/directory name early. No need to interfere. Cancel.
92
+ return;
93
+ }
94
+ }
95
+ }
96
+
97
+ // This will default to the default language when $lang is invalid or unregistered. This is fine.
98
+ $new_lang = \PLL()->model->get_language( $lang );
99
+
100
+ if ( $new_lang )
101
+ \PLL()->curlang = $new_lang;
102
+ }
103
+
104
+ \add_action( 'the_seo_framework_sitemap_hpt_query_args', __NAMESPACE__ . '\\_polylang_sitemap_append_non_translatables' );
105
+ \add_action( 'the_seo_framework_sitemap_nhpt_query_args', __NAMESPACE__ . '\\_polylang_sitemap_append_non_translatables' );
106
+ /**
107
+ * Appends nontranslatable post types to the sitemap query arguments.
108
+ * Only appends when the default sitemap language is displayed.
109
+ *
110
+ * @since 4.1.2
111
+ * @access private
112
+ *
113
+ * @param array $args The query arguments.
114
+ * @return array The augmented query arguments.
115
+ */
116
+ function _polylang_sitemap_append_non_translatables( $args ) {
117
+
118
+ if ( ! \the_seo_framework()->can_i_use( [
119
+ 'functions' => [
120
+ 'PLL',
121
+ 'pll_languages_list',
122
+ 'pll_default_language',
123
+ ],
124
+ ] ) ) return $args;
125
+
126
+ if ( ! ( \PLL() instanceof \PLL_Frontend ) ) return $args;
127
+
128
+ $default_lang = \pll_default_language( \OBJECT );
129
+
130
+ if ( ! isset( $default_lang->slug, $default_lang->term_taxonomy_id ) ) return $args;
131
+
132
+ if ( \PLL()->curlang->slug === $default_lang->slug ) {
133
+ $args['lang'] = ''; // Select all, so that Polylang doesn't affect the query below with an AND (we need OR).
134
+ $args['tax_query'] = [
135
+ 'relation' => 'OR',
136
+ [
137
+ 'taxonomy' => 'language',
138
+ 'terms' => \pll_languages_list( [ 'fields' => 'term_id' ] ),
139
+ 'operator' => 'NOT IN',
140
+ ],
141
+ [
142
+ 'taxonomy' => 'language',
143
+ 'terms' => $default_lang->term_taxonomy_id,
144
+ 'operator' => 'IN',
145
+ ],
146
+ ];
147
+ }
148
+
149
+ return $args;
150
+ }
151
+
152
  /**
153
  * Warns homepage global title and description about receiving input.
154
  *
178
  return $string;
179
  }
180
 
181
+ \add_filter( 'pll_home_url_white_list', __NAMESPACE__ . '\\_polylang_allowlist_tsf_urls' );
 
182
  /**
183
  * Accompany the most broken and asinine idea in WordPress's history.
184
  * Adds The SEO Framework's files to their allowlist of autoincompatible doom.
189
  * @param array $allowlist The wildcard file parts that are allowlisted.
190
  * @return array
191
  */
192
+ function _polylang_allowlist_tsf_urls( $allowlist ) {
193
  $allowlist[] = [ 'file' => 'autodescription/inc' ];
194
  return $allowlist;
195
  }
196
 
197
+ \add_filter( 'pll_home_url_black_list', __NAMESPACE__ . '\\_polylang_blocklist_tsf_urls' );
198
  /**
199
  * Accompany the most broken and asinine idea in WordPress's history.
200
  * ...and stop messing with the rewrite system while doing so.
202
  *
203
  * @since 3.2.4
204
  * @since 4.1.0 Renamed function and parameters to something racially neutral.
205
+ * @since 4.1.2 Prefixed function name with _polylang.
206
  *
207
  * @param array $blocklist The wildcard file parts that are blocklisted.
208
  * @return array
209
  */
210
+ function _polylang_blocklist_tsf_urls( $blocklist ) {
 
 
 
211
  $blocklist[] = [ 'file' => 'autodescription/inc/compat/plugin-polylang.php' ];
212
  return $blocklist;
213
  }
214
 
215
+ \add_filter( 'the_seo_framework_rel_canonical_output', __NAMESPACE__ . '\\_polylang_fix_home_url', 10, 2 );
216
+ \add_filter( 'the_seo_framework_ogurl_output', __NAMESPACE__ . '\\_polylang_fix_home_url', 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  /**
218
  * Adds a trailing slash to whatever's deemed as the homepage URL.
219
  * This fixes user_trailingslashit() issues.
220
  *
221
  * @since 3.2.4
222
+ * @since 4.1.2 Prefixed function name with _polylang.
223
  * @param string $url The url to fix.
224
  * @param int $id The page or term ID.
225
  */
226
+ function _polylang_fix_home_url( $url, $id ) {
227
  return \the_seo_framework()->is_front_page_by_ID( $id ) && \get_option( 'permalink_structure' ) ? \trailingslashit( $url ) : $url;
228
  }
229
 
inc/compat/plugin-wpml.php CHANGED
@@ -33,6 +33,8 @@ function _wpml_do_current_screen_action() {
33
  /**
34
  * Removes "All languages" option from WPML admin switcher.
35
  *
 
 
36
  * @since 2.8.0
37
  * @access private
38
  *
33
  /**
34
  * Removes "All languages" option from WPML admin switcher.
35
  *
36
+ * FIXME: Why did we do this again? Does it even affect the settings? Does it fix the home query? Remove me?
37
+ *
38
  * @since 2.8.0
39
  * @access private
40
  *
inc/functions/upgrade-suggestion.php CHANGED
@@ -55,6 +55,7 @@ _prepare( $previous_version, $current_version );
55
  * 3. Can now run on the front-end without crashing.
56
  * 4. Added the first two parameters, $previous_version and $current_version.
57
  * 5. Now tests if the upgrade actually happened, before invoking the suggestion.
 
58
  * @access private
59
  *
60
  * @param string $previous_version The previous version the site upgraded from, if any.
@@ -69,6 +70,17 @@ function _prepare( $previous_version, $current_version ) {
69
  if ( \defined( 'TSF_DISABLE_SUGGESTIONS' ) && TSF_DISABLE_SUGGESTIONS ) return;
70
  //? 2
71
  if ( ! \is_main_site() ) return;
 
 
 
 
 
 
 
 
 
 
 
72
  //? 3a
73
  if ( \defined( 'TSF_EXTENSION_MANAGER_VERSION' ) ) return;
74
 
@@ -149,3 +161,51 @@ function _suggest_extension_manager( $previous_version, $current_version ) {
149
  $suggest_conditions
150
  );
151
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  * 3. Can now run on the front-end without crashing.
56
  * 4. Added the first two parameters, $previous_version and $current_version.
57
  * 5. Now tests if the upgrade actually happened, before invoking the suggestion.
58
+ * @since 4.1.2 Can now communicate with Extension Manager.
59
  * @access private
60
  *
61
  * @param string $previous_version The previous version the site upgraded from, if any.
70
  if ( \defined( 'TSF_DISABLE_SUGGESTIONS' ) && TSF_DISABLE_SUGGESTIONS ) return;
71
  //? 2
72
  if ( ! \is_main_site() ) return;
73
+
74
+ $show_sale = true;
75
+
76
+ if ( \function_exists( '\\tsf_extension_manager' ) && method_exists( \tsf_extension_manager(), 'is_connected_user' ) ) {
77
+ $show_sale = ! \tsf_extension_manager()->is_connected_user();
78
+ }
79
+ if ( $show_sale ) {
80
+ // phpcs:ignore, TSF.Performance.Opcodes.ShouldHaveNamespaceEscape
81
+ _suggest_temp_sale( $previous_version, $current_version );
82
+ }
83
+
84
  //? 3a
85
  if ( \defined( 'TSF_EXTENSION_MANAGER_VERSION' ) ) return;
86
 
161
  $suggest_conditions
162
  );
163
  }
164
+
165
+ /**
166
+ * Registers "look at site" notification to applicable plugin users on upgrade.
167
+ *
168
+ * Some will hate me for this. Others will thank me they got notified.
169
+ * In the end, I can't sustain this project without money, and the whiny users still need a good working product.
170
+ * Win-win.
171
+ *
172
+ * @since 4.1.2
173
+ * @access private
174
+ *
175
+ * @param string $previous_version The previous version the site upgraded from, if any.
176
+ * @param string $current_version The current version of the site.
177
+ */
178
+ function _suggest_temp_sale( $previous_version, $current_version ) {
179
+
180
+ $tsf = \the_seo_framework();
181
+
182
+ $suggest_key = 'suggest-sale';
183
+ $suggest_args = [
184
+ 'type' => 'info',
185
+ 'icon' => false,
186
+ 'escape' => false,
187
+ ];
188
+ $suggest_conditions = [
189
+ 'screens' => [],
190
+ 'excl_screens' => [ 'update-core', 'post', 'term', 'upload', 'media', 'plugin-editor', 'plugin-install', 'themes', 'widgets', 'user', 'nav-menus', 'theme-editor', 'profile', 'export', 'site-health', 'export-personal-data', 'erase-personal-data' ],
191
+ 'capability' => 'install_plugins',
192
+ 'user' => 0,
193
+ 'count' => 2,
194
+ 'timeout' => strtotime( 'December 6th, 2020, 22:50GMT+1' ) - time(),
195
+ ];
196
+
197
+ if ( $previous_version < '4120' && $current_version < '4200' )
198
+ $tsf->register_dismissible_persistent_notice(
199
+ $tsf->convert_markdown(
200
+ sprintf(
201
+ '<p>The SEO Framework: Cyber Monday [30~50%% off](%s). This notification will self-destruct when the sale ends, or when you dismiss it.</p>',
202
+ 'https://theseoframework.com/?p=3527'
203
+ ),
204
+ [ 'a', 'em', 'strong' ],
205
+ [ 'a_internal' => false ]
206
+ ),
207
+ $suggest_key,
208
+ $suggest_args,
209
+ $suggest_conditions
210
+ );
211
+ }
inc/traits/core/overload.trait.php DELETED
@@ -1,136 +0,0 @@
1
- <?php
2
- /**
3
- * @package The_SEO_Framework\Traits\Overload
4
- */
5
-
6
- namespace The_SEO_Framework\Traits;
7
-
8
- \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
9
-
10
- /**
11
- * The SEO Framework plugin
12
- * Copyright (C) 2019 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
13
- *
14
- * This program is free software: you can redistribute it and/or modify
15
- * it under the terms of the GNU General Public License version 3 as published
16
- * by the Free Software Foundation.
17
- *
18
- * This program is distributed in the hope that it will be useful,
19
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
- * GNU General Public License for more details.
22
- *
23
- * You should have received a copy of the GNU General Public License
24
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
- */
26
-
27
- /**
28
- * Legend/Definitions:
29
- *
30
- * - Facade Legend/Definitions:
31
- * Choose one per trait.
32
- *
33
- * - Core: Final instance.
34
- * - No parents.
35
- * - Maybe children.
36
- * - All methods are protected.
37
- *
38
- * - Master: First instance of facade. Calls all the shots.
39
- * - Expects parent class.
40
- * - Has no child class.
41
- * - All methods are public.
42
- * - Except for subconstructor.
43
- * - Expects class to be labelled "final".
44
- *
45
- * - Sub: Sub instance.
46
- * - Expects child class.
47
- * - Expects parent class.
48
- *
49
- * - Child: Child instance.
50
- * - Synonymous to "static".
51
- * - Expects parent class.
52
- * - Could have child class.
53
- * - Prevents object calling.
54
- *
55
- * - Stray: Expects nothing.
56
- * - Maybe child.
57
- * - Maybe parent.
58
- *
59
- * - Visibility Legend/Definitions:
60
- * These can be combined.
61
- *
62
- * - Final: Final instance.
63
- * - Expects children classes not to contain same methods.
64
- * - All methods are labelled "final".
65
- * - Expects class to be labelled "final".
66
- *
67
- * - Solo: Single object.
68
- * - Expects no parents.
69
- * - Expects no children.
70
- * - All methods are labelled "final".
71
- * - Expects class to be labelled "final".
72
- * - Prevents facade pattern.
73
- * - All methods could be public.
74
- *
75
- * - Static: Expects class not to be initiated.
76
- * - Synonymous to "child".
77
- * - Prevents object calling.
78
- * - All public methods are static.
79
- *
80
- * - Once: Expects class to be called at most once.
81
- * - Caches method calls.
82
- * - Exits PHP on second call.
83
- *
84
- * - Interface: Contains abstract methods.
85
- *
86
- * - Private: All methods are private.
87
- *
88
- * - <No keyword>: Expects nothing.
89
- * - All methods are "protected".
90
- *
91
- * - Public: All methods are public.
92
- *
93
- * - Type Legend/Definitions:
94
- * Choose one per trait.
95
- *
96
- * - Enclose: Prevents common hacking methods through magic method nullification.
97
- *
98
- * - Construct: Holds constructor.
99
- * - When interface: Holds subsconstructor.
100
- * - Make sure the subconstructor is private. Otherwise late static binding will kick in.
101
- *
102
- * - Destruct: Holds destructor and keeps track of destruct calling.
103
- *
104
- * - Ignore_Properties_Core_Public_Final: Ignores invalid property calling. Prevents PHP warning messages.
105
- *
106
- * - <No keyword>: Should not exist.
107
- */
108
-
109
- // phpcs:disable, Squiz.Commenting.FunctionComment.Missing -- The trait doc explains it.
110
- // phpcs:disable, Generic.Files.OneObjectStructurePerFile.MultipleFound -- This is a collective, preloaded file for all overloading.
111
-
112
- /**
113
- * Holds private overloading functions to prevent injection or abstraction.
114
- *
115
- * @since 4.0.0
116
- * @access private
117
- */
118
- trait Enclose_Stray_Private {
119
-
120
- private function __clone() { }
121
-
122
- private function __wakeup() { }
123
- }
124
-
125
- /**
126
- * Forces all classes and subclasses to prevent injection or abstraction.
127
- *
128
- * @since 4.0.0
129
- * @access private
130
- */
131
- trait Enclose_Core_Final {
132
-
133
- final protected function __clone() { }
134
-
135
- final protected function __wakeup() { }
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/views/admin/metaboxes/general-metabox.php CHANGED
@@ -250,7 +250,7 @@ switch ( $instance ) :
250
  $this->wrap_fields(
251
  $this->make_checkbox(
252
  'cache_sitemap',
253
- esc_html__( 'Enable sitemap generation cache?', 'autodescription' )
254
  . ' ' . $this->make_info( __( 'Generating the sitemap can use a lot of server resources.', 'autodescription' ), '', false ),
255
  '',
256
  false
250
  $this->wrap_fields(
251
  $this->make_checkbox(
252
  'cache_sitemap',
253
+ esc_html__( 'Enable optimized sitemap generation cache?', 'autodescription' )
254
  . ' ' . $this->make_info( __( 'Generating the sitemap can use a lot of server resources.', 'autodescription' ), '', false ),
255
  '',
256
  false
inc/views/admin/metaboxes/sitemaps-metabox.php CHANGED
@@ -56,16 +56,27 @@ switch ( $instance ) :
56
  break;
57
 
58
  case 'the_seo_framework_sitemaps_metabox_general':
59
- $sitemap_url = \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url();
60
  $has_sitemap_plugin = $this->detect_sitemap_plugin();
 
61
  $sitemap_detected = $this->has_sitemap_xml();
62
 
63
  ?>
64
  <h4><?php esc_html_e( 'Sitemap Integration Settings', 'autodescription' ); ?></h4>
65
  <?php
66
- $this->description( __( 'The sitemap is an XML file that lists indexable pages of your website along with optional metadata. This helps search engines find new and updated content more quickly.', 'autodescription' ) );
67
 
68
- $this->description( __( 'The sitemap does not contribute to ranking; it only speeds up indexing. Therefore, it is perfectly fine not having every indexable page in the sitemap.', 'autodescription' ) );
 
 
 
 
 
 
 
 
 
 
69
 
70
  if ( $has_sitemap_plugin ) :
71
  echo '<hr>';
@@ -86,7 +97,7 @@ switch ( $instance ) :
86
  'sitemaps_output',
87
  esc_html__( 'Output optimized sitemap?', 'autodescription' )
88
  . ' ' . $this->make_info(
89
- __( 'This sitemap considers the indexing state of all your posts and pages.', 'autodescription' ),
90
  '',
91
  false
92
  ),
@@ -96,14 +107,28 @@ switch ( $instance ) :
96
  true
97
  );
98
 
99
- if ( ! $has_sitemap_plugin && ( $this->get_option( 'sitemaps_output' ) || ( $sitemap_detected && $this->pretty_permalinks ) ) ) {
100
- $this->description_noesc(
101
- sprintf(
102
- '<a href="%s" target=_blank rel=noopener>%s</a>',
103
- esc_url( \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url(), [ 'https', 'http' ] ),
104
- esc_html__( 'View the base sitemap.', 'autodescription' )
105
- )
106
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
108
 
109
  ?>
@@ -115,7 +140,7 @@ switch ( $instance ) :
115
  </label>
116
  </p>
117
  <?php
118
- $this->description( __( 'The sitemap is generated with two queries: Hierarchical and non-hierarchical post types. This setting affects how many posts are requested from the database per query. The homepage and blog page are included separately.', 'autodescription' ) );
119
 
120
  if ( has_filter( 'the_seo_framework_sitemap_post_limit' ) ) :
121
  ?>
@@ -228,27 +253,29 @@ switch ( $instance ) :
228
  true
229
  );
230
 
231
- ?>
232
- <hr>
 
233
 
234
- <h4><?php esc_html_e( 'Priority Settings', 'autodescription' ); ?></h4>
235
- <?php
236
- $this->description( __( 'The priority index suggests to search engines which pages are deemed more important. It has no known impact on the SEO value and it is generally ignored.', 'autodescription' ) );
237
 
238
- // Echo checkbox.
239
- $this->wrap_fields(
240
- $this->make_checkbox(
241
- 'sitemaps_priority',
242
- $this->convert_markdown(
243
- /* translators: the backticks are Markdown! Preserve them as-is! */
244
- esc_html__( 'Add `<priority>` to the sitemap?', 'autodescription' ),
245
- [ 'code' ]
 
 
 
246
  ),
247
- '',
248
- false
249
- ),
250
- true
251
- );
252
  break;
253
 
254
  case 'the_seo_framework_sitemaps_metabox_notify':
@@ -259,17 +286,30 @@ switch ( $instance ) :
259
  $this->description( __( 'By default this will happen at most once an hour.', 'autodescription' ) );
260
 
261
  $this->wrap_fields(
262
- $this->make_checkbox(
263
- 'ping_use_cron',
264
- esc_html__( 'Use cron for pinging?', 'autodescription' )
265
- . ' ' . $this->make_info(
266
- __( 'This speeds up post and term saving processes, by offsetting pinging to a later time.', 'autodescription' ),
267
- '',
268
- false
269
- ),
270
- '',
271
- false
272
- ),
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  true
274
  );
275
 
@@ -298,7 +338,7 @@ switch ( $instance ) :
298
 
299
  case 'the_seo_framework_sitemaps_metabox_style':
300
  ?>
301
- <h4><?php esc_html_e( 'Sitemap Styling Settings', 'autodescription' ); ?></h4>
302
  <?php
303
  $this->description( __( 'You can style the optimized sitemap to give it a more personal look for your visitors. Search engines do not use these styles.', 'autodescription' ) );
304
  $this->description( __( 'Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes.', 'autodescription' ) );
56
  break;
57
 
58
  case 'the_seo_framework_sitemaps_metabox_general':
59
+ $sitemap_url = The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url();
60
  $has_sitemap_plugin = $this->detect_sitemap_plugin();
61
+ $use_core_sitemaps = $this->use_core_sitemaps();
62
  $sitemap_detected = $this->has_sitemap_xml();
63
 
64
  ?>
65
  <h4><?php esc_html_e( 'Sitemap Integration Settings', 'autodescription' ); ?></h4>
66
  <?php
67
+ $this->description( __( 'The sitemap is an XML file that lists indexable pages of your website along with optional metadata. It helps search engines find new and updated content quickly.', 'autodescription' ) );
68
 
69
+ $this->description_noesc(
70
+ $this->convert_markdown(
71
+ sprintf(
72
+ /* translators: %s = Learn more URL. Markdown! */
73
+ esc_html__( 'The sitemap does not contribute to ranking; [it can only help with indexing](%s). Search engines process smaller, less complicated sitemaps quicker, which shortens the time required for indexing pages.', 'autodescription' ),
74
+ 'https://kb.theseoframework.com/?p=119'
75
+ ),
76
+ [ 'a' ],
77
+ [ 'a_internal' => false ]
78
+ )
79
+ );
80
 
81
  if ( $has_sitemap_plugin ) :
82
  echo '<hr>';
97
  'sitemaps_output',
98
  esc_html__( 'Output optimized sitemap?', 'autodescription' )
99
  . ' ' . $this->make_info(
100
+ __( 'This sitemap is processed quicker by search engines.', 'autodescription' ),
101
  '',
102
  false
103
  ),
107
  true
108
  );
109
 
110
+ if ( ! $has_sitemap_plugin && ! $sitemap_detected ) {
111
+ if ( $this->get_option( 'sitemaps_output' ) ) {
112
+ $this->description_noesc(
113
+ sprintf(
114
+ '<a href="%s" target=_blank rel=noopener>%s</a>',
115
+ esc_url( The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url(), [ 'https', 'http' ] ),
116
+ esc_html__( 'View the base sitemap.', 'autodescription' )
117
+ )
118
+ );
119
+ // TODO In settings generator (TSF 5.0): Overwite this section for Polylang/WPML and output each sitemap language link respectively.
120
+ // TODO Also add a link telling where why it may not work consistently ('try opening in another browser, incognito, etc.')
121
+ } elseif ( $use_core_sitemaps ) {
122
+ $_index_url = get_sitemap_url( 'index' );
123
+ if ( $_index_url )
124
+ $this->description_noesc(
125
+ sprintf(
126
+ '<a href="%s" target=_blank rel=noopener>%s</a>',
127
+ esc_url( $_index_url, [ 'https', 'http' ] ),
128
+ esc_html__( 'View the sitemap index.', 'autodescription' )
129
+ )
130
+ );
131
+ }
132
  }
133
 
134
  ?>
140
  </label>
141
  </p>
142
  <?php
143
+ $this->description( __( 'This setting affects how many pages are requested from the database per query.', 'autodescription' ) );
144
 
145
  if ( has_filter( 'the_seo_framework_sitemap_post_limit' ) ) :
146
  ?>
253
  true
254
  );
255
 
256
+ if ( $this->get_option( 'sitemaps_priority' ) ) :
257
+ ?>
258
+ <hr>
259
 
260
+ <h4><?php esc_html_e( 'Priority Settings', 'autodescription' ); ?></h4>
261
+ <?php
262
+ $this->description( __( 'The priority index suggests to search engines which pages are deemed more important. It has no known impact on the SEO value and it is generally ignored.', 'autodescription' ) );
263
 
264
+ // Echo checkbox.
265
+ $this->wrap_fields(
266
+ $this->make_checkbox(
267
+ 'sitemaps_priority',
268
+ $this->convert_markdown(
269
+ /* translators: the backticks are Markdown! Preserve them as-is! */
270
+ esc_html__( 'Add `<priority>` to the optimized sitemap?', 'autodescription' ),
271
+ [ 'code' ]
272
+ ),
273
+ '',
274
+ false
275
  ),
276
+ true
277
+ );
278
+ endif; // endif get_option( 'sitemaps_priority' );
 
 
279
  break;
280
 
281
  case 'the_seo_framework_sitemaps_metabox_notify':
286
  $this->description( __( 'By default this will happen at most once an hour.', 'autodescription' ) );
287
 
288
  $this->wrap_fields(
289
+ [
290
+ $this->make_checkbox(
291
+ 'ping_use_cron',
292
+ esc_html__( 'Use cron for pinging?', 'autodescription' )
293
+ . ' ' . $this->make_info(
294
+ __( 'This speeds up post and term saving processes, by offsetting pinging to a later time.', 'autodescription' ),
295
+ '',
296
+ false
297
+ ),
298
+ '',
299
+ false
300
+ ),
301
+ $this->make_checkbox(
302
+ 'ping_use_cron_prerender',
303
+ esc_html__( 'Prerender optimized sitemap before pinging via cron?', 'autodescription' )
304
+ . ' ' . $this->make_info(
305
+ __( 'This mitigates timeouts some search engines may experience when waiting for the sitemap to render. Transient caching for the sitemap must be enabled for this to work.', 'autodescription' ),
306
+ '',
307
+ false
308
+ ),
309
+ esc_html__( 'Only enable prerendering when generating the sitemap takes over 60 seconds.', 'autodescription' ),
310
+ false
311
+ ),
312
+ ],
313
  true
314
  );
315
 
338
 
339
  case 'the_seo_framework_sitemaps_metabox_style':
340
  ?>
341
+ <h4><?php esc_html_e( 'Optimized Sitemap Styling Settings', 'autodescription' ); ?></h4>
342
  <?php
343
  $this->description( __( 'You can style the optimized sitemap to give it a more personal look for your visitors. Search engines do not use these styles.', 'autodescription' ) );
344
  $this->description( __( 'Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes.', 'autodescription' ) );
inc/views/admin/metaboxes/title-metabox.php CHANGED
@@ -27,7 +27,7 @@ switch ( $instance ) :
27
  $latest_cat_id = $this->get_latest_category_id();
28
 
29
  // phpcs:ignore, WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- We don't expect users to set scripts in titles.
30
- $post_name = strip_tags( get_the_title( $latest_post_id ) ?: __( 'Example Post', 'autodescription' ) );
31
  $post_title = $this->s_title( $this->hellip_if_over( $post_name, 60 ) );
32
 
33
  // phpcs:ignore, WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- We don't expect users to set scripts in titles.
@@ -85,6 +85,33 @@ switch ( $instance ) :
85
 
86
  <hr>
87
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  $default_tabs = [
90
  'general' => [
27
  $latest_cat_id = $this->get_latest_category_id();
28
 
29
  // phpcs:ignore, WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- We don't expect users to set scripts in titles.
30
+ $post_name = strip_tags( get_the_title( $latest_post_id ) ) ?: __( 'Example Post', 'autodescription' );
31
  $post_title = $this->s_title( $this->hellip_if_over( $post_name, 60 ) );
32
 
33
  // phpcs:ignore, WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- We don't expect users to set scripts in titles.
85
 
86
  <hr>
87
  <?php
88
+ if (
89
+ ! ( defined( 'TSF_DISABLE_SUGGESTIONS' ) && TSF_DISABLE_SUGGESTIONS )
90
+ && ! current_theme_supports( 'title-tag' )
91
+ && ! defined( 'TSFEM_E_TITLE_FIX' )
92
+ && current_user_can( 'install_plugins' )
93
+ ) {
94
+ /* translators: %s = title-tag */
95
+ $_h4 = sprintf( esc_html__( 'Theme %s Support Missing', 'autodescription' ), '<code>title-tag</code>' );
96
+ ?>
97
+ <h4 class=attention><?php echo $_h4; // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped ?></h4>
98
+ <?php
99
+ $this->description_noesc(
100
+ $this->convert_markdown(
101
+ sprintf(
102
+ /* translators: 1: Extension name, 2: Extension link. Markdown! */
103
+ esc_html__( "The current theme doesn't support a feature that allows predictable output of titles. Consider installing [%1\$s](%2\$s) when you notice the title output in the browser-tab isn't as you have configured.", 'autodescription' ),
104
+ 'Title Fix',
105
+ 'https://theseoframework.com/?p=2298'
106
+ ),
107
+ [ 'a' ],
108
+ [ 'a_internal' => false ]
109
+ )
110
+ );
111
+ ?>
112
+ <hr>
113
+ <?php
114
+ }
115
 
116
  $default_tabs = [
117
  'general' => [
inc/views/edit/seo-settings-singular.php CHANGED
@@ -72,7 +72,10 @@ switch ( $instance ) :
72
  </div>
73
  <div class="tsf-flex-setting-input tsf-flex">
74
  <div>
75
- <?php echo $this->get_generated_seo_bar( $_generator_args ); ?>
 
 
 
76
  </div>
77
  </div>
78
  </div>
@@ -334,6 +337,11 @@ switch ( $instance ) :
334
  1 => $_s['force_off'],
335
  ],
336
  'default' => $this->get_post_meta_item( $_s['option'] ),
 
 
 
 
 
337
  ] );
338
  // phpcs:enable, WordPress.Security.EscapeOutput
339
  ?>
@@ -499,7 +507,12 @@ switch ( $instance ) :
499
  </div>
500
  </div>
501
  <div class="tsf-flex-setting-input tsf-flex">
502
- <textarea class="large-text" name="autodescription[_twitter_description]" id="autodescription_twitter_description" placeholder="<?php echo esc_attr( $social_placeholders['description']['twitter'] ); ?>" rows="3" cols="4" autocomplete=off><?php echo $this->esc_attr_preserve_amp( $this->get_post_meta_item( '_twitter_description' ) ); ?></textarea>
 
 
 
 
 
503
  </div>
504
  </div>
505
  <?php
72
  </div>
73
  <div class="tsf-flex-setting-input tsf-flex">
74
  <div>
75
+ <?php
76
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- get_generated_seo_bar() escapes.
77
+ echo $this->get_generated_seo_bar( $_generator_args );
78
+ ?>
79
  </div>
80
  </div>
81
  </div>
337
  1 => $_s['force_off'],
338
  ],
339
  'default' => $this->get_post_meta_item( $_s['option'] ),
340
+ 'data' => [
341
+ 'defaultUnprotected' => $_s['_default'],
342
+ /* translators: %s = default option value */
343
+ 'defaultI18n' => __( 'Default (%s)', 'autodescription' ),
344
+ ],
345
  ] );
346
  // phpcs:enable, WordPress.Security.EscapeOutput
347
  ?>
507
  </div>
508
  </div>
509
  <div class="tsf-flex-setting-input tsf-flex">
510
+ <textarea class="large-text" name="autodescription[_twitter_description]" id="autodescription_twitter_description" placeholder="<?php echo esc_attr( $social_placeholders['description']['twitter'] ); ?>" rows="3" cols="4" autocomplete=off>
511
+ <?php
512
+ // Textareas don't require sanitization in HTML5... other than removing the closing </textarea> tag...?
513
+ echo $this->esc_attr_preserve_amp( $this->get_post_meta_item( '_twitter_description' ) );
514
+ ?>
515
+ </textarea>
516
  </div>
517
  </div>
518
  <?php
inc/views/edit/seo-settings-tt.php CHANGED
@@ -46,7 +46,7 @@ $image_details = current( $this->get_generated_image_details( $_generator_ar
46
  $image_placeholder = isset( $image_details['url'] ) ? $image_details['url'] : '';
47
 
48
  $canonical_placeholder = $this->create_canonical_url( $_generator_args ); // implies get_custom_field = false
49
- $robots_defaults = $this->robots_meta( $_generator_args, The_SEO_Framework\ROBOTS_IGNORE_PROTECTION | The_SEO_Framework\ROBOTS_IGNORE_SETTINGS );
50
 
51
  // TODO reintroduce the info blocks, and place the labels at the left, instead??
52
  $robots_settings = [
@@ -342,6 +342,11 @@ $robots_settings = [
342
  ],
343
  'default' => $_s['_value'],
344
  'info' => $_s['_info'],
 
 
 
 
 
345
  ] );
346
  // phpcs:enable, WordPress.Security.EscapeOutput
347
  endforeach;
46
  $image_placeholder = isset( $image_details['url'] ) ? $image_details['url'] : '';
47
 
48
  $canonical_placeholder = $this->create_canonical_url( $_generator_args ); // implies get_custom_field = false
49
+ $robots_defaults = $this->robots_meta( $_generator_args, The_SEO_Framework\ROBOTS_IGNORE_SETTINGS );
50
 
51
  // TODO reintroduce the info blocks, and place the labels at the left, instead??
52
  $robots_settings = [
342
  ],
343
  'default' => $_s['_value'],
344
  'info' => $_s['_info'],
345
+ 'data' => [
346
+ 'defaultUnprotected' => $_s['_default'],
347
+ /* translators: %s = default option value */
348
+ 'defaultI18n' => __( 'Default (%s)', 'autodescription' ),
349
+ ],
350
  ] );
351
  // phpcs:enable, WordPress.Security.EscapeOutput
352
  endforeach;
inc/views/notice/persistent.php CHANGED
@@ -13,7 +13,7 @@ if ( ! $message ) return;
13
 
14
  // Make sure the scripts are loaded.
15
  $this->init_admin_scripts();
16
- \The_SEO_Framework\Builders\Scripts::enqueue();
17
 
18
  if ( in_array( $args['type'], [ 'warning', 'info' ], true ) )
19
  $args['type'] = "notice-{$args['type']}";
13
 
14
  // Make sure the scripts are loaded.
15
  $this->init_admin_scripts();
16
+ The_SEO_Framework\Builders\Scripts::footer_enqueue();
17
 
18
  if ( in_array( $args['type'], [ 'warning', 'info' ], true ) )
19
  $args['type'] = "notice-{$args['type']}";
inc/views/sitemap/xml-sitemap.php CHANGED
@@ -11,7 +11,8 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_
11
 
12
  $this->the_seo_framework_debug and $timer_start = microtime( true );
13
 
14
- $sitemap_bridge = \The_SEO_Framework\Bridges\Sitemap::get_instance();
 
15
  $sitemap_bridge->output_sitemap_header();
16
 
17
  if ( $this->the_seo_framework_debug ) {
@@ -21,42 +22,21 @@ if ( $this->the_seo_framework_debug ) {
21
 
22
  $sitemap_bridge->output_sitemap_urlset_open_tag();
23
 
24
- $sitemap_generated = false;
25
- $sitemap_content = $this->get_option( 'cache_sitemap' ) ? $this->get_transient( $this->get_sitemap_transient_name() ) : false;
26
-
27
- // TODO convert this to an easily accessible method that stores and returns the value.
28
- if ( false === $sitemap_content ) {
29
- $sitemap_generated = true;
30
-
31
- // Transient doesn't exist yet.
32
- $sitemap_base = new \The_SEO_Framework\Builders\Sitemap_Base;
33
- $sitemap_base->prepare_generation();
34
-
35
- $sitemap_content = $sitemap_base->build_sitemap();
36
-
37
- $sitemap_base->shutdown_generation();
38
- $sitemap_base = null; // destroy class.
39
-
40
- /**
41
- * Transient expiration: 1 week.
42
- * Keep the sitemap for at most 1 week.
43
- */
44
- $expiration = WEEK_IN_SECONDS;
45
-
46
- if ( $this->get_option( 'cache_sitemap' ) )
47
- $this->set_transient( $this->get_sitemap_transient_name(), $sitemap_content, $expiration );
48
- }
49
  // phpcs:ignore, WordPress.Security.EscapeOutput
50
- echo $sitemap_content;
51
 
52
  $sitemap_bridge->output_sitemap_urlset_close_tag();
53
 
54
- if ( $sitemap_generated ) {
55
  echo "\n" . '<!-- ' . esc_html__( 'Sitemap is generated for this view', 'autodescription' ) . ' -->';
56
  } else {
57
  echo "\n" . '<!-- ' . esc_html__( 'Sitemap is served from cache', 'autodescription' ) . ' -->';
58
  }
59
 
 
 
 
60
  if ( $this->the_seo_framework_debug ) {
61
  echo "\n" . '<!-- Site estimated current usage: ' . number_format( memory_get_usage() / 1024 / 1024, 3 ) . ' MB -->';
62
  echo "\n" . '<!-- System estimated current usage: ' . number_format( memory_get_usage( true ) / 1024 / 1024, 3 ) . ' MB -->';
11
 
12
  $this->the_seo_framework_debug and $timer_start = microtime( true );
13
 
14
+ $sitemap_bridge = The_SEO_Framework\Bridges\Sitemap::get_instance();
15
+
16
  $sitemap_bridge->output_sitemap_header();
17
 
18
  if ( $this->the_seo_framework_debug ) {
22
 
23
  $sitemap_bridge->output_sitemap_urlset_open_tag();
24
 
25
+ $sitemap_base = new The_SEO_Framework\Builders\Sitemap_Base;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  // phpcs:ignore, WordPress.Security.EscapeOutput
27
+ echo $sitemap_base->generate_sitemap( $sitemap_id );
28
 
29
  $sitemap_bridge->output_sitemap_urlset_close_tag();
30
 
31
+ if ( $sitemap_base->base_is_regenerated ) {
32
  echo "\n" . '<!-- ' . esc_html__( 'Sitemap is generated for this view', 'autodescription' ) . ' -->';
33
  } else {
34
  echo "\n" . '<!-- ' . esc_html__( 'Sitemap is served from cache', 'autodescription' ) . ' -->';
35
  }
36
 
37
+ // Destroy class.
38
+ $sitemap_base = null;
39
+
40
  if ( $this->the_seo_framework_debug ) {
41
  echo "\n" . '<!-- Site estimated current usage: ' . number_format( memory_get_usage() / 1024 / 1024, 3 ) . ' MB -->';
42
  echo "\n" . '<!-- System estimated current usage: ' . number_format( memory_get_usage( true ) / 1024 / 1024, 3 ) . ' MB -->';
language/autodescription.pot CHANGED
@@ -2,14 +2,14 @@
2
  # This file is distributed under the same license as the The SEO Framework plugin.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: The SEO Framework 4.1.1\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/trunk\n"
7
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
8
  "Language-Team: LANGUAGE <LL@li.org>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "POT-Creation-Date: 2020-09-04T17:38:54+02:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
  "X-Generator: WP-CLI 2.4.0\n"
15
  "X-Domain: autodescription\n"
@@ -32,124 +32,124 @@ msgid "The SEO Framework Team"
32
  msgstr ""
33
 
34
  #. translators: %1$s = New, lower version number, surrounded in markdown-backticks. %2$s = Old, higher version number, surrounded in markdown-backticks.
35
- #: bootstrap/upgrade.php:377
36
  msgid "Your website has been downgraded successfully to use The SEO Framework at database version `%1$s` from `%2$s`."
37
  msgstr ""
38
 
39
  #. translators: %s = Version number, surrounded in markdown-backticks.
40
- #: bootstrap/upgrade.php:425
41
  msgid "Thank you for updating The SEO Framework! Your website has been upgraded successfully to use The SEO Framework at database version `%s`."
42
  msgstr ""
43
 
44
- #: bootstrap/upgrade.php:465
45
  msgid "The SEO Framework automatically optimizes your website for search engines and social media."
46
  msgstr ""
47
 
48
  #. translators: %s = Link, markdown.
49
- #: bootstrap/upgrade.php:469
50
  msgid "To take full advantage of all SEO features, please follow our [5-minute setup guide](%s)."
51
  msgstr ""
52
 
53
- #: bootstrap/upgrade.php:599
54
  msgid "Twitter Photo Cards have been deprecated. Your site now uses Summary Cards when applicable."
55
  msgstr ""
56
 
57
- #: bootstrap/upgrade.php:624
58
  msgid "The previous sitemap timestamp settings have been converted into new global timestamp settings."
59
  msgstr ""
60
 
61
- #: bootstrap/upgrade.php:719
62
  msgid "A cronjob is now used to ping search engines, and it alerts them to changes in your sitemap."
63
  msgstr ""
64
 
65
- #: bootstrap/upgrade.php:732
66
  msgid "The positions in the \"Meta Title Additions Location\" setting for the homepage have been reversed, left to right, but the output has not been changed. If you must downgrade for some reason, remember to switch the location back again."
67
  msgstr ""
68
 
69
- #: inc/classes/admin-init.class.php:79
70
  msgid "No Search"
71
  msgstr ""
72
 
73
- #: inc/classes/admin-init.class.php:82
74
  msgid "No Archive"
75
  msgstr ""
76
 
77
- #: inc/classes/admin-init.class.php:322
78
  msgid "There's no content."
79
  msgstr ""
80
 
81
- #: inc/classes/admin-init.class.php:323
82
  msgid "It's too short and it should have more information."
83
  msgstr ""
84
 
85
- #: inc/classes/admin-init.class.php:324
86
  msgid "It's short and it could have more information."
87
  msgstr ""
88
 
89
- #: inc/classes/admin-init.class.php:325
90
  msgid "It's long and it might get truncated in search."
91
  msgstr ""
92
 
93
- #: inc/classes/admin-init.class.php:326
94
  msgid "It's too long and it will get truncated in search."
95
  msgstr ""
96
 
97
- #: inc/classes/admin-init.class.php:327
98
  msgid "Length is good."
99
  msgstr ""
100
 
101
- #: inc/classes/admin-init.class.php:330
102
  msgctxt "The string is empty"
103
  msgid "Empty"
104
  msgstr ""
105
 
106
- #: inc/classes/admin-init.class.php:331
107
  msgid "Far too short"
108
  msgstr ""
109
 
110
- #: inc/classes/admin-init.class.php:332
111
  msgid "Too short"
112
  msgstr ""
113
 
114
- #: inc/classes/admin-init.class.php:333
115
  msgid "Too long"
116
  msgstr ""
117
 
118
- #: inc/classes/admin-init.class.php:334
119
  msgid "Far too long"
120
  msgstr ""
121
 
122
- #: inc/classes/admin-init.class.php:335
123
  msgid "Good"
124
  msgstr ""
125
 
126
- #: inc/classes/admin-init.class.php:338
127
  msgctxt "The string is empty"
128
  msgid "Empty."
129
  msgstr ""
130
 
131
- #: inc/classes/admin-init.class.php:339
132
  msgid "Far too short."
133
  msgstr ""
134
 
135
- #: inc/classes/admin-init.class.php:340
136
  msgid "Too short."
137
  msgstr ""
138
 
139
- #: inc/classes/admin-init.class.php:341
140
  msgid "Too long."
141
  msgstr ""
142
 
143
- #: inc/classes/admin-init.class.php:342
144
  msgid "Far too long."
145
  msgstr ""
146
 
147
- #: inc/classes/admin-init.class.php:343
148
  msgid "Good."
149
  msgstr ""
150
 
151
  #. translators: %s = Redirect URL markdown
152
- #: inc/classes/admin-init.class.php:442
153
  msgid "There has been an error redirecting. Refresh the page or follow [this link](%s)."
154
  msgstr ""
155
 
@@ -181,51 +181,45 @@ msgstr ""
181
  msgid "Multiple SEO tools have been detected. You should only use one."
182
  msgstr ""
183
 
184
- #: inc/classes/admin-pages.class.php:1062
185
- #: inc/classes/bridges/scripts.class.php:616
186
- msgctxt "Button hover"
187
- msgid "Select social image"
188
- msgstr ""
189
-
190
- #: inc/classes/admin-pages.class.php:1073
191
  #: inc/classes/bridges/scripts.class.php:615
192
  msgid "Select Image"
193
  msgstr ""
194
 
195
- #: inc/classes/admin-pages.class.php:1119
196
  #: inc/classes/bridges/scripts.class.php:624
197
  msgid "Select Logo"
198
  msgstr ""
199
 
200
- #: inc/classes/admin-pages.class.php:1226
201
  msgid "Click to change the counter type"
202
  msgstr ""
203
 
204
  #. translators: %s = number
205
- #: inc/classes/admin-pages.class.php:1229
206
  msgid "Characters: %s"
207
  msgstr ""
208
 
209
- #: inc/classes/bridges/feed.class.php:191
210
  msgctxt "The content source"
211
  msgid "Source"
212
  msgstr ""
213
 
214
- #: inc/classes/bridges/postsettings.class.php:74
215
- #: inc/classes/bridges/postsettings.class.php:82
216
  msgid "Homepage SEO Settings"
217
  msgstr ""
218
 
219
- #: inc/classes/bridges/postsettings.class.php:76
220
  msgid "The SEO Settings may take precedence over these settings."
221
  msgstr ""
222
 
223
  #. translators: %s = Post Type label
224
- #: inc/classes/bridges/postsettings.class.php:86
225
  msgid "%s SEO Settings"
226
  msgstr ""
227
 
228
- #: inc/classes/bridges/scripts.class.php:355
229
  msgid "The changes you made will be lost if you navigate away from this page."
230
  msgstr ""
231
 
@@ -237,10 +231,6 @@ msgstr ""
237
  msgid "Remove Image"
238
  msgstr ""
239
 
240
- #: inc/classes/bridges/scripts.class.php:619
241
- msgid "Remove selected social image"
242
- msgstr ""
243
-
244
  #: inc/classes/bridges/scripts.class.php:620
245
  msgctxt "Frame title"
246
  msgid "Select Social Image"
@@ -259,10 +249,6 @@ msgstr ""
259
  msgid "Remove Logo"
260
  msgstr ""
261
 
262
- #: inc/classes/bridges/scripts.class.php:628
263
- msgid "Unset selected logo"
264
- msgstr ""
265
-
266
  #: inc/classes/bridges/scripts.class.php:629
267
  msgctxt "Frame title"
268
  msgid "Select Logo"
@@ -288,46 +274,46 @@ msgstr ""
288
  msgid "%1$d out of %2$d pixels are used."
289
  msgstr ""
290
 
291
- #: inc/classes/bridges/seosettings.class.php:73
292
  msgid "General Settings"
293
  msgstr ""
294
 
295
- #: inc/classes/bridges/seosettings.class.php:84
296
  msgid "Title Settings"
297
  msgstr ""
298
 
299
- #: inc/classes/bridges/seosettings.class.php:95
300
  msgid "Description Meta Settings"
301
  msgstr ""
302
 
303
- #: inc/classes/bridges/seosettings.class.php:106
304
  msgid "Homepage Settings"
305
  msgstr ""
306
 
307
- #: inc/classes/bridges/seosettings.class.php:117
308
  msgid "Social Meta Settings"
309
  msgstr ""
310
 
311
- #: inc/classes/bridges/seosettings.class.php:128
312
  msgid "Schema.org Settings"
313
  msgstr ""
314
 
315
- #: inc/classes/bridges/seosettings.class.php:139
316
  #: inc/views/admin/metaboxes/homepage-metabox.php:438
317
- #: inc/views/edit/seo-settings-singular.php:286
318
  #: inc/views/edit/seo-settings-tt.php:320
319
  msgid "Robots Meta Settings"
320
  msgstr ""
321
 
322
- #: inc/classes/bridges/seosettings.class.php:150
323
  msgid "Webmaster Meta Settings"
324
  msgstr ""
325
 
326
- #: inc/classes/bridges/seosettings.class.php:161
327
  msgid "Sitemap Settings"
328
  msgstr ""
329
 
330
- #: inc/classes/bridges/seosettings.class.php:172
331
  msgid "Feed Settings"
332
  msgstr ""
333
 
@@ -640,7 +626,7 @@ msgstr ""
640
  #: inc/classes/builders/seobar-term.class.php:556
641
  #: inc/classes/builders/seobar-term.class.php:565
642
  #: inc/views/admin/metaboxes/robots-metabox.php:65
643
- #: inc/views/edit/seo-settings-singular.php:239
644
  #: inc/views/edit/seo-settings-tt.php:58
645
  #: inc/views/list/bulk-post.php:20
646
  #: inc/views/list/quick-post.php:20
@@ -772,7 +758,7 @@ msgstr ""
772
  #: inc/classes/builders/seobar-term.class.php:819
773
  #: inc/classes/builders/seobar-term.class.php:828
774
  #: inc/views/admin/metaboxes/robots-metabox.php:87
775
- #: inc/views/edit/seo-settings-singular.php:255
776
  #: inc/views/edit/seo-settings-tt.php:84
777
  #: inc/views/list/bulk-post.php:34
778
  #: inc/views/list/quick-post.php:34
@@ -964,72 +950,77 @@ msgid "Term redirects visitors."
964
  msgstr ""
965
 
966
  #. translators: %s = timestamp
967
- #: inc/classes/builders/sitemap-base.class.php:73
 
 
 
 
 
968
  msgid "Sitemap is generated on %s"
969
  msgstr ""
970
 
971
- #: inc/classes/core.class.php:280
972
  msgid "Settings"
973
  msgstr ""
974
 
975
- #: inc/classes/core.class.php:287
976
  msgctxt "Plugin extensions"
977
  msgid "Extensions"
978
  msgstr ""
979
 
980
- #: inc/classes/core.class.php:292
981
  msgctxt "Plugin pricing"
982
  msgid "Pricing"
983
  msgstr ""
984
 
985
- #: inc/classes/core.class.php:325
986
  msgid "Get support"
987
  msgstr ""
988
 
989
- #: inc/classes/core.class.php:332
990
  msgid "View documentation"
991
  msgstr ""
992
 
993
- #: inc/classes/core.class.php:339
994
  msgid "View API docs"
995
  msgstr ""
996
 
997
- #: inc/classes/core.class.php:346
998
  msgctxt "Extension Manager is a product name; do not translate it."
999
  msgid "Get Extension Manager"
1000
  msgstr ""
1001
 
1002
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification, 4: Replacement function
1003
- #: inc/classes/debug.class.php:148
1004
  msgid "%1$s is %2$s since version %3$s of The SEO Framework! Use %4$s instead."
1005
  msgstr ""
1006
 
1007
- #: inc/classes/debug.class.php:150
1008
- #: inc/classes/debug.class.php:159
1009
  msgid "deprecated"
1010
  msgstr ""
1011
 
1012
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification
1013
- #: inc/classes/debug.class.php:157
1014
  msgid "%1$s is %2$s since version %3$s of The SEO Framework with no alternative available."
1015
  msgstr ""
1016
 
1017
  #. translators: 1: plugin version
1018
- #: inc/classes/debug.class.php:210
1019
  msgid "(This message was added in version %s of The SEO Framework.)"
1020
  msgstr ""
1021
 
1022
  #. translators: 1: Function name, 2: 'Incorrectly', 3: Error message 4: Plugin Version notification
1023
- #: inc/classes/debug.class.php:214
1024
  msgid "%1$s was called %2$s. %3$s %4$s"
1025
  msgstr ""
1026
 
1027
- #: inc/classes/debug.class.php:216
1028
  msgid "incorrectly"
1029
  msgstr ""
1030
 
1031
  #. translators: 1: Method or Property name, 2: The SEO Framework class. 3: Message
1032
- #: inc/classes/debug.class.php:264
1033
  msgid "%1$s is not accessible in %2$s. %3$s"
1034
  msgstr ""
1035
 
@@ -1049,35 +1040,29 @@ msgctxt "blog page description"
1049
  msgid "%1$s %2$s %3$s"
1050
  msgstr ""
1051
 
1052
- #. translators: Taxonomy term archive title. 1: Taxonomy singular name, 2: Current taxonomy term
1053
- #: inc/classes/generate-title.class.php:1271
1054
- msgctxt "tax prefix: title"
1055
- msgid "%1$s: %2$s"
1056
- msgstr ""
1057
-
1058
  #. translators: 1 = SEO Bar type title, 2 = Status reason. 3 = Assessments
1059
- #: inc/classes/interpreters/seobar.class.php:301
1060
  msgctxt "SEO Bar ARIA assessment enumeration"
1061
  msgid "%1$s: %2$s %3$s"
1062
  msgstr ""
1063
 
1064
  #. translators: 1 = Assessment number (mind the %d (D)), 2 = Assessment explanation
1065
- #: inc/classes/interpreters/seobar.class.php:345
1066
  msgctxt "assessment enumeration"
1067
  msgid "%1$d: %2$s"
1068
  msgstr ""
1069
 
1070
  #. translators: 1 = 'Assessment(s)', 2 = A list of assessments.
1071
- #: inc/classes/interpreters/seobar.class.php:347
1072
  msgctxt "assessment list"
1073
  msgid "%1$s: %2$s"
1074
  msgstr ""
1075
 
1076
- #: inc/classes/interpreters/seobar.class.php:348
1077
  msgid "Assessment"
1078
  msgstr ""
1079
 
1080
- #: inc/classes/interpreters/seobar.class.php:349
1081
  msgid "Assessments"
1082
  msgstr ""
1083
 
@@ -1101,7 +1086,7 @@ msgctxt "Twitter @username"
1101
  msgid "@your-personal-username"
1102
  msgstr ""
1103
 
1104
- #: inc/classes/render.class.php:1148
1105
  msgid "by Sybre Waaijer"
1106
  msgstr ""
1107
 
@@ -1111,7 +1096,7 @@ msgstr ""
1111
 
1112
  #: inc/views/admin/metaboxes/description-metabox.php:21
1113
  #: inc/views/admin/metaboxes/homepage-metabox.php:141
1114
- #: inc/views/edit/seo-settings-singular.php:190
1115
  #: inc/views/edit/seo-settings-tt.php:169
1116
  msgid "The meta description can be used to determine the text used under the title on search engine results pages."
1117
  msgstr ""
@@ -1320,7 +1305,7 @@ msgid "To improve performance, generated output can be stored in the database as
1320
  msgstr ""
1321
 
1322
  #: inc/views/admin/metaboxes/general-metabox.php:253
1323
- msgid "Enable sitemap generation cache?"
1324
  msgstr ""
1325
 
1326
  #: inc/views/admin/metaboxes/general-metabox.php:254
@@ -1470,13 +1455,13 @@ msgstr ""
1470
  #: inc/views/admin/metaboxes/robots-metabox.php:59
1471
  #: inc/views/admin/metaboxes/sitemaps-metabox.php:21
1472
  #: inc/views/admin/metaboxes/social-metabox.php:21
1473
- #: inc/views/admin/metaboxes/title-metabox.php:91
1474
  #: inc/views/edit/seo-settings-singular.php:29
1475
  msgid "General"
1476
  msgstr ""
1477
 
1478
  #: inc/views/admin/metaboxes/homepage-metabox.php:38
1479
- #: inc/views/admin/metaboxes/title-metabox.php:96
1480
  msgid "Additions"
1481
  msgstr ""
1482
 
@@ -1490,7 +1475,7 @@ msgid "Robots"
1490
  msgstr ""
1491
 
1492
  #: inc/views/admin/metaboxes/homepage-metabox.php:70
1493
- #: inc/views/edit/seo-settings-singular.php:116
1494
  #: inc/views/edit/seo-settings-tt.php:114
1495
  #: inc/views/list/quick-post.php:58
1496
  #: inc/views/list/quick-term.php:58
@@ -1498,7 +1483,7 @@ msgid "Meta Title"
1498
  msgstr ""
1499
 
1500
  #: inc/views/admin/metaboxes/homepage-metabox.php:74
1501
- #: inc/views/edit/seo-settings-singular.php:120
1502
  #: inc/views/edit/seo-settings-tt.php:118
1503
  msgid "The meta title can be used to determine the title used on search engine result pages."
1504
  msgstr ""
@@ -1520,7 +1505,7 @@ msgid "A plugin has been detected that suggests to maintain this option on the [
1520
  msgstr ""
1521
 
1522
  #: inc/views/admin/metaboxes/homepage-metabox.php:137
1523
- #: inc/views/edit/seo-settings-singular.php:186
1524
  #: inc/views/edit/seo-settings-tt.php:165
1525
  #: inc/views/list/quick-post.php:75
1526
  #: inc/views/list/quick-term.php:75
@@ -1546,35 +1531,35 @@ msgid "Meta Title Additions Location"
1546
  msgstr ""
1547
 
1548
  #: inc/views/admin/metaboxes/homepage-metabox.php:248
1549
- #: inc/views/admin/metaboxes/title-metabox.php:204
1550
  msgid "Left:"
1551
  msgstr ""
1552
 
1553
  #: inc/views/admin/metaboxes/homepage-metabox.php:258
1554
- #: inc/views/admin/metaboxes/title-metabox.php:214
1555
  msgid "Right:"
1556
  msgstr ""
1557
 
1558
  #: inc/views/admin/metaboxes/homepage-metabox.php:284
1559
- #: inc/views/edit/seo-settings-singular.php:424
1560
  #: inc/views/edit/seo-settings-tt.php:207
1561
  msgid "Open Graph Title"
1562
  msgstr ""
1563
 
1564
  #: inc/views/admin/metaboxes/homepage-metabox.php:308
1565
- #: inc/views/edit/seo-settings-singular.php:447
1566
  #: inc/views/edit/seo-settings-tt.php:224
1567
  msgid "Open Graph Description"
1568
  msgstr ""
1569
 
1570
  #: inc/views/admin/metaboxes/homepage-metabox.php:333
1571
- #: inc/views/edit/seo-settings-singular.php:468
1572
  #: inc/views/edit/seo-settings-tt.php:239
1573
  msgid "Twitter Title"
1574
  msgstr ""
1575
 
1576
  #: inc/views/admin/metaboxes/homepage-metabox.php:357
1577
- #: inc/views/edit/seo-settings-singular.php:491
1578
  #: inc/views/edit/seo-settings-tt.php:256
1579
  msgid "Twitter Description"
1580
  msgstr ""
@@ -1589,13 +1574,13 @@ msgid "A social image can be displayed when your homepage is shared. It is a gre
1589
  msgstr ""
1590
 
1591
  #: inc/views/admin/metaboxes/homepage-metabox.php:389
1592
- #: inc/views/edit/seo-settings-singular.php:521
1593
  #: inc/views/edit/seo-settings-tt.php:271
1594
  msgid "Social Image URL"
1595
  msgstr ""
1596
 
1597
  #: inc/views/admin/metaboxes/homepage-metabox.php:392
1598
- #: inc/views/edit/seo-settings-singular.php:525
1599
  #: inc/views/edit/seo-settings-tt.php:275
1600
  msgid "The social image URL can be used by search engines and social networks alike. It's best to use an image with a 1.91:1 aspect ratio that is at least 1200px wide for universal support."
1601
  msgstr ""
@@ -1645,7 +1630,7 @@ msgid "This tells search engines not to save a cached copy of this page."
1645
  msgstr ""
1646
 
1647
  #: inc/views/admin/metaboxes/homepage-metabox.php:489
1648
- #: inc/views/edit/seo-settings-singular.php:300
1649
  msgid "Warning: No public site should ever apply \"noindex\" or \"nofollow\" to the homepage."
1650
  msgstr ""
1651
 
@@ -1979,12 +1964,12 @@ msgid "Enable logo?"
1979
  msgstr ""
1980
 
1981
  #: inc/views/admin/metaboxes/schema-metabox.php:178
1982
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:370
1983
  msgid "Logo URL"
1984
  msgstr ""
1985
 
1986
  #: inc/views/admin/metaboxes/schema-metabox.php:182
1987
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:374
1988
  msgid "Setting a logo requires JavaScript."
1989
  msgstr ""
1990
 
@@ -2068,187 +2053,204 @@ msgstr ""
2068
  msgid "Style"
2069
  msgstr ""
2070
 
2071
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:64
2072
  msgid "Sitemap Integration Settings"
2073
  msgstr ""
2074
 
2075
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:66
2076
- msgid "The sitemap is an XML file that lists indexable pages of your website along with optional metadata. This helps search engines find new and updated content more quickly."
2077
  msgstr ""
2078
 
2079
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:68
2080
- msgid "The sitemap does not contribute to ranking; it only speeds up indexing. Therefore, it is perfectly fine not having every indexable page in the sitemap."
 
2081
  msgstr ""
2082
 
2083
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:72
2084
  msgid "Note: Another active sitemap plugin has been detected. This means that the sitemap functionality has been superseded and these settings have no effect."
2085
  msgstr ""
2086
 
2087
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:75
2088
  msgid "Note: A sitemap has been detected in the root folder of your website. This means that these settings have no effect."
2089
  msgstr ""
2090
 
2091
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:80
2092
  msgid "Sitemap Output"
2093
  msgstr ""
2094
 
2095
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:87
2096
  msgid "Output optimized sitemap?"
2097
  msgstr ""
2098
 
2099
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:89
2100
- msgid "This sitemap considers the indexing state of all your posts and pages."
2101
  msgstr ""
2102
 
2103
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:104
2104
  msgid "View the base sitemap."
2105
  msgstr ""
2106
 
2107
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:114
 
 
 
 
2108
  msgid "Sitemap Query Limit"
2109
  msgstr ""
2110
 
2111
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:118
2112
- msgid "The sitemap is generated with two queries: Hierarchical and non-hierarchical post types. This setting affects how many posts are requested from the database per query. The homepage and blog page are included separately."
2113
  msgstr ""
2114
 
2115
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:134
2116
  msgid "Consider lowering this value when the sitemap shows a white screen or notifies you of memory exhaustion."
2117
  msgstr ""
2118
 
2119
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:142
2120
  msgid "Robots.txt Settings"
2121
  msgstr ""
2122
 
2123
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:147
2124
  msgid "Note: A robots.txt file has been detected in the root folder of your website. This means these settings have no effect."
2125
  msgstr ""
2126
 
2127
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:153
2128
  msgid "Note: robots.txt files can't be generated or used on subdirectory installations."
2129
  msgstr ""
2130
 
2131
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:158
2132
  msgid "Note: You're using the plain permalink structure; so, no robots.txt file can be generated."
2133
  msgstr ""
2134
 
2135
  #. translators: 1 = Link to settings, Markdown. 2 = example input, also markdown! Preserve the Markdown as-is!
2136
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:164
2137
  msgid "Change your [Permalink Settings](%1$s). Recommended structure: `%2$s`."
2138
  msgstr ""
2139
 
2140
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:176
2141
  msgid "The robots.txt output is the first thing search engines look for before crawling your site. If you add the sitemap location in that output, then search engines may automatically access and index the sitemap."
2142
  msgstr ""
2143
 
2144
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:177
2145
  msgid "If you do not add the sitemap location to the robots.txt output, you should notify search engines manually through webmaster-interfaces provided by the search engines."
2146
  msgstr ""
2147
 
2148
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:184
2149
  msgid "Sitemap Hinting"
2150
  msgstr ""
2151
 
2152
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:189
2153
  msgid "Add sitemap location to robots.txt?"
2154
  msgstr ""
2155
 
2156
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:204
2157
  msgid "View the robots.txt output."
2158
  msgstr ""
2159
 
2160
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:212
2161
  msgid "Timestamps Settings"
2162
  msgstr ""
2163
 
2164
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:214
2165
  msgid "The modified time suggests to search engines where to look for content changes first."
2166
  msgstr ""
2167
 
2168
  #. translators: the backticks are Markdown! Preserve them as-is!
2169
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:222
2170
  msgid "Add `<lastmod>` to the sitemap?"
2171
  msgstr ""
2172
 
2173
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:234
2174
  msgid "Priority Settings"
2175
  msgstr ""
2176
 
2177
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:236
2178
  msgid "The priority index suggests to search engines which pages are deemed more important. It has no known impact on the SEO value and it is generally ignored."
2179
  msgstr ""
2180
 
2181
  #. translators: the backticks are Markdown! Preserve them as-is!
2182
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:244
2183
- msgid "Add `<priority>` to the sitemap?"
2184
  msgstr ""
2185
 
2186
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:256
2187
  msgid "Ping Settings"
2188
  msgstr ""
2189
 
2190
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:258
2191
  msgid "Notifying search engines of a sitemap change is helpful to get your content indexed as soon as possible."
2192
  msgstr ""
2193
 
2194
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:259
2195
  msgid "By default this will happen at most once an hour."
2196
  msgstr ""
2197
 
2198
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:264
2199
  msgid "Use cron for pinging?"
2200
  msgstr ""
2201
 
2202
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:266
2203
  msgid "This speeds up post and term saving processes, by offsetting pinging to a later time."
2204
  msgstr ""
2205
 
2206
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:279
 
 
 
 
 
 
 
 
 
 
 
 
2207
  msgid "Notify Search Engines"
2208
  msgstr ""
2209
 
2210
  #. translators: %s = Google
2211
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:291
2212
  msgid "Notify %s about sitemap changes?"
2213
  msgstr ""
2214
 
2215
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:301
2216
- msgid "Sitemap Styling Settings"
2217
  msgstr ""
2218
 
2219
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:303
2220
  msgid "You can style the optimized sitemap to give it a more personal look for your visitors. Search engines do not use these styles."
2221
  msgstr ""
2222
 
2223
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:304
2224
  msgid "Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes."
2225
  msgstr ""
2226
 
2227
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:308
2228
  msgid "Enable Styling"
2229
  msgstr ""
2230
 
2231
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:314
2232
  msgid "Style sitemap?"
2233
  msgstr ""
2234
 
2235
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:314
2236
  msgid "This makes the sitemap more readable for humans."
2237
  msgstr ""
2238
 
2239
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:331
2240
  msgid "Sitemap Header Background Color"
2241
  msgstr ""
2242
 
2243
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:340
2244
  msgid "Sitemap Title and Lines Color"
2245
  msgstr ""
2246
 
2247
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:349
2248
  msgid "Header Title Logo"
2249
  msgstr ""
2250
 
2251
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:355
2252
  msgid "Show logo next to sitemap header title?"
2253
  msgstr ""
2254
 
@@ -2527,7 +2529,7 @@ msgid "Example Category"
2527
  msgstr ""
2528
 
2529
  #: inc/views/admin/metaboxes/title-metabox.php:49
2530
- #: inc/views/admin/metaboxes/title-metabox.php:162
2531
  msgid "Automated Title Settings"
2532
  msgstr ""
2533
 
@@ -2543,80 +2545,90 @@ msgstr ""
2543
  msgid "Example Archive Title Output"
2544
  msgstr ""
2545
 
2546
- #: inc/views/admin/metaboxes/title-metabox.php:107
 
 
 
 
 
 
 
 
 
 
2547
  msgid "Prefixes"
2548
  msgstr ""
2549
 
2550
- #: inc/views/admin/metaboxes/title-metabox.php:135
2551
  msgid "Title Separator"
2552
  msgstr ""
2553
 
2554
- #: inc/views/admin/metaboxes/title-metabox.php:138
2555
  msgid "If the title consists of multiple parts, then the separator will go in-between them."
2556
  msgstr ""
2557
 
2558
- #: inc/views/admin/metaboxes/title-metabox.php:164
2559
  msgid "A title is generated for every page."
2560
  msgstr ""
2561
 
2562
- #: inc/views/admin/metaboxes/title-metabox.php:165
2563
  msgid "Some titles may have HTML tags inserted by the author for styling."
2564
  msgstr ""
2565
 
2566
  #. translators: %s = HTML tag example
2567
- #: inc/views/admin/metaboxes/title-metabox.php:170
2568
  msgid "This strips HTML tags, like %s, from the title. Disable this option to display generated HTML tags as plain text in meta titles."
2569
  msgstr ""
2570
 
2571
- #: inc/views/admin/metaboxes/title-metabox.php:179
2572
  msgid "Strip HTML tags from generated titles?"
2573
  msgstr ""
2574
 
2575
- #: inc/views/admin/metaboxes/title-metabox.php:186
2576
  msgid "Tip: It is a bad practice to style page titles with HTML as inconsistent behavior might occur."
2577
  msgstr ""
2578
 
2579
- #: inc/views/admin/metaboxes/title-metabox.php:193
2580
  msgid "This option does not affect the homepage; it uses a different one."
2581
  msgstr ""
2582
 
2583
- #: inc/views/admin/metaboxes/title-metabox.php:198
2584
  msgid "Site Title Location"
2585
  msgstr ""
2586
 
2587
- #: inc/views/admin/metaboxes/title-metabox.php:227
2588
  msgid "Site Title"
2589
  msgstr ""
2590
 
2591
- #: inc/views/admin/metaboxes/title-metabox.php:231
2592
  msgid "Always brand your titles. Search engines may ignore your titles with this feature enabled."
2593
  msgstr ""
2594
 
2595
- #: inc/views/admin/metaboxes/title-metabox.php:239
2596
  msgid "Remove site title from the title?"
2597
  msgstr ""
2598
 
2599
- #: inc/views/admin/metaboxes/title-metabox.php:248
2600
  msgid "Note: Only use this option if you are aware of its SEO effects."
2601
  msgstr ""
2602
 
2603
- #: inc/views/admin/metaboxes/title-metabox.php:255
2604
  msgid "Title Prefix Options"
2605
  msgstr ""
2606
 
2607
- #: inc/views/admin/metaboxes/title-metabox.php:257
2608
  msgid "For archives, a descriptive prefix may be added to generated titles."
2609
  msgstr ""
2610
 
2611
- #: inc/views/admin/metaboxes/title-metabox.php:262
2612
  msgid "Archive Title Prefixes"
2613
  msgstr ""
2614
 
2615
- #: inc/views/admin/metaboxes/title-metabox.php:266
2616
  msgid "The prefix helps visitors and search engines determine what kind of page they're visiting."
2617
  msgstr ""
2618
 
2619
- #: inc/views/admin/metaboxes/title-metabox.php:273
2620
  msgid "Remove term type prefixes from generated archive titles?"
2621
  msgstr ""
2622
 
@@ -2694,77 +2706,79 @@ msgstr ""
2694
  msgid "Doing it Right"
2695
  msgstr ""
2696
 
2697
- #: inc/views/edit/seo-settings-singular.php:165
2698
- #: inc/views/edit/seo-settings-singular.php:172
2699
  #: inc/views/edit/seo-settings-tt.php:154
2700
  msgid "Remove the site title?"
2701
  msgstr ""
2702
 
2703
- #: inc/views/edit/seo-settings-singular.php:167
2704
  msgid "For the homepage, this option must be managed on the SEO Settings page."
2705
  msgstr ""
2706
 
2707
- #: inc/views/edit/seo-settings-singular.php:174
2708
  #: inc/views/edit/seo-settings-tt.php:156
2709
  msgid "Use this when you want to rearrange the title parts manually."
2710
  msgstr ""
2711
 
2712
- #: inc/views/edit/seo-settings-singular.php:247
2713
  #: inc/views/edit/seo-settings-tt.php:71
2714
  #: inc/views/list/bulk-post.php:27
2715
  #: inc/views/list/quick-post.php:27
2716
  msgid "Link following"
2717
  msgstr ""
2718
 
2719
- #: inc/views/edit/seo-settings-singular.php:265
2720
  #: inc/views/edit/seo-settings-tt.php:302
2721
  #: inc/views/list/quick-post.php:95
2722
  #: inc/views/list/quick-term.php:95
2723
  msgid "Canonical URL"
2724
  msgstr ""
2725
 
2726
- #: inc/views/edit/seo-settings-singular.php:269
2727
  #: inc/views/edit/seo-settings-tt.php:306
2728
  msgid "This urges search engines to go to the outputted URL."
2729
  msgstr ""
2730
 
2731
- #: inc/views/edit/seo-settings-singular.php:290
2732
  msgid "These directives may urge robots not to display, follow links on, or create a cached copy of this page."
2733
  msgstr ""
2734
 
2735
- #: inc/views/edit/seo-settings-singular.php:304
2736
  msgid "Note: A non-default selection here will overwrite the global homepage SEO settings."
2737
  msgstr ""
2738
 
2739
  #. translators: %s = default option value
2740
- #: inc/views/edit/seo-settings-singular.php:332
 
2741
  #: inc/views/edit/seo-settings-tt.php:339
 
2742
  #: inc/views/list/quick-post.php:112
2743
  #: inc/views/list/quick-term.php:112
2744
  msgid "Default (%s)"
2745
  msgstr ""
2746
 
2747
- #: inc/views/edit/seo-settings-singular.php:358
2748
  msgid "Archive Settings"
2749
  msgstr ""
2750
 
2751
- #: inc/views/edit/seo-settings-singular.php:367
2752
  msgid "Exclude this page from all search queries on this site."
2753
  msgstr ""
2754
 
2755
- #: inc/views/edit/seo-settings-singular.php:376
2756
  msgid "Exclude this page from all archive queries on this site."
2757
  msgstr ""
2758
 
2759
- #: inc/views/edit/seo-settings-singular.php:390
2760
- #: inc/views/edit/seo-settings-tt.php:355
2761
  #: inc/views/list/quick-post.php:125
2762
  #: inc/views/list/quick-term.php:125
2763
  msgid "301 Redirect URL"
2764
  msgstr ""
2765
 
2766
- #: inc/views/edit/seo-settings-singular.php:395
2767
- #: inc/views/edit/seo-settings-tt.php:359
2768
  msgid "This will force visitors to go to another URL."
2769
  msgstr ""
2770
 
@@ -2813,11 +2827,11 @@ msgstr ""
2813
  msgid "This may be shown publicly."
2814
  msgstr ""
2815
 
2816
- #: inc/views/sitemap/xml-sitemap.php:55
2817
  msgid "Sitemap is generated for this view"
2818
  msgstr ""
2819
 
2820
- #: inc/views/sitemap/xml-sitemap.php:57
2821
  msgid "Sitemap is served from cache"
2822
  msgstr ""
2823
 
2
  # This file is distributed under the same license as the The SEO Framework plugin.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: The SEO Framework 4.1.2\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/trunk\n"
7
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
8
  "Language-Team: LANGUAGE <LL@li.org>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
+ "POT-Creation-Date: 2020-11-27T19:02:50+01:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
  "X-Generator: WP-CLI 2.4.0\n"
15
  "X-Domain: autodescription\n"
32
  msgstr ""
33
 
34
  #. translators: %1$s = New, lower version number, surrounded in markdown-backticks. %2$s = Old, higher version number, surrounded in markdown-backticks.
35
+ #: bootstrap/upgrade.php:373
36
  msgid "Your website has been downgraded successfully to use The SEO Framework at database version `%1$s` from `%2$s`."
37
  msgstr ""
38
 
39
  #. translators: %s = Version number, surrounded in markdown-backticks.
40
+ #: bootstrap/upgrade.php:421
41
  msgid "Thank you for updating The SEO Framework! Your website has been upgraded successfully to use The SEO Framework at database version `%s`."
42
  msgstr ""
43
 
44
+ #: bootstrap/upgrade.php:461
45
  msgid "The SEO Framework automatically optimizes your website for search engines and social media."
46
  msgstr ""
47
 
48
  #. translators: %s = Link, markdown.
49
+ #: bootstrap/upgrade.php:465
50
  msgid "To take full advantage of all SEO features, please follow our [5-minute setup guide](%s)."
51
  msgstr ""
52
 
53
+ #: bootstrap/upgrade.php:595
54
  msgid "Twitter Photo Cards have been deprecated. Your site now uses Summary Cards when applicable."
55
  msgstr ""
56
 
57
+ #: bootstrap/upgrade.php:620
58
  msgid "The previous sitemap timestamp settings have been converted into new global timestamp settings."
59
  msgstr ""
60
 
61
+ #: bootstrap/upgrade.php:715
62
  msgid "A cronjob is now used to ping search engines, and it alerts them to changes in your sitemap."
63
  msgstr ""
64
 
65
+ #: bootstrap/upgrade.php:728
66
  msgid "The positions in the \"Meta Title Additions Location\" setting for the homepage have been reversed, left to right, but the output has not been changed. If you must downgrade for some reason, remember to switch the location back again."
67
  msgstr ""
68
 
69
+ #: inc/classes/admin-init.class.php:80
70
  msgid "No Search"
71
  msgstr ""
72
 
73
+ #: inc/classes/admin-init.class.php:83
74
  msgid "No Archive"
75
  msgstr ""
76
 
77
+ #: inc/classes/admin-init.class.php:314
78
  msgid "There's no content."
79
  msgstr ""
80
 
81
+ #: inc/classes/admin-init.class.php:315
82
  msgid "It's too short and it should have more information."
83
  msgstr ""
84
 
85
+ #: inc/classes/admin-init.class.php:316
86
  msgid "It's short and it could have more information."
87
  msgstr ""
88
 
89
+ #: inc/classes/admin-init.class.php:317
90
  msgid "It's long and it might get truncated in search."
91
  msgstr ""
92
 
93
+ #: inc/classes/admin-init.class.php:318
94
  msgid "It's too long and it will get truncated in search."
95
  msgstr ""
96
 
97
+ #: inc/classes/admin-init.class.php:319
98
  msgid "Length is good."
99
  msgstr ""
100
 
101
+ #: inc/classes/admin-init.class.php:322
102
  msgctxt "The string is empty"
103
  msgid "Empty"
104
  msgstr ""
105
 
106
+ #: inc/classes/admin-init.class.php:323
107
  msgid "Far too short"
108
  msgstr ""
109
 
110
+ #: inc/classes/admin-init.class.php:324
111
  msgid "Too short"
112
  msgstr ""
113
 
114
+ #: inc/classes/admin-init.class.php:325
115
  msgid "Too long"
116
  msgstr ""
117
 
118
+ #: inc/classes/admin-init.class.php:326
119
  msgid "Far too long"
120
  msgstr ""
121
 
122
+ #: inc/classes/admin-init.class.php:327
123
  msgid "Good"
124
  msgstr ""
125
 
126
+ #: inc/classes/admin-init.class.php:330
127
  msgctxt "The string is empty"
128
  msgid "Empty."
129
  msgstr ""
130
 
131
+ #: inc/classes/admin-init.class.php:331
132
  msgid "Far too short."
133
  msgstr ""
134
 
135
+ #: inc/classes/admin-init.class.php:332
136
  msgid "Too short."
137
  msgstr ""
138
 
139
+ #: inc/classes/admin-init.class.php:333
140
  msgid "Too long."
141
  msgstr ""
142
 
143
+ #: inc/classes/admin-init.class.php:334
144
  msgid "Far too long."
145
  msgstr ""
146
 
147
+ #: inc/classes/admin-init.class.php:335
148
  msgid "Good."
149
  msgstr ""
150
 
151
  #. translators: %s = Redirect URL markdown
152
+ #: inc/classes/admin-init.class.php:434
153
  msgid "There has been an error redirecting. Refresh the page or follow [this link](%s)."
154
  msgstr ""
155
 
181
  msgid "Multiple SEO tools have been detected. You should only use one."
182
  msgstr ""
183
 
184
+ #: inc/classes/admin-pages.class.php:1085
 
 
 
 
 
 
185
  #: inc/classes/bridges/scripts.class.php:615
186
  msgid "Select Image"
187
  msgstr ""
188
 
189
+ #: inc/classes/admin-pages.class.php:1131
190
  #: inc/classes/bridges/scripts.class.php:624
191
  msgid "Select Logo"
192
  msgstr ""
193
 
194
+ #: inc/classes/admin-pages.class.php:1242
195
  msgid "Click to change the counter type"
196
  msgstr ""
197
 
198
  #. translators: %s = number
199
+ #: inc/classes/admin-pages.class.php:1245
200
  msgid "Characters: %s"
201
  msgstr ""
202
 
203
+ #: inc/classes/bridges/feed.class.php:190
204
  msgctxt "The content source"
205
  msgid "Source"
206
  msgstr ""
207
 
208
+ #: inc/classes/bridges/postsettings.class.php:73
209
+ #: inc/classes/bridges/postsettings.class.php:81
210
  msgid "Homepage SEO Settings"
211
  msgstr ""
212
 
213
+ #: inc/classes/bridges/postsettings.class.php:75
214
  msgid "The SEO Settings may take precedence over these settings."
215
  msgstr ""
216
 
217
  #. translators: %s = Post Type label
218
+ #: inc/classes/bridges/postsettings.class.php:85
219
  msgid "%s SEO Settings"
220
  msgstr ""
221
 
222
+ #: inc/classes/bridges/scripts.class.php:354
223
  msgid "The changes you made will be lost if you navigate away from this page."
224
  msgstr ""
225
 
231
  msgid "Remove Image"
232
  msgstr ""
233
 
 
 
 
 
234
  #: inc/classes/bridges/scripts.class.php:620
235
  msgctxt "Frame title"
236
  msgid "Select Social Image"
249
  msgid "Remove Logo"
250
  msgstr ""
251
 
 
 
 
 
252
  #: inc/classes/bridges/scripts.class.php:629
253
  msgctxt "Frame title"
254
  msgid "Select Logo"
274
  msgid "%1$d out of %2$d pixels are used."
275
  msgstr ""
276
 
277
+ #: inc/classes/bridges/seosettings.class.php:72
278
  msgid "General Settings"
279
  msgstr ""
280
 
281
+ #: inc/classes/bridges/seosettings.class.php:83
282
  msgid "Title Settings"
283
  msgstr ""
284
 
285
+ #: inc/classes/bridges/seosettings.class.php:94
286
  msgid "Description Meta Settings"
287
  msgstr ""
288
 
289
+ #: inc/classes/bridges/seosettings.class.php:105
290
  msgid "Homepage Settings"
291
  msgstr ""
292
 
293
+ #: inc/classes/bridges/seosettings.class.php:116
294
  msgid "Social Meta Settings"
295
  msgstr ""
296
 
297
+ #: inc/classes/bridges/seosettings.class.php:127
298
  msgid "Schema.org Settings"
299
  msgstr ""
300
 
301
+ #: inc/classes/bridges/seosettings.class.php:138
302
  #: inc/views/admin/metaboxes/homepage-metabox.php:438
303
+ #: inc/views/edit/seo-settings-singular.php:289
304
  #: inc/views/edit/seo-settings-tt.php:320
305
  msgid "Robots Meta Settings"
306
  msgstr ""
307
 
308
+ #: inc/classes/bridges/seosettings.class.php:149
309
  msgid "Webmaster Meta Settings"
310
  msgstr ""
311
 
312
+ #: inc/classes/bridges/seosettings.class.php:160
313
  msgid "Sitemap Settings"
314
  msgstr ""
315
 
316
+ #: inc/classes/bridges/seosettings.class.php:171
317
  msgid "Feed Settings"
318
  msgstr ""
319
 
626
  #: inc/classes/builders/seobar-term.class.php:556
627
  #: inc/classes/builders/seobar-term.class.php:565
628
  #: inc/views/admin/metaboxes/robots-metabox.php:65
629
+ #: inc/views/edit/seo-settings-singular.php:242
630
  #: inc/views/edit/seo-settings-tt.php:58
631
  #: inc/views/list/bulk-post.php:20
632
  #: inc/views/list/quick-post.php:20
758
  #: inc/classes/builders/seobar-term.class.php:819
759
  #: inc/classes/builders/seobar-term.class.php:828
760
  #: inc/views/admin/metaboxes/robots-metabox.php:87
761
+ #: inc/views/edit/seo-settings-singular.php:258
762
  #: inc/views/edit/seo-settings-tt.php:84
763
  #: inc/views/list/bulk-post.php:34
764
  #: inc/views/list/quick-post.php:34
950
  msgstr ""
951
 
952
  #. translators: %s = timestamp
953
+ #: inc/classes/builders/sitemap-base.class.php:174
954
+ msgid "Sitemap is prerendered on %s"
955
+ msgstr ""
956
+
957
+ #. translators: %s = timestamp
958
+ #: inc/classes/builders/sitemap-base.class.php:176
959
  msgid "Sitemap is generated on %s"
960
  msgstr ""
961
 
962
+ #: inc/classes/core.class.php:279
963
  msgid "Settings"
964
  msgstr ""
965
 
966
+ #: inc/classes/core.class.php:286
967
  msgctxt "Plugin extensions"
968
  msgid "Extensions"
969
  msgstr ""
970
 
971
+ #: inc/classes/core.class.php:291
972
  msgctxt "Plugin pricing"
973
  msgid "Pricing"
974
  msgstr ""
975
 
976
+ #: inc/classes/core.class.php:324
977
  msgid "Get support"
978
  msgstr ""
979
 
980
+ #: inc/classes/core.class.php:331
981
  msgid "View documentation"
982
  msgstr ""
983
 
984
+ #: inc/classes/core.class.php:338
985
  msgid "View API docs"
986
  msgstr ""
987
 
988
+ #: inc/classes/core.class.php:345
989
  msgctxt "Extension Manager is a product name; do not translate it."
990
  msgid "Get Extension Manager"
991
  msgstr ""
992
 
993
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification, 4: Replacement function
994
+ #: inc/classes/debug.class.php:147
995
  msgid "%1$s is %2$s since version %3$s of The SEO Framework! Use %4$s instead."
996
  msgstr ""
997
 
998
+ #: inc/classes/debug.class.php:149
999
+ #: inc/classes/debug.class.php:158
1000
  msgid "deprecated"
1001
  msgstr ""
1002
 
1003
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification
1004
+ #: inc/classes/debug.class.php:156
1005
  msgid "%1$s is %2$s since version %3$s of The SEO Framework with no alternative available."
1006
  msgstr ""
1007
 
1008
  #. translators: 1: plugin version
1009
+ #: inc/classes/debug.class.php:209
1010
  msgid "(This message was added in version %s of The SEO Framework.)"
1011
  msgstr ""
1012
 
1013
  #. translators: 1: Function name, 2: 'Incorrectly', 3: Error message 4: Plugin Version notification
1014
+ #: inc/classes/debug.class.php:213
1015
  msgid "%1$s was called %2$s. %3$s %4$s"
1016
  msgstr ""
1017
 
1018
+ #: inc/classes/debug.class.php:215
1019
  msgid "incorrectly"
1020
  msgstr ""
1021
 
1022
  #. translators: 1: Method or Property name, 2: The SEO Framework class. 3: Message
1023
+ #: inc/classes/debug.class.php:263
1024
  msgid "%1$s is not accessible in %2$s. %3$s"
1025
  msgstr ""
1026
 
1040
  msgid "%1$s %2$s %3$s"
1041
  msgstr ""
1042
 
 
 
 
 
 
 
1043
  #. translators: 1 = SEO Bar type title, 2 = Status reason. 3 = Assessments
1044
+ #: inc/classes/interpreters/seobar.class.php:300
1045
  msgctxt "SEO Bar ARIA assessment enumeration"
1046
  msgid "%1$s: %2$s %3$s"
1047
  msgstr ""
1048
 
1049
  #. translators: 1 = Assessment number (mind the %d (D)), 2 = Assessment explanation
1050
+ #: inc/classes/interpreters/seobar.class.php:344
1051
  msgctxt "assessment enumeration"
1052
  msgid "%1$d: %2$s"
1053
  msgstr ""
1054
 
1055
  #. translators: 1 = 'Assessment(s)', 2 = A list of assessments.
1056
+ #: inc/classes/interpreters/seobar.class.php:346
1057
  msgctxt "assessment list"
1058
  msgid "%1$s: %2$s"
1059
  msgstr ""
1060
 
1061
+ #: inc/classes/interpreters/seobar.class.php:347
1062
  msgid "Assessment"
1063
  msgstr ""
1064
 
1065
+ #: inc/classes/interpreters/seobar.class.php:348
1066
  msgid "Assessments"
1067
  msgstr ""
1068
 
1086
  msgid "@your-personal-username"
1087
  msgstr ""
1088
 
1089
+ #: inc/classes/render.class.php:1158
1090
  msgid "by Sybre Waaijer"
1091
  msgstr ""
1092
 
1096
 
1097
  #: inc/views/admin/metaboxes/description-metabox.php:21
1098
  #: inc/views/admin/metaboxes/homepage-metabox.php:141
1099
+ #: inc/views/edit/seo-settings-singular.php:193
1100
  #: inc/views/edit/seo-settings-tt.php:169
1101
  msgid "The meta description can be used to determine the text used under the title on search engine results pages."
1102
  msgstr ""
1305
  msgstr ""
1306
 
1307
  #: inc/views/admin/metaboxes/general-metabox.php:253
1308
+ msgid "Enable optimized sitemap generation cache?"
1309
  msgstr ""
1310
 
1311
  #: inc/views/admin/metaboxes/general-metabox.php:254
1455
  #: inc/views/admin/metaboxes/robots-metabox.php:59
1456
  #: inc/views/admin/metaboxes/sitemaps-metabox.php:21
1457
  #: inc/views/admin/metaboxes/social-metabox.php:21
1458
+ #: inc/views/admin/metaboxes/title-metabox.php:118
1459
  #: inc/views/edit/seo-settings-singular.php:29
1460
  msgid "General"
1461
  msgstr ""
1462
 
1463
  #: inc/views/admin/metaboxes/homepage-metabox.php:38
1464
+ #: inc/views/admin/metaboxes/title-metabox.php:123
1465
  msgid "Additions"
1466
  msgstr ""
1467
 
1475
  msgstr ""
1476
 
1477
  #: inc/views/admin/metaboxes/homepage-metabox.php:70
1478
+ #: inc/views/edit/seo-settings-singular.php:119
1479
  #: inc/views/edit/seo-settings-tt.php:114
1480
  #: inc/views/list/quick-post.php:58
1481
  #: inc/views/list/quick-term.php:58
1483
  msgstr ""
1484
 
1485
  #: inc/views/admin/metaboxes/homepage-metabox.php:74
1486
+ #: inc/views/edit/seo-settings-singular.php:123
1487
  #: inc/views/edit/seo-settings-tt.php:118
1488
  msgid "The meta title can be used to determine the title used on search engine result pages."
1489
  msgstr ""
1505
  msgstr ""
1506
 
1507
  #: inc/views/admin/metaboxes/homepage-metabox.php:137
1508
+ #: inc/views/edit/seo-settings-singular.php:189
1509
  #: inc/views/edit/seo-settings-tt.php:165
1510
  #: inc/views/list/quick-post.php:75
1511
  #: inc/views/list/quick-term.php:75
1531
  msgstr ""
1532
 
1533
  #: inc/views/admin/metaboxes/homepage-metabox.php:248
1534
+ #: inc/views/admin/metaboxes/title-metabox.php:231
1535
  msgid "Left:"
1536
  msgstr ""
1537
 
1538
  #: inc/views/admin/metaboxes/homepage-metabox.php:258
1539
+ #: inc/views/admin/metaboxes/title-metabox.php:241
1540
  msgid "Right:"
1541
  msgstr ""
1542
 
1543
  #: inc/views/admin/metaboxes/homepage-metabox.php:284
1544
+ #: inc/views/edit/seo-settings-singular.php:432
1545
  #: inc/views/edit/seo-settings-tt.php:207
1546
  msgid "Open Graph Title"
1547
  msgstr ""
1548
 
1549
  #: inc/views/admin/metaboxes/homepage-metabox.php:308
1550
+ #: inc/views/edit/seo-settings-singular.php:455
1551
  #: inc/views/edit/seo-settings-tt.php:224
1552
  msgid "Open Graph Description"
1553
  msgstr ""
1554
 
1555
  #: inc/views/admin/metaboxes/homepage-metabox.php:333
1556
+ #: inc/views/edit/seo-settings-singular.php:476
1557
  #: inc/views/edit/seo-settings-tt.php:239
1558
  msgid "Twitter Title"
1559
  msgstr ""
1560
 
1561
  #: inc/views/admin/metaboxes/homepage-metabox.php:357
1562
+ #: inc/views/edit/seo-settings-singular.php:499
1563
  #: inc/views/edit/seo-settings-tt.php:256
1564
  msgid "Twitter Description"
1565
  msgstr ""
1574
  msgstr ""
1575
 
1576
  #: inc/views/admin/metaboxes/homepage-metabox.php:389
1577
+ #: inc/views/edit/seo-settings-singular.php:534
1578
  #: inc/views/edit/seo-settings-tt.php:271
1579
  msgid "Social Image URL"
1580
  msgstr ""
1581
 
1582
  #: inc/views/admin/metaboxes/homepage-metabox.php:392
1583
+ #: inc/views/edit/seo-settings-singular.php:538
1584
  #: inc/views/edit/seo-settings-tt.php:275
1585
  msgid "The social image URL can be used by search engines and social networks alike. It's best to use an image with a 1.91:1 aspect ratio that is at least 1200px wide for universal support."
1586
  msgstr ""
1630
  msgstr ""
1631
 
1632
  #: inc/views/admin/metaboxes/homepage-metabox.php:489
1633
+ #: inc/views/edit/seo-settings-singular.php:303
1634
  msgid "Warning: No public site should ever apply \"noindex\" or \"nofollow\" to the homepage."
1635
  msgstr ""
1636
 
1964
  msgstr ""
1965
 
1966
  #: inc/views/admin/metaboxes/schema-metabox.php:178
1967
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:410
1968
  msgid "Logo URL"
1969
  msgstr ""
1970
 
1971
  #: inc/views/admin/metaboxes/schema-metabox.php:182
1972
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:414
1973
  msgid "Setting a logo requires JavaScript."
1974
  msgstr ""
1975
 
2053
  msgid "Style"
2054
  msgstr ""
2055
 
2056
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:65
2057
  msgid "Sitemap Integration Settings"
2058
  msgstr ""
2059
 
2060
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:67
2061
+ msgid "The sitemap is an XML file that lists indexable pages of your website along with optional metadata. It helps search engines find new and updated content quickly."
2062
  msgstr ""
2063
 
2064
+ #. translators: %s = Learn more URL. Markdown!
2065
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:73
2066
+ msgid "The sitemap does not contribute to ranking; [it can only help with indexing](%s). Search engines process smaller, less complicated sitemaps quicker, which shortens the time required for indexing pages."
2067
  msgstr ""
2068
 
2069
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:83
2070
  msgid "Note: Another active sitemap plugin has been detected. This means that the sitemap functionality has been superseded and these settings have no effect."
2071
  msgstr ""
2072
 
2073
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:86
2074
  msgid "Note: A sitemap has been detected in the root folder of your website. This means that these settings have no effect."
2075
  msgstr ""
2076
 
2077
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:91
2078
  msgid "Sitemap Output"
2079
  msgstr ""
2080
 
2081
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:98
2082
  msgid "Output optimized sitemap?"
2083
  msgstr ""
2084
 
2085
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:100
2086
+ msgid "This sitemap is processed quicker by search engines."
2087
  msgstr ""
2088
 
2089
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:116
2090
  msgid "View the base sitemap."
2091
  msgstr ""
2092
 
2093
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:128
2094
+ msgid "View the sitemap index."
2095
+ msgstr ""
2096
+
2097
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:139
2098
  msgid "Sitemap Query Limit"
2099
  msgstr ""
2100
 
2101
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:143
2102
+ msgid "This setting affects how many pages are requested from the database per query."
2103
  msgstr ""
2104
 
2105
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:159
2106
  msgid "Consider lowering this value when the sitemap shows a white screen or notifies you of memory exhaustion."
2107
  msgstr ""
2108
 
2109
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:167
2110
  msgid "Robots.txt Settings"
2111
  msgstr ""
2112
 
2113
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:172
2114
  msgid "Note: A robots.txt file has been detected in the root folder of your website. This means these settings have no effect."
2115
  msgstr ""
2116
 
2117
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:178
2118
  msgid "Note: robots.txt files can't be generated or used on subdirectory installations."
2119
  msgstr ""
2120
 
2121
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:183
2122
  msgid "Note: You're using the plain permalink structure; so, no robots.txt file can be generated."
2123
  msgstr ""
2124
 
2125
  #. translators: 1 = Link to settings, Markdown. 2 = example input, also markdown! Preserve the Markdown as-is!
2126
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:189
2127
  msgid "Change your [Permalink Settings](%1$s). Recommended structure: `%2$s`."
2128
  msgstr ""
2129
 
2130
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:201
2131
  msgid "The robots.txt output is the first thing search engines look for before crawling your site. If you add the sitemap location in that output, then search engines may automatically access and index the sitemap."
2132
  msgstr ""
2133
 
2134
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:202
2135
  msgid "If you do not add the sitemap location to the robots.txt output, you should notify search engines manually through webmaster-interfaces provided by the search engines."
2136
  msgstr ""
2137
 
2138
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:209
2139
  msgid "Sitemap Hinting"
2140
  msgstr ""
2141
 
2142
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:214
2143
  msgid "Add sitemap location to robots.txt?"
2144
  msgstr ""
2145
 
2146
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:229
2147
  msgid "View the robots.txt output."
2148
  msgstr ""
2149
 
2150
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:237
2151
  msgid "Timestamps Settings"
2152
  msgstr ""
2153
 
2154
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:239
2155
  msgid "The modified time suggests to search engines where to look for content changes first."
2156
  msgstr ""
2157
 
2158
  #. translators: the backticks are Markdown! Preserve them as-is!
2159
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:247
2160
  msgid "Add `<lastmod>` to the sitemap?"
2161
  msgstr ""
2162
 
2163
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:260
2164
  msgid "Priority Settings"
2165
  msgstr ""
2166
 
2167
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:262
2168
  msgid "The priority index suggests to search engines which pages are deemed more important. It has no known impact on the SEO value and it is generally ignored."
2169
  msgstr ""
2170
 
2171
  #. translators: the backticks are Markdown! Preserve them as-is!
2172
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:270
2173
+ msgid "Add `<priority>` to the optimized sitemap?"
2174
  msgstr ""
2175
 
2176
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:283
2177
  msgid "Ping Settings"
2178
  msgstr ""
2179
 
2180
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:285
2181
  msgid "Notifying search engines of a sitemap change is helpful to get your content indexed as soon as possible."
2182
  msgstr ""
2183
 
2184
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:286
2185
  msgid "By default this will happen at most once an hour."
2186
  msgstr ""
2187
 
2188
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:292
2189
  msgid "Use cron for pinging?"
2190
  msgstr ""
2191
 
2192
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:294
2193
  msgid "This speeds up post and term saving processes, by offsetting pinging to a later time."
2194
  msgstr ""
2195
 
2196
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:303
2197
+ msgid "Prerender optimized sitemap before pinging via cron?"
2198
+ msgstr ""
2199
+
2200
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:305
2201
+ msgid "This mitigates timeouts some search engines may experience when waiting for the sitemap to render. Transient caching for the sitemap must be enabled for this to work."
2202
+ msgstr ""
2203
+
2204
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:309
2205
+ msgid "Only enable prerendering when generating the sitemap takes over 60 seconds."
2206
+ msgstr ""
2207
+
2208
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:319
2209
  msgid "Notify Search Engines"
2210
  msgstr ""
2211
 
2212
  #. translators: %s = Google
2213
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:331
2214
  msgid "Notify %s about sitemap changes?"
2215
  msgstr ""
2216
 
2217
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:341
2218
+ msgid "Optimized Sitemap Styling Settings"
2219
  msgstr ""
2220
 
2221
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:343
2222
  msgid "You can style the optimized sitemap to give it a more personal look for your visitors. Search engines do not use these styles."
2223
  msgstr ""
2224
 
2225
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:344
2226
  msgid "Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes."
2227
  msgstr ""
2228
 
2229
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:348
2230
  msgid "Enable Styling"
2231
  msgstr ""
2232
 
2233
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:354
2234
  msgid "Style sitemap?"
2235
  msgstr ""
2236
 
2237
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:354
2238
  msgid "This makes the sitemap more readable for humans."
2239
  msgstr ""
2240
 
2241
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:371
2242
  msgid "Sitemap Header Background Color"
2243
  msgstr ""
2244
 
2245
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:380
2246
  msgid "Sitemap Title and Lines Color"
2247
  msgstr ""
2248
 
2249
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:389
2250
  msgid "Header Title Logo"
2251
  msgstr ""
2252
 
2253
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:395
2254
  msgid "Show logo next to sitemap header title?"
2255
  msgstr ""
2256
 
2529
  msgstr ""
2530
 
2531
  #: inc/views/admin/metaboxes/title-metabox.php:49
2532
+ #: inc/views/admin/metaboxes/title-metabox.php:189
2533
  msgid "Automated Title Settings"
2534
  msgstr ""
2535
 
2545
  msgid "Example Archive Title Output"
2546
  msgstr ""
2547
 
2548
+ #. translators: %s = title-tag
2549
+ #: inc/views/admin/metaboxes/title-metabox.php:95
2550
+ msgid "Theme %s Support Missing"
2551
+ msgstr ""
2552
+
2553
+ #. translators: 1: Extension name, 2: Extension link. Markdown!
2554
+ #: inc/views/admin/metaboxes/title-metabox.php:103
2555
+ msgid "The current theme doesn't support a feature that allows predictable output of titles. Consider installing [%1$s](%2$s) when you notice the title output in the browser-tab isn't as you have configured."
2556
+ msgstr ""
2557
+
2558
+ #: inc/views/admin/metaboxes/title-metabox.php:134
2559
  msgid "Prefixes"
2560
  msgstr ""
2561
 
2562
+ #: inc/views/admin/metaboxes/title-metabox.php:162
2563
  msgid "Title Separator"
2564
  msgstr ""
2565
 
2566
+ #: inc/views/admin/metaboxes/title-metabox.php:165
2567
  msgid "If the title consists of multiple parts, then the separator will go in-between them."
2568
  msgstr ""
2569
 
2570
+ #: inc/views/admin/metaboxes/title-metabox.php:191
2571
  msgid "A title is generated for every page."
2572
  msgstr ""
2573
 
2574
+ #: inc/views/admin/metaboxes/title-metabox.php:192
2575
  msgid "Some titles may have HTML tags inserted by the author for styling."
2576
  msgstr ""
2577
 
2578
  #. translators: %s = HTML tag example
2579
+ #: inc/views/admin/metaboxes/title-metabox.php:197
2580
  msgid "This strips HTML tags, like %s, from the title. Disable this option to display generated HTML tags as plain text in meta titles."
2581
  msgstr ""
2582
 
2583
+ #: inc/views/admin/metaboxes/title-metabox.php:206
2584
  msgid "Strip HTML tags from generated titles?"
2585
  msgstr ""
2586
 
2587
+ #: inc/views/admin/metaboxes/title-metabox.php:213
2588
  msgid "Tip: It is a bad practice to style page titles with HTML as inconsistent behavior might occur."
2589
  msgstr ""
2590
 
2591
+ #: inc/views/admin/metaboxes/title-metabox.php:220
2592
  msgid "This option does not affect the homepage; it uses a different one."
2593
  msgstr ""
2594
 
2595
+ #: inc/views/admin/metaboxes/title-metabox.php:225
2596
  msgid "Site Title Location"
2597
  msgstr ""
2598
 
2599
+ #: inc/views/admin/metaboxes/title-metabox.php:254
2600
  msgid "Site Title"
2601
  msgstr ""
2602
 
2603
+ #: inc/views/admin/metaboxes/title-metabox.php:258
2604
  msgid "Always brand your titles. Search engines may ignore your titles with this feature enabled."
2605
  msgstr ""
2606
 
2607
+ #: inc/views/admin/metaboxes/title-metabox.php:266
2608
  msgid "Remove site title from the title?"
2609
  msgstr ""
2610
 
2611
+ #: inc/views/admin/metaboxes/title-metabox.php:275
2612
  msgid "Note: Only use this option if you are aware of its SEO effects."
2613
  msgstr ""
2614
 
2615
+ #: inc/views/admin/metaboxes/title-metabox.php:282
2616
  msgid "Title Prefix Options"
2617
  msgstr ""
2618
 
2619
+ #: inc/views/admin/metaboxes/title-metabox.php:284
2620
  msgid "For archives, a descriptive prefix may be added to generated titles."
2621
  msgstr ""
2622
 
2623
+ #: inc/views/admin/metaboxes/title-metabox.php:289
2624
  msgid "Archive Title Prefixes"
2625
  msgstr ""
2626
 
2627
+ #: inc/views/admin/metaboxes/title-metabox.php:293
2628
  msgid "The prefix helps visitors and search engines determine what kind of page they're visiting."
2629
  msgstr ""
2630
 
2631
+ #: inc/views/admin/metaboxes/title-metabox.php:300
2632
  msgid "Remove term type prefixes from generated archive titles?"
2633
  msgstr ""
2634
 
2706
  msgid "Doing it Right"
2707
  msgstr ""
2708
 
2709
+ #: inc/views/edit/seo-settings-singular.php:168
2710
+ #: inc/views/edit/seo-settings-singular.php:175
2711
  #: inc/views/edit/seo-settings-tt.php:154
2712
  msgid "Remove the site title?"
2713
  msgstr ""
2714
 
2715
+ #: inc/views/edit/seo-settings-singular.php:170
2716
  msgid "For the homepage, this option must be managed on the SEO Settings page."
2717
  msgstr ""
2718
 
2719
+ #: inc/views/edit/seo-settings-singular.php:177
2720
  #: inc/views/edit/seo-settings-tt.php:156
2721
  msgid "Use this when you want to rearrange the title parts manually."
2722
  msgstr ""
2723
 
2724
+ #: inc/views/edit/seo-settings-singular.php:250
2725
  #: inc/views/edit/seo-settings-tt.php:71
2726
  #: inc/views/list/bulk-post.php:27
2727
  #: inc/views/list/quick-post.php:27
2728
  msgid "Link following"
2729
  msgstr ""
2730
 
2731
+ #: inc/views/edit/seo-settings-singular.php:268
2732
  #: inc/views/edit/seo-settings-tt.php:302
2733
  #: inc/views/list/quick-post.php:95
2734
  #: inc/views/list/quick-term.php:95
2735
  msgid "Canonical URL"
2736
  msgstr ""
2737
 
2738
+ #: inc/views/edit/seo-settings-singular.php:272
2739
  #: inc/views/edit/seo-settings-tt.php:306
2740
  msgid "This urges search engines to go to the outputted URL."
2741
  msgstr ""
2742
 
2743
+ #: inc/views/edit/seo-settings-singular.php:293
2744
  msgid "These directives may urge robots not to display, follow links on, or create a cached copy of this page."
2745
  msgstr ""
2746
 
2747
+ #: inc/views/edit/seo-settings-singular.php:307
2748
  msgid "Note: A non-default selection here will overwrite the global homepage SEO settings."
2749
  msgstr ""
2750
 
2751
  #. translators: %s = default option value
2752
+ #: inc/views/edit/seo-settings-singular.php:335
2753
+ #: inc/views/edit/seo-settings-singular.php:343
2754
  #: inc/views/edit/seo-settings-tt.php:339
2755
+ #: inc/views/edit/seo-settings-tt.php:348
2756
  #: inc/views/list/quick-post.php:112
2757
  #: inc/views/list/quick-term.php:112
2758
  msgid "Default (%s)"
2759
  msgstr ""
2760
 
2761
+ #: inc/views/edit/seo-settings-singular.php:366
2762
  msgid "Archive Settings"
2763
  msgstr ""
2764
 
2765
+ #: inc/views/edit/seo-settings-singular.php:375
2766
  msgid "Exclude this page from all search queries on this site."
2767
  msgstr ""
2768
 
2769
+ #: inc/views/edit/seo-settings-singular.php:384
2770
  msgid "Exclude this page from all archive queries on this site."
2771
  msgstr ""
2772
 
2773
+ #: inc/views/edit/seo-settings-singular.php:398
2774
+ #: inc/views/edit/seo-settings-tt.php:360
2775
  #: inc/views/list/quick-post.php:125
2776
  #: inc/views/list/quick-term.php:125
2777
  msgid "301 Redirect URL"
2778
  msgstr ""
2779
 
2780
+ #: inc/views/edit/seo-settings-singular.php:403
2781
+ #: inc/views/edit/seo-settings-tt.php:364
2782
  msgid "This will force visitors to go to another URL."
2783
  msgstr ""
2784
 
2827
  msgid "This may be shown publicly."
2828
  msgstr ""
2829
 
2830
+ #: inc/views/sitemap/xml-sitemap.php:32
2831
  msgid "Sitemap is generated for this view"
2832
  msgstr ""
2833
 
2834
+ #: inc/views/sitemap/xml-sitemap.php:34
2835
  msgid "Sitemap is served from cache"
2836
  msgstr ""
2837
 
lib/css/tsf.css CHANGED
@@ -63,7 +63,8 @@ body.rtl .tsf-notice.tsf-show-icon p:before {
63
  content: "\f147";
64
  }
65
 
66
- .tsf-dismiss {
 
67
  position: absolute;
68
  top: 0;
69
  right: 0;
@@ -76,7 +77,7 @@ body.rtl .tsf-notice.tsf-show-icon p:before {
76
  text-decoration: none;
77
  }
78
 
79
- body.rtl .tsf-dismiss {
80
  right: initial;
81
  left: 0;
82
  }
@@ -111,7 +112,7 @@ body.rtl .tsf-dismiss {
111
  font-size: 13px;
112
  -webkit-user-select: none;
113
  -moz-user-select: none;
114
- -ms-user-select: none;
115
  user-select: none;
116
  }
117
 
@@ -133,7 +134,7 @@ body.rtl .tsf-dismiss {
133
  white-space: pre;
134
  }
135
 
136
- body.rtl .sf-title-offset {
137
  left: initial;
138
  right: 0;
139
  }
@@ -144,7 +145,7 @@ body.rtl .sf-title-offset {
144
  color: #777;
145
  -webkit-user-select: none;
146
  -moz-user-select: none;
147
- -ms-user-select: none;
148
  user-select: none;
149
  box-sizing: content-box;
150
  top: 0;
@@ -352,9 +353,6 @@ body.rtl .tsf-counter .tsf-aja {
352
  .tsf-ajax.tsf-loading:before {
353
  content: "\f463";
354
  color: #007bd2;
355
- -webkit-animation: tsf-spin 1.5s linear infinite;
356
- -moz-animation: tsf-spin 1.5s linear infinite;
357
- -o-animation: tsf-spin 1.5s linear infinite;
358
  animation: tsf-spin 1.5s linear infinite;
359
  }
360
 
@@ -368,6 +366,11 @@ body.rtl .tsf-counter .tsf-aja {
368
  color: #0cc34b;
369
  }
370
 
 
 
 
 
 
371
  .tsf-set-image-button.button,
372
  .tsf-remove-image-button.button {
373
  margin-right: 8px;
@@ -436,15 +439,6 @@ body.rtl .tsf-remove-image-button.button {
436
  text-shadow: 0 0 1px rgba(114, 119, 124, 0.75);
437
  }
438
 
439
- @-webkit-keyframes tsf-spin {
440
- 0% {
441
- -webkit-transform: rotate( 0deg );
442
- }
443
- 100% {
444
- -webkit-transform: rotate( 360deg );
445
- }
446
- }
447
-
448
  @keyframes tsf-spin {
449
  0% {
450
  transform: rotate( 0deg );
63
  content: "\f147";
64
  }
65
 
66
+ /* Specific because common.css overrides via `.updated a` */
67
+ .tsf-notice .tsf-dismiss {
68
  position: absolute;
69
  top: 0;
70
  right: 0;
77
  text-decoration: none;
78
  }
79
 
80
+ body.rtl .tsf-notice .tsf-dismiss {
81
  right: initial;
82
  left: 0;
83
  }
112
  font-size: 13px;
113
  -webkit-user-select: none;
114
  -moz-user-select: none;
115
+ -ms-user-select: none; /* Remove me 2022 */
116
  user-select: none;
117
  }
118
 
134
  white-space: pre;
135
  }
136
 
137
+ body.rtl .tsf-title-offset {
138
  left: initial;
139
  right: 0;
140
  }
145
  color: #777;
146
  -webkit-user-select: none;
147
  -moz-user-select: none;
148
+ -ms-user-select: none; /* Remove me 2022 */
149
  user-select: none;
150
  box-sizing: content-box;
151
  top: 0;
353
  .tsf-ajax.tsf-loading:before {
354
  content: "\f463";
355
  color: #007bd2;
 
 
 
356
  animation: tsf-spin 1.5s linear infinite;
357
  }
358
 
366
  color: #0cc34b;
367
  }
368
 
369
+ .tsf-ajax.tsf-unknown:after {
370
+ content: "\f223";
371
+ color: #057c99;
372
+ }
373
+
374
  .tsf-set-image-button.button,
375
  .tsf-remove-image-button.button {
376
  margin-right: 8px;
439
  text-shadow: 0 0 1px rgba(114, 119, 124, 0.75);
440
  }
441
 
 
 
 
 
 
 
 
 
 
442
  @keyframes tsf-spin {
443
  0% {
444
  transform: rotate( 0deg );
lib/css/tsf.min.css CHANGED
@@ -1 +1 @@
1
- .tsf-js .hide-if-tsf-js,.tsf-no-js .hide-if-no-tsf-js{display:none}.notice.tsf-notice{position:relative;padding-right:38px}body.rtl .notice.tsf-notice{padding-right:12px;padding-left:38px}.tsf-notice.tsf-show-icon p:before{content:'';margin-right:12px;background:0 0;display:inline-block;font:400 14px/16px dashicons;speak:none;height:16px;text-align:center;vertical-align:middle;width:16px;line-height:14px;-webkit-font-smoothing:antialiased}body.rtl .tsf-notice.tsf-show-icon p:before{margin-right:0;margin-left:12px}.error.tsf-notice.tsf-show-icon p:before{color:#dc3232;content:"\f534"}.notice-warning.tsf-notice.tsf-show-icon p:before{color:#ffb900;content:"\f227"}.notice-info.tsf-notice.tsf-show-icon p:before{color:#00a0d2;font-size:16px;content:"\f223"}.updated.tsf-notice.tsf-show-icon p:before{color:#46b450;font-size:16px;content:"\f147"}.tsf-dismiss{position:absolute;top:0;right:0;border:none;margin:0;padding:10px;background:0 0;color:#72777c;cursor:pointer;text-decoration:none}body.rtl .tsf-dismiss{right:initial;left:0}.tsf-dismiss:hover{color:#c00}.tsf-dismiss:focus{outline:0;box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.tsf-dismiss:before{background:0 0;color:currentColor;content:"\f153";display:block;font:400 16px/20px dashicons;speak:none;height:20px;text-align:center;width:20px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tsf-seo-bar-item{text-decoration:none;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsf-title-wrap{position:relative;display:block;padding:0;height:auto;width:auto}.tsf-title-offset{visibility:hidden;height:0;display:inline-block;position:absolute;left:0;color:transparent;white-space:pre}body.rtl .sf-title-offset{left:initial;right:0}.tsf-title-placeholder-additions,.tsf-title-placeholder-prefix{position:absolute;color:#777;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:content-box;top:0;left:0;overflow:hidden;white-space:pre;text-overflow:ellipsis;will-change:left,right}body.rtl .tsf-title-placeholder-additions,body.rtl .tsf-title-placeholder-prefix{left:initial;right:0}.tsf-title-placeholder-additions{display:none}.fixed .column-tsf-seo-bar-wrap{width:11%}.tsf-seo-bar{display:block;width:95%;max-width:190px;padding:0}.tsf-seo-bar-inner-wrap{display:flex;flex-direction:row;flex:1 1 auto;flex-wrap:wrap;justify-content:flex-start}.tsf-seo-bar-section-wrap{min-width:2em;flex:1 1 auto}.tsf-seo-bar-item{display:block;color:#fff;text-align:center;border:1px solid rgba(0,0,0,.1);text-shadow:0 0 3px rgba(0,0,0,.5)}.tsf-seo-bar-item:focus{outline:0;box-shadow:0 0 0 1px currentColor inset}body.no-js .tsf-seo-bar-item{cursor:default}.tsf-seo-bar-bad{background-color:#dd3811}.tsf-seo-bar-okay{background-color:#ffa700}.tsf-seo-bar-good{background-color:#0cc34b}.tsf-seo-bar-unknown{background-color:#007bd2}.tsf-seo-bar-undefined{background-color:#767676}#col-container,#col-right{overflow:initial}.tsf-help{font-weight:400}.tsf-notice{clear:both}.tsf-flex{box-sizing:border-box;display:flex;flex:1 1 auto;flex-flow:column wrap;justify-content:flex-start}input.tsf-default-selected{border-color:#1c9d38}input.tsf-default-selected:checked:before{color:#1c9d38}input.tsf-warning-selected{border-color:#dd3811}input.tsf-warning-selected:checked:before{color:#dd3811}label.tsf-disabled{color:#999}input.tsf-disabled,label.tsf-disabled{cursor:default}.tsf-fields{font-size:13px;line-height:1.5;margin:1em 0}.tsf-fields .tsf-toblock{display:block;width:100%;margin-bottom:4px}.tsf-fields p.description{margin:7px 0 5px;color:#666}.tsf-option-spacer{margin:1em 0}.tsf-select-wrap{margin-top:7px;margin-bottom:14px}.tsf-select-wrap:last-of-type{margin-bottom:0}.tsf-select-block>select{margin-top:7px;margin-bottom:14px;display:block}.tsf-select-block:last-of-type>select{margin-bottom:0}.tsf-checkbox-wrapper{margin-top:15px}.tsf-checkbox-wrapper:first-child{margin-top:0}.tsf-metaboxes legend h4{margin:2px 0}.tsf-metaboxes legend p:last-of-type{margin-bottom:0}.tsf-counter .tsf-ajax{margin-left:3px}body.rtl .tsf-counter .tsf-aja{margin-left:0;margin-right:3px}.tsf-ajax:before{display:inline-block;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;vertical-align:middle;content:""}.tsf-ajax.tsf-loading:before{content:"\f463";color:#007bd2;-webkit-animation:tsf-spin 1.5s linear infinite;-moz-animation:tsf-spin 1.5s linear infinite;-o-animation:tsf-spin 1.5s linear infinite;animation:tsf-spin 1.5s linear infinite}.tsf-ajax.tsf-error:before{content:"\f158";color:#dd3811}.tsf-ajax.tsf-success:before{content:"\f147";color:#0cc34b}.tsf-remove-image-button.button,.tsf-set-image-button.button{margin-right:8px}body.rtl .tsf-remove-image-button.button,body.rtl .tsf-set-image-button.button{margin-right:0;margin-left:8px}.tsf-image-preview{line-height:inherit;vertical-align:text-bottom}#tsf-inpost-box input[type=text]::-webkit-input-placeholder,#tsf-inpost-box textarea::-webkit-input-placeholder,.tsf-metaboxes input[type=text]::-webkit-input-placeholder,.tsf-metaboxes textarea::-webkit-input-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]::-ms-clear,.tsf-metaboxes input[type=text]::-ms-clear,.tsf-quick-edit-columns input[type=text]::-ms-clear{display:none}#tsf-inpost-box input[type=text]::-moz-placeholder,#tsf-inpost-box textarea::-moz-placeholder,.tsf-metaboxes input[type=text]::-moz-placeholder,.tsf-metaboxes textarea::-moz-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]:-ms-input-placeholder,#tsf-inpost-box textarea:-ms-input-placeholder,.tsf-metaboxes input[type=text]:-ms-input-placeholder,.tsf-metaboxes textarea:-ms-input-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]:focus::-webkit-input-placeholder,#tsf-inpost-box textarea:focus::-webkit-input-placeholder,.tsf-metaboxes input[type=text]:focus::-webkit-input-placeholder,.tsf-metaboxes textarea:focus::-webkit-input-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}#tsf-inpost-box input[type=text]:focus::-moz-placeholder,#tsf-inpost-box textarea:focus::-moz-placeholder,.tsf-metaboxe textarea:focus::-moz-placeholder,.tsf-metaboxes input[type=text]:focus::-moz-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}#tsf-inpost-box input[type=text]:focus:-ms-input-placeholder,#tsf-inpost-box textarea:focus:-ms-input-placeholder,.tsf-metaboxes input[type=text]:focus:-ms-input-placeholder,.tsf-metaboxes textarea:focus:-ms-input-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}@-webkit-keyframes tsf-spin{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes tsf-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@media screen and (max-width:782px){.notice.tsf-notice,.wp-core-ui .notice.tsf-notice{padding-right:46px}body.rtl .notice.tsf-notice,body.rtl.wp-core-ui .notice.tsf-notice{padding-right:10px;padding-left:46px}.tsf-inpost-box p.tsf-fields,.tsf-metaboxes p.tsf-fields{line-height:2.8}#tsf-home-title-location label span,#tsf-title-location label span{min-width:40px}.wp-list-table .is-expanded td.tsf-seo-bar-wrap:not(.hidden){overflow:initial!important}}@media screen and (max-width:642px){.tsf-nav-desktop{display:none}}@-moz-document url-prefix(){input.tsf-default-selected{box-shadow:0 0 0 1px #1c9d38}input.tsf-warning-selected{box-shadow:0 0 0 1px #dd3811}}
1
+ .tsf-js .hide-if-tsf-js,.tsf-no-js .hide-if-no-tsf-js{display:none}.notice.tsf-notice{position:relative;padding-right:38px}body.rtl .notice.tsf-notice{padding-right:12px;padding-left:38px}.tsf-notice.tsf-show-icon p:before{content:'';margin-right:12px;background:0 0;display:inline-block;font:400 14px/16px dashicons;speak:none;height:16px;text-align:center;vertical-align:middle;width:16px;line-height:14px;-webkit-font-smoothing:antialiased}body.rtl .tsf-notice.tsf-show-icon p:before{margin-right:0;margin-left:12px}.error.tsf-notice.tsf-show-icon p:before{color:#dc3232;content:"\f534"}.notice-warning.tsf-notice.tsf-show-icon p:before{color:#ffb900;content:"\f227"}.notice-info.tsf-notice.tsf-show-icon p:before{color:#00a0d2;font-size:16px;content:"\f223"}.updated.tsf-notice.tsf-show-icon p:before{color:#46b450;font-size:16px;content:"\f147"}.tsf-notice .tsf-dismiss{position:absolute;top:0;right:0;border:none;margin:0;padding:10px;background:0 0;color:#72777c;cursor:pointer;text-decoration:none}body.rtl .tsf-notice .tsf-dismiss{right:initial;left:0}.tsf-dismiss:hover{color:#c00}.tsf-dismiss:focus{outline:0;box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.tsf-dismiss:before{background:0 0;color:currentColor;content:"\f153";display:block;font:400 16px/20px dashicons;speak:none;height:20px;text-align:center;width:20px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tsf-seo-bar-item{text-decoration:none;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsf-title-wrap{position:relative;display:block;padding:0;height:auto;width:auto}.tsf-title-offset{visibility:hidden;height:0;display:inline-block;position:absolute;left:0;color:transparent;white-space:pre}body.rtl .tsf-title-offset{left:initial;right:0}.tsf-title-placeholder-additions,.tsf-title-placeholder-prefix{position:absolute;color:#777;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:content-box;top:0;left:0;overflow:hidden;white-space:pre;text-overflow:ellipsis;will-change:left,right}body.rtl .tsf-title-placeholder-additions,body.rtl .tsf-title-placeholder-prefix{left:initial;right:0}.tsf-title-placeholder-additions{display:none}.fixed .column-tsf-seo-bar-wrap{width:11%}.tsf-seo-bar{display:block;width:95%;max-width:190px;padding:0}.tsf-seo-bar-inner-wrap{display:flex;flex-direction:row;flex:1 1 auto;flex-wrap:wrap;justify-content:flex-start}.tsf-seo-bar-section-wrap{min-width:2em;flex:1 1 auto}.tsf-seo-bar-item{display:block;color:#fff;text-align:center;border:1px solid rgba(0,0,0,.1);text-shadow:0 0 3px rgba(0,0,0,.5)}.tsf-seo-bar-item:focus{outline:0;box-shadow:0 0 0 1px currentColor inset}body.no-js .tsf-seo-bar-item{cursor:default}.tsf-seo-bar-bad{background-color:#dd3811}.tsf-seo-bar-okay{background-color:#ffa700}.tsf-seo-bar-good{background-color:#0cc34b}.tsf-seo-bar-unknown{background-color:#007bd2}.tsf-seo-bar-undefined{background-color:#767676}#col-container,#col-right{overflow:initial}.tsf-help{font-weight:400}.tsf-notice{clear:both}.tsf-flex{box-sizing:border-box;display:flex;flex:1 1 auto;flex-flow:column wrap;justify-content:flex-start}input.tsf-default-selected{border-color:#1c9d38}input.tsf-default-selected:checked:before{color:#1c9d38}input.tsf-warning-selected{border-color:#dd3811}input.tsf-warning-selected:checked:before{color:#dd3811}label.tsf-disabled{color:#999}input.tsf-disabled,label.tsf-disabled{cursor:default}.tsf-fields{font-size:13px;line-height:1.5;margin:1em 0}.tsf-fields .tsf-toblock{display:block;width:100%;margin-bottom:4px}.tsf-fields p.description{margin:7px 0 5px;color:#666}.tsf-option-spacer{margin:1em 0}.tsf-select-wrap{margin-top:7px;margin-bottom:14px}.tsf-select-wrap:last-of-type{margin-bottom:0}.tsf-select-block>select{margin-top:7px;margin-bottom:14px;display:block}.tsf-select-block:last-of-type>select{margin-bottom:0}.tsf-checkbox-wrapper{margin-top:15px}.tsf-checkbox-wrapper:first-child{margin-top:0}.tsf-metaboxes legend h4{margin:2px 0}.tsf-metaboxes legend p:last-of-type{margin-bottom:0}.tsf-counter .tsf-ajax{margin-left:3px}body.rtl .tsf-counter .tsf-aja{margin-left:0;margin-right:3px}.tsf-ajax:before{display:inline-block;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;vertical-align:middle;content:""}.tsf-ajax.tsf-loading:before{content:"\f463";color:#007bd2;animation:tsf-spin 1.5s linear infinite}.tsf-ajax.tsf-error:before{content:"\f158";color:#dd3811}.tsf-ajax.tsf-success:before{content:"\f147";color:#0cc34b}.tsf-ajax.tsf-unknown:after{content:"\f223";color:#057c99}.tsf-remove-image-button.button,.tsf-set-image-button.button{margin-right:8px}body.rtl .tsf-remove-image-button.button,body.rtl .tsf-set-image-button.button{margin-right:0;margin-left:8px}.tsf-image-preview{line-height:inherit;vertical-align:text-bottom}#tsf-inpost-box input[type=text]::-webkit-input-placeholder,#tsf-inpost-box textarea::-webkit-input-placeholder,.tsf-metaboxes input[type=text]::-webkit-input-placeholder,.tsf-metaboxes textarea::-webkit-input-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]::-ms-clear,.tsf-metaboxes input[type=text]::-ms-clear,.tsf-quick-edit-columns input[type=text]::-ms-clear{display:none}#tsf-inpost-box input[type=text]::-moz-placeholder,#tsf-inpost-box textarea::-moz-placeholder,.tsf-metaboxes input[type=text]::-moz-placeholder,.tsf-metaboxes textarea::-moz-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]:-ms-input-placeholder,#tsf-inpost-box textarea:-ms-input-placeholder,.tsf-metaboxes input[type=text]:-ms-input-placeholder,.tsf-metaboxes textarea:-ms-input-placeholder{transition:color .33s ease-in,text-shadow .33s ease-in}#tsf-inpost-box input[type=text]:focus::-webkit-input-placeholder,#tsf-inpost-box textarea:focus::-webkit-input-placeholder,.tsf-metaboxes input[type=text]:focus::-webkit-input-placeholder,.tsf-metaboxes textarea:focus::-webkit-input-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}#tsf-inpost-box input[type=text]:focus::-moz-placeholder,#tsf-inpost-box textarea:focus::-moz-placeholder,.tsf-metaboxe textarea:focus::-moz-placeholder,.tsf-metaboxes input[type=text]:focus::-moz-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}#tsf-inpost-box input[type=text]:focus:-ms-input-placeholder,#tsf-inpost-box textarea:focus:-ms-input-placeholder,.tsf-metaboxes input[type=text]:focus:-ms-input-placeholder,.tsf-metaboxes textarea:focus:-ms-input-placeholder{color:transparent;text-shadow:0 0 1px rgba(114,119,124,.75)}@keyframes tsf-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@media screen and (max-width:782px){.notice.tsf-notice,.wp-core-ui .notice.tsf-notice{padding-right:46px}body.rtl .notice.tsf-notice,body.rtl.wp-core-ui .notice.tsf-notice{padding-right:10px;padding-left:46px}.tsf-inpost-box p.tsf-fields,.tsf-metaboxes p.tsf-fields{line-height:2.8}#tsf-home-title-location label span,#tsf-title-location label span{min-width:40px}.wp-list-table .is-expanded td.tsf-seo-bar-wrap:not(.hidden){overflow:initial!important}}@media screen and (max-width:642px){.tsf-nav-desktop{display:none}}@-moz-document url-prefix(){input.tsf-default-selected{box-shadow:0 0 0 1px #1c9d38}input.tsf-warning-selected{box-shadow:0 0 0 1px #dd3811}}
lib/css/tsfc.css CHANGED
@@ -11,7 +11,7 @@
11
  cursor: pointer;
12
  -webkit-user-select: none;
13
  -moz-user-select: none;
14
- -ms-user-select: none;
15
  user-select: none;
16
  will-change: contents;
17
  }
11
  cursor: pointer;
12
  -webkit-user-select: none;
13
  -moz-user-select: none;
14
+ -ms-user-select: none; /* Remove me 2022 */
15
  user-select: none;
16
  will-change: contents;
17
  }
lib/js/ays.js CHANGED
@@ -106,8 +106,8 @@ window.tsfAys = function( $ ) {
106
  */
107
  const _getNodeArray = elements =>
108
  ( elements instanceof Element || elements instanceof Document )
109
- ? [elements]
110
- : [...document.querySelectorAll( Array.isArray( elements ) ? elements.join( ', ' ) : elements ) ];
111
 
112
  /**
113
  * Registers changed state.
106
  */
107
  const _getNodeArray = elements =>
108
  ( elements instanceof Element || elements instanceof Document )
109
+ ? [ elements ]
110
+ : [ ...document.querySelectorAll( Array.isArray( elements ) ? elements.join( ', ' ) : elements ) ];
111
 
112
  /**
113
  * Registers changed state.
lib/js/description.js CHANGED
@@ -77,6 +77,7 @@ window.tsfDescription = function( $ ) {
77
  states[ element.id ] = {
78
  allowReferenceChange: true,
79
  defaultDescription: '',
 
80
  };
81
  _loadDescriptionActions( element );
82
  return getInputElement( element.id );
@@ -169,6 +170,7 @@ window.tsfDescription = function( $ ) {
169
  * There's no need to escape the input, it may be double-escaped if you do so.
170
  *
171
  * @since 4.1.0
 
172
  * @access public
173
  *
174
  * @param {string} id The input element ID.
@@ -186,6 +188,7 @@ window.tsfDescription = function( $ ) {
186
  switch ( part ) {
187
  case 'allowReferenceChange':
188
  case 'defaultDescription':
 
189
  default:
190
  enqueueTriggerInput( id );
191
  break;
@@ -238,6 +241,7 @@ window.tsfDescription = function( $ ) {
238
  *
239
  * @since 4.0.0
240
  * @since 4.1.0 Now supports multiple instances.
 
241
  * @access private
242
  *
243
  * @function
@@ -249,8 +253,12 @@ window.tsfDescription = function( $ ) {
249
 
250
  if ( ! references[0] ) return;
251
 
252
- let allowReferenceChange = getStateOf( event.target.id, 'allowReferenceChange' ),
253
- text = allowReferenceChange && event.target.value || getStateOf( event.target.id, 'defaultDescription' ) || '';
 
 
 
 
254
 
255
  let referenceValue = tsf.escapeString( tsf.decodeEntities( tsf.sDoubleSpace( text.trim() ) ) );
256
 
77
  states[ element.id ] = {
78
  allowReferenceChange: true,
79
  defaultDescription: '',
80
+ useDefaultDescription: true,
81
  };
82
  _loadDescriptionActions( element );
83
  return getInputElement( element.id );
170
  * There's no need to escape the input, it may be double-escaped if you do so.
171
  *
172
  * @since 4.1.0
173
+ * @since 4.1.2 Added part `useDefaultDescription`.
174
  * @access public
175
  *
176
  * @param {string} id The input element ID.
188
  switch ( part ) {
189
  case 'allowReferenceChange':
190
  case 'defaultDescription':
191
+ case 'useDefaultDescription':
192
  default:
193
  enqueueTriggerInput( id );
194
  break;
241
  *
242
  * @since 4.0.0
243
  * @since 4.1.0 Now supports multiple instances.
244
+ * @since 4.1.2 Now listens to `useDefaultDescription` when reference isn't locked.
245
  * @access private
246
  *
247
  * @function
253
 
254
  if ( ! references[0] ) return;
255
 
256
+ let allowReferenceChange = getStateOf( event.target.id, 'allowReferenceChange' ),
257
+ useDefaultDescription = allowReferenceChange ? getStateOf( event.target.id, 'useDefaultDescription' ) : true;
258
+
259
+ let text = ( allowReferenceChange && event.target.value )
260
+ || ( useDefaultDescription && getStateOf( event.target.id, 'defaultDescription' ) )
261
+ || '';
262
 
263
  let referenceValue = tsf.escapeString( tsf.decodeEntities( tsf.sDoubleSpace( text.trim() ) ) );
264
 
lib/js/description.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfDescription=function(a){const b="undefined"!=typeof tsfDescriptionL10n&&tsfDescriptionL10n,c=new Map;let d={};const e=a=>c.get(a);let f;const g=()=>{if(f)return f;for(const a of c.keys())if(h(a,"hasLegacy"))return f=a},h=(a,b)=>b?b in d[a]?d[a][b]:void 0:d[a],i=(a,b,c)=>{if(a in d)switch(d[a][b]=c,b){case"allowReferenceChange":case"defaultDescription":default:t(a);}},j=a=>{let b=[document.getElementById("tsf-description-reference_"+a)];if(h(a,"hasLegacy")){let a=document.getElementById("tsf-description-reference");a&&b.unshift(a)}return b},k=a=>{let b=j(a.target.id);if(!b[0])return;let c=h(a.target.id,"allowReferenceChange"),d=c&&a.target.value||h(a.target.id,"defaultDescription")||"",e=tsf.escapeString(tsf.decodeEntities(tsf.sDoubleSpace(d.trim())));b.forEach(a=>{a.innerHTML=e,setTimeout(()=>{a.dispatchEvent(new Event("change"))},0)})},l=a=>{a.target.placeholder=j(a.target.id)[0].innerText},m=a=>{if("tsfC"in window){let b=document.getElementById(a.target.id+"_chars"),c=j(a.target.id)[0];b&&tsfC.updateCharacterCounter({e:b,text:c.innerHTML,field:"description",type:"search"})}},n=a=>{if("tsfC"in window){let b=document.getElementById(a.target.id+"_pixels"),c=j(a.target.id)[0];b&&tsfC.updatePixelCounter({e:b,text:c.innerHTML,field:"description",type:"search"})}},o=a=>{if(a){let b=e(a);b&&b.dispatchEvent(new Event("input"))}else c.forEach(a=>a.id&&o(a.id))},p=a=>{if(a){let b=e(a);b&&b.dispatchEvent(new CustomEvent("tsf-update-description-counter"))}else c.forEach(a=>a.id&&p(a.id))},q=a=>{k(a),l(a),r(a)},r=a=>{m(a),n(a)};let s={};const t=a=>{a in s&&clearTimeout(s[a]),s[a]=setTimeout(()=>o(a),10)},u=a=>{if("tsfAys"in window){let b=tsfAys.areSettingsChanged();o(a),!b&&tsfAys.areSettingsChanged()&&tsfAys.reset()}else o(a)};let v={};const w=a=>{a in v&&clearTimeout(v[a]),v[a]=setTimeout(()=>u(a),10)};let x=window.innerWidth;const y=()=>{let a=window.innerWidth;x<a?782>=x&&782<=a&&u():782<=x&&782>=a&&u(),x=a},z=()=>{a(document).on("wp-window-resized",y),window.addEventListener("tsf-counter-updated",()=>w())},A=a=>{!a instanceof Element||(a.addEventListener("input",q),a.addEventListener("tsf-update-description-counter",r),w(a.id))};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",z)}},{setInputElement:a=>(c.set(a.id,a),d[a.id]={allowReferenceChange:!0,defaultDescription:""},A(a),e(a.id)),getInputElement:e,getState:a=>(tsf.deprecatedFunc("tsfDescription.getState()","4.1.0","tsfDescription.getStateOf()"),h(g(),a)),getStateOf:h,updateState:(a,b)=>(tsf.deprecatedFunc("tsfDescription.updateState()","4.1.0","tsfDescription.updateStateOf()"),i(g(),a,b)),updateStateOf:i,updateStateAll:(a,b)=>{c.forEach(c=>{i(c.id,a,b)})},triggerCounter:p,triggerInput:o,enqueueTriggerInput:t,triggerUnregisteredInput:u,enqueueUnregisteredInputTrigger:w},{l10n:b})}(jQuery),window.tsfDescription.load();
1
+ 'use strict';window.tsfDescription=function(a){const b="undefined"!=typeof tsfDescriptionL10n&&tsfDescriptionL10n,c=new Map;let d={};const e=a=>c.get(a);let f;const g=()=>{if(f)return f;for(const a of c.keys())if(h(a,"hasLegacy"))return f=a},h=(a,b)=>b?b in d[a]?d[a][b]:void 0:d[a],i=(a,b,c)=>{if(a in d)switch(d[a][b]=c,b){case"allowReferenceChange":case"defaultDescription":case"useDefaultDescription":default:t(a);}},j=a=>{let b=[document.getElementById("tsf-description-reference_"+a)];if(h(a,"hasLegacy")){let a=document.getElementById("tsf-description-reference");a&&b.unshift(a)}return b},k=a=>{let b=j(a.target.id);if(!b[0])return;let c=h(a.target.id,"allowReferenceChange"),d=!c||h(a.target.id,"useDefaultDescription"),e=c&&a.target.value||d&&h(a.target.id,"defaultDescription")||"",f=tsf.escapeString(tsf.decodeEntities(tsf.sDoubleSpace(e.trim())));b.forEach(a=>{a.innerHTML=f,setTimeout(()=>{a.dispatchEvent(new Event("change"))},0)})},l=a=>{a.target.placeholder=j(a.target.id)[0].innerText},m=a=>{if("tsfC"in window){let b=document.getElementById(a.target.id+"_chars"),c=j(a.target.id)[0];b&&tsfC.updateCharacterCounter({e:b,text:c.innerHTML,field:"description",type:"search"})}},n=a=>{if("tsfC"in window){let b=document.getElementById(a.target.id+"_pixels"),c=j(a.target.id)[0];b&&tsfC.updatePixelCounter({e:b,text:c.innerHTML,field:"description",type:"search"})}},o=a=>{if(a){let b=e(a);b&&b.dispatchEvent(new Event("input"))}else c.forEach(a=>a.id&&o(a.id))},p=a=>{if(a){let b=e(a);b&&b.dispatchEvent(new CustomEvent("tsf-update-description-counter"))}else c.forEach(a=>a.id&&p(a.id))},q=a=>{k(a),l(a),r(a)},r=a=>{m(a),n(a)};let s={};const t=a=>{a in s&&clearTimeout(s[a]),s[a]=setTimeout(()=>o(a),10)},u=a=>{if("tsfAys"in window){let b=tsfAys.areSettingsChanged();o(a),!b&&tsfAys.areSettingsChanged()&&tsfAys.reset()}else o(a)};let v={};const w=a=>{a in v&&clearTimeout(v[a]),v[a]=setTimeout(()=>u(a),10)};let x=window.innerWidth;const y=()=>{let a=window.innerWidth;x<a?782>=x&&782<=a&&u():782<=x&&782>=a&&u(),x=a},z=()=>{a(document).on("wp-window-resized",y),window.addEventListener("tsf-counter-updated",()=>w())},A=a=>{!a instanceof Element||(a.addEventListener("input",q),a.addEventListener("tsf-update-description-counter",r),w(a.id))};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",z)}},{setInputElement:a=>(c.set(a.id,a),d[a.id]={allowReferenceChange:!0,defaultDescription:"",useDefaultDescription:!0},A(a),e(a.id)),getInputElement:e,getState:a=>(tsf.deprecatedFunc("tsfDescription.getState()","4.1.0","tsfDescription.getStateOf()"),h(g(),a)),getStateOf:h,updateState:(a,b)=>(tsf.deprecatedFunc("tsfDescription.updateState()","4.1.0","tsfDescription.updateStateOf()"),i(g(),a,b)),updateStateOf:i,updateStateAll:(a,b)=>{c.forEach(c=>{i(c.id,a,b)})},triggerCounter:p,triggerInput:o,enqueueTriggerInput:t,triggerUnregisteredInput:u,enqueueUnregisteredInputTrigger:w},{l10n:b})}(jQuery),window.tsfDescription.load();
lib/js/le.js CHANGED
@@ -87,7 +87,7 @@ window.tsfLe = function( $ ) {
87
  */
88
  const _setInlinePostValues = id => {
89
 
90
- let dataElement = document.getElementById( 'tsfLeData[' + id + ']' ),
91
  data = void 0;
92
 
93
  try {
@@ -98,20 +98,20 @@ window.tsfLe = function( $ ) {
98
 
99
  let element;
100
 
101
- for ( let index in data ) {
102
 
103
- element = document.getElementById( 'autodescription-quick[%s]'.replace( '%s', index ) );
104
  if ( ! element ) continue;
105
 
106
- if ( data[ index ].isSelect ) {
107
- tsf.selectByValue( element, data[ index ].value );
108
 
109
  // Do `sprintf( 'Default (%s)', x.default )`.
110
  let _default = element.querySelector( '[value="0"]' );
111
  if ( _default )
112
- _default.innerHTML = _default.innerHTML.replace( '%s', tsf.decodeEntities( data[ index ].default ) );
113
  } else {
114
- element.value = tsf.decodeEntities( data[ index ].value );
115
  }
116
  }
117
  }
@@ -135,7 +135,7 @@ window.tsfLe = function( $ ) {
135
  * @access private
136
  *
137
  * @function
138
- * @param event
139
  * @return {undefined}
140
  */
141
  const _setTitleVisibilityPrefix = event => {
@@ -177,7 +177,7 @@ window.tsfLe = function( $ ) {
177
  * @access private
178
  *
179
  * @function
180
- * @param event
181
  * @return {undefined}
182
  */
183
  const _setDefaultTitle = event => {
@@ -205,7 +205,7 @@ window.tsfLe = function( $ ) {
205
  }
206
 
207
  /**
208
- * Sets inline title input values for quick-edit.
209
  *
210
  * @since 4.1.0
211
  * @access private
@@ -271,7 +271,7 @@ window.tsfLe = function( $ ) {
271
  }
272
 
273
  /**
274
- * Sets inline description input values for quick-edit.
275
  *
276
  * @since 4.1.0
277
  * @access private
87
  */
88
  const _setInlinePostValues = id => {
89
 
90
+ let dataElement = document.getElementById( `tsfLeData[${id}]` ),
91
  data = void 0;
92
 
93
  try {
98
 
99
  let element;
100
 
101
+ for ( let option in data ) {
102
 
103
+ element = document.getElementById( 'autodescription-quick[%s]'.replace( '%s', option ) );
104
  if ( ! element ) continue;
105
 
106
+ if ( data[ option ].isSelect ) {
107
+ tsf.selectByValue( element, data[ option ].value );
108
 
109
  // Do `sprintf( 'Default (%s)', x.default )`.
110
  let _default = element.querySelector( '[value="0"]' );
111
  if ( _default )
112
+ _default.innerHTML = _default.innerHTML.replace( '%s', tsf.decodeEntities( data[ option ].default ) );
113
  } else {
114
+ element.value = tsf.decodeEntities( data[ option ].value );
115
  }
116
  }
117
  }
135
  * @access private
136
  *
137
  * @function
138
+ * @param {Event} event
139
  * @return {undefined}
140
  */
141
  const _setTitleVisibilityPrefix = event => {
177
  * @access private
178
  *
179
  * @function
180
+ * @param {Event} event
181
  * @return {undefined}
182
  */
183
  const _setDefaultTitle = event => {
205
  }
206
 
207
  /**
208
+ * Augments and binds inline title input values for quick-edit.
209
  *
210
  * @since 4.1.0
211
  * @access private
271
  }
272
 
273
  /**
274
+ * Augments and binds inline description input values for quick-edit.
275
  *
276
  * @since 4.1.0
277
  * @access private
lib/js/le.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfLe=function(){const a="undefined"!=typeof tsfLeL10n&&tsfLeL10n;let b;const c=()=>{clearTimeout(b),b=setTimeout(()=>{document.dispatchEvent(new Event("tsfLeUpdated"))},50)},d=()=>{tsfTT&&tsfTT.triggerReset()},e=a=>{let b,c=document.getElementById("tsfLeData["+a+"]");try{b=JSON.parse(c.dataset.le)||void 0}catch(a){}if(!b)return;let d;for(let c in b)if(d=document.getElementById("autodescription-quick[%s]".replace("%s",c)),d)if(b[c].isSelect){tsf.selectByValue(d,b[c].value);let a=d.querySelector("[value=\"0\"]");a&&(a.innerHTML=a.innerHTML.replace("%s",tsf.decodeEntities(b[c].default)))}else d.value=tsf.decodeEntities(b[c].value)},f=a=>e(a),g=a=>{let b=(a.originalEvent||a).target,c=b.dataset.tsfTitleId,d=tsfTitle.getStateOf(c,"prefixValue"),e="",f="public";switch(f="keep_private"===b.name?b.checked?"private":"public":b.value&&b.value.length?"password":"public",f){case"password":e=tsfTitle.protectedPrefix;break;case"private":e=tsfTitle.privatePrefix;break;default:case"public":e="";}e!==d&&tsfTitle.updateStateOf(c,"prefixValue",e)},h=a=>{let b=(a.originalEvent||a).target,c=b.dataset.tsfTitleId,d="string"==typeof b.value&&b.value.trim()||"",e=tsfTitle.stripTitleTags?tsf.stripTags(d):d,f=tsfTitle.getStateOf(c,"defaultTitle"),g="string"==typeof b.dataset.termPrefix&&b.dataset.termPrefix.trim()||"";e=e||tsfTitle.untitledTitle,g.length&&(tsf.l10n.states.isRTL?e=e+" "+g:e=g+" "+e),e=tsf.escapeString(tsf.decodeEntities(e.trim())),e!==f&&tsfTitle.updateStateOf(c,"defaultTitle",e)},i=a=>{let b,c=document.getElementById("tsfLeTitleData["+a+"]");try{b=JSON.parse(c.dataset.leTitle)||void 0}catch(a){}if(!b)return;const d="autodescription-quick[doctitle]",e=document.getElementById(d);tsfTitle.setInputElement(e),tsfTitle.updateStateOf(d,"allowReferenceChange",!b.refTitleLocked),tsfTitle.updateStateOf(d,"defaultTitle",b.defaultTitle.trim()),tsfTitle.updateStateOf(d,"addAdditions",b.addAdditions),tsfTitle.updateStateOf(d,"additionValue",b.additionValue.trim()),tsfTitle.updateStateOf(d,"additionPlacement",b.additionPlacement);let f=document.getElementById("edit-"+a);f&&(f.querySelectorAll("[name=post_password]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("input",g),a.dispatchEvent(new CustomEvent("input"))}),f.querySelectorAll("[name=keep_private]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("click",g),a.dispatchEvent(new CustomEvent("click"))}),f.querySelectorAll("[name=post_title]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("input",h),a.dispatchEvent(new CustomEvent("input"))}),f.querySelectorAll("[name=name]").forEach(a=>{a.dataset.tsfTitleId=d,a.dataset.termPrefix=b.termPrefix||"",a.addEventListener("input",h),a.dispatchEvent(new CustomEvent("input"))})),tsfTT.triggerReset()},j=a=>{let b,c=document.getElementById("tsfLeDescriptionData["+a+"]");try{b=JSON.parse(c.dataset.leDescription)||void 0}catch(a){}if(b){let a="autodescription-quick[description]",c=document.getElementById(a);tsfDescription.setInputElement(c),tsfDescription.updateStateOf(a,"allowReferenceChange",!b.refDescriptionLocked),tsfDescription.updateStateOf(a,"defaultDescription",b.defaultDescription.trim()),tsfTT.triggerReset()}},k=()=>{document.addEventListener("tsfLeDispatchUpdate",c),document.addEventListener("tsfLeUpdated",d)},l=()=>{let a;window.inlineEditPost&&(a="edit"in window.inlineEditPost&&window.inlineEditPost.edit,a&&(window.inlineEditPost.edit=function(b){let c=a.apply(this,arguments);return("object"==typeof b&&(b=window.inlineEditPost.getId(b)),!b)?c:(e(b),i(b),j(b),"tsfC"in window&&tsfC.resetCounterListener(),c)})),window.inlineEditTax&&(a="edit"in window.inlineEditTax&&window.inlineEditTax.edit,a&&(window.inlineEditTax.edit=function(b){let c=a.apply(this,arguments);return("object"==typeof b&&(b=window.inlineEditTax.getId(b)),!b)?c:(f(b),i(b),j(b),"tsfC"in window&&tsfC.resetCounterListener(),c)}))};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",k),document.body.addEventListener("tsf-onload",l)}},{},{l10n:a})}(jQuery),window.tsfLe.load();
1
+ 'use strict';window.tsfLe=function(){const a="undefined"!=typeof tsfLeL10n&&tsfLeL10n;let b;const c=()=>{clearTimeout(b),b=setTimeout(()=>{document.dispatchEvent(new Event("tsfLeUpdated"))},50)},d=()=>{tsfTT&&tsfTT.triggerReset()},e=a=>{let b,c=document.getElementById(`tsfLeData[${a}]`);try{b=JSON.parse(c.dataset.le)||void 0}catch(a){}if(!b)return;let d;for(let c in b)if(d=document.getElementById("autodescription-quick[%s]".replace("%s",c)),d)if(b[c].isSelect){tsf.selectByValue(d,b[c].value);let a=d.querySelector("[value=\"0\"]");a&&(a.innerHTML=a.innerHTML.replace("%s",tsf.decodeEntities(b[c].default)))}else d.value=tsf.decodeEntities(b[c].value)},f=a=>e(a),g=a=>{let b=(a.originalEvent||a).target,c=b.dataset.tsfTitleId,d=tsfTitle.getStateOf(c,"prefixValue"),e="",f="public";switch(f="keep_private"===b.name?b.checked?"private":"public":b.value&&b.value.length?"password":"public",f){case"password":e=tsfTitle.protectedPrefix;break;case"private":e=tsfTitle.privatePrefix;break;default:case"public":e="";}e!==d&&tsfTitle.updateStateOf(c,"prefixValue",e)},h=a=>{let b=(a.originalEvent||a).target,c=b.dataset.tsfTitleId,d="string"==typeof b.value&&b.value.trim()||"",e=tsfTitle.stripTitleTags?tsf.stripTags(d):d,f=tsfTitle.getStateOf(c,"defaultTitle"),g="string"==typeof b.dataset.termPrefix&&b.dataset.termPrefix.trim()||"";e=e||tsfTitle.untitledTitle,g.length&&(tsf.l10n.states.isRTL?e=e+" "+g:e=g+" "+e),e=tsf.escapeString(tsf.decodeEntities(e.trim())),e!==f&&tsfTitle.updateStateOf(c,"defaultTitle",e)},i=a=>{let b,c=document.getElementById("tsfLeTitleData["+a+"]");try{b=JSON.parse(c.dataset.leTitle)||void 0}catch(a){}if(!b)return;const d="autodescription-quick[doctitle]",e=document.getElementById(d);tsfTitle.setInputElement(e),tsfTitle.updateStateOf(d,"allowReferenceChange",!b.refTitleLocked),tsfTitle.updateStateOf(d,"defaultTitle",b.defaultTitle.trim()),tsfTitle.updateStateOf(d,"addAdditions",b.addAdditions),tsfTitle.updateStateOf(d,"additionValue",b.additionValue.trim()),tsfTitle.updateStateOf(d,"additionPlacement",b.additionPlacement);let f=document.getElementById("edit-"+a);f&&(f.querySelectorAll("[name=post_password]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("input",g),a.dispatchEvent(new CustomEvent("input"))}),f.querySelectorAll("[name=keep_private]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("click",g),a.dispatchEvent(new CustomEvent("click"))}),f.querySelectorAll("[name=post_title]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("input",h),a.dispatchEvent(new CustomEvent("input"))}),f.querySelectorAll("[name=name]").forEach(a=>{a.dataset.tsfTitleId=d,a.dataset.termPrefix=b.termPrefix||"",a.addEventListener("input",h),a.dispatchEvent(new CustomEvent("input"))})),tsfTT.triggerReset()},j=a=>{let b,c=document.getElementById("tsfLeDescriptionData["+a+"]");try{b=JSON.parse(c.dataset.leDescription)||void 0}catch(a){}if(b){let a="autodescription-quick[description]",c=document.getElementById(a);tsfDescription.setInputElement(c),tsfDescription.updateStateOf(a,"allowReferenceChange",!b.refDescriptionLocked),tsfDescription.updateStateOf(a,"defaultDescription",b.defaultDescription.trim()),tsfTT.triggerReset()}},k=()=>{document.addEventListener("tsfLeDispatchUpdate",c),document.addEventListener("tsfLeUpdated",d)},l=()=>{let a;window.inlineEditPost&&(a="edit"in window.inlineEditPost&&window.inlineEditPost.edit,a&&(window.inlineEditPost.edit=function(b){let c=a.apply(this,arguments);return("object"==typeof b&&(b=window.inlineEditPost.getId(b)),!b)?c:(e(b),i(b),j(b),"tsfC"in window&&tsfC.resetCounterListener(),c)})),window.inlineEditTax&&(a="edit"in window.inlineEditTax&&window.inlineEditTax.edit,a&&(window.inlineEditTax.edit=function(b){let c=a.apply(this,arguments);return("object"==typeof b&&(b=window.inlineEditTax.getId(b)),!b)?c:(f(b),i(b),j(b),"tsfC"in window&&tsfC.resetCounterListener(),c)}))};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",k),document.body.addEventListener("tsf-onload",l)}},{},{l10n:a})}(jQuery),window.tsfLe.load();
lib/js/media.js CHANGED
@@ -45,13 +45,13 @@ window.tsfMedia = function( $ ) {
45
  const l10n = 'undefined' !== typeof tsfMediaL10n && tsfMediaL10n;
46
 
47
  /**
48
- * Image cropper instance.
49
  *
50
  * @since 3.1.0
51
  * @access private
52
- * @type {!Object} cropper
53
  */
54
- let cropper = {};
55
 
56
  /**
57
  * Escapes HTML class or ID keys. Doesn't double-escape.
@@ -98,7 +98,7 @@ window.tsfMedia = function( $ ) {
98
  event.preventDefault();
99
  event.stopPropagation();
100
 
101
- // Init extend cropper.
102
  _extendCropper();
103
 
104
  const _states = {
@@ -109,7 +109,7 @@ window.tsfMedia = function( $ ) {
109
  minHeight: +( button.dataset.minHeight || 200 ),
110
  };
111
 
112
- cropper.control = {
113
  params: {
114
  flex_width: _states.isFlex ? 4096 : 0,
115
  flex_height: _states.isFlex ? 4096 : 0,
@@ -136,7 +136,7 @@ window.tsfMedia = function( $ ) {
136
  suggestedWidth: _states.suggestedWidth,
137
  suggestedHeight: _states.suggestedHeight
138
  } ),
139
- new cropper( {
140
  imgSelectOptions: _calculateImageSelectOptions
141
  } ),
142
  ],
@@ -228,7 +228,8 @@ window.tsfMedia = function( $ ) {
228
  // Don't append another one.
229
  if ( removeButton ) return;
230
 
231
- let button = document.createElement( 'button' );
 
232
  button.type = 'button';
233
  button.id = `${inputId}-remove`
234
  button.dataset.inputId = inputId;
@@ -309,7 +310,7 @@ window.tsfMedia = function( $ ) {
309
  }
310
 
311
  /**
312
- * Builds constructor for media cropper.
313
  *
314
  * @since 3.1.0
315
  * @access private
@@ -319,22 +320,10 @@ window.tsfMedia = function( $ ) {
319
  */
320
  const _extendCropper = () => {
321
 
322
- if ( 'undefined' !== typeof cropper.control )
323
  return;
324
 
325
- /**
326
- * wp.media.controller.Cropper augmentation.
327
- *
328
- * A state for cropping an image.
329
- *
330
- * @class
331
- * @augments wp.media.controller.Cropper
332
- * @augments wp.media.controller.State
333
- * @augments Backbone.Model
334
- */
335
- let TSFCropper,
336
- Controller = wp.media.controller;
337
-
338
  /**
339
  * wp.media.view.Cropper augmentation.
340
  *
@@ -345,12 +334,9 @@ window.tsfMedia = function( $ ) {
345
  * @augments wp.Backbone.View
346
  * @augments Backbone.View
347
  */
348
- let TSFView,
349
- View = wp.media.view;
350
-
351
- TSFView = View.Cropper.extend( {
352
- className: 'crop-content tsf-image',
353
- ready: function () {
354
  View.Cropper.prototype.ready.apply( this, arguments );
355
  },
356
  onImageLoad: function() {
@@ -385,7 +371,17 @@ window.tsfMedia = function( $ ) {
385
  },
386
  } );
387
 
388
- TSFCropper = Controller.Cropper.extend( {
 
 
 
 
 
 
 
 
 
 
389
  createCropContent: function() {
390
  this.cropperView = new TSFView( {
391
  controller: this,
@@ -394,9 +390,9 @@ window.tsfMedia = function( $ ) {
394
  this.cropperView.on( 'image-loaded', this.createCropToolbar, this );
395
  this.frame.content.set( this.cropperView );
396
  },
397
- doCrop: function( attachment ) {
398
  let cropDetails = attachment.get( 'cropDetails' ),
399
- control = cropper.control; // prototyped earlier.
400
 
401
  // Use crop measurements when flexible in both directions.
402
  if ( control.params.flex_width && control.params.flex_height ) {
@@ -432,18 +428,21 @@ window.tsfMedia = function( $ ) {
432
  cropDetails.dst_height = 0;
433
  }
434
 
435
- return wp.ajax.post( 'tsf-crop-image', {
436
- nonce: l10n.nonce,
437
- id: attachment.get( 'id' ),
438
- context: 'tsf-image',
439
- cropDetails: cropDetails
440
- } );
 
 
 
441
  }
442
  } );
443
 
444
  TSFCropper.prototype.control = {};
445
 
446
- cropper = TSFCropper;
447
  }
448
 
449
  /**
@@ -461,21 +460,19 @@ window.tsfMedia = function( $ ) {
461
  */
462
  const _calculateImageSelectOptions = ( attachment, controller ) => {
463
 
464
- let control = cropper.control;
465
 
466
- let flexWidth = !! parseInt( control.params.flex_width, 10 ),
467
- flexHeight = !! parseInt( control.params.flex_height, 10 ),
468
- xInit = parseInt( control.params.width, 10 ),
469
  yInit = parseInt( control.params.height, 10 );
470
 
471
- let realWidth = attachment.get( 'width' ),
472
- realHeight = attachment.get( 'height' ),
473
- ratio = xInit / yInit,
474
- xImg = xInit,
475
- yImg = yInit,
476
- x1,
477
- y1,
478
- imgSelectOptions;
479
 
480
  let canSkipCrop;
481
  if ( control.params.isFlex ) {
@@ -499,10 +496,10 @@ window.tsfMedia = function( $ ) {
499
 
500
  // Find starting points, I think? Why do we halve this?
501
  // This is taken from WordPress's very own '_calculateImageSelectOptions' as-is.
502
- x1 = ( realWidth - xInit ) / 2;
503
- y1 = ( realHeight - yInit ) / 2;
504
 
505
- imgSelectOptions = {
506
  handles: true,
507
  keys: true,
508
  instance: true,
@@ -650,7 +647,6 @@ window.tsfMedia = function( $ ) {
650
  * Sets up jQuery image editor cache.
651
  *
652
  * @since 3.1.0
653
- * TODO set a callback.
654
  * @see tsfemMedia.setupImageEditorActions() (Extension Manager plugin)
655
  * @access private
656
  *
@@ -667,6 +663,23 @@ window.tsfMedia = function( $ ) {
667
  } );
668
  }
669
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  /**
671
  * Sets up image input tooltip handler.
672
  *
@@ -793,7 +806,9 @@ window.tsfMedia = function( $ ) {
793
  // Prepares image input tooltips.
794
  document.body.addEventListener( 'tsf-ready', _prepareTooltip );
795
  }
796
- }, {}, {
 
 
797
  l10n
798
  } );
799
  }( jQuery );
45
  const l10n = 'undefined' !== typeof tsfMediaL10n && tsfMediaL10n;
46
 
47
  /**
48
+ * Image Cropper instance.
49
  *
50
  * @since 3.1.0
51
  * @access private
52
+ * @type {!Object} Cropper
53
  */
54
+ let Cropper = {};
55
 
56
  /**
57
  * Escapes HTML class or ID keys. Doesn't double-escape.
98
  event.preventDefault();
99
  event.stopPropagation();
100
 
101
+ // Init extend Cropper.
102
  _extendCropper();
103
 
104
  const _states = {
109
  minHeight: +( button.dataset.minHeight || 200 ),
110
  };
111
 
112
+ Cropper.control = {
113
  params: {
114
  flex_width: _states.isFlex ? 4096 : 0,
115
  flex_height: _states.isFlex ? 4096 : 0,
136
  suggestedWidth: _states.suggestedWidth,
137
  suggestedHeight: _states.suggestedHeight
138
  } ),
139
+ new Cropper( {
140
  imgSelectOptions: _calculateImageSelectOptions
141
  } ),
142
  ],
228
  // Don't append another one.
229
  if ( removeButton ) return;
230
 
231
+ let button = document.createElement( 'button' );
232
+
233
  button.type = 'button';
234
  button.id = `${inputId}-remove`
235
  button.dataset.inputId = inputId;
310
  }
311
 
312
  /**
313
+ * Builds constructor for media Cropper.
314
  *
315
  * @since 3.1.0
316
  * @access private
320
  */
321
  const _extendCropper = () => {
322
 
323
+ if ( 'undefined' !== typeof Cropper.control )
324
  return;
325
 
326
+ const View = wp.media.view;
 
 
 
 
 
 
 
 
 
 
 
 
327
  /**
328
  * wp.media.view.Cropper augmentation.
329
  *
334
  * @augments wp.Backbone.View
335
  * @augments Backbone.View
336
  */
337
+ const TSFView = View.Cropper.extend( {
338
+ className: 'crop-content tsf-image',
339
+ ready: function () {
 
 
 
340
  View.Cropper.prototype.ready.apply( this, arguments );
341
  },
342
  onImageLoad: function() {
371
  },
372
  } );
373
 
374
+ /**
375
+ * wp.media.controller.Cropper augmentation.
376
+ *
377
+ * A state for cropping an image.
378
+ *
379
+ * @class
380
+ * @augments wp.media.controller.Cropper
381
+ * @augments wp.media.controller.State
382
+ * @augments Backbone.Model
383
+ */
384
+ const TSFCropper = wp.media.controller.Cropper.extend( {
385
  createCropContent: function() {
386
  this.cropperView = new TSFView( {
387
  controller: this,
390
  this.cropperView.on( 'image-loaded', this.createCropToolbar, this );
391
  this.frame.content.set( this.cropperView );
392
  },
393
+ doCrop: function( attachment ) {
394
  let cropDetails = attachment.get( 'cropDetails' ),
395
+ control = Cropper.control; // prototyped prior cropping, below.
396
 
397
  // Use crop measurements when flexible in both directions.
398
  if ( control.params.flex_width && control.params.flex_height ) {
428
  cropDetails.dst_height = 0;
429
  }
430
 
431
+ return wp.ajax.post(
432
+ 'tsf-crop-image',
433
+ {
434
+ nonce: l10n.nonce,
435
+ id: attachment.get( 'id' ),
436
+ context: 'tsf-image',
437
+ cropDetails: cropDetails
438
+ }
439
+ );
440
  }
441
  } );
442
 
443
  TSFCropper.prototype.control = {};
444
 
445
+ Cropper = TSFCropper;
446
  }
447
 
448
  /**
460
  */
461
  const _calculateImageSelectOptions = ( attachment, controller ) => {
462
 
463
+ const control = Cropper.control;
464
 
465
+ let xInit = parseInt( control.params.width, 10 ),
 
 
466
  yInit = parseInt( control.params.height, 10 );
467
 
468
+ const flexWidth = !! parseInt( control.params.flex_width, 10 ),
469
+ flexHeight = !! parseInt( control.params.flex_height, 10 );
470
+
471
+ const realWidth = attachment.get( 'width' ),
472
+ realHeight = attachment.get( 'height' ),
473
+ ratio = xInit / yInit,
474
+ xImg = xInit,
475
+ yImg = yInit;
476
 
477
  let canSkipCrop;
478
  if ( control.params.isFlex ) {
496
 
497
  // Find starting points, I think? Why do we halve this?
498
  // This is taken from WordPress's very own '_calculateImageSelectOptions' as-is.
499
+ let x1 = ( realWidth - xInit ) / 2,
500
+ y1 = ( realHeight - yInit ) / 2;
501
 
502
+ const imgSelectOptions = {
503
  handles: true,
504
  keys: true,
505
  instance: true,
647
  * Sets up jQuery image editor cache.
648
  *
649
  * @since 3.1.0
 
650
  * @see tsfemMedia.setupImageEditorActions() (Extension Manager plugin)
651
  * @access private
652
  *
663
  } );
664
  }
665
 
666
+ let _debounceActionReset = void 0;
667
+ /**
668
+ * Resets image editor actions and selectors.
669
+ *
670
+ * @since 4.1.2
671
+ * @uses _setupImageEditorActions
672
+ * @access public
673
+ *
674
+ * @function
675
+ * @return {(undefined|null)}
676
+ */
677
+ const resetImageEditorActions = () => {
678
+ clearTimeout( _debounceActionReset );
679
+ // High timeout. Resets should only happen during failures or changing document states; the latter of which is slow.
680
+ _debounceActionReset = setTimeout( _setupImageEditorActions, 500 );
681
+ }
682
+
683
  /**
684
  * Sets up image input tooltip handler.
685
  *
806
  // Prepares image input tooltips.
807
  document.body.addEventListener( 'tsf-ready', _prepareTooltip );
808
  }
809
+ }, {
810
+ resetImageEditorActions,
811
+ }, {
812
  l10n
813
  } );
814
  }( jQuery );
lib/js/media.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfMedia=function(a){const b="undefined"!=typeof tsfMediaL10n&&tsfMediaL10n;let c={};const d=a=>{const d=a.target;if(d.disabled||"undefined"==typeof wp.media)return a.preventDefault(),void a.stopPropagation();const f=d.dataset.inputType||"",i=d.dataset.inputId||"";let j;a.preventDefault(),a.stopPropagation(),g();const k={suggestedWidth:+(d.dataset.width||1200),suggestedHeight:+(d.dataset.height||630),isFlex:+(d.dataset.flex||1),minWidth:+(d.dataset.minWidth||200),minHeight:+(d.dataset.minHeight||200)};c.control={params:{flex_width:k.isFlex?4096:0,flex_height:k.isFlex?4096:0,width:k.suggestedWidth,height:k.suggestedHeight,isFlex:k.isFlex,minWidth:k.minWidth,minHeight:k.minHeight}},j=wp.media({button:{text:b.labels[f].imgFrameButton,close:!1},states:[new wp.media.controller.Library({title:b.labels[f].imgFrameTitle,library:wp.media.query({type:"image"}),multiple:!1,date:!1,priority:20,suggestedWidth:k.suggestedWidth,suggestedHeight:k.suggestedHeight}),new c({imgSelectOptions:h})]});const l=document.getElementById(`${i}-url`),m=document.getElementById(`${i}-id`),n=()=>{j.setState("cropper")};j.off("select",n),j.on("select",n);const o=a=>{let b=a.url,c=a.id;l&&(l.value=b,l.dispatchEvent(new Event("change"))),m&&(m.value=c,m.dispatchEvent(new Event("change")))};j.off("cropped",o),j.on("cropped",o);const p=a=>{let b=a.get("url"),c=a.get("id");l&&(l.value=b,l.dispatchEvent(new Event("change"))),m&&(m.value=c,m.dispatchEvent(new Event("change")))};j.off("skippedcrop",p),j.on("skippedcrop",p);const q=()=>{d.innerText=b.labels[f].imgChange,l&&(l.readOnly=!0),e(d,!0),"tsfAys"in window&&tsfAys.registerChange()};j.off("skippedcrop cropped",q),j.on("skippedcrop cropped",q),j.open()},e=(c,d)=>{const e=c.dataset.inputId||"",f=c.dataset.inputType||"";if(e&&f){const g=document.getElementById(`${e}-remove`);if(!g){let g=document.createElement("button");g.type="button",g.id=`${e}-remove`,g.dataset.inputId=e,g.dataset.inputType=f,g.title=tsf.decodeEntities(b.labels[f].imgRemoveTitle),g.innerHTML=tsf.escapeString(b.labels[f].imgRemove),g.classList.add("tsf-remove-image-button","button","button-small"),c.insertAdjacentElement("afterend",g),d&&a(g).css("opacity",0).animate({opacity:1},{queue:!0,duration:1e3}),m()}}},f=c=>{const d=c.target.dataset.inputId||"",e=c.target.dataset.inputType||"";if(!d||!e)return;const f=document.getElementById(`${d}-select`);if(f.disabled)return;f.disabled=!0,f.classList.add("disabled");const g=document.getElementById(`${d}-remove`);g&&(g.disabled=!0,g.classList.add("disabled"),a(g).fadeOut(250,()=>{g.remove(),f.innerText=b.labels[e].imgSelect,f.classList.remove("disabled"),f.disabled=!1}));const h=document.getElementById(`${d}-url`);h&&(h.value="",h.dispatchEvent(new Event("change")),!h.dataset.readonly&&(h.readOnly=!1));const i=document.getElementById(`${d}-id`);i&&(i.value="",i.dispatchEvent(new Event("change"))),"tsfAys"in window&&tsfAys.registerChange()},g=()=>{if("undefined"!=typeof c.control)return;let a,d,e=wp.media.controller,f=wp.media.view;d=f.Cropper.extend({className:"crop-content tsf-image",ready:function(){f.Cropper.prototype.ready.apply(this,arguments)},onImageLoad:function(){let a,b=this.controller.get("imgSelectOptions");"function"==typeof b&&(b=b(this.options.attachment,this.controller)),"undefined"==typeof b.aspectRatio&&(b=_.extend(b,{parent:this.$el,onInit:function(){this.parent.children().on("mousedown touchstart",function(b){b.shiftKey?a.setOptions({aspectRatio:"1:1"}):a.setOptions({aspectRatio:!1})})}})),this.trigger("image-loaded"),a=this.controller.imgSelect=this.$image.imgAreaSelect(b)}}),a=e.Cropper.extend({createCropContent:function(){this.cropperView=new d({controller:this,attachment:this.get("selection").first()}),this.cropperView.on("image-loaded",this.createCropToolbar,this),this.frame.content.set(this.cropperView)},doCrop:function(a){var d=Math.round;let e=a.get("cropDetails"),f=c.control;if(f.params.flex_width&&f.params.flex_height)if(e.width===e.height)e.width>f.params.flex_width&&(e.dst_width=e.dst_height=f.params.flex_width);else if(e.width>f.params.flex_width||e.height>f.params.flex_height)if(e.width>e.height){let a=e.width/f.params.flex_width;e.dst_width=f.params.flex_width,e.dst_height=d(e.height/a)}else{let a=e.height/f.params.flex_height;e.dst_height=f.params.flex_height,e.dst_width=d(e.width/a)}return"undefined"==typeof e.dst_width&&(e.dst_width=0,e.dst_height=0),wp.ajax.post("tsf-crop-image",{nonce:b.nonce,id:a.get("id"),context:"tsf-image",cropDetails:e})}}),a.prototype.control={},c=a},h=(a,b)=>{let d,e,f,g,h=c.control,j=!!parseInt(h.params.flex_width,10),k=!!parseInt(h.params.flex_height,10),l=parseInt(h.params.width,10),m=parseInt(h.params.height,10),n=a.get("width"),o=a.get("height"),p=l/m,q=l,r=m;return g=h.params.isFlex?!i(h.params.flex_width,h.params.flex_height,n,o):p===n/o,b.set("control",h.params),b.set("canSkipCrop",g),n/o>p?(m=o,l=m*p):(l=n,m=l/p),d=(n-l)/2,e=(o-m)/2,f={handles:!0,keys:!0,instance:!0,persistent:!0,imageWidth:n,imageHeight:o,minWidth:q>l?l:q,minHeight:r>m?m:r,x1:d,y1:e,x2:l+d,y2:m+e},h.params.isFlex?k||j?(k&&(f.minHeight=h.params.minHeight,f.maxWidth=n),j&&(f.minWidth=h.params.minWidth,f.maxHeight=o)):f.aspectRatio=l+":"+m:(f.handles="corners",f.aspectRatio=l+":"+m),f},i=(a,b,c,d)=>!(c<=a&&d<=b),j=a=>{const c=a.target.dataset.id||"",d=a.target.dataset.type||"";if(c&&d){const e=document.getElementById(`${c}-select`);e.disabled||(e.innerText=a.target.value.length?b.labels[d].imgChange:b.labels[d].imgSelect)}},k=()=>{document.querySelectorAll(".tsf-set-image-button").forEach(a=>{const b=a.dataset.inputId||"",c=b&&document.getElementById(`${b}-id`),d=b&&document.getElementById(`${b}-url`);c&&0<c.value&&(d&&(d.readOnly=!0),e(a,!1)),d&&(d.addEventListener("change",j),d.dispatchEvent(new Event("change")))})},l=()=>{document.querySelectorAll(".tsf-set-image-button").forEach(a=>{a.addEventListener("click",d)})},m=()=>{document.querySelectorAll(".tsf-remove-image-button").forEach(a=>{a.addEventListener("click",f)})},n=()=>{l(),m(),document.querySelectorAll(".tsf-enable-media-if-js").forEach(a=>{a.disabled=!1,a.classList.remove("tsf-enable-media-if-js")})},o=()=>{let b={};const c=c=>{const d=p(c.target.id||""),e=d&&document.getElementById(`${d}-preview`);if(!e)return;d in b&&clearTimeout(b[d]);let f=e.dataset.tsfLoaded||!1;e.dataset.tsfLoaded=1;let g=c.target.value||c.target.placeholder||"";b[d]=setTimeout(()=>{return g.length?void(e.dataset.desc="<img src='"+tsf.escapeString(g)+"' style="+`max-width:225px;max-height:225px;min-width:60px;min-height:60px;border-radius:3px;display:block;`+" />",f?a(e).not(":visible").fadeIn(250):a(e).show(),new Image().src=g,tsfTT.triggerUpdate(e)):void(f?a(e).not(":hidden").fadeOut(250):a(e).hide())},f&&g.length?500:0)};document.querySelectorAll(".tsf-image-preview").forEach(a=>{const b=document.getElementById(`${a.dataset.for}-url`);b&&(b.addEventListener("input",c),b.addEventListener("change",c),b.dispatchEvent(new Event("change")))})},p=a=>a.replace(/-[a-z]+$/,"");return Object.assign({load:()=>{document.body.addEventListener("tsf-ready",n),document.body.addEventListener("tsf-ready",k),document.body.addEventListener("tsf-ready",o)}},{},{l10n:b})}(jQuery),window.tsfMedia.load();
1
+ 'use strict';window.tsfMedia=function(a){const b="undefined"!=typeof tsfMediaL10n&&tsfMediaL10n;let c={};const d=a=>{const d=a.target;if(d.disabled||"undefined"==typeof wp.media)return a.preventDefault(),void a.stopPropagation();const f=d.dataset.inputType||"",i=d.dataset.inputId||"";let j;a.preventDefault(),a.stopPropagation(),g();const k={suggestedWidth:+(d.dataset.width||1200),suggestedHeight:+(d.dataset.height||630),isFlex:+(d.dataset.flex||1),minWidth:+(d.dataset.minWidth||200),minHeight:+(d.dataset.minHeight||200)};c.control={params:{flex_width:k.isFlex?4096:0,flex_height:k.isFlex?4096:0,width:k.suggestedWidth,height:k.suggestedHeight,isFlex:k.isFlex,minWidth:k.minWidth,minHeight:k.minHeight}},j=wp.media({button:{text:b.labels[f].imgFrameButton,close:!1},states:[new wp.media.controller.Library({title:b.labels[f].imgFrameTitle,library:wp.media.query({type:"image"}),multiple:!1,date:!1,priority:20,suggestedWidth:k.suggestedWidth,suggestedHeight:k.suggestedHeight}),new c({imgSelectOptions:h})]});const l=document.getElementById(`${i}-url`),m=document.getElementById(`${i}-id`),n=()=>{j.setState("cropper")};j.off("select",n),j.on("select",n);const o=a=>{let b=a.url,c=a.id;l&&(l.value=b,l.dispatchEvent(new Event("change"))),m&&(m.value=c,m.dispatchEvent(new Event("change")))};j.off("cropped",o),j.on("cropped",o);const p=a=>{let b=a.get("url"),c=a.get("id");l&&(l.value=b,l.dispatchEvent(new Event("change"))),m&&(m.value=c,m.dispatchEvent(new Event("change")))};j.off("skippedcrop",p),j.on("skippedcrop",p);const q=()=>{d.innerText=b.labels[f].imgChange,l&&(l.readOnly=!0),e(d,!0),"tsfAys"in window&&tsfAys.registerChange()};j.off("skippedcrop cropped",q),j.on("skippedcrop cropped",q),j.open()},e=(c,d)=>{const e=c.dataset.inputId||"",f=c.dataset.inputType||"";if(e&&f){const g=document.getElementById(`${e}-remove`);if(!g){let g=document.createElement("button");g.type="button",g.id=`${e}-remove`,g.dataset.inputId=e,g.dataset.inputType=f,g.title=tsf.decodeEntities(b.labels[f].imgRemoveTitle),g.innerHTML=tsf.escapeString(b.labels[f].imgRemove),g.classList.add("tsf-remove-image-button","button","button-small"),c.insertAdjacentElement("afterend",g),d&&a(g).css("opacity",0).animate({opacity:1},{queue:!0,duration:1e3}),m()}}},f=c=>{const d=c.target.dataset.inputId||"",e=c.target.dataset.inputType||"";if(!d||!e)return;const f=document.getElementById(`${d}-select`);if(f.disabled)return;f.disabled=!0,f.classList.add("disabled");const g=document.getElementById(`${d}-remove`);g&&(g.disabled=!0,g.classList.add("disabled"),a(g).fadeOut(250,()=>{g.remove(),f.innerText=b.labels[e].imgSelect,f.classList.remove("disabled"),f.disabled=!1}));const h=document.getElementById(`${d}-url`);h&&(h.value="",h.dispatchEvent(new Event("change")),!h.dataset.readonly&&(h.readOnly=!1));const i=document.getElementById(`${d}-id`);i&&(i.value="",i.dispatchEvent(new Event("change"))),"tsfAys"in window&&tsfAys.registerChange()},g=()=>{if("undefined"!=typeof c.control)return;const a=wp.media.view,d=a.Cropper.extend({className:"crop-content tsf-image",ready:function(){a.Cropper.prototype.ready.apply(this,arguments)},onImageLoad:function(){let a,b=this.controller.get("imgSelectOptions");"function"==typeof b&&(b=b(this.options.attachment,this.controller)),"undefined"==typeof b.aspectRatio&&(b=_.extend(b,{parent:this.$el,onInit:function(){this.parent.children().on("mousedown touchstart",function(b){b.shiftKey?a.setOptions({aspectRatio:"1:1"}):a.setOptions({aspectRatio:!1})})}})),this.trigger("image-loaded"),a=this.controller.imgSelect=this.$image.imgAreaSelect(b)}}),e=wp.media.controller.Cropper.extend({createCropContent:function(){this.cropperView=new d({controller:this,attachment:this.get("selection").first()}),this.cropperView.on("image-loaded",this.createCropToolbar,this),this.frame.content.set(this.cropperView)},doCrop:function(a){var d=Math.round;let e=a.get("cropDetails"),f=c.control;if(f.params.flex_width&&f.params.flex_height)if(e.width===e.height)e.width>f.params.flex_width&&(e.dst_width=e.dst_height=f.params.flex_width);else if(e.width>f.params.flex_width||e.height>f.params.flex_height)if(e.width>e.height){let a=e.width/f.params.flex_width;e.dst_width=f.params.flex_width,e.dst_height=d(e.height/a)}else{let a=e.height/f.params.flex_height;e.dst_height=f.params.flex_height,e.dst_width=d(e.width/a)}return"undefined"==typeof e.dst_width&&(e.dst_width=0,e.dst_height=0),wp.ajax.post("tsf-crop-image",{nonce:b.nonce,id:a.get("id"),context:"tsf-image",cropDetails:e})}});e.prototype.control={},c=e},h=(a,b)=>{const d=c.control;let e=parseInt(d.params.width,10),f=parseInt(d.params.height,10);const g=!!parseInt(d.params.flex_width,10),h=!!parseInt(d.params.flex_height,10),j=a.get("width"),k=a.get("height"),l=e/f,m=e,n=f;let o;o=d.params.isFlex?!i(d.params.flex_width,d.params.flex_height,j,k):l===j/k,b.set("control",d.params),b.set("canSkipCrop",o),j/k>l?(f=k,e=f*l):(e=j,f=e/l);let p=(j-e)/2,q=(k-f)/2;const r={handles:!0,keys:!0,instance:!0,persistent:!0,imageWidth:j,imageHeight:k,minWidth:m>e?e:m,minHeight:n>f?f:n,x1:p,y1:q,x2:e+p,y2:f+q};return d.params.isFlex?h||g?(h&&(r.minHeight=d.params.minHeight,r.maxWidth=j),g&&(r.minWidth=d.params.minWidth,r.maxHeight=k)):r.aspectRatio=e+":"+f:(r.handles="corners",r.aspectRatio=e+":"+f),r},i=(a,b,c,d)=>!(c<=a&&d<=b),j=a=>{const c=a.target.dataset.id||"",d=a.target.dataset.type||"";if(c&&d){const e=document.getElementById(`${c}-select`);e.disabled||(e.innerText=a.target.value.length?b.labels[d].imgChange:b.labels[d].imgSelect)}},k=()=>{document.querySelectorAll(".tsf-set-image-button").forEach(a=>{const b=a.dataset.inputId||"",c=b&&document.getElementById(`${b}-id`),d=b&&document.getElementById(`${b}-url`);c&&0<c.value&&(d&&(d.readOnly=!0),e(a,!1)),d&&(d.addEventListener("change",j),d.dispatchEvent(new Event("change")))})},l=()=>{document.querySelectorAll(".tsf-set-image-button").forEach(a=>{a.addEventListener("click",d)})},m=()=>{document.querySelectorAll(".tsf-remove-image-button").forEach(a=>{a.addEventListener("click",f)})},n=()=>{l(),m(),document.querySelectorAll(".tsf-enable-media-if-js").forEach(a=>{a.disabled=!1,a.classList.remove("tsf-enable-media-if-js")})};let o;const p=()=>{clearTimeout(o),o=setTimeout(n,500)},q=()=>{let b={};const c=c=>{const d=r(c.target.id||""),e=d&&document.getElementById(`${d}-preview`);if(!e)return;d in b&&clearTimeout(b[d]);let f=e.dataset.tsfLoaded||!1;e.dataset.tsfLoaded=1;let g=c.target.value||c.target.placeholder||"";b[d]=setTimeout(()=>{return g.length?void(e.dataset.desc="<img src='"+tsf.escapeString(g)+"' style="+`max-width:225px;max-height:225px;min-width:60px;min-height:60px;border-radius:3px;display:block;`+" />",f?a(e).not(":visible").fadeIn(250):a(e).show(),new Image().src=g,tsfTT.triggerUpdate(e)):void(f?a(e).not(":hidden").fadeOut(250):a(e).hide())},f&&g.length?500:0)};document.querySelectorAll(".tsf-image-preview").forEach(a=>{const b=document.getElementById(`${a.dataset.for}-url`);b&&(b.addEventListener("input",c),b.addEventListener("change",c),b.dispatchEvent(new Event("change")))})},r=a=>a.replace(/-[a-z]+$/,"");return Object.assign({load:()=>{document.body.addEventListener("tsf-ready",n),document.body.addEventListener("tsf-ready",k),document.body.addEventListener("tsf-ready",q)}},{resetImageEditorActions:p},{l10n:b})}(jQuery),window.tsfMedia.load();
lib/js/post.js CHANGED
@@ -122,13 +122,16 @@ window.tsfPost = function( $ ) {
122
  * @return {undefined}
123
  */
124
  const triggerResize = target => {
125
- window.dispatchEvent( new CustomEvent( 'tsf-flex-resize', {
126
- bubbles: false,
127
- cancelable: false,
128
- detail: {
129
- target
130
- },
131
- } ) );
 
 
 
132
  }
133
  if ( 'undefined' !== typeof window.ResizeObserver ) {
134
  let resizeAnimationFrame = {};
@@ -340,22 +343,156 @@ window.tsfPost = function( $ ) {
340
  * Initializes canonical URL meta input listeners.
341
  *
342
  * @since 4.0.0
 
343
  * @access private
344
  *
345
  * @function
346
  * @return {undefined}
347
  */
348
- const _initCanonicalInput = () => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
- let canonicalInput = $( '#autodescription_canonical' );
351
 
352
- if ( ! canonicalInput ) return;
 
353
 
354
- const updateCanonical = ( link ) => {
355
- canonicalInput.attr( 'placeholder', link );
 
 
 
 
356
  }
357
 
 
 
 
 
 
 
 
 
 
 
 
358
  $( document ).on( 'tsf-updated-gutenberg-link', ( event, link ) => updateCanonical( link ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
 
361
  /**
@@ -421,8 +558,7 @@ window.tsfPost = function( $ ) {
421
  * @param {string} visibility
422
  * @return {undefined}
423
  */
424
- const setTitleVisibilityPrefix = ( visibility ) => {
425
-
426
  let oldPrefixValue = tsfTitle.getStateOf( _titleId, 'prefixValue' ),
427
  prefixValue = '';
428
 
@@ -453,7 +589,7 @@ window.tsfPost = function( $ ) {
453
  * @param {Event} event
454
  * @return {undefined}
455
  */
456
- const setClassicTitleVisibilityPrefix = ( event ) => {
457
  let visibility = $( '#visibility' ).find( 'input:radio:checked' ).val();
458
  if ( 'password' === visibility ) {
459
  let pass = $( '#visibility' ).find( '#post_password' ).val();
@@ -481,7 +617,7 @@ window.tsfPost = function( $ ) {
481
  * @param {string} value
482
  * @return {undefined}
483
  */
484
- const updateDefaultTitle = ( val ) => {
485
  val = typeof val === 'string' && val.trim() || '';
486
 
487
  let defaultTitle = tsfTitle.stripTitleTags ? tsf.stripTags( val ) : val
@@ -505,6 +641,7 @@ window.tsfPost = function( $ ) {
505
  * Initializes description meta input listeners.
506
  *
507
  * @since 4.0.0
 
508
  * @access private
509
  *
510
  * @function
@@ -525,10 +662,63 @@ window.tsfPost = function( $ ) {
525
  tsfDescription.updateStateOf( _descId, 'defaultDescription', state.defaultDescription.trim() );
526
  tsfDescription.updateStateOf( _descId, 'hasLegacy', !! ( state.hasLegacy || false ) );
527
 
528
- // TODO set private/protected listeners, that will empty the generated description?
529
- // TODO set post-content (via ajax) listeners?
530
-
531
  tsfDescription.enqueueUnregisteredInputTrigger( _descId );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  }
533
 
534
  /**
@@ -691,7 +881,7 @@ window.tsfPost = function( $ ) {
691
  * @return {undefined}
692
  */
693
  const _loadSettings = () => {
694
- _initCanonicalInput();
695
  _initTitleListeners();
696
  _initDescriptionListeners();
697
  _initGeneralListeners();
122
  * @return {undefined}
123
  */
124
  const triggerResize = target => {
125
+ window.dispatchEvent( new CustomEvent(
126
+ 'tsf-flex-resize',
127
+ {
128
+ bubbles: false,
129
+ cancelable: false,
130
+ detail: {
131
+ target
132
+ },
133
+ }
134
+ ) );
135
  }
136
  if ( 'undefined' !== typeof window.ResizeObserver ) {
137
  let resizeAnimationFrame = {};
343
  * Initializes canonical URL meta input listeners.
344
  *
345
  * @since 4.0.0
346
+ * @since 4.1.2 Changed name from _initCanonicalInput
347
  * @access private
348
  *
349
  * @function
350
  * @return {undefined}
351
  */
352
+ const _initVisibilityListeners = () => {
353
+
354
+ // Prefix with B because I don't trust using 'protected' (might become reserved).
355
+ const BPROTECTED = 0b01,
356
+ BNOINDEX = 0b10;
357
+
358
+ const indexSelect = document.getElementById( 'autodescription_noindex' );
359
+
360
+ let canonicalPhState = 0b00,
361
+ canonicalUrl = '';
362
+
363
+ let updateCanonicalPlaceholderDebouncer = void 0;
364
+ /**
365
+ * @since 4.1.2
366
+ *
367
+ * @function
368
+ * @param {string} link
369
+ * @return {undefined}
370
+ */
371
+ const updateCanonicalPlaceholder = () => {
372
+ clearTimeout( updateCanonicalPlaceholderDebouncer );
373
+ updateCanonicalPlaceholderDebouncer = setTimeout( () => {
374
+ let canonicalInput = document.getElementById( 'autodescription_canonical' );
375
 
376
+ if ( ! canonicalInput ) return;
377
 
378
+ // Link might not've been updated (yet). Fill it in with PHP-supplied value (if any).
379
+ canonicalUrl = canonicalUrl || canonicalInput.placeholder;
380
 
381
+ if ( ( canonicalPhState & BPROTECTED ) || ( canonicalPhState & BNOINDEX ) ) {
382
+ canonicalInput.placeholder = '';
383
+ } else {
384
+ canonicalInput.placeholder = canonicalUrl;
385
+ }
386
+ }, 50 );
387
  }
388
 
389
+ /**
390
+ * @since 4.0.0
391
+ *
392
+ * @function
393
+ * @param {string} link
394
+ * @return {undefined}
395
+ */
396
+ const updateCanonical = link => {
397
+ canonicalUrl = link;
398
+ updateCanonicalPlaceholder();
399
+ }
400
  $( document ).on( 'tsf-updated-gutenberg-link', ( event, link ) => updateCanonical( link ) );
401
+
402
+ /**
403
+ * @since 4.1.2
404
+ *
405
+ * @function
406
+ * @param {string} visibility
407
+ * @return {undefined}
408
+ */
409
+ const setRobotsDefaultIndexingState = visibility => {
410
+ let _defaultIndexOption = indexSelect.querySelector( '[value="0"]' ),
411
+ indexDefaultValue = '';
412
+
413
+ switch ( visibility ) {
414
+ case 'password':
415
+ case 'private':
416
+ indexDefaultValue = 'noindex';
417
+ canonicalPhState |= BPROTECTED;
418
+ break;
419
+
420
+ default:
421
+ case 'public':
422
+ indexDefaultValue = indexSelect.dataset.defaultUnprotected;
423
+ canonicalPhState &= ~BPROTECTED;
424
+ break;
425
+ }
426
+
427
+ if ( _defaultIndexOption ) {
428
+ _defaultIndexOption.innerHTML = indexSelect.dataset.defaultI18n.replace( '%s', tsf.decodeEntities( indexDefaultValue ) );
429
+ }
430
+ updateCanonicalPlaceholder();
431
+ }
432
+ $( document ).on( 'tsf-updated-gutenberg-visibility', ( event, visibility ) => setRobotsDefaultIndexingState( visibility ) );
433
+
434
+ /**
435
+ * @since 4.1.2
436
+ *
437
+ * @function
438
+ * @param {Event} event
439
+ * @return {undefined}
440
+ */
441
+ const setClassicRobotsDefaultIndexingState = event => {
442
+ let visibility = $( '#visibility' ).find( 'input:radio:checked' ).val();
443
+ if ( 'password' === visibility ) {
444
+ let pass = $( '#visibility' ).find( '#post_password' ).val();
445
+ // A falsy-password (like '0'), will return true in "SOME OF" WP's front-end PHP, false in WP's JS before submitting...
446
+ // It won't invoke WordPress's password protection. TODO FIXME: file WP Core bug report.
447
+ if ( ! pass || ! pass.length ) {
448
+ visibility = 'public';
449
+ }
450
+ }
451
+ setRobotsDefaultIndexingState( visibility );
452
+ }
453
+ const classicVisibilityInput = document.querySelector( '#visibility .save-post-visibility' );
454
+ classicVisibilityInput && classicVisibilityInput.addEventListener( 'click', setClassicRobotsDefaultIndexingState );
455
+
456
+ if ( l10n.states.isPrivate ) {
457
+ setRobotsDefaultIndexingState( 'private' );
458
+ } else if ( l10n.states.isProtected ) {
459
+ setRobotsDefaultIndexingState( 'password' );
460
+ } else {
461
+ setRobotsDefaultIndexingState( 'public' );
462
+ }
463
+
464
+ /**
465
+ * @since 4.1.2
466
+ *
467
+ * @function
468
+ * @param {Number} value
469
+ * @return {undefined}
470
+ */
471
+ const setRobotsIndexingState = value => {
472
+ let type = '';
473
+
474
+ switch ( +value ) {
475
+ case 0: // default, unset since unknown.
476
+ type = indexSelect.dataset.defaultUnprotected;
477
+ break;
478
+ case -1: // index
479
+ type = 'index';
480
+ break;
481
+ case 1: // noindex
482
+ type = 'noindex';
483
+ break;
484
+ }
485
+ if ( 'noindex' === type ) {
486
+ canonicalPhState |= BNOINDEX;
487
+ } else {
488
+ canonicalPhState &= ~BNOINDEX;
489
+ }
490
+
491
+ updateCanonicalPlaceholder();
492
+ }
493
+ indexSelect.addEventListener( 'change', event => setRobotsIndexingState( event.target.value ) );
494
+
495
+ setRobotsIndexingState( indexSelect.value );
496
  }
497
 
498
  /**
558
  * @param {string} visibility
559
  * @return {undefined}
560
  */
561
+ const setTitleVisibilityPrefix = visibility => {
 
562
  let oldPrefixValue = tsfTitle.getStateOf( _titleId, 'prefixValue' ),
563
  prefixValue = '';
564
 
589
  * @param {Event} event
590
  * @return {undefined}
591
  */
592
+ const setClassicTitleVisibilityPrefix = event => {
593
  let visibility = $( '#visibility' ).find( 'input:radio:checked' ).val();
594
  if ( 'password' === visibility ) {
595
  let pass = $( '#visibility' ).find( '#post_password' ).val();
617
  * @param {string} value
618
  * @return {undefined}
619
  */
620
+ const updateDefaultTitle = val => {
621
  val = typeof val === 'string' && val.trim() || '';
622
 
623
  let defaultTitle = tsfTitle.stripTitleTags ? tsf.stripTags( val ) : val
641
  * Initializes description meta input listeners.
642
  *
643
  * @since 4.0.0
644
+ * @since 4.1.2 Now prefills the 'useDefaultDescription' accordingly.
645
  * @access private
646
  *
647
  * @function
662
  tsfDescription.updateStateOf( _descId, 'defaultDescription', state.defaultDescription.trim() );
663
  tsfDescription.updateStateOf( _descId, 'hasLegacy', !! ( state.hasLegacy || false ) );
664
 
 
 
 
665
  tsfDescription.enqueueUnregisteredInputTrigger( _descId );
666
+
667
+ /**
668
+ * Sets private/protected visibility state.
669
+ *
670
+ * @function
671
+ * @param {string} visibility
672
+ * @return {undefined}
673
+ */
674
+ const setDescriptionVisibility = visibility => {
675
+ let oldUseDefaultDescription = tsfDescription.getStateOf( _descId, 'useDefaultDescription' ),
676
+ useDefaultDescription = true;
677
+
678
+ switch ( visibility ) {
679
+ case 'password':
680
+ case 'private':
681
+ useDefaultDescription = false;
682
+ break;
683
+
684
+ default:
685
+ case 'public':
686
+ useDefaultDescription = true;
687
+ break;
688
+ }
689
+
690
+ if ( useDefaultDescription !== oldUseDefaultDescription )
691
+ tsfDescription.updateStateOf( _descId, 'useDefaultDescription', useDefaultDescription );
692
+ }
693
+ $( document ).on( 'tsf-updated-gutenberg-visibility', ( event, visibility ) => setDescriptionVisibility( visibility ) );
694
+
695
+ /**
696
+ * Sets private/protected visibility state for the classic editor.
697
+ *
698
+ * @function
699
+ * @param {Event} event
700
+ * @return {undefined}
701
+ */
702
+ const setClassicTitleVisibilityPrefix = event => {
703
+ let visibility = $( '#visibility' ).find( 'input:radio:checked' ).val();
704
+ if ( 'password' === visibility ) {
705
+ let pass = $( '#visibility' ).find( '#post_password' ).val();
706
+ // A falsy-password (like '0'), will return true in "SOME OF" WP's front-end PHP, false in WP's JS before submitting...
707
+ // It won't invoke WordPress's password protection. TODO FIXME: file WP Core bug report?
708
+ if ( ! pass || ! pass.length ) {
709
+ visibility = 'public';
710
+ }
711
+ }
712
+ setDescriptionVisibility( visibility );
713
+ }
714
+ const classicVisibilityInput = document.querySelector( '#visibility .save-post-visibility' );
715
+ classicVisibilityInput && classicVisibilityInput.addEventListener( 'click', setClassicTitleVisibilityPrefix );
716
+
717
+ if ( l10n.states.isPrivate ) {
718
+ setDescriptionVisibility( 'private' );
719
+ } else if ( l10n.states.isProtected ) {
720
+ setDescriptionVisibility( 'password' );
721
+ }
722
  }
723
 
724
  /**
881
  * @return {undefined}
882
  */
883
  const _loadSettings = () => {
884
+ _initVisibilityListeners();
885
  _initTitleListeners();
886
  _initDescriptionListeners();
887
  _initGeneralListeners();
lib/js/post.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfPost=function(a){const b="undefined"!=typeof tsfPostL10n&&tsfPostL10n,c="autodescription_title",d="autodescription_description",e=()=>{if(!document.querySelector(".tsf-flex"))return;let b={};const c=d=>{let e=d.querySelector(".tsf-flex-nav-tab-inner"),f=d.getElementsByClassName("tsf-flex-nav-name"),g=e.clientWidth<=d.clientWidth;if(g){if(+(d.dataset.displayedNames||1))return;d.dataset.displayedNames=1,a(f).stop(!1,!0).fadeIn(250)}else{if(!+(d.dataset.displayedNames||1))return;d.dataset.displayedNames=0,a(f).hide()}+d.dataset.displayedNames&&(e.clientWidth>d.clientWidth?(a(f).stop(!1,!0).hide(),d.dataset.displayedNames=0):setTimeout(()=>{cancelAnimationFrame(b[d.id]),b[d.id]=requestAnimationFrame(()=>c(d))},7))};window.addEventListener("tsf-flex-resize",a=>{let d=a.detail.target||document.getElementById("tsf-flex-inpost-tabs-wrapper");d&&(b[d.id]=requestAnimationFrame(()=>c(d)))});const d=a=>{window.dispatchEvent(new CustomEvent("tsf-flex-resize",{bubbles:!1,cancelable:!1,detail:{target:a}}))};if("undefined"!=typeof window.ResizeObserver){let a={};const b=new ResizeObserver(b=>{for(const c of b){let b=c.target;cancelAnimationFrame(a[b.id]),a[b.id]=requestAnimationFrame(()=>{b.dataset.lastWidth||(b.dataset.lastWidth=0),+b.clientWidth!=+b.dataset.lastWidth&&(b.dataset.lastWidth=b.clientWidth,d(b))})}});b.observe(document.getElementById("tsf-flex-inpost-tabs-wrapper"))}else{const b=()=>setTimeout(d,10);a(document).on("wp-window-resized orientationchange",b),a("#collapse-menu").on("click",b),a(".columns-prefs :input[type=radio]").on("change",b),a(".meta-box-sortables").on("sortupdate",b),a(document).on("postbox-moved",b),a("#tsf-inpost-box .handle-order-higher, #tsf-inpost-box .handle-order-lower").on("click",b)}d()},f=()=>{let b={},c={},d={},e={};const f=new CustomEvent("tsf-flex-tab-toggled"),g=g=>{const h=g.target,i=!g.isTrusted,j=g.target.id,k=g.target.name;d.hasOwnProperty(k)||(d[k]=h.closest(".tsf-flex-nav-tab-wrapper"));const l="tsf-flex-tab-content-active",m="tsf-flex-tab-active",n=d[k].querySelector(`.${m}`);if(!i){const a=document.querySelector(`.${l} :invalid`);if(a)return a.reportValidity(),n&&(n.checked=!0),h.checked=!1,g.stopPropagation(),g.preventDefault(),!1}if(n){n.classList.remove(m);let a=document.querySelector(`.tsf-flex-nav-tab-label[for="${n.id}"]`);a&&a.classList.remove("tsf-no-focus-ring")}if(h.classList.add(m),i){const a=document.getElementById(`${j}-content`);if(!a.classList.contains(l)){const b=document.querySelectorAll(`.${k}-content`);b&&b.forEach(a=>{a.classList.remove(l)}),a&&a.classList.add(l)}document.getElementById(j).dispatchEvent(f)}else{e.hasOwnProperty(k)||(e[k]=h.closest(".inside"),b[k]=void 0);const d=150;if(c[k]=j,"undefined"!=typeof b[k])return;const g=a("."+k+"-content"),i=()=>(a(`#${c[k]}-content`).stop(!1,!0).addClass(l).fadeIn(250),e[k].style.minHeight="",new Promise(a=>setTimeout(a,d))),m=()=>{e[k].style.minHeight=e[k].getBoundingClientRect().height};b[k]=()=>new Promise(b=>{m(),a.when(g.stop(!1,!0).fadeOut(d)).then(()=>{g.removeClass(l),b()})}).then(i).then(()=>{let a=document.getElementById(`${c[k]}-content`);!a||a.classList.contains(l)?(document.getElementById(c[k]).dispatchEvent(f),b[k]=void 0):(m(),g.removeClass(l),b[k]())}),b[k]()}},h=a=>a.currentTarget.classList.add("tsf-no-focus-ring");document.querySelectorAll(".tsf-flex-nav-tab-wrapper .tsf-flex-nav-tab-label").forEach(a=>{a.addEventListener("click",h)}),document.querySelectorAll(".tsf-flex-nav-tab-radio").forEach(a=>{a.addEventListener("change",g)}),document.querySelectorAll(".tsf-flex-nav-tab-radio:checked").forEach(a=>{a.dispatchEvent(new Event("change"))})},g=()=>{let b=a("#autodescription_canonical");if(!b)return;const c=a=>{b.attr("placeholder",a)};a(document).on("tsf-updated-gutenberg-link",(a,b)=>c(b))},h=()=>{const d=document.getElementById(c);if(!d)return;const e=document.getElementById("autodescription_title_no_blogname");tsfTitle.setInputElement(d);let f=JSON.parse(document.getElementById("tsf-title-data_"+c).dataset.state);tsfTitle.updateStateOf(c,"allowReferenceChange",!f.refTitleLocked),tsfTitle.updateStateOf(c,"defaultTitle",f.defaultTitle.trim()),tsfTitle.updateStateOf(c,"addAdditions",f.addAdditions),tsfTitle.updateStateOf(c,"useSocialTagline",!!(f.useSocialTagline||!1)),tsfTitle.updateStateOf(c,"additionValue",f.additionValue.trim()),tsfTitle.updateStateOf(c,"additionPlacement",f.additionPlacement),tsfTitle.updateStateOf(c,"hasLegacy",!!(f.hasLegacy||!1));e&&(e.addEventListener("change",a=>{let d=tsfTitle.getStateOf(c,"addAdditions"),e=!a.target.checked;b.params.additionsForcedDisabled?e=!1:b.params.additionsForcedEnabled&&(e=!0),d!==e&&tsfTitle.updateStateOf(c,"addAdditions",e)}),e.dispatchEvent(new Event("change")));const g=a=>{let b=tsfTitle.getStateOf(c,"prefixValue"),d="";switch(a){case"password":d=tsfTitle.protectedPrefix;break;case"private":d=tsfTitle.privatePrefix;break;default:case"public":d="";}d!==b&&tsfTitle.updateStateOf(c,"prefixValue",d)};a(document).on("tsf-updated-gutenberg-visibility",(a,b)=>g(b));const h=document.querySelector("#visibility .save-post-visibility");h&&h.addEventListener("click",()=>{let b=a("#visibility").find("input:radio:checked").val();if("password"===b){let c=a("#visibility").find("#post_password").val();c&&c.length||(b="public")}g(b)}),b.states.isPrivate?g("private"):b.states.isProtected&&g("password");const i=a=>{a="string"==typeof a&&a.trim()||"";let b=tsfTitle.stripTitleTags?tsf.stripTags(a):a;b=b||tsfTitle.untitledTitle,tsfTitle.updateStateOf(c,"defaultTitle",b)};if(!b.params.isFront){const b=document.querySelector("#titlewrap #title");b&&b.addEventListener("input",a=>i(a.target.value)),a(document).on("tsf-updated-gutenberg-title",(a,b)=>i(b))}tsfTitle.enqueueUnregisteredInputTrigger(c)},i=()=>{let a=document.getElementById(d);if(a){let b=JSON.parse(document.getElementById("tsf-description-data_"+d).dataset.state);tsfDescription.setInputElement(a),tsfDescription.updateStateOf(d,"allowReferenceChange",!b.refDescriptionLocked),tsfDescription.updateStateOf(d,"defaultDescription",b.defaultDescription.trim()),tsfDescription.updateStateOf(d,"hasLegacy",!(!b.hasLegacy&&1)),tsfDescription.enqueueUnregisteredInputTrigger(d)}},j=()=>{const b=()=>{tsfTitle.enqueueUnregisteredInputTrigger(c),tsfDescription.enqueueUnregisteredInputTrigger(d)};a("#tsf-flex-inpost-tab-general").on("tsf-flex-tab-toggled",b),window.addEventListener("tsf-flex-resize",b)},k=()=>{b.states.isGutenbergPage&&("tsfTT"in window&&tsfTT.addBoundary("#editor"),document.addEventListener("tsf-gutenberg-sidebar-opened",()=>{"tsfTT"in window&&tsfTT.addBoundary(".edit-post-sidebar .components-panel")}))},l=()=>{if(!b.states.isGutenbergPage)return;const c=document.querySelector(".tsf-seo-bar"),e=document.querySelector("#tsf-doing-it-right-wrap .tsf-ajax"),f=document.getElementById(d),g=document.getElementById("autodescription_og_description"),h=document.getElementById("autodescription_twitter_description"),i=document.getElementById("autodescription_socialimage-url"),j={seobar:!!c,metadescription:!!f,ogdescription:!!g,twdescription:!!h,imageurl:!!i},k=b=>{switch(b=tsf.convertJSONResponse(b),b.type){case"success":const f=75;setTimeout(()=>{tsfDescription&&tsfDescription.updateStateOf(d,"defaultDescription",b.data.metadescription.trim()),tsfSocial&&(tsfSocial.updateState("ogDescPlaceholder",b.data.ogdescription.trim()),tsfSocial.updateState("twDescPlaceholder",b.data.twdescription.trim())),i.placeholder=tsf.decodeEntities(b.data.imageurl),i.dispatchEvent(new Event("change")),"tsfAys"in window&&tsfAys.reset()},f),a(c).fadeOut(f,()=>{e&&tsf.unsetAjaxLoader(e,!0)}).html(b.data.seobar).fadeIn(500,()=>{"tsfTT"in window&&tsfTT.triggerReset()});break;case"failure":e&&tsf.unsetAjaxLoader(e,!1);break;default:e&&tsf.resetAjaxLoader(e);}},l=()=>{e&&tsf.unsetAjaxLoader(e,!1)};document.addEventListener("tsf-gutenberg-onsave",()=>{e&&tsf.resetAjaxLoader(e),e&&tsf.setAjaxLoader(e);let c={method:"POST",url:ajaxurl,datatype:"json",data:{action:"the_seo_framework_update_post_data",nonce:tsf.l10n.nonces.edit_posts,post_id:b.states.id,get:j},async:!0,timeout:7e3,success:k,error:l};a.ajax(c)})},m=()=>{g(),h(),i(),j()},n=()=>{e(),f(),k(),l(),tsfSocial.initTitleInputs({ref:document.getElementById("tsf-title-reference_"+c),refNa:document.getElementById("tsf-title-noadditions-reference_"+c),meta:document.getElementById(c),og:document.getElementById("autodescription_og_title"),tw:document.getElementById("autodescription_twitter_title")}),tsfSocial.initDescriptionInputs({ref:document.getElementById("tsf-description-reference_"+d),meta:document.getElementById(d),og:document.getElementById("autodescription_og_description"),tw:document.getElementById("autodescription_twitter_description")})};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",m),document.body.addEventListener("tsf-ready",n)}},{},{l10n:b})}(jQuery),window.tsfPost.load();
1
+ 'use strict';window.tsfPost=function(a){const b="undefined"!=typeof tsfPostL10n&&tsfPostL10n,c="autodescription_title",d="autodescription_description",e=()=>{if(!document.querySelector(".tsf-flex"))return;let b={};const c=d=>{let e=d.querySelector(".tsf-flex-nav-tab-inner"),f=d.getElementsByClassName("tsf-flex-nav-name"),g=e.clientWidth<=d.clientWidth;if(g){if(+(d.dataset.displayedNames||1))return;d.dataset.displayedNames=1,a(f).stop(!1,!0).fadeIn(250)}else{if(!+(d.dataset.displayedNames||1))return;d.dataset.displayedNames=0,a(f).hide()}+d.dataset.displayedNames&&(e.clientWidth>d.clientWidth?(a(f).stop(!1,!0).hide(),d.dataset.displayedNames=0):setTimeout(()=>{cancelAnimationFrame(b[d.id]),b[d.id]=requestAnimationFrame(()=>c(d))},7))};window.addEventListener("tsf-flex-resize",a=>{let d=a.detail.target||document.getElementById("tsf-flex-inpost-tabs-wrapper");d&&(b[d.id]=requestAnimationFrame(()=>c(d)))});const d=a=>{window.dispatchEvent(new CustomEvent("tsf-flex-resize",{bubbles:!1,cancelable:!1,detail:{target:a}}))};if("undefined"!=typeof window.ResizeObserver){let a={};const b=new ResizeObserver(b=>{for(const c of b){let b=c.target;cancelAnimationFrame(a[b.id]),a[b.id]=requestAnimationFrame(()=>{b.dataset.lastWidth||(b.dataset.lastWidth=0),+b.clientWidth!=+b.dataset.lastWidth&&(b.dataset.lastWidth=b.clientWidth,d(b))})}});b.observe(document.getElementById("tsf-flex-inpost-tabs-wrapper"))}else{const b=()=>setTimeout(d,10);a(document).on("wp-window-resized orientationchange",b),a("#collapse-menu").on("click",b),a(".columns-prefs :input[type=radio]").on("change",b),a(".meta-box-sortables").on("sortupdate",b),a(document).on("postbox-moved",b),a("#tsf-inpost-box .handle-order-higher, #tsf-inpost-box .handle-order-lower").on("click",b)}d()},f=()=>{let b={},c={},d={},e={};const f=new CustomEvent("tsf-flex-tab-toggled"),g=g=>{const h=g.target,i=!g.isTrusted,j=g.target.id,k=g.target.name;d.hasOwnProperty(k)||(d[k]=h.closest(".tsf-flex-nav-tab-wrapper"));const l="tsf-flex-tab-content-active",m="tsf-flex-tab-active",n=d[k].querySelector(`.${m}`);if(!i){const a=document.querySelector(`.${l} :invalid`);if(a)return a.reportValidity(),n&&(n.checked=!0),h.checked=!1,g.stopPropagation(),g.preventDefault(),!1}if(n){n.classList.remove(m);let a=document.querySelector(`.tsf-flex-nav-tab-label[for="${n.id}"]`);a&&a.classList.remove("tsf-no-focus-ring")}if(h.classList.add(m),i){const a=document.getElementById(`${j}-content`);if(!a.classList.contains(l)){const b=document.querySelectorAll(`.${k}-content`);b&&b.forEach(a=>{a.classList.remove(l)}),a&&a.classList.add(l)}document.getElementById(j).dispatchEvent(f)}else{e.hasOwnProperty(k)||(e[k]=h.closest(".inside"),b[k]=void 0);const d=150;if(c[k]=j,"undefined"!=typeof b[k])return;const g=a("."+k+"-content"),i=()=>(a(`#${c[k]}-content`).stop(!1,!0).addClass(l).fadeIn(250),e[k].style.minHeight="",new Promise(a=>setTimeout(a,d))),m=()=>{e[k].style.minHeight=e[k].getBoundingClientRect().height};b[k]=()=>new Promise(b=>{m(),a.when(g.stop(!1,!0).fadeOut(d)).then(()=>{g.removeClass(l),b()})}).then(i).then(()=>{let a=document.getElementById(`${c[k]}-content`);!a||a.classList.contains(l)?(document.getElementById(c[k]).dispatchEvent(f),b[k]=void 0):(m(),g.removeClass(l),b[k]())}),b[k]()}},h=a=>a.currentTarget.classList.add("tsf-no-focus-ring");document.querySelectorAll(".tsf-flex-nav-tab-wrapper .tsf-flex-nav-tab-label").forEach(a=>{a.addEventListener("click",h)}),document.querySelectorAll(".tsf-flex-nav-tab-radio").forEach(a=>{a.addEventListener("change",g)}),document.querySelectorAll(".tsf-flex-nav-tab-radio:checked").forEach(a=>{a.dispatchEvent(new Event("change"))})},g=()=>{const c=1,d=2,e=document.getElementById("autodescription_noindex");let f,g=0,h="";const i=()=>{clearTimeout(f),f=setTimeout(()=>{let a=document.getElementById("autodescription_canonical");a&&(h=h||a.placeholder,a.placeholder=g&c||g&d?"":h)},50)},j=a=>{h=a,i()};a(document).on("tsf-updated-gutenberg-link",(a,b)=>j(b));const k=a=>{let b=e.querySelector("[value=\"0\"]"),d="";switch(a){case"password":case"private":d="noindex",g|=c;break;default:case"public":d=e.dataset.defaultUnprotected,g&=~c;}b&&(b.innerHTML=e.dataset.defaultI18n.replace("%s",tsf.decodeEntities(d))),i()};a(document).on("tsf-updated-gutenberg-visibility",(a,b)=>k(b));const l=document.querySelector("#visibility .save-post-visibility");l&&l.addEventListener("click",()=>{let b=a("#visibility").find("input:radio:checked").val();if("password"===b){let c=a("#visibility").find("#post_password").val();c&&c.length||(b="public")}k(b)}),b.states.isPrivate?k("private"):b.states.isProtected?k("password"):k("public");const m=a=>{let b="";switch(+a){case 0:b=e.dataset.defaultUnprotected;break;case-1:b="index";break;case 1:b="noindex";}"noindex"===b?g|=d:g&=~d,i()};e.addEventListener("change",a=>m(a.target.value)),m(e.value)},h=()=>{const d=document.getElementById(c);if(!d)return;const e=document.getElementById("autodescription_title_no_blogname");tsfTitle.setInputElement(d);let f=JSON.parse(document.getElementById("tsf-title-data_"+c).dataset.state);tsfTitle.updateStateOf(c,"allowReferenceChange",!f.refTitleLocked),tsfTitle.updateStateOf(c,"defaultTitle",f.defaultTitle.trim()),tsfTitle.updateStateOf(c,"addAdditions",f.addAdditions),tsfTitle.updateStateOf(c,"useSocialTagline",!!(f.useSocialTagline||!1)),tsfTitle.updateStateOf(c,"additionValue",f.additionValue.trim()),tsfTitle.updateStateOf(c,"additionPlacement",f.additionPlacement),tsfTitle.updateStateOf(c,"hasLegacy",!!(f.hasLegacy||!1));e&&(e.addEventListener("change",a=>{let d=tsfTitle.getStateOf(c,"addAdditions"),e=!a.target.checked;b.params.additionsForcedDisabled?e=!1:b.params.additionsForcedEnabled&&(e=!0),d!==e&&tsfTitle.updateStateOf(c,"addAdditions",e)}),e.dispatchEvent(new Event("change")));const g=a=>{let b=tsfTitle.getStateOf(c,"prefixValue"),d="";switch(a){case"password":d=tsfTitle.protectedPrefix;break;case"private":d=tsfTitle.privatePrefix;break;default:case"public":d="";}d!==b&&tsfTitle.updateStateOf(c,"prefixValue",d)};a(document).on("tsf-updated-gutenberg-visibility",(a,b)=>g(b));const h=document.querySelector("#visibility .save-post-visibility");h&&h.addEventListener("click",()=>{let b=a("#visibility").find("input:radio:checked").val();if("password"===b){let c=a("#visibility").find("#post_password").val();c&&c.length||(b="public")}g(b)}),b.states.isPrivate?g("private"):b.states.isProtected&&g("password");const i=a=>{a="string"==typeof a&&a.trim()||"";let b=tsfTitle.stripTitleTags?tsf.stripTags(a):a;b=b||tsfTitle.untitledTitle,tsfTitle.updateStateOf(c,"defaultTitle",b)};if(!b.params.isFront){const b=document.querySelector("#titlewrap #title");b&&b.addEventListener("input",a=>i(a.target.value)),a(document).on("tsf-updated-gutenberg-title",(a,b)=>i(b))}tsfTitle.enqueueUnregisteredInputTrigger(c)},i=()=>{let c=document.getElementById(d);if(!c)return;let e=JSON.parse(document.getElementById("tsf-description-data_"+d).dataset.state);tsfDescription.setInputElement(c),tsfDescription.updateStateOf(d,"allowReferenceChange",!e.refDescriptionLocked),tsfDescription.updateStateOf(d,"defaultDescription",e.defaultDescription.trim()),tsfDescription.updateStateOf(d,"hasLegacy",!!(e.hasLegacy||!1)),tsfDescription.enqueueUnregisteredInputTrigger(d);const f=a=>{let b=tsfDescription.getStateOf(d,"useDefaultDescription"),c=!0;switch(a){case"password":case"private":c=!1;break;default:case"public":c=!0;}c!==b&&tsfDescription.updateStateOf(d,"useDefaultDescription",c)};a(document).on("tsf-updated-gutenberg-visibility",(a,b)=>f(b));const g=document.querySelector("#visibility .save-post-visibility");g&&g.addEventListener("click",()=>{let b=a("#visibility").find("input:radio:checked").val();if("password"===b){let c=a("#visibility").find("#post_password").val();c&&c.length||(b="public")}f(b)}),b.states.isPrivate?f("private"):b.states.isProtected&&f("password")},j=()=>{const b=()=>{tsfTitle.enqueueUnregisteredInputTrigger(c),tsfDescription.enqueueUnregisteredInputTrigger(d)};a("#tsf-flex-inpost-tab-general").on("tsf-flex-tab-toggled",b),window.addEventListener("tsf-flex-resize",b)},k=()=>{b.states.isGutenbergPage&&("tsfTT"in window&&tsfTT.addBoundary("#editor"),document.addEventListener("tsf-gutenberg-sidebar-opened",()=>{"tsfTT"in window&&tsfTT.addBoundary(".edit-post-sidebar .components-panel")}))},l=()=>{if(!b.states.isGutenbergPage)return;const c=document.querySelector(".tsf-seo-bar"),e=document.querySelector("#tsf-doing-it-right-wrap .tsf-ajax"),f=document.getElementById(d),g=document.getElementById("autodescription_og_description"),h=document.getElementById("autodescription_twitter_description"),i=document.getElementById("autodescription_socialimage-url"),j={seobar:!!c,metadescription:!!f,ogdescription:!!g,twdescription:!!h,imageurl:!!i},k=b=>{switch(b=tsf.convertJSONResponse(b),b.type){case"success":const f=75;setTimeout(()=>{tsfDescription&&tsfDescription.updateStateOf(d,"defaultDescription",b.data.metadescription.trim()),tsfSocial&&(tsfSocial.updateState("ogDescPlaceholder",b.data.ogdescription.trim()),tsfSocial.updateState("twDescPlaceholder",b.data.twdescription.trim())),i.placeholder=tsf.decodeEntities(b.data.imageurl),i.dispatchEvent(new Event("change")),"tsfAys"in window&&tsfAys.reset()},f),a(c).fadeOut(f,()=>{e&&tsf.unsetAjaxLoader(e,!0)}).html(b.data.seobar).fadeIn(500,()=>{"tsfTT"in window&&tsfTT.triggerReset()});break;case"failure":e&&tsf.unsetAjaxLoader(e,!1);break;default:e&&tsf.resetAjaxLoader(e);}},l=()=>{e&&tsf.unsetAjaxLoader(e,!1)};document.addEventListener("tsf-gutenberg-onsave",()=>{e&&tsf.resetAjaxLoader(e),e&&tsf.setAjaxLoader(e);let c={method:"POST",url:ajaxurl,datatype:"json",data:{action:"the_seo_framework_update_post_data",nonce:tsf.l10n.nonces.edit_posts,post_id:b.states.id,get:j},async:!0,timeout:7e3,success:k,error:l};a.ajax(c)})},m=()=>{g(),h(),i(),j()},n=()=>{e(),f(),k(),l(),tsfSocial.initTitleInputs({ref:document.getElementById("tsf-title-reference_"+c),refNa:document.getElementById("tsf-title-noadditions-reference_"+c),meta:document.getElementById(c),og:document.getElementById("autodescription_og_title"),tw:document.getElementById("autodescription_twitter_title")}),tsfSocial.initDescriptionInputs({ref:document.getElementById("tsf-description-reference_"+d),meta:document.getElementById(d),og:document.getElementById("autodescription_og_description"),tw:document.getElementById("autodescription_twitter_description")})};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",m),document.body.addEventListener("tsf-ready",n)}},{},{l10n:b})}(jQuery),window.tsfPost.load();
lib/js/pt-gb.js CHANGED
@@ -232,7 +232,7 @@ window.tsfPTGB = function( $ ) {
232
 
233
  class TermSelector extends PrimaryTermSelectorHandler {
234
  constructor() {
235
- super(...arguments);
236
  this.onChange = this.onChange.bind( this );
237
  this.state = {
238
  loading: true,
232
 
233
  class TermSelector extends PrimaryTermSelectorHandler {
234
  constructor() {
235
+ super( ...arguments );
236
  this.onChange = this.onChange.bind( this );
237
  this.state = {
238
  loading: true,
lib/js/term.js CHANGED
@@ -61,24 +61,72 @@ window.tsfTerm = function( $ ) {
61
  * Initializes Canonical URL meta input listeners.
62
  *
63
  * @since 4.0.0
 
64
  * @access private
65
  *
66
  * @function
67
  * @return {undefined}
68
  */
69
- const _initCanonicalInput = () => {
 
70
 
71
- // TODO, listen to slug.
 
72
 
73
- // let canonicalInput = $( '#autodescription_canonical' );
 
 
 
 
 
 
 
 
74
 
75
- // if ( ! canonicalInput ) return;
76
 
77
- // const updateCanonical = ( link ) => {
78
- // canonicalInput.attr( 'placeholder', link );
79
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- // $( document ).on( 'tsf-updated-gutenberg-link', ( event, link ) => updateCanonical( link ) );
82
  }
83
 
84
  /**
@@ -224,7 +272,7 @@ window.tsfTerm = function( $ ) {
224
  * @return {undefined}
225
  */
226
  const _loadSettings = () => {
227
- _initCanonicalInput();
228
  _initTitleListeners();
229
  _initDescriptionListeners();
230
  }
61
  * Initializes Canonical URL meta input listeners.
62
  *
63
  * @since 4.0.0
64
+ * @since 4.1.2 Changed name from _initCanonicalInput
65
  * @access private
66
  *
67
  * @function
68
  * @return {undefined}
69
  */
70
+ const _initVisibilityListeners = () => {
71
+ const indexSelect = document.getElementById( 'autodescription-meta[noindex]' );
72
 
73
+ let canonicalUrl = '',
74
+ showcanonicalPh = true;
75
 
76
+ /**
77
+ * @since 4.1.2
78
+ *
79
+ * @function
80
+ * @param {string} link
81
+ * @return {undefined}
82
+ */
83
+ const updateCanonicalPlaceholder = () => {
84
+ let canonicalInput = document.getElementById( 'autodescription-meta[canonical]' );
85
 
86
+ if ( ! canonicalInput ) return;
87
 
88
+ // Link might not've been updated (yet). Fill it in with PHP-supplied value (if any).
89
+ canonicalUrl = canonicalUrl || canonicalInput.placeholder;
90
+
91
+ if ( ! showcanonicalPh ) {
92
+ canonicalInput.placeholder = '';
93
+ } else {
94
+ canonicalInput.placeholder = canonicalUrl;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @since 4.1.2
100
+ *
101
+ * @function
102
+ * @param {Number} value
103
+ * @return {undefined}
104
+ */
105
+ const setRobotsIndexingState = value => {
106
+ let type = '';
107
+
108
+ switch ( +value ) {
109
+ case 0: // default, unset since unknown.
110
+ type = indexSelect.dataset.defaultUnprotected;
111
+ break;
112
+ case -1: // index
113
+ type = 'index';
114
+ break;
115
+ case 1: // noindex
116
+ type = 'noindex';
117
+ break;
118
+ }
119
+ if ( 'noindex' === type ) {
120
+ showcanonicalPh = false;
121
+ } else {
122
+ showcanonicalPh = true;
123
+ }
124
+
125
+ updateCanonicalPlaceholder();
126
+ }
127
+ indexSelect.addEventListener( 'change', event => setRobotsIndexingState( event.target.value ) );
128
 
129
+ setRobotsIndexingState( indexSelect.value );
130
  }
131
 
132
  /**
272
  * @return {undefined}
273
  */
274
  const _loadSettings = () => {
275
+ _initVisibilityListeners();
276
  _initTitleListeners();
277
  _initDescriptionListeners();
278
  }
lib/js/term.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfTerm=function(){const a="undefined"!=typeof tsfTermL10n&&tsfTermL10n,b="autodescription-meta[doctitle]",c="autodescription-meta[description]",d=()=>{},e=()=>{const c=document.getElementById(b),d=document.getElementById("autodescription-meta[title_no_blog_name]");tsfTitle.setInputElement(c);let e=JSON.parse(document.getElementById("tsf-title-data_"+b).dataset.state);tsfTitle.updateStateOf(b,"allowReferenceChange",!e.refTitleLocked),tsfTitle.updateStateOf(b,"defaultTitle",e.defaultTitle.trim()),tsfTitle.updateStateOf(b,"addAdditions",e.addAdditions),tsfTitle.updateStateOf(b,"useSocialTagline",!!(e.useSocialTagline||!1)),tsfTitle.updateStateOf(b,"additionValue",e.additionValue.trim()),tsfTitle.updateStateOf(b,"additionPlacement",e.additionPlacement),tsfTitle.updateStateOf(b,"hasLegacy",!!(e.hasLegacy||!1));const f=tsf.escapeString(a.params.termPrefix);d&&(d.addEventListener("change",c=>{let d=tsfTitle.getStateOf(b,"addAdditions"),e=!c.target.checked;a.params.additionsForcedDisabled&&(e=!1),d!==e&&tsfTitle.updateStateOf(b,"addAdditions",e)}),d.dispatchEvent(new Event("change")));const g=a=>{a="string"==typeof a&&a.trim()||"";let c=tsfTitle.stripTitleTags?tsf.stripTags(a):a;c=c||tsfTitle.untitledTitle;let d;d=tsf.l10n.states.isRTL?c+" "+f:f+" "+c,tsfTitle.updateStateOf(b,"defaultTitle",d)},h=document.querySelector("#edittag #name");h&&h.addEventListener("input",a=>g(a.target.value)),tsfTitle.enqueueUnregisteredInputTrigger(b)},f=()=>{let a=JSON.parse(document.getElementById("tsf-description-data_"+c).dataset.state);tsfDescription.setInputElement(document.getElementById(c)),tsfDescription.updateStateOf(c,"defaultDescription",a.defaultDescription.trim()),tsfDescription.updateStateOf(c,"hasLegacy",!!(a.hasLegacy||!1)),tsfDescription.enqueueUnregisteredInputTrigger(c)},g=()=>{d(),e(),f()},h=()=>{tsfSocial.initTitleInputs({ref:document.getElementById("tsf-title-reference_"+b),refNa:document.getElementById("tsf-title-noadditions-reference_"+b),meta:document.getElementById(b),og:document.getElementById("autodescription-meta[og_title]"),tw:document.getElementById("autodescription-meta[tw_title]")}),tsfSocial.initDescriptionInputs({ref:document.getElementById("tsf-description-reference_"+c),meta:document.getElementById(c),og:document.getElementById("autodescription-meta[og_description]"),tw:document.getElementById("autodescription-meta[tw_description]")})};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",g),document.body.addEventListener("tsf-ready",h)}},{},{l10n:a})}(jQuery),window.tsfTerm.load();
1
+ 'use strict';window.tsfTerm=function(){const a="undefined"!=typeof tsfTermL10n&&tsfTermL10n,b="autodescription-meta[doctitle]",c="autodescription-meta[description]",d=()=>{const a=document.getElementById("autodescription-meta[noindex]");let b="",c=!0;const d=()=>{let a=document.getElementById("autodescription-meta[canonical]");a&&(b=b||a.placeholder,a.placeholder=c?b:"")},e=b=>{let e="";switch(+b){case 0:e=a.dataset.defaultUnprotected;break;case-1:e="index";break;case 1:e="noindex";}c="noindex"!==e,d()};a.addEventListener("change",a=>e(a.target.value)),e(a.value)},e=()=>{const c=document.getElementById(b),d=document.getElementById("autodescription-meta[title_no_blog_name]");tsfTitle.setInputElement(c);let e=JSON.parse(document.getElementById("tsf-title-data_"+b).dataset.state);tsfTitle.updateStateOf(b,"allowReferenceChange",!e.refTitleLocked),tsfTitle.updateStateOf(b,"defaultTitle",e.defaultTitle.trim()),tsfTitle.updateStateOf(b,"addAdditions",e.addAdditions),tsfTitle.updateStateOf(b,"useSocialTagline",!!(e.useSocialTagline||!1)),tsfTitle.updateStateOf(b,"additionValue",e.additionValue.trim()),tsfTitle.updateStateOf(b,"additionPlacement",e.additionPlacement),tsfTitle.updateStateOf(b,"hasLegacy",!!(e.hasLegacy||!1));const f=tsf.escapeString(a.params.termPrefix);d&&(d.addEventListener("change",c=>{let d=tsfTitle.getStateOf(b,"addAdditions"),e=!c.target.checked;a.params.additionsForcedDisabled&&(e=!1),d!==e&&tsfTitle.updateStateOf(b,"addAdditions",e)}),d.dispatchEvent(new Event("change")));const g=a=>{a="string"==typeof a&&a.trim()||"";let c=tsfTitle.stripTitleTags?tsf.stripTags(a):a;c=c||tsfTitle.untitledTitle;let d;d=tsf.l10n.states.isRTL?c+" "+f:f+" "+c,tsfTitle.updateStateOf(b,"defaultTitle",d)},h=document.querySelector("#edittag #name");h&&h.addEventListener("input",a=>g(a.target.value)),tsfTitle.enqueueUnregisteredInputTrigger(b)},f=()=>{let a=JSON.parse(document.getElementById("tsf-description-data_"+c).dataset.state);tsfDescription.setInputElement(document.getElementById(c)),tsfDescription.updateStateOf(c,"defaultDescription",a.defaultDescription.trim()),tsfDescription.updateStateOf(c,"hasLegacy",!!(a.hasLegacy||!1)),tsfDescription.enqueueUnregisteredInputTrigger(c)},g=()=>{d(),e(),f()},h=()=>{tsfSocial.initTitleInputs({ref:document.getElementById("tsf-title-reference_"+b),refNa:document.getElementById("tsf-title-noadditions-reference_"+b),meta:document.getElementById(b),og:document.getElementById("autodescription-meta[og_title]"),tw:document.getElementById("autodescription-meta[tw_title]")}),tsfSocial.initDescriptionInputs({ref:document.getElementById("tsf-description-reference_"+c),meta:document.getElementById(c),og:document.getElementById("autodescription-meta[og_description]"),tw:document.getElementById("autodescription-meta[tw_description]")})};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",g),document.body.addEventListener("tsf-ready",h)}},{},{l10n:a})}(jQuery),window.tsfTerm.load();
lib/js/tsf.js CHANGED
@@ -83,10 +83,8 @@ window.tsf = function( $ ) {
83
  const _canUseDOMParser = () => {
84
  if ( void 0 === _canUseDOMParserTest ) {
85
  try {
86
- if ( ( new DOMParser() ).parseFromString( '', 'text/html' ) ) {
87
- // text/html parsing is natively supported
88
- _canUseDOMParserTest = true;
89
- }
90
  } catch ( e ) { }
91
 
92
  _canUseDOMParserTest = !! _canUseDOMParserTest;
@@ -292,45 +290,6 @@ window.tsf = function( $ ) {
292
  return response;
293
  }
294
 
295
- /**
296
- * Dismissible notices that use notice wrapper class .tsf-notice.
297
- *
298
- * @since 2.6.0
299
- * @since 2.9.3 Now correctly removes the node from DOM.
300
- * @since 4.1.0 1. Now is more in line with how WordPress dismisses notices.
301
- * 2. Now also handles dismissible persistent notices.
302
- * @access private
303
- *
304
- * @function
305
- * @param {Event} event
306
- * @return {undefined}
307
- */
308
- const _dismissNotice = event => {
309
-
310
- let $notice = $( event.target ).parents( '.tsf-notice' ).first(),
311
- key = event.target.dataset && event.target.dataset.key || void 0,
312
- nonce = event.target.dataset && event.target.dataset.nonce || void 0;
313
-
314
- $notice.fadeTo( 100, 0, () => {
315
- $notice.slideUp( 100, () => {
316
- $notice.remove();
317
- } );
318
- } );
319
-
320
- if ( key && nonce ) {
321
- // The notice is removed regardless of this being completed.
322
- // Do not inform the user of its completion--it adds a lot to the annoyance.
323
- // Instead, rely on keeping the 'count' low!
324
- wp.ajax.post(
325
- 'tsf-dismiss-notice',
326
- {
327
- 'tsf-dismiss-key': key,
328
- 'tsf-dismiss-nonce': nonce,
329
- }
330
- );
331
- }
332
- }
333
-
334
  /**
335
  * Visualizes AJAX loading time through target class change.
336
  *
@@ -462,6 +421,83 @@ window.tsf = function( $ ) {
462
  } );
463
  }
464
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  let isInteractive = false;
466
  /**
467
  * Dispatches tsf-interactive event.
@@ -549,8 +585,8 @@ window.tsf = function( $ ) {
549
  // Sets postbox toggles on load.
550
  _initPostboxToggle();
551
 
552
- // Enable dismissal of PHP-inserted notices.
553
- document.querySelectorAll( '.tsf-dismiss' ).forEach( el => el.addEventListener( 'click', _dismissNotice ) );
554
 
555
  // Trigger tsf-ready event.
556
  _triggerReady();
@@ -616,6 +652,7 @@ window.tsf = function( $ ) {
616
  unsetAjaxLoader,
617
  resetAjaxLoader,
618
  deprecatedFunc,
 
619
  }, {
620
  l10n
621
  } );
83
  const _canUseDOMParser = () => {
84
  if ( void 0 === _canUseDOMParserTest ) {
85
  try {
86
+ // text/html parsing is natively supported when true.
87
+ _canUseDOMParserTest = !! ( new DOMParser() ).parseFromString( '', 'text/html' );
 
 
88
  } catch ( e ) { }
89
 
90
  _canUseDOMParserTest = !! _canUseDOMParserTest;
290
  return response;
291
  }
292
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  /**
294
  * Visualizes AJAX loading time through target class change.
295
  *
421
  } );
422
  }
423
 
424
+ /**
425
+ * Prepares notice dismissal listeners.
426
+ *
427
+ * @since 4.1.2
428
+ * @access private
429
+ *
430
+ * @function
431
+ */
432
+ const _initNotices = () => {
433
+
434
+ /**
435
+ * Dismissible notices that use notice wrapper class .tsf-notice.
436
+ *
437
+ * @since 2.6.0
438
+ * @since 2.9.3 Now correctly removes the node from DOM.
439
+ * @since 4.1.0 1. Now is more in line with how WordPress dismisses notices.
440
+ * 2. Now also handles dismissible persistent notices.
441
+ * @since 4.1.2 Moved inside other method.
442
+ *
443
+ * @function
444
+ * @param {Event} event
445
+ * @return {undefined}
446
+ */
447
+ const dismissNotice = event => {
448
+
449
+ let $notice = $( event.target ).parents( '.tsf-notice' ).first(),
450
+ key = event.target.dataset && event.target.dataset.key || void 0,
451
+ nonce = event.target.dataset && event.target.dataset.nonce || void 0;
452
+
453
+ $notice.fadeTo( 100, 0, () => {
454
+ $notice.slideUp( 100, () => {
455
+ $notice.remove();
456
+ } );
457
+ } );
458
+
459
+ if ( key && nonce ) {
460
+ // The notice is removed regardless of this being completed.
461
+ // Do not inform the user of its completion--it adds a lot to the annoyance.
462
+ // Instead, rely on keeping the 'count' low!
463
+ wp.ajax.post(
464
+ 'tsf-dismiss-notice',
465
+ {
466
+ 'tsf-dismiss-key': key,
467
+ 'tsf-dismiss-nonce': nonce,
468
+ }
469
+ );
470
+ }
471
+ }
472
+
473
+ const reset = () => {
474
+ // Enable dismissal of PHP-inserted notices.
475
+ document.querySelectorAll( '.tsf-dismiss' ).forEach( el => el.addEventListener( 'click', dismissNotice ) );
476
+ }
477
+ /**
478
+ * @access private Use triggerNoticeReset() instead.
479
+ */
480
+ document.body.addEventListener( 'tsf-reset-notice-listeners', reset );
481
+ reset();
482
+ }
483
+
484
+ let _debounceNoticeReset = void 0;
485
+ /**
486
+ * Invokes notice dismissal listener reset.
487
+ *
488
+ * @since 4.1.2
489
+ * @access public
490
+ *
491
+ * @function
492
+ */
493
+ const triggerNoticeReset = () => {
494
+ clearTimeout( _debounceNoticeReset );
495
+ _debounceNoticeReset = setTimeout(
496
+ () => document.body.dispatchEvent( new CustomEvent( 'tsf-reset-notice-listeners' ) ),
497
+ 100
498
+ );
499
+ }
500
+
501
  let isInteractive = false;
502
  /**
503
  * Dispatches tsf-interactive event.
585
  // Sets postbox toggles on load.
586
  _initPostboxToggle();
587
 
588
+ // Initializes notices
589
+ _initNotices();
590
 
591
  // Trigger tsf-ready event.
592
  _triggerReady();
652
  unsetAjaxLoader,
653
  resetAjaxLoader,
654
  deprecatedFunc,
655
+ triggerNoticeReset,
656
  }, {
657
  l10n
658
  } );
lib/js/tsf.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsf=function(a){const b="undefined"!=typeof tsfL10n&&tsfL10n;let c;const d=()=>{if(void 0===c){try{new DOMParser().parseFromString("","text/html")&&(c=!0)}catch(a){}c=!!c}return c};let e;const f=a=>{if("string"!=typeof a||!a.length)return"";let b={"<":"&#x3C;",">":"&#x3E;","\\":"&#x5C;"};return a=a.replace(/[<>\\]/g,a=>b[a]),d()?(e=e||new DOMParser,a=e.parseFromString(a,"text/html").documentElement.textContent):(e=e||document.createElement("span"),e.innerHTML=a,a=h(e.textContent)),a},g=a=>{if(!a.length)return"";let b={"&":"&#x26;","<":"&#x3C;",">":"&#x3E;",'"':"&#x22;","'":"&#x27;","\\":"&#x5C;","/":"&#x2F;"};return a.replace(/[&<>"'\\\/]/g,a=>b[a])},h=a=>a.replace(/&amp;|&#x0{0,3}26;|&#38;/gi,"&"),i=b=>{let c=a(b.target).parents(".tsf-notice").first(),d=b.target.dataset&&b.target.dataset.key||void 0,e=b.target.dataset&&b.target.dataset.nonce||void 0;c.fadeTo(100,0,()=>{c.slideUp(100,()=>{c.remove()})}),d&&e&&wp.ajax.post("tsf-dismiss-notice",{"tsf-dismiss-key":d,"tsf-dismiss-nonce":e})},j=()=>{let b=a(".postbox[id^=\"autodescription-\"], .postbox#tsf-inpost-box");a(document).on("postbox-toggled",(c,d)=>{if(!d||!b.is(d))return;d=a(d);let e=d.find("input:invalid, select:invalid, textarea:invalid");e.length&&setTimeout(()=>{if(d.is(":hidden")){let b=d.attr("id");a(`#${b}-hide`).trigger("click.postboxes")}else if(d.hasClass("closed"))d.find(".hndle, .handlediv").first().trigger("click.postboxes");else{let b=e.get(0);a(b).is(":visible")&&b.reportValidity()}})})};let k=!1;const l=()=>{k||(k=!0,document.body.dispatchEvent(new CustomEvent("tsf-interactive")))},m=()=>{document.body.dispatchEvent(new CustomEvent("tsf-ready"))},n=()=>{document.body.dispatchEvent(new CustomEvent("tsf-onload"))};let o=!1;const p=()=>{o||(document.removeEventListener("DOMContentLoaded",p),document.removeEventListener("load",p),n(),j(),document.querySelectorAll(".tsf-dismiss").forEach(a=>a.addEventListener("click",i)),m(),o=!0,document.addEventListener("load",l),setTimeout(l,100))};return Object.assign({load:()=>{"complete"!==document.readyState&&("loading"===document.readyState||document.documentElement.doScroll)?(document.addEventListener("DOMContentLoaded",p),document.addEventListener("load",p)):setTimeout(p())}},{stripTags:a=>a.length&&a.replace(/(<([^>]+)?>?)/ig,"")||"",decodeEntities:f,escapeString:g,ampHTMLtoText:h,sDoubleSpace:a=>a.replace(/\s\s+/g," "),getStringLength:a=>{let b,c=0;return a.length&&(b=document.createElement("span"),b.innerHTML=g(a).trim(),"undefined"!=typeof b.childNodes[0]&&(c=b.childNodes[0].nodeValue.length)),+c},selectByValue:(a,b)=>{if(!a instanceof HTMLSelectElement)return;let c;for(let d=0;d<a.options.length;++d)if(b==a.options[d].value){c=d;break}if(void 0===c)for(let d=0;d<a.options.length;++d)if(b==a.options[d].innerHTML){c=d;break}void 0!==c&&(a.selectedIndex=c)},convertJSONResponse:a=>{let b=a&&a.json||void 0,c=1===b;if(!c){let b=a;try{a=JSON.parse(a),c=!0}catch(a){c=!1}c||(a=b)}return a},setAjaxLoader:b=>{a(b).toggleClass("tsf-loading")},unsetAjaxLoader:(b,c)=>{let d="tsf-success",e=2500;c||(d="tsf-error",e=5e3),a(b).removeClass("tsf-loading").addClass(d).fadeOut(e)},resetAjaxLoader:b=>{a(b).stop(!1,!0).empty().prop("class","tsf-ajax").show()},deprecatedFunc:(a,b,c)=>{b=b&&` since The SEO Framework ${b}`||"",c=c&&` Use ${c} instead.`||"",console.warn(`[DEPRECATED]: ${a} is deprecated since${b}.${c}`)}},{l10n:b})}(jQuery),window.tsf.load();
1
+ 'use strict';window.tsf=function(a){const b="undefined"!=typeof tsfL10n&&tsfL10n;let c;const d=()=>{if(void 0===c){try{c=!!new DOMParser().parseFromString("","text/html")}catch(a){}c=!!c}return c};let e;const f=a=>{if("string"!=typeof a||!a.length)return"";let b={"<":"&#x3C;",">":"&#x3E;","\\":"&#x5C;"};return a=a.replace(/[<>\\]/g,a=>b[a]),d()?(e=e||new DOMParser,a=e.parseFromString(a,"text/html").documentElement.textContent):(e=e||document.createElement("span"),e.innerHTML=a,a=h(e.textContent)),a},g=a=>{if(!a.length)return"";let b={"&":"&#x26;","<":"&#x3C;",">":"&#x3E;",'"':"&#x22;","'":"&#x27;","\\":"&#x5C;","/":"&#x2F;"};return a.replace(/[&<>"'\\\/]/g,a=>b[a])},h=a=>a.replace(/&amp;|&#x0{0,3}26;|&#38;/gi,"&"),i=()=>{let b=a(".postbox[id^=\"autodescription-\"], .postbox#tsf-inpost-box");a(document).on("postbox-toggled",(c,d)=>{if(!d||!b.is(d))return;d=a(d);let e=d.find("input:invalid, select:invalid, textarea:invalid");e.length&&setTimeout(()=>{if(d.is(":hidden")){let b=d.attr("id");a(`#${b}-hide`).trigger("click.postboxes")}else if(d.hasClass("closed"))d.find(".hndle, .handlediv").first().trigger("click.postboxes");else{let b=e.get(0);a(b).is(":visible")&&b.reportValidity()}})})},j=()=>{const b=b=>{let c=a(b.target).parents(".tsf-notice").first(),d=b.target.dataset&&b.target.dataset.key||void 0,e=b.target.dataset&&b.target.dataset.nonce||void 0;c.fadeTo(100,0,()=>{c.slideUp(100,()=>{c.remove()})}),d&&e&&wp.ajax.post("tsf-dismiss-notice",{"tsf-dismiss-key":d,"tsf-dismiss-nonce":e})},c=()=>{document.querySelectorAll(".tsf-dismiss").forEach(a=>a.addEventListener("click",b))};document.body.addEventListener("tsf-reset-notice-listeners",c),c()};let k;const l=()=>{clearTimeout(k),k=setTimeout(()=>document.body.dispatchEvent(new CustomEvent("tsf-reset-notice-listeners")),100)};let m=!1;const n=()=>{m||(m=!0,document.body.dispatchEvent(new CustomEvent("tsf-interactive")))},o=()=>{document.body.dispatchEvent(new CustomEvent("tsf-ready"))},p=()=>{document.body.dispatchEvent(new CustomEvent("tsf-onload"))};let q=!1;const r=()=>{q||(document.removeEventListener("DOMContentLoaded",r),document.removeEventListener("load",r),p(),i(),j(),o(),q=!0,document.addEventListener("load",n),setTimeout(n,100))};return Object.assign({load:()=>{"complete"!==document.readyState&&("loading"===document.readyState||document.documentElement.doScroll)?(document.addEventListener("DOMContentLoaded",r),document.addEventListener("load",r)):setTimeout(r())}},{stripTags:a=>a.length&&a.replace(/(<([^>]+)?>?)/ig,"")||"",decodeEntities:f,escapeString:g,ampHTMLtoText:h,sDoubleSpace:a=>a.replace(/\s\s+/g," "),getStringLength:a=>{let b,c=0;return a.length&&(b=document.createElement("span"),b.innerHTML=g(a).trim(),"undefined"!=typeof b.childNodes[0]&&(c=b.childNodes[0].nodeValue.length)),+c},selectByValue:(a,b)=>{if(!a instanceof HTMLSelectElement)return;let c;for(let d=0;d<a.options.length;++d)if(b==a.options[d].value){c=d;break}if(void 0===c)for(let d=0;d<a.options.length;++d)if(b==a.options[d].innerHTML){c=d;break}void 0!==c&&(a.selectedIndex=c)},convertJSONResponse:a=>{let b=a&&a.json||void 0,c=1===b;if(!c){let b=a;try{a=JSON.parse(a),c=!0}catch(a){c=!1}c||(a=b)}return a},setAjaxLoader:b=>{a(b).toggleClass("tsf-loading")},unsetAjaxLoader:(b,c)=>{let d="tsf-success",e=2500;c||(d="tsf-error",e=5e3),a(b).removeClass("tsf-loading").addClass(d).fadeOut(e)},resetAjaxLoader:b=>{a(b).stop(!1,!0).empty().prop("class","tsf-ajax").show()},deprecatedFunc:(a,b,c)=>{b=b&&` since The SEO Framework ${b}`||"",c=c&&` Use ${c} instead.`||"",console.warn(`[DEPRECATED]: ${a} is deprecated since${b}.${c}`)},triggerNoticeReset:l},{l10n:b})}(jQuery),window.tsf.load();
lib/js/tt.js CHANGED
@@ -758,12 +758,12 @@ window.tsfTT = function( $ ) {
758
  * @since 3.1.0
759
  * @access public
760
  */
761
- doTooltip,
762
- removeTooltip,
763
- getTooltip,
764
- addBoundary,
765
- triggerReset,
766
- triggerUpdate,
767
  } );
768
  }( jQuery );
769
  window.tsfTT.load();
758
  * @since 3.1.0
759
  * @access public
760
  */
761
+ doTooltip,
762
+ removeTooltip,
763
+ getTooltip,
764
+ addBoundary,
765
+ triggerReset,
766
+ triggerUpdate,
767
  } );
768
  }( jQuery );
769
  window.tsfTT.load();
readme.txt CHANGED
@@ -1,10 +1,11 @@
1
  === The SEO Framework ===
2
  Contributors: Cybr
 
3
  Tags: seo, xml sitemap, google search, open graph, schema.org, twitter card, performance
4
  Requires at least: 5.1.0
5
- Tested up to: 5.5
6
  Requires PHP: 5.6.0
7
- Stable tag: 4.1.1
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -118,6 +119,7 @@ The SEO Framework works on many things without notifying you, because the best s
118
  * Full screen-reader accessibility via field anchors, ARIA labels, and title attributes.
119
  * WordPress Multisite setups, this plugin is in fact built upon one.
120
  * Detection and output of robots.txt and sitemap.xml files.
 
121
  * Output of structured data via Schema.org JSON-LD scripts.
122
  * Altering oEmbed for improved sharing on Discord.
123
  * Detection of various other SEO tools to help you switch graciously.
@@ -244,6 +246,12 @@ If you wish to display breadcrumbs, then your theme should provide this. Alterna
244
 
245
  == Changelog ==
246
 
 
 
 
 
 
 
247
  = 4.1.1 =
248
 
249
  In this major-minor update, we improved browser performance by up to 99% (not a typo) by exchanging over 300 jQuery calls for vanilla JS ones. We also added two new options for oEmbed, freed a dozen bugs that got stuck in the UI and generators, and [improved accessibility](https://theseoframework.com/?p=3623#detailed).
@@ -309,7 +317,7 @@ WordPress 5.5 brings new sitemaps. We added support for them, but we didn't inte
309
 
310
  Regardless, we don't believe the Core Sitemaps are beneficial for most WordPress sites. We stubbornly kept our sitemap simple; it's easier for us to maintain and faster search engines to process. Search engines crawl your pages more quickly using TSF's sitemap, no matter your website's size.
311
 
312
- Nevertheless, TSF will integrate with Core Sitemaps in a future update. However, since we had fewer than two months to anticipate their integration, we couldn't make this happen now.
313
 
314
  **Environment upgrade notes**
315
 
1
  === The SEO Framework ===
2
  Contributors: Cybr
3
+ Donate link: https://github.com/sponsors/sybrew
4
  Tags: seo, xml sitemap, google search, open graph, schema.org, twitter card, performance
5
  Requires at least: 5.1.0
6
+ Tested up to: 5.6
7
  Requires PHP: 5.6.0
8
+ Stable tag: 4.1.2
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
119
  * Full screen-reader accessibility via field anchors, ARIA labels, and title attributes.
120
  * WordPress Multisite setups, this plugin is in fact built upon one.
121
  * Detection and output of robots.txt and sitemap.xml files.
122
+ * Full integration with WordPress Core sitemaps.
123
  * Output of structured data via Schema.org JSON-LD scripts.
124
  * Altering oEmbed for improved sharing on Discord.
125
  * Detection of various other SEO tools to help you switch graciously.
246
 
247
  == Changelog ==
248
 
249
+ = 4.1.2 =
250
+
251
+ In this minor update, we ensured compatibility with PHP 8 and WP 5.6. TSF now also fully integrates with WordPress Core Sitemaps, which you can configure via the SEO settings. If you decide to keep using TSF's optimized sitemap, you can now enjoy prerendering, DoS protection, and full Polylang integration. Lastly, you'll find various accessibility improvements, and we fixed [about a dozen bugs](https://theseoframework.com/?p=3650#detailed).
252
+
253
+ We included a single-line self-destructing notification about our [Cyber Monday sale](https://theseoframework.com/?p=3527). We hope you'll opt to support our continuous efforts. But we're also kindly asking you to understand we must promote our premium extensions to make TSF possible. We are apprehensive about any embedded advertising and self-promotion in the plugin, so we must rely on notifications until a better system is available in WordPress.
254
+
255
  = 4.1.1 =
256
 
257
  In this major-minor update, we improved browser performance by up to 99% (not a typo) by exchanging over 300 jQuery calls for vanilla JS ones. We also added two new options for oEmbed, freed a dozen bugs that got stuck in the UI and generators, and [improved accessibility](https://theseoframework.com/?p=3623#detailed).
317
 
318
  Regardless, we don't believe the Core Sitemaps are beneficial for most WordPress sites. We stubbornly kept our sitemap simple; it's easier for us to maintain and faster search engines to process. Search engines crawl your pages more quickly using TSF's sitemap, no matter your website's size.
319
 
320
+ Nevertheless, TSF will integrate with Core Sitemaps in a future update. Since we had fewer than two months to anticipate their integration, we couldn't make this happen now.
321
 
322
  **Environment upgrade notes**
323