The SEO Framework - Version 4.1.4

Version Description

This minor update packs a major punch. TSF now supports headless mode, cementing itself as a turnkey solution. We defenestrated the pernicious object caching mechanism, and we updated some options' defaults effective only on new sites. We improved performance iterably, fixed about 12 bugs, and enjoyed the weather. Lastly, we introduced a new API for user meta handling, among other things --- developers that wrote software interfacing with TSF are employed well reading the detailed changelog.

Download this release

Release Info

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

Code changes from version 4.1.3 to 4.1.4

Files changed (117) hide show
  1. autodescription.php +10 -7
  2. bootstrap/activation.php +1 -1
  3. bootstrap/deactivation.php +1 -1
  4. bootstrap/define.php +2 -2
  5. bootstrap/envtest.php +1 -1
  6. bootstrap/load.php +1 -1
  7. bootstrap/upgrade.php +2 -2
  8. inc/classes/admin-init.class.php +22 -315
  9. inc/classes/admin-pages.class.php +21 -823
  10. inc/classes/bridges/ajax.class.php +345 -0
  11. inc/classes/bridges/feed.class.php +1 -1
  12. inc/classes/bridges/listedit.class.php +23 -9
  13. inc/classes/bridges/listtable.class.php +1 -1
  14. inc/classes/bridges/ping.class.php +4 -4
  15. inc/classes/bridges/plugintable.class.php +130 -0
  16. inc/classes/bridges/postsettings.class.php +2 -2
  17. inc/classes/bridges/scripts.class.php +3 -3
  18. inc/classes/bridges/seobar.class.php +1 -1
  19. inc/classes/bridges/seosettings.class.php +1 -1
  20. inc/classes/bridges/sitemap.class.php +10 -24
  21. inc/classes/bridges/termsettings.class.php +1 -1
  22. inc/classes/bridges/usersettings.class.php +71 -0
  23. inc/classes/builders/coresitemaps/main.class.php +1 -1
  24. inc/classes/builders/coresitemaps/posts.class.php +1 -1
  25. inc/classes/builders/coresitemaps/taxonomies.class.php +1 -1
  26. inc/classes/builders/images.class.php +16 -11
  27. inc/classes/builders/scripts.class.php +2 -2
  28. inc/classes/builders/seobar-page.class.php +9 -8
  29. inc/classes/builders/seobar-term.class.php +3 -2
  30. inc/classes/builders/seobar.class.php +21 -1
  31. inc/classes/builders/sitemap-base.class.php +34 -35
  32. inc/classes/builders/sitemap.class.php +51 -24
  33. inc/classes/cache.class.php +52 -448
  34. inc/classes/core.class.php +79 -266
  35. inc/classes/debug.class.php +3 -19
  36. inc/classes/deprecated.class.php +820 -651
  37. inc/classes/detect.class.php +20 -50
  38. inc/classes/generate-description.class.php +8 -8
  39. inc/classes/generate-image.class.php +2 -1
  40. inc/classes/generate-ldjson.class.php +3 -3
  41. inc/classes/generate-title.class.php +11 -11
  42. inc/classes/generate-url.class.php +9 -10
  43. inc/classes/generate.class.php +132 -143
  44. inc/classes/index.php +0 -1
  45. inc/classes/init.class.php +257 -185
  46. inc/classes/interpreters/form.class.php +547 -0
  47. inc/classes/interpreters/html.class.php +250 -0
  48. inc/classes/interpreters/markdown.class.php +274 -0
  49. inc/classes/interpreters/seobar.class.php +21 -12
  50. inc/classes/load.class.php +49 -46
  51. inc/classes/post-data.class.php +54 -35
  52. inc/classes/profile.class.php +0 -150
  53. inc/classes/query.class.php +79 -112
  54. inc/classes/render.class.php +287 -164
  55. inc/classes/sanitize.class.php +42 -23
  56. inc/classes/silencer.class.php +1 -1
  57. inc/classes/site-options.class.php +14 -8
  58. inc/classes/term-data.class.php +17 -50
  59. inc/classes/user-data.class.php +239 -123
  60. inc/compat/php-mbstring.php +0 -199
  61. inc/compat/plugin-bbpress.php +1 -1
  62. inc/compat/plugin-edd.php +3 -2
  63. inc/compat/plugin-polylang.php +11 -18
  64. inc/compat/plugin-woocommerce.php +142 -7
  65. inc/compat/plugin-wpforo.php +1 -1
  66. inc/compat/plugin-wpml.php +42 -0
  67. inc/functions/api.php +1 -1
  68. inc/functions/deprecated.php +2 -197
  69. inc/functions/upgrade-suggestion.php +1 -1
  70. inc/views/admin/metaboxes/description-metabox.php +15 -16
  71. inc/views/admin/metaboxes/feed-metabox.php +28 -16
  72. inc/views/admin/metaboxes/general-metabox.php +121 -178
  73. inc/views/admin/metaboxes/homepage-metabox.php +94 -103
  74. inc/views/admin/metaboxes/robots-metabox.php +59 -67
  75. inc/views/admin/metaboxes/schema-metabox.php +81 -78
  76. inc/views/admin/metaboxes/sitemaps-metabox.php +115 -120
  77. inc/views/admin/metaboxes/social-metabox.php +131 -156
  78. inc/views/admin/metaboxes/title-metabox.php +57 -67
  79. inc/views/admin/metaboxes/webmaster-metabox.php +14 -13
  80. inc/views/admin/seo-settings-wrap.php +10 -7
  81. inc/views/debug/output.php +2 -2
  82. inc/views/edit/seo-settings-singular.php +28 -25
  83. inc/views/edit/seo-settings-tt.php +29 -27
  84. inc/views/list/bulk-post.php +3 -1
  85. inc/views/list/quick-post.php +7 -5
  86. inc/views/list/quick-term.php +7 -5
  87. inc/views/notice/persistent.php +5 -3
  88. inc/views/profile/author.php +23 -6
  89. inc/views/templates/inpost/primary-term-selector.php +1 -1
  90. inc/views/templates/settings/settings.php +7 -5
  91. language/autodescription.pot +726 -732
  92. lib/css/le.css +1 -1
  93. lib/css/le.min.css +1 -1
  94. lib/css/tsf.css +1 -1
  95. lib/css/tsf.min.css +1 -1
  96. lib/js/ays.js +1 -1
  97. lib/js/c.js +2 -2
  98. lib/js/c.min.js +1 -1
  99. lib/js/description.js +1 -1
  100. lib/js/gbc.js +1 -1
  101. lib/js/le.js +28 -3
  102. lib/js/le.min.js +1 -1
  103. lib/js/media.js +78 -75
  104. lib/js/media.min.js +1 -1
  105. lib/js/post.js +11 -7
  106. lib/js/post.min.js +1 -1
  107. lib/js/pt-gb.js +1 -1
  108. lib/js/pt.js +1 -1
  109. lib/js/settings.js +1 -1
  110. lib/js/social.js +1 -1
  111. lib/js/tabs.js +1 -1
  112. lib/js/term.js +1 -1
  113. lib/js/title.js +1 -1
  114. lib/js/tsf.js +4 -4
  115. lib/js/tsf.min.js +1 -1
  116. lib/js/tt.js +1 -4
  117. readme.txt +10 -5
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.3
7
  * Author: The SEO Framework Team
8
  * Author URI: https://theseoframework.com/
9
  * License: GPLv3
@@ -17,7 +17,7 @@ defined( 'ABSPATH' ) or die;
17
 
18
  /**
19
  * The SEO Framework plugin
20
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
21
  *
22
  * This program is free software: you can redistribute it and/or modify
23
  * it under the terms of the GNU General Public License version 3 as published
@@ -36,7 +36,7 @@ defined( 'ABSPATH' ) or die;
36
  * @NOTE This file MUST be written according to WordPress's minimum PHP requirements.
37
  * Which is PHP 5.2.
38
  * When we only support WordPress 5.2+, it'll be PHP 5.6.
39
- * When we only support WordPress 5.6?+, it'll be PHP 7.1.
40
  */
41
 
42
  /**
@@ -46,7 +46,7 @@ defined( 'ABSPATH' ) or die;
46
  *
47
  * @since 2.3.5
48
  */
49
- define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.3' );
50
 
51
  /**
52
  * The plugin Database version.
@@ -89,6 +89,7 @@ if ( get_option( 'the_seo_framework_tested_upgrade_version' ) < THE_SEO_FRAMEWOR
89
  * Starts the plugin, loads files outside of the global scope.
90
  *
91
  * @since 3.1.0
 
92
  * @access private
93
  */
94
  function the_seo_framework_boot() {
@@ -104,7 +105,7 @@ function the_seo_framework_boot() {
104
  and require THE_SEO_FRAMEWORK_BOOTSTRAP_PATH . 'upgrade.php';
105
 
106
  // Load deprecated functions.
107
- require THE_SEO_FRAMEWORK_DIR_PATH_FUNCT . 'deprecated.php';
108
 
109
  // Load plugin.
110
  require THE_SEO_FRAMEWORK_BOOTSTRAP_PATH . 'load.php';
@@ -113,7 +114,9 @@ function the_seo_framework_boot() {
113
  // phpcs:disable, Squiz.Commenting.InlineComment, Squiz.PHP.CommentedOutCode
114
  //
115
  // Debug: Not to be used on production websites as it dumps and/or disables all kinds of stuff everywhere.
116
- // This is here as an easily accessible toolset used solely in the development of this plugin.
 
 
117
  //
118
  // add_action( 'plugins_loaded', function() { if ( is_super_admin() ) {
119
  // if ( is_admin() ) {
@@ -124,7 +127,7 @@ function the_seo_framework_boot() {
124
  // ( $_GET['downgrade_tsf'] ?? 0 ) and update_option( 'the_seo_framework_upgraded_db_version', (string) (int) $_GET['downgrade_tsf'] );
125
  // ( $_GET['downgrade_tsf_initial'] ?? 0 ) and update_option( 'the_seo_framework_initial_db_version', (string) (int) $_GET['downgrade_tsf_initial'] );
126
  // ( $_GET['reset_tsf_tested'] ?? 0 ) and delete_option( 'the_seo_framework_tested_upgrade_version' );
127
- // add_filter( 'the_seo_framework_use_object_cache', '__return_false' );
128
  // }
129
  // }},0);
130
  // phpcs:enable, Squiz.Commenting.InlineComment, Squiz.PHP.CommentedOutCode
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.4
7
  * Author: The SEO Framework Team
8
  * Author URI: https://theseoframework.com/
9
  * License: GPLv3
17
 
18
  /**
19
  * The SEO Framework plugin
20
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
21
  *
22
  * This program is free software: you can redistribute it and/or modify
23
  * it under the terms of the GNU General Public License version 3 as published
36
  * @NOTE This file MUST be written according to WordPress's minimum PHP requirements.
37
  * Which is PHP 5.2.
38
  * When we only support WordPress 5.2+, it'll be PHP 5.6.
39
+ * When we only support WordPress 5.9?+, it'll be PHP 7.1.
40
  */
41
 
42
  /**
46
  *
47
  * @since 2.3.5
48
  */
49
+ define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.4' );
50
 
51
  /**
52
  * The plugin Database version.
89
  * Starts the plugin, loads files outside of the global scope.
90
  *
91
  * @since 3.1.0
92
+ * @since 4.1.4 Unloaded the functions deprecated.php file.
93
  * @access private
94
  */
95
  function the_seo_framework_boot() {
105
  and require THE_SEO_FRAMEWORK_BOOTSTRAP_PATH . 'upgrade.php';
106
 
107
  // Load deprecated functions.
108
+ // require THE_SEO_FRAMEWORK_DIR_PATH_FUNCT . 'deprecated.php';
109
 
110
  // Load plugin.
111
  require THE_SEO_FRAMEWORK_BOOTSTRAP_PATH . 'load.php';
114
  // phpcs:disable, Squiz.Commenting.InlineComment, Squiz.PHP.CommentedOutCode
115
  //
116
  // Debug: Not to be used on production websites as it dumps and/or disables all kinds of stuff everywhere.
117
+ // This is here as an easily accessible toolset used solely for the development of this plugin.
118
+ //
119
+ // Headless tip: ?tsf_headless[meta]=0&tsf_headless[settings]=0&tsf_headless[user]=0
120
  //
121
  // add_action( 'plugins_loaded', function() { if ( is_super_admin() ) {
122
  // if ( is_admin() ) {
127
  // ( $_GET['downgrade_tsf'] ?? 0 ) and update_option( 'the_seo_framework_upgraded_db_version', (string) (int) $_GET['downgrade_tsf'] );
128
  // ( $_GET['downgrade_tsf_initial'] ?? 0 ) and update_option( 'the_seo_framework_initial_db_version', (string) (int) $_GET['downgrade_tsf_initial'] );
129
  // ( $_GET['reset_tsf_tested'] ?? 0 ) and delete_option( 'the_seo_framework_tested_upgrade_version' );
130
+ // ( $_GET['tsf_headless'] ?? 0 ) and define( 'THE_SEO_FRAMEWORK_HEADLESS', $_GET['tsf_headless'] === 'true' ?: $_GET['tsf_headless'] );
131
  // }
132
  // }},0);
133
  // phpcs:enable, Squiz.Commenting.InlineComment, Squiz.PHP.CommentedOutCode
bootstrap/activation.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework\Bootstrap;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2015 - 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
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (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
bootstrap/deactivation.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework\Bootstrap;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2015 - 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
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (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
bootstrap/define.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2018 - 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
@@ -144,7 +144,7 @@ namespace The_SEO_Framework;
144
  or \define( 'THE_SEO_FRAMEWORK_SETTINGS_CAP', 'manage_options' );
145
 
146
  /**
147
- * The user capability required to access the extension overview page.
148
  *
149
  * == WARNING ==
150
  * When this constant is used incorrectly, you can expose your site to
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
144
  or \define( 'THE_SEO_FRAMEWORK_SETTINGS_CAP', 'manage_options' );
145
 
146
  /**
147
+ * The user capability required to have SEO-fields on their profiles.
148
  *
149
  * == WARNING ==
150
  * When this constant is used incorrectly, you can expose your site to
bootstrap/envtest.php CHANGED
@@ -19,7 +19,7 @@ defined( 'THE_SEO_FRAMEWORK_DB_VERSION' ) or die;
19
 
20
  /**
21
  * The SEO Framework plugin
22
- * Copyright (C) 2018 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
23
  *
24
  * This program is free software: you can redistribute it and/or modify
25
  * it under the terms of the GNU General Public License version 3 as published
19
 
20
  /**
21
  * The SEO Framework plugin
22
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
23
  *
24
  * This program is free software: you can redistribute it and/or modify
25
  * it under the terms of the GNU General Public License version 3 as published
bootstrap/load.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2018 - 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
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
bootstrap/upgrade.php CHANGED
@@ -488,7 +488,7 @@ function _do_install_notice() {
488
  * @param string $current_version The current version of the site.
489
  * @return void Early when already enqueued
490
  */
491
- function _prepare_upgrade_suggestion( $previous_version, $current_version ) {
492
  // Don't invoke if the user didn't upgrade.
493
  if ( ! $previous_version ) return;
494
 
@@ -589,7 +589,7 @@ function _do_upgrade_2900() {
589
  if ( \get_option( 'the_seo_framework_initial_db_version' ) < '2900' ) {
590
  $tsf = \the_seo_framework();
591
 
592
- $card_type = trim( \esc_attr( $tsf->get_option( 'twitter_card', false ) ) );
593
  if ( 'photo' === $card_type ) {
594
  $tsf->update_option( 'twitter_card', 'summary_large_image' );
595
  _add_upgrade_notice(
488
  * @param string $current_version The current version of the site.
489
  * @return void Early when already enqueued
490
  */
491
+ function _prepare_upgrade_suggestion( $previous_version, $current_version ) { // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis
492
  // Don't invoke if the user didn't upgrade.
493
  if ( ! $previous_version ) return;
494
 
589
  if ( \get_option( 'the_seo_framework_initial_db_version' ) < '2900' ) {
590
  $tsf = \the_seo_framework();
591
 
592
+ $card_type = trim( $tsf->get_option( 'twitter_card', false ) );
593
  if ( 'photo' === $card_type ) {
594
  $tsf->update_option( 'twitter_card', 'summary_large_image' );
595
  _add_upgrade_notice(
inc/classes/admin-init.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -42,10 +42,8 @@ class Admin_Init extends Init {
42
  * @access private
43
  */
44
  public function _init_seo_bar_tables() {
45
-
46
- if ( $this->get_option( 'display_seo_bar_tables' ) ) {
47
  new Bridges\SeoBar;
48
- }
49
  }
50
 
51
  /**
@@ -93,6 +91,7 @@ class Admin_Init extends Init {
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.
@@ -107,12 +106,14 @@ class Admin_Init extends Init {
107
 
108
  $enqueue_hooks = [];
109
 
110
- if ( $this->is_archive_admin() ) {
111
- $prepare_edit_screen = $this->is_taxonomy_supported();
112
- } elseif ( $this->is_singular_admin() ) {
113
- $prepare_edit_screen = $this->is_post_type_supported( $this->get_admin_post_type() );
114
- } else {
115
- $prepare_edit_screen = false;
 
 
116
  }
117
 
118
  if ( $prepare_edit_screen ) {
@@ -342,7 +343,7 @@ class Admin_Init extends Init {
342
  *
343
  * Performs die() on failure.
344
  *
345
- * @since 3.1.0 : Introduced in 2.9.0, but the name changed.
346
  * @access private
347
  * It uses an internally and manually created prefix.
348
  * @uses WP Core check_ajax_referer()
@@ -373,17 +374,15 @@ class Admin_Init extends Init {
373
  */
374
  public function admin_redirect( $page, array $query_args = [] ) {
375
 
376
- if ( empty( $page ) )
377
- return;
378
 
379
  // This can be empty... so $target will be empty. TODO test for $success and bail?
380
  // Might cause security issues... we _must_ exit, always? Show warning?
381
  $url = html_entity_decode( \menu_page_url( $page, false ) );
382
 
383
- foreach ( $query_args as $key => $value ) {
384
  if ( empty( $key ) || empty( $value ) )
385
  unset( $query_args[ $key ] );
386
- }
387
 
388
  $target = \add_query_arg( $query_args, $url );
389
  $target = \esc_url_raw( $target, [ 'https', 'http' ] );
@@ -399,9 +398,8 @@ class Admin_Init extends Init {
399
  $success = \wp_safe_redirect( $target, 302 ); // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
400
 
401
  // White screen of death for non-debugging users. Let's make it friendlier.
402
- if ( $headers_sent ) {
403
  $this->handle_admin_redirect_error( $target );
404
- }
405
 
406
  exit;
407
  }
@@ -577,12 +575,15 @@ class Admin_Init extends Init {
577
  * Returns the snaitized notice action key.
578
  *
579
  * @since 4.1.0
 
 
 
580
  *
581
  * @param string $key The notice key.
582
  * @return string The sanitized nonce action.
583
  */
584
- protected function get_dismiss_notice_nonce_action( $key ) {
585
- return \sanitize_key( "tsf-notice-nonce-$key" );
586
  }
587
 
588
  /**
@@ -602,307 +603,13 @@ class Admin_Init extends Init {
602
  if ( empty( $notices[ $key ]['conditions']['capability'] ) ) return;
603
 
604
  // phpcs:ignore, WordPress.Security.NonceVerification.Missing -- We require the POST data to find locally stored nonces.
605
- $nonce = isset( $_POST['tsf-notice-nonce'] ) ? $_POST['tsf-notice-nonce'] : '';
606
 
607
  if ( ! \current_user_can( $notices[ $key ]['conditions']['capability'] )
608
- || ! \wp_verify_nonce( $nonce, $this->get_dismiss_notice_nonce_action( $key ) ) ) {
609
  \wp_die( -1, 403 );
610
  }
611
 
612
  $this->clear_persistent_notice( $key );
613
  }
614
-
615
- /**
616
- * Clears persistent notice on user request (clicked Dismiss icon) via AJAX.
617
- *
618
- * @since 4.1.0
619
- * Security check OK.
620
- */
621
- public function _wp_ajax_dismiss_notice() {
622
-
623
- // phpcs:ignore, WordPress.Security.NonceVerification.Missing -- We require the POST data to find locally stored nonces.
624
- $key = isset( $_POST['tsf-dismiss-key'] ) ? $_POST['tsf-dismiss-key'] : '';
625
- if ( ! $key ) {
626
- \wp_send_json_error( null, 400 );
627
- }
628
-
629
- $notices = $this->get_static_cache( 'persistent_notices', [] );
630
- if ( empty( $notices[ $key ]['conditions']['capability'] ) ) {
631
- // Notice was deleted already elsewhere, or key was faulty. Either way, ignore--should be self-resolving.
632
- \wp_send_json_error( null, 409 );
633
- }
634
-
635
- if ( ! \current_user_can( $notices[ $key ]['conditions']['capability'] )
636
- || ! \check_ajax_referer( $this->get_dismiss_notice_nonce_action( $key ), 'tsf-dismiss-nonce', false ) ) {
637
- \wp_die( -1, 403 );
638
- }
639
-
640
- $this->clear_persistent_notice( $key );
641
- \wp_send_json_success( null, 200 );
642
- }
643
-
644
- /**
645
- * Handles counter option update on AJAX request for users that can edit posts.
646
- *
647
- * @since 3.1.0 : Introduced in 2.6.0, but the name changed.
648
- * @securitycheck 3.0.0 OK.
649
- * @access private
650
- */
651
- public function _wp_ajax_update_counter_type() {
652
-
653
- // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer() does this.
654
- $this->_check_tsf_ajax_referer( 'edit_posts' );
655
-
656
- // Remove output buffer.
657
- $this->clean_response_header();
658
-
659
- // If current user isn't allowed to edit posts, don't do anything and kill PHP.
660
- if ( ! \current_user_can( 'edit_posts' ) ) {
661
- // Encode and echo results. Requires JSON decode within JS.
662
- \wp_send_json( [
663
- 'type' => 'failure',
664
- 'value' => '',
665
- ] );
666
- }
667
-
668
- /**
669
- * Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
670
- * $_POST['val'] already contains updated number.
671
- */
672
- if ( isset( $_POST['val'] ) ) {
673
- $value = (int) $_POST['val'];
674
- } else {
675
- // TODO use get_default_user_data() value instead.
676
- $value = $this->get_user_option( 0, 'counter_type', 3 ) + 1;
677
- }
678
- $value = \absint( $value );
679
-
680
- if ( $value > 3 )
681
- $value = 0;
682
-
683
- // Update the option and get results of action.
684
- $type = $this->update_user_option( 0, 'counter_type', $value ) ? 'success' : 'error';
685
-
686
- $results = [
687
- 'type' => $type,
688
- 'value' => $value,
689
- ];
690
-
691
- // Encode and echo results. Requires JSON decode within JS.
692
- \wp_send_json( $results );
693
-
694
- // phpcs:enable, WordPress.Security.NonceVerification
695
- }
696
-
697
- /**
698
- * Gets an SEO Bar for AJAX during edit-post.
699
- *
700
- * @since 4.0.0
701
- * @access private
702
- */
703
- public function _wp_ajax_get_post_data() {
704
-
705
- // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer() does this.
706
- $this->_check_tsf_ajax_referer( 'edit_posts' );
707
-
708
- // Clear output buffer.
709
- $this->clean_response_header();
710
-
711
- $post_id = \absint( $_POST['post_id'] );
712
-
713
- if ( ! $post_id || ! \current_user_can( 'edit_post', $post_id ) ) {
714
- \wp_send_json( [
715
- 'type' => 'failure',
716
- 'data' => [],
717
- ] );
718
- }
719
-
720
- $_get_defaults = [
721
- 'seobar' => false,
722
- 'metadescription' => false,
723
- 'ogdescription' => false,
724
- 'twdescription' => false,
725
- 'imageurl' => false,
726
- ];
727
-
728
- // Only get what's indexed in the defaults and set as "true".
729
- $get = array_keys(
730
- array_filter(
731
- array_intersect_key(
732
- array_merge(
733
- $_get_defaults,
734
- (array) ( isset( $_POST['get'] ) ? $_POST['get'] : [] )
735
- ),
736
- $_get_defaults
737
- )
738
- )
739
- );
740
-
741
- $_generator_args = [
742
- 'id' => $post_id,
743
- 'taxonomy' => '',
744
- ];
745
-
746
- $data = [];
747
-
748
- foreach ( $get as $g ) :
749
- switch ( $g ) {
750
- case 'seobar':
751
- $data[ $g ] = $this->get_generated_seo_bar( $_generator_args );
752
- break;
753
-
754
- case 'metadescription':
755
- case 'ogdescription':
756
- case 'twdescription':
757
- switch ( $g ) {
758
- case 'metadescription':
759
- if ( $this->is_static_frontpage( $post_id ) ) {
760
- // phpcs:disable, WordPress.WhiteSpace.PrecisionAlignment
761
- $data[ $g ] = $this->get_option( 'homepage_description' )
762
- ?: $this->get_generated_description( $_generator_args, false );
763
- // phpcs:enable, WordPress.WhiteSpace.PrecisionAlignment
764
- } else {
765
- $data[ $g ] = $this->get_generated_description( $_generator_args, false );
766
- }
767
- break;
768
- case 'ogdescription':
769
- // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- Smart loop.
770
- $_social_ph = isset( $_social_ph ) ? $_social_ph : $this->_get_social_placeholders( $_generator_args );
771
- $data[ $g ] = $_social_ph['description']['og'];
772
- break;
773
- case 'twdescription':
774
- // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- Smart loop.
775
- $_social_ph = isset( $_social_ph ) ? $_social_ph : $this->_get_social_placeholders( $_generator_args );
776
- $data[ $g ] = $_social_ph['description']['twitter'];
777
- break;
778
- }
779
-
780
- $data[ $g ] = $this->s_description( $data[ $g ] );
781
- break;
782
-
783
- case 'imageurl':
784
- if ( $this->is_static_frontpage( $post_id ) && $this->get_option( 'homepage_social_image_url' ) ) {
785
- $image_details = current( $this->get_image_details( $_generator_args, true, 'social', true ) );
786
- $data[ $g ] = isset( $image_details['url'] ) ? $image_details['url'] : '';
787
- } else {
788
- $image_details = current( $this->get_generated_image_details( $_generator_args, true, 'social', true ) );
789
- $data[ $g ] = isset( $image_details['url'] ) ? $image_details['url'] : '';
790
- }
791
- break;
792
-
793
- default:
794
- break;
795
- }
796
- endforeach;
797
-
798
- \wp_send_json( [
799
- 'type' => 'success',
800
- 'data' => $data,
801
- 'processed' => $get,
802
- ] );
803
-
804
- // phpcs:enable, WordPress.Security.NonceVerification
805
- }
806
-
807
- /**
808
- * Handles cropping of images on AJAX request.
809
- *
810
- * Copied from WordPress Core wp_ajax_crop_image.
811
- * Adjusted: 1. It accepts capability 'upload_files', instead of 'customize'.
812
- * - This was set to 'edit_post' in WP 4.7? trac ticket got lost, probably for (invalid) security reasons.
813
- * In any case, that's still incorrect, and I gave up on communicating this;
814
- * We're not editing the image, we're creating a new one!
815
- * 2. It now only accepts TSF own AJAX nonces.
816
- * 3. It now only accepts context 'tsf-image'
817
- * 4. It no longer accepts a default context.
818
- *
819
- * @since 3.1.0 : Introduced in 2.9.0, but the name changed.
820
- * @securitycheck 3.0.0 OK.
821
- * @access private
822
- */
823
- public function _wp_ajax_crop_image() {
824
-
825
- // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer does this.
826
- $this->_check_tsf_ajax_referer( 'upload_files' );
827
-
828
- if ( ! \current_user_can( 'upload_files' ) || ! isset( $_POST['id'], $_POST['context'], $_POST['cropDetails'] ) ) {
829
- \wp_send_json_error();
830
- }
831
-
832
- $attachment_id = \absint( $_POST['id'] );
833
-
834
- $context = str_replace( '_', '-', \sanitize_key( $_POST['context'] ) );
835
- $data = array_map( '\\absint', $_POST['cropDetails'] );
836
- $cropped = \wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
837
-
838
- if ( ! $cropped || \is_wp_error( $cropped ) )
839
- \wp_send_json_error( [ 'message' => \esc_js( \__( 'Image could not be processed.', 'default' ) ) ] );
840
-
841
- switch ( $context ) :
842
- case 'tsf-image':
843
- /**
844
- * Fires before a cropped image is saved.
845
- *
846
- * Allows to add filters to modify the way a cropped image is saved.
847
- *
848
- * @since 4.3.0 WordPress Core
849
- *
850
- * @param string $context The Customizer control requesting the cropped image.
851
- * @param int $attachment_id The attachment ID of the original image.
852
- * @param string $cropped Path to the cropped image file.
853
- */
854
- \do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
855
-
856
- /** This filter is documented in wp-admin/custom-header.php */
857
- $cropped = \apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
858
-
859
- $parent_url = \wp_get_attachment_url( $attachment_id );
860
- $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
861
-
862
- // phpcs:ignore, WordPress.PHP.NoSilencedErrors -- Feature may be disabled; should not cause fatal errors.
863
- $size = @getimagesize( $cropped );
864
- $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
865
-
866
- $object = [
867
- 'post_title' => basename( $cropped ),
868
- 'post_content' => $url,
869
- 'post_mime_type' => $image_type,
870
- 'guid' => $url,
871
- 'context' => $context,
872
- ];
873
-
874
- $attachment_id = \wp_insert_attachment( $object, $cropped );
875
- $metadata = \wp_generate_attachment_metadata( $attachment_id, $cropped );
876
-
877
- /**
878
- * Filters the cropped image attachment metadata.
879
- *
880
- * @since 4.3.0 WordPress Core
881
- * @see wp_generate_attachment_metadata()
882
- *
883
- * @param array $metadata Attachment metadata.
884
- */
885
- $metadata = \apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
886
- \wp_update_attachment_metadata( $attachment_id, $metadata );
887
-
888
- /**
889
- * Filters the attachment ID for a cropped image.
890
- *
891
- * @since 4.3.0 WordPress Core
892
- *
893
- * @param int $attachment_id The attachment ID of the cropped image.
894
- * @param string $context The Customizer control requesting the cropped image.
895
- */
896
- $attachment_id = \apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
897
- break;
898
-
899
- default:
900
- \wp_send_json_error( [ 'message' => \esc_js( \__( 'Image could not be processed.', 'default' ) ) ] );
901
- break;
902
- endswitch;
903
-
904
- \wp_send_json_success( \wp_prepare_attachment_for_js( $attachment_id ) );
905
-
906
- // phpcs:enable, WordPress.Security.NonceVerification
907
- }
908
  }
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
42
  * @access private
43
  */
44
  public function _init_seo_bar_tables() {
45
+ if ( $this->get_option( 'display_seo_bar_tables' ) )
 
46
  new Bridges\SeoBar;
 
47
  }
48
 
49
  /**
91
  * @since 4.0.0 Now discerns autoloading between taxonomies and singular types.
92
  * @since 4.1.0 Now invokes autoloading when persistent scripts are enqueued (regardless of validity).
93
  * @since 4.1.2 Now autoenqueues on edit.php and edit-tags.php regardless of SEO Bar output (for quick/bulk-edit support).
94
+ * @since 4.1.4 Now considers headlessness.
95
  * @access private
96
  *
97
  * @param string|null $hook The current page hook.
106
 
107
  $enqueue_hooks = [];
108
 
109
+ $prepare_edit_screen = false;
110
+
111
+ if ( ! $this->is_headless['meta'] ) {
112
+ if ( $this->is_archive_admin() ) {
113
+ $prepare_edit_screen = $this->is_taxonomy_supported();
114
+ } elseif ( $this->is_singular_admin() ) {
115
+ $prepare_edit_screen = $this->is_post_type_supported( $this->get_admin_post_type() );
116
+ }
117
  }
118
 
119
  if ( $prepare_edit_screen ) {
343
  *
344
  * Performs die() on failure.
345
  *
346
+ * @since 3.1.0 Introduced in 2.9.0, but the name changed.
347
  * @access private
348
  * It uses an internally and manually created prefix.
349
  * @uses WP Core check_ajax_referer()
374
  */
375
  public function admin_redirect( $page, array $query_args = [] ) {
376
 
377
+ if ( empty( $page ) ) return;
 
378
 
379
  // This can be empty... so $target will be empty. TODO test for $success and bail?
380
  // Might cause security issues... we _must_ exit, always? Show warning?
381
  $url = html_entity_decode( \menu_page_url( $page, false ) );
382
 
383
+ foreach ( $query_args as $key => $value )
384
  if ( empty( $key ) || empty( $value ) )
385
  unset( $query_args[ $key ] );
 
386
 
387
  $target = \add_query_arg( $query_args, $url );
388
  $target = \esc_url_raw( $target, [ 'https', 'http' ] );
398
  $success = \wp_safe_redirect( $target, 302 ); // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
399
 
400
  // White screen of death for non-debugging users. Let's make it friendlier.
401
+ if ( $headers_sent )
402
  $this->handle_admin_redirect_error( $target );
 
403
 
404
  exit;
405
  }
575
  * Returns the snaitized notice action key.
576
  *
577
  * @since 4.1.0
578
+ * @since 4.1.4 1. Now 'public', marked private.
579
+ * 2. Now uses underscores instead of dashes.
580
+ * @access private
581
  *
582
  * @param string $key The notice key.
583
  * @return string The sanitized nonce action.
584
  */
585
+ public function _get_dismiss_notice_nonce_action( $key ) {
586
+ return \sanitize_key( "tsf_notice_nonce_$key" );
587
  }
588
 
589
  /**
603
  if ( empty( $notices[ $key ]['conditions']['capability'] ) ) return;
604
 
605
  // phpcs:ignore, WordPress.Security.NonceVerification.Missing -- We require the POST data to find locally stored nonces.
606
+ $nonce = isset( $_POST['tsf_notice_nonce'] ) ? $_POST['tsf_notice_nonce'] : '';
607
 
608
  if ( ! \current_user_can( $notices[ $key ]['conditions']['capability'] )
609
+ || ! \wp_verify_nonce( $nonce, $this->_get_dismiss_notice_nonce_action( $key ) ) ) {
610
  \wp_die( -1, 403 );
611
  }
612
 
613
  $this->clear_persistent_notice( $key );
614
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  }
inc/classes/admin-pages.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -32,7 +32,7 @@ namespace The_SEO_Framework;
32
  *
33
  * @since 2.8.0
34
  */
35
- class Admin_Pages extends Profile {
36
 
37
  /**
38
  * @since 2.7.0
@@ -40,12 +40,6 @@ class Admin_Pages extends Profile {
40
  */
41
  public $seo_settings_page_hook;
42
 
43
- /**
44
- * @since 2.6.0
45
- * @var bool $load_options Determines whether to load the options.
46
- */
47
- public $load_options;
48
-
49
  /**
50
  * Adds menu links under "settings" in the wp-admin dashboard
51
  *
@@ -177,6 +171,23 @@ class Admin_Pages extends Profile {
177
  );
178
  }
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  /**
181
  * Outputs notices on SEO setting changes.
182
  *
@@ -240,113 +251,6 @@ class Admin_Pages extends Profile {
240
  $this->output_dismissible_persistent_notices();
241
  }
242
 
243
- /**
244
- * Setting nav tab wrappers.
245
- * Outputs Tabs and settings content.
246
- *
247
- * @since 2.3.6
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 {
255
- * string tab ID => array : {
256
- * string name : Tab name.
257
- * callable callback : Output function.
258
- * string dashicon : The dashicon to use.
259
- * mixed args : Optional callback function args.
260
- * }
261
- * }
262
- * @param null $depr Deprecated.
263
- * @param bool $use_tabs Whether to output tabs, only works when $tabs count is greater than 1.
264
- */
265
- public function nav_tab_wrapper( $id, $tabs = [], $depr = null, $use_tabs = true ) {
266
- Bridges\SeoSettings::_nav_tab_wrapper( $id, $tabs, $use_tabs );
267
- }
268
-
269
- /**
270
- * Outputs in-post flex navigational wrapper and its content.
271
- *
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 {
279
- * string tab ID => array : {
280
- * string name : Tab name.
281
- * callable callback : Output function.
282
- * string dashicon : The dashicon to use.
283
- * mixed args : Optional callback function args.
284
- * }
285
- * }
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
-
293
- /**
294
- * Helper function that constructs name attributes for use in form fields.
295
- *
296
- * Other page implementation classes may wish to construct and use a
297
- * get_field_id() method, if the naming format needs to be different.
298
- *
299
- * @since 2.2.2
300
- *
301
- * @param string $name Field name base
302
- * @return string Full field name
303
- */
304
- public function get_field_name( $name ) {
305
- return sprintf( '%s[%s]', THE_SEO_FRAMEWORK_SITE_OPTIONS, $name );
306
- }
307
-
308
- /**
309
- * Echo constructed name attributes in form fields.
310
- *
311
- * @since 2.2.2
312
- * @uses $this->get_field_name() Construct name attributes for use in form fields.
313
- *
314
- * @param string $name Field name base
315
- */
316
- public function field_name( $name ) {
317
- echo \esc_attr( $this->get_field_name( $name ) );
318
- }
319
-
320
- /**
321
- * Helper function that constructs id attributes for use in form fields.
322
- *
323
- * @since 2.2.2
324
- *
325
- * @param string $id Field id base
326
- * @return string Full field id
327
- */
328
- public function get_field_id( $id ) {
329
- return sprintf( '%s[%s]', THE_SEO_FRAMEWORK_SITE_OPTIONS, $id );
330
- }
331
-
332
- /**
333
- * Echo constructed id attributes in form fields.
334
- *
335
- * @since 2.2.2
336
- * @uses $this->get_field_id() Constructs id attributes for use in form fields.
337
- *
338
- * @param string $id Field id base.
339
- * @param boolean $echo Whether to escape echo or just return.
340
- * @return string Full field id
341
- */
342
- public function field_id( $id, $echo = true ) {
343
- if ( $echo ) {
344
- echo \esc_attr( $this->get_field_id( $id ) );
345
- } else {
346
- return $this->get_field_id( $id );
347
- }
348
- }
349
-
350
  /**
351
  * Generates dismissible notice.
352
  * Also loads scripts and styles if out of The SEO Framework's context.
@@ -476,560 +380,6 @@ class Admin_Pages extends Profile {
476
  }
477
  }
478
 
479
- /**
480
- * Mark up content with code tags.
481
- * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
482
- *
483
- * @since 2.0.0
484
- *
485
- * @param string $content Content to be wrapped in code tags.
486
- * @return string Content wrapped in code tags.
487
- */
488
- public function code_wrap( $content ) {
489
- return $this->code_wrap_noesc( \esc_html( $content ) );
490
- }
491
-
492
- /**
493
- * Mark up content with code tags.
494
- * Escapes no HTML.
495
- *
496
- * @since 2.2.2
497
- *
498
- * @param string $content Content to be wrapped in code tags.
499
- * @return string Content wrapped in code tags.
500
- */
501
- public function code_wrap_noesc( $content ) {
502
- return '<code>' . $content . '</code>';
503
- }
504
-
505
- /**
506
- * Mark up content in description wrap.
507
- * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
508
- *
509
- * @since 2.7.0
510
- *
511
- * @param string $content Content to be wrapped in the description wrap.
512
- * @param bool $block Whether to wrap the content in <p> tags.
513
- */
514
- public function description( $content, $block = true ) {
515
- $this->description_noesc( \esc_html( $content ), $block );
516
- }
517
-
518
- /**
519
- * Mark up content in description wrap.
520
- *
521
- * @since 2.7.0
522
- *
523
- * @param string $content Content to be wrapped in the description wrap. Expected to be escaped.
524
- * @param bool $block Whether to wrap the content in <p> tags.
525
- */
526
- public function description_noesc( $content, $block = true ) {
527
- $output = '<span class="description">' . $content . '</span>';
528
- // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
529
- echo $block ? '<p>' . $output . '</p>' : $output;
530
- }
531
-
532
- /**
533
- * Mark up content in attention wrap.
534
- * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
535
- *
536
- * @since 3.1.0
537
- *
538
- * @param string $content Content to be wrapped in the attention wrap.
539
- * @param bool $block Whether to wrap the content in <p> tags.
540
- */
541
- public function attention( $content, $block = true ) {
542
- $this->attention_noesc( \esc_html( $content ), $block );
543
- }
544
-
545
- /**
546
- * Mark up content in attention wrap.
547
- *
548
- * @since 3.1.0
549
- *
550
- * @param string $content Content to be wrapped in the attention wrap. Expected to be escaped.
551
- * @param bool $block Whether to wrap the content in <p> tags.
552
- */
553
- public function attention_noesc( $content, $block = true ) {
554
- $output = '<span class="attention">' . $content . '</span>';
555
- // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
556
- echo $block ? '<p>' . $output . '</p>' : $output;
557
- }
558
-
559
- /**
560
- * Mark up content in a description+attention wrap.
561
- * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
562
- *
563
- * @since 3.1.0
564
- *
565
- * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
566
- * @param bool $block Whether to wrap the content in <p> tags.
567
- */
568
- public function attention_description( $content, $block = true ) {
569
- $this->attention_description_noesc( \esc_html( $content ), $block );
570
- }
571
-
572
- /**
573
- * Mark up content in a description+attention wrap.
574
- *
575
- * @since 3.1.0
576
- *
577
- * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
578
- * @param bool $block Whether to wrap the content in <p> tags.
579
- */
580
- public function attention_description_noesc( $content, $block = true ) {
581
- $output = '<span class="description attention">' . $content . '</span>';
582
- // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
583
- echo $block ? '<p>' . $output . '</p>' : $output;
584
- }
585
-
586
- /**
587
- * Echo or return a chechbox fields wrapper.
588
- *
589
- * This method does NOT escape.
590
- *
591
- * @since 2.6.0
592
- *
593
- * @param string $input The input to wrap. Should already be escaped.
594
- * @param bool $echo Whether to escape echo or just return.
595
- * @return string|void Wrapped $input.
596
- */
597
- public function wrap_fields( $input = '', $echo = false ) {
598
-
599
- if ( \is_array( $input ) )
600
- $input = implode( PHP_EOL, $input );
601
-
602
- if ( $echo ) {
603
- // phpcs:ignore, WordPress.Security.EscapeOutput -- Escape your $input prior!
604
- echo '<div class="tsf-fields">' . $input . '</div>';
605
- } else {
606
- return '<div class="tsf-fields">' . $input . '</div>';
607
- }
608
- }
609
-
610
- /**
611
- * Makes either simple or JSON-encoded data-* attributes for HTML elements.
612
- *
613
- * @since 4.0.0
614
- * @since 4.1.0 No longer adds an extra space in front of the return value when no data is generated.
615
- * @internal
616
- *
617
- * @param array $data : {
618
- * string $k => mixed $v
619
- * }
620
- * @return string The HTML data attributes, with added space to the start.
621
- */
622
- public function make_data_attributes( array $data ) {
623
-
624
- $ret = [];
625
-
626
- foreach ( $data as $k => $v ) {
627
- if ( ! is_scalar( $v ) ) {
628
- $ret[] = sprintf(
629
- 'data-%s="%s"',
630
- strtolower( preg_replace(
631
- '/([A-Z])/',
632
- '-$1',
633
- preg_replace( '/[^a-z0-9_\-]/i', '', $k )
634
- ) ), // dash case.
635
- htmlspecialchars( json_encode( $v, JSON_UNESCAPED_SLASHES ), ENT_COMPAT, 'UTF-8' )
636
- );
637
- } else {
638
- $ret[] = sprintf(
639
- 'data-%s="%s"',
640
- strtolower( preg_replace(
641
- '/([A-Z])/',
642
- '-$1',
643
- preg_replace( '/[^a-z0-9_\-]/i', '', $k )
644
- ) ), // dash case.
645
- \esc_attr( $v )
646
- );
647
- }
648
- }
649
-
650
- return $ret ? ' ' . implode( ' ', $ret ) : '';
651
- }
652
-
653
- /**
654
- * Returns a chechbox wrapper.
655
- *
656
- * @since 2.6.0
657
- * @since 2.7.0 Added escape parameter. Defaults to true.
658
- * @since 3.0.3 Added $disabled parameter. Defaults to false.
659
- * @see $this->make_checkbox_array()
660
- * @todo deprecate, use make_checkbox_array() instead? (fix 49 non-descriptive instances...? Should we deprecate?)
661
- * @todo move to this system to generator instead. See https://github.com/sybrew/the-seo-framework/projects/7
662
- *
663
- * @param string $field_id The option ID. Must be within the Autodescription settings.
664
- * @param string $label The checkbox description label.
665
- * @param string $description Addition description to place beneath the checkbox.
666
- * @param bool $escape Whether to escape the label and description.
667
- * @param bool $disabled Whether to disable the input.
668
- * @return string HTML checkbox output.
669
- */
670
- public function make_checkbox( $field_id = '', $label = '', $description = '', $escape = true, $disabled = false ) {
671
- return $this->make_checkbox_array( [
672
- 'id' => $field_id,
673
- 'index' => '',
674
- 'label' => $label,
675
- 'description' => $description,
676
- 'escape' => $escape,
677
- 'disabled' => $disabled,
678
- ] );
679
- }
680
-
681
- /**
682
- * Returns a chechbox wrapper.
683
- *
684
- * @since 3.1.0
685
- * @since 4.0.5 You can now supply an extra class for the checkbox.
686
- * @since 4.1.0 You can now supply a data field via `$args`.
687
- *
688
- * @param array $args : {
689
- * string $id The option name, used as field ID.
690
- * string $class The checkbox class.
691
- * string $index The option index, used when the option is an array.
692
- * string $label The checkbox label description, placed inline of the checkbox.
693
- * string $description The checkbox additional description, placed underneat.
694
- * array $data The checkbox field data. Sub-items are expected to be escaped if they're not an array.
695
- * bool $escape Whether to enable escaping of the $label and $description.
696
- * bool $disabled Whether to disable the checkbox field.
697
- * bool $default Whether to display-as-default. This is autodetermined when no $index is set.
698
- * bool $warned Whether to warn the checkbox field value.
699
- * }
700
- * @return string HTML checkbox output.
701
- */
702
- public function make_checkbox_array( array $args = [] ) {
703
-
704
- $args = array_merge(
705
- [
706
- 'id' => '',
707
- 'class' => '',
708
- 'index' => '',
709
- 'label' => '',
710
- 'description' => '',
711
- 'data' => [],
712
- 'escape' => true,
713
- 'disabled' => false,
714
- 'default' => false,
715
- 'warned' => false,
716
- ],
717
- $args
718
- );
719
-
720
- if ( $args['escape'] ) {
721
- $args['description'] = \esc_html( $args['description'] );
722
- $args['label'] = \esc_html( $args['label'] );
723
- }
724
-
725
- $index = $this->s_field_id( $args['index'] ?: '' );
726
-
727
- $field_id = $field_name = \esc_attr( sprintf(
728
- '%s%s',
729
- $this->get_field_id( $args['id'] ),
730
- $index ? sprintf( '[%s]', $index ) : ''
731
- ) );
732
-
733
- $value = $this->get_option( $args['id'] );
734
- if ( $index ) {
735
- $value = isset( $value[ $index ] ) ? $value[ $index ] : '';
736
- }
737
-
738
- $cb_classes = [];
739
-
740
- if ( $args['class'] ) {
741
- $cb_classes[] = $args['class'];
742
- }
743
-
744
- if ( $args['disabled'] ) {
745
- $cb_classes[] = 'tsf-disabled';
746
- } elseif ( ! $args['index'] ) {
747
- // Can't fetch conditionals in index.
748
- $cb_classes[] = $this->get_is_conditional_checked( $args['id'], false );
749
- } else {
750
- if ( $args['default'] ) {
751
- $cb_classes[] = 'tsf-default-selected';
752
- } elseif ( $args['warned'] ) {
753
- $cb_classes[] = 'tsf-warning-selected';
754
- }
755
- }
756
-
757
- $output = sprintf(
758
- '<span class="tsf-toblock">%s</span>',
759
- vsprintf(
760
- '<label for="%s" %s>%s</label>',
761
- [
762
- $field_id,
763
- ( $args['disabled'] ? 'class="tsf-disabled"' : '' ),
764
- vsprintf(
765
- '<input type=checkbox class="%s" name="%s" id="%s" value="1" %s %s %s /> %s',
766
- [
767
- \esc_attr( implode( ' ', $cb_classes ) ),
768
- $field_name,
769
- $field_id,
770
- \checked( $value, true, false ),
771
- ( $args['disabled'] ? 'disabled' : '' ),
772
- $args['data'] ? $this->make_data_attributes( $args['data'] ) : '',
773
- $args['label'],
774
- ]
775
- ),
776
- ]
777
- )
778
- );
779
-
780
- $output .= $args['description'] ? sprintf( '<p class="description tsf-option-spacer">%s</p>', $args['description'] ) : '';
781
-
782
- return $output;
783
- }
784
-
785
- /**
786
- * Returns a HTML select form elements for qubit options: -1, 0, or 1.
787
- * Does not support "multiple" field selections.
788
- *
789
- * @since 4.0.0
790
- * @TODO allow arrays as index, so we can support multidimensional options easily? @see is_conditional_checked
791
- *
792
- * @param array $args : {
793
- * string $id The select field ID.
794
- * string $class The div wrapper class.
795
- * string $name The option name.
796
- * int|string $default The current option value.
797
- * array $options The select option values : { value => name }
798
- * string $label The option label.
799
- * string $required Whether the field must be required.
800
- * array $data The select field data. Sub-items are expected to be escaped if they're not an array.
801
- * array $info Extra info field data.
802
- * }
803
- * @return string The option field.
804
- */
805
- public function make_single_select_form( array $args ) {
806
-
807
- $defaults = [
808
- 'id' => '',
809
- 'class' => '',
810
- 'name' => '',
811
- 'default' => '',
812
- 'options' => [],
813
- 'label' => '',
814
- 'required' => false,
815
- 'data' => [],
816
- 'info' => [],
817
- ];
818
-
819
- $args = array_merge( $defaults, $args );
820
-
821
- // The walk below destroys the option array. Assign it to a new var to prevent confusion later.
822
- $html_options = $args['options'];
823
- /**
824
- * @param string $name The option name. Passed by reference, returned as the HTML option item.
825
- * @param mixed $value
826
- * @param mixed $default
827
- */
828
- $create_option = function( &$name, $value, $default ) {
829
- $name = sprintf(
830
- '<option value="%s"%s>%s</option>',
831
- \esc_attr( $value ),
832
- (string) $value === (string) $default ? ' selected' : '',
833
- \esc_html( $name )
834
- );
835
- };
836
- array_walk( $html_options, $create_option, $args['default'] );
837
-
838
- return vsprintf(
839
- sprintf( '<div class="%s">%s</div>',
840
- \esc_attr( $args['class'] ),
841
- ( \is_rtl() ? '%2$s%1$s%3$s' : '%1$s%2$s%3$s' )
842
- ),
843
- [
844
- $args['label'] ? sprintf(
845
- '<label for=%s>%s</label> ', // NOTE: extra space!
846
- $this->s_field_id( $args['id'] ),
847
- \esc_html( $args['label'] )
848
- ) : '',
849
- $args['info'] ? ' ' . $this->make_info(
850
- $args['info'][0],
851
- isset( $args['info'][1] ) ? $args['info'][1] : '',
852
- false
853
- ) : '',
854
- vsprintf(
855
- '<select id=%s name=%s %s %s>%s</select>',
856
- [
857
- $this->s_field_id( $args['id'] ),
858
- \esc_attr( $args['name'] ),
859
- $args['required'] ? 'required' : '',
860
- $args['data'] ? $this->make_data_attributes( $args['data'] ) : '',
861
- implode( $html_options ),
862
- ]
863
- ),
864
- ]
865
- );
866
- }
867
-
868
- /**
869
- * Return a wrapped question mark.
870
- *
871
- * @since 2.6.0
872
- * @since 3.0.0 Links are now no longer followed, referred or bound to opener.
873
- * @since 4.0.0 Now adds a tabindex to the span tag, so you can focus it using keyboard navigation.
874
- *
875
- * @param string $description The descriptive on-hover title.
876
- * @param string $link The non-escaped link.
877
- * @param bool $echo Whether to echo or return.
878
- * @return string HTML checkbox output if $echo is false.
879
- */
880
- public function make_info( $description = '', $link = '', $echo = true ) {
881
-
882
- if ( $link ) {
883
- $output = sprintf(
884
- '<a href="%1$s" class="tsf-tooltip-item tsf-help" target="_blank" rel="nofollow noreferrer noopener" title="%2$s" data-desc="%2$s">[?]</a>',
885
- \esc_url( $link, [ 'https', 'http' ] ),
886
- \esc_attr( $description )
887
- );
888
- } else {
889
- $output = sprintf(
890
- '<span class="tsf-tooltip-item tsf-help" title="%1$s" data-desc="%1$s" tabindex=0>[?]</span>',
891
- \esc_attr( $description )
892
- );
893
- }
894
-
895
- $output = sprintf( '<span class="tsf-tooltip-wrap">%s</span>', $output );
896
-
897
- if ( $echo ) {
898
- // phpcs:ignore, WordPress.Security.EscapeOutput
899
- echo $output;
900
- } else {
901
- return $output;
902
- }
903
- }
904
-
905
- /**
906
- * Returns the HTML class wrap for default Checkbox options.
907
- *
908
- * This function does nothing special. But is merely a simple wrapper.
909
- * Just like code_wrap.
910
- *
911
- * @since 2.2.5
912
- * @since 3.1.0 Deprecated second parameter.
913
- *
914
- * @param string $key The option name which returns boolean.
915
- * @param string $depr Deprecated
916
- * @param bool $wrap Whether to wrap the class name in `class="%s"`
917
- * @param bool $echo Whether to echo or return the output.
918
- * @return string Empty on echo or the class name with an optional wrapper.
919
- */
920
- public function is_default_checked( $key, $depr = '', $wrap = true, $echo = true ) {
921
-
922
- $class = '';
923
-
924
- $default = $this->get_default_settings( $key );
925
-
926
- if ( 1 === $default )
927
- $class = 'tsf-default-selected';
928
-
929
- if ( $echo ) {
930
- if ( $wrap ) {
931
- printf( 'class="%s"', \esc_attr( $class ) );
932
- } else {
933
- echo \esc_attr( $class );
934
- }
935
- } else {
936
- if ( $wrap )
937
- return sprintf( 'class="%s"', $class );
938
-
939
- return $class;
940
- }
941
- }
942
-
943
- /**
944
- * Returns the HTML class wrap for warning Checkbox options.
945
- *
946
- * @since 2.3.4
947
- * @since 3.1.0 Deprecated second parameter.
948
- *
949
- * @param string $key The option name which returns boolean.
950
- * @param string $deprecated Deprecated.
951
- * @param bool $wrap Whether to wrap the class name in `class="%s"`
952
- * @param bool $echo Whether to echo or return the output.
953
- * @return string Empty on echo or the class name with an optional wrapper.
954
- */
955
- public function is_warning_checked( $key, $deprecated = '', $wrap = true, $echo = true ) {
956
-
957
- $class = '';
958
-
959
- $warned = $this->get_warned_settings( $key, $deprecated );
960
-
961
- if ( 1 === $warned )
962
- $class = 'tsf-warning-selected';
963
-
964
- if ( $echo ) {
965
- if ( $wrap ) {
966
- printf( 'class="%s"', \esc_attr( $class ) );
967
- } else {
968
- echo \esc_attr( $class );
969
- }
970
- } else {
971
- if ( $wrap )
972
- return sprintf( 'class="%s"', $class );
973
-
974
- return $class;
975
- }
976
- }
977
-
978
- /**
979
- * Returns the HTML class wrap for warning/default Checkbox options.
980
- *
981
- * @since 2.6.0
982
- * @since 3.1.0 Added the $wrap parameter.
983
- *
984
- * @param string $key The option name which returns boolean.
985
- * @param bool $wrap Whether to wrap the class name in `class="%s"`
986
- */
987
- public function get_is_conditional_checked( $key, $wrap = true ) {
988
- return $this->is_conditional_checked( $key, '', $wrap, false );
989
- }
990
-
991
- /**
992
- * Returns the HTML class wrap for warning/default Checkbox options.
993
- *
994
- * @since 2.3.4
995
- * @since 3.1.0 Deprecated second parameter.
996
- * @TODO allow array as $key, so we can support multidimensional options?
997
- *
998
- * @param string $key The option name which returns boolean.
999
- * @param string $deprecated Deprecated. Used to be the settings field.
1000
- * @param bool $wrap Whether to wrap the class name in `class="%s"`
1001
- * @param bool $echo Whether to echo or return the output.
1002
- * @return string Empty on echo or the class name with an optional wrapper.
1003
- */
1004
- public function is_conditional_checked( $key, $deprecated = '', $wrap = true, $echo = true ) {
1005
-
1006
- $class = '';
1007
-
1008
- $default = $this->is_default_checked( $key, $deprecated, false, false );
1009
- $warned = $this->is_warning_checked( $key, $deprecated, false, false );
1010
-
1011
- if ( '' !== $default && '' !== $warned ) {
1012
- $class = $default . ' ' . $warned;
1013
- } elseif ( '' !== $default ) {
1014
- $class = $default;
1015
- } elseif ( '' !== $warned ) {
1016
- $class = $warned;
1017
- }
1018
-
1019
- if ( $echo ) {
1020
- if ( $wrap ) {
1021
- printf( 'class="%s"', \esc_attr( $class ) );
1022
- } else {
1023
- echo \esc_attr( $class );
1024
- }
1025
- } else {
1026
- if ( $wrap )
1027
- return sprintf( 'class="%s"', $class );
1028
-
1029
- return $class;
1030
- }
1031
- }
1032
-
1033
  /**
1034
  * Returns the SEO Bar.
1035
  *
@@ -1048,99 +398,6 @@ class Admin_Pages extends Profile {
1048
  return Interpreters\SeoBar::generate_bar( $query );
1049
  }
1050
 
1051
- /**
1052
- * Returns social image uploader form button.
1053
- * Also registers additional i18n strings for JS.
1054
- *
1055
- * @since 2.8.0
1056
- * @since 3.1.0 No longer prepares media l10n data.
1057
- * @since 4.0.0 Now adds a media preview dispenser.
1058
- * @since 4.1.2 No longer adds a redundant title to the selection button.
1059
- *
1060
- * @param string $input_id Required. The HTML input id to pass URL into.
1061
- * @return string The image uploader button.
1062
- */
1063
- public function get_social_image_uploader_form( $input_id ) {
1064
-
1065
- if ( ! $input_id )
1066
- return '';
1067
-
1068
- $s_input_id = \esc_attr( $input_id );
1069
-
1070
- $content = vsprintf(
1071
- '<button type=button data-href="%s" class="tsf-set-image-button button button-primary button-small" title="%s" id="%s-select"
1072
- %s>%s</button>',
1073
- [
1074
- \esc_url( \get_upload_iframe_src( 'image', $this->get_the_real_ID() ) ),
1075
- '', // redundant
1076
- $s_input_id,
1077
- $this->make_data_attributes( [
1078
- 'inputId' => $s_input_id,
1079
- 'inputType' => 'social',
1080
- 'width' => 1200,
1081
- 'height' => 630,
1082
- 'minWidth' => 200,
1083
- 'minHeight' => 200,
1084
- 'flex' => true,
1085
- ] ),
1086
- \esc_html__( 'Select Image', 'autodescription' ),
1087
- ]
1088
- );
1089
-
1090
- $content .= sprintf(
1091
- '<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>',
1092
- $s_input_id
1093
- );
1094
-
1095
- return $content;
1096
- }
1097
-
1098
- /**
1099
- * Returns logo uploader form buttons.
1100
- * Also registers additional i18n strings for JS.
1101
- *
1102
- * @since 3.0.0
1103
- * @since 3.1.0 No longer prepares media l10n data.
1104
- * @since 4.0.0 Now adds a media preview dispenser.
1105
- *
1106
- * @param string $input_id Required. The HTML input id to pass URL into.
1107
- * @return string The image uploader button.
1108
- */
1109
- public function get_logo_uploader_form( $input_id ) {
1110
-
1111
- if ( ! $input_id )
1112
- return '';
1113
-
1114
- $s_input_id = \esc_attr( $input_id );
1115
-
1116
- $content = vsprintf(
1117
- '<button type=button data-href="%s" class="tsf-set-image-button button button-primary button-small" title="%s" id="%s-select"
1118
- %s>%s</button>',
1119
- [
1120
- \esc_url( \get_upload_iframe_src( 'image', $this->get_the_real_ID() ) ),
1121
- '', // Redundant
1122
- $s_input_id,
1123
- $this->make_data_attributes( [
1124
- 'inputId' => $s_input_id,
1125
- 'inputType' => 'logo',
1126
- 'width' => 512,
1127
- 'height' => 512,
1128
- 'minWidth' => 112,
1129
- 'minHeight' => 112,
1130
- 'flex' => true,
1131
- ] ),
1132
- \esc_html__( 'Select Logo', 'autodescription' ),
1133
- ]
1134
- );
1135
-
1136
- $content .= sprintf(
1137
- '<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>',
1138
- $s_input_id
1139
- );
1140
-
1141
- return $content;
1142
- }
1143
-
1144
  /**
1145
  * Outputs floating and reference title HTML elements for JavaScript.
1146
  *
@@ -1180,7 +437,7 @@ class Admin_Pages extends Profile {
1180
  ),
1181
  \esc_attr( $id ),
1182
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
1183
- $this->make_data_attributes( $data )
1184
  );
1185
  }
1186
 
@@ -1218,66 +475,7 @@ class Admin_Pages extends Profile {
1218
  ),
1219
  \esc_attr( $id ),
1220
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
1221
- $this->make_data_attributes( $data )
1222
- );
1223
- }
1224
-
1225
- /**
1226
- * Outputs character counter wrap for both JavaScript and no-Javascript.
1227
- *
1228
- * @since 3.0.0
1229
- * @since 3.1.0 : 1. Added an "what if you click" onhover-title.
1230
- * 2. Removed second parameter's usage. For passing the expected string.
1231
- * 3. The whole output is now hidden from no-js.
1232
- * @since 4.1.0 No longer marks up the counter with the `description` HTML class.
1233
- *
1234
- * @param string $for The input ID it's for.
1235
- * @param string $depr The initial value for no-JS. Deprecated.
1236
- * @param bool $display Whether to display the counter. (options page gimmick)
1237
- */
1238
- public function output_character_counter_wrap( $for, $depr = '', $display = true ) {
1239
- vprintf(
1240
- '<div class="tsf-counter-wrap hide-if-no-tsf-js" %s><span class=tsf-counter title="%s">%s</span><span class=tsf-ajax></span></div>',
1241
- [
1242
- ( $display ? '' : 'style=display:none;' ),
1243
- \esc_attr__( 'Click to change the counter type', 'autodescription' ),
1244
- sprintf(
1245
- /* translators: %s = number */
1246
- \esc_html__( 'Characters: %s', 'autodescription' ),
1247
- sprintf(
1248
- '<span id="%s">%s</span>',
1249
- \esc_attr( "{$for}_chars" ),
1250
- 0
1251
- )
1252
- ),
1253
- ]
1254
- );
1255
- }
1256
-
1257
- /**
1258
- * Outputs pixel counter wrap for javascript.
1259
- *
1260
- * @since 3.0.0
1261
- *
1262
- * @param string $for The input ID it's for.
1263
- * @param string $type Whether it's a 'title' or 'description' counter.
1264
- * @param bool $display Whether to display the counter. (options page gimmick)
1265
- */
1266
- public function output_pixel_counter_wrap( $for, $type, $display = true ) {
1267
- vprintf(
1268
- '<div class="tsf-pixel-counter-wrap hide-if-no-tsf-js" %s>%s%s</div>',
1269
- [
1270
- ( $display ? '' : 'style="display:none;"' ),
1271
- sprintf(
1272
- '<div id="%s" class="tsf-tooltip-wrap">%s</div>',
1273
- \esc_attr( "{$for}_pixels" ),
1274
- '<span class="tsf-pixel-counter-bar tsf-tooltip-item" aria-label="" data-desc="" tabindex=0><span class="tsf-pixel-counter-fluid"></span></span>'
1275
- ),
1276
- sprintf(
1277
- '<div class="tsf-pixel-shadow-wrap"><span class="tsf-pixel-counter-shadow %s"></span></div>',
1278
- \esc_attr( "tsf-{$type}-pixel-counter-shadow" )
1279
- ),
1280
- ]
1281
  );
1282
  }
1283
 
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
32
  *
33
  * @since 2.8.0
34
  */
35
+ class Admin_Pages extends Generate_Ldjson {
36
 
37
  /**
38
  * @since 2.7.0
40
  */
41
  public $seo_settings_page_hook;
42
 
 
 
 
 
 
 
43
  /**
44
  * Adds menu links under "settings" in the wp-admin dashboard
45
  *
171
  );
172
  }
173
 
174
+ /**
175
+ * Prepares profile/user edit view, like outputting the SEO fields.
176
+ *
177
+ * @since 4.1.4
178
+ * @access private
179
+ */
180
+ public function _init_user_edit_view() {
181
+
182
+ if ( ! $this->is_profile_edit() ) return;
183
+
184
+ // WordPress made a mess of this. We can't reliably get a user future-proof. Load class for all users; check there.
185
+ // if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
186
+
187
+ \add_action( 'show_user_profile', Bridges\UserSettings::class . '::_prepare_setting_fields', 0, 1 );
188
+ \add_action( 'edit_user_profile', Bridges\UserSettings::class . '::_prepare_setting_fields', 0, 1 );
189
+ }
190
+
191
  /**
192
  * Outputs notices on SEO setting changes.
193
  *
251
  $this->output_dismissible_persistent_notices();
252
  }
253
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  /**
255
  * Generates dismissible notice.
256
  * Also loads scripts and styles if out of The SEO Framework's context.
380
  }
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  /**
384
  * Returns the SEO Bar.
385
  *
398
  return Interpreters\SeoBar::generate_bar( $query );
399
  }
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  /**
402
  * Outputs floating and reference title HTML elements for JavaScript.
403
  *
437
  ),
438
  \esc_attr( $id ),
439
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
440
+ Interpreters\HTML::make_data_attributes( $data )
441
  );
442
  }
443
 
475
  ),
476
  \esc_attr( $id ),
477
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
478
+ Interpreters\HTML::make_data_attributes( $data )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  );
480
  }
481
 
inc/classes/bridges/ajax.class.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Bridges\AJAX
4
+ * @subpackage The_SEO_Framework\Feed
5
+ */
6
+
7
+ namespace The_SEO_Framework\Bridges;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (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
+ * Holds AJAX callbacks.
30
+ *
31
+ * The methods in this class may move to other classes later, so that
32
+ * they're with their relatives.
33
+ *
34
+ * @since 4.1.4
35
+ * @access private
36
+ * @final Can't be extended.
37
+ */
38
+ final class AJAX {
39
+
40
+ /**
41
+ * Clears persistent notice on user request (clicked Dismiss icon) via AJAX.
42
+ *
43
+ * @since 4.1.0
44
+ * @since 4.1.4 Moved to \The_SEO_Framework\Bridges\AJAX and made static.
45
+ * Security check OK.
46
+ * @access private
47
+ */
48
+ public static function _wp_ajax_dismiss_notice() {
49
+
50
+ // phpcs:ignore, WordPress.Security.NonceVerification.Missing -- We require the POST data to find locally stored nonces.
51
+ $key = isset( $_POST['tsf_dismiss_key'] ) ? $_POST['tsf_dismiss_key'] : '';
52
+
53
+ if ( ! $key )
54
+ \wp_send_json_error( null, 400 );
55
+
56
+ $tsf = \the_seo_framework();
57
+
58
+ $notices = $tsf->get_static_cache( 'persistent_notices', [] );
59
+ if ( empty( $notices[ $key ]['conditions']['capability'] ) ) {
60
+ // Notice was deleted already elsewhere, or key was faulty. Either way, ignore--should be self-resolving.
61
+ \wp_send_json_error( null, 409 );
62
+ }
63
+
64
+ if ( ! \current_user_can( $notices[ $key ]['conditions']['capability'] )
65
+ || ! \check_ajax_referer( $tsf->_get_dismiss_notice_nonce_action( $key ), 'tsf_dismiss_nonce', false ) )
66
+ \wp_die( -1, 403 );
67
+
68
+ $tsf->clear_persistent_notice( $key );
69
+ \wp_send_json_success( null, 200 );
70
+ }
71
+
72
+ /**
73
+ * Handles counter option update on AJAX request for users that can edit posts.
74
+ *
75
+ * @since 3.1.0 Introduced in 2.6.0, but the name changed.
76
+ * @since 4.1.4 Moved to \The_SEO_Framework\Bridges\AJAX and made static.
77
+ * @securitycheck 3.0.0 OK.
78
+ * @access private
79
+ */
80
+ public static function _wp_ajax_update_counter_type() {
81
+
82
+ $tsf = \the_seo_framework();
83
+
84
+ // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer() does this.
85
+ $tsf->_check_tsf_ajax_referer( 'edit_posts' );
86
+
87
+ // Remove output buffer. TODO why don't we do this consistently??
88
+ $tsf->clean_response_header();
89
+
90
+ // If current user isn't allowed to edit posts, don't do anything and kill PHP.
91
+ if ( ! \current_user_can( 'edit_posts' ) ) {
92
+ // Encode and echo results. Requires JSON decode within JS.
93
+ \wp_send_json( [
94
+ 'type' => 'failure',
95
+ 'value' => '',
96
+ ] );
97
+ }
98
+
99
+ /**
100
+ * Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
101
+ * $_POST['val'] already contains updated number.
102
+ */
103
+ if ( isset( $_POST['val'] ) ) {
104
+ $value = (int) $_POST['val'];
105
+ } else {
106
+ $value = $tsf->get_user_meta_item( 'counter_type' ) + 1;
107
+ }
108
+ $value = \absint( $value );
109
+
110
+ if ( $value > 3 )
111
+ $value = 0;
112
+
113
+ // Update the option and get results of action.
114
+ $type = $tsf->update_user_option( 0, 'counter_type', $value ) ? 'success' : 'error';
115
+
116
+ $results = [
117
+ 'type' => $type,
118
+ 'value' => $value,
119
+ ];
120
+
121
+ // Encode and echo results. Requires JSON decode within JS.
122
+ \wp_send_json( $results );
123
+
124
+ // phpcs:enable, WordPress.Security.NonceVerification
125
+ }
126
+
127
+ /**
128
+ * Handles cropping of images on AJAX request.
129
+ *
130
+ * This function is necessary because we don't believe `wp_ajax_crop_image()` should check for `edit_post`,
131
+ * but for `upload_files`. This is because the 'post' (file) that's edited isn't linked to the newly cropped
132
+ * image, thus it's creating a NEW 'post'. Nothing is 'edited' on the old 'post'.
133
+ *
134
+ * Copied from WordPress Core wp_ajax_crop_image.
135
+ * Adjusted: 1. It accepts capability 'upload_files', instead of 'customize'.
136
+ * - This was set to 'edit_post' in WP 4.7? trac ticket got lost, probably for (invalid) security reasons.
137
+ * In any case, that's still incorrect, and I gave up on communicating this;
138
+ * We're not editing the image, we're creating a new one!
139
+ * 2. It now only accepts TSF own AJAX nonces.
140
+ * 3. It now only accepts context 'tsf-image'
141
+ * 4. It no longer accepts a default context.
142
+ *
143
+ * @since 3.1.0 Introduced in 2.9.0, but the name changed.
144
+ * @since 4.1.4 Moved to \The_SEO_Framework\Bridges\AJAX and made static.
145
+ * @securitycheck 3.0.0 OK.
146
+ * @access private
147
+ */
148
+ public static function _wp_ajax_crop_image() {
149
+
150
+ // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer does this.
151
+ \the_seo_framework()->_check_tsf_ajax_referer( 'upload_files' );
152
+
153
+ if ( ! \current_user_can( 'upload_files' ) || ! isset( $_POST['id'], $_POST['context'], $_POST['cropDetails'] ) )
154
+ \wp_send_json_error();
155
+
156
+ $attachment_id = \absint( $_POST['id'] );
157
+
158
+ $context = str_replace( '_', '-', \sanitize_key( $_POST['context'] ) );
159
+ $data = array_map( '\\absint', $_POST['cropDetails'] );
160
+ $cropped = \wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
161
+
162
+ if ( ! $cropped || \is_wp_error( $cropped ) )
163
+ \wp_send_json_error( [ 'message' => \esc_js( \__( 'Image could not be processed.', 'default' ) ) ] );
164
+
165
+ switch ( $context ) :
166
+ case 'tsf-image':
167
+ /**
168
+ * Fires before a cropped image is saved.
169
+ *
170
+ * Allows to add filters to modify the way a cropped image is saved.
171
+ *
172
+ * @since 4.3.0 WordPress Core
173
+ *
174
+ * @param string $context The Customizer control requesting the cropped image.
175
+ * @param int $attachment_id The attachment ID of the original image.
176
+ * @param string $cropped Path to the cropped image file.
177
+ */
178
+ \do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
179
+
180
+ /** This filter is documented in wp-admin/custom-header.php */
181
+ $cropped = \apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
182
+
183
+ $parent_url = \wp_get_attachment_url( $attachment_id );
184
+ $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
185
+
186
+ // phpcs:ignore, WordPress.PHP.NoSilencedErrors -- Feature may be disabled; should not cause fatal errors.
187
+ $size = @getimagesize( $cropped );
188
+ $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
189
+
190
+ $object = [
191
+ 'post_title' => basename( $cropped ),
192
+ 'post_content' => $url,
193
+ 'post_mime_type' => $image_type,
194
+ 'guid' => $url,
195
+ 'context' => $context,
196
+ ];
197
+
198
+ $attachment_id = \wp_insert_attachment( $object, $cropped );
199
+ $metadata = \wp_generate_attachment_metadata( $attachment_id, $cropped );
200
+
201
+ /**
202
+ * Filters the cropped image attachment metadata.
203
+ *
204
+ * @since 4.3.0 WordPress Core
205
+ * @see wp_generate_attachment_metadata()
206
+ *
207
+ * @param array $metadata Attachment metadata.
208
+ */
209
+ $metadata = \apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
210
+ \wp_update_attachment_metadata( $attachment_id, $metadata );
211
+
212
+ /**
213
+ * Filters the attachment ID for a cropped image.
214
+ *
215
+ * @since 4.3.0 WordPress Core
216
+ *
217
+ * @param int $attachment_id The attachment ID of the cropped image.
218
+ * @param string $context The Customizer control requesting the cropped image.
219
+ */
220
+ $attachment_id = \apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
221
+ break;
222
+
223
+ default:
224
+ \wp_send_json_error( [ 'message' => \esc_js( \__( 'Image could not be processed.', 'default' ) ) ] );
225
+ break;
226
+ endswitch;
227
+
228
+ \wp_send_json_success( \wp_prepare_attachment_for_js( $attachment_id ) );
229
+
230
+ // phpcs:enable, WordPress.Security.NonceVerification
231
+ }
232
+
233
+ /**
234
+ * Gets an SEO Bar for AJAX during edit-post.
235
+ *
236
+ * @since 4.0.0
237
+ * @since 4.1.4 Moved to \The_SEO_Framework\Bridges\AJAX and made static.
238
+ * @access private
239
+ */
240
+ public static function _wp_ajax_get_post_data() {
241
+
242
+ $tsf = \the_seo_framework();
243
+
244
+ // phpcs:disable, WordPress.Security.NonceVerification -- _check_tsf_ajax_referer() does this.
245
+ $tsf->_check_tsf_ajax_referer( 'edit_posts' );
246
+
247
+ // Clear output buffer.
248
+ $tsf->clean_response_header();
249
+
250
+ $post_id = \absint( $_POST['post_id'] );
251
+
252
+ if ( ! $post_id || ! \current_user_can( 'edit_post', $post_id ) ) {
253
+ \wp_send_json( [
254
+ 'type' => 'failure',
255
+ 'data' => [],
256
+ ] );
257
+ }
258
+
259
+ $_get_defaults = [
260
+ 'seobar' => false,
261
+ 'metadescription' => false,
262
+ 'ogdescription' => false,
263
+ 'twdescription' => false,
264
+ 'imageurl' => false,
265
+ ];
266
+
267
+ // Only get what's indexed in the defaults and set as "true".
268
+ $get = array_keys(
269
+ array_filter(
270
+ array_intersect_key(
271
+ array_merge(
272
+ $_get_defaults,
273
+ (array) ( isset( $_POST['get'] ) ? $_POST['get'] : [] )
274
+ ),
275
+ $_get_defaults
276
+ )
277
+ )
278
+ );
279
+
280
+ $_generator_args = [
281
+ 'id' => $post_id,
282
+ 'taxonomy' => '',
283
+ ];
284
+
285
+ $data = [];
286
+
287
+ foreach ( $get as $g ) :
288
+ switch ( $g ) {
289
+ case 'seobar':
290
+ $data[ $g ] = $tsf->get_generated_seo_bar( $_generator_args );
291
+ break;
292
+
293
+ case 'metadescription':
294
+ case 'ogdescription':
295
+ case 'twdescription':
296
+ switch ( $g ) {
297
+ case 'metadescription':
298
+ if ( $tsf->is_static_frontpage( $post_id ) ) {
299
+ // phpcs:disable, WordPress.WhiteSpace.PrecisionAlignment
300
+ $data[ $g ] = $tsf->get_option( 'homepage_description' )
301
+ ?: $tsf->get_generated_description( $_generator_args, false );
302
+ // phpcs:enable, WordPress.WhiteSpace.PrecisionAlignment
303
+ } else {
304
+ $data[ $g ] = $tsf->get_generated_description( $_generator_args, false );
305
+ }
306
+ break;
307
+ case 'ogdescription':
308
+ // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- Smart loop.
309
+ $_social_ph = isset( $_social_ph ) ? $_social_ph : $tsf->_get_social_placeholders( $_generator_args );
310
+ $data[ $g ] = $_social_ph['description']['og'];
311
+ break;
312
+ case 'twdescription':
313
+ // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- Smart loop.
314
+ $_social_ph = isset( $_social_ph ) ? $_social_ph : $tsf->_get_social_placeholders( $_generator_args );
315
+ $data[ $g ] = $_social_ph['description']['twitter'];
316
+ break;
317
+ }
318
+
319
+ $data[ $g ] = $tsf->s_description( $data[ $g ] );
320
+ break;
321
+
322
+ case 'imageurl':
323
+ if ( $tsf->is_static_frontpage( $post_id ) && $tsf->get_option( 'homepage_social_image_url' ) ) {
324
+ $image_details = current( $tsf->get_image_details( $_generator_args, true, 'social', true ) );
325
+ $data[ $g ] = isset( $image_details['url'] ) ? $image_details['url'] : '';
326
+ } else {
327
+ $image_details = current( $tsf->get_generated_image_details( $_generator_args, true, 'social', true ) );
328
+ $data[ $g ] = isset( $image_details['url'] ) ? $image_details['url'] : '';
329
+ }
330
+ break;
331
+
332
+ default:
333
+ break;
334
+ }
335
+ endforeach;
336
+
337
+ \wp_send_json( [
338
+ 'type' => 'success',
339
+ 'data' => $data,
340
+ 'processed' => $get,
341
+ ] );
342
+
343
+ // phpcs:enable, WordPress.Security.NonceVerification
344
+ }
345
+ }
inc/classes/bridges/feed.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2020 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/bridges/listedit.class.php CHANGED
@@ -6,9 +6,11 @@
6
 
7
  namespace The_SEO_Framework\Bridges;
8
 
 
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -170,8 +172,9 @@ final class ListEdit extends ListTable {
170
  'taxonomy' => '',
171
  ];
172
 
173
- $r_defaults = $tsf->robots_meta(
174
  $query,
 
175
  \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
176
  );
177
 
@@ -230,7 +233,7 @@ final class ListEdit extends ListTable {
230
  '<span class=hidden id=%s %s></span>',
231
  sprintf( 'tsfLeData[%s]', (int) $post_id ),
232
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
233
- $tsf->make_data_attributes( [ 'le' => $data ] )
234
  );
235
 
236
  if ( $tsf->is_static_frontpage( $query['id'] ) ) {
@@ -261,6 +264,9 @@ final class ListEdit extends ListTable {
261
  $is_desc_ref_locked = false;
262
  }
263
 
 
 
 
264
  $title_data = [
265
  'refTitleLocked' => $is_title_ref_locked,
266
  'defaultTitle' => $default_title,
@@ -273,19 +279,26 @@ final class ListEdit extends ListTable {
273
  'defaultDescription' => $default_description,
274
  ];
275
 
 
 
 
 
 
 
 
276
  printf(
277
  // '<span class=hidden id=%s data-le-title="%s"></span>',
278
  '<span class=hidden id=%s %s></span>',
279
  sprintf( 'tsfLeTitleData[%s]', (int) $post_id ),
280
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
281
- $tsf->make_data_attributes( [ 'leTitle' => $title_data ] )
282
  );
283
  printf(
284
  // '<span class=hidden id=%s data-le-description="%s"></span>',
285
  '<span class=hidden id=%s %s></span>',
286
  sprintf( 'tsfLeDescriptionData[%s]', (int) $post_id ),
287
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
288
- $tsf->make_data_attributes( [ 'leDescription' => $desc_data ] )
289
  );
290
 
291
  if ( $this->doing_ajax )
@@ -319,8 +332,9 @@ final class ListEdit extends ListTable {
319
  'taxonomy' => $this->taxonomy,
320
  ];
321
 
322
- $r_defaults = $tsf->robots_meta(
323
  $query,
 
324
  \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
325
  );
326
 
@@ -380,7 +394,7 @@ final class ListEdit extends ListTable {
380
  '<span class=hidden id=%s %s></span>',
381
  sprintf( 'tsfLeData[%s]', (int) $term_id ),
382
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
383
- $tsf->make_data_attributes( [ 'le' => $data ] )
384
  );
385
 
386
  $term_prefix = $tsf->use_generated_archive_prefix( \get_taxonomy( $query['taxonomy'] ) )
@@ -404,13 +418,13 @@ final class ListEdit extends ListTable {
404
  '<span class=hidden id=%s %s></span>',
405
  sprintf( 'tsfLeTitleData[%s]', (int) $term_id ),
406
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
407
- $tsf->make_data_attributes( [ 'leTitle' => $title_data ] )
408
  );
409
  $container .= sprintf(
410
  '<span class=hidden id=%s %s></span>',
411
  sprintf( 'tsfLeDescriptionData[%s]', (int) $term_id ),
412
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
413
- $tsf->make_data_attributes( [ 'leDescription' => $desc_data ] )
414
  );
415
 
416
  if ( $this->doing_ajax )
6
 
7
  namespace The_SEO_Framework\Bridges;
8
 
9
+ use \The_SEO_Framework\Interpreters\HTML;
10
+
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
172
  'taxonomy' => '',
173
  ];
174
 
175
+ $r_defaults = $tsf->generate_robots_meta(
176
  $query,
177
+ null,
178
  \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
179
  );
180
 
233
  '<span class=hidden id=%s %s></span>',
234
  sprintf( 'tsfLeData[%s]', (int) $post_id ),
235
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
236
+ HTML::make_data_attributes( [ 'le' => $data ] )
237
  );
238
 
239
  if ( $tsf->is_static_frontpage( $query['id'] ) ) {
264
  $is_desc_ref_locked = false;
265
  }
266
 
267
+ $post_data = [
268
+ 'isFront' => $tsf->is_static_frontpage( $query['id'] ),
269
+ ];
270
  $title_data = [
271
  'refTitleLocked' => $is_title_ref_locked,
272
  'defaultTitle' => $default_title,
279
  'defaultDescription' => $default_description,
280
  ];
281
 
282
+ printf(
283
+ // '<span class=hidden id=%s data-le-post-data="%s"></span>',
284
+ '<span class=hidden id=%s %s></span>',
285
+ sprintf( 'tsfLePostData[%s]', (int) $post_id ),
286
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
287
+ HTML::make_data_attributes( [ 'lePostData' => $post_data ] )
288
+ );
289
  printf(
290
  // '<span class=hidden id=%s data-le-title="%s"></span>',
291
  '<span class=hidden id=%s %s></span>',
292
  sprintf( 'tsfLeTitleData[%s]', (int) $post_id ),
293
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
294
+ HTML::make_data_attributes( [ 'leTitle' => $title_data ] )
295
  );
296
  printf(
297
  // '<span class=hidden id=%s data-le-description="%s"></span>',
298
  '<span class=hidden id=%s %s></span>',
299
  sprintf( 'tsfLeDescriptionData[%s]', (int) $post_id ),
300
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
301
+ HTML::make_data_attributes( [ 'leDescription' => $desc_data ] )
302
  );
303
 
304
  if ( $this->doing_ajax )
332
  'taxonomy' => $this->taxonomy,
333
  ];
334
 
335
+ $r_defaults = $tsf->generate_robots_meta(
336
  $query,
337
+ null,
338
  \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS
339
  );
340
 
394
  '<span class=hidden id=%s %s></span>',
395
  sprintf( 'tsfLeData[%s]', (int) $term_id ),
396
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
397
+ HTML::make_data_attributes( [ 'le' => $data ] )
398
  );
399
 
400
  $term_prefix = $tsf->use_generated_archive_prefix( \get_taxonomy( $query['taxonomy'] ) )
418
  '<span class=hidden id=%s %s></span>',
419
  sprintf( 'tsfLeTitleData[%s]', (int) $term_id ),
420
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
421
+ HTML::make_data_attributes( [ 'leTitle' => $title_data ] )
422
  );
423
  $container .= sprintf(
424
  '<span class=hidden id=%s %s></span>',
425
  sprintf( 'tsfLeDescriptionData[%s]', (int) $term_id ),
426
  // phpcs:ignore, WordPress.Security.EscapeOutput -- make_data_attributes escapes.
427
+ HTML::make_data_attributes( [ 'leDescription' => $desc_data ] )
428
  );
429
 
430
  if ( $this->doing_ajax )
inc/classes/bridges/listtable.class.php CHANGED
@@ -7,7 +7,7 @@ namespace The_SEO_Framework\Bridges;
7
 
8
  /**
9
  * The SEO Framework plugin
10
- * Copyright (C) 2019 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
11
  *
12
  * This program is free software: you can redistribute it and/or modify
13
  * it under the terms of the GNU General Public License version 3 as published
7
 
8
  /**
9
  * The SEO Framework plugin
10
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
11
  *
12
  * This program is free software: you can redistribute it and/or modify
13
  * it under the terms of the GNU General Public License version 3 as published
inc/classes/bridges/ping.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -45,7 +45,7 @@ final class Ping {
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.
@@ -160,7 +160,7 @@ final class Ping {
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
 
@@ -184,7 +184,7 @@ final class Ping {
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
 
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
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 subsequentially when successful.
49
  * @see static::engage_pinging_retry_cron()
50
  *
51
  * @return bool True on success, false on failure.
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://developers.google.com/search/docs/advanced/crawling/ask-google-to-recrawl
164
  */
165
  public static function ping_google() {
166
 
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/webmasters/help/Sitemaps-3b5cf6ed
188
  */
189
  public static function ping_bing() {
190
 
inc/classes/bridges/plugintable.class.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Bridges\PluginTable
4
+ */
5
+
6
+ namespace The_SEO_Framework\Bridges;
7
+
8
+ /**
9
+ * The SEO Framework plugin
10
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
11
+ *
12
+ * This program is free software: you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License version 3 as published
14
+ * by the Free Software Foundation.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
+ */
24
+
25
+ \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
26
+
27
+ /**
28
+ * Prepares the List Edit view interface.
29
+ *
30
+ * @since 4.1.4
31
+ * @access protected
32
+ * @internal
33
+ * @final Can't be extended.
34
+ */
35
+ final class PluginTable {
36
+
37
+ /**
38
+ * Adds various links to the plugin row on the plugin's screen.
39
+ *
40
+ * @since 3.1.0
41
+ * @since 4.1.4 Moved to PluginTable.
42
+ * @access private
43
+ *
44
+ * @param array $links The current links.
45
+ * @return array The plugin links.
46
+ */
47
+ public static function _add_plugin_action_links( $links = [] ) {
48
+
49
+ $tsf_links = [];
50
+
51
+ $tsf = \the_seo_framework();
52
+
53
+ if ( $tsf->load_options ) {
54
+ $tsf_links['settings'] = sprintf(
55
+ '<a href="%s">%s</a>',
56
+ \esc_url( \admin_url( 'admin.php?page=' . $tsf->seo_settings_page_slug ) ),
57
+ \esc_html__( 'Settings', 'autodescription' )
58
+ );
59
+ }
60
+
61
+ $tsf_links['tsfem'] = sprintf(
62
+ '<a href="%s" rel="noreferrer noopener" target="_blank">%s</a>',
63
+ 'https://theseoframework.com/extensions/',
64
+ \esc_html_x( 'Extensions', 'Plugin extensions', 'autodescription' )
65
+ );
66
+ $tsf_links['pricing'] = sprintf(
67
+ '<a href="%s" rel="noreferrer noopener" target="_blank">%s</a>',
68
+ 'https://theseoframework.com/pricing/',
69
+ \esc_html_x( 'Pricing', 'Plugin pricing', 'autodescription' )
70
+ );
71
+
72
+ return array_merge( $tsf_links, $links );
73
+ }
74
+
75
+ /**
76
+ * Adds more row meta on the plugin screen.
77
+ *
78
+ * @since 3.2.4
79
+ * @since 4.1.4 Moved to PluginTable.
80
+ * @access private
81
+ *
82
+ * @param string[] $plugin_meta An array of the plugin's metadata,
83
+ * including the version, author,
84
+ * author URI, and plugin URI.
85
+ * @param string $plugin_file Path to the plugin file relative to the plugins directory.
86
+ * @return array $plugin_meta
87
+ */
88
+ public static function _add_plugin_row_meta( $plugin_meta, $plugin_file ) {
89
+
90
+ if ( THE_SEO_FRAMEWORK_PLUGIN_BASENAME !== $plugin_file )
91
+ return $plugin_meta;
92
+
93
+ $plugins = \get_plugins();
94
+ $_get_em = empty( $plugins['the-seo-framework-extension-manager/the-seo-framework-extension-manager.php'] );
95
+
96
+ return array_merge(
97
+ $plugin_meta,
98
+ [
99
+ 'support' => vsprintf(
100
+ '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
101
+ [
102
+ 'https://tsf.fyi/support',
103
+ \esc_html__( 'Get support', 'autodescription' ),
104
+ ]
105
+ ),
106
+ 'docs' => vsprintf(
107
+ '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
108
+ [
109
+ 'https://tsf.fyi/docs',
110
+ \esc_html__( 'View documentation', 'autodescription' ),
111
+ ]
112
+ ),
113
+ 'API' => vsprintf(
114
+ '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
115
+ [
116
+ 'https://tsf.fyi/docs/api',
117
+ \esc_html__( 'View API docs', 'autodescription' ),
118
+ ]
119
+ ),
120
+ 'EM' => vsprintf(
121
+ '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
122
+ [
123
+ 'https://tsf.fyi/extension-manager',
124
+ $_get_em ? \esc_html_x( 'Get Extension Manager', 'Extension Manager is a product name; do not translate it.', 'autodescription' ) : 'Extension Manager',
125
+ ]
126
+ ),
127
+ ]
128
+ );
129
+ }
130
+ }
inc/classes/bridges/postsettings.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -71,7 +71,7 @@ final class PostSettings {
71
  $title = sprintf(
72
  $schema,
73
  \esc_html__( 'Homepage SEO Settings', 'autodescription' ),
74
- $tsf->make_info(
75
  \__( 'The SEO Settings may take precedence over these settings.', 'autodescription' ),
76
  $tsf->seo_settings_page_url(),
77
  false
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
71
  $title = sprintf(
72
  $schema,
73
  \esc_html__( 'Homepage SEO Settings', 'autodescription' ),
74
+ \The_SEO_Framework\Interpreters\HTML::make_info(
75
  \__( 'The SEO Settings may take precedence over these settings.', 'autodescription' ),
76
  $tsf->seo_settings_page_url(),
77
  false
inc/classes/bridges/scripts.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -184,7 +184,7 @@ final class Scripts {
184
  * @return mixed
185
  */
186
  public static function decode_entities( $value ) {
187
- return $value && \is_string( $value ) ? html_entity_decode( $value, ENT_QUOTES | ENT_COMPAT, 'UTF-8' ) : $value;
188
  }
189
 
190
  /**
@@ -970,7 +970,7 @@ final class Scripts {
970
  'name' => 'tsfCL10n',
971
  'data' => [
972
  'guidelines' => $tsf->get_input_guidelines(),
973
- 'counterType' => \absint( $tsf->get_user_option( 0, 'counter_type', 3 ) ),
974
  'i18n' => [
975
  'guidelines' => $tsf->get_input_guidelines_i18n(),
976
  /* translators: Pixel counter. 1: number (value), 2: number (guideline) */
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
184
  * @return mixed
185
  */
186
  public static function decode_entities( $value ) {
187
+ return $value && \is_string( $value ) ? html_entity_decode( $value, ENT_QUOTES, 'UTF-8' ) : $value;
188
  }
189
 
190
  /**
970
  'name' => 'tsfCL10n',
971
  'data' => [
972
  'guidelines' => $tsf->get_input_guidelines(),
973
+ 'counterType' => \absint( $tsf->get_user_meta_item( 'counter_type' ) ),
974
  'i18n' => [
975
  'guidelines' => $tsf->get_input_guidelines_i18n(),
976
  /* translators: Pixel counter. 1: number (value), 2: number (guideline) */
inc/classes/bridges/seobar.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/bridges/seosettings.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/bridges/sitemap.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -153,6 +153,9 @@ final class Sitemap {
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'.
@@ -164,32 +167,15 @@ final class Sitemap {
164
 
165
  if ( ! isset( $list[ $id ] ) ) return false;
166
 
167
- global $wp_rewrite;
168
-
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
 
@@ -465,7 +451,7 @@ final class Sitemap {
465
 
466
  /**
467
  * @since 2.8.0
468
- * @param array $schemas The schema list. URLs are expected to be escaped.
469
  */
470
  $schemas = (array) \apply_filters( 'the_seo_framework_sitemap_schemas', $schemas );
471
 
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
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
+ * @since 4.1.4 Now assimilates the output using the base path, so that filter
157
+ * `the_seo_framework_sitemap_base_path` also works. Glues the
158
+ * pieces together using the `get_home_host` value.
159
  * @global \WP_Rewrite $wp_rewrite
160
  *
161
  * @param string $id The base ID. Default 'base'.
167
 
168
  if ( ! isset( $list[ $id ] ) ) return false;
169
 
170
+ $host = static::$tsf->set_preferred_url_scheme( static::$tsf->get_home_host() );
171
+ $path_info = $this->get_sitemap_base_path_info();
 
 
 
 
 
 
 
 
 
 
172
 
173
+ if ( $path_info['use_query_var'] ) {
174
+ $url = "$host{$path_info['path']}$id";
 
 
175
  } else {
176
+ $url = "$host{$path_info['path']}{$list[ $id ]['endpoint']}";
177
  }
178
 
 
 
 
 
 
179
  return \esc_url_raw( $url );
180
  }
181
 
451
 
452
  /**
453
  * @since 2.8.0
454
+ * @param array $schemas The schema list. URLs and indexes are expected to be escaped.
455
  */
456
  $schemas = (array) \apply_filters( 'the_seo_framework_sitemap_schemas', $schemas );
457
 
inc/classes/bridges/termsettings.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Bridges;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/bridges/usersettings.class.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Bridges\UserSettings
4
+ * @subpackage The_SEO_Framework\Admin\Edit\User
5
+ */
6
+
7
+ namespace The_SEO_Framework\Bridges;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (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
+ * Prepares the User Settings view interface.
30
+ *
31
+ * @since 4.1.4
32
+ * @access protected
33
+ * @internal
34
+ * @final Can't be extended.
35
+ */
36
+ final class UserSettings {
37
+
38
+ /**
39
+ * Prepares the user setting fields.
40
+ *
41
+ * @since 4.1.4
42
+ * @access private
43
+ *
44
+ * @param \WP_User $user WP_User object.
45
+ */
46
+ public static function _prepare_setting_fields( \WP_User $user ) {
47
+
48
+ if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
49
+
50
+ static::add_user_author_fields( $user );
51
+ }
52
+
53
+ /**
54
+ * Outputs user profile fields.
55
+ *
56
+ * @since 4.1.4
57
+ *
58
+ * @param \WP_User $user WP_User object.
59
+ */
60
+ private static function add_user_author_fields( \WP_User $user ) {
61
+ /**
62
+ * @since 4.1.4
63
+ */
64
+ \do_action( 'the_seo_framework_before_author_fields' );
65
+ \the_seo_framework()->get_view( 'profile/author', get_defined_vars() );
66
+ /**
67
+ * @since 4.1.4
68
+ */
69
+ \do_action( 'the_seo_framework_after_author_fields' );
70
+ }
71
+ }
inc/classes/builders/coresitemaps/main.class.php CHANGED
@@ -8,7 +8,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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2020 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/builders/coresitemaps/posts.class.php CHANGED
@@ -8,7 +8,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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2020 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/builders/coresitemaps/taxonomies.class.php CHANGED
@@ -8,7 +8,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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2020 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/classes/builders/images.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -32,6 +32,13 @@ namespace The_SEO_Framework\Builders;
32
  */
33
  final class Images {
34
 
 
 
 
 
 
 
 
35
  /**
36
  * The constructor. Or rather, the lack thereof.
37
  *
@@ -149,7 +156,7 @@ final class Images {
149
  'strip' => false,
150
  ]
151
  );
152
- // TODO can we somehow limit this search to 5?
153
  preg_match_all(
154
  '/<img[^>]+src=(\"|\')?([^\"\'>\s]+)\1?.*?>/mi',
155
  $content,
@@ -159,22 +166,20 @@ final class Images {
159
  }
160
 
161
  if ( $matches ) {
162
- $i = 0;
163
- foreach ( $matches as $match ) {
 
 
164
  // Assume every URL to be correct? Yes. WordPress assumes that too.
165
- $the_match = $match[2] ?: '';
166
 
167
  // false-esque matches, like '0', are so uncommon it's not worth dealing with them.
168
- if ( ! $the_match )
169
- continue;
170
 
171
  yield [
172
- 'url' => $the_match,
173
  'id' => 0,
174
  ];
175
-
176
- // Get no more than 5 images.
177
- if ( ++$i > 4 ) break;
178
  }
179
  } else {
180
  yield [
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
32
  */
33
  final class Images {
34
 
35
+ /**
36
+ * @since 4.1.4
37
+ * @internal
38
+ * @var int MAX_CONTENT_IMAGES The maximum number of images to get from the content.
39
+ */
40
+ const MAX_CONTENT_IMAGES = 5;
41
+
42
  /**
43
  * The constructor. Or rather, the lack thereof.
44
  *
156
  'strip' => false,
157
  ]
158
  );
159
+ // TODO can we somehow limit this search to static::MAX_CONTENT_IMAGES? -> We could, via preg_match(), but the opcodes won't help.
160
  preg_match_all(
161
  '/<img[^>]+src=(\"|\')?([^\"\'>\s]+)\1?.*?>/mi',
162
  $content,
166
  }
167
 
168
  if ( $matches ) {
169
+ for ( $i = 0; $i++ < static::MAX_CONTENT_IMAGES; ) {
170
+ // Fewer than MAX_CONTENT_IMAGES matched.
171
+ if ( ! isset( $matches[ $i ][2] ) ) break;
172
+
173
  // Assume every URL to be correct? Yes. WordPress assumes that too.
174
+ $url = $matches[ $i ][2] ?: '';
175
 
176
  // false-esque matches, like '0', are so uncommon it's not worth dealing with them.
177
+ if ( ! $url ) continue;
 
178
 
179
  yield [
180
+ 'url' => $url,
181
  'id' => 0,
182
  ];
 
 
 
183
  }
184
  } else {
185
  yield [
inc/classes/builders/scripts.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2018 - 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
@@ -501,7 +501,7 @@ final class Scripts {
501
  if (
502
  ! isset( $_colors[ $_scheme ]->colors ) // phpcs:ignore, WordPress.WhiteSpace
503
  || ! \is_array( $_colors[ $_scheme ]->colors )
504
- || \count( $_colors[ $_scheme ]->colors ) < 4
505
  ) {
506
  $_colors = [
507
  '#222',
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
501
  if (
502
  ! isset( $_colors[ $_scheme ]->colors ) // phpcs:ignore, WordPress.WhiteSpace
503
  || ! \is_array( $_colors[ $_scheme ]->colors )
504
+ || \count( $_colors[ $_scheme ]->colors ) < 4 // unexpected scheme, ignore and override.
505
  ) {
506
  $_colors = [
507
  '#222',
inc/classes/builders/seobar-page.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -102,7 +102,7 @@ final class SeoBar_Page extends SeoBar {
102
  'nofollow' => false,
103
  'noarchive' => false,
104
  ],
105
- static::$tsf->robots_meta( [
106
  'id' => static::$query['id'],
107
  'taxonomy' => '',
108
  ] )
@@ -507,6 +507,7 @@ final class SeoBar_Page extends SeoBar {
507
  $max = max( $duplicated_words );
508
  $max = reset( $max );
509
 
 
510
  if ( $max > 3 || \count( $duplicated_words ) > 1 ) {
511
  // This must be resolved.
512
  $item['reason'] = $cache['reason']['foundmanydupe'];
@@ -619,8 +620,6 @@ final class SeoBar_Page extends SeoBar {
619
  ]
620
  );
621
 
622
- $robots_global = static::get_cache( 'general/detect/robotsglobal' );
623
-
624
  if ( $this->query_cache['states']['isdraft'] ) {
625
  $item = $cache['defaults']['draft'];
626
  // TODO Really stop asserting from here?
@@ -631,6 +630,8 @@ final class SeoBar_Page extends SeoBar {
631
  $item = $cache['defaults']['index'];
632
  }
633
 
 
 
634
  if ( ! $robots_global['blogpublic'] ) {
635
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
636
  $item['reason'] = $cache['reason']['notpublic'];
@@ -773,8 +774,6 @@ final class SeoBar_Page extends SeoBar {
773
  ]
774
  );
775
 
776
- $robots_global = static::get_cache( 'general/detect/robotsglobal' );
777
-
778
  if ( $this->query_cache['states']['isdraft'] ) {
779
  $item = $cache['defaults']['draft'];
780
  // TODO Really stop asserting from here?
@@ -785,6 +784,8 @@ final class SeoBar_Page extends SeoBar {
785
  $item = $cache['defaults']['follow'];
786
  }
787
 
 
 
788
  if ( ! $robots_global['blogpublic'] ) {
789
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
790
  $item['reason'] = $cache['reason']['notpublic'];
@@ -904,8 +905,6 @@ final class SeoBar_Page extends SeoBar {
904
  ]
905
  );
906
 
907
- $robots_global = static::get_cache( 'general/detect/robotsglobal' );
908
-
909
  if ( $this->query_cache['states']['isdraft'] ) {
910
  $item = $cache['defaults']['draft'];
911
  // TODO Really stop asserting from here?
@@ -916,6 +915,8 @@ final class SeoBar_Page extends SeoBar {
916
  $item = $cache['defaults']['archive'];
917
  }
918
 
 
 
919
  if ( ! $robots_global['blogpublic'] ) {
920
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
921
  $item['reason'] = $cache['reason']['notpublic'];
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
102
  'nofollow' => false,
103
  'noarchive' => false,
104
  ],
105
+ static::$tsf->generate_robots_meta( [
106
  'id' => static::$query['id'],
107
  'taxonomy' => '',
108
  ] )
507
  $max = max( $duplicated_words );
508
  $max = reset( $max );
509
 
510
+ // Warn when more than 3x triplet+/quintet+ words are found.
511
  if ( $max > 3 || \count( $duplicated_words ) > 1 ) {
512
  // This must be resolved.
513
  $item['reason'] = $cache['reason']['foundmanydupe'];
620
  ]
621
  );
622
 
 
 
623
  if ( $this->query_cache['states']['isdraft'] ) {
624
  $item = $cache['defaults']['draft'];
625
  // TODO Really stop asserting from here?
630
  $item = $cache['defaults']['index'];
631
  }
632
 
633
+ $robots_global = static::get_cache( 'general/detect/robotsglobal' );
634
+
635
  if ( ! $robots_global['blogpublic'] ) {
636
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
637
  $item['reason'] = $cache['reason']['notpublic'];
774
  ]
775
  );
776
 
 
 
777
  if ( $this->query_cache['states']['isdraft'] ) {
778
  $item = $cache['defaults']['draft'];
779
  // TODO Really stop asserting from here?
784
  $item = $cache['defaults']['follow'];
785
  }
786
 
787
+ $robots_global = static::get_cache( 'general/detect/robotsglobal' );
788
+
789
  if ( ! $robots_global['blogpublic'] ) {
790
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
791
  $item['reason'] = $cache['reason']['notpublic'];
905
  ]
906
  );
907
 
 
 
908
  if ( $this->query_cache['states']['isdraft'] ) {
909
  $item = $cache['defaults']['draft'];
910
  // TODO Really stop asserting from here?
915
  $item = $cache['defaults']['archive'];
916
  }
917
 
918
+ $robots_global = static::get_cache( 'general/detect/robotsglobal' );
919
+
920
  if ( ! $robots_global['blogpublic'] ) {
921
  $item['status'] = \The_SEO_Framework\Interpreters\SeoBar::STATE_BAD;
922
  $item['reason'] = $cache['reason']['notpublic'];
inc/classes/builders/seobar-term.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -109,7 +109,7 @@ final class SeoBar_Term extends SeoBar {
109
  'nofollow' => false,
110
  'noarchive' => false,
111
  ],
112
- static::$tsf->robots_meta( [
113
  'id' => static::$query['id'],
114
  'taxonomy' => static::$query['taxonomy'],
115
  ] )
@@ -467,6 +467,7 @@ final class SeoBar_Term extends SeoBar {
467
  $max = max( $duplicated_words );
468
  $max = reset( $max );
469
 
 
470
  if ( $max > 3 || \count( $duplicated_words ) > 1 ) {
471
  // This must be resolved.
472
  $item['reason'] = $cache['reason']['foundmanydupe'];
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
109
  'nofollow' => false,
110
  'noarchive' => false,
111
  ],
112
+ static::$tsf->generate_robots_meta( [
113
  'id' => static::$query['id'],
114
  'taxonomy' => static::$query['taxonomy'],
115
  ] )
467
  $max = max( $duplicated_words );
468
  $max = reset( $max );
469
 
470
+ // Warn when more than 3x triplet+/quintet+ words are found.
471
  if ( $max > 3 || \count( $duplicated_words ) > 1 ) {
472
  // This must be resolved.
473
  $item['reason'] = $cache['reason']['foundmanydupe'];
inc/classes/builders/seobar.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -165,6 +165,7 @@ abstract class SeoBar {
165
  * Runs one or more SEO bar tests.
166
  *
167
  * @since 4.0.0
 
168
  * @access private
169
  * @generator
170
  *
@@ -192,10 +193,28 @@ abstract class SeoBar {
192
 
193
  foreach ( $tests as $test )
194
  yield $test => $this->{"test_$test"}();
 
195
 
 
 
 
 
 
 
196
  $this->query_cache = [];
197
  }
198
 
 
 
 
 
 
 
 
 
 
 
 
199
  /**
200
  * Primes the cache.
201
  *
@@ -206,6 +225,7 @@ abstract class SeoBar {
206
 
207
  /**
208
  * Primes the current query cache.
 
209
  *
210
  * @since 4.0.0
211
  * @abstract
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
165
  * Runs one or more SEO bar tests.
166
  *
167
  * @since 4.0.0
168
+ * @since 4.1.4 No longer clears the query cache.
169
  * @access private
170
  * @generator
171
  *
193
 
194
  foreach ( $tests as $test )
195
  yield $test => $this->{"test_$test"}();
196
+ }
197
 
198
+ /**
199
+ * Clears the query cache. Saving a few bytes of memory, prepping for the next yield.
200
+ *
201
+ * @since 4.1.4
202
+ */
203
+ final public function clear_query_cache() {
204
  $this->query_cache = [];
205
  }
206
 
207
+ /**
208
+ * Presents an unalterable form of query cache.
209
+ *
210
+ * @since 4.1.4
211
+ *
212
+ * @return array The query cache, unknown values.
213
+ */
214
+ final public function get_query_cache() {
215
+ return $this->query_cache;
216
+ }
217
+
218
  /**
219
  * Primes the cache.
220
  *
225
 
226
  /**
227
  * Primes the current query cache.
228
+ * It's best to overwrite the cache whenever you generate a new SEO Bar.
229
  *
230
  * @since 4.0.0
231
  * @abstract
inc/classes/builders/sitemap-base.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -224,27 +224,28 @@ class Sitemap_Base extends Sitemap {
224
  $_args = (array) \apply_filters(
225
  'the_seo_framework_sitemap_hpt_query_args',
226
  [
227
- 'posts_per_page' => $_hierarchical_posts_limit + \count( $_exclude_ids ),
228
- 'post_type' => $hierarchical_post_types,
229
- 'orderby' => 'date',
230
- 'order' => 'ASC',
231
- 'post_status' => 'publish',
232
- 'has_password' => false,
233
- 'fields' => 'ids',
234
- 'cache_results' => false,
235
- 'suppress_filters' => false,
236
- 'no_found_rows' => true,
237
  ]
238
  );
239
 
240
- $wp_query->query = $wp_query->query_vars = $_args;
 
241
 
242
- $hierarchical_post_ids = array_diff( $wp_query->get_posts(), $_exclude_ids );
243
 
244
- // Stop confusion: trim query to set value (by one or two, depending on whether the homepage and blog are included).
245
- // This is ultimately redundant, but it'll stop support requests by making the input value more accurate.
246
- if ( \count( $hierarchical_post_ids ) > $_hierarchical_posts_limit ) {
247
- array_splice( $hierarchical_post_ids, $_hierarchical_posts_limit );
 
248
  }
249
  }
250
 
@@ -256,22 +257,23 @@ class Sitemap_Base extends Sitemap {
256
  $_args = (array) \apply_filters(
257
  'the_seo_framework_sitemap_nhpt_query_args',
258
  [
259
- 'posts_per_page' => $this->get_sitemap_post_limit( false ),
260
- 'post_type' => $non_hierarchical_post_types,
261
- 'orderby' => 'lastmod',
262
- 'order' => 'DESC',
263
- 'post_status' => 'publish',
264
- 'has_password' => false,
265
- 'fields' => 'ids',
266
- 'cache_results' => false,
267
- 'suppress_filters' => false,
268
- 'no_found_rows' => true,
269
  ]
270
  );
271
 
272
- $wp_query->query = $wp_query->query_vars = $_args;
 
273
 
274
- $non_hierarchical_post_ids = $wp_query->get_posts();
 
275
  }
276
 
277
  // Destroy query instance.
@@ -514,9 +516,8 @@ class Sitemap_Base extends Sitemap {
514
  ]
515
  );
516
 
517
- if ( $args['show_modified'] ) {
518
  $_values['lastmod'] = isset( $post->post_modified_gmt ) ? $post->post_modified_gmt : '0000-00-00 00:00:00';
519
- }
520
 
521
  if ( $args['show_priority'] ) {
522
  // Add at least 1 to prevent going negative. We added 8 extra (= 9) to smoothen the slope.
@@ -621,13 +622,11 @@ class Sitemap_Base extends Sitemap {
621
  $_values = [];
622
  $_values['loc'] = $url;
623
 
624
- if ( $args['show_modified'] ) {
625
  $_values['lastmod'] = ! empty( $values['lastmod'] ) ? $values['lastmod'] : '0000-00-00 00:00:00';
626
- }
627
 
628
- if ( $args['show_priority'] ) {
629
  $_values['priority'] = ! empty( $values['priority'] ) ? $values['priority'] : 0.9;
630
- }
631
 
632
  ++$count;
633
  yield $_values;
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
224
  $_args = (array) \apply_filters(
225
  'the_seo_framework_sitemap_hpt_query_args',
226
  [
227
+ 'posts_per_page' => $_hierarchical_posts_limit + \count( $_exclude_ids ),
228
+ 'post_type' => $hierarchical_post_types,
229
+ 'orderby' => 'date',
230
+ 'order' => 'ASC',
231
+ 'post_status' => 'publish',
232
+ 'has_password' => false,
233
+ 'fields' => 'ids',
234
+ 'cache_results' => false,
235
+ 'no_found_rows' => true,
 
236
  ]
237
  );
238
 
239
+ if ( $_args['post_type'] ) {
240
+ $wp_query->query = $wp_query->query_vars = $_args;
241
 
242
+ $hierarchical_post_ids = array_diff( $wp_query->get_posts(), $_exclude_ids );
243
 
244
+ // Stop confusion: trim query to set value (by one or two, depending on whether the homepage and blog are included).
245
+ // This is ultimately redundant, but it'll stop support requests by making the input value more accurate.
246
+ if ( \count( $hierarchical_post_ids ) > $_hierarchical_posts_limit ) {
247
+ array_splice( $hierarchical_post_ids, $_hierarchical_posts_limit );
248
+ }
249
  }
250
  }
251
 
257
  $_args = (array) \apply_filters(
258
  'the_seo_framework_sitemap_nhpt_query_args',
259
  [
260
+ 'posts_per_page' => $this->get_sitemap_post_limit( false ),
261
+ 'post_type' => $non_hierarchical_post_types,
262
+ 'orderby' => 'lastmod',
263
+ 'order' => 'DESC',
264
+ 'post_status' => 'publish',
265
+ 'has_password' => false,
266
+ 'fields' => 'ids',
267
+ 'cache_results' => false,
268
+ 'no_found_rows' => true,
 
269
  ]
270
  );
271
 
272
+ if ( $_args['post_type'] ) {
273
+ $wp_query->query = $wp_query->query_vars = $_args;
274
 
275
+ $non_hierarchical_post_ids = $wp_query->get_posts();
276
+ }
277
  }
278
 
279
  // Destroy query instance.
516
  ]
517
  );
518
 
519
+ if ( $args['show_modified'] )
520
  $_values['lastmod'] = isset( $post->post_modified_gmt ) ? $post->post_modified_gmt : '0000-00-00 00:00:00';
 
521
 
522
  if ( $args['show_priority'] ) {
523
  // Add at least 1 to prevent going negative. We added 8 extra (= 9) to smoothen the slope.
622
  $_values = [];
623
  $_values['loc'] = $url;
624
 
625
+ if ( $args['show_modified'] )
626
  $_values['lastmod'] = ! empty( $values['lastmod'] ) ? $values['lastmod'] : '0000-00-00 00:00:00';
 
627
 
628
+ if ( $args['show_priority'] )
629
  $_values['priority'] = ! empty( $values['priority'] ) ? $values['priority'] : 0.9;
 
630
 
631
  ++$count;
632
  yield $_values;
inc/classes/builders/sitemap.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Builders;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -114,6 +114,7 @@ abstract class Sitemap {
114
 
115
  /**
116
  * Creates XML entry from array input.
 
117
  *
118
  * Note: Not final, other classes may overwrite this.
119
  *
@@ -151,10 +152,11 @@ abstract class Sitemap {
151
  * @since 3.1.0 1. Resolved a PHP notice when ID is 0, resulting in returning false-esque unintentionally.
152
  * 2. Now accepts 0 in the filter.
153
  * @since 4.0.0 1. Now tests qubit options.
154
- * 2. Now tests for redirect settings.
155
  * 3. First parameter can now be a post object.
156
  * 4. If the first parameter is 0, it's now indicative of a home-as-blog page.
157
  * 5. Moved to \The_SEO_Framework\Builders\Sitemap
 
158
  *
159
  * @param int $post_id The Post ID to check.
160
  * @return bool True if included, false otherwise.
@@ -165,28 +167,39 @@ abstract class Sitemap {
165
  if ( null === $excluded ) {
166
  /**
167
  * @since 2.5.2
168
- * @since 2.8.0 : No longer accepts '0' as entry.
169
- * @since 3.1.0 : '0' is accepted again.
170
- * @param array $excluded Sequential list of excluded IDs: [ int ...post_id ]
171
  */
172
  $excluded = (array) \apply_filters( 'the_seo_framework_sitemap_exclude_ids', [] );
173
 
174
  if ( empty( $excluded ) ) {
175
  $excluded = [];
176
  } else {
 
177
  $excluded = array_flip( $excluded );
178
  }
179
  }
180
 
181
- // ROBOTS_IGNORE_PROTECTION as we don't need to test 'private' (because of sole 'publish'), and 'password' (because of false 'has_password')
182
- return ! isset( $excluded[ $post_id ] )
183
- && ! static::$tsf->is_robots_meta_noindex_set_by_args(
184
- [
185
- 'id' => $post_id,
186
- 'taxonomy' => '',
187
- ],
188
- \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
189
- );
 
 
 
 
 
 
 
 
 
 
190
  }
191
 
192
  /**
@@ -196,6 +209,7 @@ abstract class Sitemap {
196
  * The URL also isn't checked, nor the position.
197
  *
198
  * @since 4.0.0
 
199
  * @see https://github.com/sybrew/tsf-term-sitemap for example.
200
  *
201
  * @param int $term_id The Term ID to check.
@@ -208,26 +222,39 @@ abstract class Sitemap {
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', [] );
214
 
215
  if ( empty( $excluded ) ) {
216
  $excluded = [];
217
  } else {
 
218
  $excluded = array_flip( $excluded );
219
  }
220
  }
221
 
222
- // ROBOTS_IGNORE_PROTECTION is not tested for terms. However, we may use that later.
223
- return ! isset( $excluded[ $term_id ] )
224
- && ! static::$tsf->is_robots_meta_noindex_set_by_args(
225
- [
226
- 'id' => $term_id,
227
- 'taxonomy' => $taxonomy,
228
- ],
229
- \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
230
- );
 
 
 
 
 
 
 
 
 
 
 
 
231
  }
232
 
233
  /**
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
114
 
115
  /**
116
  * Creates XML entry from array input.
117
+ * Input is expected to be escaped and XML-safe.
118
  *
119
  * Note: Not final, other classes may overwrite this.
120
  *
152
  * @since 3.1.0 1. Resolved a PHP notice when ID is 0, resulting in returning false-esque unintentionally.
153
  * 2. Now accepts 0 in the filter.
154
  * @since 4.0.0 1. Now tests qubit options.
155
+ * 2. FALSE: Now tests for redirect settings. <- it never did! We did document this though...
156
  * 3. First parameter can now be a post object.
157
  * 4. If the first parameter is 0, it's now indicative of a home-as-blog page.
158
  * 5. Moved to \The_SEO_Framework\Builders\Sitemap
159
+ * @since 4.1.4 TRUE: Now tests for redirect settings.
160
  *
161
  * @param int $post_id The Post ID to check.
162
  * @return bool True if included, false otherwise.
167
  if ( null === $excluded ) {
168
  /**
169
  * @since 2.5.2
170
+ * @since 2.8.0 No longer accepts '0' as entry.
171
+ * @since 3.1.0 '0' is accepted again.
172
+ * @param int[] $excluded Sequential list of excluded IDs: [ int ...post_id ]
173
  */
174
  $excluded = (array) \apply_filters( 'the_seo_framework_sitemap_exclude_ids', [] );
175
 
176
  if ( empty( $excluded ) ) {
177
  $excluded = [];
178
  } else {
179
+ // isset() is faster than in_array(). So, we flip it.
180
  $excluded = array_flip( $excluded );
181
  }
182
  }
183
 
184
+ $included = ! isset( $excluded[ $post_id ] );
185
+
186
+ while ( $included ) :
187
+ $_args = [
188
+ 'id' => $post_id,
189
+ 'taxonomy' => '',
190
+ ];
191
+
192
+ // ROBOTS_IGNORE_PROTECTION as we don't need to test 'private' (because of sole 'publish'), and 'password' (because of false 'has_password')
193
+ $meta = static::$tsf->generate_robots_meta( $_args, null, \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION );
194
+ $included = ! ( isset( $meta['noindex'] ) && 'noindex' === $meta['noindex'] );
195
+
196
+ if ( ! $included ) break;
197
+
198
+ $included = ! static::$tsf->get_redirect_url( $_args );
199
+ break;
200
+ endwhile;
201
+
202
+ return $included;
203
  }
204
 
205
  /**
209
  * The URL also isn't checked, nor the position.
210
  *
211
  * @since 4.0.0
212
+ * @since 4.1.4 Now tests for redirect settings.
213
  * @see https://github.com/sybrew/tsf-term-sitemap for example.
214
  *
215
  * @param int $term_id The Term ID to check.
222
  if ( null === $excluded ) {
223
  /**
224
  * @since 4.0.0
225
+ * @param int[] $excluded Sequential list of excluded IDs: [ int ...term_id ]
226
  */
227
  $excluded = (array) \apply_filters( 'the_seo_framework_sitemap_exclude_term_ids', [] );
228
 
229
  if ( empty( $excluded ) ) {
230
  $excluded = [];
231
  } else {
232
+ // isset() is faster than in_array(). So, we flip it.
233
  $excluded = array_flip( $excluded );
234
  }
235
  }
236
 
237
+ $included = ! isset( $excluded[ $term_id ] );
238
+
239
+ // Yes, 90% of this code code isn't DRY. However, terms != posts. terms == posts, though :).
240
+ // Really: <https://core.trac.wordpress.org/ticket/50568>
241
+ while ( $included ) :
242
+ $_args = [
243
+ 'id' => $term_id,
244
+ 'taxonomy' => $taxonomy,
245
+ ];
246
+
247
+ // ROBOTS_IGNORE_PROTECTION is not tested for terms. However, we may use that later.
248
+ $meta = static::$tsf->generate_robots_meta( $_args, null, \The_SEO_Framework\ROBOTS_IGNORE_PROTECTION );
249
+ $included = ! ( isset( $meta['noindex'] ) && 'noindex' === $meta['noindex'] );
250
+
251
+ if ( ! $included ) break;
252
+
253
+ $included = ! static::$tsf->get_redirect_url( $_args );
254
+ break;
255
+ endwhile;
256
+
257
+ return $included;
258
  }
259
 
260
  /**
inc/classes/cache.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -45,9 +45,6 @@ class Cache extends Site_Options {
45
 
46
  $this->init_post_cache_actions();
47
 
48
- // Deletes author transient.
49
- \add_action( 'profile_update', [ $this, 'delete_author_cache' ] );
50
-
51
  // Delete Sitemap transient on permalink structure change.
52
  \add_action( 'load-options-permalink.php', [ $this, 'delete_sitemap_transient_permalink_updated' ], 20 );
53
 
@@ -99,13 +96,11 @@ class Cache extends Site_Options {
99
  * @since 2.8.0
100
  * @since 3.1.0 Added excluded post ids flush.
101
  * @since 3.1.4 Now flushes object cache.
 
102
  */
103
  public function delete_main_cache() {
104
- $this->delete_cache( 'front' );
105
  $this->delete_cache( 'sitemap' );
106
- $this->delete_cache( 'robots' );
107
  $this->delete_cache( 'excluded_post_ids' );
108
- $this->delete_cache( 'object' );
109
  }
110
 
111
  /**
@@ -115,6 +110,7 @@ class Cache extends Site_Options {
115
  * @since 3.0.0 Process is halted when no valid $post_id is supplied.
116
  * @since 4.1.3 Now flushes the sitemap cache (and instigates pinging thereof)
117
  * even when TSF sitemaps are disabled.
 
118
  *
119
  * @param int $post_id The Post ID that has been updated.
120
  * @return bool True on success, false on failure.
@@ -123,13 +119,13 @@ class Cache extends Site_Options {
123
 
124
  if ( ! $post_id ) return false;
125
 
126
- $success[] = $this->delete_cache( 'post', $post_id );
127
 
128
  // Don't flush sitemap on revision.
129
  if ( ! \wp_is_post_revision( $post_id ) )
130
- $success[] = $this->delete_cache( 'sitemap' );
131
 
132
- return ! \in_array( false, $success, true );
133
  }
134
 
135
  /**
@@ -157,38 +153,16 @@ class Cache extends Site_Options {
157
  return true;
158
  }
159
 
160
- /**
161
- * Deletes cache on profile save.
162
- *
163
- * @since 2.8.0
164
- *
165
- * @param int $user_id The User ID that has been updated.
166
- * @return bool True on success, false on failure.
167
- */
168
- public function delete_author_cache( $user_id ) {
169
- return $this->delete_cache( 'author', $user_id );
170
- }
171
-
172
- /**
173
- * Deletes object cache.
174
- *
175
- * @since 2.9.0
176
- * @since 4.0.0 Now does something.
177
- *
178
- * @return bool True on success, false on failure.
179
- */
180
- public function delete_object_cache() {
181
- return $this->delete_cache( 'object' );
182
- }
183
-
184
  /**
185
  * Handles all kinds of cache for removal.
186
  * Main cache deletion function handler.
187
  *
188
  * @since 2.8.0
189
  * @since 2.9.3 $type = 'front' now also returns true.
190
- * @since 3.1.0 Added action.
 
191
  * @since 4.0.5 Removed all JSON-LD transient clear calls.
 
192
  *
193
  * @param string $type The type
194
  * @param int $id The post, page or TT ID. Defaults to $this->get_the_real_ID().
@@ -202,64 +176,14 @@ class Cache extends Site_Options {
202
  $success = false;
203
 
204
  switch ( $type ) :
205
- case 'front':
206
- $front_id = $this->get_the_front_page_ID();
207
-
208
- $this->object_cache_delete( $this->get_meta_output_cache_key_by_type( $front_id, '', 'frontpage' ) );
209
- $success = true;
210
- break;
211
-
212
- case 'post':
213
- $post_type = \get_post_type( $id );
214
-
215
- if ( $post_type ) {
216
- switch ( $post_type ) {
217
- case 'page':
218
- case 'post':
219
- case 'attachment':
220
- break;
221
-
222
- default:
223
- // Generic key for CPT.
224
- $post_type = 'singular';
225
- break;
226
- }
227
-
228
- $this->object_cache_delete( $this->get_meta_output_cache_key_by_type( $id, '', $post_type ) );
229
- $success = true;
230
- }
231
- break;
232
-
233
- // Careful, this can only run on archive pages. For now.
234
- case 'term':
235
- $this->object_cache_delete( $this->get_meta_output_cache_key_by_type( $id, $args['term'], 'term' ) );
236
- $success = true;
237
- break;
238
-
239
- case 'author':
240
- $this->object_cache_delete( $this->get_meta_output_cache_key_by_type( $id, 'author', 'author' ) );
241
- $success = true;
242
- break;
243
-
244
  case 'sitemap':
245
  $success = $this->delete_sitemap_transient();
246
  break;
247
 
248
- case 'robots':
249
- $success = $this->object_cache_delete( $this->get_robots_txt_cache_key() );
250
- break;
251
-
252
  case 'excluded_post_ids':
253
  $success = $this->delete_excluded_post_ids_transient();
254
  break;
255
 
256
- case 'object':
257
- $success = \wp_cache_flush();
258
- break;
259
-
260
- case 'detection':
261
- break;
262
-
263
  default:
264
  break;
265
  endswitch;
@@ -353,62 +277,6 @@ class Cache extends Site_Options {
353
  return false;
354
  }
355
 
356
- /**
357
- * Object cache set wrapper.
358
- *
359
- * @since 2.4.3
360
- *
361
- * @param string $key The Object cache key.
362
- * @param mixed $data The Object cache data.
363
- * @param int $expire The Object cache expire time.
364
- * @param string $group The Object cache group.
365
- * @return bool true on set, false when disabled.
366
- */
367
- public function object_cache_set( $key, $data, $expire = 0, $group = 'the_seo_framework' ) {
368
-
369
- if ( $this->use_object_cache )
370
- return \wp_cache_set( $key, $data, $group, $expire );
371
-
372
- return false;
373
- }
374
-
375
- /**
376
- * Object cache get wrapper.
377
- *
378
- * @since 2.4.3
379
- *
380
- * @param string $key The Object cache key.
381
- * @param string $group The Object cache group.
382
- * @param bool $force Whether to force an update of the local cache.
383
- * @param bool $found Whether the key was found in the cache.
384
- * Disambiguates a return of false, a storable value. Passed by reference.
385
- * @return mixed wp_cache_get if object caching is allowed. False otherwise.
386
- */
387
- public function object_cache_get( $key, $group = 'the_seo_framework', $force = false, &$found = null ) {
388
-
389
- if ( $this->use_object_cache )
390
- return \wp_cache_get( $key, $group, $force, $found );
391
-
392
- return false;
393
- }
394
-
395
- /**
396
- * Object cache delete wrapper.
397
- *
398
- * @since 2.8.0
399
- *
400
- * @param string $key The Object cache key.
401
- * @param string $group The Object cache group.
402
- * @return mixed `wp_cache_delete()` if object caching is allowed. False otherwise.
403
- */
404
- public function object_cache_delete( $key, $group = 'the_seo_framework' ) {
405
-
406
- if ( $this->use_object_cache )
407
- return \wp_cache_delete( $key, $group );
408
-
409
- return false;
410
- }
411
-
412
  /**
413
  * Returns the post ID exclusion transient name.
414
  *
@@ -443,10 +311,10 @@ class Cache extends Site_Options {
443
  * @since 2.3.3
444
  * @since 2.6.0 Refactored.
445
  * @since 2.9.1 : 1. Added early singular type detection.
446
- * 2. Moved generation into $this->generate_cache_key_by_query().
447
- * @since 3.1.1 : The first parameter is now optional.
448
- * @see $this->generate_cache_key_by_query() to get cache key from the query.
449
- * @see $this->generate_cache_key_by_type() to get cache key outside of the query.
450
  *
451
  * @param int|string|bool $id The Taxonomy or Post ID.
452
  * @param string $taxonomy The taxonomy name.
@@ -458,157 +326,7 @@ class Cache extends Site_Options {
458
  if ( isset( $type ) )
459
  return $this->generate_cache_key_by_type( $id, $taxonomy, $type );
460
 
461
- return $this->generate_cache_key_by_query( $id, $taxonomy, $type );
462
- }
463
-
464
- /**
465
- * Generate transient key based on query vars.
466
- *
467
- * Warning: This can generate errors when used too early if no type has been set.
468
- *
469
- * @since 2.9.1
470
- * @since 3.1.1 : The first parameter is now optional.
471
- * @see $this->generate_cache_key_by_type() to get cache key outside of the query.
472
- *
473
- * @param int|string|bool $page_id The Taxonomy or Post ID.
474
- * @param string $taxonomy The Taxonomy name.
475
- * @param string $type The Post Type.
476
- * @return string The generated cache key by query.
477
- */
478
- public function generate_cache_key_by_query( $page_id = 0, $taxonomy = '', $type = null ) {
479
-
480
- $page_id = $page_id ?: $this->get_the_real_ID();
481
-
482
- static $cached_id = [];
483
-
484
- if ( isset( $cached_id[ $page_id ][ $taxonomy ] ) )
485
- return $cached_id[ $page_id ][ $taxonomy ];
486
-
487
- // Placeholder ID.
488
- $the_id = '';
489
- $_t = $taxonomy;
490
-
491
- if ( $this->is_404() ) {
492
- $the_id = '_404_';
493
- } elseif ( $this->is_archive() ) {
494
- if ( $this->is_category() || $this->is_tag() || $this->is_tax() ) {
495
-
496
- if ( empty( $_t ) ) {
497
- $o = \get_queried_object();
498
-
499
- if ( isset( $o->taxonomy ) )
500
- $_t = $o->taxonomy;
501
- }
502
-
503
- $the_id = $this->generate_taxonomical_cache_key( $page_id, $_t );
504
-
505
- if ( $this->is_tax() )
506
- $the_id = 'archives_' . $the_id;
507
-
508
- } elseif ( $this->is_author() ) {
509
- $the_id = 'author_' . $page_id;
510
- } elseif ( $this->is_date() ) {
511
- $post = \get_post();
512
-
513
- if ( $post && isset( $post->post_date ) ) {
514
- $date = $post->post_date;
515
-
516
- if ( $this->is_year() ) {
517
- $the_id .= 'year_' . \mysql2date( 'y', $date, false );
518
- } elseif ( $this->is_month() ) {
519
- $the_id .= 'month_' . \mysql2date( 'm_y', $date, false );
520
- } elseif ( $this->is_day() ) {
521
- // Day. The correct notation.
522
- $the_id .= 'day_' . \mysql2date( 'd_m_y', $date, false );
523
- }
524
- } else {
525
- // Get seconds since UNIX Epoch. This is a failsafe.
526
- // Memoize the timestamp, so that the key stays the same.
527
- static $unix = null;
528
-
529
- if ( ! isset( $unix ) )
530
- $unix = time();
531
-
532
- // Temporarily disable caches to prevent database spam.
533
- $this->the_seo_framework_use_transients = false;
534
- $this->use_object_cache = false;
535
-
536
- $the_id = 'unix_' . $unix;
537
- }
538
- } else {
539
- // Other taxonomical archives.
540
-
541
- if ( empty( $_t ) ) {
542
- $post_type = \get_query_var( 'post_type' );
543
-
544
- if ( \is_array( $post_type ) )
545
- reset( $post_type );
546
-
547
- if ( $post_type )
548
- $post_type_obj = \get_post_type_object( $post_type );
549
-
550
- if ( isset( $post_type_obj->labels->name ) )
551
- $_t = $post_type_obj->labels->name;
552
- }
553
-
554
- // Still empty? Try this.
555
- if ( empty( $_t ) )
556
- $_t = \get_query_var( 'taxonomy' );
557
-
558
- $the_id = $this->generate_taxonomical_cache_key( $page_id, $_t );
559
-
560
- $the_id = 'archives_' . $the_id;
561
- }
562
- } elseif ( ( $this->is_real_front_page() || $this->is_front_page_by_id( $page_id ) ) || ( \is_admin() && $this->is_seo_settings_page( true ) ) ) {
563
- // Front/HomePage.
564
- $the_id = $this->generate_front_page_cache_key();
565
- } elseif ( $this->is_blog_page( $page_id ) ) {
566
- $the_id = 'blog_' . $page_id;
567
- } elseif ( $this->is_singular() ) {
568
-
569
- $post_type = \get_post_type( $page_id );
570
-
571
- switch ( $post_type ) :
572
- case 'page':
573
- $the_id = 'page_' . $page_id;
574
- break;
575
-
576
- case 'post':
577
- $the_id = 'post_' . $page_id;
578
- break;
579
-
580
- case 'attachment':
581
- $the_id = 'attach_' . $page_id;
582
- break;
583
-
584
- default:
585
- $the_id = 'singular_' . $page_id;
586
- break;
587
- endswitch;
588
- } elseif ( $this->is_search() ) {
589
- // Remove spaces, jumble with md5, Limit to 12 chars.
590
- $query = \esc_sql( substr( md5( str_replace( ' ', '', \get_search_query( true ) ) ), 0, 12 ) );
591
-
592
- // Temporarily disable caches to prevent database spam.
593
- $this->the_seo_framework_use_transients = false;
594
- $this->use_object_cache = false;
595
-
596
- $the_id = $page_id . '_s_' . $query;
597
- }
598
-
599
- /**
600
- * Blog page isn't set or something else is happening. Causes all kinds of problems :(
601
- * Noob. :D
602
- */
603
- if ( empty( $the_id ) )
604
- $the_id = 'noob_' . $page_id . '_' . $_t;
605
-
606
- /**
607
- * This should be at most 25 chars. Unless the $blog_id is higher than 99,999,999.
608
- * Then some cache keys will conflict on every 10th blog ID from eachother which post something on the same day..
609
- * On the day archive. With the same description setting (short).
610
- */
611
- return $cached_id[ $page_id ][ $taxonomy ] = $this->add_cache_key_suffix( $the_id );
612
  }
613
 
614
  /**
@@ -619,8 +337,9 @@ class Cache extends Site_Options {
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
  *
625
  * @param int|string|bool $page_id The Taxonomy or Post ID.
626
  * @param string $taxonomy The term taxonomy.
@@ -629,20 +348,6 @@ class Cache extends Site_Options {
629
  */
630
  public function generate_cache_key_by_type( $page_id, $taxonomy = '', $type = '' ) {
631
  switch ( $type ) :
632
- case 'author':
633
- return $this->add_cache_key_suffix( 'author_' . $page_id );
634
- case 'frontpage':
635
- return $this->add_cache_key_suffix( $this->generate_front_page_cache_key() );
636
- case 'page':
637
- return $this->add_cache_key_suffix( 'page_' . $page_id );
638
- case 'post':
639
- return $this->add_cache_key_suffix( 'post_' . $page_id );
640
- case 'attachment':
641
- return $this->add_cache_key_suffix( 'attach_' . $page_id );
642
- case 'singular':
643
- return $this->add_cache_key_suffix( 'singular_' . $page_id );
644
- case 'term':
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':
@@ -669,134 +374,11 @@ class Cache extends Site_Options {
669
  return $key . '_' . $GLOBALS['blog_id'] . '_' . strtolower( \get_locale() );
670
  }
671
 
672
- /**
673
- * Returns the front page partial transient key.
674
- *
675
- * @since ??? (2.8+)
676
- *
677
- * @param string $type Either blog or page.
678
- * @return string the front page transient key.
679
- */
680
- public function generate_front_page_cache_key( $type = '' ) {
681
-
682
- if ( ! $type ) {
683
- if ( $this->has_page_on_front() ) {
684
- $type = 'page';
685
- } else {
686
- $type = 'blog';
687
- }
688
- }
689
-
690
- return \esc_sql( 'h' . $type . '_' . $this->get_the_front_page_ID() );
691
- }
692
-
693
- /**
694
- * Generates Cache key for taxonomical archives.
695
- *
696
- * @since 2.6.0
697
- * @since 4.0.6 No longer uses mb encoding functions, speeding up this method.
698
- *
699
- * @param int $page_id The taxonomy or page ID.
700
- * @param string $taxonomy The taxonomy name.
701
- * @return string The Taxonomical Archive cache key.
702
- */
703
- protected function generate_taxonomical_cache_key( $page_id = '', $taxonomy = '' ) {
704
-
705
- $the_id = '';
706
-
707
- if ( false !== strpos( $taxonomy, '_' ) ) {
708
- $taxonomy_name = explode( '_', $taxonomy );
709
- if ( \is_array( $taxonomy_name ) ) {
710
- foreach ( $taxonomy_name as $name ) {
711
- if ( \strlen( $name ) >= 3 ) {
712
- $the_id .= \substr( $name, 0, 3 ) . '_';
713
- } else {
714
- $the_id = $name . '_';
715
- }
716
- }
717
- }
718
- }
719
-
720
- if ( ! $the_id ) {
721
- if ( \strlen( $taxonomy ) >= 6 ) {
722
- $the_id = substr( $taxonomy, 0, 6 );
723
- } else {
724
- $the_id = $taxonomy;
725
- }
726
- }
727
-
728
- $the_id = strtolower( \esc_sql( $the_id ) );
729
-
730
- // Put it all together.
731
- return rtrim( $the_id, '_' ) . '_' . $page_id;
732
- }
733
-
734
- /**
735
- * Returns the robots.txt object cache key.
736
- *
737
- * @since 2.8.0
738
- *
739
- * @return string The robots_txt cache key.
740
- */
741
- public function get_robots_txt_cache_key() {
742
-
743
- $revision = '1';
744
-
745
- return 'robots_txt_output_' . $revision . $GLOBALS['blog_id'];
746
- }
747
-
748
- /**
749
- * Returns the TSF meta output Object cache key.
750
- *
751
- * @since 2.9.1
752
- * @uses THE_SEO_FRAMEWORK_DB_VERSION as cache key buster.
753
- *
754
- * @return string The TSF meta output cache key.
755
- */
756
- public function get_meta_output_cache_key_by_query() {
757
- /**
758
- * Cache key buster.
759
- * Busts cache on each new db version.
760
- */
761
- $key = $this->generate_cache_key_by_query() . '_' . THE_SEO_FRAMEWORK_DB_VERSION;
762
-
763
- $page = (string) $this->page();
764
- $paged = (string) $this->paged();
765
-
766
- return 'seo_framework_output_' . $key . '_' . $paged . '_' . $page;
767
- }
768
-
769
- /**
770
- * Returns the TSF meta output Object cache key.
771
- *
772
- * @since 2.9.1
773
- * @uses THE_SEO_FRAMEWORK_DB_VERSION as cache key buster.
774
- * @uses $this->generate_cache_key_by_type()
775
- * @see $this->get_meta_output_cache_key_by_query()
776
- *
777
- * @param int $id The ID. Defaults to current ID.
778
- * @param string $taxonomy The term taxonomy
779
- * @param string $type The post type.
780
- * @return string The TSF meta output cache key.
781
- */
782
- public function get_meta_output_cache_key_by_type( $id = 0, $taxonomy = '', $type = '' ) {
783
- /**
784
- * Cache key buster.
785
- * Busts cache on each new db version.
786
- */
787
- $key = $this->generate_cache_key_by_type( $id, $taxonomy, $type ) . '_' . THE_SEO_FRAMEWORK_DB_VERSION;
788
-
789
- //= Refers to the first page, always.
790
- $_page = $_paged = '1';
791
-
792
- return 'seo_framework_output_' . $key . '_' . $_paged . '_' . $_page;
793
- }
794
-
795
  /**
796
  * Checks whether the permalink structure is updated.
797
  *
798
  * @since 2.3.0
799
- * @since 2.7.0 : Added admin referer check.
800
  * @securitycheck 3.0.0 OK.
801
  *
802
  * @return bool Whether if sitemap transient is deleted.
@@ -864,11 +446,18 @@ class Cache extends Site_Options {
864
  *
865
  * @since 3.0.0
866
  * @since 3.1.0 Now no longer crashes on database errors.
 
 
867
  *
868
  * @return array : { 'archive', 'search' }
869
  */
870
  public function get_excluded_ids_from_cache() {
871
 
 
 
 
 
 
872
  static $cache = null;
873
 
874
  if ( null === $cache )
@@ -876,22 +465,37 @@ class Cache extends Site_Options {
876
 
877
  if ( false === $cache ) {
878
  global $wpdb;
879
- $cache = [];
880
 
881
- //= Two separated equals queries are faster than a single IN with 'meta_key'.
882
- $cache['archive'] = $wpdb->get_results(
883
- "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'exclude_from_archive'"
884
- ); // No cache OK, Set in autoloaded transient. DB call ok.
885
 
886
- $cache['search'] = $wpdb->get_results(
887
- "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'exclude_local_search'"
888
- ); // No cache OK, Set in autoloaded transient. DB call ok.
 
 
889
 
890
- foreach ( [ 'archive', 'search' ] as $key ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
891
  array_walk(
892
- $cache[ $key ],
893
- // Performance: This won't be cached completely by OpCache, but we cache the results, anyway.
894
- function( &$v ) {
895
  if ( isset( $v->meta_value, $v->post_id ) && $v->meta_value ) {
896
  $v = (int) $v->post_id;
897
  } else {
@@ -899,7 +503,7 @@ class Cache extends Site_Options {
899
  }
900
  }
901
  );
902
- $cache[ $key ] = array_filter( $cache[ $key ] );
903
  }
904
 
905
  $this->set_transient( $this->get_exclusion_transient_name(), $cache, 0 );
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
45
 
46
  $this->init_post_cache_actions();
47
 
 
 
 
48
  // Delete Sitemap transient on permalink structure change.
49
  \add_action( 'load-options-permalink.php', [ $this, 'delete_sitemap_transient_permalink_updated' ], 20 );
50
 
96
  * @since 2.8.0
97
  * @since 3.1.0 Added excluded post ids flush.
98
  * @since 3.1.4 Now flushes object cache.
99
+ * @since 4.1.4 No longer flushes 'front', 'robots', or 'object' cache.
100
  */
101
  public function delete_main_cache() {
 
102
  $this->delete_cache( 'sitemap' );
 
103
  $this->delete_cache( 'excluded_post_ids' );
 
104
  }
105
 
106
  /**
110
  * @since 3.0.0 Process is halted when no valid $post_id is supplied.
111
  * @since 4.1.3 Now flushes the sitemap cache (and instigates pinging thereof)
112
  * even when TSF sitemaps are disabled.
113
+ * @since 4.1.4 No longer deletes object cache for post.
114
  *
115
  * @param int $post_id The Post ID that has been updated.
116
  * @return bool True on success, false on failure.
119
 
120
  if ( ! $post_id ) return false;
121
 
122
+ $success = false;
123
 
124
  // Don't flush sitemap on revision.
125
  if ( ! \wp_is_post_revision( $post_id ) )
126
+ $success = $this->delete_cache( 'sitemap' );
127
 
128
+ return $success;
129
  }
130
 
131
  /**
153
  return true;
154
  }
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  /**
157
  * Handles all kinds of cache for removal.
158
  * Main cache deletion function handler.
159
  *
160
  * @since 2.8.0
161
  * @since 2.9.3 $type = 'front' now also returns true.
162
+ * @since 3.1.0 1. Added action.
163
+ * 2. Removed support for $type 'detection'.
164
  * @since 4.0.5 Removed all JSON-LD transient clear calls.
165
+ * @since 4.1.4 The following $type's are no longer supported: 'front', 'post', 'term', 'author', 'robots', 'object'.
166
  *
167
  * @param string $type The type
168
  * @param int $id The post, page or TT ID. Defaults to $this->get_the_real_ID().
176
  $success = false;
177
 
178
  switch ( $type ) :
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  case 'sitemap':
180
  $success = $this->delete_sitemap_transient();
181
  break;
182
 
 
 
 
 
183
  case 'excluded_post_ids':
184
  $success = $this->delete_excluded_post_ids_transient();
185
  break;
186
 
 
 
 
 
 
 
 
187
  default:
188
  break;
189
  endswitch;
277
  return false;
278
  }
279
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  /**
281
  * Returns the post ID exclusion transient name.
282
  *
311
  * @since 2.3.3
312
  * @since 2.6.0 Refactored.
313
  * @since 2.9.1 : 1. Added early singular type detection.
314
+ * 2. Moved generation into another method (v4.1.4: removed method).
315
+ * @since 3.1.1 The first parameter is now optional.
316
+ * @since 4.1.4 No longer generates a cache key when no `$type` is supplied.
317
+ * @TODO since we only support by type, it'd be best to rework this into something simple.
318
  *
319
  * @param int|string|bool $id The Taxonomy or Post ID.
320
  * @param string $taxonomy The taxonomy name.
326
  if ( isset( $type ) )
327
  return $this->generate_cache_key_by_type( $id, $taxonomy, $type );
328
 
329
+ return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  }
331
 
332
  /**
337
  * @since 2.9.1
338
  * @since 2.9.2 Now returns false when an incorrect $type is supplied.
339
  * @since 4.1.2 Now accepts $type 'sitemap_lock'.
340
+ * @since 4.1.4 Removed support for 'author', 'frontpage', 'page', 'post', 'attachment', 'singular', and 'term'.
341
  * @see $this->generate_cache_key().
342
+ * @TODO since we only support a few, it'd be best to rework this into something simple.
343
  *
344
  * @param int|string|bool $page_id The Taxonomy or Post ID.
345
  * @param string $taxonomy The term taxonomy.
348
  */
349
  public function generate_cache_key_by_type( $page_id, $taxonomy = '', $type = '' ) {
350
  switch ( $type ) :
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  case 'ping':
352
  return $this->add_cache_key_suffix( 'tsf_throttle_ping' );
353
  case 'sitemap_lock':
374
  return $key . '_' . $GLOBALS['blog_id'] . '_' . strtolower( \get_locale() );
375
  }
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  /**
378
  * Checks whether the permalink structure is updated.
379
  *
380
  * @since 2.3.0
381
+ * @since 2.7.0 Added admin referer check.
382
  * @securitycheck 3.0.0 OK.
383
  *
384
  * @return bool Whether if sitemap transient is deleted.
446
  *
447
  * @since 3.0.0
448
  * @since 3.1.0 Now no longer crashes on database errors.
449
+ * @since 4.1.4 1. Now tests against post type exclusions.
450
+ * 2. Now considers headlessness. This method runs only on the front-end.
451
  *
452
  * @return array : { 'archive', 'search' }
453
  */
454
  public function get_excluded_ids_from_cache() {
455
 
456
+ if ( $this->is_headless['meta'] ) return [
457
+ 'archive' => '',
458
+ 'search' => '',
459
+ ];
460
+
461
  static $cache = null;
462
 
463
  if ( null === $cache )
465
 
466
  if ( false === $cache ) {
467
  global $wpdb;
 
468
 
469
+ $supported_post_types = $this->get_supported_post_types();
470
+ $public_post_types = $this->get_public_post_types();
 
 
471
 
472
+ $join = '';
473
+ $where = '';
474
+ if ( $supported_post_types !== $public_post_types ) {
475
+ // Post types can be registered arbitrarily through other plugins, even manually by non-super-admins. Prepare!
476
+ $post_type__in = "'" . implode( "','", array_map( '\\esc_sql', $supported_post_types ) ) . "'";
477
 
478
+ // This is as fast as I could make it. Yes, it uses IN, but only on a (tiny) subset of data.
479
+ $join = "LEFT JOIN {$wpdb->posts} ON {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID";
480
+ $where = "AND {$wpdb->posts}.post_type IN ($post_type__in)";
481
+ }
482
+
483
+ //= Two separated equals queries are faster than a single IN with 'meta_key'.
484
+ // phpcs:disable, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We prepared our whole lives.
485
+ $cache = [
486
+ 'archive' => $wpdb->get_results(
487
+ "SELECT post_id, meta_value FROM $wpdb->postmeta $join WHERE meta_key = 'exclude_from_archive' $where"
488
+ ),
489
+ 'search' => $wpdb->get_results(
490
+ "SELECT post_id, meta_value FROM $wpdb->postmeta $join WHERE meta_key = 'exclude_local_search' $where"
491
+ ),
492
+ ];
493
+ // phpcs:enable, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
494
+
495
+ foreach ( [ 'archive', 'search' ] as $type ) {
496
  array_walk(
497
+ $cache[ $type ],
498
+ static function( &$v ) {
 
499
  if ( isset( $v->meta_value, $v->post_id ) && $v->meta_value ) {
500
  $v = (int) $v->post_id;
501
  } else {
503
  }
504
  }
505
  );
506
+ $cache[ $type ] = array_filter( $cache[ $type ] );
507
  }
508
 
509
  $this->set_transient( $this->get_exclusion_transient_name(), $cache, 0 );
inc/classes/core.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -64,6 +64,13 @@ class Core {
64
  * @param mixed $value The property value.
65
  */
66
  final public function __set( $name, $value ) {
 
 
 
 
 
 
 
67
  /**
68
  * For now, no deprecation is being handled; as no properties have been deprecated. Just removed.
69
  */
@@ -84,9 +91,15 @@ class Core {
84
  * @since 3.2.2 This method no longer invokes PHP errors, nor returns protected values.
85
  *
86
  * @param string $name The property name.
87
- * @return void
88
  */
89
  final public function __get( $name ) {
 
 
 
 
 
 
90
  $this->_inaccessible_p_or_m( 'the_seo_framework()->' . $name, 'unknown' );
91
  }
92
 
@@ -106,9 +119,8 @@ class Core {
106
  if ( \is_null( $depr_class ) )
107
  $depr_class = new Deprecated;
108
 
109
- if ( \is_callable( [ $depr_class, $name ] ) ) {
110
  return \call_user_func_array( [ $depr_class, $name ], $arguments );
111
- }
112
 
113
  $this->_inaccessible_p_or_m( 'the_seo_framework()->' . $name . '()' );
114
  }
@@ -197,7 +209,6 @@ class Core {
197
  */
198
  protected function create_view_secret( $value = null ) {
199
  static $secret;
200
- // TODO PHP7+ `$secret = $value ?? $secret;`
201
  return $secret = isset( $value ) ? $value : $secret;
202
  }
203
 
@@ -239,116 +250,6 @@ class Core {
239
  return $base . '_' . str_replace( '-', '_', $instance );
240
  }
241
 
242
- /**
243
- * Proportionate dimensions based on Width and Height.
244
- * AKA Aspect Ratio.
245
- *
246
- * @since 2.6.0
247
- *
248
- * @param int $i The dimension to resize.
249
- * @param int $r1 The deminsion that determines the ratio.
250
- * @param int $r2 The dimension to proportionate to.
251
- * @return int The proportional dimension, rounded.
252
- */
253
- public function proportionate_dimensions( $i, $r1, $r2 ) {
254
-
255
- // Get aspect ratio.
256
- $ar = $r1 / $r2;
257
- $i = $i / $ar;
258
-
259
- return round( $i );
260
- }
261
-
262
- /**
263
- * Adds various links to the plugin row on the plugin's screen.
264
- *
265
- * @since 3.1.0
266
- * @access private
267
- *
268
- * @param array $links The current links.
269
- * @return array The plugin links.
270
- */
271
- public function _add_plugin_action_links( $links = [] ) {
272
-
273
- $tsf_links = [];
274
-
275
- if ( $this->load_options ) {
276
- $tsf_links['settings'] = sprintf(
277
- '<a href="%s">%s</a>',
278
- \esc_url( \admin_url( 'admin.php?page=' . $this->seo_settings_page_slug ) ),
279
- \esc_html__( 'Settings', 'autodescription' )
280
- );
281
- }
282
-
283
- $tsf_links['tsfem'] = sprintf(
284
- '<a href="%s" rel="noreferrer noopener" target="_blank">%s</a>',
285
- 'https://theseoframework.com/extensions/',
286
- \esc_html_x( 'Extensions', 'Plugin extensions', 'autodescription' )
287
- );
288
- $tsf_links['pricing'] = sprintf(
289
- '<a href="%s" rel="noreferrer noopener" target="_blank">%s</a>',
290
- 'https://theseoframework.com/pricing/',
291
- \esc_html_x( 'Pricing', 'Plugin pricing', 'autodescription' )
292
- );
293
-
294
- return array_merge( $tsf_links, $links );
295
- }
296
-
297
- /**
298
- * Adds more row meta on the plugin screen.
299
- *
300
- * @since 3.2.4
301
- * @access private
302
- *
303
- * @param string[] $plugin_meta An array of the plugin's metadata,
304
- * including the version, author,
305
- * author URI, and plugin URI.
306
- * @param string $plugin_file Path to the plugin file relative to the plugins directory.
307
- * @return array $plugin_meta
308
- */
309
- public function _add_plugin_row_meta( $plugin_meta, $plugin_file ) {
310
-
311
- if ( THE_SEO_FRAMEWORK_PLUGIN_BASENAME !== $plugin_file )
312
- return $plugin_meta;
313
-
314
- $plugins = \get_plugins();
315
- $_get_em = empty( $plugins['the-seo-framework-extension-manager/the-seo-framework-extension-manager.php'] );
316
-
317
- return array_merge(
318
- $plugin_meta,
319
- [
320
- 'support' => vsprintf(
321
- '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
322
- [
323
- 'https://tsf.fyi/support',
324
- \esc_html__( 'Get support', 'autodescription' ),
325
- ]
326
- ),
327
- 'docs' => vsprintf(
328
- '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
329
- [
330
- 'https://tsf.fyi/docs',
331
- \esc_html__( 'View documentation', 'autodescription' ),
332
- ]
333
- ),
334
- 'API' => vsprintf(
335
- '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
336
- [
337
- 'https://tsf.fyi/docs/api',
338
- \esc_html__( 'View API docs', 'autodescription' ),
339
- ]
340
- ),
341
- 'EM' => vsprintf(
342
- '<a href="%s" rel="noreferrer noopener nofollow" target="_blank">%s</a>',
343
- [
344
- 'https://tsf.fyi/extension-manager',
345
- $_get_em ? \esc_html_x( 'Get Extension Manager', 'Extension Manager is a product name; do not translate it.', 'autodescription' ) : 'Extension Manager',
346
- ]
347
- ),
348
- ]
349
- );
350
- }
351
-
352
  /**
353
  * Returns an array of hierarchical post types.
354
  *
@@ -396,9 +297,7 @@ class Core {
396
  * @return bool Whether external redirect is allowed.
397
  */
398
  public function allow_external_redirect() {
399
-
400
  static $cache = null;
401
-
402
  /**
403
  * @since 2.1.0
404
  * @param bool $allowed Whether external redirect is allowed.
@@ -457,7 +356,7 @@ class Core {
457
  public function get_settings_capability() {
458
  /**
459
  * @since 2.6.0
460
- * @todo deprecate, use constant instead.
461
  * @param string $capability The user capability required to adjust settings.
462
  */
463
  return (string) \apply_filters( 'the_seo_framework_settings_capability', THE_SEO_FRAMEWORK_SETTINGS_CAP );
@@ -478,17 +377,14 @@ class Core {
478
  /**
479
  * Returns the SEO Settings page URL.
480
  *
481
- * @since 2.6.0
482
  *
483
  * @return string The escaped SEO Settings page URL.
484
  */
485
- public function seo_settings_page_url() {
486
-
487
- if ( $this->load_options ) {
488
- // Options are allowed to be loaded.
489
 
 
490
  $url = html_entity_decode( \menu_page_url( $this->seo_settings_page_slug, false ) );
491
-
492
  return \esc_url( $url, [ 'https', 'http' ] );
493
  }
494
 
@@ -529,11 +425,8 @@ class Core {
529
  * @return string PHP Timezone String.
530
  */
531
  protected function get_tzstring_from_offset( $offset = 0 ) {
532
-
533
- $seconds = round( $offset * HOUR_IN_SECONDS );
534
- $tzstring = timezone_name_from_abbr( '', $seconds, 1 );
535
-
536
- return $tzstring;
537
  }
538
 
539
  /**
@@ -611,11 +504,26 @@ class Core {
611
  * Returns timestamp format based on timestamp settings.
612
  *
613
  * @since 3.0.0
 
 
 
614
  *
 
615
  * @return string The timestamp format used in PHP date.
616
  */
617
- public function get_timestamp_format() {
618
- return $this->uses_time_in_timestamp_format() ? 'Y-m-d\TH:iP' : 'Y-m-d';
 
 
 
 
 
 
 
 
 
 
 
619
  }
620
 
621
  /**
@@ -629,6 +537,34 @@ class Core {
629
  return '1' === $this->get_option( 'timestamps_format' );
630
  }
631
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  /**
633
  * Shortens string and adds ellipses when over a threshold in length.
634
  *
@@ -759,7 +695,7 @@ class Core {
759
  $g = hexdec( $hex[1] );
760
  $b = hexdec( $hex[2] );
761
 
762
- $get_relative_luminance = function( $v ) {
763
  //= Convert to 0~1 value.
764
  $v /= 255;
765
 
@@ -778,17 +714,17 @@ class Core {
778
 
779
  $rel_lum = ( $sr + $sg + $sb );
780
 
 
 
 
 
 
781
  //= Invert colors if they hit luminance boundaries.
782
  if ( $rel_lum < 0.5 ) {
783
  // Build dark greyscale.
784
- $gr = 255 - ( $r * 0.2989 / 8 ) * $rel_lum;
785
- $gg = 255 - ( $g * 0.5870 / 8 ) * $rel_lum;
786
- $gb = 255 - ( $b * 0.1140 / 8 ) * $rel_lum;
787
- } else {
788
- // Build light greyscale.
789
- $gr = ( $r * 0.2989 / 8 ) * $rel_lum;
790
- $gg = ( $g * 0.5870 / 8 ) * $rel_lum;
791
- $gb = ( $b * 0.1140 / 8 ) * $rel_lum;
792
  }
793
 
794
  // Build RGB hex.
@@ -836,14 +772,13 @@ class Core {
836
  * Converts markdown text into HMTL.
837
  * Does not support list or block elements. Only inline statements.
838
  *
839
- * Note: This code has been rightfully stolen from the Extension Manager plugin (sorry Sybre!).
840
- *
841
  * @since 2.8.0
842
  * @since 2.9.0 1. Removed word boundary requirement for strong.
843
- * 2. Now accepts regex count their numeric values in string.
844
  * 3. Fixed header 1~6 calculation.
845
  * @since 2.9.3 Added $args parameter.
846
  * @since 4.0.3 Added a workaround for connected em/strong elements.
 
847
  * @link https://wordpress.org/plugins/about/readme.txt
848
  *
849
  * @param string $text The text that might contain markdown. Expected to be escaped.
@@ -853,128 +788,6 @@ class Core {
853
  * @return string The markdown converted text.
854
  */
855
  public function convert_markdown( $text, $convert = [], $args = [] ) {
856
-
857
- // preprocess
858
- $text = str_replace( "\r\n", "\n", $text );
859
- $text = str_replace( "\t", ' ', $text );
860
- $text = trim( $text );
861
-
862
- // You need 3 chars to make a markdown: *m*
863
- if ( \strlen( $text ) < 3 )
864
- return '';
865
-
866
- // Merge defaults with $args.
867
- $args = array_merge( [ 'a_internal' => false ], $args );
868
-
869
- /**
870
- * The conversion list's keys are per reference only.
871
- */
872
- $conversions = [
873
- '**' => 'strong',
874
- '*' => 'em',
875
- '`' => 'code',
876
- '[]()' => 'a',
877
- '======' => 'h6',
878
- '=====' => 'h5',
879
- '====' => 'h4',
880
- '===' => 'h3',
881
- '==' => 'h2',
882
- '=' => 'h1',
883
- ];
884
-
885
- $md_types = empty( $convert ) ? $conversions : array_intersect( $conversions, $convert );
886
-
887
- if ( 2 === \count( array_intersect( $md_types, [ 'em', 'strong' ] ) ) ) :
888
- $count = preg_match_all( '/(?:\*{3})([^\*{\3}]+)(?:\*{3})/', $text, $matches, PREG_PATTERN_ORDER );
889
- for ( $i = 0; $i < $count; $i++ ) {
890
- $text = str_replace(
891
- $matches[0][ $i ],
892
- sprintf( '<strong><em>%s</em></strong>', \esc_html( $matches[1][ $i ] ) ),
893
- $text
894
- );
895
- }
896
- endif;
897
-
898
- foreach ( $md_types as $type ) :
899
- switch ( $type ) :
900
- case 'strong':
901
- $count = preg_match_all( '/(?:\*{2})([^\*{\2}]+)(?:\*{2})/', $text, $matches, PREG_PATTERN_ORDER );
902
-
903
- for ( $i = 0; $i < $count; $i++ ) {
904
- $text = str_replace(
905
- $matches[0][ $i ],
906
- sprintf( '<strong>%s</strong>', \esc_html( $matches[1][ $i ] ) ),
907
- $text
908
- );
909
- }
910
- break;
911
-
912
- case 'em':
913
- $count = preg_match_all( '/(?:\*{1})([^\*{\1}]+)(?:\*{1})/', $text, $matches, PREG_PATTERN_ORDER );
914
-
915
- for ( $i = 0; $i < $count; $i++ ) {
916
- $text = str_replace(
917
- $matches[0][ $i ],
918
- sprintf( '<em>%s</em>', \esc_html( $matches[1][ $i ] ) ),
919
- $text
920
- );
921
- }
922
- break;
923
-
924
- case 'code':
925
- $count = preg_match_all( '/(?:`{1})([^`{\1}]+)(?:`{1})/', $text, $matches, PREG_PATTERN_ORDER );
926
-
927
- for ( $i = 0; $i < $count; $i++ ) {
928
- $text = str_replace(
929
- $matches[0][ $i ],
930
- sprintf( '<code>%s</code>', \esc_html( $matches[1][ $i ] ) ),
931
- $text
932
- );
933
- }
934
- break;
935
-
936
- case 'h6':
937
- case 'h5':
938
- case 'h4':
939
- case 'h3':
940
- case 'h2':
941
- case 'h1':
942
- // Considers word non-boundary. @TODO consider removing this?
943
- $expression = sprintf(
944
- '/(?:\={%1$d})\B([^\={\%1$s}]+)\B(?:\={%1$d})/',
945
- filter_var( $type, FILTER_SANITIZE_NUMBER_INT )
946
- );
947
-
948
- $count = preg_match_all( $expression, $text, $matches, PREG_PATTERN_ORDER );
949
-
950
- for ( $i = 0; $i < $count; $i++ ) {
951
- $text = str_replace(
952
- $matches[0][ $i ],
953
- sprintf( '<%1$s>%2$s</%1$s>', \esc_attr( $type ), \esc_html( $matches[1][ $i ] ) ),
954
- $text
955
- );
956
- }
957
- break;
958
-
959
- case 'a':
960
- $count = preg_match_all( '/(?:(?:\[{1})([^\]]+)(?:\]{1})(?:\({1})([^\)\(]+)(?:\){1}))/', $text, $matches, PREG_PATTERN_ORDER );
961
-
962
- $_string = $args['a_internal'] ? '<a href="%s">%s</a>' : '<a href="%s" target="_blank" rel="nofollow noreferrer noopener">%s</a>';
963
-
964
- for ( $i = 0; $i < $count; $i++ ) {
965
- $text = str_replace(
966
- $matches[0][ $i ],
967
- sprintf( $_string, \esc_url( $matches[2][ $i ], [ 'https', 'http' ] ), \esc_html( $matches[1][ $i ] ) ),
968
- $text
969
- );
970
- }
971
- break;
972
-
973
- default:
974
- break;
975
- endswitch;
976
- endforeach;
977
-
978
- return $text;
979
  }
980
  }
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
64
  * @param mixed $value The property value.
65
  */
66
  final public function __set( $name, $value ) {
67
+
68
+ if ( 'load_options' === $name ) {
69
+ // $this->_inaccessible_p_or_m( 'the_seo_framework()->load_options', 'since 4.2.0; use constant THE_SEO_FRAMEWORK_HEADLESS' );
70
+ $this->is_headless['settings'] = $value;
71
+ return;
72
+ }
73
+
74
  /**
75
  * For now, no deprecation is being handled; as no properties have been deprecated. Just removed.
76
  */
91
  * @since 3.2.2 This method no longer invokes PHP errors, nor returns protected values.
92
  *
93
  * @param string $name The property name.
94
+ * @return mixed
95
  */
96
  final public function __get( $name ) {
97
+
98
+ if ( 'load_options' === $name ) {
99
+ // $this->_inaccessible_p_or_m( 'the_seo_framework()->load_options', 'since 4.2.0; use constant THE_SEO_FRAMEWORK_HEADLESS' );
100
+ return ! $this->is_headless['settings'];
101
+ }
102
+
103
  $this->_inaccessible_p_or_m( 'the_seo_framework()->' . $name, 'unknown' );
104
  }
105
 
119
  if ( \is_null( $depr_class ) )
120
  $depr_class = new Deprecated;
121
 
122
+ if ( \is_callable( [ $depr_class, $name ] ) )
123
  return \call_user_func_array( [ $depr_class, $name ], $arguments );
 
124
 
125
  $this->_inaccessible_p_or_m( 'the_seo_framework()->' . $name . '()' );
126
  }
209
  */
210
  protected function create_view_secret( $value = null ) {
211
  static $secret;
 
212
  return $secret = isset( $value ) ? $value : $secret;
213
  }
214
 
250
  return $base . '_' . str_replace( '-', '_', $instance );
251
  }
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  /**
254
  * Returns an array of hierarchical post types.
255
  *
297
  * @return bool Whether external redirect is allowed.
298
  */
299
  public function allow_external_redirect() {
 
300
  static $cache = null;
 
301
  /**
302
  * @since 2.1.0
303
  * @param bool $allowed Whether external redirect is allowed.
356
  public function get_settings_capability() {
357
  /**
358
  * @since 2.6.0
359
+ * @todo deprecate 4.2.0, use constant instead.
360
  * @param string $capability The user capability required to adjust settings.
361
  */
362
  return (string) \apply_filters( 'the_seo_framework_settings_capability', THE_SEO_FRAMEWORK_SETTINGS_CAP );
377
  /**
378
  * Returns the SEO Settings page URL.
379
  *
380
+ * @since 4.1.4
381
  *
382
  * @return string The escaped SEO Settings page URL.
383
  */
384
+ public function get_seo_settings_page_url() {
 
 
 
385
 
386
+ if ( ! $this->is_headless['settings'] ) {
387
  $url = html_entity_decode( \menu_page_url( $this->seo_settings_page_slug, false ) );
 
388
  return \esc_url( $url, [ 'https', 'http' ] );
389
  }
390
 
425
  * @return string PHP Timezone String.
426
  */
427
  protected function get_tzstring_from_offset( $offset = 0 ) {
428
+ $seconds = round( $offset * HOUR_IN_SECONDS );
429
+ return timezone_name_from_abbr( '', $seconds, 1 );
 
 
 
430
  }
431
 
432
  /**
504
  * Returns timestamp format based on timestamp settings.
505
  *
506
  * @since 3.0.0
507
+ * @since 4.1.4: 1. Added options-override parameter.
508
+ * 1. Added return value filter.
509
+ * @link https://www.w3.org/TR/NOTE-datetime
510
  *
511
+ * @param null|bool $override_get_time Whether to override the $get_time from option value.
512
  * @return string The timestamp format used in PHP date.
513
  */
514
+ public function get_timestamp_format( $override_get_time = null ) {
515
+
516
+ $get_time = isset( $override_get_time )
517
+ ? $override_get_time
518
+ : $this->uses_time_in_timestamp_format();
519
+
520
+ return \apply_filters_ref_array(
521
+ 'the_seo_framework_timestamp_format',
522
+ [
523
+ $get_time ? 'Y-m-d\TH:iP' : 'Y-m-d',
524
+ $get_time,
525
+ ]
526
+ );
527
  }
528
 
529
  /**
537
  return '1' === $this->get_option( 'timestamps_format' );
538
  }
539
 
540
+ /**
541
+ * Merges arrays distinctly, much like `array_merge()`, but then for multidimensionals.
542
+ * Unlike PHP's `array_merge_recursive()`, this method doesn't convert non-unique keys as sequential.
543
+ *
544
+ * A do-while is faster than while. Sorry for the legibility.
545
+ *
546
+ * @since 4.1.4
547
+ *
548
+ * @param array ...$arrays The arrays to merge. The rightmost array's values are dominant.
549
+ * @return array The merged arrays.
550
+ */
551
+ public function array_merge_recursive_distinct( array ...$arrays ) {
552
+
553
+ $i = \count( $arrays );
554
+
555
+ if ( 2 === $i ) foreach ( $arrays[1] as $key => $value ) {
556
+ $arrays[0][ $key ] =
557
+ isset( $arrays[0][ $key ] ) && \is_array( $arrays[0][ $key ] )
558
+ ? $this->array_merge_recursive_distinct( $arrays[0][ $key ], $value )
559
+ : $value;
560
+ } else do {
561
+ // phpcs:ignore -- Imagine assigning from right to left, but also left to right. Yes:
562
+ $arrays[ --$i - 1 ] = $this->array_merge_recursive_distinct( $arrays[ $i - 1 ], $arrays[ $i ] );
563
+ } while ( $i > 1 );
564
+
565
+ return $arrays[0];
566
+ }
567
+
568
  /**
569
  * Shortens string and adds ellipses when over a threshold in length.
570
  *
695
  $g = hexdec( $hex[1] );
696
  $b = hexdec( $hex[2] );
697
 
698
+ $get_relative_luminance = static function( $v ) {
699
  //= Convert to 0~1 value.
700
  $v /= 255;
701
 
714
 
715
  $rel_lum = ( $sr + $sg + $sb );
716
 
717
+ // Build light greyscale.
718
+ $gr = ( $r * 0.2989 / 8 ) * $rel_lum;
719
+ $gg = ( $g * 0.5870 / 8 ) * $rel_lum;
720
+ $gb = ( $b * 0.1140 / 8 ) * $rel_lum;
721
+
722
  //= Invert colors if they hit luminance boundaries.
723
  if ( $rel_lum < 0.5 ) {
724
  // Build dark greyscale.
725
+ $gr = 255 - $gr;
726
+ $gg = 255 - $gg;
727
+ $gb = 255 - $gb;
 
 
 
 
 
728
  }
729
 
730
  // Build RGB hex.
772
  * Converts markdown text into HMTL.
773
  * Does not support list or block elements. Only inline statements.
774
  *
 
 
775
  * @since 2.8.0
776
  * @since 2.9.0 1. Removed word boundary requirement for strong.
777
+ * 2. Now lets regex count their numeric values in string.
778
  * 3. Fixed header 1~6 calculation.
779
  * @since 2.9.3 Added $args parameter.
780
  * @since 4.0.3 Added a workaround for connected em/strong elements.
781
+ * @since 4.1.4 Offloaded to `The_SEO_Framework\Interpreters\Markdown::convert()`
782
  * @link https://wordpress.org/plugins/about/readme.txt
783
  *
784
  * @param string $text The text that might contain markdown. Expected to be escaped.
788
  * @return string The markdown converted text.
789
  */
790
  public function convert_markdown( $text, $convert = [], $args = [] ) {
791
+ return Interpreters\Markdown::convert( $text, $convert, $args );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
  }
793
  }
inc/classes/debug.class.php CHANGED
@@ -12,7 +12,7 @@ namespace The_SEO_Framework;
12
 
13
  /**
14
  * The SEO Framework plugin
15
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
16
  *
17
  * This program is free software: you can redistribute it and/or modify
18
  * it under the terms of the GNU General Public License version 3 as published
@@ -89,20 +89,6 @@ final class Debug {
89
  return static::$instance;
90
  }
91
 
92
- /**
93
- * Mark a filter as deprecated and inform when it has been used.
94
- *
95
- * @since 2.8.0
96
- * @see @this->_deprecated_function().
97
- *
98
- * @param string $filter The function that was called.
99
- * @param string $version The version of WordPress that deprecated the function.
100
- * @param string $replacement Optional. The function that should have been called. Default null.
101
- */
102
- public function _deprecated_filter( $filter, $version, $replacement = null ) {
103
- $this->_deprecated_function( 'Filter ' . $filter, $version, $replacement ); // phpcs:ignore -- Wrong asserts, copied method name.
104
- }
105
-
106
  /**
107
  * Mark a function as deprecated and inform when it has been used.
108
  *
@@ -291,7 +277,7 @@ final class Debug {
291
  */
292
  protected function get_error( $type = null ) {
293
 
294
- $backtrace = debug_backtrace();
295
 
296
  if ( ! $backtrace ) return [];
297
 
@@ -522,7 +508,7 @@ final class Debug {
522
  /**
523
  * Sets debug query cache.
524
  *
525
- * @since 3.1.0 : Introducted in 2.8.0, but the name changed.
526
  * @access private
527
  */
528
  public function _set_debug_query_output_cache() {
@@ -601,9 +587,7 @@ final class Debug {
601
  $is_tag = $tsf->is_tag();
602
  $is_tax = $tsf->is_tax();
603
  $is_shop = $tsf->is_shop();
604
- $is_wc_shop = $tsf->is_wc_shop();
605
  $is_product = $tsf->is_product();
606
- $is_wc_product = $tsf->is_wc_product();
607
  $is_seo_settings_page = $tsf->is_seo_settings_page( true );
608
  $numpages = $tsf->numpages();
609
  $is_multipage = $tsf->is_multipage();
12
 
13
  /**
14
  * The SEO Framework plugin
15
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
16
  *
17
  * This program is free software: you can redistribute it and/or modify
18
  * it under the terms of the GNU General Public License version 3 as published
89
  return static::$instance;
90
  }
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  /**
93
  * Mark a function as deprecated and inform when it has been used.
94
  *
277
  */
278
  protected function get_error( $type = null ) {
279
 
280
+ $backtrace = debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, 5 );
281
 
282
  if ( ! $backtrace ) return [];
283
 
508
  /**
509
  * Sets debug query cache.
510
  *
511
+ * @since 3.1.0 Introducted in 2.8.0, but the name changed.
512
  * @access private
513
  */
514
  public function _set_debug_query_output_cache() {
587
  $is_tag = $tsf->is_tag();
588
  $is_tax = $tsf->is_tax();
589
  $is_shop = $tsf->is_shop();
 
590
  $is_product = $tsf->is_product();
 
591
  $is_seo_settings_page = $tsf->is_seo_settings_page( true );
592
  $numpages = $tsf->numpages();
593
  $is_multipage = $tsf->is_multipage();
inc/classes/deprecated.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -33,964 +33,1133 @@ namespace The_SEO_Framework;
33
  * @since 2.8.0
34
  * @since 3.1.0 Removed all methods deprecated in 3.0.0.
35
  * @since 4.0.0 Removed all methods deprecated in 3.1.0.
 
36
  * @ignore
37
  */
38
  final class Deprecated {
39
 
40
  /**
41
- * Returns a filterable sequential array of default scripts.
 
42
  *
43
- * @since 3.2.2
44
- * @since 4.0.0 Deprecated.
45
  * @deprecated
46
  *
47
- * @return array
 
48
  */
49
- public function get_default_scripts() {
 
 
 
 
 
50
 
51
  $tsf = \the_seo_framework();
52
- $tsf->_deprecated_function( 'the_seo_framework()->get_default_scripts()', '4.0.0' );
53
 
54
- return array_merge(
55
- \The_SEO_Framework\Bridges\Scripts::get_tsf_scripts(),
56
- \The_SEO_Framework\Bridges\Scripts::get_tt_scripts()
57
- );
 
 
 
 
 
 
 
58
  }
59
 
60
  /**
61
- * Enqueues Gutenberg-related scripts.
62
  *
63
- * @since 3.2.0
64
- * @since 4.0.0 Deprecated.
 
65
  * @deprecated
66
  *
67
- * @return void Early if already enqueued.
 
68
  */
69
- public function enqueue_gutenberg_compat_scripts() {
70
 
71
  $tsf = \the_seo_framework();
72
- $tsf->_deprecated_function( 'the_seo_framework()->enqueue_gutenberg_compat_scripts()', '4.0.0' );
73
 
74
- if ( \The_SEO_Framework\_has_run( __METHOD__ ) ) return;
 
 
 
 
 
 
 
75
 
76
- \The_SEO_Framework\Builders\Scripts::register(
77
- \The_SEO_Framework\Bridges\Scripts::get_gutenberg_compat_scripts()
78
- );
79
  }
80
 
81
  /**
82
- * Enqueues Media Upload and Cropping scripts.
 
83
  *
84
- * @since 3.1.0
85
- * @since 4.0.0 Deprecated.
 
 
 
 
 
 
86
  * @deprecated
87
  *
88
- * @return void Early if already enqueued.
 
 
 
89
  */
90
- public function enqueue_media_scripts() {
 
 
91
 
92
  $tsf = \the_seo_framework();
93
- $tsf->_deprecated_function( 'the_seo_framework()->enqueue_media_scripts()', '4.0.0' );
94
 
95
- if ( \The_SEO_Framework\_has_run( __METHOD__ ) ) return;
96
 
97
- $args = [];
98
- if ( $tsf->is_post_edit() ) {
99
- $args['post'] = $tsf->get_the_real_admin_ID();
100
  }
101
- \wp_enqueue_media( $args );
102
 
103
- \The_SEO_Framework\Builders\Scripts::register(
104
- \The_SEO_Framework\Bridges\Scripts::get_media_scripts()
105
- );
 
 
 
106
  }
107
 
108
  /**
109
- * Enqueues Primary Term Selection scripts.
110
  *
111
- * @since 3.1.0
112
- * @since 4.0.0 Deprecated.
 
 
113
  * @deprecated
114
  *
115
- * @return void Early if already enqueued.
116
  */
117
- public function enqueue_primaryterm_scripts() {
118
 
119
  $tsf = \the_seo_framework();
120
- $tsf->_deprecated_function( 'the_seo_framework()->enqueue_primaryterm_scripts()', '4.0.0' );
121
 
122
- if ( \The_SEO_Framework\_has_run( __METHOD__ ) ) return;
123
 
124
- \The_SEO_Framework\Builders\Scripts::register(
125
- \The_SEO_Framework\Bridges\Scripts::get_primaryterm_scripts()
126
- );
127
  }
128
 
129
  /**
130
- * Includes the necessary sortable metabox scripts.
131
  *
132
- * @since 2.2.2
133
- * @since 4.0.0 Deprecated.
 
134
  * @deprecated
 
 
135
  */
136
- public function metabox_scripts() {
137
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->metabox_scripts()', '4.0.0', '\The_SEO_Framework\Bridges\Scripts::prepare_metabox_scripts()' );
138
- \The_SEO_Framework\Bridges\Scripts::prepare_metabox_scripts();
 
 
 
 
139
  }
140
 
141
  /**
142
- * Returns the SEO Bar.
143
- * Memoizes the return value.
144
  *
145
- * @since 3.0.4
146
- * @since 4.0.0 Deprecated
 
 
147
  * @deprecated
148
  *
149
- * @param string $column the current column : If it's a taxonomy, this is empty
150
- * @param int $post_id the post id : If it's a taxonomy, this is the column name
151
- * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
152
  */
153
- public function get_seo_bar( $column, $post_id, $tax_id ) {
154
-
155
  $tsf = \the_seo_framework();
156
- $tsf->_deprecated_function( 'the_seo_framework()->get_seo_bar()', '4.0.0', 'the_seo_framework()->get_generated_seo_bar()' );
157
-
158
- $type = \get_post_type( $post_id );
159
-
160
- if ( false === $type || '' !== $tax_id ) {
161
- $type = $tsf->get_current_taxonomy();
162
- }
163
-
164
- if ( '' !== $tax_id ) {
165
- $column = $post_id;
166
- $post_id = $tax_id;
167
- }
168
-
169
- return $tsf->post_status( $post_id, $type );
170
  }
171
 
172
  /**
173
- * Renders post status. Memoizes the output.
174
  *
175
- * @since 2.1.9
176
- * @since 2.8.0 Third parameter `$echo` has been put into effect.
177
- * @since 4.0.0 Deprecated.
178
  * @deprecated
179
  *
180
- * @param int $post_id The Post ID or taxonomy ID.
181
- * @param string $type The content type.
182
- * @param bool $echo Whether to echo the value. Does not eliminate return.
183
- * @return string|void $content The post SEO status. Void if $echo is true.
184
  */
185
- public function post_status( $post_id, $type = '', $echo = false ) {
186
 
187
  $tsf = \the_seo_framework();
188
 
189
- $tsf->_deprecated_function( 'the_seo_framework()->post_status()', '4.0.0', 'the_seo_framework()->get_generated_seo_bar()' );
190
-
191
- if ( ! $post_id )
192
- $post_id = $tsf->get_the_real_ID();
193
-
194
- if ( 'inpost' === $type || ! $type ) {
195
- $type = \get_post_type( $post_id );
196
- }
197
-
198
- if ( $tsf->is_post_type_page( $type ) ) {
199
- $post_type = $type;
200
- } else {
201
- $taxonomy = $tsf->get_current_taxonomy();
202
- $post_type = $tsf->get_admin_post_type();
203
- }
204
-
205
- $bar = $tsf->get_generated_seo_bar( [
206
- 'id' => $post_id,
207
- 'post_type' => $post_type,
208
- 'taxonomy' => $taxonomy,
209
- ] );
210
-
211
- if ( $echo ) {
212
- // phpcs:ignore, WordPress.Security.EscapeOutput -- the SEO Bar is escaped.
213
- echo $bar;
214
- } else {
215
- return $bar;
216
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
 
219
  /**
220
- * Returns the static scripts class object.
221
  *
222
- * The first letter of the method is capitalized, to indicate it's a class caller.
 
223
  *
224
- * @since 3.1.0
225
- * @since 4.0.0 Deprecated.
 
 
 
 
 
 
 
226
  * @deprecated
227
  *
228
- * @return string The scripts class name.
 
 
 
 
 
 
 
229
  */
230
- public function Scripts() { // phpcs:ignore, WordPress.NamingConventions.ValidFunctionName
231
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->Scripts()', '4.0.0', '\The_SEO_Framework\Builders\Scripts::class' );
232
- return \The_SEO_Framework\Builders\Scripts::class;
 
 
233
  }
234
 
235
  /**
236
- * Determines if we're doing ajax.
237
- *
238
- * @since 2.9.0
239
- * @since 4.0.0 1. Now uses wp_doing_ajax()
240
- * 2. Deprecated.
241
- * @deprecated
242
  *
243
- * @return bool True if AJAX
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  */
245
- public function doing_ajax() {
246
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->doing_ajax()', '4.0.0', 'wp_doing_ajax' );
247
- return \wp_doing_ajax();
 
248
  }
249
 
250
  /**
251
- * Whether to lowercase the noun or keep it UCfirst. Memoizes the input noun's output.
252
- * Depending if language is German.
253
  *
254
  * @since 2.6.0
255
- * @since 4.0.0 Deprecated
 
 
 
 
 
 
 
 
 
256
  * @deprecated
257
  *
258
- * @param string $noun The noun to lowercase.
259
- * @return string The maybe lowercase noun.
260
  */
261
- public function maybe_lowercase_noun( $noun ) {
262
 
263
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->maybe_lowercase_noun()', '4.0.0' );
264
 
265
- static $lowercase = [];
266
 
267
- if ( isset( $lowercase[ $noun ] ) )
268
- return $lowercase[ $noun ];
 
 
 
269
 
270
- return $lowercase[ $noun ] = \the_seo_framework()->check_wp_locale( 'de' ) ? $noun : strtolower( $noun );
271
  }
272
 
273
  /**
274
- * Detect WordPress language.
275
- * Considers en_UK, en_US, en, etc.
276
- *
277
- * @since 2.6.0
278
- * @since 3.1.0 Removed caching.
279
- * @since 4.0.0 Deprecated.
 
 
 
280
  * @deprecated
281
  *
282
- * @param string $locale Required, the locale.
283
- * @return bool Whether the input $locale is in the current WordPress locale.
 
 
 
 
 
 
 
 
 
284
  */
285
- public function check_wp_locale( $locale = '' ) {
286
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->check_wp_locale()', '4.0.0' );
287
- return false !== strpos( \get_locale(), $locale );
288
  }
289
 
290
  /**
291
- * Initializes term meta data filters and functions.
292
  *
293
- * @since 2.7.0
294
- * @since 3.0.0 No longer checks for admin query.
295
- * @since 4.0.0 Deprecated.
 
 
296
  * @deprecated
 
 
 
 
 
 
 
 
 
 
 
 
297
  */
298
- public function initialize_term_meta() {
299
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->initialize_term_meta()', '4.0.0', '\the_seo_framework()->init_term_meta()' );
300
- \the_seo_framework()->init_term_meta();
301
  }
302
 
303
  /**
304
- * Ping search engines on post publish.
 
305
  *
306
- * @since 2.2.9
307
- * @since 2.8.0 Only worked when the blog was not public...
308
- * @since 3.1.0 Now allows one ping per language.
309
- * @uses $this->add_cache_key_suffix()
310
- * @since 3.2.3 1. Now works as intended again.
311
- * 2. Removed Easter egg.
312
- * @since 4.0.0 Deprecated.
313
  * @deprecated
314
  *
315
- * @return void Early if blog is not public.
 
316
  */
317
- public static function ping_searchengines() {
318
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->ping_searchengines()', '4.0.0', '\The_SEO_Framework\Bridges\Ping::ping_search_engines()' );
319
- \The_SEO_Framework\Bridges\Ping::ping_search_engines();
320
  }
321
 
322
  /**
323
- * Pings the sitemap location to Google.
 
324
  *
325
- * @since 2.2.9
326
- * @since 3.1.0 Updated ping URL. Old one still worked, too.
327
- * @since 4.0.0 Deprecated.
 
 
328
  * @deprecated
329
- * @link https://support.google.com/webmasters/answer/6065812?hl=en
 
 
330
  */
331
- public static function ping_google() {
332
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->ping_google()', '4.0.0', '\The_SEO_Framework\Bridges\Ping::ping_google()' );
333
- \The_SEO_Framework\Bridges\Ping::ping_google();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
336
  /**
337
- * Pings the sitemap location to Bing.
 
338
  *
339
- * @since 2.2.9
340
- * @since 3.2.3 Updated ping URL. Old one still worked, too.
341
- * @since 4.0.0 Deprecated.
342
- * @deprecated
343
- * @link https://www.bing.com/webmaster/help/how-to-submit-sitemaps-82a15bd4
 
 
 
 
 
344
  */
345
- public static function ping_bing() {
346
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->ping_bing()', '4.0.0', '\The_SEO_Framework\Bridges\Ping::ping_bing()' );
347
- \The_SEO_Framework\Bridges\Ping::ping_bing();
348
  }
349
 
350
  /**
351
- * Returns the stylesheet XSL location URL.
352
  *
353
- * @since 2.8.0
354
- * @since 3.0.0 : 1. No longer uses home URL from cache. But now uses `get_home_url()`.
355
- * 2. Now takes query parameters (if any) and restores them correctly.
356
- * @since 4.0.0 Deprecated.
357
  * @deprecated
358
- * @global \WP_Rewrite $wp_rewrite
359
  *
360
- * @return string URL location of the XSL stylesheet. Unescaped.
361
  */
362
- public function get_sitemap_xsl_url() {
363
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_sitemap_xsl_url()', '4.0.0', '\The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url(\'xsl-stylesheet\')' );
364
- return \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url( 'xsl-stylesheet' );
 
365
  }
366
 
367
  /**
368
- * Returns the sitemap XML location URL.
369
  *
370
- * @since 2.9.2
371
- * @since 3.0.0 : 1. No longer uses home URL from cache. But now uses `get_home_url()`.
372
- * 2. Now takes query parameters (if any) and restores them correctly.
373
- * @since 4.0.0 Deprecated.
374
- * @deprecated
375
- * @global \WP_Rewrite $wp_rewrite
376
  *
377
- * @return string URL location of the XML sitemap. Unescaped.
378
  */
379
- public function get_sitemap_xml_url() {
380
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_sitemap_xml_url()', '4.0.0', '\The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url()' );
381
- return \The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url();
 
382
  }
383
 
384
  /**
385
- * Sitemap XSL stylesheet output.
 
386
  *
387
- * @since 2.8.0
388
- * @since 3.1.0 1. Now outputs 200-response code.
389
- * 2. Now outputs robots tag, preventing indexing.
390
- * 3. Now overrides other header tags.
391
- * @since 4.0.0 Deprecated.
392
- * @deprecated
 
 
 
 
 
 
 
 
393
  */
394
- public function output_sitemap_xsl_stylesheet() {
395
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->output_sitemap_xsl_stylesheet()', '4.0.0' );
396
- return \The_SEO_Framework\Bridges\Sitemap::get_instance()->output_stylesheet();
 
397
  }
398
 
399
  /**
400
- * Determines if post type supports The SEO Framework.
401
  *
402
- * @since 2.3.9
403
- * @since 3.1.0 1. Removed caching.
404
- * 2. Now works in admin.
405
- * @since 4.0.0 Deprecated.
406
- * @deprecated
407
  *
408
- * @param string $post_type Optional. The post type to check.
409
- * @return bool true of post type is supported.
 
 
410
  */
411
- public function post_type_supports_custom_seo( $post_type = '' ) {
412
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->post_type_supports_custom_seo()', '4.0.0', 'the_seo_framework()->is_post_type_supported()' );
413
- return \the_seo_framework()->is_post_type_supported( $post_type );
 
414
  }
415
 
416
  /**
417
- * Determines if the taxonomy supports The SEO Framework.
418
- *
419
- * Checks if at least one taxonomy objects post type supports The SEO Framework,
420
- * and wether the taxonomy is public and rewritable.
421
  *
422
- * @since 3.1.0
423
- * @since 4.0.0 1. Now goes over all post types for the taxonomy.
424
- * 2. Can now return true if at least one post type for the taxonomy is supported.
425
- * 3. Deprecated.
426
- * @deprecated
427
  *
428
- * @param string $taxonomy Optional. The taxonomy name.
429
- * @return bool True if at least one post type in taxonomy isn't disabled.
 
430
  */
431
- public function taxonomy_supports_custom_seo( $taxonomy = '' ) {
432
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->taxonomy_supports_custom_seo()', '4.0.0', 'the_seo_framework()->is_taxonomy_supported()' );
433
- return \the_seo_framework()->is_taxonomy_supported( $taxonomy );
 
434
  }
435
 
436
  /**
437
- * Returns taxonomical canonical URL.
438
- * Automatically adds pagination if the ID matches the query.
439
- *
440
- * @since 3.0.0
441
- * @since 4.0.0 Deprecated
 
 
 
442
  * @deprecated
 
443
  *
444
- * @param int $term_id The term ID.
445
- * @param string $taxonomy The taxonomy.
446
- * @return string The taxonomical canonical URL, if any.
447
  */
448
- public function get_taxonomial_canonical_url( $term_id, $taxonomy ) {
449
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_taxonomial_canonical_url()', '4.0.0', 'the_seo_framework()->get_taxonomical_canonical_url()' );
450
- return \the_seo_framework()->get_taxonomical_canonical_url( $term_id, $taxonomy );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  }
452
 
453
  /**
454
- * Tries to fetch a term by $id from query.
455
- *
456
- * @since 2.6.0
457
- * @since 3.0.0 Can now get custom post type objects.
458
- * @since 4.0.0 Deprecated.
 
 
 
 
459
  * @deprecated
 
460
  *
461
- * @param int $id The possible taxonomy Term ID.
462
- * @return false|object The Term object.
463
  */
464
- public function fetch_the_term( $id = '' ) {
465
 
466
  $tsf = \the_seo_framework();
 
467
 
468
- $tsf->_deprecated_function( 'the_seo_framework()->fetch_the_term()', '4.0.0', 'WP Core\'s get_term_by()' );
469
-
470
- static $term = [];
471
 
472
- if ( isset( $term[ $id ] ) )
473
- return $term[ $id ];
474
-
475
- // Return null if no term can be detected.
476
- if ( false === $tsf->is_archive() )
477
- return false;
478
-
479
- if ( \is_admin() ) {
480
- $taxonomy = $tsf->get_current_taxonomy();
481
- if ( $taxonomy ) {
482
- $term_id = $id ?: $tsf->get_the_real_admin_ID();
483
- $term[ $id ] = \get_term( $term_id, $taxonomy );
484
- }
485
  } else {
486
- if ( $tsf->is_category() || $tsf->is_tag() ) {
487
- $term[ $id ] = \get_queried_object();
488
- } elseif ( $tsf->is_tax() ) {
489
- $term[ $id ] = \get_term_by( 'slug', \get_query_var( 'term' ), \get_query_var( 'taxonomy' ) );
490
- } elseif ( \is_post_type_archive() ) {
491
- $post_type = \get_query_var( 'post_type' );
492
- $post_type = \is_array( $post_type ) ? reset( $post_type ) : $post_type;
493
-
494
- $term[ $id ] = \get_post_type_object( $post_type );
495
- }
496
  }
497
 
498
- if ( isset( $term[ $id ] ) )
499
- return $term[ $id ];
500
-
501
- return $term[ $id ] = false;
502
  }
503
 
504
  /**
505
- * Return custom field post meta data.
506
- * Memoizes the return value.
 
 
 
 
 
 
 
 
507
  *
508
- * Return only the first value of custom field. Return false if field is
509
- * blank or not set.
 
 
 
 
 
 
 
 
 
510
  *
511
- * @since 2.0.0
512
- * @since 4.0.0 Deprecated
513
- * @deprecated
514
  *
515
- * @param string $field Custom field key.
516
- * @param int $post_id The post ID.
517
- * @return mixed|boolean Return value or false on failure.
 
518
  */
519
- public function get_custom_field( $field, $post_id = null ) {
520
 
521
  $tsf = \the_seo_framework();
 
522
 
523
- $tsf->_deprecated_function( 'the_seo_framework()->get_custom_field()', '4.0.0', 'the_seo_framework()->get_post_meta_item()' );
524
-
525
- // If field is falsesque, get_post_meta() will return an array.
526
- if ( ! $field )
527
  return false;
528
 
529
- static $field_cache = [];
 
530
 
531
- if ( isset( $field_cache[ $field ][ $post_id ] ) )
532
- return $field_cache[ $field ][ $post_id ];
533
-
534
- if ( empty( $post_id ) )
535
- $post_id = $tsf->get_the_real_ID();
536
 
537
- $custom_field = \get_post_meta( $post_id, $field, true );
538
 
539
- // If custom field is empty, empty cache..
540
- if ( empty( $custom_field ) )
541
- $field_cache[ $field ][ $post_id ] = '';
 
542
 
543
- // Render custom field, slashes stripped, sanitized if string
544
- $field_cache[ $field ][ $post_id ] = \is_array( $custom_field ) ? \stripslashes_deep( $custom_field ) : stripslashes( $custom_field );
545
 
546
- return $field_cache[ $field ][ $post_id ];
547
  }
548
 
549
  /**
550
- * Returns image URL suitable for Schema items.
551
  *
552
- * These are images that are strictly assigned to the Post or Page, fallbacks are omitted.
553
- * Themes should complement these. If not, then Open Graph should at least complement these.
554
- * If that's not even true, then I don't know what happens. But then you're
555
- * in a grey area... @TODO make images optional for Schema?
556
  *
557
- * @since 2.9.3
558
- * @since 3.2.2 No longer relies on the query.
559
- * @since 4.0.0 Deprecated.
560
  * @deprecated
561
  *
562
- * @param int|string $id The page, post, product or term ID.
563
- * @param bool $singular Whether the ID is singular or archival.
564
- * @return string|array $url The Schema.org safe image.
565
  */
566
- public function get_schema_image( $id = 0, $singular = false ) {
567
-
568
- $tsf = \the_seo_framework();
569
-
570
- $tsf->_deprecated_function( 'the_seo_framework()->get_schema_image()', '4.0.0', 'the_seo_framework()->get_safe_schema_image()' );
571
-
572
- if ( ! $singular ) return '';
573
-
574
- return $tsf->get_safe_schema_image( $id ?: null, false );
575
  }
576
 
577
  /**
578
- * Returns social image URL.
579
  *
580
- * @since 2.9.0
581
- * @since 3.0.6 Added attachment page compatibility.
582
- * @since 3.2.2 Now skips the singular meta images on archives.
583
- * @since 4.0.0 Deprecated.
584
- * @deprecated
585
  *
586
- * @param array $args The image arguments.
587
- * @return string The social image.
588
  */
589
- public function get_social_image( $args = [] ) {
590
-
591
- $tsf = \the_seo_framework();
592
-
593
- $tsf->_deprecated_function( 'the_seo_framework()->get_social_image()', '4.0.0', 'the_seo_framework()->get_image_from_cache()' );
594
-
595
- if ( isset( $args['post_id'] ) && $args['post_id'] ) {
596
- $image = current( $tsf->get_image_details( [ 'id' => $args['post_id'] ], true ) );
597
- } else {
598
- $image = current( $tsf->get_image_details( null, true ) );
599
- }
600
-
601
- return isset( $image['url'] ) ? $image['url'] : '';
602
  }
603
 
604
  /**
605
- * Returns unescaped HomePage settings image URL from post ID input.
606
  *
607
- * @since 2.9.0
608
- * @since 2.9.4 Now converts URL scheme.
609
- * @since 4.0.0 Deprecated.
610
- * @deprecated
611
  *
612
- * @param int $id The post ID.
613
- * @return string The unescaped HomePage social image URL.
614
  */
615
- public function get_social_image_url_from_home_meta( $id = 0 ) {
616
-
617
- $tsf = \the_seo_framework();
618
-
619
- $tsf->_deprecated_function( 'the_seo_framework()->get_social_image_url_from_home_meta()', '4.0.0', "the_seo_framework()->get_option( 'homepage_social_image_url' )" );
620
-
621
- if ( false === $tsf->is_front_page_by_id( $id ) )
622
- return '';
623
-
624
- $src = $tsf->get_option( 'homepage_social_image_url' );
625
-
626
- if ( ! $src )
627
- return '';
628
-
629
- if ( $src && $tsf->matches_this_domain( $src ) )
630
- $src = $tsf->set_preferred_url_scheme( $src );
631
-
632
- return $src;
633
  }
634
 
635
  /**
636
- * Returns unescaped Post settings image URL from post ID input.
637
  *
638
- * @since 2.8.0
639
- * @since 2.9.0 1. The second parameter now works.
640
- * 2. Fallback image ID has been removed.
641
- * @since 2.9.4 Now converts URL scheme.
642
- * @since 4.0.0 Deprecated.
643
- * @deprecated
644
  *
645
- * @param int $id The post ID. Required.
646
- * @return string The unescaped social image URL.
 
647
  */
648
- public function get_social_image_url_from_post_meta( $id ) {
649
-
650
- $tsf = \the_seo_framework();
651
-
652
- $tsf->_deprecated_function( 'the_seo_framework()->get_social_image_url_from_post_meta()', '4.0.0', "the_seo_framework()->get_post_meta_item( '_social_image_url' )" );
653
-
654
- $src = $id ? $tsf->get_post_meta_item( '_social_image_url', $id ) : '';
655
-
656
- if ( ! $src )
657
- return '';
658
-
659
- if ( $src && $tsf->matches_this_domain( $src ) )
660
- $src = $tsf->set_preferred_url_scheme( $src );
661
-
662
- return $src;
663
  }
664
 
665
  /**
666
- * Returns unescaped URL from options input.
 
667
  *
668
- * @since 2.8.2
669
- * @since 2.9.4 : 1. Now converts URL scheme.
670
- * 2. $set_og_dimensions now works.
671
- * @since 4.0.0 Deprecated
672
- * @deprecated
673
  *
674
- * @return string The unescaped social image fallback URL.
 
675
  */
676
- public function get_social_image_url_from_seo_settings() {
677
-
678
- $tsf = \the_seo_framework();
679
-
680
- $tsf->_deprecated_function( 'the_seo_framework()->get_social_image_url_from_seo_settings()', '4.0.0', "the_seo_framework()->get_option( 'social_image_fb_url' )" );
681
-
682
- $src = $tsf->get_option( 'social_image_fb_url' );
683
-
684
- if ( $src && $tsf->matches_this_domain( $src ) )
685
- $src = $tsf->set_preferred_url_scheme( $src );
686
-
687
- return $src;
688
  }
689
 
690
  /**
691
- * Fetches image from post thumbnail.
 
692
  *
693
- * @since 2.9.0
694
- * @since 2.9.3 Now supports 4K.
695
- * @since 2.9.4 Now converts URL scheme.
696
- * @since 4.0.0 Deprecated.
697
- * @deprecated
698
  *
699
- * @param int $id The post ID. Required.
700
- * @return string The social image URL.
701
  */
702
- public function get_social_image_url_from_post_thumbnail( $id ) {
703
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_social_image_url_from_post_thumbnail()', '4.0.0' );
704
- return \The_SEO_Framework\Builders\Images::get_featured_image_details(
705
- [
706
- 'id' => $id,
707
- 'taxonomy' => '',
708
- ]
709
- )->current()['url'];
710
  }
711
 
712
  /**
713
- * Returns the social image URL from an attachment page.
 
714
  *
715
- * @since 3.0.6
716
- * @since 4.0.0 Deprecated.
717
- * @deprecated
718
  *
719
- * @param int $id The post ID. Required.
720
- * @return string The attachment URL.
721
  */
722
- public function get_social_image_url_from_attachment( $id ) {
723
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_social_image_url_from_attachment()', '4.0.0' );
724
- return \The_SEO_Framework\Builders\Images::get_attachment_image_details(
725
- [
726
- 'id' => $id,
727
- 'taxonomy' => '',
728
- ]
729
- )->current()['url'];
730
  }
731
 
732
  /**
733
- * Fetches images id's from WooCommerce gallery
734
  *
735
- * @since 2.5.0
736
- * @since 4.0.0 Deprecated.
737
- * @deprecated
738
  *
739
- * @return array The image URL's.
 
740
  */
741
- public function get_image_from_woocommerce_gallery() {
742
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_image_from_woocommerce_gallery()', '4.0.0' );
743
-
744
- $ids = [];
745
-
746
- if ( \function_exists( '\\The_SEO_Framework\\_get_product_gallery_image_details' ) ) {
747
- foreach ( \The_SEO_Framework\_get_product_gallery_image_details() as $details ) {
748
- $ids[] = $details['id'];
749
- }
750
- }
751
-
752
- return $ids;
753
  }
754
 
755
  /**
756
- * Returns header image URL.
757
- * Also sets image dimensions. Falls back to current post ID for index.
758
  *
759
- * @since 2.7.0
760
- * @since 3.0.0 Now sets preferred canonical URL scheme.
761
- * @since 4.0.0 Deprecated.
762
- * @deprecated
763
  *
764
- * @return string The header image URL, not escaped.
 
765
  */
766
- public function get_header_image() {
767
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_header_image()', '4.0.0' );
768
- return \The_SEO_Framework\Builders\Images::get_theme_header_image_details()->current()['url'];
769
  }
770
 
771
  /**
772
- * Fetches site icon brought in WordPress 4.3
773
  *
774
- * @since 2.8.0
775
- * @since 3.0.0 : Now sets preferred canonical URL scheme.
776
- * @since 4.0.0 Deprecated.
777
- * @deprecated
778
  *
779
- * @param string|int $size The icon size, accepts 'full' and pixel values.
780
- * @return string URL site icon, not escaped.
781
  */
782
- public function get_site_icon( $size = 'full' ) {
783
-
784
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_site_icon()', '4.0.0' );
785
-
786
- $size = \is_string( $size ) ? $size : 'full';
787
-
788
- return \The_SEO_Framework\Builders\Images::get_site_icon_image_details( null, $size )->current()['url'];
789
  }
790
 
791
  /**
792
- * Fetches site logo brought in WordPress 4.5
 
793
  *
794
- * @since 2.8.0
795
- * @since 3.0.0 Now sets preferred canonical URL scheme.
796
- * @since 3.1.2 Now returns empty when it's deemed too small, and OG images are set.
797
- * @since 4.0.0 Deprecated.
798
- * @deprecated
799
  *
800
- * @return string URL site logo, not escaped.
 
801
  */
802
- public function get_site_logo() {
803
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_site_logo()', '4.0.0' );
804
- return \The_SEO_Framework\Builders\Images::get_site_logo_image_details()->current()['url'];
805
  }
806
 
807
  /**
808
- * Sanitizeses ID. Mainly removing spaces and coding characters.
809
  *
810
- * Unlike sanitize_key(), it doesn't alter the case nor applies filters.
811
- * It also maintains the '@' character.
812
- *
813
- * @see WordPress Core sanitize_key()
814
  * @since 3.1.0
815
- * @since 4.0.0 1. Now allows square brackets.
816
- * 2. Deprecated.
817
- * @deprecated
818
  *
819
- * @param string $id The unsanitized ID.
820
- * @return string The sanitized ID.
821
  */
822
- public function sanitize_field_id( $id ) {
823
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->sanitize_field_id()', '4.0.0', 'the_seo_framework()->s_field_id()' );
824
- return preg_replace( '/[^a-zA-Z0-9\[\]_\-@]/', '', $id );
825
  }
826
 
827
  /**
828
- * Checks a theme's support for title-tag.
 
 
829
  *
830
  * @since 2.6.0
831
- * @since 3.1.0 Removed caching
832
- * @since 4.0.0 Deprecated.
833
- * @deprecated
834
  *
835
- * @return bool
 
 
836
  */
837
- public function current_theme_supports_title_tag() {
838
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->sanitize_field_id()', '4.0.0' );
839
- return \the_seo_framework()->detect_theme_support( 'title-tag' );
840
  }
841
 
842
  /**
843
- * Determines if the current theme supports the custom logo addition.
844
- *
845
- * @since 2.8.0
846
- * @since 3.1.0 : 1. No longer checks for WP version 4.5+.
847
- * 2. No longer uses caching.
848
- * @since 4.0.0 Deprecated.
849
- * @deprecated
850
  *
851
- * @return bool
 
 
 
 
 
 
 
 
 
852
  */
853
- public function can_use_logo() {
854
- \the_seo_framework()->_deprecated_function( 'the_seo_framework()->can_use_logo()', '4.0.0' );
855
- return \the_seo_framework()->detect_theme_support( 'custom-logo' );
856
  }
857
 
858
  /**
859
- * Detect if the current screen type is a page or taxonomy.
860
- * Memoizes the return value.
861
- *
862
- * @since 2.3.1
863
- * @since 4.1.0 Deprecated.
864
- * @deprecated
865
- *
866
- * @param string $type the Screen type
867
- * @return bool true if post type is a page or post
 
 
 
868
  */
869
- public function is_post_type_page( $type ) {
870
-
871
- static $is_page = [];
872
-
873
- if ( isset( $is_page[ $type ] ) )
874
- return $is_page[ $type ];
875
-
876
- $tsf = \the_seo_framework();
877
-
878
- $tsf->_deprecated_function( 'the_seo_framework()->is_post_type_page()', '4.1.0' );
879
-
880
- $post_page = (array) \get_post_types( [ 'public' => true ] );
881
-
882
- foreach ( $post_page as $screen ) {
883
- if ( $type === $screen ) {
884
- return $is_page[ $type ] = true;
885
- }
886
- }
887
-
888
- return $is_page[ $type ] = false;
889
  }
890
 
891
  /**
892
- * Checks whether the taxonomy is public and rewritable.
893
  *
894
- * @since 3.1.0
895
- * @since 4.1.0 1: Now returns true on all public taxonomies; not just public taxonomies with rewrite capabilities.
896
- * 2: Deprecated.
897
- * @deprecated
898
- *
899
- * @param string $taxonomy The taxonomy name.
900
- * @return bool
 
 
 
 
 
901
  */
902
- public function is_taxonomy_public( $taxonomy = '' ) {
903
-
904
- $tsf = \the_seo_framework();
905
-
906
- $tsf->_deprecated_function( 'the_seo_framework()->is_taxonomy_public()', '4.1.0', 'the_seo_framework()->is_taxonomy_supported()' );
907
-
908
- $taxonomy = $taxonomy ?: $tsf->get_current_taxonomy();
909
- if ( ! $taxonomy ) return false;
910
-
911
- $tax = \get_taxonomy( $taxonomy );
912
-
913
- if ( false === $tax ) return false;
914
 
915
- return ! empty( $tax->public );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  }
917
 
918
  /**
919
- * Return option from the options table and cache result.
920
- * Memoizes the return value.
921
  *
922
- * Values pulled from the database are cached on each request, so a second request for the same value won't cause a
923
- * second DB interaction.
924
  *
925
- * @since 2.0.0
926
- * @since 2.8.2 No longer decodes entities on request.
927
- * @since 3.1.0 Now uses the filterable call when caching is disabled.
928
- * @since 4.1.0 Deprecated.
929
- * @thanks StudioPress (http://www.studiopress.com/) for some code.
930
- * @deprecated
931
  *
932
- * @param string $key Option name.
933
- * @param string $setting Optional. Settings field name. Eventually defaults to null if not passed as an argument.
934
- * @param boolean $use_cache Optional. Whether to use the cache value or not.
935
- * @return mixed The value of this $key in the database. Empty string on failure.
 
936
  */
937
- public function the_seo_framework_get_option( $key, $setting = null, $use_cache = true ) {
938
-
939
- if ( ! $setting ) return '';
940
-
941
- $tsf = \the_seo_framework();
942
-
943
- $tsf->_deprecated_function( 'the_seo_framework()->the_seo_framework_get_option()', '4.1.0', 'the_seo_framework()->get_option()' );
944
-
945
- if ( ! $use_cache ) {
946
- $options = $tsf->get_all_options( $setting, true );
947
- return isset( $options[ $key ] ) ? \stripslashes_deep( $options[ $key ] ) : '';
948
- }
949
-
950
- static $cache = [];
951
-
952
- if ( ! isset( $cache[ $setting ] ) )
953
- $cache[ $setting ] = \stripslashes_deep( $tsf->get_all_options( $setting ) );
954
-
955
- return isset( $cache[ $setting ][ $key ] ) ? $cache[ $setting ][ $key ] : '';
956
  }
957
-
958
  /**
959
- * Returns the homepage tagline from option or bloginfo, when set.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  *
961
- * @since 3.0.4
962
- * @since 4.0.0 Added caching.
963
- * @since 4.1.0 Deprecated.
964
- * @uses $this->get_blogdescription(), this method already trims.
965
- * @deprecated
966
  *
967
- * @return string The trimmed tagline.
 
968
  */
969
- public function get_home_page_tagline() {
970
-
971
- $tsf = \the_seo_framework();
 
972
 
973
- $tsf->_deprecated_function( 'the_seo_framework()->get_home_page_tagline()', '4.1.0', 'the_seo_framework()->get_home_title_additions()' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
974
 
975
- return $tsf->get_home_title_additions();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
976
  }
977
 
978
  /**
979
- * Cached WordPress permalink structure settings.
980
  *
981
- * @since 2.6.0
982
- * @since 3.1.0 Removed caching.
983
- * @since 4.1.0 Deprecated.
984
- * @deprecated
985
  *
986
- * @return string permalink structure.
 
 
987
  */
988
- public function permalink_structure() {
989
-
990
- $tsf = \the_seo_framework();
991
-
992
- $tsf->_deprecated_function( 'the_seo_framework()->permalink_structure()', '4.1.0', "get_option( 'permalink_structure' )" );
993
-
994
- return \get_option( 'permalink_structure' );
995
  }
996
  }
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
33
  * @since 2.8.0
34
  * @since 3.1.0 Removed all methods deprecated in 3.0.0.
35
  * @since 4.0.0 Removed all methods deprecated in 3.1.0.
36
+ * @since 4.1.4 Removed all methods deprecated in 4.0.0.
37
  * @ignore
38
  */
39
  final class Deprecated {
40
 
41
  /**
42
+ * Detect if the current screen type is a page or taxonomy.
43
+ * Memoizes the return value.
44
  *
45
+ * @since 2.3.1
46
+ * @since 4.1.0 Deprecated.
47
  * @deprecated
48
  *
49
+ * @param string $type the Screen type
50
+ * @return bool true if post type is a page or post
51
  */
52
+ public function is_post_type_page( $type ) {
53
+
54
+ static $is_page = [];
55
+
56
+ if ( isset( $is_page[ $type ] ) )
57
+ return $is_page[ $type ];
58
 
59
  $tsf = \the_seo_framework();
 
60
 
61
+ $tsf->_deprecated_function( 'the_seo_framework()->is_post_type_page()', '4.1.0' );
62
+
63
+ $post_page = (array) \get_post_types( [ 'public' => true ] );
64
+
65
+ foreach ( $post_page as $screen ) {
66
+ if ( $type === $screen ) {
67
+ return $is_page[ $type ] = true;
68
+ }
69
+ }
70
+
71
+ return $is_page[ $type ] = false;
72
  }
73
 
74
  /**
75
+ * Checks whether the taxonomy is public and rewritable.
76
  *
77
+ * @since 3.1.0
78
+ * @since 4.1.0 1: Now returns true on all public taxonomies; not just public taxonomies with rewrite capabilities.
79
+ * 2: Deprecated.
80
  * @deprecated
81
  *
82
+ * @param string $taxonomy The taxonomy name.
83
+ * @return bool
84
  */
85
+ public function is_taxonomy_public( $taxonomy = '' ) {
86
 
87
  $tsf = \the_seo_framework();
 
88
 
89
+ $tsf->_deprecated_function( 'the_seo_framework()->is_taxonomy_public()', '4.1.0', 'the_seo_framework()->is_taxonomy_supported()' );
90
+
91
+ $taxonomy = $taxonomy ?: $tsf->get_current_taxonomy();
92
+ if ( ! $taxonomy ) return false;
93
+
94
+ $tax = \get_taxonomy( $taxonomy );
95
+
96
+ if ( false === $tax ) return false;
97
 
98
+ return ! empty( $tax->public );
 
 
99
  }
100
 
101
  /**
102
+ * Return option from the options table and cache result.
103
+ * Memoizes the return value.
104
  *
105
+ * Values pulled from the database are cached on each request, so a second request for the same value won't cause a
106
+ * second DB interaction.
107
+ *
108
+ * @since 2.0.0
109
+ * @since 2.8.2 No longer decodes entities on request.
110
+ * @since 3.1.0 Now uses the filterable call when caching is disabled.
111
+ * @since 4.1.0 Deprecated.
112
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
113
  * @deprecated
114
  *
115
+ * @param string $key Option name.
116
+ * @param string $setting Optional. Settings field name. Eventually defaults to null if not passed as an argument.
117
+ * @param boolean $use_cache Optional. Whether to use the cache value or not.
118
+ * @return mixed The value of this $key in the database. Empty string on failure.
119
  */
120
+ public function the_seo_framework_get_option( $key, $setting = null, $use_cache = true ) {
121
+
122
+ if ( ! $setting ) return '';
123
 
124
  $tsf = \the_seo_framework();
 
125
 
126
+ $tsf->_deprecated_function( 'the_seo_framework()->the_seo_framework_get_option()', '4.1.0', 'the_seo_framework()->get_option()' );
127
 
128
+ if ( ! $use_cache ) {
129
+ $options = $tsf->get_all_options( $setting, true );
130
+ return isset( $options[ $key ] ) ? \stripslashes_deep( $options[ $key ] ) : '';
131
  }
 
132
 
133
+ static $cache = [];
134
+
135
+ if ( ! isset( $cache[ $setting ] ) )
136
+ $cache[ $setting ] = \stripslashes_deep( $tsf->get_all_options( $setting ) );
137
+
138
+ return isset( $cache[ $setting ][ $key ] ) ? $cache[ $setting ][ $key ] : '';
139
  }
140
 
141
  /**
142
+ * Returns the homepage tagline from option or bloginfo, when set.
143
  *
144
+ * @since 3.0.4
145
+ * @since 4.0.0 Added caching.
146
+ * @since 4.1.0 Deprecated.
147
+ * @uses $this->get_blogdescription(), this method already trims.
148
  * @deprecated
149
  *
150
+ * @return string The trimmed tagline.
151
  */
152
+ public function get_home_page_tagline() {
153
 
154
  $tsf = \the_seo_framework();
 
155
 
156
+ $tsf->_deprecated_function( 'the_seo_framework()->get_home_page_tagline()', '4.1.0', 'the_seo_framework()->get_home_title_additions()' );
157
 
158
+ return $tsf->get_home_title_additions();
 
 
159
  }
160
 
161
  /**
162
+ * Cached WordPress permalink structure settings.
163
  *
164
+ * @since 2.6.0
165
+ * @since 3.1.0 Removed caching.
166
+ * @since 4.1.0 Deprecated.
167
  * @deprecated
168
+ *
169
+ * @return string permalink structure.
170
  */
171
+ public function permalink_structure() {
172
+
173
+ $tsf = \the_seo_framework();
174
+
175
+ $tsf->_deprecated_function( 'the_seo_framework()->permalink_structure()', '4.1.0', "get_option( 'permalink_structure' )" );
176
+
177
+ return \get_option( 'permalink_structure' );
178
  }
179
 
180
  /**
181
+ * Appends given query to given URL.
 
182
  *
183
+ * @since 3.0.0
184
+ * @since 3.1.0 Now uses parse_str and add_query_arg, preventing duplicated entries.
185
+ * @since 4.1.4 Deprecated silently.
186
+ * @since 4.2.0 Hard deprecation.
187
  * @deprecated
188
  *
189
+ * @param string $url A fully qualified URL.
190
+ * @param string $query A fully qualified query taken from parse_url( $url, PHP_URL_QUERY );
191
+ * @return string A fully qualified URL with appended $query.
192
  */
193
+ public function append_php_query( $url, $query = '' ) {
 
194
  $tsf = \the_seo_framework();
195
+ // $tsf->_deprecated_function( 'the_seo_framework()->append_php_query()', '4.2.0', 'the_seo_framework()->append_url_query()' );
196
+ return $tsf->append_url_query( $url, $query );
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
 
199
  /**
200
+ * Generates front-end HTMl output.
201
  *
202
+ * @since 4.0.5
203
+ * @since 4.1.4 Deprecated silently.
204
+ * @since 4.2.0 Hard deprecation.
205
  * @deprecated
206
  *
207
+ * @return string The HTML output.
 
 
 
208
  */
209
+ public function get_html_output() {
210
 
211
  $tsf = \the_seo_framework();
212
 
213
+ // $tsf->_deprecated_function( 'the_seo_framework()->get_html_output()', '4.2.0' );
214
+
215
+ $robots = $tsf->robots();
216
+
217
+ /** @since 4.0.4 Added as WP 5.3 patch. */
218
+ $tsf->set_timezone( 'UTC' );
219
+
220
+ /**
221
+ * @since 2.6.0
222
+ * @param string $before The content before the SEO output.
223
+ */
224
+ $before = (string) \apply_filters( 'the_seo_framework_pre', '' );
225
+
226
+ $before_legacy = $tsf->get_legacy_header_filters_output( 'before' );
227
+
228
+ // Limit processing and redundant tags on 404 and search.
229
+ if ( $tsf->is_search() ) :
230
+ $output = $tsf->og_locale()
231
+ . $tsf->og_type()
232
+ . $tsf->og_title()
233
+ . $tsf->og_url()
234
+ . $tsf->og_sitename()
235
+ . $tsf->theme_color()
236
+ . $tsf->shortlink()
237
+ . $tsf->canonical()
238
+ . $tsf->paged_urls()
239
+ . $tsf->google_site_output()
240
+ . $tsf->bing_site_output()
241
+ . $tsf->yandex_site_output()
242
+ . $tsf->baidu_site_output()
243
+ . $tsf->pint_site_output();
244
+ elseif ( $tsf->is_404() ) :
245
+ $output = $tsf->theme_color()
246
+ . $tsf->google_site_output()
247
+ . $tsf->bing_site_output()
248
+ . $tsf->yandex_site_output()
249
+ . $tsf->baidu_site_output()
250
+ . $tsf->pint_site_output();
251
+ elseif ( $tsf->is_query_exploited() ) :
252
+ // aqp = advanced query protection
253
+ $output = '<meta name="tsf:aqp" value="1" />' . PHP_EOL;
254
+ else :
255
+ // Inefficient concatenation is inefficient. Improve this?
256
+ $output = $tsf->the_description()
257
+ . $tsf->og_image()
258
+ . $tsf->og_locale()
259
+ . $tsf->og_type()
260
+ . $tsf->og_title()
261
+ . $tsf->og_description()
262
+ . $tsf->og_url()
263
+ . $tsf->og_sitename()
264
+ . $tsf->facebook_publisher()
265
+ . $tsf->facebook_author()
266
+ . $tsf->facebook_app_id()
267
+ . $tsf->article_published_time()
268
+ . $tsf->article_modified_time()
269
+ . $tsf->twitter_card()
270
+ . $tsf->twitter_site()
271
+ . $tsf->twitter_creator()
272
+ . $tsf->twitter_title()
273
+ . $tsf->twitter_description()
274
+ . $tsf->twitter_image()
275
+ . $tsf->theme_color()
276
+ . $tsf->shortlink()
277
+ . $tsf->canonical()
278
+ . $tsf->paged_urls()
279
+ . $tsf->ld_json()
280
+ . $tsf->google_site_output()
281
+ . $tsf->bing_site_output()
282
+ . $tsf->yandex_site_output()
283
+ . $tsf->baidu_site_output()
284
+ . $tsf->pint_site_output();
285
+ endif;
286
+
287
+ $after_legacy = $tsf->get_legacy_header_filters_output( 'after' );
288
+
289
+ /**
290
+ * @since 2.6.0
291
+ * @param string $after The content after the SEO output.
292
+ */
293
+ $after = (string) \apply_filters( 'the_seo_framework_pro', '' );
294
+
295
+ /** @since 4.0.4 Added as WP 5.3 patch. */
296
+ $tsf->reset_timezone();
297
+
298
+ return "{$robots}{$before}{$before_legacy}{$output}{$after_legacy}{$after}";
299
  }
300
 
301
  /**
302
+ * Generates the `noindex` robots meta code array from arguments.
303
  *
304
+ * This method is tailor-made for everything that relies on the noindex-state, as it's
305
+ * a very controlling and powerful feature.
306
  *
307
+ * Note that the home-as-blog page can be used for this method.
308
+ *
309
+ * We deprecated this because in the real world, it barely mattered. We'd much rather
310
+ * have a proper and predictable API.
311
+ *
312
+ * @since 4.0.0
313
+ * @since 4.1.0 Now uses the new taxonomy robots settings.
314
+ * @since 4.1.4 Soft deprecated. Use 'robots_meta' instead.
315
+ * @since 4.2.0 Hard deprecation.
316
  * @deprecated
317
  *
318
+ * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
319
+ * @param int <bit> $ignore The ignore level. {
320
+ * 0 = 0b00: Ignore nothing.
321
+ * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
322
+ * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
323
+ * 3 = 0b11: Ignore protection and post/term setting.
324
+ * }
325
+ * @return bool Whether noindex is set or not
326
  */
327
+ public function is_robots_meta_noindex_set_by_args( $args, $ignore = 0b00 ) {
328
+ $tsf = \the_seo_framework();
329
+ // $tsf->_deprecated_function( 'the_seo_framework()->is_robots_meta_noindex_set_by_args()', '4.2.0', 'the_seo_framework()->robots_meta()' );
330
+ $meta = $tsf->generate_robots_meta( $args, null, $ignore );
331
+ return isset( $meta['noindex'] ) && 'noindex' === $meta['noindex'];
332
  }
333
 
334
  /**
335
+ * Returns the `noindex`, `nofollow`, `noarchive` robots meta code array.
 
 
 
 
 
336
  *
337
+ * @since 2.2.2
338
+ * @since 2.2.4 Added robots SEO settings check.
339
+ * @since 2.2.8 Added check for empty archives.
340
+ * @since 2.8.0 Added check for protected/private posts.
341
+ * @since 3.0.0 : 1. Removed noodp.
342
+ * 2. Improved efficiency by grouping if statements.
343
+ * @since 3.1.0 : 1. Simplified statements, often (not always) speeding things up.
344
+ * 2. Now checks for wc_shop and blog types for pagination.
345
+ * 3. Removed noydir.
346
+ * @since 4.0.0 : 1. Now tests for qubit metadata.
347
+ * 2. Added custom query support.
348
+ * 3. Added two parameters.
349
+ * @since 4.0.2 : 1. Added new copyright directive tags.
350
+ * 2. Now strictly parses the validity of robots directives via a boolean check.
351
+ * @since 4.0.3 : 1. Changed `max_snippet_length` to `max_snippet`
352
+ * 2. Changed the copyright directive's spacer from `=` to `:`.
353
+ * @since 4.0.5 : 1. Removed copyright directive bug workaround. <https://kb.theseoframework.com/kb/why-is-max-image-preview-none-purged/>
354
+ * 2. Now sets noindex and nofollow when queries are exploited (requires option enabled).
355
+ * @since 4.1.4 Deprecated silently. Use generate_robots_meta() instead.
356
+ * @since 4.2.0 Hard deprecation.
357
+ *
358
+ * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
359
+ * @param int <bit> $ignore The ignore level. {
360
+ * 0 = 0b00: Ignore nothing.
361
+ * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
362
+ * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
363
+ * 3 = 0b11: Ignore protection and post/term setting.
364
+ * }
365
+ * @return array Only values actualized for display: {
366
+ * string index : string value
367
+ * }
368
  */
369
+ public function robots_meta( $args = null, $ignore = 0b00 ) {
370
+ $tsf = \the_seo_framework();
371
+ // $tsf->_deprecated_function( 'the_seo_framework()->robots_meta()', '5.0.0', 'the_seo_framework()->generate_robots_meta()' );
372
+ return $tsf->generate_robots_meta( $args, null, $ignore );
373
  }
374
 
375
  /**
376
+ * Determines whether to add a line within robots based by plugin detection, or sitemap output option.
 
377
  *
378
  * @since 2.6.0
379
+ * @since 2.8.0 Added check_option parameter.
380
+ * @since 2.9.0 Now also checks for subdirectory installations.
381
+ * @since 2.9.2 Now also checks for permalinks.
382
+ * @since 2.9.3 Now also checks for sitemap_robots option.
383
+ * @since 3.1.0 Removed Jetpack's sitemap check -- it's no longer valid.
384
+ * @since 4.0.0 : 1. Now uses has_robots_txt()
385
+ * : 2. Now uses the get_robots_txt_url() to determine validity.
386
+ * FIXME This method also checks for file existence (and location...), but is only used when the file definitely doesn't exist.
387
+ * @since 4.1.4 Soft deprecated.
388
+ * @since 4.2.0 Hard deprecation.
389
  * @deprecated
390
  *
391
+ * @param bool $check_option Whether to check for sitemap option.
392
+ * @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
393
  */
394
+ public function can_do_sitemap_robots( $check_option = true ) {
395
 
396
+ $tsf = \the_seo_framework();
397
 
398
+ // $tsf->_deprecated_function( 'the_seo_framework()->is_robots_meta_noindex_set_by_args()', '4.2.0' );
399
 
400
+ if ( $check_option ) {
401
+ if ( ! $tsf->get_option( 'sitemaps_output' )
402
+ || ! $tsf->get_option( 'sitemaps_robots' ) )
403
+ return false;
404
+ }
405
 
406
+ return ! $tsf->has_robots_txt() && \strlen( $tsf->get_robots_txt_url() );
407
  }
408
 
409
  /**
410
+ * Setting nav tab wrappers.
411
+ * Outputs Tabs and settings content.
412
+ *
413
+ * @since 2.3.6
414
+ * @since 2.6.0 Refactored.
415
+ * @since 3.1.0 Now prefixes the IDs.
416
+ * @since 4.0.0 Deprecated third parameter, silently.
417
+ * @since 4.1.4 Deprecated silently. Use `\The_SEO_Framework\Bridges\SeoSettings::_nav_tab_wrapper()` instead.
418
+ * @since 4.2.0 Hard deprecation.
419
  * @deprecated
420
  *
421
+ * @param string $id The nav-tab ID
422
+ * @param array $tabs The tab content {
423
+ * string tab ID => array : {
424
+ * string name : Tab name.
425
+ * callable callback : Output function.
426
+ * string dashicon : The dashicon to use.
427
+ * mixed args : Optional callback function args.
428
+ * }
429
+ * }
430
+ * @param null $depr Deprecated.
431
+ * @param bool $use_tabs Whether to output tabs, only works when $tabs count is greater than 1.
432
  */
433
+ public function nav_tab_wrapper( $id, $tabs = [], $depr = null, $use_tabs = true ) {
434
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->nav_tab_wrapper()', '4.2.0', '\The_SEO_Framework\Bridges\PostSettings::_nav_tab_wrapper' );
435
+ \The_SEO_Framework\Bridges\SeoSettings::_nav_tab_wrapper( $id, $tabs, $use_tabs );
436
  }
437
 
438
  /**
439
+ * Outputs in-post flex navigational wrapper and its content.
440
  *
441
+ * @since 2.9.0
442
+ * @since 3.0.0 Converted to view.
443
+ * @since 4.0.0 Deprecated third parameter, silently.
444
+ * @since 4.1.4 Deprecated silently. Use `\The_SEO_Framework\Bridges\PostSettings()` instead.
445
+ * @since 4.2.0 Hard deprecation.
446
  * @deprecated
447
+ *
448
+ * @param string $id The nav-tab ID
449
+ * @param array $tabs The tab content {
450
+ * string tab ID => array : {
451
+ * string name : Tab name.
452
+ * callable callback : Output function.
453
+ * string dashicon : The dashicon to use.
454
+ * mixed args : Optional callback function args.
455
+ * }
456
+ * }
457
+ * @param null $_depr Deprecated.
458
+ * @param bool $use_tabs Whether to output tabs, only works when $tabs count is greater than 1.
459
  */
460
+ public function inpost_flex_nav_tab_wrapper( $id, $tabs = [], $_depr = null, $use_tabs = true ) {
461
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->inpost_flex_nav_tab_wrapper()', '4.2.0', '\The_SEO_Framework\Bridges\PostSettings::_flex_nav_tab_wrapper' );
462
+ \The_SEO_Framework\Bridges\PostSettings::_flex_nav_tab_wrapper( $id, $tabs, $use_tabs );
463
  }
464
 
465
  /**
466
+ * Returns social image uploader form button.
467
+ * Also registers additional i18n strings for JS.
468
  *
469
+ * @since 2.8.0
470
+ * @since 3.1.0 No longer prepares media l10n data.
471
+ * @since 4.0.0 Now adds a media preview dispenser.
472
+ * @since 4.1.2 No longer adds a redundant title to the selection button.
473
+ * @since 4.1.4 Deprecated. Use `get_image_uploader_form()` instead.
474
+ * @since 4.2.0 Hard deprecation.
 
475
  * @deprecated
476
  *
477
+ * @param string $input_id Required. The HTML input id to pass URL into.
478
+ * @return string The image uploader button.
479
  */
480
+ public function get_social_image_uploader_form( $input_id ) {
481
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_social_image_uploader_form()', '4.2.0', 'The_SEO_Framework\Interpreters\Form::get_image_uploader_form()' );
482
+ return \The_SEO_Framework\Interpreters\Form::get_image_uploader_form( [ 'id' => $input_id ] );
483
  }
484
 
485
  /**
486
+ * Returns logo uploader form buttons.
487
+ * Also registers additional i18n strings for JS.
488
  *
489
+ * @since 3.0.0
490
+ * @since 3.1.0 No longer prepares media l10n data.
491
+ * @since 4.0.0 Now adds a media preview dispenser.
492
+ * @since 4.1.4 Deprecated silently. Use `get_image_uploader_form()` instead.
493
+ * @since 4.2.0 Hard deprecation.
494
  * @deprecated
495
+ *
496
+ * @param string $input_id Required. The HTML input id to pass URL into.
497
+ * @return string The image uploader button.
498
  */
499
+ public function get_logo_uploader_form( $input_id ) {
500
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_logo_uploader_form()', '4.2.0', 'The_SEO_Framework\Interpreters\Form::get_image_uploader_form()' );
501
+ return \The_SEO_Framework\Interpreters\Form::get_image_uploader_form( [
502
+ 'id' => $input_id,
503
+ 'data' => [
504
+ 'inputType' => 'logo',
505
+ 'width' => 512,
506
+ 'height' => 512,
507
+ 'minWidth' => 112,
508
+ 'minHeight' => 112,
509
+ 'flex' => true,
510
+ ],
511
+ 'i18n' => [
512
+ 'button_title' => '',
513
+ 'button_text' => \__( 'Select Logo', 'autodescription' ),
514
+ ],
515
+ ] );
516
  }
517
 
518
  /**
519
+ * Proportionate dimensions based on Width and Height.
520
+ * AKA Aspect Ratio.
521
  *
522
+ * @since 2.6.0
523
+ * @ignore Unused. The relying methods were yeeted off in 4.0.0.
524
+ * "We no longer automatically resize images when they’re deemed too large."
525
+ * @since 4.1.4 Deprecated silently. Marked for quick deletion.
526
+ * @TODO delete me, bypass deprecation? This method makes no sense to the outsider, anyway. -> 4.2.0
527
+ *
528
+ * @param int $i The dimension to resize.
529
+ * @param int $r1 The dimension that determines the ratio.
530
+ * @param int $r2 The dimension to proportionate to.
531
+ * @return int The proportional dimension, rounded.
532
  */
533
+ public function proportionate_dimensions( $i, $r1, $r2 ) {
534
+ return round( $i / ( $r1 / $r2 ) );
 
535
  }
536
 
537
  /**
538
+ * Returns the SEO Settings page URL.
539
  *
540
+ * @since 2.6.0
541
+ * @since 4.1.4 Deprecated silently. Use `get_seo_settings_page_url()` instead.
542
+ * @since 4.2.0 Hard deprecation.
 
543
  * @deprecated
 
544
  *
545
+ * @return string The escaped SEO Settings page URL.
546
  */
547
+ public function seo_settings_page_url() {
548
+ $tsf = \the_seo_framework();
549
+ // $tsf->_deprecated_function( 'the_seo_framework()->seo_settings_page_url()', '4.2.0', 'the_seo_framework()->get_seo_settings_page_url()' );
550
+ return $tsf->get_seo_settings_page_url();
551
  }
552
 
553
  /**
554
+ * Returns default user meta.
555
  *
556
+ * @since 3.0.0
557
+ * @since 4.1.4 Deprecated silently. Use `get_user_meta_defaults()` instead.
558
+ * @since 4.2.0 Hard deprecation.
 
 
 
559
  *
560
+ * @return array The default user meta index and values.
561
  */
562
+ public function get_default_user_data() {
563
+ $tsf = \the_seo_framework();
564
+ // $tsf->_deprecated_function( 'the_seo_framework()->get_default_user_data()', '4.2.0', 'the_seo_framework()->get_user_meta_defaults()' );
565
+ return $tsf->get_user_meta_defaults();
566
  }
567
 
568
  /**
569
+ * Fetches user SEO user meta data by name.
570
+ * Memoizes all meta data per $user_id.
571
  *
572
+ * If no $user_id is supplied, it will fetch the current logged in user ID.
573
+ * TODO supplement $default===null for $this->get_user_meta_defaults()[$option]?
574
+ *
575
+ * @since 2.7.0
576
+ * @since 3.0.0 1. Default is no longer cached.
577
+ * 2. Now always fallbacks to $default.
578
+ * 3. Added not-found cache.
579
+ * @since 4.1.4 Deprecated silently. Use `get_user_meta()` instead.
580
+ * @since 4.2.0 Hard deprecation.
581
+ *
582
+ * @param int $user_id The user ID. When empty, it will try to fetch the current user.
583
+ * @param string $option The option name.
584
+ * @param mixed $default The default value to return when the data doesn't exist.
585
+ * @return mixed The metadata value.
586
  */
587
+ public function get_user_option( $user_id = 0, $option = '', $default = null ) {
588
+ $tsf = \the_seo_framework();
589
+ // $tsf->_deprecated_function( 'the_seo_framework()->get_user_option()', '4.2.0', 'the_seo_framework()->get_user_meta_item()' );
590
+ return $tsf->get_user_meta_item( $user_id ?: $tsf->get_user_id(), $option ) ?: $default;
591
  }
592
 
593
  /**
594
+ * Returns current post author option.
595
  *
596
+ * @since 3.0.0
597
+ * @since 4.1.4 Silently deprecated. use `get_current_post_author_id()` instead.
598
+ * @since 4.2.0 Hard deprecation.
 
 
599
  *
600
+ * @param int $author_id The author ID. When empty, it will return $default.
601
+ * @param string $option The option name. When empty, it will return $default.
602
+ * @param mixed $default The default value to return when the data doesn't exist.
603
+ * @return mixed The metadata value
604
  */
605
+ public function get_author_option( $author_id, $option, $default = null ) {
606
+ $tsf = \the_seo_framework();
607
+ // $tsf->_deprecated_function( 'the_seo_framework()->get_author_option()', '4.2.0', 'the_seo_framework()->get_current_post_author_id()' );
608
+ return $tsf->get_user_meta_item( $option, $author_id ?: $tsf->get_current_post_author_id() ) ?: $default;
609
  }
610
 
611
  /**
612
+ * Returns current post author option.
 
 
 
613
  *
614
+ * @since 3.0.0
615
+ * @since 4.1.4 Silently deprecated. Use `get_current_post_author_meta_item()` instead.
616
+ * @since 4.2.0 Hard deprecation.
 
 
617
  *
618
+ * @param string $option The option name.
619
+ * @param mixed $default The default value to return when the data doesn't exist.
620
+ * @return mixed The metadata value
621
  */
622
+ public function get_current_author_option( $option, $default = null ) {
623
+ $tsf = \the_seo_framework();
624
+ // $tsf->_deprecated_function( 'the_seo_framework()->get_current_author_option()', '4.2.0', 'the_seo_framework()->get_current_post_author_meta_item()' );
625
+ return $tsf->get_current_post_author_meta_item( $option ) ?: $default;
626
  }
627
 
628
  /**
629
+ * Determines if the $post is the WooCommerce plugin shop page.
630
+ *
631
+ * @since 2.5.2
632
+ * @since 4.0.5 Now has a first parameter `$post`.
633
+ * @since 4.0.5 Soft deprecated.
634
+ * @since 4.1.4 1. Another silent deprecation. Use `is_shop()` instead.
635
+ * 2. Removed output memoization.
636
+ * @since 4.2.0 Hard deprecation.
637
  * @deprecated
638
+ * @internal
639
  *
640
+ * @param int|WP_Post|null $post (Optional) Post ID or post object.
641
+ * @return bool True if on the WooCommerce shop page.
 
642
  */
643
+ public function is_wc_shop( $post = null ) {
644
+
645
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->is_wc_shop()', '5.0.0', 'the_seo_framework()->is_shop()' );
646
+
647
+ if ( isset( $post ) ) {
648
+ $post = \get_post( $post );
649
+ $id = $post ? $post->ID : 0;
650
+ } else {
651
+ $id = null;
652
+ }
653
+
654
+ if ( isset( $id ) ) {
655
+ $is_shop = (int) \get_option( 'woocommerce_shop_page_id' ) === $id;
656
+ } else {
657
+ $is_shop = ! \is_admin() && \function_exists( 'is_shop' ) && \is_shop();
658
+ }
659
+
660
+ return $is_shop;
661
  }
662
 
663
  /**
664
+ * Determines if the page is the WooCommerce plugin Product page.
665
+ *
666
+ * @since 2.5.2
667
+ * @since 4.0.0 : 1. Added admin support.
668
+ * 2. Added parameter for the Post ID or post to test.
669
+ * @since 4.0.5 Soft deprecated.
670
+ * @since 4.1.4 1. Another silent deprecation. Use `is_product()` instead.
671
+ * 2. Removed output memoization.
672
+ * @since 4.2.0 Hard deprecation.
673
  * @deprecated
674
+ * @internal
675
  *
676
+ * @param int|\WP_Post $post When set, checks if the post is of type product.
677
+ * @return bool True if on a WooCommerce Product page.
678
  */
679
+ public function is_wc_product( $post = 0 ) {
680
 
681
  $tsf = \the_seo_framework();
682
+ // $tsf->_deprecated_function( 'the_seo_framework()->is_wc_product()', '5.0.0', 'the_seo_framework()->is_product()' );
683
 
684
+ if ( \is_admin() )
685
+ return $tsf->is_wc_product_admin();
 
686
 
687
+ if ( $post ) {
688
+ $is_product = 'product' === \get_post_type( $post );
 
 
 
 
 
 
 
 
 
 
 
689
  } else {
690
+ $is_product = \function_exists( 'is_product' ) && \is_product();
 
 
 
 
 
 
 
 
 
691
  }
692
 
693
+ return $is_product;
 
 
 
694
  }
695
 
696
  /**
697
+ * Detects products within the admin area.
698
+ *
699
+ * @since 4.0.0
700
+ * @see $this->is_wc_product()
701
+ * @since 4.0.5 Soft deprecated.
702
+ * @since 4.1.4 1. Another silent deprecation. Use `is_product_admin()` instead.
703
+ * 2. Removed output memoization.
704
+ * @since 4.2.0 Hard deprecation.
705
+ * @deprecated
706
+ * @internal
707
  *
708
+ * @return bool
709
+ */
710
+ public function is_wc_product_admin() {
711
+ $tsf = \the_seo_framework();
712
+ // $tsf->_deprecated_function( 'the_seo_framework()->is_wc_product_admin()', '5.0.0', 'the_seo_framework()->is_product_admin()' );
713
+ // Checks for "is_singular_admin()" because the post type is non-hierarchical.
714
+ return $tsf->is_singular_admin() && 'product' === $tsf->get_admin_post_type();
715
+ }
716
+
717
+ /**
718
+ * Updates user SEO option.
719
  *
720
+ * @since 2.7.0
721
+ * @since 2.8.0 New users now get a new array assigned.
722
+ * @since 4.1.4 Deprecated silently. Use `update_single_user_meta_item()` instead.
723
  *
724
+ * @param int $user_id The user ID.
725
+ * @param string $option The user's SEO metadata option.
726
+ * @param mixed $value The escaped option value.
727
+ * @return bool True on success. False on failure.
728
  */
729
+ public function update_user_option( $user_id = 0, $option = '', $value = '' ) {
730
 
731
  $tsf = \the_seo_framework();
732
+ // $tsf->_deprecated_function( 'the_seo_framework()->update_user_option()', '5.0.0', 'the_seo_framework()->update_single_user_meta_item()' );
733
 
734
+ if ( ! $option )
 
 
 
735
  return false;
736
 
737
+ if ( empty( $user_id ) )
738
+ $user_id = $tsf->get_user_id();
739
 
740
+ if ( empty( $user_id ) )
741
+ return false;
 
 
 
742
 
743
+ $meta = $tsf->get_user_meta( $user_id, false );
744
 
745
+ /**
746
+ * @since 2.8.0 initializes new array on empty values.
747
+ */
748
+ \is_array( $meta ) or $meta = [];
749
 
750
+ $meta[ $option ] = $value;
 
751
 
752
+ return \update_user_meta( $user_id, THE_SEO_FRAMEWORK_USER_OPTIONS, $meta );
753
  }
754
 
755
  /**
756
+ * Helper function that constructs name attributes for use in form fields.
757
  *
758
+ * Other page implementation classes may wish to construct and use a
759
+ * get_field_id() method, if the naming format needs to be different.
 
 
760
  *
761
+ * @since 2.2.2
762
+ * @since 4.1.4 Deprecated silently.
763
+ * @since 5.0.0 Hard deprecation.
764
  * @deprecated
765
  *
766
+ * @param string $name Field name base
767
+ * @return string Full field name
 
768
  */
769
+ public function get_field_name( $name ) {
770
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_field_name()', '5.0.0' );
771
+ return \The_SEO_Framework\Interpreters\Form::get_field_name( $name );
 
 
 
 
 
 
772
  }
773
 
774
  /**
775
+ * Echo constructed name attributes in form fields.
776
  *
777
+ * @since 2.2.2
778
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
779
+ * @since 5.0.0 Hard deprecation.
780
+ * @uses $this->get_field_name() Construct name attributes for use in form fields.
 
781
  *
782
+ * @param string $name Field name base
 
783
  */
784
+ public function field_name( $name ) {
785
+ // $tsf->_deprecated_function( 'the_seo_framework()->field_name()', '5.0.0' );
786
+ return \The_SEO_Framework\Interpreters\Form::field_name( $name );
 
 
 
 
 
 
 
 
 
 
787
  }
788
 
789
  /**
790
+ * Helper function that constructs id attributes for use in form fields.
791
  *
792
+ * @since 2.2.2
793
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
794
+ * @since 5.0.0 Hard deprecation.
 
795
  *
796
+ * @param string $id Field id base
797
+ * @return string Full field id
798
  */
799
+ public function get_field_id( $id ) {
800
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_field_id()', '5.0.0' );
801
+ return \The_SEO_Framework\Interpreters\Form::get_field_id( $id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
802
  }
803
 
804
  /**
805
+ * Echo constructed id attributes in form fields.
806
  *
807
+ * @since 2.2.2
808
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
809
+ * @since 5.0.0 Hard deprecation.
810
+ * @uses $this->get_field_id() Constructs id attributes for use in form fields.
 
 
811
  *
812
+ * @param string $id Field id base.
813
+ * @param boolean $echo Whether to escape echo or just return.
814
+ * @return string Full field id
815
  */
816
+ public function field_id( $id, $echo = true ) {
817
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->field_id()', '5.0.0' );
818
+ return \The_SEO_Framework\Interpreters\Form::field_id( $id, $echo );
 
 
 
 
 
 
 
 
 
 
 
 
819
  }
820
 
821
  /**
822
+ * Mark up content with code tags.
823
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
824
  *
825
+ * @since 2.0.0
826
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
827
+ * @since 5.0.0 Hard deprecation.
 
 
828
  *
829
+ * @param string $content Content to be wrapped in code tags.
830
+ * @return string Content wrapped in code tags.
831
  */
832
+ public function code_wrap( $content ) {
833
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->code_wrap()', '5.0.0' );
834
+ return \The_SEO_Framework\Interpreters\HTML::code_wrap( $content );
 
 
 
 
 
 
 
 
 
835
  }
836
 
837
  /**
838
+ * Mark up content with code tags.
839
+ * Escapes no HTML.
840
  *
841
+ * @since 2.2.2
842
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
843
+ * @since 5.0.0 Hard deprecation.
 
 
844
  *
845
+ * @param string $content Content to be wrapped in code tags.
846
+ * @return string Content wrapped in code tags.
847
  */
848
+ public function code_wrap_noesc( $content ) {
849
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->code_wrap_noesc()', '5.0.0' );
850
+ return \The_SEO_Framework\Interpreters\HTML::code_wrap_noesc( $content );
 
 
 
 
 
851
  }
852
 
853
  /**
854
+ * Mark up content in description wrap.
855
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
856
  *
857
+ * @since 2.7.0
858
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
859
+ * @since 5.0.0 Hard deprecation.
860
  *
861
+ * @param string $content Content to be wrapped in the description wrap.
862
+ * @param bool $block Whether to wrap the content in <p> tags.
863
  */
864
+ public function description( $content, $block = true ) {
865
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->description()', '5.0.0' );
866
+ return \The_SEO_Framework\Interpreters\HTML::description( $content, $block );
 
 
 
 
 
867
  }
868
 
869
  /**
870
+ * Mark up content in description wrap.
871
  *
872
+ * @since 2.7.0
873
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
874
+ * @since 5.0.0 Hard deprecation.
875
  *
876
+ * @param string $content Content to be wrapped in the description wrap. Expected to be escaped.
877
+ * @param bool $block Whether to wrap the content in <p> tags.
878
  */
879
+ public function description_noesc( $content, $block = true ) {
880
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->description_noesc()', '5.0.0' );
881
+ return \The_SEO_Framework\Interpreters\HTML::description_noesc( $content, $block );
 
 
 
 
 
 
 
 
 
882
  }
883
 
884
  /**
885
+ * Mark up content in attention wrap.
886
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
887
  *
888
+ * @since 3.1.0
889
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
890
+ * @since 5.0.0 Hard deprecation.
 
891
  *
892
+ * @param string $content Content to be wrapped in the attention wrap.
893
+ * @param bool $block Whether to wrap the content in <p> tags.
894
  */
895
+ public function attention( $content, $block = true ) {
896
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->attention()', '5.0.0' );
897
+ return \The_SEO_Framework\Interpreters\HTML::attention( $content, $block );
898
  }
899
 
900
  /**
901
+ * Mark up content in attention wrap.
902
  *
903
+ * @since 3.1.0
904
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
905
+ * @since 5.0.0 Hard deprecation.
 
906
  *
907
+ * @param string $content Content to be wrapped in the attention wrap. Expected to be escaped.
908
+ * @param bool $block Whether to wrap the content in <p> tags.
909
  */
910
+ public function attention_noesc( $content, $block = true ) {
911
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->attention_noesc()', '5.0.0' );
912
+ return \The_SEO_Framework\Interpreters\HTML::attention_noesc( $content, $block );
 
 
 
 
913
  }
914
 
915
  /**
916
+ * Mark up content in a description+attention wrap.
917
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
918
  *
919
+ * @since 3.1.0
920
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
921
+ * @since 5.0.0 Hard deprecation.
 
 
922
  *
923
+ * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
924
+ * @param bool $block Whether to wrap the content in <p> tags.
925
  */
926
+ public function attention_description( $content, $block = true ) {
927
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->attention_description()', '5.0.0' );
928
+ return \The_SEO_Framework\Interpreters\HTML::attention_description( $content, $block );
929
  }
930
 
931
  /**
932
+ * Mark up content in a description+attention wrap.
933
  *
 
 
 
 
934
  * @since 3.1.0
935
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
936
+ * @since 5.0.0 Hard deprecation.
 
937
  *
938
+ * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
939
+ * @param bool $block Whether to wrap the content in <p> tags.
940
  */
941
+ public function attention_description_noesc( $content, $block = true ) {
942
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->attention_description_noesc()', '5.0.0' );
943
+ return \The_SEO_Framework\Interpreters\HTML::attention_description_noesc( $content, $block );
944
  }
945
 
946
  /**
947
+ * Echo or return a chechbox fields wrapper.
948
+ *
949
+ * This method does NOT escape.
950
  *
951
  * @since 2.6.0
952
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
953
+ * @since 5.0.0 Hard deprecation.
 
954
  *
955
+ * @param string $input The input to wrap. Should already be escaped.
956
+ * @param bool $echo Whether to escape echo or just return.
957
+ * @return string|void Wrapped $input.
958
  */
959
+ public function wrap_fields( $input = '', $echo = false ) {
960
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->wrap_fields()', '5.0.0' );
961
+ return \The_SEO_Framework\Interpreters\HTML::wrap_fields( $input, $echo );
962
  }
963
 
964
  /**
965
+ * Return a wrapped question mark.
 
 
 
 
 
 
966
  *
967
+ * @since 2.6.0
968
+ * @since 3.0.0 Links are now no longer followed, referred or bound to opener.
969
+ * @since 4.0.0 Now adds a tabindex to the span tag, so you can focus it using keyboard navigation.
970
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
971
+ * @since 5.0.0 Hard deprecation.
972
+ *
973
+ * @param string $description The descriptive on-hover title.
974
+ * @param string $link The non-escaped link.
975
+ * @param bool $echo Whether to echo or return.
976
+ * @return string HTML checkbox output if $echo is false.
977
  */
978
+ public function make_info( $description = '', $link = '', $echo = true ) {
979
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->make_info()', '5.0.0' );
980
+ return \The_SEO_Framework\Interpreters\HTML::make_info( $description, $link, $echo );
981
  }
982
 
983
  /**
984
+ * Makes either simple or JSON-encoded data-* attributes for HTML elements.
985
+ *
986
+ * @since 4.0.0
987
+ * @since 4.1.0 No longer adds an extra space in front of the return value when no data is generated.
988
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
989
+ * @since 5.0.0 Hard deprecation.
990
+ * @internal
991
+ *
992
+ * @param array $data : {
993
+ * string $k => mixed $v
994
+ * }
995
+ * @return string The HTML data attributes, with added space to the start.
996
  */
997
+ public function make_data_attributes( array $data ) {
998
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->make_data_attributes()', '5.0.0' );
999
+ return \The_SEO_Framework\Interpreters\HTML::make_data_attributes( $data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1000
  }
1001
 
1002
  /**
1003
+ * Returns a chechbox wrapper.
1004
  *
1005
+ * @since 2.6.0
1006
+ * @since 2.7.0 Added escape parameter. Defaults to true.
1007
+ * @since 3.0.3 Added $disabled parameter. Defaults to false.
1008
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1009
+ * @since 5.0.0 Hard deprecation.
1010
+ *
1011
+ * @param string $field_id The option ID. Must be within the Autodescription settings.
1012
+ * @param string $label The checkbox description label.
1013
+ * @param string $description Addition description to place beneath the checkbox.
1014
+ * @param bool $escape Whether to escape the label and description.
1015
+ * @param bool $disabled Whether to disable the input.
1016
+ * @return string HTML checkbox output.
1017
  */
1018
+ public function make_checkbox( $field_id = '', $label = '', $description = '', $escape = true, $disabled = false ) {
1019
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->make_checkbox()', '5.0.0' );
1020
+ return \The_SEO_Framework\Interpreters\Form::make_checkbox( [
1021
+ 'id' => $field_id,
1022
+ 'index' => '',
1023
+ 'label' => $label,
1024
+ 'description' => $description,
1025
+ 'escape' => $escape,
1026
+ 'disabled' => $disabled,
1027
+ ] );
1028
+ }
 
1029
 
1030
+ /**
1031
+ * Returns a HTML select form elements for qubit options: -1, 0, or 1.
1032
+ * Does not support "multiple" field selections.
1033
+ *
1034
+ * @since 4.0.0
1035
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1036
+ * @since 5.0.0 Hard deprecation.
1037
+ *
1038
+ * @param array $args : {
1039
+ * string $id The select field ID.
1040
+ * string $class The div wrapper class.
1041
+ * string $name The option name.
1042
+ * int|string $default The current option value.
1043
+ * array $options The select option values : { value => name }
1044
+ * string $label The option label.
1045
+ * string $required Whether the field must be required.
1046
+ * array $data The select field data. Sub-items are expected to be escaped if they're not an array.
1047
+ * array $info Extra info field data.
1048
+ * }
1049
+ * @return string The option field.
1050
+ */
1051
+ public function make_single_select_form( array $args ) {
1052
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->make_single_select_form()', '5.0.0' );
1053
+ return \The_SEO_Framework\Interpreters\Form::make_single_select_form( $args );
1054
  }
1055
 
1056
  /**
1057
+ * Returns the HTML class wrap for default Checkbox options.
 
1058
  *
1059
+ * This function does nothing special. But is merely a simple wrapper.
1060
+ * Just like code_wrap.
1061
  *
1062
+ * @since 2.2.5
1063
+ * @since 3.1.0 Deprecated second parameter.
1064
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1065
+ * @since 5.0.0 Hard deprecation.
 
 
1066
  *
1067
+ * @param string $key The option name which returns boolean.
1068
+ * @param string $depr Deprecated
1069
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
1070
+ * @param bool $echo Whether to echo or return the output.
1071
+ * @return string Empty on echo or the class name with an optional wrapper.
1072
  */
1073
+ public function is_default_checked( $key, $depr = '', $wrap = true, $echo = true ) {
1074
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->is_default_checked()', '5.0.0' );
1075
+ return \The_SEO_Framework\Interpreters\Form::is_default_checked( $key, $wrap, $echo );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1076
  }
 
1077
  /**
1078
+ * Returns the HTML class wrap for warning Checkbox options.
1079
+ *
1080
+ * @since 2.3.4
1081
+ * @since 3.1.0 Deprecated second parameter.
1082
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1083
+ * @since 5.0.0 Hard deprecation.
1084
+ *
1085
+ * @param string $key The option name which returns boolean.
1086
+ * @param string $deprecated Deprecated.
1087
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
1088
+ * @param bool $echo Whether to echo or return the output.
1089
+ * @return string Empty on echo or the class name with an optional wrapper.
1090
+ */
1091
+ public function is_warning_checked( $key, $deprecated = '', $wrap = true, $echo = true ) {
1092
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->is_warning_checked()', '5.0.0' );
1093
+ return \The_SEO_Framework\Interpreters\Form::is_warning_checked( $key, $wrap, $echo );
1094
+ }
1095
+ /**
1096
+ * Returns the HTML class wrap for warning/default Checkbox options.
1097
  *
1098
+ * @since 2.6.0
1099
+ * @since 3.1.0 Added the $wrap parameter.
1100
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1101
+ * @since 5.0.0 Hard deprecation.
 
1102
  *
1103
+ * @param string $key The option name which returns boolean.
1104
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
1105
  */
1106
+ public function get_is_conditional_checked( $key, $wrap = true ) {
1107
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->get_is_conditional_checked()', '5.0.0' );
1108
+ return \The_SEO_Framework\Interpreters\Form::get_is_conditional_checked( $key, $wrap );
1109
+ }
1110
 
1111
+ /**
1112
+ * Returns the HTML class wrap for warning/default Checkbox options.
1113
+ *
1114
+ * @since 2.3.4
1115
+ * @since 3.1.0 Deprecated second parameter.
1116
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1117
+ * @since 5.0.0 Hard deprecation.
1118
+ *
1119
+ * @param string $key The option name which returns boolean.
1120
+ * @param string $deprecated Deprecated. Used to be the settings field.
1121
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
1122
+ * @param bool $echo Whether to echo or return the output.
1123
+ * @return string Empty on echo or the class name with an optional wrapper.
1124
+ */
1125
+ public function is_conditional_checked( $key, $deprecated = '', $wrap = true, $echo = true ) {
1126
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->is_conditional_checked()', '5.0.0' );
1127
+ return \The_SEO_Framework\Interpreters\Form::is_conditional_checked( $key, $wrap, $echo );
1128
+ }
1129
 
1130
+ /**
1131
+ * Outputs character counter wrap for both JavaScript and no-Javascript.
1132
+ *
1133
+ * @since 3.0.0
1134
+ * @since 3.1.0 : 1. Added an "what if you click" onhover-title.
1135
+ * 2. Removed second parameter's usage. For passing the expected string.
1136
+ * 3. The whole output is now hidden from no-js.
1137
+ * @since 4.1.0 No longer marks up the counter with the `description` HTML class.
1138
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1139
+ * @since 5.0.0 Hard deprecation.
1140
+ *
1141
+ * @param string $for The input ID it's for.
1142
+ * @param string $depr The initial value for no-JS. Deprecated.
1143
+ * @param bool $display Whether to display the counter. (options page gimmick)
1144
+ */
1145
+ public function output_character_counter_wrap( $for, $depr = '', $display = true ) {
1146
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->output_character_counter_wrap()', '5.0.0' );
1147
+ return \The_SEO_Framework\Interpreters\Form::output_character_counter_wrap( $for, $display );
1148
  }
1149
 
1150
  /**
1151
+ * Outputs pixel counter wrap for javascript.
1152
  *
1153
+ * @since 3.0.0
1154
+ * @since 4.1.4 Deprecated silently. Alternative marked for deletion.
1155
+ * @since 5.0.0 Hard deprecation.
 
1156
  *
1157
+ * @param string $for The input ID it's for.
1158
+ * @param string $type Whether it's a 'title' or 'description' counter.
1159
+ * @param bool $display Whether to display the counter. (options page gimmick)
1160
  */
1161
+ public function output_pixel_counter_wrap( $for, $type, $display = true ) {
1162
+ // \the_seo_framework()->_deprecated_function( 'the_seo_framework()->output_pixel_counter_wrap()', '5.0.0' );
1163
+ return \The_SEO_Framework\Interpreters\Form::output_pixel_counter_wrap( $for, $type, $display );
 
 
 
 
1164
  }
1165
  }
inc/classes/detect.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -196,6 +196,7 @@ class Detect extends Render {
196
  * Memoizes the return value for the input argument--sorts the array deeply to ensure a match.
197
  *
198
  * @since 2.5.2
 
199
  * @uses $this->detect_plugin_multi()
200
  *
201
  * @param array $plugins Array of array for globals, constants, classes
@@ -212,18 +213,14 @@ class Detect extends Render {
212
  $mapped = [];
213
 
214
  // Prepare multidimensional array for cache.
215
- foreach ( $plugins as $key => $func ) {
216
  if ( ! \is_array( $func ) )
217
  return false; // doing it wrong...
218
 
219
- // Sort alphanumeric by value, put values back after sorting.
220
- // TODO Use asort or usort instead???
221
- $func = array_flip( $func );
222
- ksort( $func );
223
- $func = array_flip( $func );
224
 
225
  // Glue with underscore and space for debugging purposes.
226
- $mapped[ $key ] = $key . '_' . implode( ' ', $func );
227
  }
228
 
229
  ksort( $mapped );
@@ -645,33 +642,6 @@ class Detect extends Render {
645
  );
646
  }
647
 
648
- /**
649
- * Determines whether to add a line within robots based by plugin detection, or sitemap output option.
650
- *
651
- * @since 2.6.0
652
- * @since 2.8.0 Added check_option parameter.
653
- * @since 2.9.0 Now also checks for subdirectory installations.
654
- * @since 2.9.2 Now also checks for permalinks.
655
- * @since 2.9.3 Now also checks for sitemap_robots option.
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.
663
- */
664
- public function can_do_sitemap_robots( $check_option = true ) {
665
-
666
- if ( $check_option ) {
667
- if ( ! $this->get_option( 'sitemaps_output' )
668
- || ! $this->get_option( 'sitemaps_robots' ) )
669
- return false;
670
- }
671
-
672
- return ! $this->has_robots_txt() && \strlen( $this->get_robots_txt_url() );
673
- }
674
-
675
  /**
676
  * Detects presence of robots.txt in root folder.
677
  * Memoizes the return value.
@@ -978,7 +948,7 @@ class Detect extends Render {
978
  */
979
  public function is_post_type_supported( $post_type = '' ) {
980
 
981
- $post_type = $post_type ?: $this->get_post_type_real_ID() ?: $this->get_admin_post_type();
982
 
983
  /**
984
  * @since 2.6.2
@@ -1048,7 +1018,7 @@ class Detect extends Render {
1048
  if ( isset( $cache[ $post_type ] ) )
1049
  return $cache[ $post_type ];
1050
 
1051
- $post_type = $post_type ?: $this->get_post_type_real_ID() ?: $this->get_admin_post_type();
1052
  if ( ! $post_type ) return false;
1053
 
1054
  if ( \get_object_taxonomies( $post_type, 'names' ) )
@@ -1081,6 +1051,7 @@ class Detect extends Render {
1081
  * Memoizes the return value.
1082
  *
1083
  * @since 4.1.0
 
1084
  *
1085
  * @return array All public post types.
1086
  */
@@ -1088,17 +1059,17 @@ class Detect extends Render {
1088
 
1089
  static $cache = null;
1090
 
1091
- return isset( $cache ) ? $cache : $cache = array_filter(
1092
- array_unique(
1093
- array_merge(
1094
- $this->get_forced_supported_post_types(),
1095
- //? array_values() because get_post_types() gives a sequential array.
1096
- array_values( (array) \get_post_types( [
1097
- 'public' => true,
1098
- ] ) )
1099
- )
1100
- ),
1101
- '\\is_post_type_viewable'
1102
  );
1103
  }
1104
 
@@ -1177,7 +1148,6 @@ class Detect extends Render {
1177
  );
1178
  }
1179
 
1180
-
1181
  /**
1182
  * Determines if the post type is disabled from SEO all optimization.
1183
  *
@@ -1191,7 +1161,7 @@ class Detect extends Render {
1191
  */
1192
  public function is_post_type_disabled( $post_type = '' ) {
1193
 
1194
- $post_type = $post_type ?: $this->get_post_type_real_ID() ?: $this->get_admin_post_type();
1195
 
1196
  /**
1197
  * @since 3.1.2
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
196
  * Memoizes the return value for the input argument--sorts the array deeply to ensure a match.
197
  *
198
  * @since 2.5.2
199
+ * @since 4.1.4 Fixed sorting algorithm from fribbling-me to resolving-me. Nothing changed but legibility.
200
  * @uses $this->detect_plugin_multi()
201
  *
202
  * @param array $plugins Array of array for globals, constants, classes
213
  $mapped = [];
214
 
215
  // Prepare multidimensional array for cache.
216
+ foreach ( $plugins as $type => $func ) {
217
  if ( ! \is_array( $func ) )
218
  return false; // doing it wrong...
219
 
220
+ sort( $func );
 
 
 
 
221
 
222
  // Glue with underscore and space for debugging purposes.
223
+ $mapped[ $type ] = $type . '_' . implode( ' ', $func );
224
  }
225
 
226
  ksort( $mapped );
642
  );
643
  }
644
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  /**
646
  * Detects presence of robots.txt in root folder.
647
  * Memoizes the return value.
948
  */
949
  public function is_post_type_supported( $post_type = '' ) {
950
 
951
+ $post_type = $post_type ?: $this->get_current_post_type();
952
 
953
  /**
954
  * @since 2.6.2
1018
  if ( isset( $cache[ $post_type ] ) )
1019
  return $cache[ $post_type ];
1020
 
1021
+ $post_type = $post_type ?: $this->get_current_post_type();
1022
  if ( ! $post_type ) return false;
1023
 
1024
  if ( \get_object_taxonomies( $post_type, 'names' ) )
1051
  * Memoizes the return value.
1052
  *
1053
  * @since 4.1.0
1054
+ * @since 4.1.4 Now resets the index keys of the return value.
1055
  *
1056
  * @return array All public post types.
1057
  */
1059
 
1060
  static $cache = null;
1061
 
1062
+ return isset( $cache ) ? $cache : $cache = array_values(
1063
+ array_filter(
1064
+ array_unique(
1065
+ array_merge(
1066
+ $this->get_forced_supported_post_types(),
1067
+ //? array_values() because get_post_types() gives a sequential array.
1068
+ array_keys( (array) \get_post_types( [ 'public' => true ] ) )
1069
+ )
1070
+ ),
1071
+ '\\is_post_type_viewable'
1072
+ )
1073
  );
1074
  }
1075
 
1148
  );
1149
  }
1150
 
 
1151
  /**
1152
  * Determines if the post type is disabled from SEO all optimization.
1153
  *
1161
  */
1162
  public function is_post_type_disabled( $post_type = '' ) {
1163
 
1164
+ $post_type = $post_type ?: $this->get_current_post_type();
1165
 
1166
  /**
1167
  * @since 3.1.2
inc/classes/generate-description.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -749,9 +749,9 @@ class Generate_Description extends Generate {
749
  * Fetches or parses the excerpt of the post.
750
  *
751
  * @since 1.0.0
752
- * @since 2.8.2 : Added 4th parameter for escaping.
753
- * @since 3.1.0 1. No longer returns anything for terms.
754
- * 2. Now strips plausible embeds URLs.
755
  * @since 4.0.1 The second parameter `$id` now defaults to int 0, instead of an empty string.
756
  *
757
  * @param string $excerpt The Excerpt.
@@ -853,7 +853,7 @@ class Generate_Description extends Generate {
853
  public function trim_excerpt( $excerpt, $depr = 0, $max_char_length = 0 ) {
854
 
855
  // Decode to get a more accurate character length in Unicode.
856
- $excerpt = html_entity_decode( $excerpt, ENT_QUOTES | ENT_COMPAT, 'UTF-8' );
857
 
858
  // Find all words with $max_char_length, and trim when the last word boundary or punctuation is found.
859
  preg_match( sprintf( '/.{0,%d}([^\P{Po}\'\":]|[\p{Pc}\p{Pd}\p{Pf}\p{Z}]|$){1}/su', $max_char_length ), trim( $excerpt ), $matches );
@@ -864,9 +864,9 @@ class Generate_Description extends Generate {
864
  if ( ! $excerpt ) return '';
865
 
866
  // Texturize to recognize the sentence structure. Decode thereafter since we get HTML returned.
867
- $excerpt = htmlentities( $excerpt, ENT_QUOTES | ENT_COMPAT, 'UTF-8' );
868
  $excerpt = \wptexturize( $excerpt );
869
- $excerpt = html_entity_decode( $excerpt, ENT_QUOTES | ENT_COMPAT, 'UTF-8' );
870
  /**
871
  * Play with it here: https://regex101.com/r/u0DIgx/5/tests
872
  *
@@ -923,7 +923,7 @@ class Generate_Description extends Generate {
923
  $excerpt = '';
924
  }
925
 
926
- return trim( htmlentities( $excerpt, ENT_QUOTES | ENT_COMPAT, 'UTF-8' ) );
927
  }
928
 
929
  /**
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
749
  * Fetches or parses the excerpt of the post.
750
  *
751
  * @since 1.0.0
752
+ * @since 2.8.2 Added 4th parameter for escaping.
753
+ * @since 3.1.0 : 1. No longer returns anything for terms.
754
+ * 2. Now strips plausible embeds URLs.
755
  * @since 4.0.1 The second parameter `$id` now defaults to int 0, instead of an empty string.
756
  *
757
  * @param string $excerpt The Excerpt.
853
  public function trim_excerpt( $excerpt, $depr = 0, $max_char_length = 0 ) {
854
 
855
  // Decode to get a more accurate character length in Unicode.
856
+ $excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
857
 
858
  // Find all words with $max_char_length, and trim when the last word boundary or punctuation is found.
859
  preg_match( sprintf( '/.{0,%d}([^\P{Po}\'\":]|[\p{Pc}\p{Pd}\p{Pf}\p{Z}]|$){1}/su', $max_char_length ), trim( $excerpt ), $matches );
864
  if ( ! $excerpt ) return '';
865
 
866
  // Texturize to recognize the sentence structure. Decode thereafter since we get HTML returned.
867
+ $excerpt = htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' );
868
  $excerpt = \wptexturize( $excerpt );
869
+ $excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
870
  /**
871
  * Play with it here: https://regex101.com/r/u0DIgx/5/tests
872
  *
923
  $excerpt = '';
924
  }
925
 
926
+ return trim( htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' ) );
927
  }
928
 
929
  /**
inc/classes/generate-image.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -44,6 +44,7 @@ class Generate_Image extends Generate_Url {
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,
49
  * int id: The image ID,
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
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
+ * @param bool $single Whether to return at most a single array item.
48
  * @return array The image details array, sequential: int => {
49
  * string url: The image URL,
50
  * int id: The image ID,
inc/classes/generate-ldjson.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -187,8 +187,8 @@ class Generate_Ldjson extends Generate_Image {
187
  $alternate_name = $kname && $kname !== $blogname ? $kname : '';
188
 
189
  $data += [
190
- 'name' => $this->escape_title( $blogname ),
191
- 'alternateName' => $this->escape_title( $alternate_name ),
192
  ];
193
 
194
  //= The searchbox part.
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
187
  $alternate_name = $kname && $kname !== $blogname ? $kname : '';
188
 
189
  $data += [
190
+ 'name' => \strlen( $blogname ) ? $this->escape_title( $blogname ) : '',
191
+ 'alternateName' => \strlen( $alternate_name ) ? $this->escape_title( $alternate_name ) : '',
192
  ];
193
 
194
  //= The searchbox part.
inc/classes/generate-title.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -597,10 +597,11 @@ class Generate_Title extends Generate_Description {
597
  * Removes default title filters, for consistent output and sanitation.
598
  * Memoizes the filters removed, so it can add them back on reset.
599
  *
 
 
600
  * @since 3.1.0
601
  * @since 4.1.0 Added a second parameter, $args, to help soften the burden of this method.
602
  * @internal Only to be used within $this->get_raw_generated_title()
603
- * Peformance test: 0.000003s per remove+reset on PHP 7.3, single core VPN. This is the heaviest method of the plugin.
604
  *
605
  * @param bool $reset Whether to reset the removed filters.
606
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
@@ -611,13 +612,11 @@ class Generate_Title extends Generate_Description {
611
  static $filtered = [];
612
 
613
  if ( $reset ) {
614
- foreach ( $filtered as $tag => $priorities ) {
615
- foreach ( $priorities as $priority => $functions ) {
616
- foreach ( $functions as $function ) {
617
  \add_filter( $tag, $function, $priority );
618
- }
619
- }
620
- }
621
  // Reset filters.
622
  $filtered = [];
623
  } else {
@@ -640,17 +639,17 @@ class Generate_Title extends Generate_Description {
640
  */
641
  $functions = [ 'wptexturize' ];
642
 
643
- if ( ! $this->get_option( 'title_strip_tags' ) ) {
644
  $functions[] = 'strip_tags';
645
- }
646
 
647
  foreach ( $filters as $tag ) {
648
  foreach ( $functions as $function ) {
 
649
  $it = 10;
650
  $i = 0;
651
  // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
652
  while ( $priority = \has_filter( $tag, $function ) ) {
653
- $filtered[ $tag ][ $priority ][] = $function;
654
  \remove_filter( $tag, $function, $priority );
655
  // Some noob might've destroyed \WP_Hook. Safeguard.
656
  if ( ++$i > $it ) break 1;
@@ -730,6 +729,7 @@ class Generate_Title extends Generate_Description {
730
  * Generates front page title.
731
  *
732
  * @since 3.1.0
 
733
  *
734
  * @return string The generated front page title.
735
  */
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
597
  * Removes default title filters, for consistent output and sanitation.
598
  * Memoizes the filters removed, so it can add them back on reset.
599
  *
600
+ * Performance test: 0.007ms per remove+reset on PHP 8.0, single core VPN.
601
+ *
602
  * @since 3.1.0
603
  * @since 4.1.0 Added a second parameter, $args, to help soften the burden of this method.
604
  * @internal Only to be used within $this->get_raw_generated_title()
 
605
  *
606
  * @param bool $reset Whether to reset the removed filters.
607
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
612
  static $filtered = [];
613
 
614
  if ( $reset ) {
615
+ foreach ( $filtered as $tag => $functions )
616
+ foreach ( $functions as $function => $priorities )
617
+ foreach ( $priorities as $priority )
618
  \add_filter( $tag, $function, $priority );
619
+
 
 
620
  // Reset filters.
621
  $filtered = [];
622
  } else {
639
  */
640
  $functions = [ 'wptexturize' ];
641
 
642
+ if ( ! $this->get_option( 'title_strip_tags' ) )
643
  $functions[] = 'strip_tags';
 
644
 
645
  foreach ( $filters as $tag ) {
646
  foreach ( $functions as $function ) {
647
+ // Only grab 10 of these. Yes, one might transform still on the 11th.
648
  $it = 10;
649
  $i = 0;
650
  // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
651
  while ( $priority = \has_filter( $tag, $function ) ) {
652
+ $filtered[ $tag ][ $function ][] = $priority;
653
  \remove_filter( $tag, $function, $priority );
654
  // Some noob might've destroyed \WP_Hook. Safeguard.
655
  if ( ++$i > $it ) break 1;
729
  * Generates front page title.
730
  *
731
  * @since 3.1.0
732
+ * @TODO figure out why we didn't choose to use $this->get_blogname()?
733
  *
734
  * @return string The generated front page title.
735
  */
inc/classes/generate-url.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -705,7 +705,7 @@ class Generate_Url extends Generate_Title {
705
  }
706
 
707
  if ( $_query )
708
- $url = $this->append_php_query( $url, $_query );
709
  } else {
710
  if ( $_use_base ) {
711
  $url = \add_query_arg( 'paged', $_page, $url );
@@ -775,7 +775,7 @@ class Generate_Url extends Generate_Title {
775
 
776
  // Add back the query.
777
  if ( $_query )
778
- $_url = $this->append_php_query( $_url, $_query );
779
  }
780
 
781
  $url = $_url;
@@ -874,7 +874,7 @@ class Generate_Url extends Generate_Title {
874
 
875
  //? Append queries other plugins might've filtered.
876
  if ( $this->is_singular() ) {
877
- $url = $this->append_php_query(
878
  $url,
879
  parse_url( \get_permalink( $id ), PHP_URL_QUERY )
880
  );
@@ -976,8 +976,8 @@ class Generate_Url extends Generate_Title {
976
  * Memoizes the return value.
977
  *
978
  * @since 2.7.0
979
- * @since 2.9.2 : Now considers port too.
980
- * : Now uses get_home_url(), rather than get_option('home').
981
  *
982
  * @return string The home URL host.
983
  */
@@ -1068,14 +1068,13 @@ class Generate_Url extends Generate_Title {
1068
  /**
1069
  * Appends given query to given URL.
1070
  *
1071
- * @since 3.0.0
1072
- * @since 3.1.0 Now uses parse_str and add_query_arg, preventing duplicated entries.
1073
  *
1074
  * @param string $url A fully qualified URL.
1075
  * @param string $query A fully qualified query taken from parse_url( $url, PHP_URL_QUERY );
1076
  * @return string A fully qualified URL with appended $query.
1077
  */
1078
- public function append_php_query( $url, $query = '' ) {
1079
 
1080
  if ( ! $query )
1081
  return $url;
@@ -1083,7 +1082,7 @@ class Generate_Url extends Generate_Title {
1083
  $_fragment = parse_url( $url, PHP_URL_FRAGMENT );
1084
 
1085
  if ( $_fragment )
1086
- $url = str_replace( '#' . $_fragment, '', $url );
1087
 
1088
  parse_str( $query, $results );
1089
 
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
705
  }
706
 
707
  if ( $_query )
708
+ $url = $this->append_url_query( $url, $_query );
709
  } else {
710
  if ( $_use_base ) {
711
  $url = \add_query_arg( 'paged', $_page, $url );
775
 
776
  // Add back the query.
777
  if ( $_query )
778
+ $_url = $this->append_url_query( $_url, $_query );
779
  }
780
 
781
  $url = $_url;
874
 
875
  //? Append queries other plugins might've filtered.
876
  if ( $this->is_singular() ) {
877
+ $url = $this->append_url_query(
878
  $url,
879
  parse_url( \get_permalink( $id ), PHP_URL_QUERY )
880
  );
976
  * Memoizes the return value.
977
  *
978
  * @since 2.7.0
979
+ * @since 2.9.2 : 1. Now considers port too.
980
+ * 2. Now uses get_home_url(), rather than get_option('home').
981
  *
982
  * @return string The home URL host.
983
  */
1068
  /**
1069
  * Appends given query to given URL.
1070
  *
1071
+ * @since 4.1.4
 
1072
  *
1073
  * @param string $url A fully qualified URL.
1074
  * @param string $query A fully qualified query taken from parse_url( $url, PHP_URL_QUERY );
1075
  * @return string A fully qualified URL with appended $query.
1076
  */
1077
+ public function append_url_query( $url, $query = '' ) {
1078
 
1079
  if ( ! $query )
1080
  return $url;
1082
  $_fragment = parse_url( $url, PHP_URL_FRAGMENT );
1083
 
1084
  if ( $_fragment )
1085
+ $url = str_replace( "#$_fragment", '', $url );
1086
 
1087
  parse_str( $query, $results );
1088
 
inc/classes/generate.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -71,37 +71,21 @@ class Generate extends User_Data {
71
  /**
72
  * Returns the `noindex`, `nofollow`, `noarchive` robots meta code array.
73
  *
74
- * @since 2.2.2
75
- * @since 2.2.4 Added robots SEO settings check.
76
- * @since 2.2.8 Added check for empty archives.
77
- * @since 2.8.0 Added check for protected/private posts.
78
- * @since 3.0.0 : 1. Removed noodp.
79
- * 2. Improved efficiency by grouping if statements.
80
- * @since 3.1.0 : 1. Simplified statements, often (not always) speeding things up.
81
- * 2. Now checks for wc_shop and blog types for pagination.
82
- * 3. Removed noydir.
83
- * @since 4.0.0 : 1. Now tests for qubit metadata.
84
- * 2. Added custom query support.
85
- * 3. Added two parameters.
86
- * @since 4.0.2 : 1. Added new copyright directive tags.
87
- * 2. Now strictly parses the validity of robots directives via a boolean check.
88
- * @since 4.0.3 : 1. Changed `max_snippet_length` to `max_snippet`
89
- * 2. Changed the copyright directive's spacer from `=` to `:`.
90
- * @since 4.0.5 : 1. Removed copyright directive bug workaround. <https://kb.theseoframework.com/kb/why-is-max-image-preview-none-purged/>
91
- * 2. Now sets noindex and nofollow when queries are exploited (requires option enabled).
92
  *
93
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
 
94
  * @param int <bit> $ignore The ignore level. {
95
  * 0 = 0b00: Ignore nothing.
96
  * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
97
  * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
98
  * 3 = 0b11: Ignore protection and post/term setting.
99
  * }
100
- * @return array {
101
  * string index : string value
102
  * }
103
  */
104
- public function robots_meta( $args = null, $ignore = 0b00 ) {
105
 
106
  if ( null === $args ) {
107
  $_meta = $this->get_robots_meta_by_query( $ignore );
@@ -142,7 +126,14 @@ class Generate extends User_Data {
142
  * @since 4.0.2 Now contains the copyright diretive values.
143
  * @since 4.0.3 Changed `$meta` key `max_snippet_length` to `max_snippet`
144
  *
145
- * @param array $meta The current robots meta.
 
 
 
 
 
 
 
146
  * @param array|null $args The query arguments. Contains 'id' and 'taxonomy'.
147
  * Is null when query is autodetermined.
148
  * @param int <bit> $ignore The ignore level. {
@@ -184,12 +175,12 @@ class Generate extends User_Data {
184
  * 3 = 0b11: Ignore protection and post/term meta setting.
185
  * }
186
  * @return array|null robots : {
187
- * bool 'noindex'
188
- * bool 'nofollow'
189
- * bool 'noarchive'
190
- * false|int <R>=-1> 'max_snippet'
191
- * false|string 'max_image_preview'
192
- * fasle|int <R>=-1> 'max_video_preview'
193
  * }
194
  */
195
  protected function get_robots_meta_by_query( $ignore = 0b00 ) {
@@ -208,9 +199,8 @@ class Generate extends User_Data {
208
 
209
  // Check homepage SEO settings, set noindex, nofollow and noarchive
210
  if ( $this->is_real_front_page() ) {
211
- $noindex = $noindex || $this->get_option( 'homepage_noindex' );
212
- $nofollow = $nofollow || $this->get_option( 'homepage_nofollow' );
213
- $noarchive = $noarchive || $this->get_option( 'homepage_noarchive' );
214
 
215
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
216
  $noindex = $noindex
@@ -227,7 +217,7 @@ class Generate extends User_Data {
227
  * which is exactly as intended. All other pages will relay 404.
228
  *
229
  * We do not test the post_count here, because we want to have
230
- * the first page indexable via user-intend only.
231
  */
232
  $noindex = $noindex || $this->is_404();
233
  } else {
@@ -238,11 +228,20 @@ 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
- * TODO Consider toggling this for author archives?
243
  */
244
- if ( isset( $wp_query->post_count ) && ! $wp_query->post_count )
245
- $noindex = true;
 
 
 
 
 
 
 
 
 
 
 
246
  }
247
 
248
  if (
@@ -256,22 +255,21 @@ class Generate extends User_Data {
256
 
257
  if ( $this->is_archive() ) {
258
  if ( $this->is_author() ) {
259
- $noindex = $noindex || $this->get_option( 'author_noindex' );
260
- $nofollow = $nofollow || $this->get_option( 'author_nofollow' );
261
- $noarchive = $noarchive || $this->get_option( 'author_noarchive' );
262
  } elseif ( $this->is_date() ) {
263
- $noindex = $noindex || $this->get_option( 'date_noindex' );
264
- $nofollow = $nofollow || $this->get_option( 'date_nofollow' );
265
- $noarchive = $noarchive || $this->get_option( 'date_noarchive' );
266
  }
267
  } elseif ( $this->is_search() ) {
268
- $noindex = $noindex || $this->get_option( 'search_noindex' );
269
- $nofollow = $nofollow || $this->get_option( 'search_nofollow' );
270
- $noarchive = $noarchive || $this->get_option( 'search_noarchive' );
271
  }
272
  }
273
 
274
  if ( $this->is_archive() ) {
 
 
275
  $_post_type_meta = [];
276
  // Store values from each post type bound to the taxonomy.
277
  foreach ( $this->get_post_types_from_taxonomy() as $post_type ) {
@@ -281,14 +279,11 @@ class Generate extends User_Data {
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();
289
- foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
290
  $$r = $$r || $this->is_taxonomy_robots_set( $r, $taxonomy );
291
- }
292
 
293
  if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
294
  $term_meta = $this->get_current_term_meta();
@@ -301,11 +296,10 @@ class Generate extends User_Data {
301
  }
302
  endif;
303
  } elseif ( $this->is_singular() ) {
 
304
 
305
- $post_type = $this->get_post_type_real_ID() ?: $this->get_admin_post_type();
306
- foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r ) {
307
  $$r = $$r || $this->is_post_type_robots_set( $r, $post_type );
308
- }
309
 
310
  if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
311
  $post_meta = [
@@ -320,6 +314,15 @@ class Generate extends User_Data {
320
  }
321
  endif;
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() );
@@ -460,88 +463,6 @@ class Generate extends User_Data {
460
  return compact( 'noindex', 'nofollow', 'noarchive', 'max_snippet', 'max_image_preview', 'max_video_preview' );
461
  }
462
 
463
- /**
464
- * Generates the `noindex` robots meta code array from arguments.
465
- *
466
- * This method is tailor-made for everything that relies on the noindex-state, as it's
467
- * a very controlling and powerful feature.
468
- *
469
- * Note that the home-as-blog page can be used for this method.
470
- *
471
- * @since 4.0.0
472
- * @since 4.1.0 Now uses the new taxonomy robots settings.
473
- *
474
- * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
475
- * @param int <bit> $ignore The ignore level. {
476
- * 0 = 0b00: Ignore nothing.
477
- * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
478
- * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
479
- * 3 = 0b11: Ignore protection and post/term setting.
480
- * }
481
- * @return bool Whether noindex is set or not
482
- */
483
- public function is_robots_meta_noindex_set_by_args( $args, $ignore = 0b00 ) {
484
-
485
- $this->fix_generation_args( $args );
486
-
487
- $noindex = (bool) $this->get_option( 'site_noindex' );
488
-
489
- if ( ! $args['taxonomy'] && $this->is_real_front_page_by_id( $args['id'] ) ) {
490
- $noindex = $noindex || $this->get_option( 'homepage_noindex' );
491
- }
492
-
493
- if ( $args['taxonomy'] ) {
494
- // This block is not used internally...
495
-
496
- $term = \get_term( $args['id'], $args['taxonomy'] );
497
- /**
498
- * Check if archive is empty: set noindex for those.
499
- */
500
- if ( empty( $term->count ) )
501
- $noindex = true;
502
-
503
- $_post_type_meta = [];
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'] );
515
-
516
- if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
517
- $term_meta = $this->get_term_meta( $args['id'] );
518
-
519
- if ( isset( $term_meta['noindex'] ) ) {
520
- // Test qubit
521
- $noindex = ( $noindex | (int) $term_meta['noindex'] ) > .33;
522
- }
523
- endif;
524
- } elseif ( $args['id'] ) {
525
- $post_type = \get_post_type( $args['id'] );
526
- $noindex = $noindex || $this->is_post_type_robots_set( 'noindex', $post_type );
527
-
528
- if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
529
- $post_meta = [
530
- 'noindex' => $this->get_post_meta_item( '_genesis_noindex', $args['id'] ),
531
- ];
532
- // Test qubit
533
- $noindex = ( $noindex | (int) $post_meta['noindex'] ) > .33;
534
- endif;
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
-
542
- return $noindex;
543
- }
544
-
545
  /**
546
  * Determines if the post type has a robots value set.
547
  *
@@ -558,7 +479,7 @@ class Generate extends User_Data {
558
  return ! empty(
559
  $this->get_option(
560
  $this->get_robots_post_type_option_id( $type )
561
- )[ $post_type ?: $this->get_post_type_real_ID() ?: $this->get_admin_post_type() ]
562
  );
563
  }
564
 
@@ -606,13 +527,12 @@ class Generate extends User_Data {
606
  * Memoizes the return value.
607
  *
608
  * @since 2.5.2
 
609
  *
610
  * @return string $blogname The escaped and sanitized blogname.
611
  */
612
  public function get_blogname() {
613
-
614
- static $blogname = null;
615
-
616
  return isset( $blogname ) ? $blogname : $blogname = trim( \get_bloginfo( 'name', 'display' ) );
617
  }
618
 
@@ -626,9 +546,7 @@ class Generate extends User_Data {
626
  * @return string $blogname The escaped and sanitized blog description.
627
  */
628
  public function get_blogdescription() {
629
-
630
- static $description = null;
631
-
632
  return isset( $description ) ? $description : $description = trim( \get_bloginfo( 'description', 'display' ) );
633
  }
634
 
@@ -733,6 +651,44 @@ class Generate extends User_Data {
733
  );
734
  }
735
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
  /**
737
  * @since 3.1.0
738
  * @TODO use this
@@ -748,6 +704,7 @@ class Generate extends User_Data {
748
  * @since 2.8.2 Now considers description output.
749
  * @since 2.9.0 Now listens to $this->get_available_twitter_cards().
750
  * @since 3.1.0 Now inherits filter `the_seo_framework_twittercard_output`.
 
751
  *
752
  * @return string The Twitter Card type. When no social title is found, an empty string will be returned.
753
  */
@@ -758,7 +715,6 @@ class Generate extends User_Data {
758
  if ( ! $available_cards ) return '';
759
 
760
  $option = $this->get_option( 'twitter_card' );
761
- $option = trim( \esc_attr( $option ) );
762
 
763
  // Option is equal to found cards. Output option.
764
  if ( \in_array( $option, $available_cards, true ) ) {
@@ -872,4 +828,37 @@ class Generate extends User_Data {
872
  'summary_large_image' => 'summary-large-image',
873
  ];
874
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
  }
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
71
  /**
72
  * Returns the `noindex`, `nofollow`, `noarchive` robots meta code array.
73
  *
74
+ * @since 4.1.4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  *
76
  * @param array|null $args The query arguments. Accepts 'id' and 'taxonomy'.
77
+ * @param null $get Reserved.
78
  * @param int <bit> $ignore The ignore level. {
79
  * 0 = 0b00: Ignore nothing.
80
  * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
81
  * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
82
  * 3 = 0b11: Ignore protection and post/term setting.
83
  * }
84
+ * @return array Only values actualized for display: {
85
  * string index : string value
86
  * }
87
  */
88
+ public function generate_robots_meta( $args = null, $get = null, $ignore = 0b00 ) {
89
 
90
  if ( null === $args ) {
91
  $_meta = $this->get_robots_meta_by_query( $ignore );
126
  * @since 4.0.2 Now contains the copyright diretive values.
127
  * @since 4.0.3 Changed `$meta` key `max_snippet_length` to `max_snippet`
128
  *
129
+ * @param array $meta The current robots meta. {
130
+ * 'noindex' : 'noindex'|''
131
+ * 'nofollow' : 'nofollow'|''
132
+ * 'noarchive' : 'noarchive'|''
133
+ * 'max_snippet' : 'max-snippet:<int>'|''
134
+ * 'max_image_preview' : 'max-image-preview:<string>'|''
135
+ * 'max_video_preview' : 'max-video-preview:<string>'|''
136
+ * }
137
  * @param array|null $args The query arguments. Contains 'id' and 'taxonomy'.
138
  * Is null when query is autodetermined.
139
  * @param int <bit> $ignore The ignore level. {
175
  * 3 = 0b11: Ignore protection and post/term meta setting.
176
  * }
177
  * @return array|null robots : {
178
+ * bool 'noindex'
179
+ * bool 'nofollow'
180
+ * bool 'noarchive'
181
+ * false|int <R>=-1<=600> 'max_snippet'
182
+ * false|string 'max_image_preview'
183
+ * fasle|int <R>=-1<=600> 'max_video_preview'
184
  * }
185
  */
186
  protected function get_robots_meta_by_query( $ignore = 0b00 ) {
199
 
200
  // Check homepage SEO settings, set noindex, nofollow and noarchive
201
  if ( $this->is_real_front_page() ) {
202
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
203
+ $$r = $$r || $this->get_option( "homepage_$r" );
 
204
 
205
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
206
  $noindex = $noindex
217
  * which is exactly as intended. All other pages will relay 404.
218
  *
219
  * We do not test the post_count here, because we want to have
220
+ * the first page indexable via user-intent only.
221
  */
222
  $noindex = $noindex || $this->is_404();
223
  } else {
228
  * because page builders and templates can and will take over.
229
  *
230
  * Don't use empty(), null is regarded as indexable.
 
 
231
  */
232
+ if ( isset( $wp_query->post_count ) && ! $wp_query->post_count ) {
233
+ /**
234
+ * We recommend using this filter ONLY for archives that have useful content but no "posts" attached.
235
+ * For example: a specially custom-developed author page for an author that never published a post.
236
+ *
237
+ * This filter won't run when a few other conditions for noindex have been met.
238
+ *
239
+ * @since 4.1.4
240
+ * @link <https://github.com/sybrew/the-seo-framework/issues/194#issuecomment-864298702>
241
+ * @param bool $noindex Whether to enable no posts protection.
242
+ */
243
+ $noindex = $noindex || \apply_filters( 'the_seo_framework_enable_noindex_no_posts', true );
244
+ }
245
  }
246
 
247
  if (
255
 
256
  if ( $this->is_archive() ) {
257
  if ( $this->is_author() ) {
258
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
259
+ $$r = $$r || $this->get_option( "author_$r" );
 
260
  } elseif ( $this->is_date() ) {
261
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
262
+ $$r = $$r || $this->get_option( "date_$r" );
 
263
  }
264
  } elseif ( $this->is_search() ) {
265
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
266
+ $$r = $$r || $this->get_option( "search_$r" );
 
267
  }
268
  }
269
 
270
  if ( $this->is_archive() ) {
271
+ $taxonomy = $this->get_current_taxonomy();
272
+
273
  $_post_type_meta = [];
274
  // Store values from each post type bound to the taxonomy.
275
  foreach ( $this->get_post_types_from_taxonomy() as $post_type ) {
279
  }
280
  }
281
  // Only enable if all post types have the value ticked.
282
+ foreach ( $_post_type_meta as $r => $_values )
283
  $$r = $$r || ! \in_array( false, $_values, true );
 
284
 
285
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
 
286
  $$r = $$r || $this->is_taxonomy_robots_set( $r, $taxonomy );
 
287
 
288
  if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
289
  $term_meta = $this->get_current_term_meta();
296
  }
297
  endif;
298
  } elseif ( $this->is_singular() ) {
299
+ $post_type = $this->get_current_post_type();
300
 
301
+ foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $r )
 
302
  $$r = $$r || $this->is_post_type_robots_set( $r, $post_type );
 
303
 
304
  if ( ! ( $ignore & ROBOTS_IGNORE_SETTINGS ) ) :
305
  $post_meta = [
314
  }
315
  endif;
316
 
317
+ // I hate this reiteration of the very same code as above... but, homepage may not always be singular.
318
+ // The conditions below MUST overwrite this, too. So, this is the perfect placement.
319
+ if ( $this->is_real_front_page() ) {
320
+ if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
321
+ $noindex = $noindex
322
+ || ( $this->get_option( 'home_paged_noindex' ) && ( $this->page() > 1 || $this->paged() > 1 ) );
323
+ endif;
324
+ }
325
+
326
  // Overwrite and ignore the user's settings, regardless; unless ignore is set.
327
  if ( ! ( $ignore & ROBOTS_IGNORE_PROTECTION ) ) :
328
  $noindex = $noindex || $this->is_protected( $this->get_the_real_ID() );
463
  return compact( 'noindex', 'nofollow', 'noarchive', 'max_snippet', 'max_image_preview', 'max_video_preview' );
464
  }
465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  /**
467
  * Determines if the post type has a robots value set.
468
  *
479
  return ! empty(
480
  $this->get_option(
481
  $this->get_robots_post_type_option_id( $type )
482
+ )[ $post_type ?: $this->get_current_post_type() ]
483
  );
484
  }
485
 
527
  * Memoizes the return value.
528
  *
529
  * @since 2.5.2
530
+ * TODO add filter?
531
  *
532
  * @return string $blogname The escaped and sanitized blogname.
533
  */
534
  public function get_blogname() {
535
+ static $blogname;
 
 
536
  return isset( $blogname ) ? $blogname : $blogname = trim( \get_bloginfo( 'name', 'display' ) );
537
  }
538
 
546
  * @return string $blogname The escaped and sanitized blog description.
547
  */
548
  public function get_blogdescription() {
549
+ static $description;
 
 
550
  return isset( $description ) ? $description : $description = trim( \get_bloginfo( 'description', 'display' ) );
551
  }
552
 
651
  );
652
  }
653
 
654
+ /**
655
+ * Returns the post's modified time.
656
+ * Memoizes the return value.
657
+ *
658
+ * @since 4.1.4
659
+ *
660
+ * @return string The current post's modified time
661
+ */
662
+ public function get_modified_time() {
663
+
664
+ static $time = null;
665
+
666
+ if ( isset( $time ) )
667
+ return $time;
668
+
669
+ $id = $this->get_the_real_ID();
670
+
671
+ $post = \get_post( $id );
672
+ $post_modified_gmt = $post->post_modified_gmt;
673
+
674
+ if ( '0000-00-00 00:00:00' === $post_modified_gmt )
675
+ return $time = '';
676
+
677
+ /**
678
+ * @since 2.3.0
679
+ * @since 2.7.0 Added output within filter.
680
+ * @param string $time The article modified time.
681
+ * @param int $id The current page or term ID.
682
+ */
683
+ return $time = (string) \apply_filters_ref_array(
684
+ 'the_seo_framework_modifiedtime_output',
685
+ [
686
+ $this->gmt2date( $this->get_timestamp_format(), $post_modified_gmt ),
687
+ $id,
688
+ ]
689
+ );
690
+ }
691
+
692
  /**
693
  * @since 3.1.0
694
  * @TODO use this
704
  * @since 2.8.2 Now considers description output.
705
  * @since 2.9.0 Now listens to $this->get_available_twitter_cards().
706
  * @since 3.1.0 Now inherits filter `the_seo_framework_twittercard_output`.
707
+ * @since 4.1.4 Removed needless preprocessing of the option.
708
  *
709
  * @return string The Twitter Card type. When no social title is found, an empty string will be returned.
710
  */
715
  if ( ! $available_cards ) return '';
716
 
717
  $option = $this->get_option( 'twitter_card' );
 
718
 
719
  // Option is equal to found cards. Output option.
720
  if ( \in_array( $option, $available_cards, true ) ) {
828
  'summary_large_image' => 'summary-large-image',
829
  ];
830
  }
831
+
832
+ /**
833
+ * Returns the redirect URL, if any.
834
+ *
835
+ * @since 4.1.4
836
+ *
837
+ * @param null|array $args The redirect URL arguments, leave null to autodetermine query : {
838
+ * int $id The Post, Page or Term ID to generate the URL for.
839
+ * string $taxonomy The taxonomy.
840
+ * }
841
+ * @return string The canonical URL if found, empty string otherwise.
842
+ */
843
+ public function get_redirect_url( $args = null ) {
844
+
845
+ $url = '';
846
+
847
+ if ( null === $args ) {
848
+ if ( $this->is_singular() ) {
849
+ $url = $this->get_post_meta_item( 'redirect' ) ?: '';
850
+ } elseif ( $this->is_term_meta_capable() ) {
851
+ $url = $this->get_term_meta_item( 'redirect' ) ?: '';
852
+ }
853
+ } else {
854
+ $this->fix_generation_args( $args );
855
+ if ( ! $args['taxonomy'] ) {
856
+ $url = $this->get_post_meta_item( 'redirect', $args['id'] ) ?: '';
857
+ } else {
858
+ $url = $this->get_term_meta_item( 'redirect', $args['id'] ) ?: '';
859
+ }
860
+ }
861
+
862
+ return $url;
863
+ }
864
  }
inc/classes/index.php CHANGED
@@ -35,7 +35,6 @@
35
  * | Sanitize
36
  * | Site_Options
37
  * | Cache
38
- * | Feed
39
  * | Load
40
  * |-> Final
41
  * |-> Instanced in function `the_seo_framework()`
35
  * | Sanitize
36
  * | Site_Options
37
  * | Cache
 
38
  * | Load
39
  * |-> Final
40
  * |-> Instanced in function `the_seo_framework()`
inc/classes/init.class.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2015 - 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
@@ -85,6 +85,9 @@ class Init extends Query {
85
 
86
  if ( \wp_doing_cron() )
87
  $this->init_cron_actions();
 
 
 
88
  }
89
 
90
  /**
@@ -103,6 +106,7 @@ class Init extends Query {
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.
@@ -110,15 +114,34 @@ class Init extends Query {
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
  /**
123
  * Initializes Admin Menu actions.
124
  *
@@ -135,67 +158,62 @@ class Init extends Query {
135
  //= Initialize caching actions.
136
  $this->init_admin_caching_actions();
137
 
138
- //= Initialize profile fields.
139
- $this->init_profile_fields();
140
-
141
- //= Initialize term meta filters and actions.
142
- $this->init_term_meta();
143
 
144
- // Save post data.
145
- \add_action( 'save_post', [ $this, '_update_post_meta' ], 1, 2 );
146
- \add_action( 'edit_attachment', [ $this, '_update_attachment_meta' ], 1 );
147
- \add_action( 'save_post', [ $this, '_save_inpost_primary_term' ], 1, 2 );
148
 
149
- // Enqueues admin scripts.
150
- \add_action( 'admin_enqueue_scripts', [ $this, '_init_admin_scripts' ], 0, 1 );
151
 
152
- // Add plugin links to the plugin activation page.
153
- \add_filter( 'plugin_action_links_' . THE_SEO_FRAMEWORK_PLUGIN_BASENAME, [ $this, '_add_plugin_action_links' ], 10, 2 );
154
- \add_filter( 'plugin_row_meta', [ $this, '_add_plugin_row_meta' ], 10, 2 );
155
 
156
- if ( $this->load_options ) :
157
- // Set up site settings and allow saving resetting them.
158
- \add_action( 'admin_init', [ $this, 'register_settings' ], 5 );
159
 
160
  // Initialize the SEO Bar for tables.
161
  \add_action( 'admin_init', [ $this, '_init_seo_bar_tables' ] );
 
 
 
 
 
162
 
163
  // Initialize List Edit for tables.
164
  \add_action( 'admin_init', [ $this, '_init_list_edit' ] );
165
 
166
- // Adds post states to list view tables.
167
- \add_filter( 'display_post_states', [ $this, '_add_post_state' ], 10, 2 );
168
-
169
  // Loads setting notices.
170
  \add_action( 'the_seo_framework_setting_notices', [ $this, '_do_settings_page_notices' ] );
171
 
172
- // Enqueue Post meta boxes.
173
- \add_action( 'add_meta_boxes', [ $this, '_init_post_edit_view' ], 5, 2 );
174
-
175
- // Enqueue Term meta output.
176
- \add_action( 'current_screen', [ $this, '_init_term_edit_view' ] );
177
-
178
  // Add menu links and register $this->seo_settings_page_hook
179
  \add_action( 'admin_menu', [ $this, 'add_menu_link' ] );
 
 
 
 
 
180
 
 
 
 
 
 
181
  // Set up notices.
182
  \add_action( 'admin_notices', [ $this, '_output_notices' ] );
183
 
184
  // Fallback HTML-only notice dismissal.
185
  \add_action( 'admin_init', [ $this, '_dismiss_notice' ] );
186
 
187
- // Admin AJAX for notice dismissal.
188
- \add_action( 'wp_ajax_tsf-dismiss-notice', [ $this, '_wp_ajax_dismiss_notice' ] );
189
-
190
- // Admin AJAX for counter options.
191
- \add_action( 'wp_ajax_the_seo_framework_update_counter', [ $this, '_wp_ajax_update_counter_type' ] );
192
-
193
- // Admin AJAX for Gutenberg SEO Bar update.
194
- \add_action( 'wp_ajax_the_seo_framework_update_post_data', [ $this, '_wp_ajax_get_post_data' ] );
195
 
196
- // Admin AJAX for TSF Cropper
197
- \add_action( 'wp_ajax_tsf-crop-image', [ $this, '_wp_ajax_crop_image' ] );
198
- endif;
199
 
200
  /**
201
  * @since 2.9.4
@@ -307,6 +325,14 @@ class Init extends Query {
307
  }
308
  }
309
 
 
 
 
 
 
 
 
 
310
  if ( $this->get_option( 'og_tags' ) ) { // independent from filter at use_og_tags--let that be deciding later.
311
  // Disable Jetpack's Open Graph tags. But Sybre, compat files? Yes.
312
  \add_filter( 'jetpack_enable_open_graph', '__return_false' );
@@ -386,6 +412,17 @@ class Init extends Query {
386
 
387
  if ( $this->is_preview() || $this->is_customize_preview() || ! $this->query_supports_seo() ) return;
388
 
 
 
 
 
 
 
 
 
 
 
 
389
  /**
390
  * @since 2.6.0
391
  */
@@ -399,21 +436,14 @@ class Init extends Query {
399
  */
400
  $init_start = microtime( true );
401
 
402
- if ( $this->use_object_cache ) {
403
- $cache_key = $this->get_meta_output_cache_key_by_query();
404
- $output = $this->object_cache_get( $cache_key );
405
- } else {
406
- $cache_key = '';
407
- $output = false;
408
- }
409
 
410
- if ( false === $output ) {
411
- $output = $this->get_html_output();
412
- $this->use_object_cache and $this->object_cache_set( $cache_key, $output, DAY_IN_SECONDS );
413
- }
414
 
415
- // phpcs:ignore, WordPress.Security.EscapeOutput -- $output is escaped.
416
- echo PHP_EOL, $this->get_plugin_indicator( 'before' ), $output, $this->get_plugin_indicator( 'after', $init_start ), PHP_EOL;
417
 
418
  /**
419
  * @since 2.6.0
@@ -422,99 +452,117 @@ class Init extends Query {
422
  }
423
 
424
  /**
425
- * Generates front-end HTMl output.
426
  *
427
- * @since 4.0.5
428
- * @todo convert $output to array and allow filtering it.
429
- *
430
- * @return string The HTML output.
431
  */
432
- public function get_html_output() {
 
 
433
 
434
- $robots = $this->robots();
435
 
436
- /** @since 4.0.4 : Added as WP 5.3 patch. */
437
  $this->set_timezone( 'UTC' );
438
 
439
  /**
440
  * @since 2.6.0
441
- * @param string $before The content before the SEO output. Stored in object cache.
442
  */
443
- $before = (string) \apply_filters( 'the_seo_framework_pre', '' );
444
 
445
- $before_legacy = $this->get_legacy_header_filters_output( 'before' );
446
 
447
  // Limit processing and redundant tags on 404 and search.
448
  if ( $this->is_search() ) :
449
- $output = $this->og_locale()
450
- . $this->og_type()
451
- . $this->og_title()
452
- . $this->og_url()
453
- . $this->og_sitename()
454
- . $this->theme_color()
455
- . $this->shortlink()
456
- . $this->canonical()
457
- . $this->paged_urls()
458
- . $this->google_site_output()
459
- . $this->bing_site_output()
460
- . $this->yandex_site_output()
461
- . $this->baidu_site_output()
462
- . $this->pint_site_output();
 
 
 
 
 
463
  elseif ( $this->is_404() ) :
464
- $output = $this->theme_color()
465
- . $this->google_site_output()
466
- . $this->bing_site_output()
467
- . $this->yandex_site_output()
468
- . $this->baidu_site_output()
469
- . $this->pint_site_output();
 
 
 
 
 
470
  elseif ( $this->is_query_exploited() ) :
471
- // aqp = advanced query protection
472
- $output = '<meta name="tsf:aqp" value="1" />' . PHP_EOL;
473
  else :
474
- // Inefficient concatenation is inefficient. Improve this?
475
- $output = $this->the_description()
476
- . $this->og_image()
477
- . $this->og_locale()
478
- . $this->og_type()
479
- . $this->og_title()
480
- . $this->og_description()
481
- . $this->og_url()
482
- . $this->og_sitename()
483
- . $this->facebook_publisher()
484
- . $this->facebook_author()
485
- . $this->facebook_app_id()
486
- . $this->article_published_time()
487
- . $this->article_modified_time()
488
- . $this->twitter_card()
489
- . $this->twitter_site()
490
- . $this->twitter_creator()
491
- . $this->twitter_title()
492
- . $this->twitter_description()
493
- . $this->twitter_image()
494
- . $this->theme_color()
495
- . $this->shortlink()
496
- . $this->canonical()
497
- . $this->paged_urls()
498
- . $this->ld_json()
499
- . $this->google_site_output()
500
- . $this->bing_site_output()
501
- . $this->yandex_site_output()
502
- . $this->baidu_site_output()
503
- . $this->pint_site_output();
 
 
 
 
 
504
  endif;
505
 
506
- $after_legacy = $this->get_legacy_header_filters_output( 'after' );
 
 
 
 
 
507
 
508
  /**
509
  * @since 2.6.0
510
- * @param string $after The content after the SEO output. Stored in object cache.
511
  */
512
- $after = (string) \apply_filters( 'the_seo_framework_pro', '' );
513
 
514
- /** @since 4.0.4 : Added as WP 5.3 patch. */
515
  $this->reset_timezone();
516
 
517
- return "{$robots}{$before}{$before_legacy}{$output}{$after_legacy}{$after}";
518
  }
519
 
520
  /**
@@ -534,13 +582,7 @@ class Init extends Query {
534
 
535
  if ( $this->is_preview() || $this->is_customize_preview() || ! $this->query_supports_seo() ) return;
536
 
537
- $url = '';
538
-
539
- if ( $this->is_singular() ) {
540
- $url = $this->get_post_meta_item( 'redirect' ) ?: '';
541
- } elseif ( $this->is_term_meta_capable() ) {
542
- $url = $this->get_term_meta_item( 'redirect' ) ?: '';
543
- }
544
 
545
  if ( $url ) {
546
  /**
@@ -653,10 +695,12 @@ class Init extends Query {
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.
659
  * See Monitor extension.
 
660
  *
661
  * @param string $robots_txt The current robots_txt output. Not used.
662
  * @param string $public The blog_public option value.
@@ -664,47 +708,38 @@ class Init extends Query {
664
  */
665
  public function robots_txt( $robots_txt = '', $public = '' ) {
666
 
667
- if ( $this->use_object_cache ) {
668
- $cache_key = $this->get_robots_txt_cache_key();
669
- $output = $this->object_cache_get( $cache_key );
670
- } else {
671
- $output = false;
672
- }
673
-
674
- if ( false === $output ) :
675
- $output = '';
676
 
677
- $site_path = \esc_attr( parse_url( \site_url(), PHP_URL_PATH ) ) ?: '';
678
-
679
- /**
680
- * @since 2.5.0
681
- * @param string $pre The output before this plugin's output.
682
- * Don't forget to add line breaks ( "\r\n" || PHP_EOL )!
683
- */
684
- $output .= (string) \apply_filters( 'the_seo_framework_robots_txt_pre', '' );
685
 
686
- // Output defaults
687
- $output .= "User-agent: *\r\n";
688
- $output .= "Disallow: $site_path/wp-admin/\r\n";
689
- $output .= "Allow: $site_path/wp-admin/admin-ajax.php\r\n";
690
 
691
- /**
692
- * @since 2.5.0
693
- * @param bool $disallow Whether to disallow robots queries.
694
- */
695
- if ( \apply_filters( 'the_seo_framework_robots_disallow_queries', false ) ) {
696
- $output .= "Disallow: /*?*\r\n";
697
- }
698
 
699
- /**
700
- * @since 2.5.0
701
- * @param string $pro The output after this plugin's output.
702
- * Don't forget to add line breaks ( "\r\n" || PHP_EOL )!
703
- */
704
- $output .= (string) \apply_filters( 'the_seo_framework_robots_txt_pro', '' );
705
 
706
- // Add extra whitespace and sitemap full URL
707
- if ( $this->can_do_sitemap_robots( true ) ) {
 
708
  $sitemaps = Bridges\Sitemap::get_instance();
709
  foreach ( $sitemaps->get_sitemap_endpoint_list() as $id => $data ) {
710
  if ( ! empty( $data['robots'] ) ) {
@@ -712,7 +747,7 @@ class Init extends Query {
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' ) ) {
@@ -721,9 +756,7 @@ class Init extends Query {
721
  }
722
  }
723
  }
724
-
725
- $this->use_object_cache and $this->object_cache_set( $cache_key, $output, 86400 );
726
- endif;
727
 
728
  $raw_uri = rawurldecode(
729
  \wp_check_invalid_utf8(
@@ -742,7 +775,7 @@ class Init extends Query {
742
  }
743
 
744
  /**
745
- * The robots.txt output. This filter output not cached; however, the $output variable can be via object caching.
746
  *
747
  * @since 4.0.5
748
  * @param string $output The (cached) robots.txt output.
@@ -852,7 +885,7 @@ class Init extends Query {
852
  if ( ! isset( $wp_query->query['s'] ) )
853
  return;
854
 
855
- if ( $this->is_archive_query_adjustment_blocked( $wp_query ) )
856
  return;
857
 
858
  $excluded = $this->get_ids_excluded_from_search();
@@ -885,7 +918,7 @@ class Init extends Query {
885
  public function _alter_archive_query_in( $wp_query ) {
886
 
887
  if ( $wp_query->is_archive || $wp_query->is_home ) {
888
- if ( $this->is_archive_query_adjustment_blocked( $wp_query ) )
889
  return;
890
 
891
  $excluded = $this->get_ids_excluded_from_archive();
@@ -917,13 +950,12 @@ class Init extends Query {
917
  public function _alter_search_query_post( $posts, $wp_query ) {
918
 
919
  if ( $wp_query->is_search ) {
920
- if ( $this->is_archive_query_adjustment_blocked( $wp_query ) )
921
  return $posts;
922
 
923
  foreach ( $posts as $n => $post ) {
924
- if ( $this->get_post_meta_item( 'exclude_local_search', $post->ID ) ) {
925
  unset( $posts[ $n ] );
926
- }
927
  }
928
  //= Reset numeric index.
929
  $posts = array_values( $posts );
@@ -945,13 +977,12 @@ class Init extends Query {
945
  public function _alter_archive_query_post( $posts, $wp_query ) {
946
 
947
  if ( $wp_query->is_archive || $wp_query->is_home ) {
948
- if ( $this->is_archive_query_adjustment_blocked( $wp_query ) )
949
  return $posts;
950
 
951
  foreach ( $posts as $n => $post ) {
952
- if ( $this->get_post_meta_item( 'exclude_from_archive', $post->ID ) ) {
953
  unset( $posts[ $n ] );
954
- }
955
  }
956
  //= Reset numeric index.
957
  $posts = array_values( $posts );
@@ -963,35 +994,76 @@ class Init extends Query {
963
  /**
964
  * Determines whether the archive query adjustment is blocked.
965
  *
 
 
 
 
 
 
 
 
 
 
 
966
  * @since 2.9.4
967
  * @since 3.1.0 Now checks for the post type.
 
 
 
968
  *
969
  * @param \WP_Query $wp_query WP_Query object.
970
  * @return bool
971
  */
972
- protected function is_archive_query_adjustment_blocked( $wp_query ) {
973
 
974
  static $has_filter = null;
975
 
976
- $blocked = false;
977
-
978
  if ( null === $has_filter ) {
979
  $has_filter = \has_filter( 'the_seo_framework_do_adjust_archive_query' );
980
  }
981
  if ( $has_filter ) {
982
  /**
 
 
983
  * @since 2.9.4
984
  * @param bool $do True is unblocked (do adjustment), false is blocked (don't do adjustment).
985
- * @param \WP_Query $wp_query The current query. Passed by reference.
986
  */
987
  if ( ! \apply_filters_ref_array( 'the_seo_framework_do_adjust_archive_query', [ true, $wp_query ] ) )
988
- $blocked = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
989
  }
990
 
991
- if ( isset( $wp_query->query_vars->post_type ) )
992
- $blocked = $this->is_post_type_disabled( $wp_query->query_vars->post_type );
 
 
 
 
 
 
 
 
 
 
 
993
 
994
- return $blocked;
995
  }
996
 
997
  /**
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (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
85
 
86
  if ( \wp_doing_cron() )
87
  $this->init_cron_actions();
88
+
89
+ if ( \wp_doing_ajax() )
90
+ $this->init_ajax_actions();
91
  }
92
 
93
  /**
106
  * @since 2.8.0
107
  * @since 4.1.2 1. Added hook for sitemap prerender.
108
  * 2. Added hook for ping retry.
109
+ * TODO make protected?
110
  */
111
  public function init_cron_actions() {
112
  // Init post update/delete caching actions which may occur during cronjobs.
114
 
115
  // Ping searchengines.
116
  if ( $this->get_option( 'ping_use_cron' ) ) {
117
+ if ( $this->get_option( 'sitemaps_output' ) && $this->get_option( 'ping_use_cron_prerender' ) )
118
  \add_action( 'tsf_sitemap_cron_hook_before', [ new Builders\Sitemap_Base, 'prerender_sitemap' ] );
 
119
 
120
  \add_action( 'tsf_sitemap_cron_hook', Bridges\Ping::class . '::ping_search_engines' );
121
  \add_action( 'tsf_sitemap_cron_hook_retry', Bridges\Ping::class . '::retry_ping_search_engines' );
122
  }
123
  }
124
 
125
+ /**
126
+ * Initializes AJAX actions.
127
+ *
128
+ * @since 4.1.4
129
+ */
130
+ protected function init_ajax_actions() {
131
+
132
+ // Admin AJAX for notice dismissal.
133
+ \add_action( 'wp_ajax_tsf_dismiss_notice', '\The_SEO_Framework\Bridges\AJAX::_wp_ajax_dismiss_notice' );
134
+
135
+ // Admin AJAX for TSF Cropper
136
+ \add_action( 'wp_ajax_tsf_crop_image', '\The_SEO_Framework\Bridges\AJAX::_wp_ajax_crop_image' );
137
+
138
+ // Admin AJAX for counter options.
139
+ \add_action( 'wp_ajax_tsf_update_counter', '\The_SEO_Framework\Bridges\AJAX::_wp_ajax_update_counter_type' );
140
+
141
+ // Admin AJAX for Gutenberg SEO Bar update.
142
+ \add_action( 'wp_ajax_tsf_update_post_data', '\The_SEO_Framework\Bridges\AJAX::_wp_ajax_get_post_data' );
143
+ }
144
+
145
  /**
146
  * Initializes Admin Menu actions.
147
  *
158
  //= Initialize caching actions.
159
  $this->init_admin_caching_actions();
160
 
161
+ if ( ! $this->is_headless['meta'] ) {
162
+ //= Initialize term meta filters and actions.
163
+ $this->init_term_meta();
 
 
164
 
165
+ //= Initialize term meta filters and actions.
166
+ $this->init_post_meta();
 
 
167
 
168
+ // Enqueue Post meta boxes.
169
+ \add_action( 'add_meta_boxes', [ $this, '_init_post_edit_view' ], 5, 2 );
170
 
171
+ // Enqueue Term meta output.
172
+ \add_action( 'current_screen', [ $this, '_init_term_edit_view' ] );
 
173
 
174
+ // Adds post states to list view tables.
175
+ \add_filter( 'display_post_states', [ $this, '_add_post_state' ], 10, 2 );
 
176
 
177
  // Initialize the SEO Bar for tables.
178
  \add_action( 'admin_init', [ $this, '_init_seo_bar_tables' ] );
179
+ }
180
+
181
+ if ( ! $this->is_headless['settings'] ) {
182
+ // Set up site settings and allow saving resetting them.
183
+ \add_action( 'admin_init', [ $this, 'register_settings' ], 5 );
184
 
185
  // Initialize List Edit for tables.
186
  \add_action( 'admin_init', [ $this, '_init_list_edit' ] );
187
 
 
 
 
188
  // Loads setting notices.
189
  \add_action( 'the_seo_framework_setting_notices', [ $this, '_do_settings_page_notices' ] );
190
 
 
 
 
 
 
 
191
  // Add menu links and register $this->seo_settings_page_hook
192
  \add_action( 'admin_menu', [ $this, 'add_menu_link' ] );
193
+ }
194
+
195
+ if ( ! $this->is_headless['user'] ) {
196
+ //= Initialize user meta filters and actions.
197
+ $this->init_user_meta();
198
 
199
+ // Enqueue user meta output.
200
+ \add_action( 'current_screen', [ $this, '_init_user_edit_view' ] );
201
+ }
202
+
203
+ if ( \in_array( false, $this->is_headless, true ) ) {
204
  // Set up notices.
205
  \add_action( 'admin_notices', [ $this, '_output_notices' ] );
206
 
207
  // Fallback HTML-only notice dismissal.
208
  \add_action( 'admin_init', [ $this, '_dismiss_notice' ] );
209
 
210
+ // Enqueues admin scripts.
211
+ \add_action( 'admin_enqueue_scripts', [ $this, '_init_admin_scripts' ], 0, 1 );
212
+ }
 
 
 
 
 
213
 
214
+ // Add plugin links to the plugin activation page.
215
+ \add_filter( 'plugin_action_links_' . THE_SEO_FRAMEWORK_PLUGIN_BASENAME, '\The_SEO_Framework\Bridges\PluginTable::_add_plugin_action_links', 10, 2 );
216
+ \add_filter( 'plugin_row_meta', '\The_SEO_Framework\Bridges\PluginTable::_add_plugin_row_meta', 10, 2 );
217
 
218
  /**
219
  * @since 2.9.4
325
  }
326
  }
327
 
328
+ /**
329
+ * @since 4.1.4
330
+ * @param bool $kill_core_robots Whether you lack sympathy for rocks tricked to think.
331
+ */
332
+ if ( \apply_filters( 'the_seo_framework_kill_core_robots', true ) ) {
333
+ \remove_filter( 'wp_robots', 'wp_robots_max_image_preview_large' );
334
+ }
335
+
336
  if ( $this->get_option( 'og_tags' ) ) { // independent from filter at use_og_tags--let that be deciding later.
337
  // Disable Jetpack's Open Graph tags. But Sybre, compat files? Yes.
338
  \add_filter( 'jetpack_enable_open_graph', '__return_false' );
412
 
413
  if ( $this->is_preview() || $this->is_customize_preview() || ! $this->query_supports_seo() ) return;
414
 
415
+ /**
416
+ * We added this filter a second time, for this method is conditional (see two lines above).
417
+ * When the query doesn't support TSF's SEO, we want default behavior to ensue.
418
+ *
419
+ * @since 4.1.4
420
+ * @param bool $kill_core_robots Whether you feel sympathy for rocks tricked to think.
421
+ */
422
+ if ( \apply_filters( 'the_seo_framework_kill_core_robots', true ) ) {
423
+ \remove_filter( 'wp_robots', 'wp_robots_noindex_search' );
424
+ }
425
+
426
  /**
427
  * @since 2.6.0
428
  */
436
  */
437
  $init_start = microtime( true );
438
 
439
+ // phpcs:disable, WordPress.Security.EscapeOutput -- Output is escaped.
440
+ // phpcs:ignore Squiz.WhiteSpace.LanguageConstructSpacing -- We're fancy here.
441
+ echo PHP_EOL, $this->get_plugin_indicator( 'before' );
 
 
 
 
442
 
443
+ $this->do_meta_output();
 
 
 
444
 
445
+ echo $this->get_plugin_indicator( 'after', $init_start ), PHP_EOL;
446
+ // phpcs:enable, WordPress.Security.EscapeOutput
447
 
448
  /**
449
  * @since 2.6.0
452
  }
453
 
454
  /**
455
+ * Outputs all meta tags for the current query.
456
  *
457
+ * @since 4.1.4
 
 
 
458
  */
459
+ public function do_meta_output() {
460
+
461
+ // phpcs:disable, WordPress.Security.EscapeOutput -- Everything we produce is escaped.
462
 
463
+ $get = [ 'robots' ];
464
 
465
+ /** @since 4.0.4 Added as WP 5.3 patch. */
466
  $this->set_timezone( 'UTC' );
467
 
468
  /**
469
  * @since 2.6.0
470
+ * @param string $before The content before the SEO output.
471
  */
472
+ echo \apply_filters( 'the_seo_framework_pre', '' );
473
 
474
+ echo $this->get_legacy_header_filters_output( 'before' );
475
 
476
  // Limit processing and redundant tags on 404 and search.
477
  if ( $this->is_search() ) :
478
+ array_push(
479
+ $get,
480
+ ...[
481
+ 'og_locale',
482
+ 'og_type',
483
+ 'og_title',
484
+ 'og_url',
485
+ 'og_sitename',
486
+ 'theme_color',
487
+ 'shortlink',
488
+ 'canonical',
489
+ 'paged_urls',
490
+ 'google_site_output',
491
+ 'bing_site_output',
492
+ 'yandex_site_output',
493
+ 'baidu_site_output',
494
+ 'pint_site_output',
495
+ ]
496
+ );
497
  elseif ( $this->is_404() ) :
498
+ array_push(
499
+ $get,
500
+ ...[
501
+ 'theme_color',
502
+ 'google_site_output',
503
+ 'bing_site_output',
504
+ 'yandex_site_output',
505
+ 'baidu_site_output',
506
+ 'pint_site_output',
507
+ ]
508
+ );
509
  elseif ( $this->is_query_exploited() ) :
510
+ $get[] = 'advanced_query_protection';
 
511
  else :
512
+ array_push(
513
+ $get,
514
+ ...[
515
+ 'the_description',
516
+ 'og_image',
517
+ 'og_locale',
518
+ 'og_type',
519
+ 'og_title',
520
+ 'og_description',
521
+ 'og_url',
522
+ 'og_sitename',
523
+ 'og_updated_time',
524
+ 'facebook_publisher',
525
+ 'facebook_author',
526
+ 'facebook_app_id',
527
+ 'article_published_time',
528
+ 'article_modified_time',
529
+ 'twitter_card',
530
+ 'twitter_site',
531
+ 'twitter_creator',
532
+ 'twitter_title',
533
+ 'twitter_description',
534
+ 'twitter_image',
535
+ 'theme_color',
536
+ 'shortlink',
537
+ 'canonical',
538
+ 'paged_urls',
539
+ 'ld_json',
540
+ 'google_site_output',
541
+ 'bing_site_output',
542
+ 'yandex_site_output',
543
+ 'baidu_site_output',
544
+ 'pint_site_output',
545
+ ]
546
+ );
547
  endif;
548
 
549
+ // TODO add filter? It won't last a few major updates though...
550
+ // But that's why I created this method like so... anyway... tough luck.
551
+ foreach ( $get as $method )
552
+ echo $this->{$method}();
553
+
554
+ echo $this->get_legacy_header_filters_output( 'after' );
555
 
556
  /**
557
  * @since 2.6.0
558
+ * @param string $after The content after the SEO output.
559
  */
560
+ echo \apply_filters( 'the_seo_framework_pro', '' );
561
 
562
+ /** @since 4.0.4 Added as WP 5.3 patch. */
563
  $this->reset_timezone();
564
 
565
+ // phpcs:enable, WordPress.Security.EscapeOutput
566
  }
567
 
568
  /**
582
 
583
  if ( $this->is_preview() || $this->is_customize_preview() || ! $this->query_supports_seo() ) return;
584
 
585
+ $url = $this->get_redirect_url();
 
 
 
 
 
 
586
 
587
  if ( $url ) {
588
  /**
695
  * 4. Now marked as private. Will be renamed to `_robots_txt()` in the future.
696
  * @since 4.1.0 Now adds the WordPress Core sitemap URL.
697
  * @since 4.1.2 Now only adds the WP Core sitemap URL when the provider tells us it's enabled.
698
+ * @since 4.1.4 Removed object caching support.
699
  * @uses robots_txt filter located at WP core
700
  * @access private
701
  * @TODO extrapolate the contents without a warning to get_robots_txt(). Forward filter to it.
702
  * See Monitor extension.
703
+ * @TODO rework into a workable standard...
704
  *
705
  * @param string $robots_txt The current robots_txt output. Not used.
706
  * @param string $public The blog_public option value.
708
  */
709
  public function robots_txt( $robots_txt = '', $public = '' ) {
710
 
711
+ $site_path = \esc_attr( parse_url( \site_url(), PHP_URL_PATH ) ) ?: '';
 
 
 
 
 
 
 
 
712
 
713
+ /**
714
+ * @since 2.5.0
715
+ * @param string $pre The output before this plugin's output.
716
+ * Don't forget to add line breaks ( "\r\n" || PHP_EOL )!
717
+ */
718
+ $output = (string) \apply_filters( 'the_seo_framework_robots_txt_pre', '' );
 
 
719
 
720
+ // Output defaults
721
+ $output .= "User-agent: *\r\n";
722
+ $output .= "Disallow: $site_path/wp-admin/\r\n";
723
+ $output .= "Allow: $site_path/wp-admin/admin-ajax.php\r\n";
724
 
725
+ /**
726
+ * @since 2.5.0
727
+ * @param bool $disallow Whether to disallow robots queries.
728
+ */
729
+ if ( \apply_filters( 'the_seo_framework_robots_disallow_queries', false ) ) {
730
+ $output .= "Disallow: /*?*\r\n";
731
+ }
732
 
733
+ /**
734
+ * @since 2.5.0
735
+ * @param string $pro The output after this plugin's output.
736
+ * Don't forget to add line breaks ( "\r\n" || PHP_EOL )!
737
+ */
738
+ $output .= (string) \apply_filters( 'the_seo_framework_robots_txt_pro', '' );
739
 
740
+ // Add extra whitespace and sitemap full URL
741
+ if ( $this->get_option( 'sitemaps_robots' ) ) {
742
+ if ( $this->get_option( 'sitemaps_output' ) ) {
743
  $sitemaps = Bridges\Sitemap::get_instance();
744
  foreach ( $sitemaps->get_sitemap_endpoint_list() as $id => $data ) {
745
  if ( ! empty( $data['robots'] ) ) {
747
  }
748
  }
749
  $output .= "\r\n";
750
+ } elseif ( ! $this->detect_sitemap_plugin() ) { // detect_sitemap_plugin() temp backward compat.
751
  if ( $this->use_core_sitemaps() ) {
752
  $wp_sitemaps_server = \wp_sitemaps_get_server();
753
  if ( method_exists( $wp_sitemaps_server, 'add_robots' ) ) {
756
  }
757
  }
758
  }
759
+ }
 
 
760
 
761
  $raw_uri = rawurldecode(
762
  \wp_check_invalid_utf8(
775
  }
776
 
777
  /**
778
+ * The robots.txt output.
779
  *
780
  * @since 4.0.5
781
  * @param string $output The (cached) robots.txt output.
885
  if ( ! isset( $wp_query->query['s'] ) )
886
  return;
887
 
888
+ if ( $this->is_query_adjustment_blocked( $wp_query ) )
889
  return;
890
 
891
  $excluded = $this->get_ids_excluded_from_search();
918
  public function _alter_archive_query_in( $wp_query ) {
919
 
920
  if ( $wp_query->is_archive || $wp_query->is_home ) {
921
+ if ( $this->is_query_adjustment_blocked( $wp_query ) )
922
  return;
923
 
924
  $excluded = $this->get_ids_excluded_from_archive();
950
  public function _alter_search_query_post( $posts, $wp_query ) {
951
 
952
  if ( $wp_query->is_search ) {
953
+ if ( $this->is_query_adjustment_blocked( $wp_query ) )
954
  return $posts;
955
 
956
  foreach ( $posts as $n => $post ) {
957
+ if ( $this->get_post_meta_item( 'exclude_local_search', $post->ID ) )
958
  unset( $posts[ $n ] );
 
959
  }
960
  //= Reset numeric index.
961
  $posts = array_values( $posts );
977
  public function _alter_archive_query_post( $posts, $wp_query ) {
978
 
979
  if ( $wp_query->is_archive || $wp_query->is_home ) {
980
+ if ( $this->is_query_adjustment_blocked( $wp_query ) )
981
  return $posts;
982
 
983
  foreach ( $posts as $n => $post ) {
984
+ if ( $this->get_post_meta_item( 'exclude_from_archive', $post->ID ) )
985
  unset( $posts[ $n ] );
 
986
  }
987
  //= Reset numeric index.
988
  $posts = array_values( $posts );
994
  /**
995
  * Determines whether the archive query adjustment is blocked.
996
  *
997
+ * We do NOT treat this feature with security: If a post still slips through
998
+ * a query, then so be it. The post may be accessed anyway, otherwise,
999
+ * if not redirected. This last part is of concern, however, because one
1000
+ * might think the contents of a post is hidden thanks to the redirect, for it
1001
+ * to be exposable via other means. Nevertheless, we never (and won't ever)
1002
+ * redirect REST queries, which may access post content regardless of user settings.
1003
+ *
1004
+ * Perhaps, we should add a disclaimer: Even IF you redirect the post, noindex it,
1005
+ * exclude it from search and archive queries, the post content may still be readable
1006
+ * to the public.
1007
+ *
1008
  * @since 2.9.4
1009
  * @since 3.1.0 Now checks for the post type.
1010
+ * @since 4.1.4 1. Renamed from `is_archive_query_adjustment_blocked()`
1011
+ * 2. Added taxonomy-supported lookups.
1012
+ * 3. Added WP Rest checks for the Block Editor.
1013
  *
1014
  * @param \WP_Query $wp_query WP_Query object.
1015
  * @return bool
1016
  */
1017
+ protected function is_query_adjustment_blocked( $wp_query ) {
1018
 
1019
  static $has_filter = null;
1020
 
 
 
1021
  if ( null === $has_filter ) {
1022
  $has_filter = \has_filter( 'the_seo_framework_do_adjust_archive_query' );
1023
  }
1024
  if ( $has_filter ) {
1025
  /**
1026
+ * This filter affects both 'search-"archives"' and terms/taxonomies.
1027
+ *
1028
  * @since 2.9.4
1029
  * @param bool $do True is unblocked (do adjustment), false is blocked (don't do adjustment).
1030
+ * @param \WP_Query $wp_query The current query.
1031
  */
1032
  if ( ! \apply_filters_ref_array( 'the_seo_framework_do_adjust_archive_query', [ true, $wp_query ] ) )
1033
+ return true;
1034
+ }
1035
+
1036
+ if ( \defined( 'REST_REQUEST' ) && REST_REQUEST ) {
1037
+ $referer = \wp_get_referer();
1038
+ if ( false !== strpos( $referer, 'post.php' ) || false !== strpos( $referer, 'post-new.php' ) ) {
1039
+ /**
1040
+ * WordPress should've authenthicated the user at
1041
+ * WP_REST_Server::check_authentication() -> rest_cookie_check_errors() -> wp_nonce et al.
1042
+ * before executing the query. For REST_REQUEST can not be true otherwise. Ergo,
1043
+ * \current_user_can() should work. If it returns true, we can trust it's a safe request.
1044
+ * If it returns false, the user may still be logged in, but the request isn't sent via
1045
+ * WordPress's API with the proper nonces supplied. This is as perfect as it can be.
1046
+ */
1047
+ if ( \current_user_can( 'edit_posts' ) )
1048
+ return true;
1049
+ }
1050
  }
1051
 
1052
+ // This primarily affects 'terms'.
1053
+ if ( ! empty( $wp_query->tax_query->queries ) ) :
1054
+ $unsupported = [];
1055
+
1056
+ foreach ( $wp_query->tax_query->queries as $_query ) {
1057
+ if ( isset( $_query['taxonomy'] ) )
1058
+ $unsupported[] = ! $this->is_taxonomy_supported( $_query['taxonomy'] );
1059
+ }
1060
+
1061
+ // Only block when taxonomies were found and all of them are unsupported.
1062
+ if ( $unsupported && ! \in_array( false, $unsupported, true ) )
1063
+ return true;
1064
+ endif;
1065
 
1066
+ return false;
1067
  }
1068
 
1069
  /**
inc/classes/interpreters/form.class.php ADDED
@@ -0,0 +1,547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Interpreters\Form
4
+ * @subpackage The_SEO_Framework\Admin\Settings
5
+ */
6
+
7
+ namespace The_SEO_Framework\Interpreters;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (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
+ * Interprets anything you send here into HTML. Or so it should.
30
+ *
31
+ * @since 4.1.4
32
+ *
33
+ * @access protected
34
+ * Everything in this class is subject to change or deletion.
35
+ * @internal
36
+ * @final Can't be extended.
37
+ */
38
+ final class Form {
39
+
40
+ /**
41
+ * Helper function that constructs header elements.
42
+ *
43
+ * @since 4.1.4
44
+ *
45
+ * @param string $title The header title.
46
+ */
47
+ public static function header_title( $title ) {
48
+ printf( '<h4>%s</h4>', \esc_html( $title ) );
49
+ }
50
+
51
+ /**
52
+ * Helper function that constructs name attributes for use in form fields.
53
+ *
54
+ * Other page implementation classes may wish to construct and use a
55
+ * get_field_id() method, if the naming format needs to be different.
56
+ *
57
+ * @since 4.1.4
58
+ *
59
+ * @param string $name Field name base
60
+ * @return string Full field name
61
+ */
62
+ public static function get_field_name( $name ) {
63
+ return sprintf( '%s[%s]', THE_SEO_FRAMEWORK_SITE_OPTIONS, $name );
64
+ }
65
+
66
+ /**
67
+ * Echo constructed name attributes in form fields.
68
+ *
69
+ * @since 4.1.4
70
+ * @uses static::get_field_name() Construct name attributes for use in form fields.
71
+ *
72
+ * @param string $name Field name base
73
+ */
74
+ public static function field_name( $name ) {
75
+ echo \esc_attr( static::get_field_name( $name ) );
76
+ }
77
+
78
+ /**
79
+ * Helper function that constructs id attributes for use in form fields.
80
+ *
81
+ * @since 4.1.4
82
+ *
83
+ * @param string $id Field id base
84
+ * @return string Full field id
85
+ */
86
+ public static function get_field_id( $id ) {
87
+ return sprintf( '%s[%s]', THE_SEO_FRAMEWORK_SITE_OPTIONS, $id );
88
+ }
89
+
90
+ /**
91
+ * Echo constructed id attributes in form fields.
92
+ *
93
+ * @since 4.1.4
94
+ * @uses static::get_field_id() Constructs id attributes for use in form fields.
95
+ *
96
+ * @param string $id Field id base.
97
+ * @param boolean $echo Whether to escape echo or just return.
98
+ * @return string Full field id
99
+ */
100
+ public static function field_id( $id, $echo = true ) {
101
+ if ( $echo ) {
102
+ echo \esc_attr( static::get_field_id( $id ) );
103
+ } else {
104
+ return static::get_field_id( $id );
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Returns a chechbox wrapper.
110
+ *
111
+ * @since 4.1.4
112
+ *
113
+ * @param array $args : {
114
+ * string $id The option name, used as field ID.
115
+ * string $class The checkbox class.
116
+ * string $index The option index, used when the option is an array.
117
+ * string $label The checkbox label description, placed inline of the checkbox.
118
+ * string $description The checkbox additional description, placed underneat.
119
+ * array $data The checkbox field data. Sub-items are expected to be escaped if they're not an array.
120
+ * bool $escape Whether to enable escaping of the $label and $description.
121
+ * bool $disabled Whether to disable the checkbox field.
122
+ * bool $default Whether to display-as-default. This is autodetermined when no $index is set.
123
+ * bool $warned Whether to warn the checkbox field value.
124
+ * }
125
+ * @return string HTML checkbox output.
126
+ */
127
+ public static function make_checkbox( array $args = [] ) {
128
+
129
+ $args = array_merge(
130
+ [
131
+ 'id' => '',
132
+ 'class' => '',
133
+ 'index' => '',
134
+ 'label' => '',
135
+ 'description' => '',
136
+ 'data' => [],
137
+ 'escape' => true,
138
+ 'disabled' => false,
139
+ 'default' => false,
140
+ 'warned' => false,
141
+ ],
142
+ $args
143
+ );
144
+
145
+ if ( $args['escape'] ) {
146
+ $args['description'] = \esc_html( $args['description'] );
147
+ $args['label'] = \esc_html( $args['label'] );
148
+ }
149
+
150
+ $tsf = \the_seo_framework();
151
+
152
+ $index = $args['index'] ? $tsf->s_field_id( $args['index'] ?: '' ) : '';
153
+
154
+ $field_id = $field_name = \esc_attr( sprintf(
155
+ '%s%s',
156
+ Form::get_field_id( $args['id'] ),
157
+ $index ? sprintf( '[%s]', $index ) : ''
158
+ ) );
159
+
160
+ $value = $tsf->get_option( $args['id'] );
161
+ if ( $index ) {
162
+ $value = isset( $value[ $index ] ) ? $value[ $index ] : '';
163
+ }
164
+
165
+ $cb_classes = [];
166
+
167
+ if ( $args['class'] ) {
168
+ $cb_classes[] = $args['class'];
169
+ }
170
+
171
+ if ( $args['disabled'] ) {
172
+ $cb_classes[] = 'tsf-disabled';
173
+ } elseif ( ! $args['index'] ) {
174
+ // Can't fetch conditionals in index.
175
+ $cb_classes[] = static::get_is_conditional_checked( $args['id'], false );
176
+ } else {
177
+ if ( $args['default'] ) {
178
+ $cb_classes[] = 'tsf-default-selected';
179
+ } elseif ( $args['warned'] ) {
180
+ $cb_classes[] = 'tsf-warning-selected';
181
+ }
182
+ }
183
+
184
+ $output = sprintf(
185
+ '<span class="tsf-toblock">%s</span>',
186
+ vsprintf(
187
+ '<label for="%s" %s>%s</label>',
188
+ [
189
+ $field_id,
190
+ ( $args['disabled'] ? 'class="tsf-disabled"' : '' ),
191
+ vsprintf(
192
+ '<input type=checkbox class="%s" name="%s" id="%s" value="1" %s %s %s /> %s',
193
+ [
194
+ \esc_attr( implode( ' ', $cb_classes ) ),
195
+ $field_name,
196
+ $field_id,
197
+ \checked( $value, true, false ),
198
+ ( $args['disabled'] ? 'disabled' : '' ),
199
+ $args['data'] ? HTML::make_data_attributes( $args['data'] ) : '',
200
+ $args['label'],
201
+ ]
202
+ ),
203
+ ]
204
+ )
205
+ );
206
+
207
+ $output .= $args['description'] ? sprintf( '<p class="description tsf-option-spacer">%s</p>', $args['description'] ) : '';
208
+
209
+ return $output;
210
+ }
211
+
212
+ /**
213
+ * Returns a HTML select form elements for qubit options: -1, 0, or 1.
214
+ * Does not support "multiple" field selections.
215
+ *
216
+ * @since 4.1.4
217
+ * @TODO allow arrays as index, so we can support multidimensional options easily? @see is_conditional_checked
218
+ *
219
+ * @param array $args : {
220
+ * string $id The select field ID.
221
+ * string $class The div wrapper class.
222
+ * string $name The option name.
223
+ * int|string $default The current option value.
224
+ * array $options The select option values : { value => name }
225
+ * string $label The option label.
226
+ * string $required Whether the field must be required.
227
+ * array $data The select field data. Sub-items are expected to be escaped if they're not an array.
228
+ * array $info Extra info field data.
229
+ * }
230
+ * @return string The option field.
231
+ */
232
+ public static function make_single_select_form( array $args ) {
233
+
234
+ $defaults = [
235
+ 'id' => '',
236
+ 'class' => '',
237
+ 'name' => '',
238
+ 'default' => '',
239
+ 'options' => [],
240
+ 'label' => '',
241
+ 'required' => false,
242
+ 'data' => [],
243
+ 'info' => [],
244
+ ];
245
+
246
+ $args = array_merge( $defaults, $args );
247
+
248
+ // The walk below destroys the option array. Assign it to a new var to prevent confusion later.
249
+ $html_options = $args['options'];
250
+ /**
251
+ * @param string $name The option name. Passed by reference, returned as the HTML option item.
252
+ * @param mixed $value
253
+ * @param mixed $default
254
+ */
255
+ $create_option = static function( &$name, $value, $default ) {
256
+ $name = sprintf(
257
+ '<option value="%s"%s>%s</option>',
258
+ \esc_attr( $value ),
259
+ (string) $value === (string) $default ? ' selected' : '',
260
+ \esc_html( $name )
261
+ );
262
+ };
263
+ array_walk( $html_options, $create_option, $args['default'] );
264
+
265
+ $tsf = \the_seo_framework();
266
+
267
+ return vsprintf(
268
+ sprintf( '<div class="%s">%s</div>',
269
+ \esc_attr( $args['class'] ),
270
+ ( \is_rtl() ? '%2$s%1$s%3$s' : '%1$s%2$s%3$s' )
271
+ ),
272
+ [
273
+ $args['label'] ? sprintf(
274
+ '<label for=%s>%s</label> ', // NOTE: extra space!
275
+ $tsf->s_field_id( $args['id'] ),
276
+ \esc_html( $args['label'] )
277
+ ) : '',
278
+ $args['info'] ? ' ' . HTML::make_info(
279
+ $args['info'][0],
280
+ isset( $args['info'][1] ) ? $args['info'][1] : '',
281
+ false
282
+ ) : '',
283
+ vsprintf(
284
+ '<select id=%s name=%s %s %s>%s</select>',
285
+ [
286
+ $tsf->s_field_id( $args['id'] ),
287
+ \esc_attr( $args['name'] ),
288
+ $args['required'] ? 'required' : '',
289
+ $args['data'] ? HTML::make_data_attributes( $args['data'] ) : '',
290
+ implode( $html_options ),
291
+ ]
292
+ ),
293
+ ]
294
+ );
295
+ }
296
+
297
+ /**
298
+ * Returns the HTML class wrap for default Checkbox options.
299
+ *
300
+ * This function does nothing special. But is merely a simple wrapper.
301
+ * Just like code_wrap.
302
+ *
303
+ * @since 4.1.4
304
+ *
305
+ * @param string $key The option name which returns boolean.
306
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
307
+ * @param bool $echo Whether to echo or return the output.
308
+ * @return string Empty on echo or the class name with an optional wrapper.
309
+ */
310
+ public static function is_default_checked( $key, $wrap = true, $echo = true ) {
311
+
312
+ $class = '';
313
+
314
+ $default = \the_seo_framework()->get_default_settings( $key );
315
+
316
+ if ( 1 === $default )
317
+ $class = 'tsf-default-selected';
318
+
319
+ if ( $echo ) {
320
+ if ( $wrap ) {
321
+ printf( 'class="%s"', \esc_attr( $class ) );
322
+ } else {
323
+ echo \esc_attr( $class );
324
+ }
325
+ } else {
326
+ if ( $wrap )
327
+ return sprintf( 'class="%s"', $class );
328
+
329
+ return $class;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Returns the HTML class wrap for warning Checkbox options.
335
+ *
336
+ * @since 4.1.4
337
+ *
338
+ * @param string $key The option name which returns boolean.
339
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
340
+ * @param bool $echo Whether to echo or return the output.
341
+ * @return string Empty on echo or the class name with an optional wrapper.
342
+ */
343
+ public static function is_warning_checked( $key, $wrap = true, $echo = true ) {
344
+
345
+ $class = '';
346
+
347
+ $warned = \the_seo_framework()->get_warned_settings( $key );
348
+
349
+ if ( 1 === $warned )
350
+ $class = 'tsf-warning-selected';
351
+
352
+ if ( $echo ) {
353
+ if ( $wrap ) {
354
+ printf( 'class="%s"', \esc_attr( $class ) );
355
+ } else {
356
+ echo \esc_attr( $class );
357
+ }
358
+ } else {
359
+ if ( $wrap )
360
+ return sprintf( 'class="%s"', $class );
361
+
362
+ return $class;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Returns the HTML class wrap for warning/default Checkbox options.
368
+ *
369
+ * @since 4.1.4
370
+ *
371
+ * @param string $key The option name which returns boolean.
372
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
373
+ */
374
+ public static function get_is_conditional_checked( $key, $wrap = true ) {
375
+ return static::is_conditional_checked( $key, '', $wrap, false );
376
+ }
377
+
378
+ /**
379
+ * Returns the HTML class wrap for warning/default Checkbox options.
380
+ *
381
+ * @since 4.1.4
382
+ *
383
+ * @param string $key The option name which returns boolean.
384
+ * @param bool $wrap Whether to wrap the class name in `class="%s"`
385
+ * @param bool $echo Whether to echo or return the output.
386
+ * @return string Empty on echo or the class name with an optional wrapper.
387
+ */
388
+ public static function is_conditional_checked( $key, $wrap = true, $echo = true ) {
389
+
390
+ $class = '';
391
+
392
+ $default = static::is_default_checked( $key, false, false );
393
+ $warned = static::is_warning_checked( $key, false, false );
394
+
395
+ if ( '' !== $default && '' !== $warned ) {
396
+ $class = $default . ' ' . $warned;
397
+ } elseif ( '' !== $default ) {
398
+ $class = $default;
399
+ } elseif ( '' !== $warned ) {
400
+ $class = $warned;
401
+ }
402
+
403
+ if ( $echo ) {
404
+ if ( $wrap ) {
405
+ printf( 'class="%s"', \esc_attr( $class ) );
406
+ } else {
407
+ echo \esc_attr( $class );
408
+ }
409
+ } else {
410
+ if ( $wrap )
411
+ return sprintf( 'class="%s"', $class );
412
+
413
+ return $class;
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Outputs character counter wrap for both JavaScript and no-Javascript.
419
+ *
420
+ * @since 4.1.4
421
+ *
422
+ * @param string $for The input ID it's for.
423
+ * @param bool $display Whether to display the counter. (options page gimmick)
424
+ */
425
+ public static function output_character_counter_wrap( $for, $display = true ) {
426
+ vprintf(
427
+ '<div class="tsf-counter-wrap hide-if-no-tsf-js" %s><span class=tsf-counter title="%s">%s</span><span class=tsf-ajax></span></div>',
428
+ [
429
+ ( $display ? '' : 'style=display:none;' ),
430
+ \esc_attr__( 'Click to change the counter type', 'autodescription' ),
431
+ sprintf(
432
+ /* translators: %s = number */
433
+ \esc_html__( 'Characters: %s', 'autodescription' ),
434
+ sprintf(
435
+ '<span id="%s">%s</span>',
436
+ \esc_attr( "{$for}_chars" ),
437
+ 0
438
+ )
439
+ ),
440
+ ]
441
+ );
442
+ }
443
+
444
+ /**
445
+ * Outputs pixel counter wrap for javascript.
446
+ *
447
+ * @since 4.1.4
448
+ *
449
+ * @param string $for The input ID it's for.
450
+ * @param string $type Whether it's a 'title' or 'description' counter.
451
+ * @param bool $display Whether to display the counter. (options page gimmick)
452
+ */
453
+ public static function output_pixel_counter_wrap( $for, $type, $display = true ) {
454
+ vprintf(
455
+ '<div class="tsf-pixel-counter-wrap hide-if-no-tsf-js" %s>%s%s</div>',
456
+ [
457
+ ( $display ? '' : 'style="display:none;"' ),
458
+ sprintf(
459
+ '<div id="%s" class="tsf-tooltip-wrap">%s</div>',
460
+ \esc_attr( "{$for}_pixels" ),
461
+ '<span class="tsf-pixel-counter-bar tsf-tooltip-item" aria-label="" data-desc="" tabindex=0><span class="tsf-pixel-counter-fluid"></span></span>'
462
+ ),
463
+ sprintf(
464
+ '<div class="tsf-pixel-shadow-wrap"><span class="tsf-pixel-counter-shadow %s"></span></div>',
465
+ \esc_attr( "tsf-{$type}-pixel-counter-shadow" )
466
+ ),
467
+ ]
468
+ );
469
+ }
470
+
471
+ /**
472
+ * Returns image uploader form button.
473
+ * Also registers additional i18n strings for JS, and registers a tooltip for image preview.
474
+ *
475
+ * The default arguments conform to Facebook and Twitter image sharing.
476
+ *
477
+ * @since 4.1.4
478
+ *
479
+ * @param array $args Required. The image uploader arguments : {
480
+ * 'id' => string Required. The HTML input id to pass URL into.
481
+ * 'post_id' => int Optional. The Post ID to bind the uploaded file to. Default current post ID.
482
+ * 'data' => [
483
+ * 'inputType' => string Optional. Whether the upload type is 'social' or 'logo' for i18n. Default 'social'.
484
+ * 'width' => int Optional. The suggested image width. Default 1200.
485
+ * 'height' => int Optional. The suggested image height. Default 630.
486
+ * 'minWidth' => int Optional. The minimum image width. Default 200.
487
+ * 'minHeight' => int Optional. The minimum image height. Default 200.
488
+ * 'flex' => bool Optional. Whether the image W:H ratio may be changed. Default true.
489
+ * ],
490
+ * 'i18n' => [
491
+ * 'button_title' => string Optional. The image-select button on-hover title for accessibility. Default ''.
492
+ * Tip: Only fill if 'button_text' is ambiguous.
493
+ * 'button_text' => string Optional. The image-select button title. Defaults l10n 'Select Image',
494
+ * ],
495
+ * }
496
+ * @return string The image uploader button.
497
+ */
498
+ public static function get_image_uploader_form( array $args ) {
499
+
500
+ static $image_input_id = 0;
501
+ $image_input_id++;
502
+
503
+ $tsf = \the_seo_framework();
504
+
505
+ $defaults = [
506
+ 'id' => null,
507
+ 'post_id' => $tsf->get_the_real_ID(),
508
+ 'data' => [
509
+ 'inputType' => 'social',
510
+ 'width' => 1200, // TODO make 1280 - 80px overflow margin? It'd be better for mixed platforms.
511
+ 'height' => 630, // TODO make 640 - 80px overflow margin? It'd be better for mixed platforms.
512
+ 'minWidth' => 200,
513
+ 'minHeight' => 200,
514
+ 'flex' => true,
515
+ ],
516
+ 'i18n' => [
517
+ 'button_title' => '',
518
+ 'button_text' => \__( 'Select Image', 'autodescription' ),
519
+ ],
520
+ ];
521
+
522
+ $args = $tsf->array_merge_recursive_distinct( $defaults, $args );
523
+
524
+ if ( ! $args['id'] ) return '';
525
+
526
+ $content = vsprintf(
527
+ '<button type=button data-href="%s" class="tsf-set-image-button button button-primary button-small" title="%s" id="%s-select" %s>%s</button>',
528
+ [
529
+ \esc_url( \get_upload_iframe_src( 'image', $defaults['post_id'] ) ),
530
+ \esc_attr( $args['i18n']['button_title'] ),
531
+ \esc_attr( $args['id'] ),
532
+ HTML::make_data_attributes(
533
+ [ 'inputId' => $args['id'] ]
534
+ + $args['data']
535
+ ),
536
+ \esc_html( $args['i18n']['button_text'] ),
537
+ ]
538
+ );
539
+
540
+ $content .= sprintf(
541
+ '<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>',
542
+ \esc_attr( $args['id'] )
543
+ );
544
+
545
+ return $content;
546
+ }
547
+ }
inc/classes/interpreters/html.class.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Interpreters\HTML
4
+ * @subpackage The_SEO_Framework\Admin\Settings
5
+ */
6
+
7
+ namespace The_SEO_Framework\Interpreters;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (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
+ * Interprets anything you send here into HTML. Or so it should.
30
+ *
31
+ * @since 4.1.4
32
+ *
33
+ * @access protected
34
+ * Everything in this class is subject to change or deletion.
35
+ * @internal
36
+ * @final Can't be extended.
37
+ */
38
+ final class HTML {
39
+
40
+ /**
41
+ * Mark up content with code tags.
42
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
43
+ *
44
+ * @since 4.1.4
45
+ *
46
+ * @param string $content Content to be wrapped in code tags.
47
+ * @return string Content wrapped in code tags.
48
+ */
49
+ public static function code_wrap( $content ) {
50
+ return static::code_wrap_noesc( \esc_html( $content ) );
51
+ }
52
+
53
+ /**
54
+ * Mark up content with code tags.
55
+ * Escapes no HTML.
56
+ *
57
+ * @since 4.1.4
58
+ *
59
+ * @param string $content Content to be wrapped in code tags.
60
+ * @return string Content wrapped in code tags.
61
+ */
62
+ public static function code_wrap_noesc( $content ) {
63
+ return '<code>' . $content . '</code>';
64
+ }
65
+
66
+ /**
67
+ * Mark up content in description wrap.
68
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
69
+ *
70
+ * @since 4.1.4
71
+ *
72
+ * @param string $content Content to be wrapped in the description wrap.
73
+ * @param bool $block Whether to wrap the content in <p> tags.
74
+ */
75
+ public static function description( $content, $block = true ) {
76
+ static::description_noesc( \esc_html( $content ), $block );
77
+ }
78
+
79
+ /**
80
+ * Mark up content in description wrap.
81
+ *
82
+ * @since 4.1.4
83
+ *
84
+ * @param string $content Content to be wrapped in the description wrap. Expected to be escaped.
85
+ * @param bool $block Whether to wrap the content in <p> tags.
86
+ */
87
+ public static function description_noesc( $content, $block = true ) {
88
+ $output = '<span class="description">' . $content . '</span>';
89
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
90
+ echo $block ? '<p>' . $output . '</p>' : $output;
91
+ }
92
+
93
+ /**
94
+ * Mark up content in attention wrap.
95
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
96
+ *
97
+ * @since 4.1.4
98
+ *
99
+ * @param string $content Content to be wrapped in the attention wrap.
100
+ * @param bool $block Whether to wrap the content in <p> tags.
101
+ */
102
+ public static function attention( $content, $block = true ) {
103
+ static::attention_noesc( \esc_html( $content ), $block );
104
+ }
105
+
106
+ /**
107
+ * Mark up content in attention wrap.
108
+ *
109
+ * @since 3.1.0
110
+ *
111
+ * @param string $content Content to be wrapped in the attention wrap. Expected to be escaped.
112
+ * @param bool $block Whether to wrap the content in <p> tags.
113
+ */
114
+ public static function attention_noesc( $content, $block = true ) {
115
+ $output = '<span class="attention">' . $content . '</span>';
116
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
117
+ echo $block ? '<p>' . $output . '</p>' : $output;
118
+ }
119
+
120
+ /**
121
+ * Mark up content in a description+attention wrap.
122
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
123
+ *
124
+ * @since 3.1.0
125
+ *
126
+ * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
127
+ * @param bool $block Whether to wrap the content in <p> tags.
128
+ */
129
+ public static function attention_description( $content, $block = true ) {
130
+ static::attention_description_noesc( \esc_html( $content ), $block );
131
+ }
132
+
133
+ /**
134
+ * Mark up content in a description+attention wrap.
135
+ *
136
+ * @since 3.1.0
137
+ *
138
+ * @param string $content Content to be wrapped in the wrap. Expected to be escaped.
139
+ * @param bool $block Whether to wrap the content in <p> tags.
140
+ */
141
+ public static function attention_description_noesc( $content, $block = true ) {
142
+ $output = '<span class="description attention">' . $content . '</span>';
143
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- Method clearly states it's not escaped.
144
+ echo $block ? '<p>' . $output . '</p>' : $output;
145
+ }
146
+
147
+ /**
148
+ * Echo or return a chechbox fields wrapper.
149
+ *
150
+ * This method does NOT escape.
151
+ *
152
+ * @since 2.6.0
153
+ *
154
+ * @param string $input The input to wrap. Should already be escaped.
155
+ * @param bool $echo Whether to escape echo or just return.
156
+ * @return string|void Wrapped $input.
157
+ */
158
+ public static function wrap_fields( $input = '', $echo = false ) {
159
+
160
+ if ( \is_array( $input ) )
161
+ $input = implode( PHP_EOL, $input );
162
+
163
+ if ( $echo ) {
164
+ // phpcs:ignore, WordPress.Security.EscapeOutput -- Escape your $input prior!
165
+ echo '<div class="tsf-fields">' . $input . '</div>';
166
+ } else {
167
+ return '<div class="tsf-fields">' . $input . '</div>';
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Return a wrapped question mark.
173
+ *
174
+ * @since 2.6.0
175
+ * @since 3.0.0 Links are now no longer followed, referred or bound to opener.
176
+ * @since 4.0.0 Now adds a tabindex to the span tag, so you can focus it using keyboard navigation.
177
+ *
178
+ * @param string $description The descriptive on-hover title.
179
+ * @param string $link The non-escaped link.
180
+ * @param bool $echo Whether to echo or return.
181
+ * @return string HTML checkbox output if $echo is false.
182
+ */
183
+ public static function make_info( $description = '', $link = '', $echo = true ) {
184
+
185
+ if ( $link ) {
186
+ $output = sprintf(
187
+ '<a href="%1$s" class="tsf-tooltip-item tsf-help" target="_blank" rel="nofollow noreferrer noopener" title="%2$s" data-desc="%2$s">[?]</a>',
188
+ \esc_url( $link, [ 'https', 'http' ] ),
189
+ \esc_attr( $description )
190
+ );
191
+ } else {
192
+ $output = sprintf(
193
+ '<span class="tsf-tooltip-item tsf-help" title="%1$s" data-desc="%1$s" tabindex=0>[?]</span>',
194
+ \esc_attr( $description )
195
+ );
196
+ }
197
+
198
+ $output = sprintf( '<span class="tsf-tooltip-wrap">%s</span>', $output );
199
+
200
+ if ( $echo ) {
201
+ // phpcs:ignore, WordPress.Security.EscapeOutput
202
+ echo $output;
203
+ } else {
204
+ return $output;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Makes either simple or JSON-encoded data-* attributes for HTML elements.
210
+ *
211
+ * @since 4.0.0
212
+ * @since 4.1.0 No longer adds an extra space in front of the return value when no data is generated.
213
+ * @internal
214
+ *
215
+ * @param array $data : {
216
+ * string $k => mixed $v
217
+ * }
218
+ * @return string The HTML data attributes, with added space to the start.
219
+ */
220
+ public static function make_data_attributes( array $data ) {
221
+
222
+ $ret = [];
223
+
224
+ foreach ( $data as $k => $v ) {
225
+ if ( ! is_scalar( $v ) ) {
226
+ $ret[] = sprintf(
227
+ 'data-%s="%s"',
228
+ strtolower( preg_replace(
229
+ '/([A-Z])/',
230
+ '-$1',
231
+ preg_replace( '/[^a-z0-9_\-]/i', '', $k )
232
+ ) ), // dash case.
233
+ htmlspecialchars( json_encode( $v, JSON_UNESCAPED_SLASHES ), ENT_COMPAT, 'UTF-8' )
234
+ );
235
+ } else {
236
+ $ret[] = sprintf(
237
+ 'data-%s="%s"',
238
+ strtolower( preg_replace(
239
+ '/([A-Z])/',
240
+ '-$1',
241
+ preg_replace( '/[^a-z0-9_\-]/i', '', $k )
242
+ ) ), // dash case.
243
+ \esc_attr( $v )
244
+ );
245
+ }
246
+ }
247
+
248
+ return $ret ? ' ' . implode( ' ', $ret ) : '';
249
+ }
250
+ }
inc/classes/interpreters/markdown.class.php ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package The_SEO_Framework\Classes\Interpreters\Markdown
4
+ * @subpackage The_SEO_Framework\Admin\Settings
5
+ */
6
+
7
+ namespace The_SEO_Framework\Interpreters;
8
+
9
+ /**
10
+ * The SEO Framework plugin
11
+ * Copyright (C) 2021 Sybre Waaijer, CyberWire B.V. (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
+ * Interprets anything you send here into Markdown. Or so it should.
30
+ *
31
+ * @since 4.1.4
32
+ * @note Use `the_seo_framework()->convert_markdown() for easy access.
33
+ *
34
+ * @access protected
35
+ * Everything in this class is subject to change or deletion.
36
+ * @internal
37
+ * @final Can't be extended.
38
+ */
39
+ final class Markdown {
40
+
41
+ /**
42
+ * Converts markdown text into HMTL.
43
+ * Does not support list or block elements. Only inline statements.
44
+ *
45
+ * Note: This code has been rightfully stolen from the Extension Manager plugin (sorry Sybre!).
46
+ *
47
+ * @since 4.1.4
48
+ * @link https://wordpress.org/plugins/about/readme.txt
49
+ *
50
+ * @param string $text The text that might contain markdown. Expected to be escaped.
51
+ * @param array $convert The markdown style types wished to be converted.
52
+ * If left empty, it will convert all.
53
+ * @param array $args The function arguments.
54
+ * @return string The markdown converted text.
55
+ */
56
+ public static function convert( $text, $convert = [], $args = [] ) {
57
+
58
+ // preprocess
59
+ $text = str_replace( "\r\n", "\n", $text );
60
+ $text = str_replace( "\t", ' ', $text );
61
+ $text = trim( $text );
62
+
63
+ // You need 3 chars to make a markdown: *m*
64
+ if ( \strlen( $text ) < 3 )
65
+ return '';
66
+
67
+ // Merge defaults with $args.
68
+ $args = array_merge( [ 'a_internal' => false ], $args );
69
+
70
+ /**
71
+ * The conversion list's keys are per reference only.
72
+ */
73
+ $conversions = [
74
+ '**' => 'strong',
75
+ '*' => 'em',
76
+ '`' => 'code',
77
+ '[]()' => 'a',
78
+ '======' => 'h6',
79
+ '=====' => 'h5',
80
+ '====' => 'h4',
81
+ '===' => 'h3',
82
+ '==' => 'h2',
83
+ '=' => 'h1',
84
+ ];
85
+
86
+ $md_types = empty( $convert ) ? $conversions : array_intersect( $conversions, $convert );
87
+
88
+ if ( 2 === \count( array_intersect( $md_types, [ 'em', 'strong' ] ) ) ) :
89
+ $text = static::strong_em( $text );
90
+ endif;
91
+
92
+ foreach ( $md_types as $type ) :
93
+ switch ( $type ) :
94
+ case 'strong':
95
+ $text = static::strong( $text );
96
+ break;
97
+
98
+ case 'em':
99
+ $text = static::em( $text );
100
+ break;
101
+
102
+ case 'code':
103
+ $text = static::code( $text );
104
+ break;
105
+
106
+ case 'h6':
107
+ case 'h5':
108
+ case 'h4':
109
+ case 'h3':
110
+ case 'h2':
111
+ case 'h1':
112
+ $text = static::h123456( $text, $type );
113
+ break;
114
+
115
+ case 'a':
116
+ $text = static::a( $text, $args['a_internal'] );
117
+ break;
118
+
119
+ default:
120
+ break;
121
+ endswitch;
122
+ endforeach;
123
+
124
+ return $text;
125
+ }
126
+
127
+ /**
128
+ * Makes strong>em elements.
129
+ * We do this separately because em and strong use the same operators.
130
+ *
131
+ * @since 4.1.4
132
+ *
133
+ * @param string $text The input text.
134
+ * @return string
135
+ */
136
+ private static function strong_em( $text ) {
137
+
138
+ $count = preg_match_all( '/(?:\*{3})([^\*{\3}]+)(?:\*{3})/', $text, $matches, PREG_PATTERN_ORDER );
139
+ for ( $i = 0; $i < $count; $i++ ) {
140
+ $text = str_replace(
141
+ $matches[0][ $i ],
142
+ sprintf( '<strong><em>%s</em></strong>', \esc_html( $matches[1][ $i ] ) ),
143
+ $text
144
+ );
145
+ }
146
+
147
+ return $text;
148
+ }
149
+
150
+ /**
151
+ * Makes strong elements.
152
+ *
153
+ * @since 4.1.4
154
+ *
155
+ * @param string $text The input text.
156
+ * @return string
157
+ */
158
+ private static function strong( $text ) {
159
+
160
+ $count = preg_match_all( '/(?:\*{2})([^\*{\2}]+)(?:\*{2})/', $text, $matches, PREG_PATTERN_ORDER );
161
+
162
+ for ( $i = 0; $i < $count; $i++ ) {
163
+ $text = str_replace(
164
+ $matches[0][ $i ],
165
+ sprintf( '<strong>%s</strong>', \esc_html( $matches[1][ $i ] ) ),
166
+ $text
167
+ );
168
+ }
169
+
170
+ return $text;
171
+ }
172
+
173
+ /**
174
+ * Makes em elements.
175
+ *
176
+ * @since 4.1.4
177
+ *
178
+ * @param string $text The input text.
179
+ * @return string
180
+ */
181
+ private static function em( $text ) {
182
+
183
+ $count = preg_match_all( '/(?:\*{1})([^\*{\1}]+)(?:\*{1})/', $text, $matches, PREG_PATTERN_ORDER );
184
+
185
+ for ( $i = 0; $i < $count; $i++ ) {
186
+ $text = str_replace(
187
+ $matches[0][ $i ],
188
+ sprintf( '<em>%s</em>', \esc_html( $matches[1][ $i ] ) ),
189
+ $text
190
+ );
191
+ }
192
+
193
+ return $text;
194
+ }
195
+
196
+ /**
197
+ * Makes code elements.
198
+ *
199
+ * @since 4.1.4
200
+ *
201
+ * @param string $text The input text.
202
+ * @return string
203
+ */
204
+ private static function code( $text ) {
205
+
206
+ $count = preg_match_all( '/(?:`{1})([^`{\1}]+)(?:`{1})/', $text, $matches, PREG_PATTERN_ORDER );
207
+
208
+ for ( $i = 0; $i < $count; $i++ ) {
209
+ $text = str_replace(
210
+ $matches[0][ $i ],
211
+ sprintf( '<code>%s</code>', \esc_html( $matches[1][ $i ] ) ),
212
+ $text
213
+ );
214
+ }
215
+ return $text;
216
+ }
217
+
218
+ /**
219
+ * Makes header h1~6 elements.
220
+ *
221
+ * @since 4.1.4
222
+ *
223
+ * @param string $text The input text.
224
+ * @param string $type The header type. Accepts `/h[1-6]{1}/`.
225
+ * @return string
226
+ */
227
+ private static function h123456( $text, $type = 'h1' ) {
228
+
229
+ // Considers word non-boundary. @TODO consider removing that?
230
+ $expression = sprintf(
231
+ '/(?:\={%1$d})\B([^\={\%1$s}]+)\B(?:\={%1$d})/',
232
+ filter_var( $type, FILTER_SANITIZE_NUMBER_INT )
233
+ );
234
+
235
+ $count = preg_match_all( $expression, $text, $matches, PREG_PATTERN_ORDER );
236
+
237
+ for ( $i = 0; $i < $count; $i++ ) {
238
+ $text = str_replace(
239
+ $matches[0][ $i ],
240
+ sprintf( '<%1$s>%2$s</%1$s>', \esc_attr( $type ), \esc_html( $matches[1][ $i ] ) ),
241
+ $text
242
+ );
243
+ }
244
+
245
+ return $text;
246
+ }
247
+
248
+ /**
249
+ * Makes a elements.
250
+ *
251
+ * @since 4.1.4
252
+ *
253
+ * @param string $text The input text.
254
+ * @param bool $internal Whether the link is internal (_self) or external (_blank).
255
+ * External-type links also get no-follow/referrer/opener'd.
256
+ * @return string
257
+ */
258
+ private static function a( $text, $internal = true ) {
259
+
260
+ $count = preg_match_all( '/(?:(?:\[{1})([^\]]+)(?:\]{1})(?:\({1})([^\)\(]+)(?:\){1}))/', $text, $matches, PREG_PATTERN_ORDER );
261
+
262
+ $_string = $internal ? '<a href="%s">%s</a>' : '<a href="%s" target="_blank" rel="nofollow noreferrer noopener">%s</a>';
263
+
264
+ for ( $i = 0; $i < $count; $i++ ) {
265
+ $text = str_replace(
266
+ $matches[0][ $i ],
267
+ sprintf( $_string, \esc_url( $matches[2][ $i ], [ 'https', 'http' ] ), \esc_html( $matches[1][ $i ] ) ),
268
+ $text
269
+ );
270
+ }
271
+
272
+ return $text;
273
+ }
274
+ }
inc/classes/interpreters/seobar.class.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Interpreters;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -33,6 +33,7 @@ namespace The_SEO_Framework\Interpreters;
33
  *
34
  * @access public
35
  * Note that you can't instance this class. Only static methods and properties are accessible.
 
36
  */
37
  final class SeoBar {
38
 
@@ -84,7 +85,10 @@ final class SeoBar {
84
  }
85
 
86
  /**
 
 
87
  * @since 4.0.0
 
88
  *
89
  * @param array $query : {
90
  * int $id : Required. The current post or term ID.
@@ -110,11 +114,17 @@ final class SeoBar {
110
  if ( ! static::$query['taxonomy'] )
111
  static::$query['post_type'] = static::$query['post_type'] ?: \get_post_type( static::$query['id'] );
112
 
 
 
 
 
 
 
113
  $instance = static::get_instance();
114
- $instance->store_default_bar_items();
115
 
116
  /**
117
- * Add or adjust SEO Bar items here.
118
  *
119
  * @link Example: https://gist.github.com/sybrew/59130560fcbeb98f7580dc11c54ba174
120
  * @since 4.0.0
@@ -126,6 +136,7 @@ final class SeoBar {
126
 
127
  // There's no need to leak memory.
128
  $instance->clear_seo_bar_items();
 
129
 
130
  return $bar;
131
  }
@@ -203,23 +214,21 @@ final class SeoBar {
203
  * Stores the SEO Bar items.
204
  *
205
  * @since 4.0.0
 
206
  * @factory
 
 
207
  */
208
- private function store_default_bar_items() {
209
-
210
- if ( static::$query['taxonomy'] ) {
211
- $builder = \The_SEO_Framework\Builders\SeoBar_Term::get_instance();
212
- } else {
213
- $builder = \The_SEO_Framework\Builders\SeoBar_Page::get_instance();
214
- }
215
 
216
  /**
217
- * Adjust interpreter and builder items here.
218
  *
219
  * The only use we can think of here is removing items from `$builder::$tests`,
220
- * and reading `$interpreter::$query`. Do not add tests here. Do not alter the query.
221
  *
222
  * @link Example: https://gist.github.com/sybrew/03dd428deadc860309879e1d5208e1c4
 
223
  * @since 4.0.0
224
  * @param string $interpreter The current class name.
225
  * @param \The_SEO_Framework\Builders\SeoBar $builder The builder object.
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
33
  *
34
  * @access public
35
  * Note that you can't instance this class. Only static methods and properties are accessible.
36
+ * @final Can't be extended.
37
  */
38
  final class SeoBar {
39
 
85
  }
86
 
87
  /**
88
+ * Generates the SEO Bar.
89
+ *
90
  * @since 4.0.0
91
+ * @since 4.1.4 Now manages the builder, too.
92
  *
93
  * @param array $query : {
94
  * int $id : Required. The current post or term ID.
114
  if ( ! static::$query['taxonomy'] )
115
  static::$query['post_type'] = static::$query['post_type'] ?: \get_post_type( static::$query['id'] );
116
 
117
+ if ( static::$query['taxonomy'] ) {
118
+ $builder = \The_SEO_Framework\Builders\SeoBar_Term::get_instance();
119
+ } else {
120
+ $builder = \The_SEO_Framework\Builders\SeoBar_Page::get_instance();
121
+ }
122
+
123
  $instance = static::get_instance();
124
+ $instance->store_default_bar_items( $builder );
125
 
126
  /**
127
+ * Add or adjust SEO Bar items here, after the tests have run.
128
  *
129
  * @link Example: https://gist.github.com/sybrew/59130560fcbeb98f7580dc11c54ba174
130
  * @since 4.0.0
136
 
137
  // There's no need to leak memory.
138
  $instance->clear_seo_bar_items();
139
+ $builder->clear_query_cache();
140
 
141
  return $bar;
142
  }
214
  * Stores the SEO Bar items.
215
  *
216
  * @since 4.0.0
217
+ * @since 4.1.4 Offloaded the builder's instantiation.
218
  * @factory
219
+ *
220
+ * @param \The_SEO_Framework\Builders\SeoBar $builder The builder instance.
221
  */
222
+ private function store_default_bar_items( $builder ) {
 
 
 
 
 
 
223
 
224
  /**
225
+ * Adjust interpreter and builder items here, before the tests have run.
226
  *
227
  * The only use we can think of here is removing items from `$builder::$tests`,
228
+ * and reading `$builder::$query{_cache}`. Do not add tests here. Do not alter the query.
229
  *
230
  * @link Example: https://gist.github.com/sybrew/03dd428deadc860309879e1d5208e1c4
231
+ * @see related (recommended) action 'the_seo_framework_seo_bar'
232
  * @since 4.0.0
233
  * @param string $interpreter The current class name.
234
  * @param \The_SEO_Framework\Builders\SeoBar $builder The builder object.
inc/classes/load.class.php CHANGED
@@ -11,7 +11,7 @@ namespace The_SEO_Framework;
11
 
12
  /**
13
  * The SEO Framework plugin
14
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
15
  *
16
  * This program is free software: you can redistribute it and/or modify
17
  * it under the terms of the GNU General Public License version 3 as published
@@ -34,23 +34,20 @@ namespace The_SEO_Framework;
34
  * @since 2.8.0
35
  * @since 4.0.0 No longer implements an interface. It's implied.
36
  * @since 4.1.0 Now extends `Cache` instead of `Feed`.
 
37
  */
38
  final class Load extends Cache {
39
 
40
- /**
41
- * @since 2.4.3
42
- * @var bool Enable object caching.
43
- */
44
- protected $use_object_cache = false;
45
-
46
  /**
47
  * @since 2.2.9
 
48
  * @var bool $the_seo_framework_debug Whether TSF-specific debug is enabled.
49
  */
50
  public $the_seo_framework_debug = false;
51
 
52
  /**
53
  * @since 2.2.9
 
54
  * @var bool $the_seo_framework_debug Whether TSF-specific transients are used.
55
  */
56
  public $the_seo_framework_use_transients = true;
@@ -61,11 +58,27 @@ final class Load extends Cache {
61
  */
62
  public $script_debug = false;
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * Constructor, setup debug vars and then load parent constructor.
66
  *
67
  * @since 2.8.0
68
  * @since 4.0.0 Now informs developer of invalid class instancing.
 
69
  *
70
  * @return null If called twice or more.
71
  */
@@ -91,27 +104,38 @@ final class Load extends Cache {
91
  //= Register the capabilities early.
92
  \add_filter( 'option_page_capability_' . THE_SEO_FRAMEWORK_SITE_OPTIONS, [ $this, 'get_settings_capability' ] );
93
 
94
- /**
95
- * @since 2.2.2
96
- * @param bool $load_options Whether to show or hide option pages.
97
- */
98
- $this->load_options = (bool) \apply_filters( 'the_seo_framework_load_options', true );
99
-
100
- /**
101
- * @since 2.4.3
102
- * @since 2.8.0 : Uses method $this->use_object_cache() as default.
103
- * @param bool $use_object_cache Whether to enable object caching.
104
- */
105
- $this->use_object_cache = (bool) \apply_filters(
106
- 'the_seo_framework_use_object_cache',
107
- \wp_using_ext_object_cache() && $this->get_option( 'cache_object' )
108
- );
109
-
110
- //? We always use this, because we need to test whether the sitemap must be outputted.
111
  $this->pretty_permalinks = '' !== \get_option( 'permalink_structure' );
112
 
113
  //= Load plugin at init 0.
114
  \add_action( 'init', [ $this, 'init_the_seo_framework' ], 0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
 
117
  /**
@@ -122,9 +146,8 @@ final class Load extends Cache {
122
  public function init_debug_vars() {
123
 
124
  $this->the_seo_framework_debug = \defined( 'THE_SEO_FRAMEWORK_DEBUG' ) && THE_SEO_FRAMEWORK_DEBUG ?: $this->the_seo_framework_debug;
125
- if ( $this->the_seo_framework_debug ) {
126
  \The_SEO_Framework\Debug::_set_instance( $this->the_seo_framework_debug );
127
- }
128
 
129
  $this->the_seo_framework_use_transients = \defined( 'THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS' ) && THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS ? false : $this->the_seo_framework_use_transients;
130
 
@@ -143,11 +166,6 @@ final class Load extends Cache {
143
  */
144
  public function _load_early_compat_files() {
145
 
146
- // phpcs:disable, Squiz.PHP.CommentedOutCode
147
- // if ( ! extension_loaded( 'mbstring' ) )
148
- // $this->_include_compat( 'mbstring', 'php' );
149
- // phpcs:enable, Squiz.PHP.CommentedOutCode
150
-
151
  // Disable Headway theme SEO.
152
  \add_filter( 'headway_seo_disabled', '__return_true' );
153
 
@@ -191,21 +209,6 @@ final class Load extends Cache {
191
  }
192
  }
193
 
194
- /**
195
- * Mark a filter as deprecated and inform when it has been used.
196
- *
197
- * @since 2.8.0
198
- * @see $this->_deprecated_function().
199
- * @access private
200
- *
201
- * @param string $filter The function that was called.
202
- * @param string $version The version of WordPress that deprecated the function.
203
- * @param string $replacement Optional. The function that should have been called. Default null.
204
- */
205
- public function _deprecated_filter( $filter, $version, $replacement = null ) {
206
- Debug::get_instance()->_deprecated_filter( $filter, $version, $replacement );
207
- }
208
-
209
  /**
210
  * Mark a function as deprecated and inform when it has been used.
211
  * Taken from WordPress core, but added extra parameters and linguistic alterations.
11
 
12
  /**
13
  * The SEO Framework plugin
14
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
15
  *
16
  * This program is free software: you can redistribute it and/or modify
17
  * it under the terms of the GNU General Public License version 3 as published
34
  * @since 2.8.0
35
  * @since 4.0.0 No longer implements an interface. It's implied.
36
  * @since 4.1.0 Now extends `Cache` instead of `Feed`.
37
+ * @since 4.1.4 Removed protected property $use_object_cache.
38
  */
39
  final class Load extends Cache {
40
 
 
 
 
 
 
 
41
  /**
42
  * @since 2.2.9
43
+ * @TODO make this 'meta_debug'
44
  * @var bool $the_seo_framework_debug Whether TSF-specific debug is enabled.
45
  */
46
  public $the_seo_framework_debug = false;
47
 
48
  /**
49
  * @since 2.2.9
50
+ * @TODO make this just 'cache_transients'
51
  * @var bool $the_seo_framework_debug Whether TSF-specific transients are used.
52
  */
53
  public $the_seo_framework_use_transients = true;
58
  */
59
  public $script_debug = false;
60
 
61
+ /**
62
+ * @since 4.1.4
63
+ * @access protected
64
+ * DO NOT OVERWRITE.
65
+ * Feel free to read.
66
+ * Use constant `THE_SEO_FRAMEWORK_HEADLESS` instead.
67
+ * @var bool|array $is_headless Whether headless TSF is enabled.
68
+ * If not false, then array: {
69
+ * 'meta' => bool True to disable post/term-meta-data storing/fetching.
70
+ * 'settings' => bool True to disable non-default setting.
71
+ * 'user' => bool True to disable SEO user-meta-data storing/fetching.
72
+ * }
73
+ */
74
+ public $is_headless = false;
75
+
76
  /**
77
  * Constructor, setup debug vars and then load parent constructor.
78
  *
79
  * @since 2.8.0
80
  * @since 4.0.0 Now informs developer of invalid class instancing.
81
+ * @since 4.1.4.Now constructs headlessness.
82
  *
83
  * @return null If called twice or more.
84
  */
104
  //= Register the capabilities early.
105
  \add_filter( 'option_page_capability_' . THE_SEO_FRAMEWORK_SITE_OPTIONS, [ $this, 'get_settings_capability' ] );
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  $this->pretty_permalinks = '' !== \get_option( 'permalink_structure' );
108
 
109
  //= Load plugin at init 0.
110
  \add_action( 'init', [ $this, 'init_the_seo_framework' ], 0 );
111
+
112
+ $this->is_headless = [
113
+ 'meta' => false,
114
+ 'settings' => false,
115
+ 'user' => false,
116
+ ];
117
+
118
+ if ( ! \apply_filters_deprecated(
119
+ 'the_seo_framework_load_options',
120
+ [ true ],
121
+ '4.1.4 of The SEO Framework',
122
+ 'constant THE_SEO_FRAMEWORK_HEADLESS'
123
+ ) ) \defined( 'THE_SEO_FRAMEWORK_HEADLESS' ) or \define( 'THE_SEO_FRAMEWORK_HEADLESS', true );
124
+
125
+ //= A headless boi is a good boi. Far less annoying, they are.
126
+ if ( \defined( 'THE_SEO_FRAMEWORK_HEADLESS' ) ) {
127
+ $this->is_headless = [
128
+ 'meta' => true,
129
+ 'settings' => true,
130
+ 'user' => true,
131
+ ];
132
+
133
+ \is_array( THE_SEO_FRAMEWORK_HEADLESS )
134
+ and $this->is_headless = array_map(
135
+ 'wp_validate_boolean',
136
+ array_merge( $this->is_headless, THE_SEO_FRAMEWORK_HEADLESS )
137
+ );
138
+ }
139
  }
140
 
141
  /**
146
  public function init_debug_vars() {
147
 
148
  $this->the_seo_framework_debug = \defined( 'THE_SEO_FRAMEWORK_DEBUG' ) && THE_SEO_FRAMEWORK_DEBUG ?: $this->the_seo_framework_debug;
149
+ if ( $this->the_seo_framework_debug )
150
  \The_SEO_Framework\Debug::_set_instance( $this->the_seo_framework_debug );
 
151
 
152
  $this->the_seo_framework_use_transients = \defined( 'THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS' ) && THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS ? false : $this->the_seo_framework_use_transients;
153
 
166
  */
167
  public function _load_early_compat_files() {
168
 
 
 
 
 
 
169
  // Disable Headway theme SEO.
170
  \add_filter( 'headway_seo_disabled', '__return_true' );
171
 
209
  }
210
  }
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  /**
213
  * Mark a function as deprecated and inform when it has been used.
214
  * Taken from WordPress core, but added extra parameters and linguistic alterations.
inc/classes/post-data.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -47,6 +47,18 @@ class Post_Data extends Detect {
47
  */
48
  public $inpost_nonce_field = 'tsf_inpost_nonce';
49
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  /**
51
  * Returns a post SEO meta item by key.
52
  *
@@ -82,6 +94,8 @@ class Post_Data extends Detect {
82
  *
83
  * @since 4.0.0
84
  * @since 4.0.2 Now tests for valid post ID in the post object.
 
 
85
  *
86
  * @param int $post_id The post ID.
87
  * @param bool $use_cache Whether to use caching.
@@ -99,7 +113,7 @@ class Post_Data extends Detect {
99
  // get_post_meta() requires a valid post ID. Make sure that post exists.
100
  $post = \get_post( $post_id );
101
 
102
- if ( empty( $post->ID ) )
103
  return $cache[ $post_id ] = [];
104
 
105
  /**
@@ -111,28 +125,35 @@ class Post_Data extends Detect {
111
  $this->get_post_meta_defaults( $post->ID )
112
  );
113
 
114
- // Filter the post meta items based on defaults' keys.
115
- $meta = array_intersect_key(
116
- \get_post_meta( $post->ID ), // Gets all post meta. This is a discrepancy with get_term_meta()!
117
- $defaults
118
- );
 
 
 
119
 
120
- // WP converts all entries to arrays, because we got ALL entries. Disarray!
121
- foreach ( $meta as $key => $value ) {
122
- $meta[ $key ] = $value[0];
123
  }
124
 
125
  /**
126
  * @since 4.0.5
 
 
127
  * @note Do not delete/unset/add indexes! It'll cause errors.
128
  * @param array $meta The current post meta.
129
  * @param int $post_id The post ID.
 
130
  */
131
  $meta = \apply_filters_ref_array(
132
  'the_seo_framework_post_meta',
133
  [
134
  array_merge( $defaults, $meta ),
135
  $post->ID,
 
136
  ]
137
  );
138
 
@@ -155,20 +176,36 @@ class Post_Data extends Detect {
155
  * @return array The default post meta.
156
  */
157
  public function get_post_meta_defaults( $post_id = 0 ) {
 
158
  /**
159
- * @since 3.1.0
160
  * @param array $defaults
161
  * @param integer $post_id Post ID.
162
  * @param \WP_Post $post Post object.
163
  */
164
- return (array) \apply_filters_ref_array(
165
- 'the_seo_framework_inpost_seo_save_defaults', // TODO rename to the_seo_framework_post_meta_defaults. 4.1.0?
166
  [
167
  $this->get_unfiltered_post_meta_defaults(),
168
  $post_id,
169
- \get_post( $post_id ),
170
  ]
171
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  }
173
 
174
  /**
@@ -228,6 +265,7 @@ class Post_Data extends Detect {
228
  * Save post meta / custom field data for a singular post type.
229
  *
230
  * @since 4.0.0
 
231
  *
232
  * @param \WP_Post|integer $post The post object or post ID.
233
  * @param array $data The post meta fields, will be merged with the defaults.
@@ -239,25 +277,6 @@ class Post_Data extends Detect {
239
  if ( ! $post ) return;
240
 
241
  $data = (array) \wp_parse_args( $data, $this->get_post_meta_defaults( $post->ID ) );
242
- $data = $this->s_post_meta( $data );
243
-
244
- if ( \has_filter( 'the_seo_framework_save_custom_fields' ) ) {
245
- $this->_deprecated_filter( 'the_seo_framework_save_custom_fields', '4.0.0', 'the_seo_framework_save_post_meta' );
246
- /**
247
- * @since 3.1.0
248
- * @since 4.0.0 Deprecated.
249
- * @deprecated
250
- * @param array $data The data that's going to be saved.
251
- * @param \WP_Post $post The post object.
252
- */
253
- $data = (array) \apply_filters_ref_array(
254
- 'the_seo_framework_save_custom_fields',
255
- [
256
- $data,
257
- $post,
258
- ]
259
- );
260
- }
261
 
262
  /**
263
  * @since 4.0.0
@@ -267,7 +286,7 @@ class Post_Data extends Detect {
267
  $data = (array) \apply_filters_ref_array(
268
  'the_seo_framework_save_post_meta',
269
  [
270
- $data,
271
  $post,
272
  ]
273
  );
@@ -606,7 +625,7 @@ class Post_Data extends Detect {
606
  * @since 2.6.6
607
  * @since 3.1.0 Added Elementor detection
608
  * @since 4.0.0 Now detects page builders before looping over the meta.
609
- * @TODO deprecate?
610
  * @ignore unused.
611
  *
612
  * @param int $post_id The post ID to check.
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
47
  */
48
  public $inpost_nonce_field = 'tsf_inpost_nonce';
49
 
50
+ /**
51
+ * Initializes post meta data handlers.
52
+ *
53
+ * @since 4.1.4
54
+ */
55
+ protected function init_post_meta() {
56
+ // Save post data.
57
+ \add_action( 'save_post', [ $this, '_update_post_meta' ], 1, 2 );
58
+ \add_action( 'edit_attachment', [ $this, '_update_attachment_meta' ], 1 );
59
+ \add_action( 'save_post', [ $this, '_save_inpost_primary_term' ], 1, 2 );
60
+ }
61
+
62
  /**
63
  * Returns a post SEO meta item by key.
64
  *
94
  *
95
  * @since 4.0.0
96
  * @since 4.0.2 Now tests for valid post ID in the post object.
97
+ * @since 4.1.4 1. Now returns an empty array when the post type isn't supported.
98
+ * 2. Now considers headlessness.
99
  *
100
  * @param int $post_id The post ID.
101
  * @param bool $use_cache Whether to use caching.
113
  // get_post_meta() requires a valid post ID. Make sure that post exists.
114
  $post = \get_post( $post_id );
115
 
116
+ if ( empty( $post->ID ) || ! $this->is_post_type_supported( $post->post_type ) )
117
  return $cache[ $post_id ] = [];
118
 
119
  /**
125
  $this->get_post_meta_defaults( $post->ID )
126
  );
127
 
128
+ if ( $this->is_headless['meta'] ) {
129
+ $meta = [];
130
+ } else {
131
+ // Filter the post meta items based on defaults' keys.
132
+ $meta = array_intersect_key(
133
+ \get_post_meta( $post->ID ), // Gets all post meta. This is a discrepancy with get_term_meta()!
134
+ $defaults
135
+ );
136
 
137
+ // WP converts all entries to arrays, because we got ALL entries. Disarray!
138
+ foreach ( $meta as $key => $value )
139
+ $meta[ $key ] = $value[0];
140
  }
141
 
142
  /**
143
  * @since 4.0.5
144
+ * @since 4.1.4 1. Now considers headlessness.
145
+ * 2. Now returns a 3rd parameter: boolean $headless.
146
  * @note Do not delete/unset/add indexes! It'll cause errors.
147
  * @param array $meta The current post meta.
148
  * @param int $post_id The post ID.
149
+ * @param bool $headless Whether the meta are headless.
150
  */
151
  $meta = \apply_filters_ref_array(
152
  'the_seo_framework_post_meta',
153
  [
154
  array_merge( $defaults, $meta ),
155
  $post->ID,
156
+ $this->is_headless['meta'],
157
  ]
158
  );
159
 
176
  * @return array The default post meta.
177
  */
178
  public function get_post_meta_defaults( $post_id = 0 ) {
179
+
180
  /**
181
+ * @since 4.1.4
182
  * @param array $defaults
183
  * @param integer $post_id Post ID.
184
  * @param \WP_Post $post Post object.
185
  */
186
+ $defaults = (array) \apply_filters_ref_array(
187
+ 'the_seo_framework_post_meta_defaults',
188
  [
189
  $this->get_unfiltered_post_meta_defaults(),
190
  $post_id,
191
+ $post = \get_post( $post_id ),
192
  ]
193
  );
194
+
195
+ /**
196
+ * @since 3.1.0
197
+ * @since 4.1.4 Deprecated. Use filter `the_seo_framework_post_meta_defaults` instead.
198
+ * @deprecated
199
+ * @param array $defaults
200
+ * @param integer $post_id Post ID.
201
+ * @param \WP_Post $post Post object.
202
+ */
203
+ return (array) \apply_filters_deprecated(
204
+ 'the_seo_framework_inpost_seo_save_defaults',
205
+ [ $defaults, $post_id, $post ],
206
+ '4.1.4 of The SEO Framework',
207
+ 'the_seo_framework_post_meta_defaults'
208
+ );
209
  }
210
 
211
  /**
265
  * Save post meta / custom field data for a singular post type.
266
  *
267
  * @since 4.0.0
268
+ * @since 4.1.4 Removed deprecated filter.
269
  *
270
  * @param \WP_Post|integer $post The post object or post ID.
271
  * @param array $data The post meta fields, will be merged with the defaults.
277
  if ( ! $post ) return;
278
 
279
  $data = (array) \wp_parse_args( $data, $this->get_post_meta_defaults( $post->ID ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
  /**
282
  * @since 4.0.0
286
  $data = (array) \apply_filters_ref_array(
287
  'the_seo_framework_save_post_meta',
288
  [
289
+ $this->s_post_meta( $data ),
290
  $post,
291
  ]
292
  );
625
  * @since 2.6.6
626
  * @since 3.1.0 Added Elementor detection
627
  * @since 4.0.0 Now detects page builders before looping over the meta.
628
+ * @TODO deprecate? -> We may use this data for they have FSE builders. We may want to interface with those, some day.
629
  * @ignore unused.
630
  *
631
  * @param int $post_id The post ID to check.
inc/classes/profile.class.php DELETED
@@ -1,150 +0,0 @@
1
- <?php
2
- /**
3
- * @package The_SEO_Framework\Classes\Facade\Profile
4
- * @subpackage The_SEO_Framework\Admin\Profile
5
- */
6
-
7
- namespace The_SEO_Framework;
8
-
9
- \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
10
-
11
- /**
12
- * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
- *
15
- * This program is free software: you can redistribute it and/or modify
16
- * it under the terms of the GNU General Public License version 3 as published
17
- * by the Free Software Foundation.
18
- *
19
- * This program is distributed in the hope that it will be useful,
20
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
- * GNU General Public License for more details.
23
- *
24
- * You should have received a copy of the GNU General Public License
25
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
26
- */
27
-
28
- /**
29
- * Class The_SEO_Framework\Profile
30
- *
31
- * Outputs Profile fields and saves metadata.
32
- * TODO move this to admin-pages, invoke like `_init_post_edit_view()`?
33
- *
34
- * @since 3.0.0
35
- */
36
- class Profile extends Generate_Ldjson {
37
-
38
- /**
39
- * Outputs profile fields and prepares saving thereof.
40
- *
41
- * @since 3.0.0
42
- */
43
- protected function init_profile_fields() {
44
-
45
- //= No need to load anything if the current user can't even author posts.
46
- // This is ultimately useless checking this on EVERY admin page.
47
- // Debug me... 294 microseconds overhead. The cap check is cached, it seems. Takes as much time adding the actions.
48
- if ( ! \current_user_can( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
49
-
50
- \add_action( 'show_user_profile', [ $this, '_add_user_author_fields' ], 0, 1 );
51
- \add_action( 'edit_user_profile', [ $this, '_add_user_author_fields' ], 0, 1 );
52
-
53
- \add_action( 'personal_options_update', [ $this, '_update_user_settings' ], 10, 1 );
54
- \add_action( 'edit_user_profile_update', [ $this, '_update_user_settings' ], 10, 1 );
55
- }
56
-
57
- /**
58
- * Returns the current profile field settings.
59
- *
60
- * @since 4.0.0
61
- *
62
- * @return \stdClass The profile settings.
63
- */
64
- protected function get_profile_field_settings() {
65
- return (object) [
66
- 'keys' => [
67
- 'facebook_page' => 'tsf_facebook_page',
68
- 'twitter_page' => 'tsf_twitter_page',
69
- ],
70
- 'sanitization' => [
71
- 'facebook_page' => 's_facebook_profile',
72
- 'twitter_page' => 's_twitter_name',
73
- ],
74
- ];
75
- }
76
-
77
- /**
78
- * Outputs user profile fields.
79
- *
80
- * @since 3.0.0
81
- * @access private
82
- *
83
- * @param \WP_User $user WP_User object.
84
- */
85
- public function _add_user_author_fields( \WP_User $user ) {
86
-
87
- if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
88
-
89
- // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- includes...
90
- $_field_settings = $this->get_profile_field_settings();
91
-
92
- $fields = [
93
- $_field_settings->keys['facebook_page'] => (object) [
94
- 'name' => \__( 'Facebook profile page', 'autodescription' ),
95
- 'type' => 'url',
96
- 'placeholder' => \_x( 'https://www.facebook.com/YourPersonalProfile', 'Example Facebook Personal URL', 'autodescription' ),
97
- 'value' => $this->get_user_option( $user->ID, 'facebook_page' ),
98
- 'class' => '',
99
- ],
100
- $_field_settings->keys['twitter_page'] => (object) [
101
- 'name' => \__( 'Twitter profile name', 'autodescription' ),
102
- 'type' => 'text',
103
- 'placeholder' => \_x( '@your-personal-username', 'Twitter @username', 'autodescription' ),
104
- 'value' => $this->get_user_option( $user->ID, 'twitter_page' ),
105
- 'class' => 'ltr',
106
- ],
107
- ];
108
- // phpcs:enable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
109
-
110
- $this->get_view( 'profile/author', get_defined_vars() );
111
- }
112
-
113
- /**
114
- * Saves user profile fields.
115
- *
116
- * @since 3.0.0
117
- * @securitycheck 3.0.0 OK. NOTE: Nonces and refer(r)ers have been checked prior
118
- * to the actions bound to this method. We check against them, redundantly.
119
- * @access private
120
- *
121
- * @param int $user_id The user ID.
122
- */
123
- public function _update_user_settings( $user_id ) {
124
-
125
- if ( empty( $_POST ) ) return;
126
-
127
- \check_admin_referer( 'update-user_' . $user_id );
128
- if ( ! \current_user_can( 'edit_user', $user_id ) ) return;
129
-
130
- $user = new \WP_User( $user_id );
131
-
132
- if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
133
-
134
- $success = [];
135
- $defaults = $this->get_default_user_data();
136
-
137
- $_field_settings = $this->get_profile_field_settings();
138
-
139
- foreach ( $_field_settings->keys as $option => $post_key ) {
140
- if ( isset( $_POST[ $post_key ] ) ) {
141
- $value = $this->{$_field_settings->sanitization[ $option ]}( $_POST[ $post_key ] )
142
- ?: $defaults[ $option ]; // phpcs:ignore, WordPress.WhiteSpace
143
-
144
- $success[] = (bool) $this->update_user_option( $user_id, $option, $value );
145
- }
146
- }
147
-
148
- //? Do something with $success?
149
- }
150
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/classes/query.class.php CHANGED
@@ -9,7 +9,7 @@ namespace The_SEO_Framework;
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2015 - 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
@@ -296,6 +296,17 @@ class Query extends Core {
296
  return $cache = ! empty( $_object->taxonomy ) ? $_object->taxonomy : '';
297
  }
298
 
 
 
 
 
 
 
 
 
 
 
 
299
  /**
300
  * Detects 404.
301
  *
@@ -499,6 +510,19 @@ class Query extends Core {
499
  return isset( $current_screen->base ) && \in_array( $current_screen->base, [ 'edit-tags', 'edit' ], true );
500
  }
501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  /**
503
  * Detects author archives.
504
  *
@@ -574,6 +598,8 @@ class Query extends Core {
574
  * Checks blog page by sole ID.
575
  *
576
  * @since 4.0.0
 
 
577
  * @todo deprecate
578
  * @see is_wc_shop() -- that's the correct implementation.
579
  *
@@ -582,9 +608,15 @@ class Query extends Core {
582
  */
583
  public function is_blog_page_by_id( $id ) {
584
 
585
- $pfp = (int) \get_option( 'page_for_posts' );
 
586
 
587
- return 0 !== $pfp && $id === $pfp;
 
 
 
 
 
588
  }
589
 
590
  /**
@@ -715,7 +747,7 @@ class Query extends Core {
715
  *
716
  * @NOTE This doesn't check for anomalies in the query.
717
  * So, don't use this to test user-engaged WordPress queries, ever.
718
- * WARNING: This will lead to **FALSE POSITIVES** for Date, PTA, Search, and other archives.
719
  *
720
  * @see $this->is_front_page_by_id(), which supports query checking.
721
  * @see $this->is_real_front_page(), which solely uses query checking.
@@ -1009,16 +1041,19 @@ class Query extends Core {
1009
  * Detects the static front page.
1010
  *
1011
  * @since 2.3.8
 
1012
  *
1013
  * @param int $id the Page ID to check. If empty, the current ID will be fetched.
1014
- * @return bool true if is blog page. Always false if the homepage is a blog.
1015
  */
1016
  public function is_static_frontpage( $id = 0 ) {
1017
 
1018
- if ( 'page' === \get_option( 'show_on_front' ) )
1019
- return (int) \get_option( 'page_on_front' ) === ( $id ?: $this->get_the_real_ID() );
1020
 
1021
- return false;
 
 
 
1022
  }
1023
 
1024
  /**
@@ -1092,23 +1127,35 @@ class Query extends Core {
1092
  * Determines if the $post is a shop page.
1093
  *
1094
  * @since 4.0.5
 
1095
  *
1096
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1097
  * @return bool
1098
  */
1099
  public function is_shop( $post = null ) {
 
 
 
 
 
1100
  /**
1101
  * @since 4.0.5
 
1102
  * @param bool $is_shop Whether the post ID is a shop.
1103
  * @param int $id The current or supplied post ID.
1104
  */
1105
- return \apply_filters_ref_array( 'the_seo_framework_is_shop', [ false, $post ] );
 
 
 
 
1106
  }
1107
 
1108
  /**
1109
  * Determines if the page is a product page.
1110
  *
1111
  * @since 4.0.5
 
1112
  *
1113
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1114
  * @return bool True if on a WooCommerce Product page.
@@ -1118,120 +1165,47 @@ class Query extends Core {
1118
  if ( \is_admin() )
1119
  return $this->is_product_admin();
1120
 
 
 
 
 
1121
  /**
1122
  * @since 4.0.5
 
1123
  * @param bool $is_product
1124
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1125
  */
1126
- return (bool) \apply_filters_ref_array( 'the_seo_framework_is_product', [ false, $post ] );
 
 
 
 
1127
  }
1128
 
1129
  /**
1130
  * Determines if the admin page is for a product page.
1131
  *
1132
  * @since 4.0.5
 
1133
  *
1134
  * @return bool
1135
  */
1136
  public function is_product_admin() {
1137
- /**
1138
- * @since 4.0.5
1139
- * @param bool $is_product_admin
1140
- */
1141
- return (bool) \apply_filters( 'the_seo_framework_is_product_admin', false );
1142
- }
1143
-
1144
- /**
1145
- * Determines if the $post is the WooCommerce plugin shop page.
1146
- *
1147
- * @since 2.5.2
1148
- * @since 4.0.5 Now has a first parameter `$post`.
1149
- * @since 4.0.5 Soft deprecated.
1150
- * @deprecated
1151
- * @internal
1152
- *
1153
- * @param int|WP_Post|null $post (Optional) Post ID or post object.
1154
- * @return bool True if on the WooCommerce shop page.
1155
- */
1156
- public function is_wc_shop( $post = null ) {
1157
-
1158
- if ( isset( $post ) ) {
1159
- $post = \get_post( $post );
1160
- $id = $post ? $post->ID : 0;
1161
- } else {
1162
- $id = null;
1163
- }
1164
-
1165
- // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
1166
- if ( null !== $cache = $this->get_query_cache( __METHOD__, null, $id ) )
1167
- return $cache;
1168
-
1169
- if ( isset( $id ) ) {
1170
- $is_shop = (int) \get_option( 'woocommerce_shop_page_id' ) === $id;
1171
- } else {
1172
- $is_shop = ! \is_admin() && \function_exists( 'is_shop' ) && \is_shop();
1173
- }
1174
-
1175
- $this->set_query_cache(
1176
- __METHOD__,
1177
- $is_shop,
1178
- $id
1179
- );
1180
-
1181
- return $is_shop;
1182
- }
1183
-
1184
- /**
1185
- * Determines if the page is the WooCommerce plugin Product page.
1186
- *
1187
- * @since 2.5.2
1188
- * @since 4.0.0 : 1. Added admin support.
1189
- * 2. Added parameter for the Post ID or post to test.
1190
- * @since 4.0.5 Soft deprecated.
1191
- * @deprecated
1192
- * @internal
1193
- *
1194
- * @param int|\WP_Post $post When set, checks if the post is of type product.
1195
- * @return bool True if on a WooCommerce Product page.
1196
- */
1197
- public function is_wc_product( $post = 0 ) {
1198
-
1199
- if ( \is_admin() )
1200
- return $this->is_wc_product_admin();
1201
 
1202
  // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
1203
- if ( null !== $cache = $this->get_query_cache( __METHOD__, null, $post ) )
1204
  return $cache;
1205
 
1206
- if ( $post ) {
1207
- $is_product = 'product' === \get_post_type( $post );
1208
- } else {
1209
- $is_product = \function_exists( 'is_product' ) && \is_product();
1210
- }
1211
-
1212
- $this->set_query_cache(
1213
- __METHOD__,
1214
- $is_product,
1215
- $post
1216
- );
1217
 
1218
- return $is_product;
1219
- }
1220
 
1221
- /**
1222
- * Detects products within the admin area.
1223
- *
1224
- * @since 4.0.0
1225
- * @see $this->is_wc_product()
1226
- * @since 4.0.5 Soft deprecated.
1227
- * @deprecated
1228
- * @internal
1229
- *
1230
- * @return bool
1231
- */
1232
- public function is_wc_product_admin() {
1233
- // Checks for "is_singular_admin()" because the post type is non-hierarchical.
1234
- return $this->is_singular_admin() && 'product' === $this->get_admin_post_type();
1235
  }
1236
 
1237
  /**
@@ -1548,23 +1522,16 @@ class Query extends Core {
1548
 
1549
  static $cache = [];
1550
 
1551
- if ( $hash ) {
1552
- // phpcs:ignore, WordPress.PHP.DiscouragedPHPFunctions -- No objects are inserted, nor is this ever unserialized.
1553
- $hash = serialize( $hash );
1554
- } else {
1555
- $hash = false;
1556
- }
1557
 
1558
  if ( isset( $value_to_set ) ) {
1559
  $fresh_set = ! isset( $cache[ $method ][ $hash ] );
1560
  $cache[ $method ][ $hash ] = $value_to_set;
1561
  return $fresh_set;
1562
- } else {
1563
- if ( isset( $cache[ $method ][ $hash ] ) )
1564
- return $cache[ $method ][ $hash ];
1565
  }
1566
 
1567
- return null;
1568
  }
1569
 
1570
  /**
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (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
296
  return $cache = ! empty( $_object->taxonomy ) ? $_object->taxonomy : '';
297
  }
298
 
299
+ /**
300
+ * Returns the current post type, if any.
301
+ *
302
+ * @since 4.1.4
303
+ *
304
+ * @return string The queried post type.
305
+ */
306
+ public function get_current_post_type() {
307
+ return $this->get_post_type_real_ID() ?: $this->get_admin_post_type();
308
+ }
309
+
310
  /**
311
  * Detects 404.
312
  *
510
  return isset( $current_screen->base ) && \in_array( $current_screen->base, [ 'edit-tags', 'edit' ], true );
511
  }
512
 
513
+ /**
514
+ * Detects Profile edit screen in WP Admin.
515
+ *
516
+ * @since 4.1.4
517
+ * @global \WP_Screen $current_screen
518
+ *
519
+ * @return bool True if on Profile Edit screen. False otherwise.
520
+ */
521
+ public function is_profile_edit() {
522
+ global $current_screen;
523
+ return isset( $current_screen->base ) && \in_array( $current_screen->base, [ 'profile', 'user-edit' ], true );
524
+ }
525
+
526
  /**
527
  * Detects author archives.
528
  *
598
  * Checks blog page by sole ID.
599
  *
600
  * @since 4.0.0
601
+ * @since 4.1.4 1. Improved performance by switching the conditional.
602
+ * 2. Improved performance by adding memoization.
603
  * @todo deprecate
604
  * @see is_wc_shop() -- that's the correct implementation.
605
  *
608
  */
609
  public function is_blog_page_by_id( $id ) {
610
 
611
+ // ID 0 cannot be a blog page.
612
+ if ( ! $id ) return false;
613
 
614
+ static $pfp = null;
615
+
616
+ if ( \is_null( $pfp ) )
617
+ $pfp = (int) \get_option( 'page_for_posts' );
618
+
619
+ return $pfp === $id;
620
  }
621
 
622
  /**
747
  *
748
  * @NOTE This doesn't check for anomalies in the query.
749
  * So, don't use this to test user-engaged WordPress queries, ever.
750
+ * WARNING: This will lead to **FALSE POSITIVES** for Date, CPTA, Search, and other archives.
751
  *
752
  * @see $this->is_front_page_by_id(), which supports query checking.
753
  * @see $this->is_real_front_page(), which solely uses query checking.
1041
  * Detects the static front page.
1042
  *
1043
  * @since 2.3.8
1044
+ * @since 4.1.4 Added memoization.
1045
  *
1046
  * @param int $id the Page ID to check. If empty, the current ID will be fetched.
1047
+ * @return bool True when homepage is static and given/current ID matches.
1048
  */
1049
  public function is_static_frontpage( $id = 0 ) {
1050
 
1051
+ static $front_id;
 
1052
 
1053
+ if ( ! isset( $front_id ) )
1054
+ $front_id = 'page' === \get_option( 'show_on_front' ) ? (int) \get_option( 'page_on_front' ) : false;
1055
+
1056
+ return false !== $front_id && ( $id ?: $this->get_the_real_ID() ) === $front_id;
1057
  }
1058
 
1059
  /**
1127
  * Determines if the $post is a shop page.
1128
  *
1129
  * @since 4.0.5
1130
+ * @since 4.1.4 Added memoization.
1131
  *
1132
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1133
  * @return bool
1134
  */
1135
  public function is_shop( $post = null ) {
1136
+
1137
+ // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
1138
+ if ( null !== $cache = $this->get_query_cache( __METHOD__, null, $post ) )
1139
+ return $cache;
1140
+
1141
  /**
1142
  * @since 4.0.5
1143
+ * @since 4.1.4 Now has its return value memoized.
1144
  * @param bool $is_shop Whether the post ID is a shop.
1145
  * @param int $id The current or supplied post ID.
1146
  */
1147
+ $is_shop = \apply_filters_ref_array( 'the_seo_framework_is_shop', [ false, $post ] );
1148
+
1149
+ $this->set_query_cache( __METHOD__, $is_shop, $post );
1150
+
1151
+ return $is_shop;
1152
  }
1153
 
1154
  /**
1155
  * Determines if the page is a product page.
1156
  *
1157
  * @since 4.0.5
1158
+ * @since 4.1.4 Added memoization.
1159
  *
1160
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1161
  * @return bool True if on a WooCommerce Product page.
1165
  if ( \is_admin() )
1166
  return $this->is_product_admin();
1167
 
1168
+ // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
1169
+ if ( null !== $cache = $this->get_query_cache( __METHOD__, null, $post ) )
1170
+ return $cache;
1171
+
1172
  /**
1173
  * @since 4.0.5
1174
+ * @since 4.1.4 Now has its return value memoized.
1175
  * @param bool $is_product
1176
  * @param int|WP_Post|null $post (Optional) Post ID or post object.
1177
  */
1178
+ $is_product = (bool) \apply_filters_ref_array( 'the_seo_framework_is_product', [ false, $post ] );
1179
+
1180
+ $this->set_query_cache( __METHOD__, $is_product, $post );
1181
+
1182
+ return $is_product;
1183
  }
1184
 
1185
  /**
1186
  * Determines if the admin page is for a product page.
1187
  *
1188
  * @since 4.0.5
1189
+ * @since 4.1.4 Added memoization.
1190
  *
1191
  * @return bool
1192
  */
1193
  public function is_product_admin() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1194
 
1195
  // phpcs:ignore, WordPress.CodeAnalysis.AssignmentInCondition
1196
+ if ( null !== $cache = $this->get_query_cache( __METHOD__ ) )
1197
  return $cache;
1198
 
1199
+ /**
1200
+ * @since 4.0.5
1201
+ * @since 4.1.4 Now has its return value memoized.
1202
+ * @param bool $is_product_admin
1203
+ */
1204
+ $is_product_admin = (bool) \apply_filters( 'the_seo_framework_is_product_admin', false );
 
 
 
 
 
1205
 
1206
+ $this->set_query_cache( __METHOD__, $is_product_admin );
 
1207
 
1208
+ return $is_product_admin;
 
 
 
 
 
 
 
 
 
 
 
 
 
1209
  }
1210
 
1211
  /**
1522
 
1523
  static $cache = [];
1524
 
1525
+ // phpcs:ignore, WordPress.PHP.DiscouragedPHPFunctions -- No objects are inserted, nor is this ever unserialized.
1526
+ $hash = $hash ? serialize( $hash ) : false;
 
 
 
 
1527
 
1528
  if ( isset( $value_to_set ) ) {
1529
  $fresh_set = ! isset( $cache[ $method ][ $hash ] );
1530
  $cache[ $method ][ $hash ] = $value_to_set;
1531
  return $fresh_set;
 
 
 
1532
  }
1533
 
1534
+ return isset( $cache[ $method ][ $hash ] ) ? $cache[ $method ][ $hash ] : null;
1535
  }
1536
 
1537
  /**
inc/classes/render.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -132,10 +132,105 @@ class Render extends Admin_Init {
132
  * @return string The cached Twitter card.
133
  */
134
  public function get_current_twitter_card_type() {
 
 
 
135
 
136
- static $cache = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
- return isset( $cache ) ? $cache : $cache = $this->generate_twitter_card_type();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
140
 
141
  /**
@@ -152,7 +247,7 @@ class Render extends Admin_Init {
152
 
153
  /**
154
  * @since 2.3.0
155
- * @since 2.7.0 : Added output within filter.
156
  * @param string $description The generated description.
157
  * @param int $id The page or term ID.
158
  */
@@ -164,10 +259,10 @@ class Render extends Admin_Init {
164
  ]
165
  );
166
 
167
- if ( $description )
168
- return '<meta name="description" content="' . \esc_attr( $description ) . '" />' . "\r\n";
169
-
170
- return '';
171
  }
172
 
173
  /**
@@ -198,10 +293,10 @@ class Render extends Admin_Init {
198
  ]
199
  );
200
 
201
- if ( $description )
202
- return '<meta property="og:description" content="' . \esc_attr( $description ) . '" />' . "\r\n";
203
-
204
- return '';
205
  }
206
 
207
  /**
@@ -230,10 +325,10 @@ class Render extends Admin_Init {
230
  ]
231
  );
232
 
233
- if ( $locale )
234
- return '<meta property="og:locale" content="' . \esc_attr( $locale ) . '" />' . "\r\n";
235
-
236
- return '';
237
  }
238
 
239
  /**
@@ -264,10 +359,10 @@ class Render extends Admin_Init {
264
  ]
265
  );
266
 
267
- if ( $title )
268
- return '<meta property="og:title" content="' . \esc_attr( $title ) . '" />' . "\r\n";
269
-
270
- return '';
271
  }
272
 
273
  /**
@@ -284,10 +379,10 @@ class Render extends Admin_Init {
284
 
285
  $type = $this->get_og_type();
286
 
287
- if ( $type )
288
- return '<meta property="og:type" content="' . \esc_attr( $type ) . '" />' . "\r\n";
289
-
290
- return '';
291
  }
292
 
293
  /**
@@ -310,19 +405,31 @@ class Render extends Admin_Init {
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'] ) {
316
- $output .= '<meta property="og:image:width" content="' . \esc_attr( $image['width'] ) . '" />' . "\r\n";
317
- $output .= '<meta property="og:image:height" content="' . \esc_attr( $image['height'] ) . '" />' . "\r\n";
 
 
 
 
 
 
318
  }
319
 
320
  if ( $image['alt'] ) {
321
- $output .= '<meta property="og:image:alt" content="' . \esc_attr( $image['alt'] ) . '" />' . "\r\n";
 
 
 
322
  }
323
 
324
- if ( ! $multi )
325
- break;
326
  }
327
 
328
  return $output;
@@ -354,10 +461,10 @@ class Render extends Admin_Init {
354
  ]
355
  );
356
 
357
- if ( $sitename )
358
- return '<meta property="og:site_name" content="' . \esc_attr( $sitename ) . '" />' . "\r\n";
359
-
360
- return '';
361
  }
362
 
363
  /**
@@ -365,6 +472,7 @@ class Render extends Admin_Init {
365
  *
366
  * @since 1.3.0
367
  * @since 2.9.3 Added filter
 
368
  * @uses $this->get_current_canonical_url()
369
  *
370
  * @return string The Open Graph URL meta tag.
@@ -386,11 +494,10 @@ class Render extends Admin_Init {
386
  ]
387
  );
388
 
389
- // TODO add esc_attr()? The URL is already safe for attribute usage... I'm not sure if that'll potentially break the URL.
390
- if ( $url )
391
- return '<meta property="og:url" content="' . $url . '" />' . "\r\n";
392
-
393
- return '';
394
  }
395
 
396
  /**
@@ -406,10 +513,10 @@ class Render extends Admin_Init {
406
 
407
  $card = $this->get_current_twitter_card_type();
408
 
409
- if ( $card )
410
- return '<meta name="twitter:card" content="' . \esc_attr( $card ) . '" />' . "\r\n";
411
-
412
- return '';
413
  }
414
 
415
  /**
@@ -437,10 +544,10 @@ class Render extends Admin_Init {
437
  ]
438
  );
439
 
440
- if ( $site )
441
- return '<meta name="twitter:site" content="' . \esc_attr( $site ) . '" />' . "\r\n";
442
-
443
- return '';
444
  }
445
 
446
  /**
@@ -460,21 +567,21 @@ class Render extends Admin_Init {
460
  /**
461
  * @since 2.3.0
462
  * @since 2.7.0 Added output within filter.
463
- * @param string $twitter_page The Twitter page creator.
464
- * @param int $id The current page or term ID.
465
  */
466
- $twitter_page = (string) \apply_filters_ref_array(
467
  'the_seo_framework_twittercreator_output',
468
  [
469
- $this->get_current_author_option( 'twitter_page' ) ?: $this->get_option( 'twitter_creator' ),
470
  $this->get_the_real_ID(),
471
  ]
472
  );
473
 
474
- if ( $twitter_page )
475
- return '<meta name="twitter:creator" content="' . \esc_attr( $twitter_page ) . '" />' . "\r\n";
476
-
477
- return '';
478
  }
479
 
480
  /**
@@ -504,10 +611,10 @@ class Render extends Admin_Init {
504
  ]
505
  );
506
 
507
- if ( $title )
508
- return '<meta name="twitter:title" content="' . \esc_attr( $title ) . '" />' . "\r\n";
509
-
510
- return '';
511
  }
512
 
513
  /**
@@ -537,10 +644,10 @@ class Render extends Admin_Init {
537
  ]
538
  );
539
 
540
- if ( $description )
541
- return '<meta name="twitter:description" content="' . \esc_attr( $description ) . '" />' . "\r\n";
542
-
543
- return '';
544
  }
545
 
546
  /**
@@ -560,15 +667,27 @@ class Render extends Admin_Init {
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'] ) {
566
- $output .= '<meta name="twitter:image:width" content="' . \esc_attr( $image['width'] ) . '" />' . "\r\n";
567
- $output .= '<meta name="twitter:image:height" content="' . \esc_attr( $image['height'] ) . '" />' . "\r\n";
 
 
 
 
 
 
568
  }
569
 
570
  if ( $image['alt'] ) {
571
- $output .= '<meta name="twitter:image:alt" content="' . \esc_attr( $image['alt'] ) . '" />' . "\r\n";
 
 
 
572
  }
573
 
574
  // Only grab a single image. Twitter grabs the final (less favorable) image otherwise.
@@ -587,14 +706,12 @@ class Render extends Admin_Init {
587
  */
588
  public function theme_color() {
589
 
590
- $output = '';
591
-
592
  $theme_color = $this->get_option( 'theme_color' );
593
 
594
- if ( $theme_color )
595
- $output = '<meta name="theme-color" content="' . \esc_attr( $theme_color ) . '" />' . "\r\n";
596
-
597
- return $output;
598
  }
599
 
600
  /**
@@ -620,15 +737,15 @@ class Render extends Admin_Init {
620
  $facebook_page = (string) \apply_filters_ref_array(
621
  'the_seo_framework_facebookauthor_output',
622
  [
623
- $this->get_current_author_option( 'facebook_page' ) ?: $this->get_option( 'facebook_author' ),
624
  $this->get_the_real_ID(),
625
  ]
626
  );
627
 
628
- if ( $facebook_page )
629
- return '<meta property="article:author" content="' . \esc_attr( \esc_url_raw( $facebook_page, [ 'https', 'http' ] ) ) . '" />' . "\r\n";
630
-
631
- return '';
632
  }
633
 
634
  /**
@@ -658,10 +775,10 @@ class Render extends Admin_Init {
658
  ]
659
  );
660
 
661
- if ( $publisher )
662
- return '<meta property="article:publisher" content="' . \esc_attr( \esc_url_raw( $publisher, [ 'https', 'http' ] ) ) . '" />' . "\r\n";
663
-
664
- return '';
665
  }
666
 
667
  /**
@@ -689,10 +806,10 @@ class Render extends Admin_Init {
689
  ]
690
  );
691
 
692
- if ( $app_id )
693
- return '<meta property="fb:app_id" content="' . \esc_attr( $app_id ) . '" />' . "\r\n";
694
-
695
- return '';
696
  }
697
 
698
  /**
@@ -732,60 +849,55 @@ class Render extends Admin_Init {
732
  ]
733
  );
734
 
735
- if ( $time )
736
- return '<meta property="article:published_time" content="' . \esc_attr( $time ) . '" />' . "\r\n";
737
-
738
- return '';
739
  }
740
 
741
  /**
742
  * Renders Article Modified Time meta tag.
743
- * Also renders the Open Graph Updated Time meta tag if Open Graph tags are enabled.
744
  *
745
  * @since 2.2.2
746
  * @since 2.7.0 Listens to $this->get_the_real_ID() instead of WordPress Core ID determination.
747
  * @since 2.8.0 Returns empty on product pages.
748
  * @since 3.0.0 : 1. Now checks for 0000 timestamps.
749
  * 2. Now uses timestamp formats.
 
 
750
  *
751
- * @return string The Article Modified Time meta tag, and optionally the Open Graph Updated Time.
752
  */
753
  public function article_modified_time() {
754
 
755
  if ( ! $this->output_modified_time() ) return '';
756
 
757
- $id = $this->get_the_real_ID();
758
-
759
- $post = \get_post( $id );
760
- $post_modified_gmt = $post->post_modified_gmt;
761
-
762
- if ( '0000-00-00 00:00:00' === $post_modified_gmt )
763
- return '';
764
 
765
- /**
766
- * @since 2.3.0
767
- * @since 2.7.0 Added output within filter.
768
- * @param string $time The article modified time.
769
- * @param int $id The current page or term ID.
770
- */
771
- $time = (string) \apply_filters_ref_array(
772
- 'the_seo_framework_modifiedtime_output',
773
- [
774
- $this->gmt2date( $this->get_timestamp_format(), $post_modified_gmt ),
775
- $id,
776
- ]
777
- );
778
 
779
- if ( $time ) {
780
- $output = '<meta property="article:modified_time" content="' . \esc_attr( $time ) . '" />' . "\r\n";
 
 
 
 
 
 
781
 
782
- if ( $this->use_og_tags() )
783
- $output .= '<meta property="og:updated_time" content="' . \esc_attr( $time ) . '" />' . "\r\n";
784
 
785
- return $output;
786
- }
787
 
788
- return '';
 
 
 
789
  }
790
 
791
  /**
@@ -824,11 +936,13 @@ class Render extends Admin_Init {
824
  }
825
  }
826
 
827
- // TODO add esc_attr()? The URL is already safe for attribute usage... I'm not sure if that'll potentially break the URL.
828
- if ( $url )
829
- return '<link rel="canonical" href="' . $url . '" />' . PHP_EOL;
830
-
831
- return '';
 
 
832
  }
833
 
834
  /**
@@ -880,10 +994,10 @@ class Render extends Admin_Init {
880
  ]
881
  );
882
 
883
- if ( $code )
884
- return '<meta name="google-site-verification" content="' . \esc_attr( $code ) . '" />' . PHP_EOL;
885
-
886
- return '';
887
  }
888
 
889
  /**
@@ -908,10 +1022,10 @@ class Render extends Admin_Init {
908
  ]
909
  );
910
 
911
- if ( $code )
912
- return '<meta name="msvalidate.01" content="' . \esc_attr( $code ) . '" />' . PHP_EOL;
913
-
914
- return '';
915
  }
916
 
917
  /**
@@ -936,10 +1050,10 @@ class Render extends Admin_Init {
936
  ]
937
  );
938
 
939
- if ( $code )
940
- return '<meta name="yandex-verification" content="' . \esc_attr( $code ) . '" />' . PHP_EOL;
941
-
942
- return '';
943
  }
944
 
945
  /**
@@ -964,10 +1078,10 @@ class Render extends Admin_Init {
964
  ]
965
  );
966
 
967
- if ( $code )
968
- return '<meta name="baidu-site-verification" content="' . \esc_attr( $code ) . '" />' . PHP_EOL;
969
-
970
- return '';
971
  }
972
 
973
  /**
@@ -992,10 +1106,10 @@ class Render extends Admin_Init {
992
  ]
993
  );
994
 
995
- if ( $code )
996
- return '<meta name="p:domain_verify" content="' . \esc_attr( $code ) . '" />' . PHP_EOL;
997
-
998
- return '';
999
  }
1000
 
1001
  /**
@@ -1014,10 +1128,10 @@ class Render extends Admin_Init {
1014
 
1015
  $meta = $this->get_robots_meta();
1016
 
1017
- if ( empty( $meta ) )
1018
- return '';
1019
-
1020
- return sprintf( '<meta name="robots" content="%s" />' . PHP_EOL, \esc_attr( implode( ',', $meta ) ) );
1021
  }
1022
 
1023
  /**
@@ -1030,7 +1144,7 @@ class Render extends Admin_Init {
1030
  */
1031
  public function get_robots_meta() {
1032
 
1033
- static $cache = null;
1034
 
1035
  /**
1036
  * @since 2.6.0
@@ -1040,7 +1154,7 @@ class Render extends Admin_Init {
1040
  return isset( $cache ) ? $cache : $cache = (array) \apply_filters_ref_array(
1041
  'the_seo_framework_robots_meta',
1042
  [
1043
- $this->robots_meta(),
1044
  $this->get_the_real_ID(),
1045
  ]
1046
  );
@@ -1070,10 +1184,13 @@ class Render extends Admin_Init {
1070
  ]
1071
  );
1072
 
1073
- if ( $url )
1074
- return '<link rel="shortlink" href="' . $url . '" />' . PHP_EOL;
1075
-
1076
- return '';
 
 
 
1077
  }
1078
 
1079
  /**
@@ -1102,7 +1219,6 @@ class Render extends Admin_Init {
1102
  $id,
1103
  ]
1104
  );
1105
-
1106
  /**
1107
  * @since 2.6.0
1108
  * @param string $next The previous-page URL.
@@ -1116,13 +1232,20 @@ class Render extends Admin_Init {
1116
  ]
1117
  );
1118
 
1119
- $output = '';
1120
-
1121
- if ( $prev )
1122
- $output .= '<link rel="prev" href="' . $prev . '" />' . PHP_EOL;
1123
-
1124
- if ( $next )
1125
- $output .= '<link rel="next" href="' . $next . '" />' . PHP_EOL;
 
 
 
 
 
 
 
1126
 
1127
  return $output;
1128
  }
@@ -1176,8 +1299,8 @@ class Render extends Admin_Init {
1176
  if ( $cache['show_timer'] && $timing ) {
1177
  $timers = sprintf(
1178
  ' | %s meta | %s boot',
1179
- number_format( ( microtime( true ) - $timing ) * 1e3, 2 ) . 'ms',
1180
- number_format( _bootstrap_timer() * 1e3, 2 ) . 'ms'
1181
  );
1182
  } else {
1183
  $timers = '';
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
132
  * @return string The cached Twitter card.
133
  */
134
  public function get_current_twitter_card_type() {
135
+ static $cache;
136
+ return isset( $cache ) ? $cache : $cache = $this->generate_twitter_card_type();
137
+ }
138
 
139
+ /**
140
+ * Renders an XHTML element. Sane drop-in for DOMDocument and whatnot.
141
+ *
142
+ * Even though most (if not all) WordPress sites use HTML5, we expect some still use XHTML.
143
+ * We expect HTML5 fully on the back-end.
144
+ *
145
+ * This method should not be used by you. Eventually, it'll print or spawn demigods.
146
+ *
147
+ * @since 4.1.4
148
+ * @access protected
149
+ * Not finished for 'public' use; method may (will) change unannounced.
150
+ * @internal
151
+ * @link <https://github.com/sybrew/the-seo-framework/commit/894d7d3a74e0ed6890b6e8851ef0866df15ea522>
152
+ * Which is something we eventually want to go to, but that's not ready yet.
153
+ *
154
+ * @param array $attributes Associative array of tag names and tag values : {
155
+ * string $name => string $value
156
+ * }
157
+ * @param string $tag The element's tag-name.
158
+ * @param bool|string $text The element's contents, if any.
159
+ * @param bool $new_line Whether to add a new line to the end of the element.
160
+ */
161
+ public function render_element( array $attributes = [], $tag = 'meta', $text = false, $new_line = true ) {
162
 
163
+ $attr = '';
164
+
165
+ foreach ( $attributes as $_name => $_value ) {
166
+ if ( \in_array( $_name, [ 'href', 'xlink:href', 'src' ], true ) ) {
167
+ $_secure_attr_value = \esc_url_raw( $_value );
168
+ } else {
169
+ $_secure_attr_value = \esc_attr( $_value );
170
+ }
171
+
172
+ // phpcs:disable -- Security hint for later, left code intact; Redundant, internal... for now.
173
+ // elseif ( \in_array(
174
+ // $_name,
175
+ // /** @link <https://www.w3.org/TR/2011/WD-html5-20110525/elements.html> */
176
+ // [ 'onabort', 'onblur', 'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'oncontextmenu', 'oncuechange', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onfocus', 'oninput', 'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onpause', 'onplay', 'onplaying', 'onprogress', 'onratechange', 'onreadystatechange', 'onreset', 'onscroll', 'onseeked', 'onseeking', 'onselect', 'onshow', 'onstalled', 'onsubmit', 'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting' ],
177
+ // true
178
+ // ) ) {
179
+ // // Nope. Not this function.
180
+ // continue;
181
+ // }
182
+ // phpcs:enable
183
+
184
+ $attr .= sprintf(
185
+ ' %s="%s"',
186
+ /**
187
+ * @link <https://www.w3.org/TR/2011/WD-html5-20110525/syntax.html#attributes-0>
188
+ * This will strip "safe" characters outside of the alphabet, 0-9, and :_-.
189
+ * I don't want angry parents ringing me at home for their site didn't
190
+ * support proper UTF. We can afford empty tags in rare situations -- not here.
191
+ */
192
+ preg_replace( '/[^a-zA-Z0-9:_-]+/', '', $_name ),
193
+ $_secure_attr_value
194
+ );
195
+ }
196
+
197
+ if ( $text ) {
198
+ $el = vsprintf(
199
+ '<%1$s%2$s>%3$s</%1$s>',
200
+ [
201
+ /** @link <https://www.w3.org/TR/2011/WD-html5-20110525/syntax.html#syntax-tag-name> */
202
+ preg_replace( '/[^0-9a-zA-Z]+/', '', $tag ),
203
+ $attr,
204
+ \esc_html( $text ),
205
+ ]
206
+ );
207
+ } else {
208
+ $el = vsprintf(
209
+ '<%s%s />',
210
+ [
211
+ /** @link <https://www.w3.org/TR/2011/WD-html5-20110525/syntax.html#syntax-tag-name> */
212
+ preg_replace( '/[^0-9a-zA-Z]+/', '', $tag ),
213
+ $attr,
214
+ ]
215
+ );
216
+ }
217
+
218
+ return $el . ( $new_line ? PHP_EOL : '' );
219
+ }
220
+
221
+ /**
222
+ * Renders the 'tsf:aqp' meta tag. Useful for identifying when query-exploit detection
223
+ * is triggered.
224
+ *
225
+ * @since 4.1.4
226
+ *
227
+ * @return string The advanced query protection (aqp) identifier.
228
+ */
229
+ public function advanced_query_protection() {
230
+ return $this->render_element( [
231
+ 'name' => 'tsf:aqp',
232
+ 'value' => '1',
233
+ ] );
234
  }
235
 
236
  /**
247
 
248
  /**
249
  * @since 2.3.0
250
+ * @since 2.7.0 Added output within filter.
251
  * @param string $description The generated description.
252
  * @param int $id The page or term ID.
253
  */
259
  ]
260
  );
261
 
262
+ return $description ? $this->render_element( [
263
+ 'name' => 'description',
264
+ 'content' => $description,
265
+ ] ) : '';
266
  }
267
 
268
  /**
293
  ]
294
  );
295
 
296
+ return $description ? $this->render_element( [
297
+ 'property' => 'og:description',
298
+ 'content' => $description,
299
+ ] ) : '';
300
  }
301
 
302
  /**
325
  ]
326
  );
327
 
328
+ return $locale ? $this->render_element( [
329
+ 'property' => 'og:locale',
330
+ 'content' => $locale,
331
+ ] ) : '';
332
  }
333
 
334
  /**
359
  ]
360
  );
361
 
362
+ return $title ? $this->render_element( [
363
+ 'property' => 'og:title',
364
+ 'content' => $title,
365
+ ] ) : '';
366
  }
367
 
368
  /**
379
 
380
  $type = $this->get_og_type();
381
 
382
+ return $type ? $this->render_element( [
383
+ 'property' => 'og:type',
384
+ 'content' => $type,
385
+ ] ) : '';
386
  }
387
 
388
  /**
405
  $multi = (bool) $this->get_option( 'multi_og_image' );
406
 
407
  foreach ( $this->get_image_details_from_cache( ! $multi ) as $image ) {
408
+ $output .= $this->render_element( [
409
+ 'property' => 'og:image',
410
+ 'content' => $image['url'],
411
+ ] );
412
 
413
  if ( $image['height'] && $image['width'] ) {
414
+ $output .= $this->render_element( [
415
+ 'property' => 'og:image:width',
416
+ 'content' => $image['width'],
417
+ ] );
418
+ $output .= $this->render_element( [
419
+ 'property' => 'og:image:height',
420
+ 'content' => $image['height'],
421
+ ] );
422
  }
423
 
424
  if ( $image['alt'] ) {
425
+ $output .= $this->render_element( [
426
+ 'property' => 'og:image:alt',
427
+ 'content' => $image['alt'],
428
+ ] );
429
  }
430
 
431
+ // Redundant?
432
+ if ( ! $multi ) break;
433
  }
434
 
435
  return $output;
461
  ]
462
  );
463
 
464
+ return $sitename ? $this->render_element( [
465
+ 'property' => 'og:site_name',
466
+ 'content' => $sitename,
467
+ ] ) : '';
468
  }
469
 
470
  /**
472
  *
473
  * @since 1.3.0
474
  * @since 2.9.3 Added filter
475
+ * @since 4.1.4 Now uses `render_element()`, which applies `esc_attr()` on the URL.
476
  * @uses $this->get_current_canonical_url()
477
  *
478
  * @return string The Open Graph URL meta tag.
494
  ]
495
  );
496
 
497
+ return $url ? $this->render_element( [
498
+ 'property' => 'og:url',
499
+ 'content' => $url,
500
+ ] ) : '';
 
501
  }
502
 
503
  /**
513
 
514
  $card = $this->get_current_twitter_card_type();
515
 
516
+ return $card ? $this->render_element( [
517
+ 'name' => 'twitter:card',
518
+ 'content' => $card,
519
+ ] ) : '';
520
  }
521
 
522
  /**
544
  ]
545
  );
546
 
547
+ return $site ? $this->render_element( [
548
+ 'name' => 'twitter:site',
549
+ 'content' => $site,
550
+ ] ) : '';
551
  }
552
 
553
  /**
567
  /**
568
  * @since 2.3.0
569
  * @since 2.7.0 Added output within filter.
570
+ * @param string $creator The Twitter page creator.
571
+ * @param int $id The current page or term ID.
572
  */
573
+ $creator = (string) \apply_filters_ref_array(
574
  'the_seo_framework_twittercreator_output',
575
  [
576
+ $this->get_current_post_author_meta_item( 'twitter_page' ) ?: $this->get_option( 'twitter_creator' ),
577
  $this->get_the_real_ID(),
578
  ]
579
  );
580
 
581
+ return $creator ? $this->render_element( [
582
+ 'name' => 'twitter:creator',
583
+ 'content' => $creator,
584
+ ] ) : '';
585
  }
586
 
587
  /**
611
  ]
612
  );
613
 
614
+ return $title ? $this->render_element( [
615
+ 'name' => 'twitter:title',
616
+ 'content' => $title,
617
+ ] ) : '';
618
  }
619
 
620
  /**
644
  ]
645
  );
646
 
647
+ return $description ? $this->render_element( [
648
+ 'name' => 'twitter:description',
649
+ 'content' => $description,
650
+ ] ) : '';
651
  }
652
 
653
  /**
667
  $output = '';
668
 
669
  foreach ( $this->get_image_details_from_cache( ! $this->get_option( 'multi_og_image' ) ) as $image ) {
670
+ $output .= $this->render_element( [
671
+ 'name' => 'twitter:image',
672
+ 'content' => $image['url'],
673
+ ] );
674
 
675
  if ( $image['height'] && $image['width'] ) {
676
+ $output .= $this->render_element( [
677
+ 'name' => 'twitter:image:width',
678
+ 'content' => $image['width'],
679
+ ] );
680
+ $output .= $this->render_element( [
681
+ 'name' => 'twitter:image:height',
682
+ 'content' => $image['height'],
683
+ ] );
684
  }
685
 
686
  if ( $image['alt'] ) {
687
+ $output .= $this->render_element( [
688
+ 'name' => 'twitter:image:alt',
689
+ 'content' => $image['alt'],
690
+ ] );
691
  }
692
 
693
  // Only grab a single image. Twitter grabs the final (less favorable) image otherwise.
706
  */
707
  public function theme_color() {
708
 
 
 
709
  $theme_color = $this->get_option( 'theme_color' );
710
 
711
+ return $theme_color ? $this->render_element( [
712
+ 'name' => 'theme-color',
713
+ 'content' => $theme_color,
714
+ ] ) : '';
715
  }
716
 
717
  /**
737
  $facebook_page = (string) \apply_filters_ref_array(
738
  'the_seo_framework_facebookauthor_output',
739
  [
740
+ $this->get_current_post_author_meta_item( 'facebook_page' ) ?: $this->get_option( 'facebook_author' ),
741
  $this->get_the_real_ID(),
742
  ]
743
  );
744
 
745
+ return $facebook_page ? $this->render_element( [
746
+ 'property' => 'article:author',
747
+ 'content' => $facebook_page,
748
+ ] ) : '';
749
  }
750
 
751
  /**
775
  ]
776
  );
777
 
778
+ return $publisher ? $this->render_element( [
779
+ 'property' => 'article:publisher',
780
+ 'content' => $publisher,
781
+ ] ) : '';
782
  }
783
 
784
  /**
806
  ]
807
  );
808
 
809
+ return $app_id ? $this->render_element( [
810
+ 'property' => 'fb:app_id',
811
+ 'content' => $app_id,
812
+ ] ) : '';
813
  }
814
 
815
  /**
849
  ]
850
  );
851
 
852
+ return $time ? $this->render_element( [
853
+ 'property' => 'article:published_time',
854
+ 'content' => $time,
855
+ ] ) : '';
856
  }
857
 
858
  /**
859
  * Renders Article Modified Time meta tag.
 
860
  *
861
  * @since 2.2.2
862
  * @since 2.7.0 Listens to $this->get_the_real_ID() instead of WordPress Core ID determination.
863
  * @since 2.8.0 Returns empty on product pages.
864
  * @since 3.0.0 : 1. Now checks for 0000 timestamps.
865
  * 2. Now uses timestamp formats.
866
+ * @since 4.1.4 No longer renders the Open Graph Updated Time meta tag.
867
+ * @see og_updated_time()
868
  *
869
+ * @return string The Article Modified Time meta tag
870
  */
871
  public function article_modified_time() {
872
 
873
  if ( ! $this->output_modified_time() ) return '';
874
 
875
+ $time = $this->get_modified_time();
 
 
 
 
 
 
876
 
877
+ return $time ? $this->render_element( [
878
+ 'property' => 'article:modified_time',
879
+ 'content' => $time,
880
+ ] ) : '';
881
+ }
 
 
 
 
 
 
 
 
882
 
883
+ /**
884
+ * Renders the Open Graph Updated Time meta tag.
885
+ *
886
+ * @since 4.1.4
887
+ *
888
+ * @return string The Article Modified Time meta tag, and optionally the Open Graph Updated Time.
889
+ */
890
+ public function og_updated_time() {
891
 
892
+ if ( ! $this->use_og_tags() ) return '';
893
+ if ( ! $this->output_published_time() ) return '';
894
 
895
+ $time = $this->get_modified_time();
 
896
 
897
+ return $time ? $this->render_element( [
898
+ 'property' => 'og:updated_time',
899
+ 'content' => $time,
900
+ ] ) : '';
901
  }
902
 
903
  /**
936
  }
937
  }
938
 
939
+ return $url ? $this->render_element(
940
+ [
941
+ 'rel' => 'canonical',
942
+ 'href' => $url,
943
+ ],
944
+ 'link'
945
+ ) : '';
946
  }
947
 
948
  /**
994
  ]
995
  );
996
 
997
+ return $code ? $this->render_element( [
998
+ 'name' => 'google-site-verification',
999
+ 'content' => $code,
1000
+ ] ) : '';
1001
  }
1002
 
1003
  /**
1022
  ]
1023
  );
1024
 
1025
+ return $code ? $this->render_element( [
1026
+ 'name' => 'msvalidate.01',
1027
+ 'content' => $code,
1028
+ ] ) : '';
1029
  }
1030
 
1031
  /**
1050
  ]
1051
  );
1052
 
1053
+ return $code ? $this->render_element( [
1054
+ 'name' => 'yandex-verification',
1055
+ 'content' => $code,
1056
+ ] ) : '';
1057
  }
1058
 
1059
  /**
1078
  ]
1079
  );
1080
 
1081
+ return $code ? $this->render_element( [
1082
+ 'name' => 'baidu-site-verification',
1083
+ 'content' => $code,
1084
+ ] ) : '';
1085
  }
1086
 
1087
  /**
1106
  ]
1107
  );
1108
 
1109
+ return $code ? $this->render_element( [
1110
+ 'name' => 'p:domain_verify',
1111
+ 'content' => $code,
1112
+ ] ) : '';
1113
  }
1114
 
1115
  /**
1128
 
1129
  $meta = $this->get_robots_meta();
1130
 
1131
+ return $meta ? $this->render_element( [
1132
+ 'name' => 'robots',
1133
+ 'content' => implode( ',', $meta ),
1134
+ ] ) : '';
1135
  }
1136
 
1137
  /**
1144
  */
1145
  public function get_robots_meta() {
1146
 
1147
+ static $cache;
1148
 
1149
  /**
1150
  * @since 2.6.0
1154
  return isset( $cache ) ? $cache : $cache = (array) \apply_filters_ref_array(
1155
  'the_seo_framework_robots_meta',
1156
  [
1157
+ $this->generate_robots_meta(),
1158
  $this->get_the_real_ID(),
1159
  ]
1160
  );
1184
  ]
1185
  );
1186
 
1187
+ return $url ? $this->render_element(
1188
+ [
1189
+ 'rel' => 'shortlink',
1190
+ 'href' => $url,
1191
+ ],
1192
+ 'link'
1193
+ ) : '';
1194
  }
1195
 
1196
  /**
1219
  $id,
1220
  ]
1221
  );
 
1222
  /**
1223
  * @since 2.6.0
1224
  * @param string $next The previous-page URL.
1232
  ]
1233
  );
1234
 
1235
+ $output = $prev ? $this->render_element(
1236
+ [
1237
+ 'rel' => 'prev',
1238
+ 'href' => $prev,
1239
+ ],
1240
+ 'link'
1241
+ ) : '';
1242
+ $output .= $next ? $this->render_element(
1243
+ [
1244
+ 'rel' => 'next',
1245
+ 'href' => $next,
1246
+ ],
1247
+ 'link'
1248
+ ) : '';
1249
 
1250
  return $output;
1251
  }
1299
  if ( $cache['show_timer'] && $timing ) {
1300
  $timers = sprintf(
1301
  ' | %s meta | %s boot',
1302
+ number_format( ( microtime( true ) - $timing ) * 1e3, 2, null, '' ) . 'ms',
1303
+ number_format( _bootstrap_timer() * 1e3, 2, null, '' ) . 'ms'
1304
  );
1305
  } else {
1306
  $timers = '';
inc/classes/sanitize.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -261,7 +261,6 @@ class Sanitize extends Admin_Pages {
261
  'display_character_counter',
262
 
263
  'cache_sitemap',
264
- 'cache_object',
265
 
266
  'display_seo_bar_tables',
267
  'display_seo_bar_metabox',
@@ -834,6 +833,35 @@ class Sanitize extends Admin_Pages {
834
  return $data;
835
  }
836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  /**
838
  * Returns the title separator value string.
839
  *
@@ -883,9 +911,9 @@ class Sanitize extends Admin_Pages {
883
  * Returns an one-line sanitized description and escapes it.
884
  *
885
  * @since 2.5.0
886
- * @since 2.6.6 : Removes duplicated spaces.
887
- * @since 2.8.0 : Method is now public.
888
- * @since 2.8.2 : Added extra sanitation.
889
  * @uses $this->s_description_raw().
890
  * @uses $this->escape_description().
891
  *
@@ -1246,8 +1274,8 @@ class Sanitize extends Admin_Pages {
1246
 
1247
  if ( ! \is_array( $new_values ) ) return [];
1248
 
1249
- foreach ( $new_values as $index => $value ) {
1250
- $new_values[ $index ] = $this->s_one_zero( $value );
1251
  }
1252
 
1253
  return $new_values;
@@ -1285,14 +1313,7 @@ class Sanitize extends Admin_Pages {
1285
  * @return array
1286
  */
1287
  public function s_taxonomies( $new_values ) {
1288
-
1289
- if ( ! \is_array( $new_values ) ) return [];
1290
-
1291
- foreach ( $new_values as $index => $value ) {
1292
- $new_values[ $index ] = $this->s_one_zero( $value );
1293
- }
1294
-
1295
- return $new_values;
1296
  }
1297
 
1298
  /**
@@ -1357,6 +1378,7 @@ class Sanitize extends Admin_Pages {
1357
  * of entities in HTML input value attributes.
1358
  *
1359
  * @since 4.0.0
 
1360
  *
1361
  * @param string $new_value String with possibly ampersands.
1362
  * @return string
@@ -1514,9 +1536,7 @@ class Sanitize extends Admin_Pages {
1514
 
1515
  if ( strpos( $link, 'profile.php' ) ) {
1516
  //= Gets query parameters.
1517
- // FIXME why don't we use parse_url( $link, PHP_URL_QUERY );?
1518
- $q = strtok( substr( $link, strpos( $link, '?' ) ), '?' );
1519
- parse_str( $q, $r );
1520
  if ( isset( $r['id'] ) ) {
1521
  $link = 'https://www.facebook.com/profile.php?id=' . \absint( $r['id'] );
1522
  $link = $this->s_url_query( $link );
@@ -1806,7 +1826,6 @@ class Sanitize extends Admin_Pages {
1806
  *
1807
  * @see WordPress Core sanitize_key()
1808
  * @since 4.0.0
1809
- * @deprecated
1810
  *
1811
  * @param string $id The unsanitized ID.
1812
  * @return string The sanitized ID.
@@ -1941,6 +1960,8 @@ class Sanitize extends Admin_Pages {
1941
  * @since 4.0.0
1942
  * @since 4.0.2 Now finds smaller images when they're over 4K.
1943
  * @since 4.0.5 Now faults images with filename extensions APNG, BMP, ICO, TIFF, or SVG.
 
 
1944
  * @NOTE If the input details are in an associative array, they'll be converted to sequential.
1945
  *
1946
  * @param array $details The image details, either associative (see $defaults) or sequential.
@@ -1997,8 +2018,7 @@ class Sanitize extends Admin_Pages {
1997
  if ( ! $width || ! $height )
1998
  $width = $height = 0;
1999
 
2000
- if ( $width > 4096 || $height > 4096 ) {
2001
- // FIXME Why do we assume there's an $id available here, because we only know $width/$height when there's an $id?
2002
  $new_image = $this->get_largest_acceptable_image_src( $id, 4096 );
2003
  $url = $new_image ? $this->s_url_relative_to_current_scheme( $new_image[0] ) : '';
2004
 
@@ -2041,9 +2061,8 @@ class Sanitize extends Admin_Pages {
2041
  if ( isset( $details_array['url'] ) )
2042
  $details_array = [ $details_array ];
2043
 
2044
- foreach ( $details_array as $details ) {
2045
  $cleaned_details[] = $this->s_image_details( $details );
2046
- }
2047
 
2048
  return array_values(
2049
  array_intersect_key(
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
261
  'display_character_counter',
262
 
263
  'cache_sitemap',
 
264
 
265
  'display_seo_bar_tables',
266
  'display_seo_bar_metabox',
833
  return $data;
834
  }
835
 
836
+ /**
837
+ * Sanitizes user meta.
838
+ *
839
+ * @since 4.1.4
840
+ *
841
+ * @param array $data The user meta to sanitize.
842
+ * @return array The sanitized user meta.
843
+ */
844
+ public function s_user_meta( array $data ) {
845
+
846
+ foreach ( $data as $key => &$value ) :
847
+ switch ( $key ) :
848
+ case 'facebook_page':
849
+ $value = $this->s_facebook_profile( $value );
850
+ continue 2;
851
+
852
+ case 'twitter_page':
853
+ $value = $this->s_twitter_name( $value );
854
+ continue 2;
855
+
856
+ default:
857
+ unset( $data[ $key ] );
858
+ break;
859
+ endswitch;
860
+ endforeach;
861
+
862
+ return $data;
863
+ }
864
+
865
  /**
866
  * Returns the title separator value string.
867
  *
911
  * Returns an one-line sanitized description and escapes it.
912
  *
913
  * @since 2.5.0
914
+ * @since 2.6.6 Removes duplicated spaces.
915
+ * @since 2.8.0 Method is now public.
916
+ * @since 2.8.2 Added extra sanitation.
917
  * @uses $this->s_description_raw().
918
  * @uses $this->escape_description().
919
  *
1274
 
1275
  if ( ! \is_array( $new_values ) ) return [];
1276
 
1277
+ foreach ( $new_values as $index => &$value ) {
1278
+ $value = $this->s_one_zero( $value );
1279
  }
1280
 
1281
  return $new_values;
1313
  * @return array
1314
  */
1315
  public function s_taxonomies( $new_values ) {
1316
+ return $this->s_post_types( $new_values );
 
 
 
 
 
 
 
1317
  }
1318
 
1319
  /**
1378
  * of entities in HTML input value attributes.
1379
  *
1380
  * @since 4.0.0
1381
+ * TODO a better name would've been "esc_attr_revert_amp"...?
1382
  *
1383
  * @param string $new_value String with possibly ampersands.
1384
  * @return string
1536
 
1537
  if ( strpos( $link, 'profile.php' ) ) {
1538
  //= Gets query parameters.
1539
+ parse_str( parse_url( $link, PHP_URL_QUERY ), $r );
 
 
1540
  if ( isset( $r['id'] ) ) {
1541
  $link = 'https://www.facebook.com/profile.php?id=' . \absint( $r['id'] );
1542
  $link = $this->s_url_query( $link );
1826
  *
1827
  * @see WordPress Core sanitize_key()
1828
  * @since 4.0.0
 
1829
  *
1830
  * @param string $id The unsanitized ID.
1831
  * @return string The sanitized ID.
1960
  * @since 4.0.0
1961
  * @since 4.0.2 Now finds smaller images when they're over 4K.
1962
  * @since 4.0.5 Now faults images with filename extensions APNG, BMP, ICO, TIFF, or SVG.
1963
+ * @since 4.1.4 Fixed theoretical issue where a different image could be set when width
1964
+ * and height are supplied and either over 4K, but no ID is given.
1965
  * @NOTE If the input details are in an associative array, they'll be converted to sequential.
1966
  *
1967
  * @param array $details The image details, either associative (see $defaults) or sequential.
2018
  if ( ! $width || ! $height )
2019
  $width = $height = 0;
2020
 
2021
+ if ( $id && ( $width > 4096 || $height > 4096 ) ) {
 
2022
  $new_image = $this->get_largest_acceptable_image_src( $id, 4096 );
2023
  $url = $new_image ? $this->s_url_relative_to_current_scheme( $new_image[0] ) : '';
2024
 
2061
  if ( isset( $details_array['url'] ) )
2062
  $details_array = [ $details_array ];
2063
 
2064
+ foreach ( $details_array as $details )
2065
  $cleaned_details[] = $this->s_image_details( $details );
 
2066
 
2067
  return array_values(
2068
  array_intersect_key(
inc/classes/silencer.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2018 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
inc/classes/site-options.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -79,7 +79,6 @@ class Site_Options extends Sanitize {
79
  'alter_search_query_type' => 'in_query', // Search query type.
80
 
81
  'cache_sitemap' => 1, // Sitemap transient cache.
82
- 'cache_object' => 1, // Object caching.
83
 
84
  // General. Layout.
85
  'display_seo_bar_tables' => 1, // SEO Bar post-list tables.
@@ -141,7 +140,7 @@ class Site_Options extends Sanitize {
141
  'advanced_query_protection' => 1,
142
 
143
  // Robots pagination index.
144
- 'paged_noindex' => 1, // Every second or later page noindex
145
  'home_paged_noindex' => 0, // Every second or later homepage noindex
146
 
147
  // Robots copyright.
@@ -194,7 +193,7 @@ class Site_Options extends Sanitize {
194
  // oEmbed.
195
  'oembed_use_og_title' => 0, // Use custom meta titles in oEmbeds
196
  'oembed_use_social_image' => 1, // Use social images in oEmbeds
197
- 'oembed_remove_author' => 0, // Remove author from oEmbeds
198
 
199
  // Social on/off.
200
  'og_tags' => 1, // Output of Open Graph meta tags
@@ -247,7 +246,7 @@ class Site_Options extends Sanitize {
247
 
248
  // Sitemaps.
249
  'sitemaps_output' => 1, // Output of sitemap
250
- 'sitemap_query_limit' => 3000, // Sitemap post limit.
251
 
252
  'sitemaps_modified' => 1, // Add sitemap modified time.
253
  'sitemaps_priority' => 0, // Add sitemap priorities.
@@ -376,14 +375,21 @@ class Site_Options extends Sanitize {
376
 
377
  /**
378
  * @since 2.0.0
 
 
 
379
  * @param array $settings The settings
380
  * @param string $setting The settings field.
 
381
  */
382
  return $cache[ $setting ] = \apply_filters_ref_array(
383
  'the_seo_framework_get_options',
384
  [
385
- \get_option( $setting ),
 
 
386
  $setting,
 
387
  ]
388
  );
389
  }
@@ -541,7 +547,7 @@ class Site_Options extends Sanitize {
541
  * Get the default of any of the The SEO Framework settings.
542
  *
543
  * @since 2.2.4
544
- * @since 2.8.2 : No longer decodes entities on request.
545
  * @since 3.1.0 : 1. Now returns null if the option doesn't exist, instead of -1.
546
  * 2. Is now influenced by filters.
547
  * 3. Now also strips slashes when using cache.
@@ -579,7 +585,7 @@ class Site_Options extends Sanitize {
579
  * Get the warned setting of any of the The SEO Framework settings.
580
  *
581
  * @since 2.3.4
582
- * @since 3.1.0 : Now returns 0 if the option doesn't exist, instead of -1.
583
  * @uses THE_SEO_FRAMEWORK_SITE_OPTIONS
584
  * @uses $this->get_warned_site_options()
585
  *
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
79
  'alter_search_query_type' => 'in_query', // Search query type.
80
 
81
  'cache_sitemap' => 1, // Sitemap transient cache.
 
82
 
83
  // General. Layout.
84
  'display_seo_bar_tables' => 1, // SEO Bar post-list tables.
140
  'advanced_query_protection' => 1,
141
 
142
  // Robots pagination index.
143
+ 'paged_noindex' => 0, // Every second or later page noindex
144
  'home_paged_noindex' => 0, // Every second or later homepage noindex
145
 
146
  // Robots copyright.
193
  // oEmbed.
194
  'oembed_use_og_title' => 0, // Use custom meta titles in oEmbeds
195
  'oembed_use_social_image' => 1, // Use social images in oEmbeds
196
+ 'oembed_remove_author' => 1, // Remove author from oEmbeds
197
 
198
  // Social on/off.
199
  'og_tags' => 1, // Output of Open Graph meta tags
246
 
247
  // Sitemaps.
248
  'sitemaps_output' => 1, // Output of sitemap
249
+ 'sitemap_query_limit' => 1000, // Sitemap post limit.
250
 
251
  'sitemaps_modified' => 1, // Add sitemap modified time.
252
  'sitemaps_priority' => 0, // Add sitemap priorities.
375
 
376
  /**
377
  * @since 2.0.0
378
+ * @since 4.1.4 1. Now considers headlessness.
379
+ * 2. Now returns a 3rd parameter: boolean $headless.
380
+ *
381
  * @param array $settings The settings
382
  * @param string $setting The settings field.
383
+ * @param bool $headless Whether the options are headless.
384
  */
385
  return $cache[ $setting ] = \apply_filters_ref_array(
386
  'the_seo_framework_get_options',
387
  [
388
+ $this->is_headless['settings'] && THE_SEO_FRAMEWORK_SITE_OPTIONS === $setting
389
+ ? $this->get_default_site_options()
390
+ : \get_option( $setting ),
391
  $setting,
392
+ $this->is_headless['settings'],
393
  ]
394
  );
395
  }
547
  * Get the default of any of the The SEO Framework settings.
548
  *
549
  * @since 2.2.4
550
+ * @since 2.8.2 No longer decodes entities on request.
551
  * @since 3.1.0 : 1. Now returns null if the option doesn't exist, instead of -1.
552
  * 2. Is now influenced by filters.
553
  * 3. Now also strips slashes when using cache.
585
  * Get the warned setting of any of the The SEO Framework settings.
586
  *
587
  * @since 2.3.4
588
+ * @since 3.1.0 Now returns 0 if the option doesn't exist, instead of -1.
589
  * @uses THE_SEO_FRAMEWORK_SITE_OPTIONS
590
  * @uses $this->get_warned_site_options()
591
  *
inc/classes/term-data.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -35,13 +35,13 @@ namespace The_SEO_Framework;
35
  class Term_Data extends Post_Data {
36
 
37
  /**
38
- * Initializes term meta data filters and functions.
39
  *
40
  * @since 4.0.0
 
41
  */
42
- public function init_term_meta() {
43
  \add_action( 'edit_term', [ $this, '_update_term_meta' ], 10, 3 );
44
- \add_action( 'delete_term', [ $this, '_delete_term_meta' ], 10, 3 );
45
  }
46
 
47
  /**
@@ -59,6 +59,8 @@ class Term_Data extends Post_Data {
59
  /**
60
  * Returns the term meta item by key.
61
  *
 
 
62
  * @param string $item The item to get.
63
  * @param int $term_id The Term ID.
64
  * @param bool $use_cache Whether to use caching; only has effect when $term_id is set.
@@ -112,6 +114,8 @@ class Term_Data extends Post_Data {
112
  * @since 3.1.0 Deprecated filter.
113
  * @since 4.0.0 1. Removed deprecated filter.
114
  * 2. Now fills in defaults.
 
 
115
  *
116
  * @param int $term_id The Term ID.
117
  * @param bool $use_cache Whether to use caching.
@@ -135,49 +139,27 @@ class Term_Data extends Post_Data {
135
  $this->get_term_meta_defaults( $term_id )
136
  );
137
 
138
- $meta = \get_term_meta( $term_id, THE_SEO_FRAMEWORK_TERM_OPTIONS, true ) ?: [];
139
-
140
- static $has_deprecated_filter = null;
141
- if ( null === $has_deprecated_filter && \has_filter( 'the_seo_framework_current_term_meta' ) ) {
142
- $has_deprecated_filter = true;
143
- $this->_deprecated_filter( 'the_seo_framework_current_term_meta', '4.0.0', 'the_seo_framework_term_meta' );
144
- }
145
-
146
- if ( $has_deprecated_filter && $meta ) {
147
- /**
148
- * @since 3.0.0
149
- * @since 4.0.0 Deprecated.
150
- * @deprecated
151
- * @param array $meta The CURRENT term metadata.
152
- * @param int $term_id The term ID.
153
- */
154
- $meta = \apply_filters( 'the_seo_framework_current_term_meta', $meta, $term_id );
155
-
156
- /**
157
- * Filter the extraneous term meta items based on defaults' keys.
158
- * This is redundant, but in line with the requirement at `get_post_meta()`
159
- * where we get all metadata without a key.
160
- *
161
- * @see `$this->s_term_meta()`, which strips them out, already. As such,
162
- * we only use this when the (deprecated) filter is used.
163
- */
164
- $meta = array_intersect_key(
165
- $meta,
166
- $defaults
167
- );
168
  }
169
 
170
  /**
171
  * @since 4.0.5
 
 
172
  * @note Do not delete/unset/add indexes! It'll cause errors.
173
  * @param array $meta The current term meta.
174
  * @param int $term_id The term ID.
 
175
  */
176
  $meta = \apply_filters_ref_array(
177
  'the_seo_framework_term_meta',
178
  [
179
  array_merge( $defaults, $meta ),
180
  $term_id,
 
181
  ]
182
  );
183
 
@@ -410,21 +392,6 @@ class Term_Data extends Post_Data {
410
  \update_term_meta( $term->term_id, THE_SEO_FRAMEWORK_TERM_OPTIONS, $data );
411
  }
412
 
413
- /**
414
- * Delete term meta data when a term is deleted.
415
- * Deletes only the default data keys; or everything when only that is present.
416
- *
417
- * @since 4.0.0
418
- * @access private
419
- *
420
- * @param int $term_id Term ID.
421
- * @param int $tt_id Term Taxonomy ID.
422
- * @param string $taxonomy Taxonomy slug.
423
- */
424
- public function _delete_term_meta( $term_id, $tt_id, $taxonomy ) { // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
425
- $this->delete_term_meta( $term_id );
426
- }
427
-
428
  /**
429
  * Deletes term meta.
430
  * Deletes only the default data keys; or everything when only that is present.
@@ -520,7 +487,7 @@ class Term_Data extends Post_Data {
520
  $taxonomies = \get_object_taxonomies( $post_type, 'objects' );
521
  $taxonomies = array_filter(
522
  $taxonomies,
523
- function( $t ) {
524
  return ! empty( $t->hierarchical );
525
  }
526
  );
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
35
  class Term_Data extends Post_Data {
36
 
37
  /**
38
+ * Initializes term meta data handlers.
39
  *
40
  * @since 4.0.0
41
+ * @since 4.1.4 Now protected.
42
  */
43
+ protected function init_term_meta() {
44
  \add_action( 'edit_term', [ $this, '_update_term_meta' ], 10, 3 );
 
45
  }
46
 
47
  /**
59
  /**
60
  * Returns the term meta item by key.
61
  *
62
+ * @since 4.0.0
63
+ *
64
  * @param string $item The item to get.
65
  * @param int $term_id The Term ID.
66
  * @param bool $use_cache Whether to use caching; only has effect when $term_id is set.
114
  * @since 3.1.0 Deprecated filter.
115
  * @since 4.0.0 1. Removed deprecated filter.
116
  * 2. Now fills in defaults.
117
+ * @since 4.1.4 1. Removed deprecated filter.
118
+ * 2. Now considers headlessness.
119
  *
120
  * @param int $term_id The Term ID.
121
  * @param bool $use_cache Whether to use caching.
139
  $this->get_term_meta_defaults( $term_id )
140
  );
141
 
142
+ if ( $this->is_headless['meta'] ) {
143
+ $meta = [];
144
+ } else {
145
+ $meta = \get_term_meta( $term_id, THE_SEO_FRAMEWORK_TERM_OPTIONS, true ) ?: [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
 
148
  /**
149
  * @since 4.0.5
150
+ * @since 4.1.4 1. Now considers headlessness.
151
+ * 2. Now returns a 3rd parameter: boolean $headless.
152
  * @note Do not delete/unset/add indexes! It'll cause errors.
153
  * @param array $meta The current term meta.
154
  * @param int $term_id The term ID.
155
+ * @param bool $headless Whether the meta are headless.
156
  */
157
  $meta = \apply_filters_ref_array(
158
  'the_seo_framework_term_meta',
159
  [
160
  array_merge( $defaults, $meta ),
161
  $term_id,
162
+ $this->is_headless['meta'],
163
  ]
164
  );
165
 
392
  \update_term_meta( $term->term_id, THE_SEO_FRAMEWORK_TERM_OPTIONS, $data );
393
  }
394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  /**
396
  * Deletes term meta.
397
  * Deletes only the default data keys; or everything when only that is present.
487
  $taxonomies = \get_object_taxonomies( $post_type, 'objects' );
488
  $taxonomies = array_filter(
489
  $taxonomies,
490
+ static function( $t ) {
491
  return ! empty( $t->hierarchical );
492
  }
493
  );
inc/classes/user-data.class.php CHANGED
@@ -10,7 +10,7 @@ namespace The_SEO_Framework;
10
 
11
  /**
12
  * The SEO Framework plugin
13
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
@@ -35,201 +35,317 @@ namespace The_SEO_Framework;
35
  class User_Data extends Term_Data {
36
 
37
  /**
38
- * Returns default user meta.
39
  *
40
- * @since 3.0.0
41
- * @TODO add filter as with get_term_meta_defaults() and get_post_meta_defaults()
42
- * (also define unfiltered values via a different function)
43
- *
44
- * @return array The default user meta index and values.
45
  */
46
- public function get_default_user_data() {
47
- return [
48
- 'counter_type' => 3,
49
- 'facebook_page' => '',
50
- 'twitter_page' => '',
51
- ];
52
  }
53
 
54
  /**
55
- * Returns the current post author ID.
56
- * Memoizes the return value for the current request.
57
  *
58
- * @since 3.0.0
59
- * @since 3.2.2 : 1. Now no longer returns the latest post author ID on home-as-blog pages.
60
- * 2. Now always returns an integer.
61
  *
62
- * @return int Post author ID on success, 0 on failure.
 
 
 
63
  */
64
- public function get_current_post_author_id() {
65
-
66
- static $cache;
67
 
68
- if ( isset( $cache ) )
69
- return $cache;
70
-
71
- if ( $this->is_singular() ) {
72
- $post = \get_post( $this->get_the_real_ID() );
73
- $cache = isset( $post->post_author ) ? (int) $post->post_author : 0;
74
  }
75
 
76
- return $cache ?: $cache = 0;
77
  }
78
 
79
  /**
80
- * Sets up user ID and returns it if user is found.
81
- * To be used in AJAX, back-end and front-end.
82
  *
83
- * @since 2.7.0
84
  *
85
- * @return int The user ID. 0 if user is not found.
 
 
86
  */
87
- public function get_user_id() {
88
-
89
- static $user_id = null;
90
-
91
- if ( isset( $user_id ) )
92
- return $user_id;
93
-
94
- $user = \wp_get_current_user();
95
 
96
- return $user_id = $user->exists() ? (int) $user->ID : 0;
 
 
 
 
 
 
 
 
 
 
97
  }
98
 
99
  /**
100
- * Fetches The SEO Framework usermeta.
101
  * Memoizes the return value, can be bypassed.
102
  *
103
  * @since 2.7.0
104
  * @since 2.8.0 Always returns array, even if no value is assigned.
105
- * @TODO update to return default values as with `get_post_meta` and `get_term_meta`
 
 
106
  *
107
- * @param int $user_id The user ID.
108
- * @param string $key The user metadata key. Leave empty to fetch all data.
109
- * @param bool $use_cache Whether to store and use options from cache, or bypass it.
110
  * @return array The user SEO meta data.
111
  */
112
- public function get_user_meta( $user_id, $key = THE_SEO_FRAMEWORK_USER_OPTIONS, $use_cache = true ) {
 
 
113
 
114
- if ( false === $use_cache )
115
- return ( $meta = \get_user_meta( $user_id, $key, true ) ) && \is_array( $meta ) ? $meta : [];
116
 
117
- static $usermeta_cache = [];
 
 
 
 
 
118
 
119
- if ( isset( $usermeta_cache[ $user_id ][ $key ] ) )
120
- return $usermeta_cache[ $user_id ][ $key ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- return $usermeta_cache[ $user_id ][ $key ] = ( $meta = \get_user_meta( $user_id, $key, true ) ) && \is_array( $meta ) ? $meta : [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  }
124
 
125
  /**
126
- * Returns current post author option.
127
  *
128
- * @since 3.0.0
129
  *
130
- * @param int $author_id The author ID. When empty, it will return $default.
131
- * @param string $option The option name. When empty, it will return $default.
132
- * @param mixed $default The default value to return when the data doesn't exist.
133
- * @return mixed The metadata value
134
  */
135
- public function get_author_option( $author_id, $option, $default = null ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- if ( ! $author_id || ! $option )
138
- return $default;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- return $this->get_user_option( $author_id, $option, $default );
 
 
 
 
 
 
141
  }
142
 
143
  /**
144
- * Returns current post author option.
145
  *
146
- * @since 3.0.0
147
  *
148
- * @param string $option The option name.
149
- * @param mixed $default The default value to return when the data doesn't exist.
150
- * @return mixed The metadata value
151
  */
152
- public function get_current_author_option( $option, $default = null ) {
153
- return $this->get_author_option( $this->get_current_post_author_id(), $option, $default );
 
 
 
 
 
 
 
 
 
154
  }
155
 
156
  /**
157
- * Fetches user SEO user meta data by name.
158
- * Memoizes all meta data per $user_id.
159
  *
160
- * If no $user_id is supplied, it will fetch the current logged in user ID.
161
  *
162
- * @since 2.7.0
163
- * @since 3.0.0 1. Default is no longer cached.
164
- * 2. Now always fallbacks to $default.
165
- * 3. Added not-found cache.
166
- *
167
- * @param int $user_id The user ID. When empty, it will try to fetch the current user.
168
- * @param string $option The option name.
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;
176
 
177
- if ( empty( $user_id ) )
178
- $user_id = $this->get_user_id();
179
 
180
- if ( ! $user_id )
181
- return $default;
182
 
183
- static $options_cache = [], $notfound_cache = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
- if ( isset( $options_cache[ $user_id ][ $option ] ) )
186
- return $options_cache[ $user_id ][ $option ];
 
 
 
 
 
 
 
 
 
187
 
188
- if ( isset( $notfound_cache[ $user_id ][ $option ] ) )
189
- return $default;
190
 
191
- $usermeta = $this->get_user_meta( $user_id );
192
 
193
- if ( isset( $usermeta[ $option ] ) ) {
194
- return $options_cache[ $user_id ][ $option ] = $usermeta[ $option ];
195
- } else {
196
- $notfound_cache[ $user_id ][ $option ] = true;
197
  }
198
 
199
- return $default;
200
  }
201
 
202
  /**
203
- * Updates user SEO option.
 
204
  *
205
  * @since 2.7.0
206
- * @since 2.8.0 New users now get a new array assigned.
207
  *
208
- * @param int $user_id The user ID.
209
- * @param string $option The user's SEO metadata option.
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;
217
-
218
- if ( empty( $user_id ) )
219
- $user_id = $this->get_user_id();
220
 
221
- if ( empty( $user_id ) )
222
- return false;
223
 
224
- $meta = $this->get_user_meta( $user_id, THE_SEO_FRAMEWORK_USER_OPTIONS, false );
 
225
 
226
- /**
227
- * @since 2.8.0 initializes new array on empty values.
228
- */
229
- \is_array( $meta ) or $meta = [];
230
 
231
- $meta[ $option ] = $value;
 
232
 
233
- return \update_user_meta( $user_id, THE_SEO_FRAMEWORK_USER_OPTIONS, $meta );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
  }
10
 
11
  /**
12
  * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
14
  *
15
  * This program is free software: you can redistribute it and/or modify
16
  * it under the terms of the GNU General Public License version 3 as published
35
  class User_Data extends Term_Data {
36
 
37
  /**
38
+ * Initializes user meta data handlers.
39
  *
40
+ * @since 4.1.4 Now protected.
 
 
 
 
41
  */
42
+ protected function init_user_meta() {
43
+ \add_action( 'personal_options_update', [ $this, '_update_user_meta' ], 10, 1 );
44
+ \add_action( 'edit_user_profile_update', [ $this, '_update_user_meta' ], 10, 1 );
 
 
 
45
  }
46
 
47
  /**
48
+ * Returns the user meta item by key.
 
49
  *
50
+ * @since 4.1.4
 
 
51
  *
52
+ * @param string $item The item to get. Required.
53
+ * @param int $user_id The user ID. Optional.
54
+ * @param bool $use_cache Whether to use caching.
55
+ * @return mixed The user meta item. Null when not found.
56
  */
57
+ public function get_user_meta_item( $item, $user_id = 0, $use_cache = true ) {
 
 
58
 
59
+ if ( ! $user_id ) {
60
+ $meta = $this->get_user_meta( $this->get_user_id(), $use_cache );
61
+ } else {
62
+ $meta = $this->get_user_meta( $user_id, $use_cache );
 
 
63
  }
64
 
65
+ return isset( $meta[ $item ] ) ? $meta[ $item ] : null;
66
  }
67
 
68
  /**
69
+ * Returns the author meta item by key.
 
70
  *
71
+ * @since 4.1.4
72
  *
73
+ * @param string $item The item to get. Required.
74
+ * @param bool $use_cache Whether to use caching.
75
+ * @return mixed The author meta item. Null when not found.
76
  */
77
+ public function get_current_post_author_meta_item( $item, $use_cache = true ) {
78
+ return $this->get_user_meta_item( $item, $this->get_current_post_author_id(), $use_cache );
79
+ }
 
 
 
 
 
80
 
81
+ /**
82
+ * Returns and caches author meta for the current query.
83
+ * Memoizes the return value for the current request.
84
+ *
85
+ * @since 4.1.4
86
+ *
87
+ * @return array The current author meta.
88
+ */
89
+ public function get_current_post_author_meta() {
90
+ static $cache;
91
+ return isset( $cache ) ? $cache : $cache = $this->get_user_meta( $this->get_current_post_author_id() );
92
  }
93
 
94
  /**
95
+ * Fetches usermeta set by The SEO Framework.
96
  * Memoizes the return value, can be bypassed.
97
  *
98
  * @since 2.7.0
99
  * @since 2.8.0 Always returns array, even if no value is assigned.
100
+ * @since 4.1.4 1. Now returns default values when custom values are missing.
101
+ * 2. Now listens to headlessness.
102
+ * 3. Deprecated the second argument, and moved it to the second.
103
  *
104
+ * @param int $user_id The user ID.
105
+ * @param bool $use_cache Whether to store and use options from cache, or bypass it.
106
+ * @param bool $depr Deprecated.
107
  * @return array The user SEO meta data.
108
  */
109
+ public function get_user_meta( $user_id = 0, $use_cache = true, $depr = true ) {
110
+
111
+ if ( false === $depr ) $use_cache = false;
112
 
113
+ $user_id = $user_id ?: $this->get_user_id();
 
114
 
115
+ if ( $use_cache ) {
116
+ static $cache = [];
117
+
118
+ if ( isset( $cache[ $user_id ] ) )
119
+ return $cache[ $user_id ];
120
+ }
121
 
122
+ /**
123
+ * We can't trust the filter to always contain the expected keys.
124
+ * However, it may contain more keys than we anticipated. Merge them.
125
+ */
126
+ $defaults = array_merge(
127
+ $this->get_unfiltered_user_meta_defaults(),
128
+ $this->get_user_meta_defaults( $user_id )
129
+ );
130
+
131
+ if ( $this->is_headless['user'] ) {
132
+ $meta = [];
133
+
134
+ if ( \in_array( false, $this->is_headless, true ) ) {
135
+ // Some data is still used for the interface elsewhere. Let's retrieve that at least.
136
+ // We filter out the rest because that's 'not supported' or otherwise 'immutable' in headless-mode.
137
+ $_meta = \get_user_meta( $user_id, THE_SEO_FRAMEWORK_USER_OPTIONS, true ) ?: [];
138
+
139
+ foreach ( $this->get_headless_user_meta_support() as $meta_key => $supports ) {
140
+ if ( ! isset( $_meta[ $meta_key ] ) ) continue;
141
+ foreach ( $supports as $support_type ) {
142
+ if ( $this->is_headless[ $support_type ] ) continue;
143
+ $meta[ $meta_key ] = $_meta[ $meta_key ];
144
+ continue 2;
145
+ }
146
+ }
147
+ }
148
+ } else {
149
+ $meta = \get_user_meta( $user_id, THE_SEO_FRAMEWORK_USER_OPTIONS, true ) ?: [];
150
+ }
151
 
152
+ /**
153
+ * @since 4.1.4
154
+ * @note Do not delete/unset/add indexes! It'll cause errors.
155
+ * @param array $meta The current user meta.
156
+ * @param int $user_id The user ID.
157
+ * @param bool $headless Whether the meta are headless.
158
+ */
159
+ $meta = \apply_filters_ref_array(
160
+ 'the_seo_framework_user_meta',
161
+ [
162
+ array_merge( $defaults, $meta ),
163
+ $user_id,
164
+ $this->is_headless['user'],
165
+ ]
166
+ );
167
+
168
+ return $cache[ $user_id ] = $meta;
169
  }
170
 
171
  /**
172
+ * Returns an array of default user meta.
173
  *
174
+ * @since 4.1.4
175
  *
176
+ * @param int $user_id The user ID. Defaults to CURRENT USER, NOT CURRENT POST AUTHOR.
177
+ * @return array The user meta defaults.
 
 
178
  */
179
+ public function get_user_meta_defaults( $user_id = 0 ) {
180
+ /**
181
+ * @since 4.1.4
182
+ * @param array $defaults
183
+ * @param int $user_id
184
+ */
185
+ return (array) \apply_filters_ref_array(
186
+ 'the_seo_framework_user_meta_defaults',
187
+ [
188
+ $this->get_unfiltered_user_meta_defaults(),
189
+ $user_id ?: $this->get_user_id(),
190
+ ]
191
+ );
192
+ }
193
 
194
+ /**
195
+ * Returns the unfiltered user meta defaults.
196
+ *
197
+ * @since 4.1.4
198
+ *
199
+ * @return array The default, unfiltered, post meta.
200
+ */
201
+ protected function get_unfiltered_user_meta_defaults() {
202
+ return [
203
+ 'counter_type' => 3,
204
+ 'facebook_page' => '',
205
+ 'twitter_page' => '',
206
+ ];
207
+ }
208
+
209
+ /**
210
+ * Saves user profile fields.
211
+ *
212
+ * @since 4.1.4
213
+ * @access private
214
+ *
215
+ * @param int $user_id The user ID.
216
+ */
217
+ public function _update_user_meta( $user_id ) {
218
+
219
+ if ( empty( $_POST ) ) return;
220
+
221
+ \check_admin_referer( 'update-user_' . $user_id );
222
+ if ( ! \current_user_can( 'edit_user', $user_id ) ) return;
223
 
224
+ $user = \get_userdata( $user_id );
225
+
226
+ if ( ! $user->has_cap( THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP ) ) return;
227
+
228
+ $data = (array) $_POST['tsf-user-meta'];
229
+
230
+ $this->save_user_meta( $user_id, $data );
231
  }
232
 
233
  /**
234
+ * Updates user SEO option.
235
  *
236
+ * @since 4.1.4
237
  *
238
+ * @param int $user_id The user ID.
239
+ * @param string $option The user's SEO metadata to update.
240
+ * @param mixed $value The option value.
241
  */
242
+ public function update_single_user_meta_item( $user_id, $option, $value ) {
243
+
244
+ $user = \get_userdata( $user_id );
245
+
246
+ // We could test for !$user, but this is more to the point.
247
+ if ( empty( $user->ID ) ) return;
248
+
249
+ $meta = $this->get_user_meta( $user_id, false );
250
+ $meta[ $option ] = $value;
251
+
252
+ $this->save_user_meta( $user_id, $meta );
253
  }
254
 
255
  /**
256
+ * Updates users meta from input.
 
257
  *
258
+ * @since 4.1.4
259
  *
260
+ * @param int $user_id The user ID.
261
+ * @param array $data The data to save.
 
 
 
 
 
 
 
262
  */
263
+ public function save_user_meta( $user_id, array $data ) {
264
 
265
+ $user = \get_userdata( $user_id );
 
266
 
267
+ // We could test for !$user, but this is more to the point.
268
+ if ( empty( $user->ID ) ) return;
269
 
270
+ $data = (array) \wp_parse_args( $data, $this->get_user_meta_defaults( $user->ID ) );
271
+ $data = $this->s_user_meta( $data );
272
 
273
+ /**
274
+ * @since 4.1.4
275
+ * @param array $data The data that's going to be saved.
276
+ * @param int $user_id The user ID.
277
+ */
278
+ $data = (array) \apply_filters_ref_array(
279
+ 'the_seo_framework_save_user_data',
280
+ [
281
+ $data,
282
+ $user->ID,
283
+ ]
284
+ );
285
+
286
+ return \update_user_meta( $user->ID, THE_SEO_FRAMEWORK_USER_OPTIONS, $data );
287
+ }
288
 
289
+ /**
290
+ * Returns the current post author ID.
291
+ * Memoizes the return value for the current request.
292
+ *
293
+ * @since 3.0.0
294
+ * @since 3.2.2 : 1. Now no longer returns the latest post author ID on home-as-blog pages.
295
+ * 2. Now always returns an integer.
296
+ *
297
+ * @return int Post author ID on success, 0 on failure.
298
+ */
299
+ public function get_current_post_author_id() {
300
 
301
+ static $cache;
 
302
 
303
+ if ( isset( $cache ) ) return $cache;
304
 
305
+ if ( $this->is_singular() ) {
306
+ $post = \get_post( $this->get_the_real_ID() );
307
+ $cache = isset( $post->post_author ) ? (int) $post->post_author : 0;
 
308
  }
309
 
310
+ return $cache ?: ( $cache = 0 );
311
  }
312
 
313
  /**
314
+ * Sets up user ID and returns it if user is found.
315
+ * To be used in AJAX, back-end and front-end.
316
  *
317
  * @since 2.7.0
 
318
  *
319
+ * @return int The user ID. 0 if user is not found.
 
 
 
320
  */
321
+ public function get_user_id() {
 
 
 
 
 
 
322
 
323
+ static $user_id = null;
 
324
 
325
+ if ( isset( $user_id ) )
326
+ return $user_id;
327
 
328
+ $user = \wp_get_current_user();
 
 
 
329
 
330
+ return $user_id = $user->exists() ? (int) $user->ID : 0;
331
+ }
332
 
333
+ /**
334
+ * Returns meta keys for user settings that may still be required when headless.
335
+ * Used only when not all of TSF is headless.
336
+ *
337
+ * @since 4.1.4
338
+ *
339
+ * @return array The headless meta keys. : {
340
+ * string $meta_key => string[] $supports
341
+ * }
342
+ */
343
+ protected function get_headless_user_meta_support() {
344
+ return [
345
+ 'counter_type' => [
346
+ 'meta',
347
+ 'settings',
348
+ ],
349
+ ];
350
  }
351
  }
inc/compat/php-mbstring.php DELETED
@@ -1,199 +0,0 @@
1
- <?php
2
- /**
3
- * @package The_SEO_Framework\Compat\PHP\mbstring
4
- * @subpackage The_SEO_Framework\Compatibility
5
- *
6
- * @ignore this file isn't loaded.
7
- */
8
-
9
- // phpcs:disable -- This file isn't loaded.
10
-
11
- \defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
12
-
13
- /**
14
- * Extended charset support
15
- *
16
- * @see _mb_strpos()
17
- *
18
- * @param string $haystack The string to search in.
19
- * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
20
- * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative.
21
- * @param string|null $encoding Optional. Character encoding to use. Default null.
22
- *
23
- * @license GLPv2 or later
24
- * @return int Position of first occurrence found of $haystack of `$needle`.
25
- */
26
- if ( ! function_exists( 'mb_strpos' ) ) :
27
- function mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) {
28
- return _mb_strpos( $haystack, $needle, $offset, $encoding );
29
- }
30
- endif;
31
-
32
- /**
33
- * Compat function to mimic mb_strpos().
34
- *
35
- * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
36
- * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
37
- * The behavior of this function for invalid inputs is PHP compliant.
38
- *
39
- * @since 2.2.0 The SEO Framework
40
- * @license GLPv2 or later
41
- *
42
- * @param string $haystack The string to search in.
43
- * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
44
- * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative.
45
- * @param string|null $encoding Optional. Character encoding to use. Default null.
46
- *
47
- * @license GLPv2 or later
48
- * @return int Position of first occurrence found of $haystack of `$needle`.
49
- */
50
- if ( ! function_exists( '_mb_strpos' ) ) :
51
- function _mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) {
52
-
53
- if ( null === $encoding ) {
54
- $encoding = get_option( 'blog_charset' );
55
- }
56
-
57
- // The solution below works only for UTF-8,
58
- // So in case of a different charset just use built-in strpos()
59
- if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ), true ) ) {
60
- return 0 === $offset ? strpos( $haystack, $needle ) : strpos( $haystack, $needle, $offset );
61
- }
62
-
63
- $haystack_len = mb_strlen( $haystack );
64
-
65
- if ( $offset < (int) 0 || $offset > $haystack_len ) {
66
- trigger_error( 'mb_strpos(): Offset not contained in string', E_USER_WARNING );
67
- return false;
68
- }
69
-
70
- if ( ! is_string( $needle ) ) {
71
- $needle = (int) $needle;
72
-
73
- if ( ! is_int( $needle ) ) {
74
- trigger_error( 'mb_strpos(): Array to string conversion', E_USER_WARNING );
75
- return false;
76
- }
77
- }
78
-
79
- if ( empty( $needle ) ) {
80
- trigger_error( 'mb_strpos(): Empty needle', E_USER_WARNING );
81
- return false;
82
- }
83
-
84
- // Slice off the offset
85
- $haystack_sub = mb_substr( $haystack, $offset );
86
-
87
- if ( _wp_can_use_pcre_u() ) {
88
- // Use the regex unicode support to separate the UTF-8 characters into an array
89
- preg_match_all( '/./us', $haystack, $match_h );
90
- preg_match_all( "/$needle/us", $haystack_sub, $match_n );
91
-
92
- $inter = array_intersect( $match_h[0], $match_n[0] );
93
-
94
- if ( ! isset( $inter ) )
95
- return false;
96
-
97
- // Prevent bugs, (re)assign var.
98
- $pos = null;
99
-
100
- // Find first occurrence greater than or equal to offset
101
- foreach ( $inter as $key => $value ) {
102
- if ( $key >= $offset ) {
103
- $pos = $key;
104
- break;
105
- }
106
- }
107
-
108
- // No key has been found.
109
- if ( ! isset( $pos ) )
110
- return false;
111
-
112
- return (int) $pos;
113
- }
114
-
115
- $regex = '/(
116
- [\x00-\x7F] # single-byte sequences 0xxxxxxx
117
- | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
118
- | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
119
- | [\xE1-\xEC][\x80-\xBF]{2}
120
- | \xED[\x80-\x9F][\x80-\xBF]
121
- | [\xEE-\xEF][\x80-\xBF]{2}
122
- | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
123
- | [\xF1-\xF3][\x80-\xBF]{3}
124
- | \xF4[\x80-\x8F][\x80-\xBF]{2}
125
- )/x';
126
-
127
- /**
128
- * Place haystack into array
129
- */
130
- $match_h = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
131
- do {
132
- // We had some string left over from the last round, but we counted it in that last round.
133
- array_pop( $match_h );
134
-
135
- // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
136
- $pieces = preg_split( $regex, $haystack, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
137
-
138
- $match_h = array_merge( $match_h, $pieces );
139
- } while ( count( $pieces ) > 1 && $haystack = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
140
-
141
- /**
142
- * Place haystack offset into array
143
- */
144
- $match_hs = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
145
- do {
146
- // We had some string left over from the last round, but we counted it in that last round.
147
- array_pop( $match_hs );
148
-
149
- // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
150
- $pieces = preg_split( $regex, $haystack_sub, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
151
-
152
- $match_hs = array_merge( $match_hs, $pieces );
153
- } while ( count( $pieces ) > 1 && $haystack_sub = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
154
-
155
- /**
156
- * Put needle into array
157
- */
158
- $match_n = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
159
- do {
160
- // We had some string left over from the last round, but we counted it in that last round.
161
- array_pop( $match_n );
162
-
163
- // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
164
- $pieces = preg_split( $regex, $needle, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
165
-
166
- $match_n = array_merge( $match_n, $pieces );
167
- } while ( count( $pieces ) > 1 && $needle = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
168
-
169
- /**
170
- * Compute match of haystack offset with needle
171
- * If passed, return the array key number within the full haystack.
172
- */
173
- if ( false !== in_array( $match_n[0], $match_hs, true ) ) {
174
- $inter = array_intersect( $match_h, $match_n );
175
-
176
- if ( ! isset( $inter ) )
177
- return false;
178
-
179
- // Prevent bugs, (re)assign var.
180
- $pos = null;
181
-
182
- // Find first occurrence greater than or equal to offset
183
- foreach ( $inter as $key => $value ) {
184
- if ( $key >= $offset ) {
185
- $pos = $key;
186
- break;
187
- }
188
- }
189
-
190
- // No key has been found.
191
- if ( ! isset( $pos ) )
192
- return false;
193
-
194
- return (int) $pos;
195
- } else {
196
- return false;
197
- }
198
- }
199
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/compat/plugin-bbpress.php CHANGED
@@ -270,7 +270,7 @@ function _bbpress_filter_pre_title( $title = '', $args = null ) {
270
  * This function fixes the Excerpt part.
271
  *
272
  * @since 2.9.0
273
- * @since 3.0.4 : Default value for $max_char_length has been increased from 155 to 300.
274
  * @since 3.1.0 Now no longer fixes the description when `is_tax()` is true.
275
  * @see `_bbpress_filter_pre_title()` for explanation.
276
  * @since 4.0.0 No longer overrules external queries.
270
  * This function fixes the Excerpt part.
271
  *
272
  * @since 2.9.0
273
+ * @since 3.0.4 Default value for $max_char_length has been increased from 155 to 300.
274
  * @since 3.1.0 Now no longer fixes the description when `is_tax()` is true.
275
  * @see `_bbpress_filter_pre_title()` for explanation.
276
  * @since 4.0.0 No longer overrules external queries.
inc/compat/plugin-edd.php CHANGED
@@ -23,8 +23,9 @@ function _set_edd_is_product( $is_product, $post ) {
23
 
24
  if ( ! $is_product ) {
25
  if ( \function_exists( 'edd_get_download' ) ) {
26
- $post_id = $post ? \get_post( $post ) : \the_seo_framework()->get_the_real_ID();
27
- $download = \edd_get_download( $post_id );
 
28
 
29
  $is_product = ! empty( $download->ID );
30
  }
23
 
24
  if ( ! $is_product ) {
25
  if ( \function_exists( 'edd_get_download' ) ) {
26
+ $download = \edd_get_download(
27
+ $post ? \get_post( $post ) : \the_seo_framework()->get_the_real_ID()
28
+ );
29
 
30
  $is_product = ! empty( $download->ID );
31
  }
inc/compat/plugin-polylang.php CHANGED
@@ -14,6 +14,10 @@ namespace The_SEO_Framework;
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
  *
@@ -41,23 +45,6 @@ function _polylang_fix_sitemap_base_bath( $path ) {
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.
@@ -130,7 +117,7 @@ function _polylang_sitemap_append_non_translatables( $args ) {
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
  [
@@ -165,6 +152,7 @@ function _polylang_sitemap_append_non_translatables( $args ) {
165
  * Enables string translation support on titles and descriptions.
166
  *
167
  * @since 3.1.0
 
168
  *
169
  * @param string $string The title or description
170
  * @return string
@@ -185,6 +173,7 @@ function pll__( $string ) {
185
  *
186
  * @since 3.2.4
187
  * @since 4.1.0 Renamed function and parameters to something racially neutral.
 
188
  *
189
  * @param array $allowlist The wildcard file parts that are allowlisted.
190
  * @return array
@@ -203,6 +192,7 @@ function _polylang_allowlist_tsf_urls( $allowlist ) {
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
@@ -220,8 +210,11 @@ function _polylang_blocklist_tsf_urls( $blocklist ) {
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;
14
  * This resolves an issue where the sitemap would otherwise return a 404 error after a
15
  * cookie has been set.
16
  *
17
+ * This function uses the same methods as the main filter.
18
+ * HOWEVER, because of the languid asinine catabolic implementation of the black/block-list in Polylang,
19
+ * this method returns a different value. It's truly astounding how one cannot trust the API.
20
+
21
  * @since 4.1.2
22
  * @access private
23
  *
45
  return $path;
46
  }
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  \add_action( 'the_seo_framework_sitemap_header', __NAMESPACE__ . '\\_polylang_set_sitemap_language' );
49
  /**
50
  * Sets the correct Polylang query language for the sitemap based on the 'lang' GET parameter.
117
  if ( ! isset( $default_lang->slug, $default_lang->term_taxonomy_id ) ) return $args;
118
 
119
  if ( \PLL()->curlang->slug === $default_lang->slug ) {
120
+ $args['lang'] = ''; // Select all lang, so that Polylang doesn't affect the query below with an AND (we need OR).
121
  $args['tax_query'] = [
122
  'relation' => 'OR',
123
  [
152
  * Enables string translation support on titles and descriptions.
153
  *
154
  * @since 3.1.0
155
+ * @access private
156
  *
157
  * @param string $string The title or description
158
  * @return string
173
  *
174
  * @since 3.2.4
175
  * @since 4.1.0 Renamed function and parameters to something racially neutral.
176
+ * @access private
177
  *
178
  * @param array $allowlist The wildcard file parts that are allowlisted.
179
  * @return array
192
  * @since 3.2.4
193
  * @since 4.1.0 Renamed function and parameters to something racially neutral.
194
  * @since 4.1.2 Prefixed function name with _polylang.
195
+ * @access private
196
  *
197
  * @param array $blocklist The wildcard file parts that are blocklisted.
198
  * @return array
210
  *
211
  * @since 3.2.4
212
  * @since 4.1.2 Prefixed function name with _polylang.
213
+ * @access private
214
+ *
215
  * @param string $url The url to fix.
216
  * @param int $id The page or term ID.
217
+ * @return string The fixed home URL.
218
  */
219
  function _polylang_fix_home_url( $url, $id ) {
220
  return \the_seo_framework()->is_front_page_by_ID( $id ) && \get_option( 'permalink_structure' ) ? \trailingslashit( $url ) : $url;
inc/compat/plugin-woocommerce.php CHANGED
@@ -15,6 +15,9 @@ namespace The_SEO_Framework;
15
  * @since 3.1.0
16
  * @since 4.0.3 Added primary term support to products.
17
  * @since 4.1.1 Added primary term support to category widgets.
 
 
 
18
  * @access private
19
  * @uses \is_product()
20
  */
@@ -41,6 +44,11 @@ function _init_wc_compat() {
41
 
42
  // Adjust the widget's tree primary term. Coincidentally(?), it uses the same filter structure; although, it misses the $post object.
43
  \add_filter( 'woocommerce_product_categories_widget_main_term', [ $tsf, '_adjust_post_link_category' ], 10, 2 );
 
 
 
 
 
44
  }
45
 
46
  \add_filter( 'the_seo_framework_real_id', __NAMESPACE__ . '\\_set_real_id_wc_shop' );
@@ -55,9 +63,8 @@ function _init_wc_compat() {
55
  */
56
  function _set_real_id_wc_shop( $id ) {
57
 
58
- if ( \the_seo_framework()->is_wc_shop() ) {
59
  $id = (int) \get_option( 'woocommerce_shop_page_id' );
60
- }
61
 
62
  return $id;
63
  }
@@ -82,15 +89,28 @@ function _set_shop_singular_archive( $is_singular_archive, $id ) {
82
  * Sets the is_shop query.
83
  *
84
  * @since 4.0.5
 
85
  * @access private
86
- * @TODO is this redundant for TSF?
87
  *
88
  * @param bool $is_shop Whether this is a shop page.
89
  * @param int|WP_Post|null $post Post ID or post object.
90
  * @return bool
91
  */
92
  function _set_wc_is_shop( $is_shop, $post ) {
93
- return $is_shop || \the_seo_framework()->is_wc_shop( $post );
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  \add_filter( 'the_seo_framework_is_product', __NAMESPACE__ . '\\_set_wc_is_product', 10, 2 );
@@ -98,13 +118,23 @@ function _set_wc_is_shop( $is_shop, $post ) {
98
  * Sets the is_product query.
99
  *
100
  * @since 4.0.5
 
101
  *
102
  * @param bool $is_product Whether this is a product page.
103
  * @param int|WP_Post|null $post Post ID or post object.
104
  * @return bool
105
  */
106
  function _set_wc_is_product( $is_product, $post ) {
107
- return $is_product || \the_seo_framework()->is_wc_product( $post );
 
 
 
 
 
 
 
 
 
108
  }
109
 
110
  \add_filter( 'the_seo_framework_is_product_admin', __NAMESPACE__ . '\\_set_wc_is_product_admin' );
@@ -112,14 +142,119 @@ function _set_wc_is_product( $is_product, $post ) {
112
  * Sets the is_product_admin query.
113
  *
114
  * @since 4.0.5
 
115
  * @access private
116
- * @TODO is this redundant for TSF?
117
  *
118
  * @param bool $is_product_admin Whether this is a product admin query.
119
  * @return bool
120
  */
121
  function _set_wc_is_product_admin( $is_product_admin ) {
122
- return $is_product_admin || \the_seo_framework()->is_wc_product_admin();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  }
124
 
125
  \add_filter( 'the_seo_framework_image_generation_params', __NAMESPACE__ . '\\_adjust_wc_image_generation_params', 10, 2 );
15
  * @since 3.1.0
16
  * @since 4.0.3 Added primary term support to products.
17
  * @since 4.1.1 Added primary term support to category widgets.
18
+ * @since 4.1.4 1. Now unbinds wc_page_noindex action.
19
+ * 2. Now unbinds wc_page_no_robots filter.
20
+ * 3. Now modifies the SEO Bar.
21
  * @access private
22
  * @uses \is_product()
23
  */
44
 
45
  // Adjust the widget's tree primary term. Coincidentally(?), it uses the same filter structure; although, it misses the $post object.
46
  \add_filter( 'woocommerce_product_categories_widget_main_term', [ $tsf, '_adjust_post_link_category' ], 10, 2 );
47
+
48
+ // WP <5.7
49
+ \remove_action( 'wp_head', 'wc_page_noindex' );
50
+ // WP 5.7+
51
+ \remove_filter( 'wp_robots', 'wc_page_no_robots' );
52
  }
53
 
54
  \add_filter( 'the_seo_framework_real_id', __NAMESPACE__ . '\\_set_real_id_wc_shop' );
63
  */
64
  function _set_real_id_wc_shop( $id ) {
65
 
66
+ if ( \the_seo_framework()->is_wc_shop() )
67
  $id = (int) \get_option( 'woocommerce_shop_page_id' );
 
68
 
69
  return $id;
70
  }
89
  * Sets the is_shop query.
90
  *
91
  * @since 4.0.5
92
+ * @since 4.1.4 Now handles the assertion fully.
93
  * @access private
94
+ * @TODO is this redundant for TSF? -> yes. lol. It's used nowhere, for now...
95
  *
96
  * @param bool $is_shop Whether this is a shop page.
97
  * @param int|WP_Post|null $post Post ID or post object.
98
  * @return bool
99
  */
100
  function _set_wc_is_shop( $is_shop, $post ) {
101
+
102
+ if ( $is_shop ) return $is_shop;
103
+
104
+ if ( isset( $post ) ) {
105
+ $post = \get_post( $post );
106
+ $id = $post ? $post->ID : 0;
107
+
108
+ $is_shop = (int) \get_option( 'woocommerce_shop_page_id' ) === $id;
109
+ } else {
110
+ $is_shop = ! \is_admin() && \function_exists( 'is_shop' ) && \is_shop();
111
+ }
112
+
113
+ return $is_shop;
114
  }
115
 
116
  \add_filter( 'the_seo_framework_is_product', __NAMESPACE__ . '\\_set_wc_is_product', 10, 2 );
118
  * Sets the is_product query.
119
  *
120
  * @since 4.0.5
121
+ * @since 4.1.4 Now handles the assertion fully.
122
  *
123
  * @param bool $is_product Whether this is a product page.
124
  * @param int|WP_Post|null $post Post ID or post object.
125
  * @return bool
126
  */
127
  function _set_wc_is_product( $is_product, $post ) {
128
+
129
+ if ( $is_product ) return $is_product;
130
+
131
+ if ( $post ) {
132
+ $is_product = 'product' === \get_post_type( $post );
133
+ } else {
134
+ $is_product = \function_exists( 'is_product' ) && \is_product();
135
+ }
136
+
137
+ return $is_product;
138
  }
139
 
140
  \add_filter( 'the_seo_framework_is_product_admin', __NAMESPACE__ . '\\_set_wc_is_product_admin' );
142
  * Sets the is_product_admin query.
143
  *
144
  * @since 4.0.5
145
+ * @since 4.1.4 Now handles the assertion fully.
146
  * @access private
147
+ * @TODO is this redundant for TSF? Yup. This very much is because we do not show an interface for OG types.
148
  *
149
  * @param bool $is_product_admin Whether this is a product admin query.
150
  * @return bool
151
  */
152
  function _set_wc_is_product_admin( $is_product_admin ) {
153
+
154
+ if ( $is_product_admin ) return $is_product_admin;
155
+
156
+ $tsf = \the_seo_framework();
157
+
158
+ return $tsf->is_singular_admin() && 'product' === $tsf->get_admin_post_type();
159
+ }
160
+
161
+ \add_filter( 'the_seo_framework_robots_meta_array', __NAMESPACE__ . '\\_set_wc_noindex_defaults', 10, 3 );
162
+ /**
163
+ * Sets 'noindex' default values for WooCommerce's restrictive pages.
164
+ *
165
+ * @since 4.1.4
166
+ * @access private
167
+ *
168
+ * @param array $meta The parsed robots meta. {
169
+ * string 'noindex', ideally be empty or 'noindex'
170
+ * string 'nofollow', ideally be empty or 'nofollow'
171
+ * string 'noarchive', ideally be empty or 'noarchive'
172
+ * string 'max_snippet', ideally be empty or 'max-snippet:<R>=-1>'
173
+ * string 'max_image_preview', ideally be empty or 'max-image-preview:<none|standard|large>'
174
+ * string 'max_video_preview', ideally be empty or 'max-video-preview:<R>=-1>'
175
+ * }
176
+ * @param array|null $args The query arguments. Contains 'id' and 'taxonomy'.
177
+ * Is null when query is autodetermined.
178
+ * @param int <bit> $ignore The ignore level. {
179
+ * 0 = 0b00: Ignore nothing.
180
+ * 1 = 0b01: Ignore protection. (\The_SEO_Framework\ROBOTS_IGNORE_PROTECTION)
181
+ * 2 = 0b10: Ignore post/term setting. (\The_SEO_Framework\ROBOTS_IGNORE_SETTINGS)
182
+ * 3 = 0b11: Ignore protection and post/term setting.
183
+ * }
184
+ * @return array
185
+ */
186
+ function _set_wc_noindex_defaults( $meta, $args, $ignore ) {
187
+
188
+ // Nothing to do here...
189
+ if ( 'noindex' === $meta['noindex'] ) return $meta;
190
+
191
+ $tsf = \the_seo_framework();
192
+
193
+ if ( null === $args ) {
194
+ if ( \is_singular() )
195
+ $page_id = $tsf->get_the_real_ID();
196
+ } else {
197
+ if ( '' === $args['taxonomy'] )
198
+ $page_id = $args['id'];
199
+ }
200
+
201
+ // No page_id was found: unsupported query.
202
+ if ( empty( $page_id ) ) return $meta;
203
+
204
+ static $page_ids;
205
+
206
+ if ( ! isset( $page_ids ) ) {
207
+ if ( ! \function_exists( '\\wc_get_page_id' ) ) return $meta;
208
+
209
+ $page_ids = array_filter( [ \wc_get_page_id( 'cart' ), \wc_get_page_id( 'checkout' ), \wc_get_page_id( 'myaccount' ) ] );
210
+ }
211
+
212
+ // This current page isn't a WC cart/checkout/myaccount page.
213
+ if ( ! \in_array( $page_id, $page_ids, true ) ) return $meta;
214
+
215
+ // Set the default.
216
+ if ( $ignore & \The_SEO_Framework\ROBOTS_IGNORE_SETTINGS ) {
217
+ $meta['noindex'] = 'noindex';
218
+ } elseif ( 0 === $tsf->s_qubit( $tsf->get_post_meta_item( '_genesis_noindex', $page_id ) ) ) {
219
+ $meta['noindex'] = 'noindex';
220
+ }
221
+
222
+ return $meta;
223
+ }
224
+
225
+ \add_action( 'the_seo_framework_seo_bar', __NAMESPACE__ . '\\_assert_wc_noindex_defaults_seo_bar' );
226
+ /**
227
+ * Appends noindex default checks to the noindex item of the SEO Bar for pages.
228
+ *
229
+ * @since 4.1.4
230
+ * @access private
231
+ *
232
+ * @param string $interpreter The interpreter class name.
233
+ */
234
+ function _assert_wc_noindex_defaults_seo_bar( $interpreter ) {
235
+
236
+ if ( $interpreter::$query['taxonomy'] ) return;
237
+
238
+ static $page_ids;
239
+
240
+ if ( ! isset( $page_ids ) )
241
+ $page_ids = array_filter( [ \wc_get_page_id( 'cart' ), \wc_get_page_id( 'checkout' ), \wc_get_page_id( 'myaccount' ) ] );
242
+
243
+ if ( ! \in_array( $interpreter::$query['id'], $page_ids, true ) ) return;
244
+
245
+ $items = $interpreter::collect_seo_bar_items();
246
+
247
+ // Don't do anything if there's a blocking redirect.
248
+ if ( ! empty( $items['redirect']['meta']['blocking'] ) ) return;
249
+
250
+ $index_item = &$interpreter::edit_seo_bar_item( 'indexing' );
251
+ $index_item['status'] =
252
+ 0 !== \the_seo_framework()->s_qubit(
253
+ \The_SEO_Framework\Builders\SeoBar_Page::get_instance()->get_query_cache()['meta']['_genesis_noindex']
254
+ )
255
+ ? $interpreter::STATE_OKAY
256
+ : $interpreter::STATE_UNKNOWN;
257
+ $index_item['assess']['recommends'] = \__( 'WooCommerce recommends not indexing this dynamic page.', 'autodescription' );
258
  }
259
 
260
  \add_filter( 'the_seo_framework_image_generation_params', __NAMESPACE__ . '\\_adjust_wc_image_generation_params', 10, 2 );
inc/compat/plugin-wpforo.php CHANGED
@@ -70,7 +70,7 @@ function _wpforo_disable_html_output() {
70
  * @param \WP_Post $post Post object.
71
  * @return string
72
  */
73
- function _wpforo_filter_canonical_url( $canonical_url, $post ) {
74
  return \function_exists( '\\wpforo_get_request_uri' ) ? \wpforo_get_request_uri() : $canonical_url;
75
  }
76
 
70
  * @param \WP_Post $post Post object.
71
  * @return string
72
  */
73
+ function _wpforo_filter_canonical_url( $canonical_url, $post ) { // phpcs:ignore, VariableAnalysis.CodeAnalysis.VariableAnalysis
74
  return \function_exists( '\\wpforo_get_request_uri' ) ? \wpforo_get_request_uri() : $canonical_url;
75
  }
76
 
inc/compat/plugin-wpml.php CHANGED
@@ -89,3 +89,45 @@ function _wpml_flush_sitemap( $type, $id, $args, $success ) {
89
  $cleared = true;
90
  }
91
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  $cleared = true;
90
  }
91
  }
92
+
93
+ \add_action( 'the_seo_framework_sitemap_header', __NAMESPACE__ . '\\_wpml_sitemap_filter_display_translatables' );
94
+ /**
95
+ * Filters "display translatable" post types from the sitemap query arguments.
96
+ * Only appends actually translated posts to the translated sitemap.
97
+ *
98
+ * @since 4.1.4
99
+ * @access private
100
+ */
101
+ function _wpml_sitemap_filter_display_translatables() {
102
+ // ez.
103
+ \add_filter( 'wpml_should_use_display_as_translated_snippet', '__return_false' );
104
+ }
105
+
106
+ \add_action( 'the_seo_framework_sitemap_hpt_query_args', __NAMESPACE__ . '\\_wpml_sitemap_filter_non_translatables' );
107
+ \add_action( 'the_seo_framework_sitemap_nhpt_query_args', __NAMESPACE__ . '\\_wpml_sitemap_filter_non_translatables' );
108
+ /**
109
+ * Filters nontranslatable post types from the sitemap query arguments.
110
+ * Only appends when the default sitemap language is not displayed.
111
+ *
112
+ * @since 4.1.4
113
+ * @access private
114
+ * @global $sitepress \SitePress
115
+ *
116
+ * @param array $args The query arguments.
117
+ * @return array The augmented query arguments.
118
+ */
119
+ function _wpml_sitemap_filter_non_translatables( $args ) {
120
+ global $sitepress;
121
+
122
+ if ( empty( $sitepress )
123
+ || ! method_exists( $sitepress, 'get_default_language' )
124
+ || ! method_exists( $sitepress, 'get_current_language' )
125
+ || ! method_exists( $sitepress, 'is_translated_post_type' ) ) return $args;
126
+
127
+ if ( $sitepress->get_default_language() === $sitepress->get_current_language() ) return $args;
128
+
129
+ // Filter out only 'Not translatable'.
130
+ $args['post_type'] = array_filter( (array) $args['post_type'], [ $sitepress, 'is_translated_post_type' ] );
131
+
132
+ return $args;
133
+ }
inc/functions/api.php CHANGED
@@ -9,7 +9,7 @@ namespace {
9
 
10
  /**
11
  * The SEO Framework plugin
12
- * Copyright (C) 2018 - 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
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/functions/deprecated.php CHANGED
@@ -6,7 +6,7 @@
6
 
7
  /**
8
  * The SEO Framework plugin
9
- * Copyright (C) 2015 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
10
  *
11
  * This program is free software: you can redistribute it and/or modify
12
  * it under the terms of the GNU General Public License version 3 as published
@@ -33,200 +33,5 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) or die;
33
  * @since 2.3.5 Emptied. (~2.5 months later)
34
  * @since 2.6.2 Emptied. (~6 months later)
35
  * @since 2.9.2 Emptied. (~8 months later)
36
- * @TODO 5.0.0 empty this.
37
  */
38
-
39
- /**
40
- * Checks if The SEO FrameWork is active based on filter.
41
- *
42
- * @since 2.2.5
43
- * @since 3.1.0 Deprecated
44
- * @deprecated
45
- *
46
- * @return bool true if SEO framework is active
47
- */
48
- function the_seo_framework_active() {
49
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->loaded' );
50
- return the_seo_framework()->loaded;
51
- }
52
-
53
- /**
54
- * Returns The SEO FrameWork version number.
55
- * Useful for version comparing.
56
- *
57
- * @since 2.2.5
58
- * @since 3.1.0 Deprecated
59
- * @deprecated
60
- *
61
- * @return string|null The SEO Framework three point version number. (e.g. '2.2.5')
62
- */
63
- function the_seo_framework_version() {
64
-
65
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'THE_SEO_FRAMEWORK_VERSION' );
66
-
67
- if ( the_seo_framework()->loaded )
68
- return THE_SEO_FRAMEWORK_VERSION;
69
-
70
- return null;
71
- }
72
-
73
- /**
74
- * Compares The SEO Framework dot versions.
75
- *
76
- * @since 2.4.0
77
- * @since 3.1.0 Deprecated
78
- * @deprecated
79
- *
80
- * @param string $version The two dot version: x.v
81
- * @return bool False plugin inactive or version compare yields negative results.
82
- */
83
- function the_seo_framework_dot_version( $version = '2.4' ) {
84
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0' );
85
-
86
- $current_version = the_seo_framework_version();
87
-
88
- if ( $current_version ) {
89
- $version_len = strlen( $version );
90
- $current_version_len = strlen( $current_version );
91
-
92
- // Only allow 3 length.
93
- if ( 3 !== $version_len )
94
- $version = substr( $version, 0, 3 );
95
-
96
- if ( 3 !== $current_version_len )
97
- $current_version = substr( $current_version, 0, 3 );
98
-
99
- if ( $current_version === $version )
100
- return true;
101
- }
102
-
103
- return false;
104
- }
105
-
106
- /**
107
- * Fetch the The SEO Framework Options pagehook.
108
- *
109
- * @since 2.7.0
110
- * @since 3.1.0 Deprecated
111
- * @deprecated
112
- *
113
- * @return string|null The pagehook.
114
- */
115
- function the_seo_framework_options_pagehook() {
116
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->pagehook' );
117
- return the_seo_framework()->pagehook;
118
- }
119
-
120
- /**
121
- * Fetch an option from The SEO Framework.
122
- *
123
- * @since 2.7.0
124
- * @since 2.8.0 Now works as intended, by including the required parameters.
125
- * @since 3.1.0 Deprecated
126
- * @deprecated
127
- *
128
- * @param string $key Option name.
129
- * @param boolean $use_cache Optional. Whether to use the cache value or not. Defaults to true.
130
- * @return mixed The option value.
131
- */
132
- function the_seo_framework_get_option( $key, $use_cache = true ) {
133
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->get_option( \'option_key\' )' );
134
- return the_seo_framework()->get_option( $key, $use_cache );
135
- }
136
-
137
- /**
138
- * Fetch title from cache. Only works within Loop.
139
- *
140
- * @since 2.4.2
141
- * @since 3.1.0 Deprecated
142
- * @deprecated
143
- *
144
- * @param string|null $title the previous title
145
- * @return string|null The current page title.
146
- */
147
- function the_seo_framework_title_from_cache( $title = null ) { // phpcs:ignore,VariableAnalysis -- deprecated
148
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->get_title(...)' );
149
- return the_seo_framework()->get_title();
150
- }
151
-
152
- /**
153
- * Fetch description from cache. Only works within Loop.
154
- *
155
- * @since 2.4.2
156
- * @deprecated
157
- * @since 3.0.6 Silently deprecated.
158
- * @since 3.1.0 Deprecated
159
- * @deprecated
160
- *
161
- * @param bool $deprecated Deprecated.
162
- * @return string|null The current page description.
163
- */
164
- function the_seo_framework_description_from_cache( $deprecated = false ) { // phpcs:ignore,VariableAnalysis -- deprecated
165
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->get_description()' );
166
- return the_seo_framework()->get_description();
167
- }
168
-
169
- /**
170
- * Fetch url from cache. Only works within Loop.
171
- *
172
- * @since 2.4.2
173
- * @since 3.1.0 Deprecated
174
- * @deprecated
175
- *
176
- * @return string|null The current page URL.
177
- */
178
- function the_seo_framework_the_url_from_cache() {
179
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->get_current_canonical_url()' );
180
- return the_seo_framework()->get_current_canonical_url();
181
- }
182
-
183
- /**
184
- * Whether we're on the SEO settings page.
185
- *
186
- * @since 2.6.0
187
- * @since 2.7.0 No longer checks for $_GET requests. Only uses global $pagehook.
188
- * @since 3.1.0 Deprecated
189
- * @deprecated
190
- *
191
- * @return bool
192
- */
193
- function the_seo_framework_is_settings_page() {
194
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->is_seo_settings_page()' );
195
- return the_seo_framework()->is_seo_settings_page( true );
196
- }
197
-
198
- /**
199
- * Updates The SEO Framework site options.
200
- *
201
- * @since 2.7.0
202
- * @since 3.1.0 Deprecated
203
- * @deprecated
204
- *
205
- * @param string|array $new_option {
206
- * if string: The string will act as a key for a new empty string option, e.g. : {
207
- * 'sitemap_index' becomes ['sitemap_index' => '']
208
- * }
209
- * if array: The option name(s) and value(s), e.g. : {
210
- * ['sitemap_index' => 1]
211
- * }
212
- * }
213
- * @return bool True on success. False on failure.
214
- */
215
- function the_seo_framework_update_option( $new_option ) {
216
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->update_settings()' );
217
- return the_seo_framework()->update_settings( $new_option );
218
- }
219
-
220
- /**
221
- * Returns the parent slug name of The SEO Framework plugin.
222
- *
223
- * @since 2.7.0
224
- * @since 3.1.0 Deprecated
225
- * @deprecated
226
- *
227
- * @return bool|string False on failure, the slug on success.
228
- */
229
- function the_seo_framework_options_page_slug() {
230
- the_seo_framework()->_deprecated_function( __FUNCTION__, '3.1.0', 'the_seo_framework()->seo_settings_page_slug' );
231
- return the_seo_framework()->seo_settings_page_slug;
232
- }
6
 
7
  /**
8
  * The SEO Framework plugin
9
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
10
  *
11
  * This program is free software: you can redistribute it and/or modify
12
  * it under the terms of the GNU General Public License version 3 as published
33
  * @since 2.3.5 Emptied. (~2.5 months later)
34
  * @since 2.6.2 Emptied. (~6 months later)
35
  * @since 2.9.2 Emptied. (~8 months later)
36
+ * @since 4.1.4 Emptied. (~22 months after the 3.1.0 deprecations)
37
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions/upgrade-suggestion.php CHANGED
@@ -8,7 +8,7 @@ namespace The_SEO_Framework\Suggestion;
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2018 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
inc/views/admin/metaboxes/description-metabox.php CHANGED
@@ -7,6 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
  // Fetch the required instance within this file.
@@ -14,38 +17,34 @@ $instance = $this->get_view_instance( 'the_seo_framework_description_metabox', $
14
 
15
  switch ( $instance ) :
16
  case 'the_seo_framework_description_metabox_main':
17
- ?>
18
- <h4><?php esc_html_e( 'Description Settings', 'autodescription' ); ?></h4>
19
- <?php
20
- $this->description(
21
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' )
22
  );
23
 
24
  ?>
25
  <hr>
26
-
27
- <h4><?php esc_html_e( 'Automated Description Settings', 'autodescription' ); ?></h4>
28
  <?php
29
- $this->description(
 
30
  __( 'A description can be automatically generated for every page.', 'autodescription' )
31
  );
32
- $this->description(
33
  __( 'Open Graph and Twitter Cards require descriptions. Therefore, it is best to leave this option enabled.', 'autodescription' )
34
  );
35
 
36
- $info = $this->make_info(
37
  __( 'Learn how this feature works.', 'autodescription' ),
38
  'https://kb.theseoframework.com/?p=65',
39
  false
40
  );
41
 
42
- $this->wrap_fields(
43
- $this->make_checkbox(
44
- 'auto_description',
45
- esc_html__( 'Automatically generate descriptions?', 'autodescription' ) . ' ' . $info,
46
- '',
47
- false
48
- ),
49
  true
50
  );
51
  break;
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Interpreters\HTML,
11
+ The_SEO_Framework\Interpreters\Form;
12
+
13
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
14
 
15
  // Fetch the required instance within this file.
17
 
18
  switch ( $instance ) :
19
  case 'the_seo_framework_description_metabox_main':
20
+ Form::header_title( __( 'Description Settings', 'autodescription' ) );
21
+ HTML::description(
 
 
22
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' )
23
  );
24
 
25
  ?>
26
  <hr>
 
 
27
  <?php
28
+ Form::header_title( __( 'Automated Description Settings', 'autodescription' ) );
29
+ HTML::description(
30
  __( 'A description can be automatically generated for every page.', 'autodescription' )
31
  );
32
+ HTML::description(
33
  __( 'Open Graph and Twitter Cards require descriptions. Therefore, it is best to leave this option enabled.', 'autodescription' )
34
  );
35
 
36
+ $info = HTML::make_info(
37
  __( 'Learn how this feature works.', 'autodescription' ),
38
  'https://kb.theseoframework.com/?p=65',
39
  false
40
  );
41
 
42
+ HTML::wrap_fields(
43
+ Form::make_checkbox( [
44
+ 'id' => 'auto_description',
45
+ 'label' => esc_html__( 'Automatically generate descriptions?', 'autodescription' ) . ' ' . $info,
46
+ 'escape' => false,
47
+ ] ),
 
48
  true
49
  );
50
  break;
inc/views/admin/metaboxes/feed-metabox.php CHANGED
@@ -7,6 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
  // Fetch the required instance within this file.
@@ -14,37 +17,46 @@ $instance = $this->get_view_instance( 'the_seo_framework_feed_metabox', $instanc
14
 
15
  switch ( $instance ) :
16
  case 'the_seo_framework_feed_metabox_main':
17
- ?>
18
- <h4><?php esc_html_e( 'Content Feed Settings', 'autodescription' ); ?></h4>
19
- <?php
20
- $this->description( __( "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt.", 'autodescription' ) );
21
- $this->description( __( 'Adding a backlink below the feed entries will also let the visitors know where the content came from.', 'autodescription' ) );
22
 
23
  ?>
24
  <hr>
25
-
26
- <h4><?php esc_html_e( 'Change Feed Settings', 'autodescription' ); ?></h4>
27
  <?php
 
28
  $excerpt_the_feed_label = esc_html__( 'Convert feed entries into excerpts?', 'autodescription' );
29
- $excerpt_the_feed_label .= ' ' . $this->make_info( __( 'By default the excerpt will be at most 400 characters long.', 'autodescription' ), '', false );
30
 
31
  $source_the_feed_label = esc_html__( 'Add link to source below the feed entry content?', 'autodescription' );
32
- $source_the_feed_label .= ' ' . $this->make_info( __( 'This link will not be followed by search engines.', 'autodescription' ), '', false );
33
 
34
  $index_the_feed_label = esc_html__( 'Allow indexing of feeds?', 'autodescription' );
35
- $index_the_feed_label .= ' ' . $this->make_info( __( 'If this site publishes podcasts, enable this option. Otherwise, leave it disabled. Indexing feeds can cause search engines to crawl and index new pages slower; however, some podcast services require feeds to be indexable.', 'autodescription' ), '', false );
36
 
37
- $this->wrap_fields(
38
  [
39
- $this->make_checkbox( 'excerpt_the_feed', $excerpt_the_feed_label, '', false ),
40
- $this->make_checkbox( 'source_the_feed', $source_the_feed_label, '', false ),
41
- $this->make_checkbox( 'index_the_feed', $index_the_feed_label, '', false ),
 
 
 
 
 
 
 
 
 
 
 
 
42
  ],
43
  true
44
  );
45
 
46
  if ( get_option( 'rss_use_excerpt' ) ) {
47
- $this->description_noesc(
48
  $this->convert_markdown(
49
  sprintf(
50
  /* translators: %s = Reading Settings URL. Links are in Markdown! */
@@ -57,7 +69,7 @@ switch ( $instance ) :
57
  );
58
  }
59
 
60
- $this->description_noesc(
61
  sprintf(
62
  '<a href="%s" target=_blank rel=noopener>%s</a>',
63
  esc_url( get_feed_link(), [ 'https', 'http' ] ),
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Interpreters\HTML,
11
+ The_SEO_Framework\Interpreters\Form;
12
+
13
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
14
 
15
  // Fetch the required instance within this file.
17
 
18
  switch ( $instance ) :
19
  case 'the_seo_framework_feed_metabox_main':
20
+ Form::header_title( __( 'Content Feed Settings', 'autodescription' ) );
21
+ HTML::description( __( "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt.", 'autodescription' ) );
22
+ HTML::description( __( 'Adding a backlink below the feed entries will also let the visitors know where the content came from.', 'autodescription' ) );
 
 
23
 
24
  ?>
25
  <hr>
 
 
26
  <?php
27
+ Form::header_title( __( 'Change Feed Settings', 'autodescription' ) );
28
  $excerpt_the_feed_label = esc_html__( 'Convert feed entries into excerpts?', 'autodescription' );
29
+ $excerpt_the_feed_label .= ' ' . HTML::make_info( __( 'By default the excerpt will be at most 400 characters long.', 'autodescription' ), '', false );
30
 
31
  $source_the_feed_label = esc_html__( 'Add link to source below the feed entry content?', 'autodescription' );
32
+ $source_the_feed_label .= ' ' . HTML::make_info( __( 'This link will not be followed by search engines.', 'autodescription' ), '', false );
33
 
34
  $index_the_feed_label = esc_html__( 'Allow indexing of feeds?', 'autodescription' );
35
+ $index_the_feed_label .= ' ' . HTML::make_info( __( 'If this site publishes podcasts, enable this option. Otherwise, leave it disabled. Indexing feeds can cause search engines to crawl and index new pages slower; however, some podcast services require feeds to be indexable.', 'autodescription' ), '', false );
36
 
37
+ HTML::wrap_fields(
38
  [
39
+ Form::make_checkbox( [
40
+ 'id' => 'excerpt_the_feed',
41
+ 'label' => $excerpt_the_feed_label,
42
+ 'escape' => false,
43
+ ] ),
44
+ Form::make_checkbox( [
45
+ 'id' => 'source_the_feed',
46
+ 'label' => $source_the_feed_label,
47
+ 'escape' => false,
48
+ ] ),
49
+ Form::make_checkbox( [
50
+ 'id' => 'index_the_feed',
51
+ 'label' => $index_the_feed_label,
52
+ 'escape' => false,
53
+ ] ),
54
  ],
55
  true
56
  );
57
 
58
  if ( get_option( 'rss_use_excerpt' ) ) {
59
+ HTML::description_noesc(
60
  $this->convert_markdown(
61
  sprintf(
62
  /* translators: %s = Reading Settings URL. Links are in Markdown! */
69
  );
70
  }
71
 
72
+ HTML::description_noesc(
73
  sprintf(
74
  '<a href="%s" target=_blank rel=noopener>%s</a>',
75
  esc_url( get_feed_link(), [ 'https', 'http' ] ),
inc/views/admin/metaboxes/general-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -57,99 +59,87 @@ switch ( $instance ) :
57
  break;
58
 
59
  case 'the_seo_framework_general_metabox_layout':
60
- ?>
61
- <h4><?php esc_html_e( 'Administrative Layout Settings', 'autodescription' ); ?></h4>
62
- <?php
63
- $this->description( __( 'SEO hints can be visually displayed throughout the dashboard.', 'autodescription' ) );
64
 
65
  ?>
66
  <hr>
67
-
68
- <h4><?php esc_html_e( 'SEO Bar Settings', 'autodescription' ); ?></h4>
69
  <?php
70
- $this->wrap_fields(
 
71
  [
72
- $this->make_checkbox(
73
- 'display_seo_bar_tables',
74
- esc_html__( 'Display the SEO Bar in overview tables?', 'autodescription' ),
75
- '',
76
- false
77
- ),
78
- $this->make_checkbox(
79
- 'display_seo_bar_metabox',
80
- esc_html__( 'Display the SEO Bar in the SEO Settings metabox?', 'autodescription' ),
81
- '',
82
- false
83
- ),
84
- $this->make_checkbox(
85
- 'seo_bar_symbols',
86
- esc_html__( 'Use symbols for warnings?', 'autodescription' ) . ' ' . $this->make_info(
87
  __( 'If you have difficulty discerning colors, this may help you spot issues more easily.', 'autodescription' ),
88
  '',
89
  false
90
  ),
91
- '',
92
- false
93
- ),
94
  ],
95
  true
96
  );
97
 
98
  ?>
99
  <hr>
100
-
101
- <h4><?php esc_html_e( 'Counter Settings', 'autodescription' ); ?></h4>
102
  <?php
 
103
 
104
- $pixel_info = $this->make_info(
105
  __( 'The pixel counter computes whether the input will fit on search engine result pages.', 'autodescription' ),
106
  'https://kb.theseoframework.com/?p=48',
107
  false
108
  );
109
 
110
- $character_info = $this->make_info(
111
  __( 'The character counter is based on guidelines.', 'autodescription' ),
112
  '',
113
  false
114
  );
115
 
116
- $this->wrap_fields(
117
  [
118
- $this->make_checkbox(
119
- 'display_pixel_counter',
120
- esc_html__( 'Display pixel counters?', 'autodescription' ) . ' ' . $pixel_info,
121
- '',
122
- false
123
- ),
124
- $this->make_checkbox(
125
- 'display_character_counter',
126
- esc_html__( 'Display character counters?', 'autodescription' ) . ' ' . $character_info,
127
- '',
128
- false
129
- ),
130
  ],
131
  true
132
  );
133
  break;
134
 
135
  case 'the_seo_framework_general_metabox_performance':
136
- ?>
137
- <h4><?php esc_html_e( 'Performance Settings', 'autodescription' ); ?></h4>
138
- <?php
139
- $this->description( __( "Depending on your server's configuration, adjusting these settings can affect performance.", 'autodescription' ) );
140
 
141
  ?>
142
  <hr>
143
-
144
- <h4><?php esc_html_e( 'Query Alteration Settings', 'autodescription' ); ?></h4>
145
  <?php
146
- $this->description_noesc(
 
147
  esc_html__( "Altering the query allows for more control of the site's hierarchy.", 'autodescription' )
148
  . '<br>' .
149
  esc_html__( 'If your website has thousands of pages, these options can greatly affect database performance.', 'autodescription' )
150
  );
151
 
152
- $this->description_noesc(
153
  esc_html__( 'Altering the query in the database is more accurate, but can increase database query time.', 'autodescription' )
154
  . '<br>' .
155
  esc_html__( 'Altering the query on the site is much faster, but can lead to inconsistent pagination. It can also lead to 404 error messages if all queried pages have been excluded.', 'autodescription' )
@@ -195,9 +185,9 @@ switch ( $instance ) :
195
  '<label for="%1$s">%2$s</label>
196
  <select name="%3$s" id="%1$s">%4$s</select>',
197
  [
198
- $this->get_field_id( 'alter_search_query_type' ),
199
  $perform_alteration_i18n,
200
- $this->get_field_name( 'alter_search_query_type' ),
201
  $search_query_select_options,
202
  ]
203
  );
@@ -206,92 +196,67 @@ switch ( $instance ) :
206
  '<label for="%1$s">%2$s</label>
207
  <select name="%3$s" id="%1$s">%4$s</select>',
208
  [
209
- $this->get_field_id( 'alter_archive_query_type' ),
210
  $perform_alteration_i18n,
211
- $this->get_field_name( 'alter_archive_query_type' ),
212
  $archive_query_select_options,
213
  ]
214
  );
215
 
216
- $this->wrap_fields(
217
  [
218
- $this->make_checkbox(
219
- 'alter_search_query',
220
- esc_html__( 'Enable search query alteration?', 'autodescription' )
221
- . ' ' . $this->make_info( __( 'This allows you to exclude pages from on-site search results.', 'autodescription' ), '', false ),
222
- '',
223
- false
224
- ),
225
  $search_query_select_field,
226
  ],
227
  true
228
  );
229
 
230
- $this->wrap_fields(
231
  [
232
- $this->make_checkbox(
233
- 'alter_archive_query',
234
- esc_html__( 'Enable archive query alteration?', 'autodescription' )
235
- . ' ' . $this->make_info( __( 'This allows you to exclude pages from on-site archive listings.', 'autodescription' ), '', false ),
236
- '',
237
- false
238
- ),
239
  $archive_query_select_field,
240
  ],
241
  true
242
  );
243
  ?>
244
  <hr>
245
-
246
- <h4><?php esc_html_e( 'Transient Cache Settings', 'autodescription' ); ?></h4>
247
  <?php
248
- $this->description( __( 'To improve performance, generated output can be stored in the database as transient cache.', 'autodescription' ) );
249
-
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
257
- ),
258
  true
259
  );
260
-
261
- if ( wp_using_ext_object_cache() ) :
262
- ?>
263
- <hr>
264
-
265
- <h4><?php esc_html_e( 'Object Cache Settings', 'autodescription' ); ?></h4>
266
- <?php
267
-
268
- $this->wrap_fields(
269
- $this->make_checkbox(
270
- 'cache_object',
271
- esc_html__( 'Enable object cache?', 'autodescription' ),
272
- esc_html__( 'An object cache handler has been detected.', 'autodescription' ),
273
- false
274
- ),
275
- true
276
- );
277
- endif;
278
  break;
279
 
280
  case 'the_seo_framework_general_metabox_canonical':
281
- ?>
282
- <h4><?php esc_html_e( 'Canonical URL Settings', 'autodescription' ); ?></h4>
283
- <?php
284
- $this->description( __( 'The canonical URL meta tag urges search engines to go to the outputted URL.', 'autodescription' ) );
285
- $this->description( __( 'If the canonical URL meta tag represents the visited page, then the search engine will crawl the visited page. Otherwise, the search engine may go to the outputted URL.', 'autodescription' ) );
286
  ?>
287
  <hr>
288
-
289
- <h4><?php esc_html_e( 'Scheme Settings', 'autodescription' ); ?></h4>
290
  <?php
291
- $this->description( __( 'If your website is accessible via both HTTP as HTTPS, you may want to set this to HTTPS if not detected automatically. Secure connections are preferred by search engines.', 'autodescription' ) );
 
292
  ?>
293
- <label for="<?php $this->field_id( 'canonical_scheme' ); ?>"><?php echo esc_html_x( 'Preferred canonical URL scheme:', '= Detect Automatically, HTTPS, HTTP', 'autodescription' ); ?></label>
294
- <select name="<?php $this->field_name( 'canonical_scheme' ); ?>" id="<?php $this->field_id( 'canonical_scheme' ); ?>">
295
  <?php
296
  $scheme_types = (array) apply_filters(
297
  'the_seo_framework_canonical_scheme_types',
@@ -311,93 +276,75 @@ switch ( $instance ) :
311
  </select>
312
 
313
  <hr>
314
-
315
- <h4><?php esc_html_e( 'Link Relationship Settings', 'autodescription' ); ?></h4>
316
  <?php
317
- $this->description( __( 'Some search engines look for relations between the content of your pages. If you have pagination on a post or page, or have archives indexed, these options will help search engines look for the right page to display in the search results.', 'autodescription' ) );
318
- $this->description( __( "It's recommended to turn these options on for better SEO consistency and to prevent duplicated content issues.", 'autodescription' ) );
 
319
 
320
- $prev_next_posts_checkbox = $this->make_checkbox(
321
- 'prev_next_posts',
322
- $this->convert_markdown(
323
  /* translators: the backticks are Markdown! Preserve them as-is! */
324
  esc_html__( 'Add `rel` link tags to posts and pages?', 'autodescription' ),
325
  [ 'code' ]
326
  ),
327
- '',
328
- false
329
- );
330
 
331
- $prev_next_archives_checkbox = $this->make_checkbox(
332
- 'prev_next_archives',
333
- $this->convert_markdown(
334
  /* translators: the backticks are Markdown! Preserve them as-is! */
335
  esc_html__( 'Add `rel` link tags to archives?', 'autodescription' ),
336
  [ 'code' ]
337
  ),
338
- '',
339
- false
340
- );
341
 
342
- $prev_next_frontpage_checkbox = $this->make_checkbox(
343
- 'prev_next_frontpage',
344
- $this->convert_markdown(
345
  /* translators: the backticks are Markdown! Preserve them as-is! */
346
  esc_html__( 'Add `rel` link tags to the homepage?', 'autodescription' ),
347
  [ 'code' ]
348
  ),
349
- '',
350
- false
351
- );
352
 
353
- $this->wrap_fields( $prev_next_posts_checkbox . $prev_next_archives_checkbox . $prev_next_frontpage_checkbox, true );
354
  break;
355
 
356
-
357
  case 'the_seo_framework_general_metabox_timestamps':
358
- $timestamp_0 = gmdate( 'Y-m-d' );
 
359
 
360
- /**
361
- * @link https://www.w3.org/TR/NOTE-datetime
362
- * We use the second expression of the time zone offset handling.
363
- */
364
- $timestamp_1 = gmdate( 'Y-m-d\TH:iP' );
365
-
366
- ?>
367
- <h4><?php esc_html_e( 'Timestamp Settings', 'autodescription' ); ?></h4>
368
- <?php
369
- $this->description( __( 'Timestamps help indicate when a page has been published and modified.', 'autodescription' ) );
370
  ?>
371
  <hr>
372
 
373
  <fieldset>
374
- <legend>
375
- <h4><?php esc_html_e( 'Timestamp Format Settings', 'autodescription' ); ?></h4>
376
- </legend>
377
- <?php $this->description( __( 'This setting determines how specific the timestamp is.', 'autodescription' ) ); ?>
378
 
379
  <p id="sitemaps-timestamp-format" class="tsf-fields">
380
  <span class="tsf-toblock">
381
- <input type="radio" name="<?php $this->field_name( 'timestamps_format' ); ?>" id="<?php $this->field_id( 'timestamps_format_0' ); ?>" value="0" <?php checked( $this->get_option( 'timestamps_format' ), '0' ); ?> />
382
- <label for="<?php $this->field_id( 'timestamps_format_0' ); ?>">
383
  <?php
384
  // phpcs:ignore, WordPress.Security.EscapeOutput -- code_wrap escapes.
385
- echo $this->code_wrap( $timestamp_0 );
386
- echo ' ';
387
- $this->make_info(
388
  __( 'This outputs the complete date.', 'autodescription' )
389
  );
390
  ?>
391
  </label>
392
  </span>
393
  <span class="tsf-toblock">
394
- <input type="radio" name="<?php $this->field_name( 'timestamps_format' ); ?>" id="<?php $this->field_id( 'timestamps_format_1' ); ?>" value="1" <?php checked( $this->get_option( 'timestamps_format' ), '1' ); ?> />
395
- <label for="<?php $this->field_id( 'timestamps_format_1' ); ?>">
396
  <?php
397
  // phpcs:ignore, WordPress.Security.EscapeOutput -- code_wrap escapes.
398
- echo $this->code_wrap( $timestamp_1 );
399
- echo ' ';
400
- $this->make_info(
401
  __( 'This outputs the complete date including hours, minutes, and timezone.', 'autodescription' )
402
  );
403
  ?>
@@ -412,26 +359,23 @@ switch ( $instance ) :
412
  $default_options = $this->get_default_site_options();
413
  $warned_options = $this->get_warned_site_options();
414
 
415
- ?>
416
- <h4><?php esc_html_e( 'Exclusion Settings', 'autodescription' ); ?></h4>
417
- <?php
418
- $this->description( __( 'When checked, these options will remove meta optimizations, SEO suggestions, and sitemap inclusions for the selected post types and taxonomies. This will allow search engines to crawl the post type and taxonomies without advanced restrictions or directions.', 'autodescription' ) );
419
- $this->attention_description_noesc(
420
  $this->convert_markdown(
421
  /* translators: backticks are code wraps. Markdown! */
422
  esc_html__( "These options should not need changing when post types and taxonomies are registered correctly. When they aren't, consider applying `noindex` to purge them from search engines, instead.", 'autodescription' ),
423
  [ 'code' ]
424
  )
425
  );
426
- $this->description( __( 'Default post types and taxonomies can not be excluded.', 'autodescription' ) );
427
  ?>
428
 
429
  <hr>
430
-
431
- <h4><?php esc_html_e( 'Post Type Exclusions', 'autodescription' ); ?></h4>
432
  <?php
433
- $this->description( __( 'Select post types which should be excluded.', 'autodescription' ) );
434
- $this->description( __( 'These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect.', 'autodescription' ) );
 
435
 
436
  $forced_pt = $this->get_forced_supported_post_types();
437
  $boxes = [];
@@ -448,7 +392,7 @@ switch ( $instance ) :
448
  esc_html( $post_type )
449
  );
450
 
451
- $boxes[] = $this->make_checkbox_array( [
452
  'id' => $pt_option_id,
453
  'class' => 'tsf-excluded-post-types',
454
  'index' => $post_type,
@@ -460,15 +404,14 @@ switch ( $instance ) :
460
  ] );
461
  }
462
 
463
- $this->wrap_fields( $boxes, true );
464
 
465
  ?>
466
  <hr>
467
-
468
- <h4><?php esc_html_e( 'Taxonomy Exclusions', 'autodescription' ); ?></h4>
469
  <?php
470
- $this->description( __( 'Select taxonomies which should be excluded.', 'autodescription' ) );
471
- $this->description( __( 'When taxonomies have all their bound post types excluded, they will inherit their exclusion status.', 'autodescription' ) );
 
472
 
473
  $forced_tax = $this->get_forced_supported_taxonomies();
474
  $boxes = [];
@@ -485,7 +428,7 @@ switch ( $instance ) :
485
  esc_html( $taxonomy )
486
  );
487
 
488
- $boxes[] = $this->make_checkbox_array( [
489
  'id' => 'disabled_taxonomies',
490
  'class' => 'tsf-excluded-taxonomies',
491
  'index' => $taxonomy,
@@ -500,7 +443,7 @@ switch ( $instance ) :
500
  ] );
501
  }
502
 
503
- $this->wrap_fields( $boxes, true );
504
  break;
505
 
506
  default:
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
59
  break;
60
 
61
  case 'the_seo_framework_general_metabox_layout':
62
+ Form::header_title( __( 'Administrative Layout Settings', 'autodescription' ) );
63
+ HTML::description( __( 'SEO hints can be visually displayed throughout the dashboard.', 'autodescription' ) );
 
 
64
 
65
  ?>
66
  <hr>
 
 
67
  <?php
68
+ Form::header_title( __( 'SEO Bar Settings', 'autodescription' ) );
69
+ HTML::wrap_fields(
70
  [
71
+ Form::make_checkbox( [
72
+ 'id' => 'display_seo_bar_tables',
73
+ 'label' => esc_html__( 'Display the SEO Bar in overview tables?', 'autodescription' ),
74
+ 'escape' => false,
75
+ ] ),
76
+ Form::make_checkbox( [
77
+ 'id' => 'display_seo_bar_metabox',
78
+ 'label' => esc_html__( 'Display the SEO Bar in the SEO Settings metabox?', 'autodescription' ),
79
+ 'escape' => false,
80
+ ] ),
81
+ Form::make_checkbox( [
82
+ 'id' => 'seo_bar_symbols',
83
+ 'label' => esc_html__( 'Use symbols for warnings?', 'autodescription' ) . ' ' . HTML::make_info(
 
 
84
  __( 'If you have difficulty discerning colors, this may help you spot issues more easily.', 'autodescription' ),
85
  '',
86
  false
87
  ),
88
+ 'escape' => false,
89
+ ] ),
 
90
  ],
91
  true
92
  );
93
 
94
  ?>
95
  <hr>
 
 
96
  <?php
97
+ Form::header_title( __( 'Counter Settings', 'autodescription' ) );
98
 
99
+ $pixel_info = HTML::make_info(
100
  __( 'The pixel counter computes whether the input will fit on search engine result pages.', 'autodescription' ),
101
  'https://kb.theseoframework.com/?p=48',
102
  false
103
  );
104
 
105
+ $character_info = HTML::make_info(
106
  __( 'The character counter is based on guidelines.', 'autodescription' ),
107
  '',
108
  false
109
  );
110
 
111
+ HTML::wrap_fields(
112
  [
113
+ Form::make_checkbox( [
114
+ 'id' => 'display_pixel_counter',
115
+ 'label' => esc_html__( 'Display pixel counters?', 'autodescription' ) . ' ' . $pixel_info,
116
+ 'escape' => false,
117
+ ] ),
118
+ Form::make_checkbox( [
119
+ 'id' => 'display_character_counter',
120
+ 'label' => esc_html__( 'Display character counters?', 'autodescription' ) . ' ' . $character_info,
121
+ 'escape' => false,
122
+ ] ),
 
 
123
  ],
124
  true
125
  );
126
  break;
127
 
128
  case 'the_seo_framework_general_metabox_performance':
129
+ Form::header_title( __( 'Performance Settings', 'autodescription' ) );
130
+ HTML::description( __( "Depending on your server's configuration, adjusting these settings can affect performance.", 'autodescription' ) );
 
 
131
 
132
  ?>
133
  <hr>
 
 
134
  <?php
135
+ Form::header_title( __( 'Query Alteration Settings', 'autodescription' ) );
136
+ HTML::description_noesc(
137
  esc_html__( "Altering the query allows for more control of the site's hierarchy.", 'autodescription' )
138
  . '<br>' .
139
  esc_html__( 'If your website has thousands of pages, these options can greatly affect database performance.', 'autodescription' )
140
  );
141
 
142
+ HTML::description_noesc(
143
  esc_html__( 'Altering the query in the database is more accurate, but can increase database query time.', 'autodescription' )
144
  . '<br>' .
145
  esc_html__( 'Altering the query on the site is much faster, but can lead to inconsistent pagination. It can also lead to 404 error messages if all queried pages have been excluded.', 'autodescription' )
185
  '<label for="%1$s">%2$s</label>
186
  <select name="%3$s" id="%1$s">%4$s</select>',
187
  [
188
+ Form::get_field_id( 'alter_search_query_type' ),
189
  $perform_alteration_i18n,
190
+ Form::get_field_name( 'alter_search_query_type' ),
191
  $search_query_select_options,
192
  ]
193
  );
196
  '<label for="%1$s">%2$s</label>
197
  <select name="%3$s" id="%1$s">%4$s</select>',
198
  [
199
+ Form::get_field_id( 'alter_archive_query_type' ),
200
  $perform_alteration_i18n,
201
+ Form::get_field_name( 'alter_archive_query_type' ),
202
  $archive_query_select_options,
203
  ]
204
  );
205
 
206
+ HTML::wrap_fields(
207
  [
208
+ Form::make_checkbox( [
209
+ 'id' => 'alter_search_query',
210
+ 'label' => esc_html__( 'Enable search query alteration?', 'autodescription' )
211
+ . ' ' . HTML::make_info( __( 'This allows you to exclude pages from on-site search results.', 'autodescription' ), '', false ),
212
+ 'escape' => false,
213
+ ] ),
 
214
  $search_query_select_field,
215
  ],
216
  true
217
  );
218
 
219
+ HTML::wrap_fields(
220
  [
221
+ Form::make_checkbox( [
222
+ 'id' => 'alter_archive_query',
223
+ 'label' => esc_html__( 'Enable archive query alteration?', 'autodescription' )
224
+ . ' ' . HTML::make_info( __( 'This allows you to exclude pages from on-site archive listings.', 'autodescription' ), '', false ),
225
+ 'escape' => false,
226
+ ] ),
 
227
  $archive_query_select_field,
228
  ],
229
  true
230
  );
231
  ?>
232
  <hr>
 
 
233
  <?php
234
+ Form::header_title( __( 'Transient Cache Settings', 'autodescription' ) );
235
+ HTML::description( __( 'To improve performance, generated output can be stored in the database as transient cache.', 'autodescription' ) );
236
+
237
+ HTML::wrap_fields(
238
+ Form::make_checkbox( [
239
+ 'id' => 'cache_sitemap',
240
+ 'label' => esc_html__( 'Enable optimized sitemap generation cache?', 'autodescription' )
241
+ . ' ' . HTML::make_info( __( 'Generating the sitemap can use a lot of server resources.', 'autodescription' ), '', false ),
242
+ 'escape' => false,
243
+ ] ),
244
  true
245
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  break;
247
 
248
  case 'the_seo_framework_general_metabox_canonical':
249
+ Form::header_title( __( 'Canonical URL Settings', 'autodescription' ) );
250
+ HTML::description( __( 'The canonical URL meta tag urges search engines to go to the outputted URL.', 'autodescription' ) );
251
+ HTML::description( __( 'If the canonical URL meta tag represents the visited page, then the search engine will crawl the visited page. Otherwise, the search engine may go to the outputted URL.', 'autodescription' ) );
 
 
252
  ?>
253
  <hr>
 
 
254
  <?php
255
+ Form::header_title( __( 'Scheme Settings', 'autodescription' ) );
256
+ HTML::description( __( 'If your website is accessible via both HTTP as HTTPS, you may want to set this to HTTPS if not detected automatically. Secure connections are preferred by search engines.', 'autodescription' ) );
257
  ?>
258
+ <label for="<?php Form::field_id( 'canonical_scheme' ); ?>"><?php echo esc_html_x( 'Preferred canonical URL scheme:', '= Detect Automatically, HTTPS, HTTP', 'autodescription' ); ?></label>
259
+ <select name="<?php Form::field_name( 'canonical_scheme' ); ?>" id="<?php Form::field_id( 'canonical_scheme' ); ?>">
260
  <?php
261
  $scheme_types = (array) apply_filters(
262
  'the_seo_framework_canonical_scheme_types',
276
  </select>
277
 
278
  <hr>
 
 
279
  <?php
280
+ Form::header_title( __( 'Link Relationship Settings', 'autodescription' ) );
281
+ HTML::description( __( 'Some search engines look for relations between the content of your pages. If you have pagination on a post or page, or have archives indexed, these options will help search engines look for the right page to display in the search results.', 'autodescription' ) );
282
+ HTML::description( __( "It's recommended to turn these options on for better SEO consistency and to prevent duplicated content issues.", 'autodescription' ) );
283
 
284
+ $prev_next_posts_checkbox = Form::make_checkbox( [
285
+ 'id' => 'prev_next_posts',
286
+ 'label' => $this->convert_markdown(
287
  /* translators: the backticks are Markdown! Preserve them as-is! */
288
  esc_html__( 'Add `rel` link tags to posts and pages?', 'autodescription' ),
289
  [ 'code' ]
290
  ),
291
+ 'escape' => false,
292
+ ] );
 
293
 
294
+ $prev_next_archives_checkbox = Form::make_checkbox( [
295
+ 'id' => 'prev_next_archives',
296
+ 'label' => $this->convert_markdown(
297
  /* translators: the backticks are Markdown! Preserve them as-is! */
298
  esc_html__( 'Add `rel` link tags to archives?', 'autodescription' ),
299
  [ 'code' ]
300
  ),
301
+ 'escape' => false,
302
+ ] );
 
303
 
304
+ $prev_next_frontpage_checkbox = Form::make_checkbox( [
305
+ 'id' => 'prev_next_frontpage',
306
+ 'label' => $this->convert_markdown(
307
  /* translators: the backticks are Markdown! Preserve them as-is! */
308
  esc_html__( 'Add `rel` link tags to the homepage?', 'autodescription' ),
309
  [ 'code' ]
310
  ),
311
+ 'escape' => false,
312
+ ] );
 
313
 
314
+ HTML::wrap_fields( $prev_next_posts_checkbox . $prev_next_archives_checkbox . $prev_next_frontpage_checkbox, true );
315
  break;
316
 
 
317
  case 'the_seo_framework_general_metabox_timestamps':
318
+ $timestamp_0 = gmdate( $this->get_timestamp_format( false ) );
319
+ $timestamp_1 = gmdate( $this->get_timestamp_format( true ) );
320
 
321
+ Form::header_title( __( 'Timestamp Settings', 'autodescription' ) );
322
+ HTML::description( __( 'Timestamps help indicate when a page has been published and modified.', 'autodescription' ) );
 
 
 
 
 
 
 
 
323
  ?>
324
  <hr>
325
 
326
  <fieldset>
327
+ <legend><?php Form::header_title( __( 'Timestamp Format Settings', 'autodescription' ) ); ?></legend>
328
+ <?php HTML::description( __( 'This setting determines how specific the timestamp is.', 'autodescription' ) ); ?>
 
 
329
 
330
  <p id="sitemaps-timestamp-format" class="tsf-fields">
331
  <span class="tsf-toblock">
332
+ <input type="radio" name="<?php Form::field_name( 'timestamps_format' ); ?>" id="<?php Form::field_id( 'timestamps_format_0' ); ?>" value="0" <?php checked( $this->get_option( 'timestamps_format' ), '0' ); ?> />
333
+ <label for="<?php Form::field_id( 'timestamps_format_0' ); ?>">
334
  <?php
335
  // phpcs:ignore, WordPress.Security.EscapeOutput -- code_wrap escapes.
336
+ echo HTML::code_wrap( $timestamp_0 ), ' ', HTML::make_info(
 
 
337
  __( 'This outputs the complete date.', 'autodescription' )
338
  );
339
  ?>
340
  </label>
341
  </span>
342
  <span class="tsf-toblock">
343
+ <input type="radio" name="<?php Form::field_name( 'timestamps_format' ); ?>" id="<?php Form::field_id( 'timestamps_format_1' ); ?>" value="1" <?php checked( $this->get_option( 'timestamps_format' ), '1' ); ?> />
344
+ <label for="<?php Form::field_id( 'timestamps_format_1' ); ?>">
345
  <?php
346
  // phpcs:ignore, WordPress.Security.EscapeOutput -- code_wrap escapes.
347
+ echo HTML::code_wrap( $timestamp_1 ), ' ', HTML::make_info(
 
 
348
  __( 'This outputs the complete date including hours, minutes, and timezone.', 'autodescription' )
349
  );
350
  ?>
359
  $default_options = $this->get_default_site_options();
360
  $warned_options = $this->get_warned_site_options();
361
 
362
+ Form::header_title( __( 'Exclusion Settings', 'autodescription' ) );
363
+ HTML::description( __( 'When checked, these options will remove meta optimizations, SEO suggestions, and sitemap inclusions for the selected post types and taxonomies. This will allow search engines to crawl the post type and taxonomies without advanced restrictions or directions.', 'autodescription' ) );
364
+ HTML::attention_description_noesc(
 
 
365
  $this->convert_markdown(
366
  /* translators: backticks are code wraps. Markdown! */
367
  esc_html__( "These options should not need changing when post types and taxonomies are registered correctly. When they aren't, consider applying `noindex` to purge them from search engines, instead.", 'autodescription' ),
368
  [ 'code' ]
369
  )
370
  );
371
+ HTML::description( __( 'Default post types and taxonomies can not be excluded.', 'autodescription' ) );
372
  ?>
373
 
374
  <hr>
 
 
375
  <?php
376
+ Form::header_title( __( 'Post Type Exclusions', 'autodescription' ) );
377
+ HTML::description( __( 'Select post types which should be excluded.', 'autodescription' ) );
378
+ HTML::description( __( 'These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect.', 'autodescription' ) );
379
 
380
  $forced_pt = $this->get_forced_supported_post_types();
381
  $boxes = [];
392
  esc_html( $post_type )
393
  );
394
 
395
+ $boxes[] = Form::make_checkbox( [
396
  'id' => $pt_option_id,
397
  'class' => 'tsf-excluded-post-types',
398
  'index' => $post_type,
404
  ] );
405
  }
406
 
407
+ HTML::wrap_fields( $boxes, true );
408
 
409
  ?>
410
  <hr>
 
 
411
  <?php
412
+ Form::header_title( __( 'Taxonomy Exclusions', 'autodescription' ) );
413
+ HTML::description( __( 'Select taxonomies which should be excluded.', 'autodescription' ) );
414
+ HTML::description( __( 'When taxonomies have all their bound post types excluded, they will inherit their exclusion status.', 'autodescription' ) );
415
 
416
  $forced_tax = $this->get_forced_supported_taxonomies();
417
  $boxes = [];
428
  esc_html( $taxonomy )
429
  );
430
 
431
+ $boxes[] = Form::make_checkbox( [
432
  'id' => 'disabled_taxonomies',
433
  'class' => 'tsf-excluded-taxonomies',
434
  'index' => $taxonomy,
443
  ] );
444
  }
445
 
446
+ HTML::wrap_fields( $boxes, true );
447
  break;
448
 
449
  default:
inc/views/admin/metaboxes/homepage-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -23,7 +25,7 @@ $_generator_args = [
23
 
24
  switch ( $instance ) :
25
  case 'the_seo_framework_homepage_metabox_main':
26
- $this->description( __( 'These settings will take precedence over the settings set within the homepage edit screen, if any.', 'autodescription' ) );
27
  ?>
28
  <hr>
29
  <?php
@@ -66,28 +68,28 @@ switch ( $instance ) :
66
  case 'the_seo_framework_homepage_metabox_general':
67
  ?>
68
  <p>
69
- <label for="<?php $this->field_id( 'homepage_title' ); ?>" class="tsf-toblock">
70
  <strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong>
71
  <?php
72
  echo ' ';
73
- $this->make_info(
74
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
75
- 'https://support.google.com/webmasters/answer/35624#page-titles'
76
  );
77
  ?>
78
  </label>
79
  </p>
80
  <?php
81
  // Output these unconditionally, with inline CSS attached to allow reacting on settings.
82
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_title' ), '', (bool) $this->get_option( 'display_character_counter' ) );
83
- $this->output_pixel_counter_wrap( $this->get_field_id( 'homepage_title' ), 'title', (bool) $this->get_option( 'display_pixel_counter' ) );
84
  ?>
85
  <p class=tsf-title-wrap>
86
- <input type="text" name="<?php $this->field_name( 'homepage_title' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_title' ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_title' ) ); ?>" autocomplete=off />
87
  <?php
88
  $this->output_js_title_elements(); // legacy
89
  $this->output_js_title_data(
90
- $this->get_field_id( 'homepage_title' ),
91
  [
92
  'state' => [
93
  'refTitleLocked' => false,
@@ -105,10 +107,10 @@ switch ( $instance ) :
105
  ?>
106
  </p>
107
  <?php
108
- $this->description( __( 'Note: The input value of this field may be used to describe the name of the site elsewhere.', 'autodescription' ) );
109
 
110
  if ( $home_id && $this->get_post_meta_item( '_genesis_title', $home_id ) ) {
111
- $this->description( __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' ) );
112
  }
113
 
114
  /**
@@ -116,7 +118,7 @@ switch ( $instance ) :
116
  * @param bool $warn Whether to warn that there's a plugin active with multiple homepages.
117
  */
118
  if ( $home_id && apply_filters( 'the_seo_framework_warn_homepage_global_title', false ) ) {
119
- $this->attention_noesc(
120
  // Markdown escapes.
121
  $this->convert_markdown(
122
  sprintf(
@@ -133,28 +135,28 @@ switch ( $instance ) :
133
  <hr>
134
 
135
  <p>
136
- <label for="<?php $this->field_id( 'homepage_description' ); ?>" class="tsf-toblock">
137
  <strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong>
138
  <?php
139
  echo ' ';
140
- $this->make_info(
141
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
142
- 'https://support.google.com/webmasters/answer/35624#meta-descriptions'
143
  );
144
  ?>
145
  </label>
146
  </p>
147
  <?php
148
  // Output these unconditionally, with inline CSS attached to allow reacting on settings.
149
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_description' ), '', (bool) $this->get_option( 'display_character_counter' ) );
150
- $this->output_pixel_counter_wrap( $this->get_field_id( 'homepage_description' ), 'description', (bool) $this->get_option( 'display_pixel_counter' ) );
151
  ?>
152
  <p>
153
- <textarea name="<?php $this->field_name( 'homepage_description' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_description' ); ?>" rows="3" cols="70"><?php echo esc_attr( $this->get_option( 'homepage_description' ) ); ?></textarea>
154
  <?php
155
  $this->output_js_description_elements(); // legacy
156
  $this->output_js_description_data(
157
- $this->get_field_id( 'homepage_description' ),
158
  [
159
  'state' => [
160
  'defaultDescription' =>
@@ -169,7 +171,7 @@ switch ( $instance ) :
169
  <?php
170
 
171
  if ( $home_id && $this->get_post_meta_item( '_genesis_description', $home_id ) ) {
172
- $this->description(
173
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
174
  );
175
  }
@@ -179,7 +181,7 @@ switch ( $instance ) :
179
  * @param bool $warn Whether to warn that there's a plugin active with multiple homepages.
180
  */
181
  if ( $home_id && apply_filters( 'the_seo_framework_warn_homepage_global_description', false ) ) {
182
- $this->attention_noesc(
183
  // Markdown escapes.
184
  $this->convert_markdown(
185
  sprintf(
@@ -201,7 +203,8 @@ switch ( $instance ) :
201
  $_example_title = $this->escape_title(
202
  $this->get_filtered_raw_custom_field_title( $_generator_args ) ?: $this->get_filtered_raw_generated_title( $_generator_args )
203
  );
204
- // FIXME? When no blog description or tagline is set... this will be empty and be ugly on no-JS.
 
205
  $_example_blogname = $this->escape_title( $this->get_home_title_additions() ?: $this->get_static_untitled_title() );
206
  $_example_separator = esc_html( $this->get_separator( 'title' ) );
207
 
@@ -212,23 +215,21 @@ switch ( $instance ) :
212
  ?>
213
 
214
  <p>
215
- <label for="<?php $this->field_id( 'homepage_title_tagline' ); ?>" class="tsf-toblock">
216
  <strong><?php esc_html_e( 'Meta Title Additions', 'autodescription' ); ?></strong>
217
  </label>
218
  </p>
219
  <p>
220
- <input type="text" name="<?php $this->field_name( 'homepage_title_tagline' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_title_tagline' ); ?>" placeholder="<?php echo esc_attr( $tagline_placeholder ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_title_tagline' ) ); ?>" autocomplete=off />
221
  </p>
222
 
223
  <div id="tsf-title-tagline-toggle">
224
  <?php
225
- $this->wrap_fields(
226
- $this->make_checkbox(
227
- 'homepage_tagline',
228
- esc_html__( 'Add Meta Title Additions to the homepage title?', 'autodescription' ),
229
- '',
230
- false
231
- ),
232
  true
233
  );
234
  ?>
@@ -237,28 +238,26 @@ switch ( $instance ) :
237
  <hr>
238
 
239
  <fieldset>
240
- <legend>
241
- <h4><?php esc_html_e( 'Meta Title Additions Location', 'autodescription' ); ?></h4>
242
- </legend>
243
 
244
  <p id="tsf-home-title-location" class="tsf-fields">
245
  <span class="tsf-toblock">
246
- <input type="radio" name="<?php $this->field_name( 'home_title_location' ); ?>" id="<?php $this->field_id( 'home_title_location_left' ); ?>" value="left" <?php checked( $this->get_option( 'home_title_location' ), 'left' ); ?> />
247
- <label for="<?php $this->field_id( 'home_title_location_left' ); ?>">
248
  <span><?php esc_html_e( 'Left:', 'autodescription' ); ?></span>
249
  <?php
250
  // phpcs:ignore, WordPress.Security.EscapeOutput -- $example_left is already escaped.
251
- echo $this->code_wrap_noesc( $example_left );
252
  ?>
253
  </label>
254
  </span>
255
  <span class="tsf-toblock">
256
- <input type="radio" name="<?php $this->field_name( 'home_title_location' ); ?>" id="<?php $this->field_id( 'home_title_location_right' ); ?>" value="right" <?php checked( $this->get_option( 'home_title_location' ), 'right' ); ?> />
257
- <label for="<?php $this->field_id( 'home_title_location_right' ); ?>">
258
  <span><?php esc_html_e( 'Right:', 'autodescription' ); ?></span>
259
  <?php
260
  // phpcs:ignore, WordPress.Security.EscapeOutput -- $example_right is already escaped.
261
- echo $this->code_wrap_noesc( $example_right );
262
  ?>
263
  </label>
264
  </span>
@@ -278,7 +277,7 @@ switch ( $instance ) :
278
 
279
  ?>
280
  <p>
281
- <label for="<?php $this->field_id( 'homepage_og_title' ); ?>" class="tsf-toblock">
282
  <strong>
283
  <?php
284
  esc_html_e( 'Open Graph Title', 'autodescription' );
@@ -288,21 +287,21 @@ switch ( $instance ) :
288
  </p>
289
  <?php
290
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
291
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_og_title' ), '', (bool) $this->get_option( 'display_character_counter' ) );
292
  ?>
293
  <p>
294
- <input type="text" name="<?php $this->field_name( 'homepage_og_title' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_og_title' ); ?>" placeholder="<?php echo esc_attr( $social_placeholders['title']['og'] ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_og_title' ) ); ?>" autocomplete=off />
295
  </p>
296
  <?php
297
  if ( $this->has_page_on_front() && $custom_og_title ) {
298
- $this->description(
299
  __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
300
  );
301
  }
302
  ?>
303
 
304
  <p>
305
- <label for="<?php $this->field_id( 'homepage_og_description' ); ?>" class="tsf-toblock">
306
  <strong>
307
  <?php
308
  esc_html_e( 'Open Graph Description', 'autodescription' );
@@ -312,14 +311,14 @@ switch ( $instance ) :
312
  </p>
313
  <?php
314
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
315
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_og_description' ), '', (bool) $this->get_option( 'display_character_counter' ) );
316
  ?>
317
  <p>
318
- <textarea name="<?php $this->field_name( 'homepage_og_description' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_og_description' ); ?>" rows="3" cols="70" placeholder="<?php echo esc_attr( $social_placeholders['description']['og'] ); ?>" autocomplete=off><?php echo esc_attr( $this->get_option( 'homepage_og_description' ) ); ?></textarea>
319
  </p>
320
  <?php
321
  if ( $this->has_page_on_front() && $custom_og_desc ) {
322
- $this->description(
323
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
324
  );
325
  }
@@ -327,7 +326,7 @@ switch ( $instance ) :
327
  <hr>
328
 
329
  <p>
330
- <label for="<?php $this->field_id( 'homepage_twitter_title' ); ?>" class="tsf-toblock">
331
  <strong>
332
  <?php
333
  esc_html_e( 'Twitter Title', 'autodescription' );
@@ -337,21 +336,21 @@ switch ( $instance ) :
337
  </p>
338
  <?php
339
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
340
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_twitter_title' ), '', (bool) $this->get_option( 'display_character_counter' ) );
341
  ?>
342
  <p>
343
- <input type="text" name="<?php $this->field_name( 'homepage_twitter_title' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_twitter_title' ); ?>" placeholder="<?php echo esc_attr( $social_placeholders['title']['twitter'] ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_twitter_title' ) ); ?>" autocomplete=off />
344
  </p>
345
  <?php
346
  if ( $this->has_page_on_front() && ( $custom_og_title || $custom_tw_title ) ) {
347
- $this->description(
348
  __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
349
  );
350
  }
351
  ?>
352
 
353
  <p>
354
- <label for="<?php $this->field_id( 'homepage_twitter_description' ); ?>" class="tsf-toblock">
355
  <strong>
356
  <?php
357
  esc_html_e( 'Twitter Description', 'autodescription' );
@@ -361,23 +360,22 @@ switch ( $instance ) :
361
  </p>
362
  <?php
363
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
364
- $this->output_character_counter_wrap( $this->get_field_id( 'homepage_twitter_description' ), '', (bool) $this->get_option( 'display_character_counter' ) );
365
  ?>
366
  <p>
367
- <textarea name="<?php $this->field_name( 'homepage_twitter_description' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_twitter_description' ); ?>" rows="3" cols="70" placeholder="<?php echo esc_attr( $social_placeholders['description']['twitter'] ); ?>" autocomplete=off><?php echo esc_attr( $this->get_option( 'homepage_twitter_description' ) ); ?></textarea>
368
  </p>
369
  <?php
370
  if ( $this->has_page_on_front() && ( $custom_og_desc || $custom_tw_desc ) ) {
371
- $this->description(
372
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
373
  );
374
  }
375
  ?>
376
  <hr>
377
-
378
- <h4><?php esc_html_e( 'Social Image Settings', 'autodescription' ); ?></h4>
379
  <?php
380
- $this->description( __( 'A social image can be displayed when your homepage is shared. It is a great way to grab attention.', 'autodescription' ) );
 
381
 
382
  // Fetch image placeholder.
383
  $image_details = current( $this->get_generated_image_details( $_generator_args, true, 'social', true ) );
@@ -388,7 +386,7 @@ switch ( $instance ) :
388
  <label for="tsf_homepage_socialimage-url">
389
  <strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong>
390
  <?php
391
- $this->make_info(
392
  __( "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.", 'autodescription' ),
393
  'https://developers.facebook.com/docs/sharing/best-practices#images'
394
  );
@@ -396,13 +394,13 @@ switch ( $instance ) :
396
  </label>
397
  </p>
398
  <p>
399
- <input class="large-text" type="url" name="<?php $this->field_name( 'homepage_social_image_url' ); ?>" id="tsf_homepage_socialimage-url" placeholder="<?php echo esc_url( $image_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'homepage_social_image_url' ) ); ?>" />
400
- <input type="hidden" name="<?php $this->field_name( 'homepage_social_image_id' ); ?>" id="tsf_homepage_socialimage-id" value="<?php echo absint( $this->get_option( 'homepage_social_image_id' ) ); ?>" disabled class="tsf-enable-media-if-js" />
401
  </p>
402
  <p class="hide-if-no-tsf-js">
403
  <?php
404
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
405
- echo $this->get_social_image_uploader_form( 'tsf_homepage_socialimage' );
406
  ?>
407
  </p>
408
  <?php
@@ -434,9 +432,7 @@ switch ( $instance ) :
434
  );
435
  }
436
 
437
- ?>
438
- <h4><?php esc_html_e( 'Robots Meta Settings', 'autodescription' ); ?></h4>
439
- <?php
440
 
441
  $i_label = sprintf(
442
  /* translators: 1: Option label, 2: [?] option info note, 3: Optional warning */
@@ -446,9 +442,9 @@ switch ( $instance ) :
446
  esc_html__( 'Apply `noindex` to the homepage?', 'autodescription' ),
447
  [ 'code' ]
448
  ),
449
- $this->make_info(
450
  __( 'This tells search engines not to show this page in their search results.', 'autodescription' ),
451
- 'https://support.google.com/webmasters/answer/93710',
452
  false
453
  ),
454
  $noindex_post ? $checked_home : ''
@@ -462,9 +458,9 @@ switch ( $instance ) :
462
  esc_html__( 'Apply `nofollow` to the homepage?', 'autodescription' ),
463
  [ 'code' ]
464
  ),
465
- $this->make_info(
466
  __( 'This tells search engines not to follow links on this page.', 'autodescription' ),
467
- 'https://support.google.com/webmasters/answer/96569',
468
  false
469
  ),
470
  $nofollow_post ? $checked_home : ''
@@ -478,42 +474,39 @@ switch ( $instance ) :
478
  esc_html__( 'Apply `noarchive` to the homepage?', 'autodescription' ),
479
  [ 'code' ]
480
  ),
481
- $this->make_info(
482
  __( 'This tells search engines not to save a cached copy of this page.', 'autodescription' ),
483
- 'https://support.google.com/webmasters/answer/79812',
484
  false
485
  ),
486
  $noarchive_post ? $checked_home : ''
487
  );
488
 
489
- $this->attention_description( __( 'Warning: No public site should ever apply "noindex" or "nofollow" to the homepage.', 'autodescription' ) );
490
 
491
- $this->wrap_fields(
492
  [
493
- $this->make_checkbox(
494
- 'homepage_noindex',
495
- $i_label,
496
- '',
497
- false
498
- ),
499
- $this->make_checkbox(
500
- 'homepage_nofollow',
501
- $f_label,
502
- '',
503
- false
504
- ),
505
- $this->make_checkbox(
506
- 'homepage_noarchive',
507
- $a_label,
508
- '',
509
- false
510
- ),
511
  ],
512
  true
513
  );
514
 
515
  if ( $this->has_page_on_front() ) {
516
- $this->description_noesc(
517
  $this->convert_markdown(
518
  sprintf(
519
  /* translators: %s = Homepage URL markdown */
@@ -528,23 +521,21 @@ switch ( $instance ) :
528
  ?>
529
 
530
  <hr>
531
-
532
- <h4><?php esc_html_e( 'Homepage Pagination Robots Settings', 'autodescription' ); ?></h4>
533
  <?php
534
- $this->description( __( "If your homepage is paginated and outputs content that's also found elsewhere on the website, enabling this option may prevent duplicate content.", 'autodescription' ) );
 
535
 
536
  // Echo checkbox.
537
- $this->wrap_fields(
538
- $this->make_checkbox(
539
- 'home_paged_noindex',
540
- $this->convert_markdown(
541
  /* translators: the backticks are Markdown! Preserve them as-is! */
542
  esc_html__( 'Apply `noindex` to every second or later page on the homepage?', 'autodescription' ),
543
  [ 'code' ]
544
  ),
545
- '',
546
- false
547
- ),
548
  true
549
  );
550
  break;
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
25
 
26
  switch ( $instance ) :
27
  case 'the_seo_framework_homepage_metabox_main':
28
+ HTML::description( __( 'These settings will take precedence over the settings set within the homepage edit screen, if any.', 'autodescription' ) );
29
  ?>
30
  <hr>
31
  <?php
68
  case 'the_seo_framework_homepage_metabox_general':
69
  ?>
70
  <p>
71
+ <label for="<?php Form::field_id( 'homepage_title' ); ?>" class="tsf-toblock">
72
  <strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong>
73
  <?php
74
  echo ' ';
75
+ HTML::make_info(
76
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
77
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#page-titles'
78
  );
79
  ?>
80
  </label>
81
  </p>
82
  <?php
83
  // Output these unconditionally, with inline CSS attached to allow reacting on settings.
84
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_title' ), (bool) $this->get_option( 'display_character_counter' ) );
85
+ Form::output_pixel_counter_wrap( Form::get_field_id( 'homepage_title' ), 'title', (bool) $this->get_option( 'display_pixel_counter' ) );
86
  ?>
87
  <p class=tsf-title-wrap>
88
+ <input type="text" name="<?php Form::field_name( 'homepage_title' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_title' ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_title' ) ); ?>" autocomplete=off />
89
  <?php
90
  $this->output_js_title_elements(); // legacy
91
  $this->output_js_title_data(
92
+ Form::get_field_id( 'homepage_title' ),
93
  [
94
  'state' => [
95
  'refTitleLocked' => false,
107
  ?>
108
  </p>
109
  <?php
110
+ HTML::description( __( 'Note: The input value of this field may be used to describe the name of the site elsewhere.', 'autodescription' ) );
111
 
112
  if ( $home_id && $this->get_post_meta_item( '_genesis_title', $home_id ) ) {
113
+ HTML::description( __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' ) );
114
  }
115
 
116
  /**
118
  * @param bool $warn Whether to warn that there's a plugin active with multiple homepages.
119
  */
120
  if ( $home_id && apply_filters( 'the_seo_framework_warn_homepage_global_title', false ) ) {
121
+ HTML::attention_noesc(
122
  // Markdown escapes.
123
  $this->convert_markdown(
124
  sprintf(
135
  <hr>
136
 
137
  <p>
138
+ <label for="<?php Form::field_id( 'homepage_description' ); ?>" class="tsf-toblock">
139
  <strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong>
140
  <?php
141
  echo ' ';
142
+ HTML::make_info(
143
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
144
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#meta-descriptions'
145
  );
146
  ?>
147
  </label>
148
  </p>
149
  <?php
150
  // Output these unconditionally, with inline CSS attached to allow reacting on settings.
151
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_description' ), (bool) $this->get_option( 'display_character_counter' ) );
152
+ Form::output_pixel_counter_wrap( Form::get_field_id( 'homepage_description' ), 'description', (bool) $this->get_option( 'display_pixel_counter' ) );
153
  ?>
154
  <p>
155
+ <textarea name="<?php Form::field_name( 'homepage_description' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_description' ); ?>" rows="3" cols="70"><?php echo esc_attr( $this->get_option( 'homepage_description' ) ); ?></textarea>
156
  <?php
157
  $this->output_js_description_elements(); // legacy
158
  $this->output_js_description_data(
159
+ Form::get_field_id( 'homepage_description' ),
160
  [
161
  'state' => [
162
  'defaultDescription' =>
171
  <?php
172
 
173
  if ( $home_id && $this->get_post_meta_item( '_genesis_description', $home_id ) ) {
174
+ HTML::description(
175
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
176
  );
177
  }
181
  * @param bool $warn Whether to warn that there's a plugin active with multiple homepages.
182
  */
183
  if ( $home_id && apply_filters( 'the_seo_framework_warn_homepage_global_description', false ) ) {
184
+ HTML::attention_noesc(
185
  // Markdown escapes.
186
  $this->convert_markdown(
187
  sprintf(
203
  $_example_title = $this->escape_title(
204
  $this->get_filtered_raw_custom_field_title( $_generator_args ) ?: $this->get_filtered_raw_generated_title( $_generator_args )
205
  );
206
+ // On JS: The 'Untitled' title will disappear, this is intentional. On no-JS one will see 'Untitled'.
207
+ // TODO: Deprecate no-JS support? WordPress doesn't function without JS since 5.0 anyway...
208
  $_example_blogname = $this->escape_title( $this->get_home_title_additions() ?: $this->get_static_untitled_title() );
209
  $_example_separator = esc_html( $this->get_separator( 'title' ) );
210
 
215
  ?>
216
 
217
  <p>
218
+ <label for="<?php Form::field_id( 'homepage_title_tagline' ); ?>" class="tsf-toblock">
219
  <strong><?php esc_html_e( 'Meta Title Additions', 'autodescription' ); ?></strong>
220
  </label>
221
  </p>
222
  <p>
223
+ <input type="text" name="<?php Form::field_name( 'homepage_title_tagline' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_title_tagline' ); ?>" placeholder="<?php echo esc_attr( $tagline_placeholder ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_title_tagline' ) ); ?>" autocomplete=off />
224
  </p>
225
 
226
  <div id="tsf-title-tagline-toggle">
227
  <?php
228
+ HTML::wrap_fields(
229
+ Form::make_checkbox( [
230
+ 'id' => 'homepage_tagline',
231
+ 'label' => __( 'Add Meta Title Additions to the homepage title?', 'autodescription' ),
232
+ ] ),
 
 
233
  true
234
  );
235
  ?>
238
  <hr>
239
 
240
  <fieldset>
241
+ <legend><?php Form::header_title( __( 'Meta Title Additions Location', 'autodescription' ) ); ?></legend>
 
 
242
 
243
  <p id="tsf-home-title-location" class="tsf-fields">
244
  <span class="tsf-toblock">
245
+ <input type="radio" name="<?php Form::field_name( 'home_title_location' ); ?>" id="<?php Form::field_id( 'home_title_location_left' ); ?>" value="left" <?php checked( $this->get_option( 'home_title_location' ), 'left' ); ?> />
246
+ <label for="<?php Form::field_id( 'home_title_location_left' ); ?>">
247
  <span><?php esc_html_e( 'Left:', 'autodescription' ); ?></span>
248
  <?php
249
  // phpcs:ignore, WordPress.Security.EscapeOutput -- $example_left is already escaped.
250
+ echo HTML::code_wrap_noesc( $example_left );
251
  ?>
252
  </label>
253
  </span>
254
  <span class="tsf-toblock">
255
+ <input type="radio" name="<?php Form::field_name( 'home_title_location' ); ?>" id="<?php Form::field_id( 'home_title_location_right' ); ?>" value="right" <?php checked( $this->get_option( 'home_title_location' ), 'right' ); ?> />
256
+ <label for="<?php Form::field_id( 'home_title_location_right' ); ?>">
257
  <span><?php esc_html_e( 'Right:', 'autodescription' ); ?></span>
258
  <?php
259
  // phpcs:ignore, WordPress.Security.EscapeOutput -- $example_right is already escaped.
260
+ echo HTML::code_wrap_noesc( $example_right );
261
  ?>
262
  </label>
263
  </span>
277
 
278
  ?>
279
  <p>
280
+ <label for="<?php Form::field_id( 'homepage_og_title' ); ?>" class="tsf-toblock">
281
  <strong>
282
  <?php
283
  esc_html_e( 'Open Graph Title', 'autodescription' );
287
  </p>
288
  <?php
289
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
290
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_og_title' ), (bool) $this->get_option( 'display_character_counter' ) );
291
  ?>
292
  <p>
293
+ <input type="text" name="<?php Form::field_name( 'homepage_og_title' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_og_title' ); ?>" placeholder="<?php echo esc_attr( $social_placeholders['title']['og'] ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_og_title' ) ); ?>" autocomplete=off />
294
  </p>
295
  <?php
296
  if ( $this->has_page_on_front() && $custom_og_title ) {
297
+ HTML::description(
298
  __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
299
  );
300
  }
301
  ?>
302
 
303
  <p>
304
+ <label for="<?php Form::field_id( 'homepage_og_description' ); ?>" class="tsf-toblock">
305
  <strong>
306
  <?php
307
  esc_html_e( 'Open Graph Description', 'autodescription' );
311
  </p>
312
  <?php
313
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
314
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_og_description' ), (bool) $this->get_option( 'display_character_counter' ) );
315
  ?>
316
  <p>
317
+ <textarea name="<?php Form::field_name( 'homepage_og_description' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_og_description' ); ?>" rows="3" cols="70" placeholder="<?php echo esc_attr( $social_placeholders['description']['og'] ); ?>" autocomplete=off><?php echo esc_attr( $this->get_option( 'homepage_og_description' ) ); ?></textarea>
318
  </p>
319
  <?php
320
  if ( $this->has_page_on_front() && $custom_og_desc ) {
321
+ HTML::description(
322
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
323
  );
324
  }
326
  <hr>
327
 
328
  <p>
329
+ <label for="<?php Form::field_id( 'homepage_twitter_title' ); ?>" class="tsf-toblock">
330
  <strong>
331
  <?php
332
  esc_html_e( 'Twitter Title', 'autodescription' );
336
  </p>
337
  <?php
338
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
339
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_twitter_title' ), (bool) $this->get_option( 'display_character_counter' ) );
340
  ?>
341
  <p>
342
+ <input type="text" name="<?php Form::field_name( 'homepage_twitter_title' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_twitter_title' ); ?>" placeholder="<?php echo esc_attr( $social_placeholders['title']['twitter'] ); ?>" value="<?php echo $this->esc_attr_preserve_amp( $this->get_option( 'homepage_twitter_title' ) ); ?>" autocomplete=off />
343
  </p>
344
  <?php
345
  if ( $this->has_page_on_front() && ( $custom_og_title || $custom_tw_title ) ) {
346
+ HTML::description(
347
  __( 'Note: The title placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
348
  );
349
  }
350
  ?>
351
 
352
  <p>
353
+ <label for="<?php Form::field_id( 'homepage_twitter_description' ); ?>" class="tsf-toblock">
354
  <strong>
355
  <?php
356
  esc_html_e( 'Twitter Description', 'autodescription' );
360
  </p>
361
  <?php
362
  // Output this unconditionally, with inline CSS attached to allow reacting on settings.
363
+ Form::output_character_counter_wrap( Form::get_field_id( 'homepage_twitter_description' ), (bool) $this->get_option( 'display_character_counter' ) );
364
  ?>
365
  <p>
366
+ <textarea name="<?php Form::field_name( 'homepage_twitter_description' ); ?>" class="large-text" id="<?php Form::field_id( 'homepage_twitter_description' ); ?>" rows="3" cols="70" placeholder="<?php echo esc_attr( $social_placeholders['description']['twitter'] ); ?>" autocomplete=off><?php echo esc_attr( $this->get_option( 'homepage_twitter_description' ) ); ?></textarea>
367
  </p>
368
  <?php
369
  if ( $this->has_page_on_front() && ( $custom_og_desc || $custom_tw_desc ) ) {
370
+ HTML::description(
371
  __( 'Note: The description placeholder is fetched from the Page SEO Settings on the homepage.', 'autodescription' )
372
  );
373
  }
374
  ?>
375
  <hr>
 
 
376
  <?php
377
+ Form::header_title( __( 'Social Image Settings', 'autodescription' ) );
378
+ HTML::description( __( 'A social image can be displayed when your homepage is shared. It is a great way to grab attention.', 'autodescription' ) );
379
 
380
  // Fetch image placeholder.
381
  $image_details = current( $this->get_generated_image_details( $_generator_args, true, 'social', true ) );
386
  <label for="tsf_homepage_socialimage-url">
387
  <strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong>
388
  <?php
389
+ HTML::make_info(
390
  __( "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.", 'autodescription' ),
391
  'https://developers.facebook.com/docs/sharing/best-practices#images'
392
  );
394
  </label>
395
  </p>
396
  <p>
397
+ <input class="large-text" type="url" name="<?php Form::field_name( 'homepage_social_image_url' ); ?>" id="tsf_homepage_socialimage-url" placeholder="<?php echo esc_url( $image_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'homepage_social_image_url' ) ); ?>" />
398
+ <input type="hidden" name="<?php Form::field_name( 'homepage_social_image_id' ); ?>" id="tsf_homepage_socialimage-id" value="<?php echo absint( $this->get_option( 'homepage_social_image_id' ) ); ?>" disabled class="tsf-enable-media-if-js" />
399
  </p>
400
  <p class="hide-if-no-tsf-js">
401
  <?php
402
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
403
+ echo Form::get_image_uploader_form( [ 'id' => 'tsf_homepage_socialimage' ] );
404
  ?>
405
  </p>
406
  <?php
432
  );
433
  }
434
 
435
+ Form::header_title( __( 'Robots Meta Settings', 'autodescription' ) );
 
 
436
 
437
  $i_label = sprintf(
438
  /* translators: 1: Option label, 2: [?] option info note, 3: Optional warning */
442
  esc_html__( 'Apply `noindex` to the homepage?', 'autodescription' ),
443
  [ 'code' ]
444
  ),
445
+ HTML::make_info(
446
  __( 'This tells search engines not to show this page in their search results.', 'autodescription' ),
447
+ 'https://developers.google.com/search/docs/advanced/crawling/block-indexing',
448
  false
449
  ),
450
  $noindex_post ? $checked_home : ''
458
  esc_html__( 'Apply `nofollow` to the homepage?', 'autodescription' ),
459
  [ 'code' ]
460
  ),
461
+ HTML::make_info(
462
  __( 'This tells search engines not to follow links on this page.', 'autodescription' ),
463
+ 'https://developers.google.com/search/docs/advanced/guidelines/qualify-outbound-links',
464
  false
465
  ),
466
  $nofollow_post ? $checked_home : ''
474
  esc_html__( 'Apply `noarchive` to the homepage?', 'autodescription' ),
475
  [ 'code' ]
476
  ),
477
+ HTML::make_info(
478
  __( 'This tells search engines not to save a cached copy of this page.', 'autodescription' ),
479
+ 'https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives',
480
  false
481
  ),
482
  $noarchive_post ? $checked_home : ''
483
  );
484
 
485
+ HTML::attention_description( __( 'Warning: No public site should ever apply "noindex" or "nofollow" to the homepage.', 'autodescription' ) );
486
 
487
+ HTML::wrap_fields(
488
  [
489
+ Form::make_checkbox( [
490
+ 'id' => 'homepage_noindex',
491
+ 'label' => $i_label,
492
+ 'escape' => false,
493
+ ] ),
494
+ Form::make_checkbox( [
495
+ 'id' => 'homepage_nofollow',
496
+ 'label' => $f_label,
497
+ 'escape' => false,
498
+ ] ),
499
+ Form::make_checkbox( [
500
+ 'id' => 'homepage_noarchive',
501
+ 'label' => $a_label,
502
+ 'escape' => false,
503
+ ] ),
 
 
 
504
  ],
505
  true
506
  );
507
 
508
  if ( $this->has_page_on_front() ) {
509
+ HTML::description_noesc(
510
  $this->convert_markdown(
511
  sprintf(
512
  /* translators: %s = Homepage URL markdown */
521
  ?>
522
 
523
  <hr>
 
 
524
  <?php
525
+ Form::header_title( __( 'Homepage Pagination Robots Settings', 'autodescription' ) );
526
+ HTML::description( __( "If your homepage is paginated and outputs content that's also found elsewhere on the website, enabling this option may prevent duplicate content.", 'autodescription' ) );
527
 
528
  // Echo checkbox.
529
+ HTML::wrap_fields(
530
+ Form::make_checkbox( [
531
+ 'id' => 'home_paged_noindex',
532
+ 'label' => $this->convert_markdown(
533
  /* translators: the backticks are Markdown! Preserve them as-is! */
534
  esc_html__( 'Apply `noindex` to every second or later page on the homepage?', 'autodescription' ),
535
  [ 'code' ]
536
  ),
537
+ 'escape' => false,
538
+ ] ),
 
539
  true
540
  );
541
  break;
inc/views/admin/metaboxes/robots-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -109,54 +111,45 @@ switch ( $instance ) :
109
  break;
110
 
111
  case 'the_seo_framework_robots_metabox_general':
112
- ?>
113
- <h4><?php esc_html_e( 'Advanced Query Protection', 'autodescription' ); ?></h4>
114
- <?php
115
- $this->description( __( 'Some URL queries can cause WordPress to show faux archives. When search engines spot these, they will crawl and index them, which may cause a drop in ranking. Advanced query protection will prevent robots from indexing these archives.', 'autodescription' ) );
116
-
117
- $this->wrap_fields(
118
- $this->make_checkbox(
119
- 'advanced_query_protection',
120
- esc_html__( 'Enable advanced query protection?', 'autodescription' ),
121
- '',
122
- false
123
- ),
124
  true
125
  );
126
  ?>
127
  <hr>
128
-
129
- <h4><?php esc_html_e( 'Paginated Archive Settings', 'autodescription' ); ?></h4>
130
  <?php
131
- $this->description( __( "Indexing the second or later page of any archive might cause duplication errors. Search engines look down upon them; therefore, it's recommended to disable indexing of those pages.", 'autodescription' ) );
 
132
 
133
- $this->wrap_fields(
134
- $this->make_checkbox(
135
- 'paged_noindex',
136
- $this->convert_markdown(
137
  /* translators: the backticks are Markdown! Preserve them as-is! */
138
  esc_html__( 'Apply `noindex` to every second or later archive page?', 'autodescription' ),
139
  [ 'code' ]
140
  ),
141
- '',
142
- false
143
- ),
144
  true
145
  );
146
  ?>
147
  <hr>
148
-
149
- <h4><?php esc_html_e( 'Copyright Directive Settings', 'autodescription' ); ?></h4>
150
  <?php
151
- $this->description( __( "Some search engines allow you to control copyright directives on the content they aggregate. It's best to allow some content to be taken by these aggregators, as that can improve contextualized exposure via snippets and previews. When left unspecified, regional regulations may apply. It is up to the aggregator to honor these requests.", 'autodescription' ) );
152
-
153
- $this->wrap_fields(
154
- $this->make_checkbox(
155
- 'set_copyright_directives',
156
- esc_html__( 'Specify aggregator copyright compliance directives?', 'autodescription' ),
157
- '',
158
- false
159
- ),
160
  true
161
  );
162
 
@@ -189,17 +182,17 @@ switch ( $instance ) :
189
 
190
  $text_snippet_options .= sprintf( '<optgroup label="%s">%s</optgroup>', esc_attr( $_label ), $_options );
191
  }
192
- $this->wrap_fields(
193
  vsprintf(
194
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
195
  <p><select name="%3$s" id="%1$s">%4$s</select></p>
196
  <p class=description>%6$s</p>',
197
  [
198
- $this->get_field_id( 'max_snippet_length' ),
199
  esc_html__( 'Maximum text snippet length', 'autodescription' ),
200
- $this->get_field_name( 'max_snippet_length' ),
201
  $text_snippet_options,
202
- $this->make_info(
203
  __( 'This may limit the text snippet length for all pages on this site.', 'autodescription' ),
204
  '',
205
  false
@@ -227,16 +220,16 @@ switch ( $instance ) :
227
  ]
228
  );
229
  }
230
- $this->wrap_fields(
231
  vsprintf(
232
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
233
  <p><select name="%3$s" id="%1$s">%4$s</select></p>',
234
  [
235
- $this->get_field_id( 'max_image_preview' ),
236
  esc_html__( 'Maximum image preview size', 'autodescription' ),
237
- $this->get_field_name( 'max_image_preview' ),
238
  $image_preview_options,
239
- $this->make_info(
240
  __( 'This may limit the image preview size for all images from this site.', 'autodescription' ),
241
  '',
242
  false
@@ -275,16 +268,16 @@ switch ( $instance ) :
275
 
276
  $video_preview_options .= sprintf( '<optgroup label="%s">%s</optgroup>', esc_attr( $_label ), $_options );
277
  }
278
- $this->wrap_fields(
279
  vsprintf(
280
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
281
  <p><select name="%3$s" id="%1$s">%4$s</select></p>',
282
  [
283
- $this->get_field_id( 'max_video_preview' ),
284
  esc_html__( 'Maximum video preview length', 'autodescription' ),
285
- $this->get_field_name( 'max_video_preview' ),
286
  $video_preview_options,
287
- $this->make_info(
288
  __( 'This may limit the video preview length for all videos on this site.', 'autodescription' ),
289
  '',
290
  false
@@ -304,33 +297,30 @@ switch ( $instance ) :
304
  /* translators: PLURAL. 1 = noindex/nofollow/noarchive, 2 = Archives, Posts, Pages, etc. */
305
  $apply_x_to_y_i18n_plural = esc_html_x( 'Apply %1$s to %2$s?', 'plural', 'autodescription' );
306
 
307
- $ro_name_wrapped = $this->code_wrap( $ro_value );
308
 
309
  $default_options = $this->get_default_site_options();
310
  $warned_options = $this->get_warned_site_options();
311
 
312
- ?>
313
- <h4><?php esc_html_e( 'Robots Settings', 'autodescription' ); ?></h4>
314
- <?php
315
- $this->description( $ro_i18n );
316
  ?>
317
  <hr>
318
-
319
- <h4><?php esc_html_e( 'Post Type Settings', 'autodescription' ); ?></h4>
320
  <?php
321
- $this->description( __( 'These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect.', 'autodescription' ) );
 
322
 
323
  $pt_option_id = $this->get_robots_post_type_option_id( $ro_value );
324
 
325
  // When the post OR page post types are available, show this warning.
326
  if ( in_array( $ro_value, [ 'noindex', 'nofollow' ], true ) && array_intersect( $post_types, [ 'post', 'page' ] ) )
327
- $this->attention_description( __( 'Warning: No site should enable these options for Posts and Pages.', 'autodescription' ) );
328
 
329
  // TODO can we assume that there's at least one post type at all times? Can WP be used in this way, albeit headless?
330
  $checkboxes = [];
331
 
332
  foreach ( $post_types as $post_type ) {
333
- $checkboxes[] = $this->make_checkbox_array( [
334
  'id' => $pt_option_id,
335
  'class' => 'tsf-robots-post-types',
336
  'index' => $post_type,
@@ -350,14 +340,13 @@ switch ( $instance ) :
350
  ] );
351
  }
352
 
353
- $this->wrap_fields( $checkboxes, true );
354
 
355
  ?>
356
  <hr>
357
-
358
- <h4><?php esc_html_e( 'Taxonomy Settings', 'autodescription' ); ?></h4>
359
  <?php
360
- $this->description( __( "These settings apply to the taxonomies of post types. When taxonomies have all their bound post types' options checked, they will inherit their status.", 'autodescription' ) );
 
361
 
362
  $tax_option_id = $this->get_robots_taxonomy_option_id( $ro_value );
363
 
@@ -365,7 +354,7 @@ switch ( $instance ) :
365
  $checkboxes = [];
366
 
367
  foreach ( $taxonomies as $taxonomy ) {
368
- $checkboxes[] = $this->make_checkbox_array( [
369
  'id' => $tax_option_id,
370
  'class' => 'tsf-robots-taxonomies',
371
  'index' => $taxonomy,
@@ -386,14 +375,13 @@ switch ( $instance ) :
386
  ] );
387
  }
388
 
389
- $this->wrap_fields( $checkboxes, true );
390
 
391
  ?>
392
  <hr>
393
-
394
- <h4><?php esc_html_e( 'Global Settings', 'autodescription' ); ?></h4>
395
  <?php
396
- $this->description( __( 'These settings apply to other globally registered content types.', 'autodescription' ) );
 
397
 
398
  $checkboxes = '';
399
  foreach ( $global_types as $type => $data ) {
@@ -417,10 +405,14 @@ switch ( $instance ) :
417
  );
418
  }
419
 
420
- $checkboxes .= $this->make_checkbox( $id, $label, '', false );
 
 
 
 
421
  }
422
 
423
- $this->wrap_fields( $checkboxes, true );
424
  break;
425
 
426
  default:
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
111
  break;
112
 
113
  case 'the_seo_framework_robots_metabox_general':
114
+ Form::header_title( __( 'Advanced Query Protection', 'autodescription' ) );
115
+ HTML::description( __( 'Some URL queries can cause WordPress to show faux archives. When search engines spot these, they will crawl and index them, which may cause a drop in ranking. Advanced query protection will prevent robots from indexing these archives.', 'autodescription' ) );
116
+
117
+ HTML::wrap_fields(
118
+ Form::make_checkbox( [
119
+ 'id' => 'advanced_query_protection',
120
+ 'label' => __( 'Enable advanced query protection?', 'autodescription' ),
121
+ ] ),
 
 
 
 
122
  true
123
  );
124
  ?>
125
  <hr>
 
 
126
  <?php
127
+ Form::header_title( __( 'Paginated Archive Settings', 'autodescription' ) );
128
+ HTML::description( __( "Indexing the second or later page of any archive might cause duplication errors. Search engines look down upon them; therefore, it's recommended to disable indexing of those pages.", 'autodescription' ) );
129
 
130
+ HTML::wrap_fields(
131
+ Form::make_checkbox( [
132
+ 'id' => 'paged_noindex',
133
+ 'label' => $this->convert_markdown(
134
  /* translators: the backticks are Markdown! Preserve them as-is! */
135
  esc_html__( 'Apply `noindex` to every second or later archive page?', 'autodescription' ),
136
  [ 'code' ]
137
  ),
138
+ 'escape' => false,
139
+ ] ),
 
140
  true
141
  );
142
  ?>
143
  <hr>
 
 
144
  <?php
145
+ Form::header_title( __( 'Copyright Directive Settings', 'autodescription' ) );
146
+ HTML::description( __( "Some search engines allow you to control copyright directives on the content they aggregate. It's best to allow some content to be taken by these aggregators, as that can improve contextualized exposure via snippets and previews. When left unspecified, regional regulations may apply. It is up to the aggregator to honor these requests.", 'autodescription' ) );
147
+
148
+ HTML::wrap_fields(
149
+ Form::make_checkbox( [
150
+ 'id' => 'set_copyright_directives',
151
+ 'label' => __( 'Specify aggregator copyright compliance directives?', 'autodescription' ),
152
+ ] ),
 
153
  true
154
  );
155
 
182
 
183
  $text_snippet_options .= sprintf( '<optgroup label="%s">%s</optgroup>', esc_attr( $_label ), $_options );
184
  }
185
+ HTML::wrap_fields(
186
  vsprintf(
187
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
188
  <p><select name="%3$s" id="%1$s">%4$s</select></p>
189
  <p class=description>%6$s</p>',
190
  [
191
+ Form::get_field_id( 'max_snippet_length' ),
192
  esc_html__( 'Maximum text snippet length', 'autodescription' ),
193
+ Form::get_field_name( 'max_snippet_length' ),
194
  $text_snippet_options,
195
+ HTML::make_info(
196
  __( 'This may limit the text snippet length for all pages on this site.', 'autodescription' ),
197
  '',
198
  false
220
  ]
221
  );
222
  }
223
+ HTML::wrap_fields(
224
  vsprintf(
225
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
226
  <p><select name="%3$s" id="%1$s">%4$s</select></p>',
227
  [
228
+ Form::get_field_id( 'max_image_preview' ),
229
  esc_html__( 'Maximum image preview size', 'autodescription' ),
230
+ Form::get_field_name( 'max_image_preview' ),
231
  $image_preview_options,
232
+ HTML::make_info(
233
  __( 'This may limit the image preview size for all images from this site.', 'autodescription' ),
234
  '',
235
  false
268
 
269
  $video_preview_options .= sprintf( '<optgroup label="%s">%s</optgroup>', esc_attr( $_label ), $_options );
270
  }
271
+ HTML::wrap_fields(
272
  vsprintf(
273
  '<p><label for="%1$s"><strong>%2$s</strong> %5$s</label></p>
274
  <p><select name="%3$s" id="%1$s">%4$s</select></p>',
275
  [
276
+ Form::get_field_id( 'max_video_preview' ),
277
  esc_html__( 'Maximum video preview length', 'autodescription' ),
278
+ Form::get_field_name( 'max_video_preview' ),
279
  $video_preview_options,
280
+ HTML::make_info(
281
  __( 'This may limit the video preview length for all videos on this site.', 'autodescription' ),
282
  '',
283
  false
297
  /* translators: PLURAL. 1 = noindex/nofollow/noarchive, 2 = Archives, Posts, Pages, etc. */
298
  $apply_x_to_y_i18n_plural = esc_html_x( 'Apply %1$s to %2$s?', 'plural', 'autodescription' );
299
 
300
+ $ro_name_wrapped = HTML::code_wrap( $ro_value );
301
 
302
  $default_options = $this->get_default_site_options();
303
  $warned_options = $this->get_warned_site_options();
304
 
305
+ Form::header_title( __( 'Robots Settings', 'autodescription' ) );
306
+ HTML::description( $ro_i18n );
 
 
307
  ?>
308
  <hr>
 
 
309
  <?php
310
+ Form::header_title( __( 'Post Type Settings', 'autodescription' ) );
311
+ HTML::description( __( 'These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect.', 'autodescription' ) );
312
 
313
  $pt_option_id = $this->get_robots_post_type_option_id( $ro_value );
314
 
315
  // When the post OR page post types are available, show this warning.
316
  if ( in_array( $ro_value, [ 'noindex', 'nofollow' ], true ) && array_intersect( $post_types, [ 'post', 'page' ] ) )
317
+ HTML::attention_description( __( 'Warning: No site should enable these options for Posts and Pages.', 'autodescription' ) );
318
 
319
  // TODO can we assume that there's at least one post type at all times? Can WP be used in this way, albeit headless?
320
  $checkboxes = [];
321
 
322
  foreach ( $post_types as $post_type ) {
323
+ $checkboxes[] = Form::make_checkbox( [
324
  'id' => $pt_option_id,
325
  'class' => 'tsf-robots-post-types',
326
  'index' => $post_type,
340
  ] );
341
  }
342
 
343
+ HTML::wrap_fields( $checkboxes, true );
344
 
345
  ?>
346
  <hr>
 
 
347
  <?php
348
+ Form::header_title( __( 'Taxonomy Settings', 'autodescription' ) );
349
+ HTML::description( __( "These settings apply to the taxonomies of post types. When taxonomies have all their bound post types' options checked, they will inherit their status.", 'autodescription' ) );
350
 
351
  $tax_option_id = $this->get_robots_taxonomy_option_id( $ro_value );
352
 
354
  $checkboxes = [];
355
 
356
  foreach ( $taxonomies as $taxonomy ) {
357
+ $checkboxes[] = Form::make_checkbox( [
358
  'id' => $tax_option_id,
359
  'class' => 'tsf-robots-taxonomies',
360
  'index' => $taxonomy,
375
  ] );
376
  }
377
 
378
+ HTML::wrap_fields( $checkboxes, true );
379
 
380
  ?>
381
  <hr>
 
 
382
  <?php
383
+ Form::header_title( __( 'Global Settings', 'autodescription' ) );
384
+ HTML::description( __( 'These settings apply to other globally registered content types.', 'autodescription' ) );
385
 
386
  $checkboxes = '';
387
  foreach ( $global_types as $type => $data ) {
405
  );
406
  }
407
 
408
+ $checkboxes .= Form::make_checkbox( [
409
+ 'id' => $id,
410
+ 'label' => $label,
411
+ 'escape' => false,
412
+ ] );
413
  }
414
 
415
+ HTML::wrap_fields( $checkboxes, true );
416
  break;
417
 
418
  default:
inc/views/admin/metaboxes/schema-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -16,16 +18,14 @@ $instance = $this->get_view_instance( 'the_seo_framework_schema_metabox', $insta
16
 
17
  switch ( $instance ) :
18
  case 'the_seo_framework_schema_metabox_main':
19
- ?>
20
- <h4><?php esc_html_e( 'Schema.org Output Settings', 'autodescription' ); ?></h4>
21
- <?php
22
 
23
  if ( $this->has_json_ld_plugin() )
24
- $this->attention_description( __( 'Another Schema.org plugin has been detected. These markup settings might conflict.', 'autodescription' ) );
25
 
26
- $this->description( __( 'The Schema.org markup is a standard way of annotating structured data for search engines. This markup is represented within hidden scripts throughout the website.', 'autodescription' ) );
27
- $this->description( __( 'When your web pages include structured data markup, search engines can use that data to index your content better, present it more prominently in search results, and use it in several different applications.', 'autodescription' ) );
28
- $this->description( __( 'This is also known as the "Knowledge Graph" and "Structured Data", which is under heavy active development by several search engines. Therefore, the usage of the outputted markup is not guaranteed.', 'autodescription' ) );
29
 
30
  $default_tabs = [
31
  'structure' => [
@@ -53,29 +53,25 @@ switch ( $instance ) :
53
  break;
54
 
55
  case 'the_seo_framework_schema_metabox_structure':
 
 
 
56
  ?>
57
- <h4><?php esc_html_e( 'Site Structure Options', 'autodescription' ); ?></h4>
58
- <?php
59
- $this->description( __( 'The site structure Schema.org output allows search engines to gain knowledge on how your website is built.', 'autodescription' ) );
60
- $this->description( __( "For example, search engines display your pages' URLs when listed in the search results. These options allow you to enhance those URLs output.", 'autodescription' ) );
61
- ?>
62
- <hr>
63
- <h4><?php esc_html_e( 'Breadcrumbs', 'autodescription' ); ?></h4>
64
- <?php
65
- $this->description( __( "Breadcrumb trails indicate page positions in the site's hierarchy. Using the following option will show the hierarchy within the search results when available.", 'autodescription' ) );
66
 
67
- $info = $this->make_info(
68
  __( 'Learn how this data is used.', 'autodescription' ),
69
  'https://developers.google.com/search/docs/data-types/breadcrumb',
70
  false
71
  );
72
- $this->wrap_fields(
73
- $this->make_checkbox(
74
- 'ld_json_breadcrumbs',
75
- esc_html__( 'Enable Breadcrumbs?', 'autodescription' ) . ' ' . $info,
76
- '',
77
- false
78
- ),
79
  true
80
  );
81
 
@@ -83,52 +79,48 @@ switch ( $instance ) :
83
  <hr>
84
  <h4><?php echo esc_html( _x( 'Sitelinks Searchbox', 'Product name', 'autodescription' ) ); ?></h4>
85
  <?php
86
- $this->description( __( 'When Search users search for your brand name, the following option allows them to search through this website directly from the search results.', 'autodescription' ) );
87
 
88
- $info = $this->make_info(
89
  __( 'Learn how this data is used.', 'autodescription' ),
90
  'https://developers.google.com/search/docs/data-types/sitelinks-searchbox',
91
  false
92
  );
93
- $this->wrap_fields(
94
- $this->make_checkbox(
95
- 'ld_json_searchbox',
96
- esc_html_x( 'Enable Sitelinks Searchbox?', 'Sitelinks Searchbox is a Product name', 'autodescription' ) . ' ' . $info,
97
- '',
98
- false
99
- ),
100
  true
101
  );
102
  break;
103
 
104
  case 'the_seo_framework_schema_metabox_presence':
105
- ?>
106
- <h4><?php esc_html_e( 'Authorized Presence Options', 'autodescription' ); ?></h4>
107
- <?php
108
- $this->description( __( 'The authorized presence Schema.org output helps search engine users find ways to interact with this website.', 'autodescription' ) );
109
 
110
- $info = $this->make_info(
111
  __( 'Learn how this data is used.', 'autodescription' ),
112
  'https://developers.google.com/search/docs/guides/enhance-site#add-your-sites-name-logo-and-social-links',
113
  false
114
  );
115
  // Echo checkbox.
116
- $this->wrap_fields(
117
- $this->make_checkbox(
118
- 'knowledge_output',
119
- esc_html__( 'Output Authorized Presence?', 'autodescription' ) . ' ' . $info,
120
- '',
121
- false
122
- ),
123
  true
124
  );
125
  ?>
126
  <hr>
127
 
128
- <h4><?php esc_html_e( 'About this website', 'autodescription' ); ?></h4>
129
  <p>
130
- <label for="<?php $this->field_id( 'knowledge_type' ); ?>"><?php echo esc_html_x( 'This website represents:', '...Organization or Person.', 'autodescription' ); ?></label>
131
- <select name="<?php $this->field_name( 'knowledge_type' ); ?>" id="<?php $this->field_id( 'knowledge_type' ); ?>">
132
  <?php
133
  $knowledge_type = (array) apply_filters(
134
  'the_seo_framework_knowledge_types',
@@ -145,30 +137,28 @@ switch ( $instance ) :
145
  </p>
146
 
147
  <p>
148
- <label for="<?php $this->field_id( 'knowledge_name' ); ?>">
149
  <strong><?php esc_html_e( 'The organization or personal name', 'autodescription' ); ?></strong>
150
  </label>
151
  </p>
152
  <p>
153
- <input type="text" name="<?php $this->field_name( 'knowledge_name' ); ?>" class="large-text" id="<?php $this->field_id( 'knowledge_name' ); ?>" placeholder="<?php echo esc_attr( $this->get_blogname() ); ?>" value="<?php echo esc_attr( $this->get_option( 'knowledge_name' ) ); ?>" autocomplete=off />
154
  </p>
155
  <hr>
156
-
157
- <h4><?php esc_html_e( 'Website logo', 'autodescription' ); ?></h4>
158
  <?php
159
- $this->description( esc_html__( 'These options are used when this site represents an organization. When no logo is outputted, search engine will look elsewhere.', 'autodescription' ) );
160
- $info = $this->make_info(
 
161
  __( 'Learn how this data is used.', 'autodescription' ),
162
  'https://developers.google.com/search/docs/data-types/logo',
163
  false
164
  );
165
- $this->wrap_fields(
166
- $this->make_checkbox(
167
- 'knowledge_logo',
168
- esc_html__( 'Enable logo?', 'autodescription' ) . ' ' . $info,
169
- '',
170
- false
171
- ),
172
  true );
173
 
174
  $logo_placeholder = $this->get_knowledge_logo( false );
@@ -180,13 +170,27 @@ switch ( $instance ) :
180
  </p>
181
  <p>
182
  <span class="hide-if-tsf-js attention"><?php esc_html_e( 'Setting a logo requires JavaScript.', 'autodescription' ); ?></span>
183
- <input class="large-text" type="url" readonly="readonly" data-readonly="1" name="<?php $this->field_name( 'knowledge_logo_url' ); ?>" id="knowledge_logo-url" placeholder="<?php echo esc_url( $logo_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'knowledge_logo_url' ) ); ?>" />
184
- <input type="hidden" name="<?php $this->field_name( 'knowledge_logo_id' ); ?>" id="knowledge_logo-id" value="<?php echo absint( $this->get_option( 'knowledge_logo_id' ) ); ?>" />
185
  </p>
186
  <p class="hide-if-no-tsf-js">
187
  <?php
188
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
189
- echo $this->get_logo_uploader_form( 'knowledge_logo' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  ?>
191
  </p>
192
  <?php
@@ -265,25 +269,24 @@ switch ( $instance ) :
265
  ],
266
  ];
267
 
268
- $output_social_precense = false;
269
 
270
  foreach ( $socialsites as $key => $v ) {
271
  if ( strlen( $this->get_option( $v['option'] ) ) ) {
272
- $output_social_precense = true;
273
  break;
274
  }
275
  }
276
 
277
- if ( $output_social_precense ) :
278
  ?>
279
  <hr>
280
-
281
- <h4><?php esc_html_e( 'Connected Social Pages', 'autodescription' ); ?></h4>
282
  <?php
283
- $this->description( __( "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway.", 'autodescription' ) );
284
- $this->description( __( 'Add links that lead directly to the connected social pages of this website.', 'autodescription' ) );
285
- $this->description( __( 'These settings do not affect sharing behavior with the social networks.', 'autodescription' ) );
286
- $this->attention_description_noesc(
 
287
  $this->convert_markdown(
288
  sprintf(
289
  /* translators: %s = Learn more URL. Markdown! */
@@ -301,11 +304,11 @@ switch ( $instance ) :
301
 
302
  ?>
303
  <p>
304
- <label for="<?php $this->field_id( $v['option'] ); ?>">
305
  <strong><?php echo esc_html( $v['desc'] ); ?></strong>
306
  <?php
307
  if ( $v['examplelink'] ) {
308
- $this->make_info(
309
  __( 'View your profile.', 'autodescription' ),
310
  $v['examplelink']
311
  );
@@ -314,11 +317,11 @@ switch ( $instance ) :
314
  </label>
315
  </p>
316
  <p>
317
- <input type="url" name="<?php $this->field_name( $v['option'] ); ?>" class="large-text" id="<?php $this->field_id( $v['option'] ); ?>" placeholder="<?php echo esc_attr( $v['placeholder'] ); ?>" value="<?php echo esc_attr( $this->get_option( $v['option'] ) ); ?>" autocomplete=off />
318
  </p>
319
  <?php
320
  }
321
- endif; /* end $output_social_precense */
322
  break;
323
 
324
  default:
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
18
 
19
  switch ( $instance ) :
20
  case 'the_seo_framework_schema_metabox_main':
21
+ Form::header_title( __( 'Schema.org Output Settings', 'autodescription' ) );
 
 
22
 
23
  if ( $this->has_json_ld_plugin() )
24
+ HTML::attention_description( __( 'Another Schema.org plugin has been detected. These markup settings might conflict.', 'autodescription' ) );
25
 
26
+ HTML::description( __( 'The Schema.org markup is a standard way of annotating structured data for search engines. This markup is represented within hidden scripts throughout the website.', 'autodescription' ) );
27
+ HTML::description( __( 'When your web pages include structured data markup, search engines can use that data to index your content better, present it more prominently in search results, and use it in several different applications.', 'autodescription' ) );
28
+ HTML::description( __( 'This is also known as the "Knowledge Graph" and "Structured Data", which is under heavy active development by several search engines. Therefore, the usage of the outputted markup is not guaranteed.', 'autodescription' ) );
29
 
30
  $default_tabs = [
31
  'structure' => [
53
  break;
54
 
55
  case 'the_seo_framework_schema_metabox_structure':
56
+ Form::header_title( __( 'Site Structure Options', 'autodescription' ) );
57
+ HTML::description( __( 'The site structure Schema.org output allows search engines to gain knowledge on how your website is built.', 'autodescription' ) );
58
+ HTML::description( __( "For example, search engines display your pages' URLs when listed in the search results. These options allow you to enhance those URLs output.", 'autodescription' ) );
59
  ?>
60
+ <hr> <?php
61
+ Form::header_title( __( 'Breadcrumbs', 'autodescription' ) );
62
+ HTML::description( __( "Breadcrumb trails indicate page positions in the site's hierarchy. Using the following option will show the hierarchy within the search results when available.", 'autodescription' ) );
 
 
 
 
 
 
63
 
64
+ $info = HTML::make_info(
65
  __( 'Learn how this data is used.', 'autodescription' ),
66
  'https://developers.google.com/search/docs/data-types/breadcrumb',
67
  false
68
  );
69
+ HTML::wrap_fields(
70
+ Form::make_checkbox( [
71
+ 'id' => 'ld_json_breadcrumbs',
72
+ 'label' => esc_html__( 'Enable Breadcrumbs?', 'autodescription' ) . ' ' . $info,
73
+ 'escape' => false,
74
+ ] ),
 
75
  true
76
  );
77
 
79
  <hr>
80
  <h4><?php echo esc_html( _x( 'Sitelinks Searchbox', 'Product name', 'autodescription' ) ); ?></h4>
81
  <?php
82
+ HTML::description( __( 'When Search users search for your brand name, the following option allows them to search through this website directly from the search results.', 'autodescription' ) );
83
 
84
+ $info = HTML::make_info(
85
  __( 'Learn how this data is used.', 'autodescription' ),
86
  'https://developers.google.com/search/docs/data-types/sitelinks-searchbox',
87
  false
88
  );
89
+ HTML::wrap_fields(
90
+ Form::make_checkbox( [
91
+ 'id' => 'ld_json_searchbox',
92
+ 'label' => esc_html_x( 'Enable Sitelinks Searchbox?', 'Sitelinks Searchbox is a Product name', 'autodescription' ) . ' ' . $info,
93
+ 'escape' => false,
94
+ ] ),
 
95
  true
96
  );
97
  break;
98
 
99
  case 'the_seo_framework_schema_metabox_presence':
100
+ Form::header_title( __( 'Authorized Presence Options', 'autodescription' ) );
101
+ HTML::description( __( 'The authorized presence Schema.org output helps search engine users find ways to interact with this website.', 'autodescription' ) );
 
 
102
 
103
+ $info = HTML::make_info(
104
  __( 'Learn how this data is used.', 'autodescription' ),
105
  'https://developers.google.com/search/docs/guides/enhance-site#add-your-sites-name-logo-and-social-links',
106
  false
107
  );
108
  // Echo checkbox.
109
+ HTML::wrap_fields(
110
+ Form::make_checkbox( [
111
+ 'id' => 'knowledge_output',
112
+ 'label' => esc_html__( 'Output Authorized Presence?', 'autodescription' ) . ' ' . $info,
113
+ 'escape' => false,
114
+ ] ),
 
115
  true
116
  );
117
  ?>
118
  <hr>
119
 
120
+ <?php Form::header_title( __( 'About this website', 'autodescription' ) ); ?>
121
  <p>
122
+ <label for="<?php Form::field_id( 'knowledge_type' ); ?>"><?php echo esc_html_x( 'This website represents:', '...Organization or Person.', 'autodescription' ); ?></label>
123
+ <select name="<?php Form::field_name( 'knowledge_type' ); ?>" id="<?php Form::field_id( 'knowledge_type' ); ?>">
124
  <?php
125
  $knowledge_type = (array) apply_filters(
126
  'the_seo_framework_knowledge_types',
137
  </p>
138
 
139
  <p>
140
+ <label for="<?php Form::field_id( 'knowledge_name' ); ?>">
141
  <strong><?php esc_html_e( 'The organization or personal name', 'autodescription' ); ?></strong>
142
  </label>
143
  </p>
144
  <p>
145
+ <input type="text" name="<?php Form::field_name( 'knowledge_name' ); ?>" class="large-text" id="<?php Form::field_id( 'knowledge_name' ); ?>" placeholder="<?php echo esc_attr( $this->get_blogname() ); ?>" value="<?php echo esc_attr( $this->get_option( 'knowledge_name' ) ); ?>" autocomplete=off />
146
  </p>
147
  <hr>
 
 
148
  <?php
149
+ Form::header_title( __( 'Website logo', 'autodescription' ) );
150
+ HTML::description( esc_html__( 'These options are used when this site represents an organization. When no logo is outputted, search engine will look elsewhere.', 'autodescription' ) );
151
+ $info = HTML::make_info(
152
  __( 'Learn how this data is used.', 'autodescription' ),
153
  'https://developers.google.com/search/docs/data-types/logo',
154
  false
155
  );
156
+ HTML::wrap_fields(
157
+ Form::make_checkbox( [
158
+ 'id' => 'knowledge_logo',
159
+ 'label' => esc_html__( 'Enable logo?', 'autodescription' ) . ' ' . $info,
160
+ 'escape' => false,
161
+ ] ),
 
162
  true );
163
 
164
  $logo_placeholder = $this->get_knowledge_logo( false );
170
  </p>
171
  <p>
172
  <span class="hide-if-tsf-js attention"><?php esc_html_e( 'Setting a logo requires JavaScript.', 'autodescription' ); ?></span>
173
+ <input class="large-text" type="url" readonly="readonly" data-readonly="1" name="<?php Form::field_name( 'knowledge_logo_url' ); ?>" id="knowledge_logo-url" placeholder="<?php echo esc_url( $logo_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'knowledge_logo_url' ) ); ?>" />
174
+ <input type="hidden" name="<?php Form::field_name( 'knowledge_logo_id' ); ?>" id="knowledge_logo-id" value="<?php echo absint( $this->get_option( 'knowledge_logo_id' ) ); ?>" />
175
  </p>
176
  <p class="hide-if-no-tsf-js">
177
  <?php
178
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
179
+ echo Form::get_image_uploader_form( [
180
+ 'id' => 'sitemap_logo',
181
+ 'data' => [
182
+ 'inputType' => 'logo',
183
+ 'width' => 512,
184
+ 'height' => 512,
185
+ 'minWidth' => 112,
186
+ 'minHeight' => 112,
187
+ 'flex' => true,
188
+ ],
189
+ 'i18n' => [
190
+ 'button_title' => '',
191
+ 'button_text' => __( 'Select Logo', 'autodescription' ),
192
+ ],
193
+ ] );
194
  ?>
195
  </p>
196
  <?php
269
  ],
270
  ];
271
 
272
+ $output_social_presence = false;
273
 
274
  foreach ( $socialsites as $key => $v ) {
275
  if ( strlen( $this->get_option( $v['option'] ) ) ) {
276
+ $output_social_presence = true;
277
  break;
278
  }
279
  }
280
 
281
+ if ( $output_social_presence ) :
282
  ?>
283
  <hr>
 
 
284
  <?php
285
+ Form::header_title( __( 'Connected Social Pages', 'autodescription' ) );
286
+ HTML::description( __( "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway.", 'autodescription' ) );
287
+ HTML::description( __( 'Add links that lead directly to the connected social pages of this website.', 'autodescription' ) );
288
+ HTML::description( __( 'These settings do not affect sharing behavior with the social networks.', 'autodescription' ) );
289
+ HTML::attention_description_noesc(
290
  $this->convert_markdown(
291
  sprintf(
292
  /* translators: %s = Learn more URL. Markdown! */
304
 
305
  ?>
306
  <p>
307
+ <label for="<?php Form::field_id( $v['option'] ); ?>">
308
  <strong><?php echo esc_html( $v['desc'] ); ?></strong>
309
  <?php
310
  if ( $v['examplelink'] ) {
311
+ HTML::make_info(
312
  __( 'View your profile.', 'autodescription' ),
313
  $v['examplelink']
314
  );
317
  </label>
318
  </p>
319
  <p>
320
+ <input type="url" name="<?php Form::field_name( $v['option'] ); ?>" class="large-text" id="<?php Form::field_id( $v['option'] ); ?>" placeholder="<?php echo esc_attr( $v['placeholder'] ); ?>" value="<?php echo esc_attr( $this->get_option( $v['option'] ) ); ?>" autocomplete=off />
321
  </p>
322
  <?php
323
  }
324
+ endif; /* end $output_social_presence */
325
  break;
326
 
327
  default:
inc/views/admin/metaboxes/sitemaps-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -61,12 +63,10 @@ switch ( $instance ) :
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! */
@@ -80,36 +80,34 @@ switch ( $instance ) :
80
 
81
  if ( $has_sitemap_plugin ) :
82
  echo '<hr>';
83
- $this->attention_description( __( 'Note: Another active sitemap plugin has been detected. This means that the sitemap functionality has been superseded and these settings have no effect.', 'autodescription' ) );
84
  elseif ( $sitemap_detected ) :
85
  echo '<hr>';
86
- $this->attention_description( __( 'Note: A sitemap has been detected in the root folder of your website. This means that these settings have no effect.', 'autodescription' ) );
87
  endif;
88
  ?>
89
  <hr>
90
-
91
- <h4><?php esc_html_e( 'Sitemap Output', 'autodescription' ); ?></h4>
92
  <?php
 
93
 
94
  // Echo checkbox.
95
- $this->wrap_fields(
96
- $this->make_checkbox(
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
  ),
104
- '',
105
- false
106
- ),
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' ] ),
@@ -121,7 +119,7 @@ switch ( $instance ) :
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' ] ),
@@ -135,54 +133,52 @@ switch ( $instance ) :
135
  <hr>
136
 
137
  <p>
138
- <label for="<?php $this->field_id( 'sitemap_query_limit' ); ?>">
139
  <strong><?php esc_html_e( 'Sitemap Query Limit', 'autodescription' ); ?></strong>
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
  ?>
147
- <input type=hidden name="<?php $this->field_name( 'sitemap_query_limit' ); ?>" value="<?php echo absint( $this->get_sitemap_post_limit() ); ?>">
148
  <p>
149
- <input type="number" id="<?php $this->field_id( 'sitemap_query_limit' ); ?>" value="<?php echo absint( $this->get_sitemap_post_limit() ); ?>" disabled />
150
  </p>
151
  <?php
152
  else :
153
  ?>
154
  <p>
155
- <input type="number" min=1 max=50000 name="<?php $this->field_name( 'sitemap_query_limit' ); ?>" id="<?php $this->field_id( 'sitemap_query_limit' ); ?>" placeholder="<?php echo absint( $this->get_default_option( 'sitemap_query_limit' ) ); ?>" value="<?php echo absint( $this->get_option( 'sitemap_query_limit' ) ); ?>" />
156
  </p>
157
  <?php
158
  endif;
159
- $this->description( __( 'Consider lowering this value when the sitemap shows a white screen or notifies you of memory exhaustion.', 'autodescription' ) );
160
  break;
161
 
162
  case 'the_seo_framework_sitemaps_metabox_robots':
163
  $show_settings = true;
164
  $robots_url = $this->get_robots_txt_url();
165
 
166
- ?>
167
- <h4><?php esc_html_e( 'Robots.txt Settings', 'autodescription' ); ?></h4>
168
- <?php
169
 
170
  if ( $this->has_robots_txt() ) :
171
- $this->attention_description(
172
  __( 'Note: A robots.txt file has been detected in the root folder of your website. This means these settings have no effect.', 'autodescription' )
173
  );
174
  echo '<hr>';
175
  elseif ( ! $robots_url ) :
176
  if ( $this->is_subdirectory_installation() ) {
177
- $this->attention_description(
178
  __( "Note: robots.txt files can't be generated or used on subdirectory installations.", 'autodescription' )
179
  );
180
  echo '<hr>';
181
  } elseif ( ! $this->pretty_permalinks ) {
182
- $this->attention_description(
183
  __( "Note: You're using the plain permalink structure; so, no robots.txt file can be generated.", 'autodescription' )
184
  );
185
- $this->description_noesc(
186
  $this->convert_markdown(
187
  sprintf(
188
  /* translators: 1 = Link to settings, Markdown. 2 = example input, also markdown! Preserve the Markdown as-is! */
@@ -198,8 +194,8 @@ switch ( $instance ) :
198
  }
199
  endif;
200
 
201
- $this->description( __( '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.', 'autodescription' ) );
202
- $this->description( __( '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.', 'autodescription' ) );
203
 
204
  echo '<hr>';
205
 
@@ -208,13 +204,11 @@ switch ( $instance ) :
208
  '<h4>%s</h4>',
209
  esc_html__( 'Sitemap Hinting', 'autodescription' )
210
  );
211
- $this->wrap_fields(
212
- $this->make_checkbox(
213
- 'sitemaps_robots',
214
- esc_html__( 'Add sitemap location to robots.txt?', 'autodescription' ),
215
- '',
216
- false
217
- ),
218
  true
219
  );
220
  endif;
@@ -222,7 +216,7 @@ switch ( $instance ) :
222
  $robots_url = $this->get_robots_txt_url();
223
 
224
  if ( $robots_url ) {
225
- $this->description_noesc(
226
  sprintf(
227
  '<a href="%s" target=_blank rel=noopener>%s</a>',
228
  esc_url( $robots_url, [ 'https', 'http' ] ),
@@ -233,91 +227,82 @@ switch ( $instance ) :
233
  break;
234
 
235
  case 'the_seo_framework_sitemaps_metabox_metadata':
236
- ?>
237
- <h4><?php esc_html_e( 'Timestamps Settings', 'autodescription' ); ?></h4>
238
- <?php
239
- $this->description( __( 'The modified time suggests to search engines where to look for content changes first.', 'autodescription' ) );
240
 
241
  // Echo checkbox.
242
- $this->wrap_fields(
243
- $this->make_checkbox(
244
- 'sitemaps_modified',
245
- $this->convert_markdown(
246
  /* translators: the backticks are Markdown! Preserve them as-is! */
247
  esc_html__( 'Add `<lastmod>` to the sitemap?', 'autodescription' ),
248
  [ 'code' ]
249
  ),
250
- '',
251
- false
252
- ),
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':
282
- ?>
283
- <h4><?php esc_html_e( 'Ping Settings', 'autodescription' ); ?></h4>
284
- <?php
285
- $this->description( __( 'Notifying search engines of a sitemap change is helpful to get your content indexed as soon as possible.', 'autodescription' ) );
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
 
316
  ?>
317
  <hr>
318
-
319
- <h4><?php esc_html_e( 'Notify Search Engines', 'autodescription' ); ?></h4>
320
  <?php
 
321
 
322
  $engines = [
323
  'ping_google' => 'Google',
@@ -329,32 +314,31 @@ switch ( $instance ) :
329
  foreach ( $engines as $option => $engine ) {
330
  /* translators: %s = Google */
331
  $ping_label = sprintf( __( 'Notify %s about sitemap changes?', 'autodescription' ), $engine );
332
- $ping_checkbox .= $this->make_checkbox( $option, $ping_label, '', true );
 
 
 
333
  }
334
 
335
  // Echo checkbox.
336
- $this->wrap_fields( $ping_checkbox, true );
337
  break;
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' ) );
345
  ?>
346
  <hr>
347
-
348
- <h4><?php esc_html_e( 'Enable Styling', 'autodescription' ); ?></h4>
349
  <?php
350
-
351
- $this->wrap_fields(
352
- $this->make_checkbox(
353
- 'sitemap_styles',
354
- esc_html__( 'Style sitemap?', 'autodescription' ) . ' ' . $this->make_info( __( 'This makes the sitemap more readable for humans.', 'autodescription' ), '', false ),
355
- '',
356
- false
357
- ),
358
  true
359
  );
360
 
@@ -367,35 +351,32 @@ switch ( $instance ) :
367
 
368
  ?>
369
  <p>
370
- <label for="<?php $this->field_id( 'sitemap_color_main' ); ?>">
371
  <strong><?php esc_html_e( 'Sitemap Header Background Color', 'autodescription' ); ?></strong>
372
  </label>
373
  </p>
374
  <p>
375
- <input type="text" name="<?php $this->field_name( 'sitemap_color_main' ); ?>" class="tsf-color-picker" id="<?php $this->field_id( 'sitemap_color_main' ); ?>" placeholder="<?php echo esc_attr( $default_colors['main'] ); ?>" value="<?php echo esc_attr( $current_colors['main'] ); ?>" data-tsf-default-color="<?php echo esc_attr( $default_colors['main'] ); ?>" />
376
  </p>
377
 
378
  <p>
379
- <label for="<?php $this->field_id( 'sitemap_color_accent' ); ?>">
380
  <strong><?php esc_html_e( 'Sitemap Title and Lines Color', 'autodescription' ); ?></strong>
381
  </label>
382
  </p>
383
  <p>
384
- <input type="text" name="<?php $this->field_name( 'sitemap_color_accent' ); ?>" class="tsf-color-picker" id="<?php $this->field_id( 'sitemap_color_accent' ); ?>" placeholder="<?php echo esc_attr( $default_colors['accent'] ); ?>" value="<?php echo esc_attr( $current_colors['accent'] ); ?>" data-tsf-default-color="<?php echo esc_attr( $default_colors['accent'] ); ?>" />
385
  </p>
386
 
387
  <hr>
388
-
389
- <h4><?php esc_html_e( 'Header Title Logo', 'autodescription' ); ?></h4>
390
  <?php
 
391
 
392
- $this->wrap_fields(
393
- $this->make_checkbox(
394
- 'sitemap_logo',
395
- __( 'Show logo next to sitemap header title?', 'autodescription' ),
396
- '',
397
- true
398
- ),
399
  true
400
  );
401
 
@@ -412,13 +393,27 @@ switch ( $instance ) :
412
  </p>
413
  <p>
414
  <span class="hide-if-tsf-js attention"><?php esc_html_e( 'Setting a logo requires JavaScript.', 'autodescription' ); ?></span>
415
- <input class="large-text" type="url" readonly="readonly" data-readonly="1" name="<?php $this->field_name( 'sitemap_logo_url' ); ?>" id="sitemap_logo-url" placeholder="<?php echo esc_url( $logo_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'sitemap_logo_url' ) ); ?>" />
416
- <input type="hidden" name="<?php $this->field_name( 'sitemap_logo_id' ); ?>" id="sitemap_logo-id" value="<?php echo absint( $this->get_option( 'sitemap_logo_id' ) ); ?>" />
417
  </p>
418
  <p class="hide-if-no-tsf-js">
419
  <?php
420
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
421
- echo $this->get_logo_uploader_form( 'sitemap_logo' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
  ?>
423
  </p>
424
  <?php
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
63
  $use_core_sitemaps = $this->use_core_sitemaps();
64
  $sitemap_detected = $this->has_sitemap_xml();
65
 
66
+ Form::header_title( __( 'Sitemap Integration Settings', 'autodescription' ) );
67
+ HTML::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
+ HTML::description_noesc(
70
  $this->convert_markdown(
71
  sprintf(
72
  /* translators: %s = Learn more URL. Markdown! */
80
 
81
  if ( $has_sitemap_plugin ) :
82
  echo '<hr>';
83
+ HTML::attention_description( __( 'Note: Another active sitemap plugin has been detected. This means that the sitemap functionality has been superseded and these settings have no effect.', 'autodescription' ) );
84
  elseif ( $sitemap_detected ) :
85
  echo '<hr>';
86
+ HTML::attention_description( __( 'Note: A sitemap has been detected in the root folder of your website. This means that these settings have no effect.', 'autodescription' ) );
87
  endif;
88
  ?>
89
  <hr>
 
 
90
  <?php
91
+ Form::header_title( __( 'Sitemap Output', 'autodescription' ) );
92
 
93
  // Echo checkbox.
94
+ HTML::wrap_fields(
95
+ Form::make_checkbox( [
96
+ 'id' => 'sitemaps_output',
97
+ 'label' => esc_html__( 'Output optimized sitemap?', 'autodescription' )
98
+ . ' ' . HTML::make_info(
99
  __( 'This sitemap is processed quicker by search engines.', 'autodescription' ),
100
  '',
101
  false
102
  ),
103
+ 'escape' => false,
104
+ ] ),
 
105
  true
106
  );
107
 
108
  if ( ! $has_sitemap_plugin && ! $sitemap_detected ) {
109
  if ( $this->get_option( 'sitemaps_output' ) ) {
110
+ HTML::description_noesc(
111
  sprintf(
112
  '<a href="%s" target=_blank rel=noopener>%s</a>',
113
  esc_url( The_SEO_Framework\Bridges\Sitemap::get_instance()->get_expected_sitemap_endpoint_url(), [ 'https', 'http' ] ),
119
  } elseif ( $use_core_sitemaps ) {
120
  $_index_url = get_sitemap_url( 'index' );
121
  if ( $_index_url )
122
+ HTML::description_noesc(
123
  sprintf(
124
  '<a href="%s" target=_blank rel=noopener>%s</a>',
125
  esc_url( $_index_url, [ 'https', 'http' ] ),
133
  <hr>
134
 
135
  <p>
136
+ <label for="<?php Form::field_id( 'sitemap_query_limit' ); ?>">
137
  <strong><?php esc_html_e( 'Sitemap Query Limit', 'autodescription' ); ?></strong>
138
  </label>
139
  </p>
140
  <?php
141
+ HTML::description( __( 'This setting affects how many pages are requested from the database per query.', 'autodescription' ) );
142
 
143
  if ( has_filter( 'the_seo_framework_sitemap_post_limit' ) ) :
144
  ?>
145
+ <input type=hidden name="<?php Form::field_name( 'sitemap_query_limit' ); ?>" value="<?php echo absint( $this->get_sitemap_post_limit() ); ?>">
146
  <p>
147
+ <input type="number" id="<?php Form::field_id( 'sitemap_query_limit' ); ?>" value="<?php echo absint( $this->get_sitemap_post_limit() ); ?>" disabled />
148
  </p>
149
  <?php
150
  else :
151
  ?>
152
  <p>
153
+ <input type="number" min=1 max=50000 name="<?php Form::field_name( 'sitemap_query_limit' ); ?>" id="<?php Form::field_id( 'sitemap_query_limit' ); ?>" placeholder="<?php echo absint( $this->get_default_option( 'sitemap_query_limit' ) ); ?>" value="<?php echo absint( $this->get_option( 'sitemap_query_limit' ) ); ?>" />
154
  </p>
155
  <?php
156
  endif;
157
+ HTML::description( __( 'Consider lowering this value when the sitemap shows a white screen or notifies you of memory exhaustion.', 'autodescription' ) );
158
  break;
159
 
160
  case 'the_seo_framework_sitemaps_metabox_robots':
161
  $show_settings = true;
162
  $robots_url = $this->get_robots_txt_url();
163
 
164
+ Form::header_title( __( 'Robots.txt Settings', 'autodescription' ) );
 
 
165
 
166
  if ( $this->has_robots_txt() ) :
167
+ HTML::attention_description(
168
  __( 'Note: A robots.txt file has been detected in the root folder of your website. This means these settings have no effect.', 'autodescription' )
169
  );
170
  echo '<hr>';
171
  elseif ( ! $robots_url ) :
172
  if ( $this->is_subdirectory_installation() ) {
173
+ HTML::attention_description(
174
  __( "Note: robots.txt files can't be generated or used on subdirectory installations.", 'autodescription' )
175
  );
176
  echo '<hr>';
177
  } elseif ( ! $this->pretty_permalinks ) {
178
+ HTML::attention_description(
179
  __( "Note: You're using the plain permalink structure; so, no robots.txt file can be generated.", 'autodescription' )
180
  );
181
+ HTML::description_noesc(
182
  $this->convert_markdown(
183
  sprintf(
184
  /* translators: 1 = Link to settings, Markdown. 2 = example input, also markdown! Preserve the Markdown as-is! */
194
  }
195
  endif;
196
 
197
+ HTML::description( __( '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.', 'autodescription' ) );
198
+ HTML::description( __( '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.', 'autodescription' ) );
199
 
200
  echo '<hr>';
201
 
204
  '<h4>%s</h4>',
205
  esc_html__( 'Sitemap Hinting', 'autodescription' )
206
  );
207
+ HTML::wrap_fields(
208
+ Form::make_checkbox( [
209
+ 'id' => 'sitemaps_robots',
210
+ 'label' => __( 'Add sitemap location to robots.txt?', 'autodescription' ),
211
+ ] ),
 
 
212
  true
213
  );
214
  endif;
216
  $robots_url = $this->get_robots_txt_url();
217
 
218
  if ( $robots_url ) {
219
+ HTML::description_noesc(
220
  sprintf(
221
  '<a href="%s" target=_blank rel=noopener>%s</a>',
222
  esc_url( $robots_url, [ 'https', 'http' ] ),
227
  break;
228
 
229
  case 'the_seo_framework_sitemaps_metabox_metadata':
230
+ Form::header_title( __( 'Timestamps Settings', 'autodescription' ) );
231
+ HTML::description( __( 'The modified time suggests to search engines where to look for content changes first.', 'autodescription' ) );
 
 
232
 
233
  // Echo checkbox.
234
+ HTML::wrap_fields(
235
+ Form::make_checkbox( [
236
+ 'id' => 'sitemaps_modified',
237
+ 'label' => $this->convert_markdown(
238
  /* translators: the backticks are Markdown! Preserve them as-is! */
239
  esc_html__( 'Add `<lastmod>` to the sitemap?', 'autodescription' ),
240
  [ 'code' ]
241
  ),
242
+ 'escape' => false,
243
+ ] ),
 
244
  true
245
  );
246
 
247
  if ( $this->get_option( 'sitemaps_priority' ) ) :
248
  ?>
249
  <hr>
 
 
250
  <?php
251
+ Form::header_title( __( 'Priority Settings', 'autodescription' ) );
252
+ HTML::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' ) );
253
 
254
  // Echo checkbox.
255
+ HTML::wrap_fields(
256
+ Form::make_checkbox( [
257
+ 'id' => 'sitemaps_priority',
258
+ 'label' => $this->convert_markdown(
259
  /* translators: the backticks are Markdown! Preserve them as-is! */
260
  esc_html__( 'Add `<priority>` to the optimized sitemap?', 'autodescription' ),
261
  [ 'code' ]
262
  ),
263
+ 'escape' => false,
264
+ ] ),
 
265
  true
266
  );
267
+ endif;
268
  break;
269
 
270
  case 'the_seo_framework_sitemaps_metabox_notify':
271
+ Form::header_title( __( 'Ping Settings', 'autodescription' ) );
272
+ HTML::description( __( 'Notifying search engines of a sitemap change is helpful to get your content indexed as soon as possible.', 'autodescription' ) );
273
+ HTML::description( __( 'By default this will happen at most once an hour.', 'autodescription' ) );
 
 
274
 
275
+ HTML::wrap_fields(
276
  [
277
+ Form::make_checkbox( [
278
+ 'id' => 'ping_use_cron',
279
+ 'label' => esc_html__( 'Use cron for pinging?', 'autodescription' )
280
+ . ' ' . HTML::make_info(
281
  __( 'This speeds up post and term saving processes, by offsetting pinging to a later time.', 'autodescription' ),
282
  '',
283
  false
284
  ),
285
+ 'escape' => false,
286
+ ] ),
287
+ Form::make_checkbox( [
288
+ 'id' => 'ping_use_cron_prerender',
289
+ 'label' => esc_html__( 'Prerender optimized sitemap before pinging via cron?', 'autodescription' )
290
+ . ' ' . HTML::make_info(
 
291
  __( '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' ),
292
  '',
293
  false
294
  ),
295
+ 'description' => esc_html__( 'Only enable prerendering when generating the sitemap takes over 60 seconds.', 'autodescription' ),
296
+ 'escape' => false,
297
+ ] ),
298
  ],
299
  true
300
  );
301
 
302
  ?>
303
  <hr>
 
 
304
  <?php
305
+ Form::header_title( __( 'Notify Search Engines', 'autodescription' ) );
306
 
307
  $engines = [
308
  'ping_google' => 'Google',
314
  foreach ( $engines as $option => $engine ) {
315
  /* translators: %s = Google */
316
  $ping_label = sprintf( __( 'Notify %s about sitemap changes?', 'autodescription' ), $engine );
317
+ $ping_checkbox .= Form::make_checkbox( [
318
+ 'id' => $option,
319
+ 'label' => $ping_label,
320
+ ] );
321
  }
322
 
323
  // Echo checkbox.
324
+ HTML::wrap_fields( $ping_checkbox, true );
325
  break;
326
 
327
  case 'the_seo_framework_sitemaps_metabox_style':
328
+ Form::header_title( __( 'Optimized Sitemap Styling Settings', 'autodescription' ) );
329
+ HTML::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' ) );
330
+ HTML::description( __( 'Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes.', 'autodescription' ) );
 
 
331
  ?>
332
  <hr>
 
 
333
  <?php
334
+ Form::header_title( __( 'Enable Styling', 'autodescription' ) );
335
+
336
+ HTML::wrap_fields(
337
+ Form::make_checkbox( [
338
+ 'id' => 'sitemap_styles',
339
+ 'label' => esc_html__( 'Style sitemap?', 'autodescription' ) . ' ' . HTML::make_info( __( 'This makes the sitemap more readable for humans.', 'autodescription' ), '', false ),
340
+ 'escape' => false,
341
+ ] ),
342
  true
343
  );
344
 
351
 
352
  ?>
353
  <p>
354
+ <label for="<?php Form::field_id( 'sitemap_color_main' ); ?>">
355
  <strong><?php esc_html_e( 'Sitemap Header Background Color', 'autodescription' ); ?></strong>
356
  </label>
357
  </p>
358
  <p>
359
+ <input type="text" name="<?php Form::field_name( 'sitemap_color_main' ); ?>" class="tsf-color-picker" id="<?php Form::field_id( 'sitemap_color_main' ); ?>" placeholder="<?php echo esc_attr( $default_colors['main'] ); ?>" value="<?php echo esc_attr( $current_colors['main'] ); ?>" data-tsf-default-color="<?php echo esc_attr( $default_colors['main'] ); ?>" />
360
  </p>
361
 
362
  <p>
363
+ <label for="<?php Form::field_id( 'sitemap_color_accent' ); ?>">
364
  <strong><?php esc_html_e( 'Sitemap Title and Lines Color', 'autodescription' ); ?></strong>
365
  </label>
366
  </p>
367
  <p>
368
+ <input type="text" name="<?php Form::field_name( 'sitemap_color_accent' ); ?>" class="tsf-color-picker" id="<?php Form::field_id( 'sitemap_color_accent' ); ?>" placeholder="<?php echo esc_attr( $default_colors['accent'] ); ?>" value="<?php echo esc_attr( $current_colors['accent'] ); ?>" data-tsf-default-color="<?php echo esc_attr( $default_colors['accent'] ); ?>" />
369
  </p>
370
 
371
  <hr>
 
 
372
  <?php
373
+ Form::header_title( __( 'Header Title Logo', 'autodescription' ) );
374
 
375
+ HTML::wrap_fields(
376
+ Form::make_checkbox( [
377
+ 'id' => 'sitemap_logo',
378
+ 'label' => __( 'Show logo next to sitemap header title?', 'autodescription' ),
379
+ ] ),
 
 
380
  true
381
  );
382
 
393
  </p>
394
  <p>
395
  <span class="hide-if-tsf-js attention"><?php esc_html_e( 'Setting a logo requires JavaScript.', 'autodescription' ); ?></span>
396
+ <input class="large-text" type="url" readonly="readonly" data-readonly="1" name="<?php Form::field_name( 'sitemap_logo_url' ); ?>" id="sitemap_logo-url" placeholder="<?php echo esc_url( $logo_placeholder ); ?>" value="<?php echo esc_url( $this->get_option( 'sitemap_logo_url' ) ); ?>" />
397
+ <input type="hidden" name="<?php Form::field_name( 'sitemap_logo_id' ); ?>" id="sitemap_logo-id" value="<?php echo absint( $this->get_option( 'sitemap_logo_id' ) ); ?>" />
398
  </p>
399
  <p class="hide-if-no-tsf-js">
400
  <?php
401
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
402
+ echo Form::get_image_uploader_form( [
403
+ 'id' => 'sitemap_logo',
404
+ 'data' => [
405
+ 'inputType' => 'logo',
406
+ 'width' => 512,
407
+ 'height' => 512,
408
+ 'minWidth' => 64,
409
+ 'minHeight' => 64,
410
+ 'flex' => true,
411
+ ],
412
+ 'i18n' => [
413
+ 'button_title' => '',
414
+ 'button_text' => __( 'Select Logo', 'autodescription' ),
415
+ ],
416
+ ] );
417
  ?>
418
  </p>
419
  <?php
inc/views/admin/metaboxes/social-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -57,144 +59,130 @@ switch ( $instance ) :
57
  break;
58
 
59
  case 'the_seo_framework_social_metabox_general':
60
- ?>
61
- <h4><?php esc_html_e( 'Social Meta Tags Settings', 'autodescription' ); ?></h4>
62
- <?php
63
- $this->description( __( 'Output various meta tags for social site integration, among other third-party services.', 'autodescription' ) );
64
 
65
  ?>
66
  <hr>
67
  <?php
68
 
69
  // Echo Open Graph Tags checkboxes.
70
- $this->wrap_fields(
71
- $this->make_checkbox(
72
- 'og_tags',
73
- __( 'Output Open Graph meta tags?', 'autodescription' ),
74
- __( 'Facebook, Twitter, Pinterest and many other social sites make use of these meta tags.', 'autodescription' ),
75
- true
76
- ),
77
  true
78
  );
79
  if ( $this->detect_og_plugin() )
80
- $this->attention_description( __( 'Note: Another Open Graph plugin has been detected. These meta tags might conflict.', 'autodescription' ) );
81
 
82
  // Echo Facebook Tags checkbox.
83
- $this->wrap_fields(
84
- $this->make_checkbox(
85
- 'facebook_tags',
86
- __( 'Output Facebook meta tags?', 'autodescription' ),
87
- __( 'Output various meta tags targeted at Facebook.', 'autodescription' ),
88
- true
89
- ),
90
  true
91
  );
92
 
93
  // Echo Twitter Tags checkboxes.
94
- $this->wrap_fields(
95
- $this->make_checkbox(
96
- 'twitter_tags',
97
- __( 'Output Twitter meta tags?', 'autodescription' ),
98
- __( 'Output various meta tags targeted at Twitter.', 'autodescription' ),
99
- true
100
- ),
101
  true
102
  );
103
  if ( $this->detect_twitter_card_plugin() )
104
- $this->attention_description( __( 'Note: Another Twitter Card plugin has been detected. These meta tags might conflict.', 'autodescription' ) );
105
 
106
  // Echo oEmbed scripts checkboxes.
107
- $this->wrap_fields(
108
- $this->make_checkbox(
109
- 'oembed_scripts',
110
- __( 'Output oEmbed scripts?', 'autodescription' ),
111
- __( 'WordPress, Discord, Drupal, Squarespace, and many other clients can make use of these scripts.', 'autodescription' ),
112
- true
113
- ),
114
  true
115
  );
116
  ?>
117
  <hr>
118
-
119
- <h4><?php esc_html_e( 'Social Title Settings', 'autodescription' ); ?></h4>
120
  <?php
121
- $this->description( __( 'Most social sites and third-party services automatically include the website URL inside their embeds. When the site title is described well in the site URL, including it in the social title will be redundant.', 'autodescription' ) );
 
122
 
123
- $info = $this->make_info(
124
  __( 'When you provide a custom Open Graph or Twitter title, the site title will be omitted automatically.', 'autodescription' ),
125
  '',
126
  false
127
  );
128
 
129
- $this->wrap_fields(
130
- $this->make_checkbox(
131
- 'social_title_rem_additions',
132
- esc_html__( 'Remove site title from generated social titles?', 'autodescription' ) . ' ' . $info,
133
- '',
134
- false
135
- ),
136
  true
137
  );
138
  ?>
139
  <hr>
140
-
141
- <h4><?php esc_html_e( 'Social Image Settings', 'autodescription' ); ?></h4>
142
  <?php
143
- $this->description( __( 'A social image can be displayed when your website is shared. It is a great way to grab attention.', 'autodescription' ) );
144
-
145
- $this->wrap_fields(
146
- $this->make_checkbox(
147
- 'multi_og_image',
148
- __( 'Output multiple Open Graph image tags?', 'autodescription' ),
149
- __( 'This enables users to select any image attached to the page shared on social networks, like Facebook.', 'autodescription' ),
150
- true
151
- ),
152
  true
153
  );
154
  ?>
155
  <p>
156
  <label for="tsf_fb_socialimage-url">
157
  <strong><?php esc_html_e( 'Social Image Fallback URL', 'autodescription' ); ?></strong>
158
- <?php $this->make_info( __( 'When no image is available from the page or term, this fallback image will be used instead.', 'autodescription' ), 'https://developers.facebook.com/docs/sharing/best-practices#images' ); ?>
159
  </label>
160
  </p>
161
  <p>
162
- <input class="large-text" type="url" name="<?php $this->field_name( 'social_image_fb_url' ); ?>" id="tsf_fb_socialimage-url" value="<?php echo esc_url( $this->get_option( 'social_image_fb_url' ) ); ?>" />
163
- <input type="hidden" name="<?php $this->field_name( 'social_image_fb_id' ); ?>" id="tsf_fb_socialimage-id" value="<?php echo absint( $this->get_option( 'social_image_fb_id' ) ); ?>" disabled class="tsf-enable-media-if-js" />
164
  </p>
165
  <p class="hide-if-no-tsf-js">
166
  <?php
167
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
168
- echo $this->get_social_image_uploader_form( 'tsf_fb_socialimage' );
169
  ?>
170
  </p>
171
  <hr>
172
-
173
- <h4><?php esc_html_e( 'Theme Color Settings', 'autodescription' ); ?></h4>
174
  <?php
175
- $this->description( __( 'Discord styles embeds with the theme color. The theme color can also affect the tab-color in some browsers.', 'autodescription' ) );
 
176
  ?>
177
  <p>
178
- <label for="<?php $this->field_id( 'theme_color' ); ?>">
179
  <strong><?php esc_html_e( 'Theme Color', 'autodescription' ); ?></strong>
180
  </label>
181
  </p>
182
  <p>
183
- <input type="text" name="<?php $this->field_name( 'theme_color' ); ?>" class="tsf-color-picker" id="<?php $this->field_id( 'theme_color' ); ?>" value="<?php echo esc_attr( $this->get_option( 'theme_color' ) ); ?>" data-tsf-default-color="" />
184
  </p>
185
  <hr>
186
-
187
- <h4><?php esc_html_e( 'Site Shortlink Settings', 'autodescription' ); ?></h4>
188
  <?php
189
- $this->description( __( 'The shortlink tag can be manually used for microblogging services like Twitter, but it has no SEO value whatsoever.', 'autodescription' ) );
190
-
191
- $this->wrap_fields(
192
- $this->make_checkbox(
193
- 'shortlink_tag',
194
- __( 'Output shortlink tag?', 'autodescription' ),
195
- '',
196
- true
197
- ),
198
  true
199
  );
200
  break;
@@ -209,20 +197,18 @@ switch ( $instance ) :
209
  $fb_appid = $this->get_option( 'facebook_appid' );
210
  $fb_appid_placeholder = '123456789012345';
211
 
212
- ?>
213
- <h4><?php esc_html_e( 'Facebook Integration Settings', 'autodescription' ); ?></h4>
214
- <?php
215
- $this->description( __( 'Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options.', 'autodescription' ) );
216
- $this->description( __( 'When these options are filled in, Facebook might link the Facebook profile to be followed and liked when your post or page is shared.', 'autodescription' ) );
217
  ?>
218
  <hr>
219
 
220
  <p>
221
- <label for="<?php $this->field_id( 'facebook_appid' ); ?>">
222
  <strong><?php esc_html_e( 'Facebook App ID', 'autodescription' ); ?></strong>
223
  <?php
224
  echo ' ';
225
- $this->make_info(
226
  __( 'Get Facebook App ID.', 'autodescription' ),
227
  'https://developers.facebook.com/apps'
228
  );
@@ -230,15 +216,15 @@ switch ( $instance ) :
230
  </label>
231
  </p>
232
  <p>
233
- <input type="text" name="<?php $this->field_name( 'facebook_appid' ); ?>" class="large-text ltr" id="<?php $this->field_id( 'facebook_appid' ); ?>" placeholder="<?php echo esc_attr( $fb_appid_placeholder ); ?>" value="<?php echo esc_attr( $fb_appid ); ?>" />
234
  </p>
235
 
236
  <p>
237
- <label for="<?php $this->field_id( 'facebook_publisher' ); ?>">
238
  <strong><?php esc_html_e( 'Facebook Publisher page', 'autodescription' ); ?></strong>
239
  <?php
240
  echo ' ';
241
- $this->make_info(
242
  __( 'Only Facebook Business Pages are accepted.', 'autodescription' ),
243
  'https://www.facebook.com/business/pages/set-up'
244
  );
@@ -246,24 +232,24 @@ switch ( $instance ) :
246
  </label>
247
  </p>
248
  <p>
249
- <input type="url" name="<?php $this->field_name( 'facebook_publisher' ); ?>" class="large-text" id="<?php $this->field_id( 'facebook_publisher' ); ?>" placeholder="<?php echo esc_attr( $fb_publisher_placeholder ); ?>" value="<?php echo esc_attr( $fb_publisher ); ?>" />
250
  </p>
251
 
252
  <p>
253
- <label for="<?php $this->field_id( 'facebook_author' ); ?>">
254
  <strong><?php esc_html_e( 'Facebook Author Fallback Page', 'autodescription' ); ?></strong>
255
  <?php
256
  echo ' ';
257
- $this->make_info(
258
  __( 'Your Facebook profile.', 'autodescription' ),
259
  'https://facebook.com/me'
260
  );
261
  ?>
262
  </label>
263
  </p>
264
- <?php $this->description( __( 'Authors can override this option on their profile page.', 'autodescription' ) ); ?>
265
  <p>
266
- <input type="url" name="<?php $this->field_name( 'facebook_author' ); ?>" class="large-text" id="<?php $this->field_id( 'facebook_author' ); ?>" placeholder="<?php echo esc_attr( $fb_author_placeholder ); ?>" value="<?php echo esc_attr( $fb_author ); ?>" />
267
  </p>
268
  <?php
269
  break;
@@ -277,18 +263,16 @@ switch ( $instance ) :
277
 
278
  $twitter_card = $this->get_twitter_card_types();
279
 
280
- ?>
281
- <h4><?php esc_html_e( 'Twitter Integration Settings', 'autodescription' ); ?></h4>
282
- <?php
283
- $this->description( __( 'Twitter post sharing works mostly through Twitter Cards, and may fall back to use Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options.', 'autodescription' ) );
284
 
285
  ?>
286
  <hr>
287
 
288
  <fieldset id="tsf-twitter-cards">
289
- <legend><h4><?php esc_html_e( 'Twitter Card Type', 'autodescription' ); ?></h4></legend>
290
  <?php
291
- $this->description(
292
  __( 'The Twitter Card type may have the image highlighted, either small at the side or large above.', 'autodescription' )
293
  );
294
  ?>
@@ -298,13 +282,13 @@ switch ( $instance ) :
298
  foreach ( $twitter_card as $type => $name ) {
299
  ?>
300
  <span class="tsf-toblock">
301
- <input type="radio" name="<?php $this->field_name( 'twitter_card' ); ?>" id="<?php $this->field_id( 'twitter_card_' . $type ); ?>" value="<?php echo esc_attr( $type ); ?>" <?php checked( $this->get_option( 'twitter_card' ), $type ); ?> />
302
- <label for="<?php $this->field_id( 'twitter_card_' . $type ); ?>">
303
  <span>
304
  <?php
305
- echo $this->code_wrap( $name ); // phpcs:ignore, WordPress.Security.EscapeOutput
306
  echo ' ';
307
- $this->make_info(
308
  __( 'Learn more about this card.', 'autodescription' ),
309
  'https://dev.twitter.com/cards/types/' . $name
310
  );
@@ -319,20 +303,19 @@ switch ( $instance ) :
319
  </fieldset>
320
 
321
  <hr>
322
-
323
- <h4><?php esc_html_e( 'Card and Content Attribution', 'autodescription' ); ?></h4>
324
  <?php
 
325
  /* source: https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started#attribution */
326
- $this->description( __( 'Twitter claims users will be able to follow and view the profiles of attributed accounts directly from the card when these fields are filled in.', 'autodescription' ) );
327
- $this->description( __( 'However, for now, these fields seem to have no discernible effect.', 'autodescription' ) );
328
  ?>
329
 
330
  <p>
331
- <label for="<?php $this->field_id( 'twitter_site' ); ?>" class="tsf-toblock">
332
  <strong><?php esc_html_e( 'Website Twitter Profile', 'autodescription' ); ?></strong>
333
  <?php
334
  echo ' ';
335
- $this->make_info(
336
  __( 'Find your @username.', 'autodescription' ),
337
  'https://twitter.com/home'
338
  );
@@ -340,68 +323,64 @@ switch ( $instance ) :
340
  </label>
341
  </p>
342
  <p>
343
- <input type="text" name="<?php $this->field_name( 'twitter_site' ); ?>" class="large-text ltr" id="<?php $this->field_id( 'twitter_site' ); ?>" placeholder="<?php echo esc_attr( $tw_site_placeholder ); ?>" value="<?php echo esc_attr( $tw_site ); ?>" />
344
  </p>
345
 
346
  <p>
347
- <label for="<?php $this->field_id( 'twitter_creator' ); ?>" class="tsf-toblock">
348
  <strong><?php esc_html_e( 'Twitter Author Fallback Profile', 'autodescription' ); ?></strong>
349
  <?php
350
  echo ' ';
351
- $this->make_info(
352
  __( 'Find your @username.', 'autodescription' ),
353
  'https://twitter.com/home'
354
  );
355
  ?>
356
  </label>
357
  </p>
358
- <?php $this->description( __( 'Authors can override this option on their profile page.', 'autodescription' ) ); ?>
359
  <p>
360
- <input type="text" name="<?php $this->field_name( 'twitter_creator' ); ?>" class="large-text ltr" id="<?php $this->field_id( 'twitter_creator' ); ?>" placeholder="<?php echo esc_attr( $tw_creator_placeholder ); ?>" value="<?php echo esc_attr( $tw_creator ); ?>" />
361
  </p>
362
  <?php
363
  break;
364
 
365
  case 'the_seo_framework_social_metabox_oembed':
366
- ?>
367
- <h4><?php esc_html_e( 'oEmbed Settings', 'autodescription' ); ?></h4>
368
- <?php
369
- $this->description( __( 'Some social sharing services and clients, like WordPress, LinkedIn, and Discord, obtain the linked page information via oEmbed.', 'autodescription' ) );
370
  ?>
371
  <hr>
372
  <?php
373
 
374
  // Split the wraps--the informational messages make for bad legibility otherwise.
375
- $this->wrap_fields(
376
- $this->make_checkbox(
377
- 'oembed_use_og_title',
378
- __( 'Use Open Graph title?', 'autodescription' ),
379
- __( 'Check this option if you want to replace page titles with Open Graph titles in embeds.', 'autodescription' ),
380
- true
381
- ),
382
  true
383
  );
384
- $_info = $this->make_info(
385
  __( 'Only custom social images that are selected via the Media Library are considered.', 'autodescription' ),
386
  '',
387
  false
388
  );
389
- $this->wrap_fields(
390
- $this->make_checkbox(
391
- 'oembed_use_social_image',
392
- esc_html__( 'Use social image?', 'autodescription' ) . ' ' . $_info,
393
- esc_html__( "LinkedIn displays the post's featured image in embeds. Check this option if you want to replace it with the social image.", 'autodescription' ),
394
- false
395
- ),
396
  true
397
  );
398
- $this->wrap_fields(
399
- $this->make_checkbox(
400
- 'oembed_remove_author',
401
- __( 'Remove author name?', 'autodescription' ),
402
- __( "Discord shows the page author's name above the sharing embed. Check this option if you find this undesirable.", 'autodescription' ),
403
- true
404
- ),
405
  true
406
  );
407
 
@@ -409,36 +388,32 @@ switch ( $instance ) :
409
  case 'the_seo_framework_social_metabox_postdates':
410
  $posts_i18n = esc_html__( 'Posts', 'autodescription' );
411
 
412
- ?>
413
- <h4><?php esc_html_e( 'Post Date Settings', 'autodescription' ); ?></h4>
414
- <?php
415
- $this->description( __( "Some social sites output the shared post's publishing and modified data in the sharing snippet.", 'autodescription' ) );
416
  ?>
417
  <hr>
418
  <?php
419
 
420
- $this->wrap_fields(
421
  [
422
- $this->make_checkbox(
423
- 'post_publish_time',
424
- $this->convert_markdown(
425
  /* translators: the backticks are Markdown! Preserve them as-is! */
426
  esc_html__( 'Add `article:published_time` to posts?', 'autodescription' ),
427
  [ 'code' ]
428
  ),
429
- '',
430
- false
431
- ),
432
- $this->make_checkbox(
433
- 'post_modify_time',
434
- $this->convert_markdown(
435
  /* translators: the backticks are Markdown! Preserve them as-is! */
436
  esc_html__( 'Add `article:modified_time` to posts?', 'autodescription' ),
437
  [ 'code' ]
438
  ),
439
- '',
440
- false
441
- ),
442
  ],
443
  true
444
  );
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
59
  break;
60
 
61
  case 'the_seo_framework_social_metabox_general':
62
+ Form::header_title( __( 'Social Meta Tags Settings', 'autodescription' ) );
63
+ HTML::description( __( 'Output various meta tags for social site integration, among other third-party services.', 'autodescription' ) );
 
 
64
 
65
  ?>
66
  <hr>
67
  <?php
68
 
69
  // Echo Open Graph Tags checkboxes.
70
+ HTML::wrap_fields(
71
+ Form::make_checkbox( [
72
+ 'id' => 'og_tags',
73
+ 'label' => __( 'Output Open Graph meta tags?', 'autodescription' ),
74
+ 'description' => __( 'Facebook, Twitter, Pinterest and many other social sites make use of these meta tags.', 'autodescription' ),
75
+ ] ),
 
76
  true
77
  );
78
  if ( $this->detect_og_plugin() )
79
+ HTML::attention_description( __( 'Note: Another Open Graph plugin has been detected. These meta tags might conflict.', 'autodescription' ) );
80
 
81
  // Echo Facebook Tags checkbox.
82
+ HTML::wrap_fields(
83
+ Form::make_checkbox( [
84
+ 'id' => 'facebook_tags',
85
+ 'label' => __( 'Output Facebook meta tags?', 'autodescription' ),
86
+ 'description' => __( 'Output various meta tags targeted at Facebook.', 'autodescription' ),
87
+ ] ),
 
88
  true
89
  );
90
 
91
  // Echo Twitter Tags checkboxes.
92
+ HTML::wrap_fields(
93
+ Form::make_checkbox( [
94
+ 'id' => 'twitter_tags',
95
+ 'label' => __( 'Output Twitter meta tags?', 'autodescription' ),
96
+ 'description' => __( 'Output various meta tags targeted at Twitter.', 'autodescription' ),
97
+ ] ),
 
98
  true
99
  );
100
  if ( $this->detect_twitter_card_plugin() )
101
+ HTML::attention_description( __( 'Note: Another Twitter Card plugin has been detected. These meta tags might conflict.', 'autodescription' ) );
102
 
103
  // Echo oEmbed scripts checkboxes.
104
+ HTML::wrap_fields(
105
+ Form::make_checkbox( [
106
+ 'id' => 'oembed_scripts',
107
+ 'label' => __( 'Output oEmbed scripts?', 'autodescription' ),
108
+ 'description' => __( 'WordPress, Discord, Drupal, Squarespace, and many other clients can make use of these scripts.', 'autodescription' ),
109
+ ] ),
 
110
  true
111
  );
112
  ?>
113
  <hr>
 
 
114
  <?php
115
+ Form::header_title( __( 'Social Title Settings', 'autodescription' ) );
116
+ HTML::description( __( 'Most social sites and third-party services automatically include the website URL inside their embeds. When the site title is described well in the site URL, including it in the social title will be redundant.', 'autodescription' ) );
117
 
118
+ $info = HTML::make_info(
119
  __( 'When you provide a custom Open Graph or Twitter title, the site title will be omitted automatically.', 'autodescription' ),
120
  '',
121
  false
122
  );
123
 
124
+ HTML::wrap_fields(
125
+ Form::make_checkbox( [
126
+ 'id' => 'social_title_rem_additions',
127
+ 'label' => esc_html__( 'Remove site title from generated social titles?', 'autodescription' ) . ' ' . $info,
128
+ 'escape' => false,
129
+ ] ),
 
130
  true
131
  );
132
  ?>
133
  <hr>
 
 
134
  <?php
135
+ Form::header_title( __( 'Social Image Settings', 'autodescription' ) );
136
+ HTML::description( __( 'A social image can be displayed when your website is shared. It is a great way to grab attention.', 'autodescription' ) );
137
+
138
+ HTML::wrap_fields(
139
+ Form::make_checkbox( [
140
+ 'id' => 'multi_og_image',
141
+ 'label' => __( 'Output multiple Open Graph image tags?', 'autodescription' ),
142
+ 'description' => __( 'This enables users to select any image attached to the page shared on social networks, like Facebook.', 'autodescription' ),
143
+ ] ),
144
  true
145
  );
146
  ?>
147
  <p>
148
  <label for="tsf_fb_socialimage-url">
149
  <strong><?php esc_html_e( 'Social Image Fallback URL', 'autodescription' ); ?></strong>
150
+ <?php HTML::make_info( __( 'When no image is available from the page or term, this fallback image will be used instead.', 'autodescription' ), 'https://developers.facebook.com/docs/sharing/best-practices#images' ); ?>
151
  </label>
152
  </p>
153
  <p>
154
+ <input class="large-text" type="url" name="<?php Form::field_name( 'social_image_fb_url' ); ?>" id="tsf_fb_socialimage-url" value="<?php echo esc_url( $this->get_option( 'social_image_fb_url' ) ); ?>" />
155
+ <input type="hidden" name="<?php Form::field_name( 'social_image_fb_id' ); ?>" id="tsf_fb_socialimage-id" value="<?php echo absint( $this->get_option( 'social_image_fb_id' ) ); ?>" disabled class="tsf-enable-media-if-js" />
156
  </p>
157
  <p class="hide-if-no-tsf-js">
158
  <?php
159
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
160
+ echo Form::get_image_uploader_form( [ 'id' => 'tsf_fb_socialimage' ] );
161
  ?>
162
  </p>
163
  <hr>
 
 
164
  <?php
165
+ Form::header_title( __( 'Theme Color Settings', 'autodescription' ) );
166
+ HTML::description( __( 'Discord styles embeds with the theme color. The theme color can also affect the tab-color in some browsers.', 'autodescription' ) );
167
  ?>
168
  <p>
169
+ <label for="<?php Form::field_id( 'theme_color' ); ?>">
170
  <strong><?php esc_html_e( 'Theme Color', 'autodescription' ); ?></strong>
171
  </label>
172
  </p>
173
  <p>
174
+ <input type="text" name="<?php Form::field_name( 'theme_color' ); ?>" class="tsf-color-picker" id="<?php Form::field_id( 'theme_color' ); ?>" value="<?php echo esc_attr( $this->get_option( 'theme_color' ) ); ?>" data-tsf-default-color="" />
175
  </p>
176
  <hr>
 
 
177
  <?php
178
+ Form::header_title( __( 'Site Shortlink Settings', 'autodescription' ) );
179
+ HTML::description( __( 'The shortlink tag can be manually used for microblogging services like Twitter, but it has no SEO value whatsoever.', 'autodescription' ) );
180
+
181
+ HTML::wrap_fields(
182
+ Form::make_checkbox( [
183
+ 'id' => 'shortlink_tag',
184
+ 'label' => __( 'Output shortlink tag?', 'autodescription' ),
185
+ ] ),
 
186
  true
187
  );
188
  break;
197
  $fb_appid = $this->get_option( 'facebook_appid' );
198
  $fb_appid_placeholder = '123456789012345';
199
 
200
+ Form::header_title( __( 'Facebook Integration Settings', 'autodescription' ) );
201
+ HTML::description( __( 'Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options.', 'autodescription' ) );
202
+ HTML::description( __( 'When these options are filled in, Facebook might link the Facebook profile to be followed and liked when your post or page is shared.', 'autodescription' ) );
 
 
203
  ?>
204
  <hr>
205
 
206
  <p>
207
+ <label for="<?php Form::field_id( 'facebook_appid' ); ?>">
208
  <strong><?php esc_html_e( 'Facebook App ID', 'autodescription' ); ?></strong>
209
  <?php
210
  echo ' ';
211
+ HTML::make_info(
212
  __( 'Get Facebook App ID.', 'autodescription' ),
213
  'https://developers.facebook.com/apps'
214
  );
216
  </label>
217
  </p>
218
  <p>
219
+ <input type="text" name="<?php Form::field_name( 'facebook_appid' ); ?>" class="large-text ltr" id="<?php Form::field_id( 'facebook_appid' ); ?>" placeholder="<?php echo esc_attr( $fb_appid_placeholder ); ?>" value="<?php echo esc_attr( $fb_appid ); ?>" />
220
  </p>
221
 
222
  <p>
223
+ <label for="<?php Form::field_id( 'facebook_publisher' ); ?>">
224
  <strong><?php esc_html_e( 'Facebook Publisher page', 'autodescription' ); ?></strong>
225
  <?php
226
  echo ' ';
227
+ HTML::make_info(
228
  __( 'Only Facebook Business Pages are accepted.', 'autodescription' ),
229
  'https://www.facebook.com/business/pages/set-up'
230
  );
232
  </label>
233
  </p>
234
  <p>
235
+ <input type="url" name="<?php Form::field_name( 'facebook_publisher' ); ?>" class="large-text" id="<?php Form::field_id( 'facebook_publisher' ); ?>" placeholder="<?php echo esc_attr( $fb_publisher_placeholder ); ?>" value="<?php echo esc_attr( $fb_publisher ); ?>" />
236
  </p>
237
 
238
  <p>
239
+ <label for="<?php Form::field_id( 'facebook_author' ); ?>">
240
  <strong><?php esc_html_e( 'Facebook Author Fallback Page', 'autodescription' ); ?></strong>
241
  <?php
242
  echo ' ';
243
+ HTML::make_info(
244
  __( 'Your Facebook profile.', 'autodescription' ),
245
  'https://facebook.com/me'
246
  );
247
  ?>
248
  </label>
249
  </p>
250
+ <?php HTML::description( __( 'Authors can override this option on their profile page.', 'autodescription' ) ); ?>
251
  <p>
252
+ <input type="url" name="<?php Form::field_name( 'facebook_author' ); ?>" class="large-text" id="<?php Form::field_id( 'facebook_author' ); ?>" placeholder="<?php echo esc_attr( $fb_author_placeholder ); ?>" value="<?php echo esc_attr( $fb_author ); ?>" />
253
  </p>
254
  <?php
255
  break;
263
 
264
  $twitter_card = $this->get_twitter_card_types();
265
 
266
+ Form::header_title( __( 'Twitter Integration Settings', 'autodescription' ) );
267
+ HTML::description( __( 'Twitter post sharing works mostly through Twitter Cards, and may fall back to use Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options.', 'autodescription' ) );
 
 
268
 
269
  ?>
270
  <hr>
271
 
272
  <fieldset id="tsf-twitter-cards">
273
+ <legend><?php Form::header_title( __( 'Twitter Card Type', 'autodescription' ) ); ?></legend>
274
  <?php
275
+ HTML::description(
276
  __( 'The Twitter Card type may have the image highlighted, either small at the side or large above.', 'autodescription' )
277
  );
278
  ?>
282
  foreach ( $twitter_card as $type => $name ) {
283
  ?>
284
  <span class="tsf-toblock">
285
+ <input type="radio" name="<?php Form::field_name( 'twitter_card' ); ?>" id="<?php Form::field_id( 'twitter_card_' . $type ); ?>" value="<?php echo esc_attr( $type ); ?>" <?php checked( $this->get_option( 'twitter_card' ), $type ); ?> />
286
+ <label for="<?php Form::field_id( 'twitter_card_' . $type ); ?>">
287
  <span>
288
  <?php
289
+ echo HTML::code_wrap( $name ); // phpcs:ignore, WordPress.Security.EscapeOutput
290
  echo ' ';
291
+ HTML::make_info(
292
  __( 'Learn more about this card.', 'autodescription' ),
293
  'https://dev.twitter.com/cards/types/' . $name
294
  );
303
  </fieldset>
304
 
305
  <hr>
 
 
306
  <?php
307
+ Form::header_title( __( 'Card and Content Attribution', 'autodescription' ) );
308
  /* source: https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started#attribution */
309
+ HTML::description( __( 'Twitter claims users will be able to follow and view the profiles of attributed accounts directly from the card when these fields are filled in.', 'autodescription' ) );
310
+ HTML::description( __( 'However, for now, these fields seem to have no discernible effect.', 'autodescription' ) );
311
  ?>
312
 
313
  <p>
314
+ <label for="<?php Form::field_id( 'twitter_site' ); ?>" class="tsf-toblock">
315
  <strong><?php esc_html_e( 'Website Twitter Profile', 'autodescription' ); ?></strong>
316
  <?php
317
  echo ' ';
318
+ HTML::make_info(
319
  __( 'Find your @username.', 'autodescription' ),
320
  'https://twitter.com/home'
321
  );
323
  </label>
324
  </p>
325
  <p>
326
+ <input type="text" name="<?php Form::field_name( 'twitter_site' ); ?>" class="large-text ltr" id="<?php Form::field_id( 'twitter_site' ); ?>" placeholder="<?php echo esc_attr( $tw_site_placeholder ); ?>" value="<?php echo esc_attr( $tw_site ); ?>" />
327
  </p>
328
 
329
  <p>
330
+ <label for="<?php Form::field_id( 'twitter_creator' ); ?>" class="tsf-toblock">
331
  <strong><?php esc_html_e( 'Twitter Author Fallback Profile', 'autodescription' ); ?></strong>
332
  <?php
333
  echo ' ';
334
+ HTML::make_info(
335
  __( 'Find your @username.', 'autodescription' ),
336
  'https://twitter.com/home'
337
  );
338
  ?>
339
  </label>
340
  </p>
341
+ <?php HTML::description( __( 'Authors can override this option on their profile page.', 'autodescription' ) ); ?>
342
  <p>
343
+ <input type="text" name="<?php Form::field_name( 'twitter_creator' ); ?>" class="large-text ltr" id="<?php Form::field_id( 'twitter_creator' ); ?>" placeholder="<?php echo esc_attr( $tw_creator_placeholder ); ?>" value="<?php echo esc_attr( $tw_creator ); ?>" />
344
  </p>
345
  <?php
346
  break;
347
 
348
  case 'the_seo_framework_social_metabox_oembed':
349
+ Form::header_title( __( 'oEmbed Settings', 'autodescription' ) );
350
+ HTML::description( __( 'Some social sharing services and clients, like WordPress, LinkedIn, and Discord, obtain the linked page information via oEmbed.', 'autodescription' ) );
 
 
351
  ?>
352
  <hr>
353
  <?php
354
 
355
  // Split the wraps--the informational messages make for bad legibility otherwise.
356
+ HTML::wrap_fields(
357
+ Form::make_checkbox( [
358
+ 'id' => 'oembed_use_og_title',
359
+ 'label' => __( 'Use Open Graph title?', 'autodescription' ),
360
+ 'description' => __( 'Check this option if you want to replace page titles with Open Graph titles in embeds.', 'autodescription' ),
361
+ ] ),
 
362
  true
363
  );
364
+ $_info = HTML::make_info(
365
  __( 'Only custom social images that are selected via the Media Library are considered.', 'autodescription' ),
366
  '',
367
  false
368
  );
369
+ HTML::wrap_fields(
370
+ Form::make_checkbox( [
371
+ 'id' => 'oembed_use_social_image',
372
+ 'label' => esc_html__( 'Use social image?', 'autodescription' ) . ' ' . $_info,
373
+ 'description' => esc_html__( "LinkedIn displays the post's featured image in embeds. Check this option if you want to replace it with the social image.", 'autodescription' ),
374
+ 'escape' => false,
375
+ ] ),
376
  true
377
  );
378
+ HTML::wrap_fields(
379
+ Form::make_checkbox( [
380
+ 'id' => 'oembed_remove_author',
381
+ 'label' => __( 'Remove author name?', 'autodescription' ),
382
+ 'description' => __( "Discord shows the page author's name above the sharing embed. Check this option if you find this undesirable.", 'autodescription' ),
383
+ ] ),
 
384
  true
385
  );
386
 
388
  case 'the_seo_framework_social_metabox_postdates':
389
  $posts_i18n = esc_html__( 'Posts', 'autodescription' );
390
 
391
+ Form::header_title( __( 'Post Date Settings', 'autodescription' ) );
392
+ HTML::description( __( "Some social sites output the shared post's publishing and modified data in the sharing snippet.", 'autodescription' ) );
 
 
393
  ?>
394
  <hr>
395
  <?php
396
 
397
+ HTML::wrap_fields(
398
  [
399
+ Form::make_checkbox( [
400
+ 'id' => 'post_publish_time',
401
+ 'label' => $this->convert_markdown(
402
  /* translators: the backticks are Markdown! Preserve them as-is! */
403
  esc_html__( 'Add `article:published_time` to posts?', 'autodescription' ),
404
  [ 'code' ]
405
  ),
406
+ 'escape' => false,
407
+ ] ),
408
+ Form::make_checkbox( [
409
+ 'id' => 'post_modify_time',
410
+ 'label' => $this->convert_markdown(
 
411
  /* translators: the backticks are Markdown! Preserve them as-is! */
412
  esc_html__( 'Add `article:modified_time` to posts?', 'autodescription' ),
413
  [ 'code' ]
414
  ),
415
+ 'escape' => false,
416
+ ] ),
 
417
  ],
418
  true
419
  );
inc/views/admin/metaboxes/title-metabox.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\SeoSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -45,40 +47,38 @@ switch ( $instance ) :
45
  $example_tax_left = '<em>' . $additions_left . $tax_title . '</em>';
46
  $example_tax_right = '<em>' . $tax_title . $additions_right . '</em>';
47
 
48
- ?>
49
- <h4><?php esc_html_e( 'Automated Title Settings', 'autodescription' ); ?></h4>
50
- <?php
51
- $this->description( __( 'The page title is prominently shown within the browser tab as well as within the search engine results pages.', 'autodescription' ) );
52
 
 
53
  ?>
54
- <h4><?php esc_html_e( 'Example Page Title Output', 'autodescription' ); ?></h4>
55
  <p>
56
  <span class="tsf-title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>">
57
  <?php
58
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
59
- echo $this->code_wrap_noesc( $example_post_left );
60
  ?>
61
  </span>
62
  <span class="tsf-title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>">
63
  <?php
64
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
65
- echo $this->code_wrap_noesc( $example_post_right );
66
  ?>
67
  </span>
68
  </p>
69
 
70
- <h4><?php esc_html_e( 'Example Archive Title Output', 'autodescription' ); ?></h4>
71
  <p>
72
  <span class="tsf-title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>">
73
  <?php
74
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
75
- echo $this->code_wrap_noesc( $example_tax_left );
76
  ?>
77
  </span>
78
  <span class="tsf-title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>">
79
  <?php
80
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
81
- echo $this->code_wrap_noesc( $example_tax_right );
82
  ?>
83
  </span>
84
  </p>
@@ -96,7 +96,7 @@ switch ( $instance ) :
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! */
@@ -158,11 +158,9 @@ switch ( $instance ) :
158
 
159
  ?>
160
  <fieldset>
161
- <legend>
162
- <h4><?php esc_html_e( 'Title Separator', 'autodescription' ); ?></h4>
163
- </legend>
164
  <?php
165
- $this->description( __( 'If the title consists of multiple parts, then the separator will go in-between them.', 'autodescription' ) );
166
  ?>
167
  <p id="tsf-title-separator" class="tsf-fields">
168
  <?php
@@ -170,11 +168,11 @@ switch ( $instance ) :
170
  vprintf(
171
  '<input type=radio name="%1$s" id="%2$s" value="%3$s" %4$s %5$s /><label for="%2$s">%6$s</label>',
172
  [
173
- esc_attr( $this->get_field_name( 'title_separator' ) ),
174
- esc_attr( $this->get_field_id( 'title_separator_' . $name ) ),
175
  esc_attr( $name ),
176
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- make_data_attributes() escapes.
177
- $this->make_data_attributes( [ 'entity' => esc_html( $html ) ] ), // This will double escape, but we found no issues.
178
  checked( $default_title_separator, $name, false ),
179
  esc_html( $html ),
180
  ]
@@ -185,13 +183,12 @@ switch ( $instance ) :
185
  </fieldset>
186
 
187
  <hr>
188
-
189
- <h4><?php esc_html_e( 'Automated Title Settings', 'autodescription' ); ?></h4>
190
  <?php
191
- $this->description( __( 'A title is generated for every page.', 'autodescription' ) );
192
- $this->description( __( 'Some titles may have HTML tags inserted by the author for styling.', 'autodescription' ) );
 
193
 
194
- $info = $this->make_info(
195
  sprintf(
196
  /* translators: %s = HTML tag example */
197
  __( 'This strips HTML tags, like %s, from the title. Disable this option to display generated HTML tags as plain text in meta titles.', 'autodescription' ),
@@ -200,17 +197,16 @@ switch ( $instance ) :
200
  '',
201
  false
202
  );
203
- $this->wrap_fields(
204
- $this->make_checkbox(
205
- 'title_strip_tags',
206
- esc_html__( 'Strip HTML tags from generated titles?', 'autodescription' ) . ' ' . $info,
207
- '',
208
- false
209
- ),
210
  true
211
  );
212
 
213
- $this->description( __( 'Tip: It is a bad practice to style page titles with HTML as inconsistent behavior might occur.', 'autodescription' ) );
214
  break;
215
 
216
  case 'the_seo_framework_title_metabox_additions':
@@ -221,86 +217,80 @@ switch ( $instance ) :
221
 
222
  ?>
223
  <fieldset>
224
- <legend>
225
- <h4><?php esc_html_e( 'Site Title Location', 'autodescription' ); ?></h4>
226
- </legend>
227
  <p id="tsf-title-location" class="tsf-fields">
228
  <span class="tsf-toblock">
229
- <input type="radio" name="<?php $this->field_name( 'title_location' ); ?>" id="<?php $this->field_id( 'title_location_left' ); ?>" value="left" <?php checked( $this->get_option( 'title_location' ), 'left' ); ?> />
230
- <label for="<?php $this->field_id( 'title_location_left' ); ?>">
231
  <span><?php esc_html_e( 'Left:', 'autodescription' ); ?></span>
232
  <?php
233
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
234
- echo $this->code_wrap_noesc( $example_left );
235
  ?>
236
  </label>
237
  </span>
238
  <span class="tsf-toblock">
239
- <input type="radio" name="<?php $this->field_name( 'title_location' ); ?>" id="<?php $this->field_id( 'title_location_right' ); ?>" value="right" <?php checked( $this->get_option( 'title_location' ), 'right' ); ?> />
240
- <label for="<?php $this->field_id( 'title_location_right' ); ?>">
241
  <span><?php esc_html_e( 'Right:', 'autodescription' ); ?></span>
242
  <?php
243
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
244
- echo $this->code_wrap_noesc( $example_right );
245
  ?>
246
  </label>
247
  </span>
248
  </p>
249
- <?php $this->description( $homepage_has_option ); ?>
250
  </fieldset>
251
 
252
  <hr>
253
 
254
- <h4><?php esc_html_e( 'Site Title', 'autodescription' ); ?></h4>
255
  <div id="tsf-title-additions-toggle">
256
  <?php
257
- $info = $this->make_info(
258
  __( 'Always brand your titles. Search engines may ignore your titles with this feature enabled.', 'autodescription' ),
259
- 'https://support.google.com/webmasters/answer/35624#page-titles',
260
  false
261
  );
262
 
263
- $this->wrap_fields(
264
- $this->make_checkbox(
265
- 'title_rem_additions',
266
- esc_html__( 'Remove site title from the title?', 'autodescription' ) . ' ' . $info,
267
- '',
268
- false
269
- ),
270
  true
271
  );
272
  ?>
273
  </div>
274
  <?php
275
- $this->attention_description( __( 'Note: Only use this option if you are aware of its SEO effects.', 'autodescription' ), false );
276
  echo ' ';
277
- $this->description( $homepage_has_option, false );
278
  break;
279
 
280
  case 'the_seo_framework_title_metabox_prefixes':
281
- ?>
282
- <h4><?php esc_html_e( 'Title Prefix Options', 'autodescription' ); ?></h4>
283
- <?php
284
- $this->description( __( 'For archives, a descriptive prefix may be added to generated titles.', 'autodescription' ) );
285
 
286
  ?>
287
  <hr>
288
 
289
- <h4><?php esc_html_e( 'Archive Title Prefixes', 'autodescription' ); ?></h4>
290
  <div id="tsf-title-prefixes-toggle">
291
  <?php
292
- $info = $this->make_info(
293
  __( "The prefix helps visitors and search engines determine what kind of page they're visiting.", 'autodescription' ),
294
  'https://kb.theseoframework.com/?p=34',
295
  false
296
  );
297
- $this->wrap_fields(
298
- $this->make_checkbox(
299
- 'title_rem_prefixes',
300
- esc_html__( 'Remove term type prefixes from generated archive titles?', 'autodescription' ) . ' ' . $info,
301
- '',
302
- false
303
- ),
304
  true
305
  );
306
  ?>
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\SeoSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
47
  $example_tax_left = '<em>' . $additions_left . $tax_title . '</em>';
48
  $example_tax_right = '<em>' . $tax_title . $additions_right . '</em>';
49
 
50
+ Form::header_title( __( 'Automated Title Settings', 'autodescription' ) );
51
+ HTML::description( __( 'The page title is prominently shown within the browser tab as well as within the search engine results pages.', 'autodescription' ) );
 
 
52
 
53
+ Form::header_title( __( 'Example Page Title Output', 'autodescription' ) );
54
  ?>
 
55
  <p>
56
  <span class="tsf-title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>">
57
  <?php
58
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
59
+ echo HTML::code_wrap_noesc( $example_post_left );
60
  ?>
61
  </span>
62
  <span class="tsf-title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>">
63
  <?php
64
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
65
+ echo HTML::code_wrap_noesc( $example_post_right );
66
  ?>
67
  </span>
68
  </p>
69
 
70
+ <?php Form::header_title( __( 'Example Archive Title Output', 'autodescription' ) ); ?>
71
  <p>
72
  <span class="tsf-title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>">
73
  <?php
74
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
75
+ echo HTML::code_wrap_noesc( $example_tax_left );
76
  ?>
77
  </span>
78
  <span class="tsf-title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>">
79
  <?php
80
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
81
+ echo HTML::code_wrap_noesc( $example_tax_right );
82
  ?>
83
  </span>
84
  </p>
96
  ?>
97
  <h4 class=attention><?php echo $_h4; // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped ?></h4>
98
  <?php
99
+ HTML::description_noesc(
100
  $this->convert_markdown(
101
  sprintf(
102
  /* translators: 1: Extension name, 2: Extension link. Markdown! */
158
 
159
  ?>
160
  <fieldset>
161
+ <legend><?php Form::header_title( __( 'Title Separator', 'autodescription' ) ); ?></legend>
 
 
162
  <?php
163
+ HTML::description( __( 'If the title consists of multiple parts, then the separator will go in-between them.', 'autodescription' ) );
164
  ?>
165
  <p id="tsf-title-separator" class="tsf-fields">
166
  <?php
168
  vprintf(
169
  '<input type=radio name="%1$s" id="%2$s" value="%3$s" %4$s %5$s /><label for="%2$s">%6$s</label>',
170
  [
171
+ esc_attr( Form::get_field_name( 'title_separator' ) ),
172
+ esc_attr( Form::get_field_id( 'title_separator_' . $name ) ),
173
  esc_attr( $name ),
174
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- make_data_attributes() escapes.
175
+ HTML::make_data_attributes( [ 'entity' => esc_html( $html ) ] ), // This will double escape, but we found no issues.
176
  checked( $default_title_separator, $name, false ),
177
  esc_html( $html ),
178
  ]
183
  </fieldset>
184
 
185
  <hr>
 
 
186
  <?php
187
+ Form::header_title( __( 'Automated Title Settings', 'autodescription' ) );
188
+ HTML::description( __( 'A title is generated for every page.', 'autodescription' ) );
189
+ HTML::description( __( 'Some titles may have HTML tags inserted by the author for styling.', 'autodescription' ) );
190
 
191
+ $info = HTML::make_info(
192
  sprintf(
193
  /* translators: %s = HTML tag example */
194
  __( 'This strips HTML tags, like %s, from the title. Disable this option to display generated HTML tags as plain text in meta titles.', 'autodescription' ),
197
  '',
198
  false
199
  );
200
+ HTML::wrap_fields(
201
+ Form::make_checkbox( [
202
+ 'id' => 'title_strip_tags',
203
+ 'label' => esc_html__( 'Strip HTML tags from generated titles?', 'autodescription' ) . ' ' . $info,
204
+ 'escape' => false,
205
+ ] ),
 
206
  true
207
  );
208
 
209
+ HTML::description( __( 'Tip: It is a bad practice to style page titles with HTML as inconsistent behavior might occur.', 'autodescription' ) );
210
  break;
211
 
212
  case 'the_seo_framework_title_metabox_additions':
217
 
218
  ?>
219
  <fieldset>
220
+ <legend><?php Form::header_title( __( 'Site Title Location', 'autodescription' ) ); ?></legend>
 
 
221
  <p id="tsf-title-location" class="tsf-fields">
222
  <span class="tsf-toblock">
223
+ <input type="radio" name="<?php Form::field_name( 'title_location' ); ?>" id="<?php Form::field_id( 'title_location_left' ); ?>" value="left" <?php checked( $this->get_option( 'title_location' ), 'left' ); ?> />
224
+ <label for="<?php Form::field_id( 'title_location_left' ); ?>">
225
  <span><?php esc_html_e( 'Left:', 'autodescription' ); ?></span>
226
  <?php
227
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
228
+ echo HTML::code_wrap_noesc( $example_left );
229
  ?>
230
  </label>
231
  </span>
232
  <span class="tsf-toblock">
233
+ <input type="radio" name="<?php Form::field_name( 'title_location' ); ?>" id="<?php Form::field_id( 'title_location_right' ); ?>" value="right" <?php checked( $this->get_option( 'title_location' ), 'right' ); ?> />
234
+ <label for="<?php Form::field_id( 'title_location_right' ); ?>">
235
  <span><?php esc_html_e( 'Right:', 'autodescription' ); ?></span>
236
  <?php
237
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped.
238
+ echo HTML::code_wrap_noesc( $example_right );
239
  ?>
240
  </label>
241
  </span>
242
  </p>
243
+ <?php HTML::description( $homepage_has_option ); ?>
244
  </fieldset>
245
 
246
  <hr>
247
 
248
+ <?php Form::header_title( __( 'Site Title', 'autodescription' ) ); ?>
249
  <div id="tsf-title-additions-toggle">
250
  <?php
251
+ $info = HTML::make_info(
252
  __( 'Always brand your titles. Search engines may ignore your titles with this feature enabled.', 'autodescription' ),
253
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#page-titles',
254
  false
255
  );
256
 
257
+ HTML::wrap_fields(
258
+ Form::make_checkbox( [
259
+ 'id' => 'title_rem_additions',
260
+ 'label' => esc_html__( 'Remove site title from the title?', 'autodescription' ) . ' ' . $info,
261
+ 'escape' => false,
262
+ ] ),
 
263
  true
264
  );
265
  ?>
266
  </div>
267
  <?php
268
+ HTML::attention_description( __( 'Note: Only use this option if you are aware of its SEO effects.', 'autodescription' ), false );
269
  echo ' ';
270
+ HTML::description( $homepage_has_option, false );
271
  break;
272
 
273
  case 'the_seo_framework_title_metabox_prefixes':
274
+ Form::header_title( __( 'Title Prefix Options', 'autodescription' ) );
275
+ HTML::description( __( 'For archives, a descriptive prefix may be added to generated titles.', 'autodescription' ) );
 
 
276
 
277
  ?>
278
  <hr>
279
 
280
+ <?php Form::header_title( __( 'Archive Title Prefixes', 'autodescription' ) ); ?>
281
  <div id="tsf-title-prefixes-toggle">
282
  <?php
283
+ $info = HTML::make_info(
284
  __( "The prefix helps visitors and search engines determine what kind of page they're visiting.", 'autodescription' ),
285
  'https://kb.theseoframework.com/?p=34',
286
  false
287
  );
288
+ HTML::wrap_fields(
289
+ Form::make_checkbox( [
290
+ 'id' => 'title_rem_prefixes',
291
+ 'label' => esc_html__( 'Remove term type prefixes from generated archive titles?', 'autodescription' ) . ' ' . $info,
292
+ 'escape' => false,
293
+ ] ),
 
294
  true
295
  );
296
  ?>
inc/views/admin/metaboxes/webmaster-metabox.php CHANGED
@@ -7,6 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
  // Fetch the required instance within this file.
@@ -25,7 +28,7 @@ switch ( $instance ) :
25
  'google' => [
26
  'setting' => 'google_verification',
27
  'label' => __( 'Google Search Console Verification Code', 'autodescription' ),
28
- 'info' => $this->make_info(
29
  __( 'Get the Google verification code.', 'autodescription' ),
30
  'https://www.google.com/webmasters/verification/verification?siteUrl=' . rawurlencode( $site_url ) . '&tid=alternate&vtype=vmeta',
31
  false
@@ -35,7 +38,7 @@ switch ( $instance ) :
35
  'bing' => [
36
  'setting' => 'bing_verification',
37
  'label' => __( 'Bing Webmaster Verification Code', 'autodescription' ),
38
- 'info' => $this->make_info(
39
  __( 'Get the Bing verification code.', 'autodescription' ),
40
  'https://www.bing.com/webmaster/home/addsite?addurl=' . rawurlencode( $site_url ),
41
  false
@@ -45,7 +48,7 @@ switch ( $instance ) :
45
  'yandex' => [
46
  'setting' => 'yandex_verification',
47
  'label' => __( 'Yandex Webmaster Verification Code', 'autodescription' ),
48
- 'info' => $this->make_info(
49
  __( 'Get the Yandex verification code.', 'autodescription' ),
50
  'https://webmaster.yandex.com/sites/add/?hostName=' . rawurlencode( $site_url ),
51
  false
@@ -56,7 +59,7 @@ switch ( $instance ) :
56
  'setting' => 'baidu_verification',
57
  /* translators: literal translation from '百度搜索资源平台'-Code */
58
  'label' => __( 'Baidu Search Resource Platform Code', 'autodescription' ),
59
- 'info' => $this->make_info(
60
  __( 'Get the Baidu verification code.', 'autodescription' ),
61
  'https://ziyuan.baidu.com/login/index?u=/site/siteadd',
62
  false
@@ -66,7 +69,7 @@ switch ( $instance ) :
66
  'pinterest' => [
67
  'setting' => 'pint_verification',
68
  'label' => __( 'Pinterest Analytics Verification Code', 'autodescription' ),
69
- 'info' => $this->make_info(
70
  __( 'Get the Pinterest verification code.', 'autodescription' ),
71
  'https://analytics.pinterest.com/',
72
  false
@@ -75,11 +78,9 @@ switch ( $instance ) :
75
  ],
76
  ];
77
 
78
- ?>
79
- <h4><?php esc_html_e( 'Webmaster Integration Settings', 'autodescription' ); ?></h4>
80
- <?php
81
- $this->description( __( "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes.", 'autodescription' ) );
82
- $this->description( __( "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content.", 'autodescription' ) );
83
 
84
  ?>
85
  <hr>
@@ -88,7 +89,7 @@ switch ( $instance ) :
88
  vprintf(
89
  '<p><label for=%s><strong>%s</strong> %s</label></p>',
90
  [
91
- esc_attr( $this->get_field_id( $setting['setting'] ) ),
92
  esc_html( $setting['label'] ),
93
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- should be escaped in list.
94
  $setting['info'],
@@ -97,8 +98,8 @@ switch ( $instance ) :
97
  vprintf(
98
  '<p><input type=text name=%s class="large-text ltr" id=%s placeholder="%s" value="%s" /></p>',
99
  [
100
- esc_attr( $this->get_field_name( $setting['setting'] ) ),
101
- esc_attr( $this->get_field_id( $setting['setting'] ) ),
102
  esc_attr( $setting['placeholder'] ),
103
  esc_attr( $this->get_option( $setting['setting'] ) ),
104
  ]
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Interpreters\HTML,
11
+ The_SEO_Framework\Interpreters\Form;
12
+
13
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
14
 
15
  // Fetch the required instance within this file.
28
  'google' => [
29
  'setting' => 'google_verification',
30
  'label' => __( 'Google Search Console Verification Code', 'autodescription' ),
31
+ 'info' => HTML::make_info(
32
  __( 'Get the Google verification code.', 'autodescription' ),
33
  'https://www.google.com/webmasters/verification/verification?siteUrl=' . rawurlencode( $site_url ) . '&tid=alternate&vtype=vmeta',
34
  false
38
  'bing' => [
39
  'setting' => 'bing_verification',
40
  'label' => __( 'Bing Webmaster Verification Code', 'autodescription' ),
41
+ 'info' => HTML::make_info(
42
  __( 'Get the Bing verification code.', 'autodescription' ),
43
  'https://www.bing.com/webmaster/home/addsite?addurl=' . rawurlencode( $site_url ),
44
  false
48
  'yandex' => [
49
  'setting' => 'yandex_verification',
50
  'label' => __( 'Yandex Webmaster Verification Code', 'autodescription' ),
51
+ 'info' => HTML::make_info(
52
  __( 'Get the Yandex verification code.', 'autodescription' ),
53
  'https://webmaster.yandex.com/sites/add/?hostName=' . rawurlencode( $site_url ),
54
  false
59
  'setting' => 'baidu_verification',
60
  /* translators: literal translation from '百度搜索资源平台'-Code */
61
  'label' => __( 'Baidu Search Resource Platform Code', 'autodescription' ),
62
+ 'info' => HTML::make_info(
63
  __( 'Get the Baidu verification code.', 'autodescription' ),
64
  'https://ziyuan.baidu.com/login/index?u=/site/siteadd',
65
  false
69
  'pinterest' => [
70
  'setting' => 'pint_verification',
71
  'label' => __( 'Pinterest Analytics Verification Code', 'autodescription' ),
72
+ 'info' => HTML::make_info(
73
  __( 'Get the Pinterest verification code.', 'autodescription' ),
74
  'https://analytics.pinterest.com/',
75
  false
78
  ],
79
  ];
80
 
81
+ Form::header_title( __( 'Webmaster Integration Settings', 'autodescription' ) );
82
+ HTML::description( __( "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes.", 'autodescription' ) );
83
+ HTML::description( __( "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content.", 'autodescription' ) );
 
 
84
 
85
  ?>
86
  <hr>
89
  vprintf(
90
  '<p><label for=%s><strong>%s</strong> %s</label></p>',
91
  [
92
+ esc_attr( Form::get_field_id( $setting['setting'] ) ),
93
  esc_html( $setting['label'] ),
94
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- should be escaped in list.
95
  $setting['info'],
98
  vprintf(
99
  '<p><input type=text name=%s class="large-text ltr" id=%s placeholder="%s" value="%s" /></p>',
100
  [
101
+ esc_attr( Form::get_field_name( $setting['setting'] ) ),
102
+ esc_attr( Form::get_field_id( $setting['setting'] ) ),
103
  esc_attr( $setting['placeholder'] ),
104
  esc_attr( $this->get_option( $setting['setting'] ) ),
105
  ]
inc/views/admin/seo-settings-wrap.php CHANGED
@@ -7,6 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
  $_ays_reset = esc_js( __( 'Are you sure you want to reset all SEO settings to their defaults?', 'autodescription' ) );
@@ -21,7 +24,7 @@ $_save_button = get_submit_button(
21
  $_reset_button = get_submit_button(
22
  __( 'Reset Settings', 'autodescription' ),
23
  'secondary',
24
- $this->get_field_name( 'tsf-settings-reset' ),
25
  false,
26
  [
27
  'id' => '', // we ouput this twice, don't set ID.
@@ -31,14 +34,14 @@ $_reset_button = get_submit_button(
31
 
32
  ?>
33
  <div class="wrap tsf-metaboxes">
34
- <form method="post" action="options.php">
35
  <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
36
  <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
37
  <?php settings_fields( THE_SEO_FRAMEWORK_SITE_OPTIONS ); ?>
38
 
39
- <div class="tsf-top-wrap">
40
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
41
- <p class="tsf-top-buttons">
42
  <?php
43
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- submit_button() escapes (mostly...)
44
  echo $_save_button, $_reset_button;
@@ -46,9 +49,9 @@ $_reset_button = get_submit_button(
46
  </p>
47
  </div>
48
 
49
- <hr class="wp-header-end">
50
 
51
- <div class="tsf-notice-wrap">
52
  <?php
53
  do_action( 'the_seo_framework_setting_notices' );
54
  ?>
@@ -58,7 +61,7 @@ $_reset_button = get_submit_button(
58
  do_action( "{$this->seo_settings_page_hook}_settings_page_boxes", $this->seo_settings_page_hook );
59
  ?>
60
 
61
- <div class="tsf-bottom-buttons">
62
  <?php
63
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- submit_button() escapes (mostly...)
64
  echo $_save_button, $_reset_button;
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Interpreters\HTML,
11
+ The_SEO_Framework\Interpreters\Form;
12
+
13
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
14
 
15
  $_ays_reset = esc_js( __( 'Are you sure you want to reset all SEO settings to their defaults?', 'autodescription' ) );
24
  $_reset_button = get_submit_button(
25
  __( 'Reset Settings', 'autodescription' ),
26
  'secondary',
27
+ Form::get_field_name( 'tsf-settings-reset' ),
28
  false,
29
  [
30
  'id' => '', // we ouput this twice, don't set ID.
34
 
35
  ?>
36
  <div class="wrap tsf-metaboxes">
37
+ <form method=post action=options.php autocomplete=off data-form-type=other>
38
  <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
39
  <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
40
  <?php settings_fields( THE_SEO_FRAMEWORK_SITE_OPTIONS ); ?>
41
 
42
+ <div class=tsf-top-wrap>
43
  <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
44
+ <p class=tsf-top-buttons>
45
  <?php
46
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- submit_button() escapes (mostly...)
47
  echo $_save_button, $_reset_button;
49
  </p>
50
  </div>
51
 
52
+ <hr class=wp-header-end>
53
 
54
+ <div class=tsf-notice-wrap>
55
  <?php
56
  do_action( 'the_seo_framework_setting_notices' );
57
  ?>
61
  do_action( "{$this->seo_settings_page_hook}_settings_page_boxes", $this->seo_settings_page_hook );
62
  ?>
63
 
64
+ <div class=tsf-bottom-buttons>
65
  <?php
66
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- submit_button() escapes (mostly...)
67
  echo $_save_button, $_reset_button;
inc/views/debug/output.php CHANGED
@@ -37,7 +37,7 @@ if ( is_admin() ) {
37
  echo ' :: ';
38
  echo esc_html( 'Type: ' . $type );
39
  echo esc_html( $mdash . 'ID: ' . $id );
40
- echo esc_html( $mdash . 'Cache key: ' . $cache_key );
41
  echo esc_html( $mdash . 'Plugin version: ' . THE_SEO_FRAMEWORK_VERSION );
42
  echo esc_html( $mdash . 'Plugin DB version: c' . get_option( 'the_seo_framework_upgraded_db_version' ) . ' | e' . THE_SEO_FRAMEWORK_DB_VERSION );
43
  endif;
@@ -61,7 +61,7 @@ if ( is_admin() ) {
61
  echo ' :: ';
62
  echo 'Type: ' . esc_html( $type );
63
  echo esc_html( $mdash . 'ID: ' . $id );
64
- echo esc_html( $mdash . 'Cache key: ' . $cache_key );
65
  echo esc_html( $mdash . 'Plugin version: ' . THE_SEO_FRAMEWORK_VERSION );
66
  echo esc_html( $mdash . 'Plugin DB version: c' . get_option( 'the_seo_framework_upgraded_db_version' ) . ' | e' . THE_SEO_FRAMEWORK_DB_VERSION );
67
  ?>
37
  echo ' :: ';
38
  echo esc_html( 'Type: ' . $type );
39
  echo esc_html( $mdash . 'ID: ' . $id );
40
+ echo esc_html( $mdash . 'Cache key: ' . ( $cache_key ?: 'N/A' ) );
41
  echo esc_html( $mdash . 'Plugin version: ' . THE_SEO_FRAMEWORK_VERSION );
42
  echo esc_html( $mdash . 'Plugin DB version: c' . get_option( 'the_seo_framework_upgraded_db_version' ) . ' | e' . THE_SEO_FRAMEWORK_DB_VERSION );
43
  endif;
61
  echo ' :: ';
62
  echo 'Type: ' . esc_html( $type );
63
  echo esc_html( $mdash . 'ID: ' . $id );
64
+ echo esc_html( $mdash . 'Cache key: ' . ( $cache_key ?: 'N/A' ) );
65
  echo esc_html( $mdash . 'Plugin version: ' . THE_SEO_FRAMEWORK_VERSION );
66
  echo esc_html( $mdash . 'Plugin DB version: c' . get_option( 'the_seo_framework_upgraded_db_version' ) . ' | e' . THE_SEO_FRAMEWORK_DB_VERSION );
67
  ?>
inc/views/edit/seo-settings-singular.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\PostSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -119,18 +121,18 @@ switch ( $instance ) :
119
  <div><strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong></div>
120
  <div>
121
  <?php
122
- $this->make_info(
123
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
124
- 'https://support.google.com/webmasters/answer/35624#page-titles'
125
  );
126
  ?>
127
  </div>
128
  </label>
129
  <?php
130
  $this->get_option( 'display_character_counter' )
131
- and $this->output_character_counter_wrap( 'autodescription_title' );
132
  $this->get_option( 'display_pixel_counter' )
133
- and $this->output_pixel_counter_wrap( 'autodescription_title', 'title' );
134
  ?>
135
  </div>
136
  </div>
@@ -167,14 +169,14 @@ switch ( $instance ) :
167
  <?php
168
  esc_html_e( 'Remove the site title?', 'autodescription' );
169
  echo ' ';
170
- $this->make_info( __( 'For the homepage, this option must be managed on the SEO Settings page.', 'autodescription' ) );
171
  else :
172
  ?>
173
  <input type="checkbox" name="autodescription[_tsf_title_no_blogname]" id="autodescription_title_no_blogname" value="1" <?php checked( $this->get_post_meta_item( '_tsf_title_no_blogname' ) ); ?> />
174
  <?php
175
  esc_html_e( 'Remove the site title?', 'autodescription' );
176
  echo ' ';
177
- $this->make_info( __( 'Use this when you want to rearrange the title parts manually.', 'autodescription' ) );
178
  endif;
179
  ?>
180
  </label>
@@ -189,18 +191,18 @@ switch ( $instance ) :
189
  <div><strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong></div>
190
  <div>
191
  <?php
192
- $this->make_info(
193
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
194
- 'https://support.google.com/webmasters/answer/35624#meta-descriptions'
195
  );
196
  ?>
197
  </div>
198
  </label>
199
  <?php
200
  $this->get_option( 'display_character_counter' )
201
- and $this->output_character_counter_wrap( 'autodescription_description' );
202
  $this->get_option( 'display_pixel_counter' )
203
- and $this->output_pixel_counter_wrap( 'autodescription_description', 'description' );
204
  ?>
205
  </div>
206
  </div>
@@ -229,8 +231,9 @@ switch ( $instance ) :
229
  $canonical_placeholder = $this->create_canonical_url( $_generator_args );
230
 
231
  // Get robots defaults.
232
- $r_defaults = $this->robots_meta(
233
  $_generator_args,
 
234
  The_SEO_Framework\ROBOTS_IGNORE_SETTINGS | The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
235
  );
236
  $r_settings = [
@@ -268,9 +271,9 @@ switch ( $instance ) :
268
  <div><strong><?php esc_html_e( 'Canonical URL', 'autodescription' ); ?></strong></div>
269
  <div>
270
  <?php
271
- $this->make_info(
272
  __( 'This urges search engines to go to the outputted URL.', 'autodescription' ),
273
- 'https://support.google.com/webmasters/answer/139066'
274
  );
275
  ?>
276
  </div>
@@ -289,9 +292,9 @@ switch ( $instance ) :
289
  <div><strong><?php esc_html_e( 'Robots Meta Settings', 'autodescription' ); ?></strong></div>
290
  <div>
291
  <?php
292
- $this->make_info(
293
  __( 'These directives may urge robots not to display, follow links on, or create a cached copy of this page.', 'autodescription' ),
294
- 'https://developers.google.com/search/reference/robots_meta_tag#valid-indexing--serving-directives'
295
  );
296
  ?>
297
  </div>
@@ -325,7 +328,7 @@ switch ( $instance ) :
325
  <div class="tsf-flex-setting-input tsf-flex">
326
  <?php
327
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
328
- echo $this->make_single_select_form( [
329
  'id' => $_s['id'],
330
  'class' => 'tsf-select-block',
331
  'name' => sprintf( 'autodescription[%s]', $_s['option'] ),
@@ -399,9 +402,9 @@ switch ( $instance ) :
399
  </div>
400
  <div>
401
  <?php
402
- $this->make_info(
403
  __( 'This will force visitors to go to another URL.', 'autodescription' ),
404
- 'https://support.google.com/webmasters/answer/93633'
405
  );
406
  ?>
407
  </div>
@@ -435,7 +438,7 @@ switch ( $instance ) :
435
  </label>
436
  <?php
437
  $this->get_option( 'display_character_counter' )
438
- and $this->output_character_counter_wrap( 'autodescription_og_title' );
439
  ?>
440
  </div>
441
  </div>
@@ -458,7 +461,7 @@ switch ( $instance ) :
458
  </label>
459
  <?php
460
  $this->get_option( 'display_character_counter' )
461
- and $this->output_character_counter_wrap( 'autodescription_og_description' );
462
  ?>
463
  </div>
464
  </div>
@@ -479,7 +482,7 @@ switch ( $instance ) :
479
  </label>
480
  <?php
481
  $this->get_option( 'display_character_counter' )
482
- and $this->output_character_counter_wrap( 'autodescription_twitter_title' );
483
  ?>
484
  </div>
485
  </div>
@@ -502,7 +505,7 @@ switch ( $instance ) :
502
  </label>
503
  <?php
504
  $this->get_option( 'display_character_counter' )
505
- and $this->output_character_counter_wrap( 'autodescription_twitter_description' );
506
  ?>
507
  </div>
508
  </div>
@@ -532,7 +535,7 @@ switch ( $instance ) :
532
  <div><strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong></div>
533
  <div>
534
  <?php
535
- $this->make_info(
536
  __( "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.", 'autodescription' ),
537
  'https://developers.facebook.com/docs/sharing/best-practices#images'
538
  );
@@ -547,7 +550,7 @@ switch ( $instance ) :
547
  <div class="hide-if-no-tsf-js tsf-social-image-buttons">
548
  <?php
549
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped. (phpcs is broken here?)
550
- echo $this->get_social_image_uploader_form( 'autodescription_socialimage' );
551
  ?>
552
  </div>
553
  </div>
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\PostSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
121
  <div><strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong></div>
122
  <div>
123
  <?php
124
+ HTML::make_info(
125
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
126
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#page-titles'
127
  );
128
  ?>
129
  </div>
130
  </label>
131
  <?php
132
  $this->get_option( 'display_character_counter' )
133
+ and Form::output_character_counter_wrap( 'autodescription_title' );
134
  $this->get_option( 'display_pixel_counter' )
135
+ and Form::output_pixel_counter_wrap( 'autodescription_title', 'title' );
136
  ?>
137
  </div>
138
  </div>
169
  <?php
170
  esc_html_e( 'Remove the site title?', 'autodescription' );
171
  echo ' ';
172
+ HTML::make_info( __( 'For the homepage, this option must be managed on the SEO Settings page.', 'autodescription' ) );
173
  else :
174
  ?>
175
  <input type="checkbox" name="autodescription[_tsf_title_no_blogname]" id="autodescription_title_no_blogname" value="1" <?php checked( $this->get_post_meta_item( '_tsf_title_no_blogname' ) ); ?> />
176
  <?php
177
  esc_html_e( 'Remove the site title?', 'autodescription' );
178
  echo ' ';
179
+ HTML::make_info( __( 'Use this when you want to rearrange the title parts manually.', 'autodescription' ) );
180
  endif;
181
  ?>
182
  </label>
191
  <div><strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong></div>
192
  <div>
193
  <?php
194
+ HTML::make_info(
195
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
196
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#meta-descriptions'
197
  );
198
  ?>
199
  </div>
200
  </label>
201
  <?php
202
  $this->get_option( 'display_character_counter' )
203
+ and Form::output_character_counter_wrap( 'autodescription_description' );
204
  $this->get_option( 'display_pixel_counter' )
205
+ and Form::output_pixel_counter_wrap( 'autodescription_description', 'description' );
206
  ?>
207
  </div>
208
  </div>
231
  $canonical_placeholder = $this->create_canonical_url( $_generator_args );
232
 
233
  // Get robots defaults.
234
+ $r_defaults = $this->generate_robots_meta(
235
  $_generator_args,
236
+ null,
237
  The_SEO_Framework\ROBOTS_IGNORE_SETTINGS | The_SEO_Framework\ROBOTS_IGNORE_PROTECTION
238
  );
239
  $r_settings = [
271
  <div><strong><?php esc_html_e( 'Canonical URL', 'autodescription' ); ?></strong></div>
272
  <div>
273
  <?php
274
+ HTML::make_info(
275
  __( 'This urges search engines to go to the outputted URL.', 'autodescription' ),
276
+ 'https://developers.google.com/search/docs/advanced/crawling/consolidate-duplicate-urls'
277
  );
278
  ?>
279
  </div>
292
  <div><strong><?php esc_html_e( 'Robots Meta Settings', 'autodescription' ); ?></strong></div>
293
  <div>
294
  <?php
295
+ HTML::make_info(
296
  __( 'These directives may urge robots not to display, follow links on, or create a cached copy of this page.', 'autodescription' ),
297
+ 'https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives'
298
  );
299
  ?>
300
  </div>
328
  <div class="tsf-flex-setting-input tsf-flex">
329
  <?php
330
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
331
+ echo Form::make_single_select_form( [
332
  'id' => $_s['id'],
333
  'class' => 'tsf-select-block',
334
  'name' => sprintf( 'autodescription[%s]', $_s['option'] ),
402
  </div>
403
  <div>
404
  <?php
405
+ HTML::make_info(
406
  __( 'This will force visitors to go to another URL.', 'autodescription' ),
407
+ 'https://developers.google.com/search/docs/advanced/crawling/301-redirects'
408
  );
409
  ?>
410
  </div>
438
  </label>
439
  <?php
440
  $this->get_option( 'display_character_counter' )
441
+ and Form::output_character_counter_wrap( 'autodescription_og_title' );
442
  ?>
443
  </div>
444
  </div>
461
  </label>
462
  <?php
463
  $this->get_option( 'display_character_counter' )
464
+ and Form::output_character_counter_wrap( 'autodescription_og_description' );
465
  ?>
466
  </div>
467
  </div>
482
  </label>
483
  <?php
484
  $this->get_option( 'display_character_counter' )
485
+ and Form::output_character_counter_wrap( 'autodescription_twitter_title' );
486
  ?>
487
  </div>
488
  </div>
505
  </label>
506
  <?php
507
  $this->get_option( 'display_character_counter' )
508
+ and Form::output_character_counter_wrap( 'autodescription_twitter_description' );
509
  ?>
510
  </div>
511
  </div>
535
  <div><strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong></div>
536
  <div>
537
  <?php
538
+ HTML::make_info(
539
  __( "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.", 'autodescription' ),
540
  'https://developers.facebook.com/docs/sharing/best-practices#images'
541
  );
550
  <div class="hide-if-no-tsf-js tsf-social-image-buttons">
551
  <?php
552
  // phpcs:ignore, WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped. (phpcs is broken here?)
553
+ echo Form::get_image_uploader_form( [ 'id' => 'autodescription_socialimage' ] );
554
  ?>
555
  </div>
556
  </div>
inc/views/edit/seo-settings-tt.php CHANGED
@@ -7,7 +7,9 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
- use The_SEO_Framework\Bridges\TermSettings;
 
 
11
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
@@ -46,7 +48,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_SETTINGS );
50
 
51
  // TODO reintroduce the info blocks, and place the labels at the left, instead??
52
  $robots_settings = [
@@ -60,7 +62,7 @@ $robots_settings = [
60
  '_value' => $noindex,
61
  '_info' => [
62
  __( 'This tells search engines not to show this term in their search results.', 'autodescription' ),
63
- 'https://support.google.com/webmasters/answer/93710',
64
  ],
65
  ],
66
  'nofollow' => [
@@ -73,7 +75,7 @@ $robots_settings = [
73
  '_value' => $nofollow,
74
  '_info' => [
75
  __( 'This tells search engines not to follow links on this term.', 'autodescription' ),
76
- 'https://support.google.com/webmasters/answer/96569',
77
  ],
78
  ],
79
  'noarchive' => [
@@ -86,7 +88,7 @@ $robots_settings = [
86
  '_value' => $noarchive,
87
  '_info' => [
88
  __( 'This tells search engines not to save a cached copy of this term.', 'autodescription' ),
89
- 'https://support.google.com/webmasters/answer/79812',
90
  ],
91
  ],
92
  ];
@@ -114,17 +116,17 @@ $robots_settings = [
114
  <strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong>
115
  <?php
116
  echo ' ';
117
- $this->make_info(
118
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
119
- 'https://support.google.com/webmasters/answer/35624#page-titles'
120
  );
121
  ?>
122
  </label>
123
  <?php
124
  $this->get_option( 'display_character_counter' )
125
- and $this->output_character_counter_wrap( 'autodescription-meta[doctitle]' );
126
  $this->get_option( 'display_pixel_counter' )
127
- and $this->output_pixel_counter_wrap( 'autodescription-meta[doctitle]', 'title' );
128
  ?>
129
  </th>
130
  <td>
@@ -153,7 +155,7 @@ $robots_settings = [
153
  <?php
154
  esc_html_e( 'Remove the site title?', 'autodescription' );
155
  echo ' ';
156
- $this->make_info( __( 'Use this when you want to rearrange the title parts manually.', 'autodescription' ) );
157
  ?>
158
  </label>
159
  </td>
@@ -165,17 +167,17 @@ $robots_settings = [
165
  <strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong>
166
  <?php
167
  echo ' ';
168
- $this->make_info(
169
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
170
- 'https://support.google.com/webmasters/answer/35624#meta-descriptions'
171
  );
172
  ?>
173
  </label>
174
  <?php
175
  $this->get_option( 'display_character_counter' )
176
- and $this->output_character_counter_wrap( 'autodescription-meta[description]' );
177
  $this->get_option( 'display_pixel_counter' )
178
- and $this->output_pixel_counter_wrap( 'autodescription-meta[description]', 'description' );
179
  ?>
180
  </th>
181
  <td>
@@ -208,7 +210,7 @@ $robots_settings = [
208
  </label>
209
  <?php
210
  $this->get_option( 'display_character_counter' )
211
- and $this->output_character_counter_wrap( 'autodescription-meta[og_title]' );
212
  ?>
213
  </th>
214
  <td>
@@ -225,7 +227,7 @@ $robots_settings = [
225
  </label>
226
  <?php
227
  $this->get_option( 'display_character_counter' )
228
- and $this->output_character_counter_wrap( 'autodescription-meta[og_description]' );
229
  ?>
230
  </th>
231
  <td>
@@ -240,7 +242,7 @@ $robots_settings = [
240
  </label>
241
  <?php
242
  $this->get_option( 'display_character_counter' )
243
- and $this->output_character_counter_wrap( 'autodescription-meta[tw_title]' );
244
  ?>
245
  </th>
246
  <td>
@@ -257,7 +259,7 @@ $robots_settings = [
257
  </label>
258
  <?php
259
  $this->get_option( 'display_character_counter' )
260
- and $this->output_character_counter_wrap( 'autodescription-meta[tw_description]' );
261
  ?>
262
  </th>
263
  <td>
@@ -271,7 +273,7 @@ $robots_settings = [
271
  <strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong>
272
  <?php
273
  echo ' ';
274
- $this->make_info(
275
  __( "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.", 'autodescription' ),
276
  'https://developers.facebook.com/docs/sharing/best-practices#images'
277
  );
@@ -284,7 +286,7 @@ $robots_settings = [
284
  <div class="hide-if-no-tsf-js tsf-term-button-wrap">
285
  <?php
286
  // phpcs:ignore, WordPress.Security.EscapeOutput -- Already escaped.
287
- echo $this->get_social_image_uploader_form( 'autodescription_meta_socialimage' );
288
  ?>
289
  </div>
290
  </td>
@@ -302,9 +304,9 @@ $robots_settings = [
302
  <strong><?php esc_html_e( 'Canonical URL', 'autodescription' ); ?></strong>
303
  <?php
304
  echo ' ';
305
- $this->make_info(
306
  __( 'This urges search engines to go to the outputted URL.', 'autodescription' ),
307
- 'https://support.google.com/webmasters/answer/139066'
308
  );
309
  ?>
310
  </label>
@@ -319,9 +321,9 @@ $robots_settings = [
319
  <?php
320
  esc_html_e( 'Robots Meta Settings', 'autodescription' );
321
  echo ' ';
322
- $this->make_info(
323
  __( 'These directives may urge robots not to display, follow links on, or create a cached copy of this term.', 'autodescription' ),
324
- 'https://developers.google.com/search/reference/robots_meta_tag#valid-indexing--serving-directives'
325
  );
326
  ?>
327
  </th>
@@ -329,7 +331,7 @@ $robots_settings = [
329
  <?php
330
  foreach ( $robots_settings as $_s ) :
331
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
332
- echo $this->make_single_select_form( [
333
  'id' => $_s['id'],
334
  'class' => 'tsf-term-select-wrap',
335
  'name' => $_s['name'],
@@ -360,9 +362,9 @@ $robots_settings = [
360
  <strong><?php esc_html_e( '301 Redirect URL', 'autodescription' ); ?></strong>
361
  <?php
362
  echo ' ';
363
- $this->make_info(
364
  __( 'This will force visitors to go to another URL.', 'autodescription' ),
365
- 'https://support.google.com/webmasters/answer/93633'
366
  );
367
  ?>
368
  </label>
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Bridges\TermSettings,
11
+ The_SEO_Framework\Interpreters\HTML,
12
+ The_SEO_Framework\Interpreters\Form;
13
 
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
48
  $image_placeholder = isset( $image_details['url'] ) ? $image_details['url'] : '';
49
 
50
  $canonical_placeholder = $this->create_canonical_url( $_generator_args ); // implies get_custom_field = false
51
+ $robots_defaults = $this->generate_robots_meta( $_generator_args, null, The_SEO_Framework\ROBOTS_IGNORE_SETTINGS );
52
 
53
  // TODO reintroduce the info blocks, and place the labels at the left, instead??
54
  $robots_settings = [
62
  '_value' => $noindex,
63
  '_info' => [
64
  __( 'This tells search engines not to show this term in their search results.', 'autodescription' ),
65
+ 'https://developers.google.com/search/docs/advanced/crawling/block-indexing',
66
  ],
67
  ],
68
  'nofollow' => [
75
  '_value' => $nofollow,
76
  '_info' => [
77
  __( 'This tells search engines not to follow links on this term.', 'autodescription' ),
78
+ 'https://developers.google.com/search/docs/advanced/guidelines/qualify-outbound-links',
79
  ],
80
  ],
81
  'noarchive' => [
88
  '_value' => $noarchive,
89
  '_info' => [
90
  __( 'This tells search engines not to save a cached copy of this term.', 'autodescription' ),
91
+ 'https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives',
92
  ],
93
  ],
94
  ];
116
  <strong><?php esc_html_e( 'Meta Title', 'autodescription' ); ?></strong>
117
  <?php
118
  echo ' ';
119
+ HTML::make_info(
120
  __( 'The meta title can be used to determine the title used on search engine result pages.', 'autodescription' ),
121
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#page-titles'
122
  );
123
  ?>
124
  </label>
125
  <?php
126
  $this->get_option( 'display_character_counter' )
127
+ and Form::output_character_counter_wrap( 'autodescription-meta[doctitle]' );
128
  $this->get_option( 'display_pixel_counter' )
129
+ and Form::output_pixel_counter_wrap( 'autodescription-meta[doctitle]', 'title' );
130
  ?>
131
  </th>
132
  <td>
155
  <?php
156
  esc_html_e( 'Remove the site title?', 'autodescription' );
157
  echo ' ';
158
+ HTML::make_info( __( 'Use this when you want to rearrange the title parts manually.', 'autodescription' ) );
159
  ?>
160
  </label>
161
  </td>
167
  <strong><?php esc_html_e( 'Meta Description', 'autodescription' ); ?></strong>
168
  <?php
169
  echo ' ';
170
+ HTML::make_info(
171
  __( 'The meta description can be used to determine the text used under the title on search engine results pages.', 'autodescription' ),
172
+ 'https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets#meta-descriptions'
173
  );
174
  ?>
175
  </label>
176
  <?php
177
  $this->get_option( 'display_character_counter' )
178
+ and Form::output_character_counter_wrap( 'autodescription-meta[description]' );
179
  $this->get_option( 'display_pixel_counter' )
180
+ and Form::output_pixel_counter_wrap( 'autodescription-meta[description]', 'description' );
181
  ?>
182
  </th>
183
  <td>
210
  </label>
211
  <?php
212
  $this->get_option( 'display_character_counter' )
213
+ and Form::output_character_counter_wrap( 'autodescription-meta[og_title]' );
214
  ?>
215
  </th>
216
  <td>
227
  </label>
228
  <?php
229
  $this->get_option( 'display_character_counter' )
230
+ and Form::output_character_counter_wrap( 'autodescription-meta[og_description]' );
231
  ?>
232
  </th>
233
  <td>
242
  </label>
243
  <?php
244
  $this->get_option( 'display_character_counter' )
245
+ and Form::output_character_counter_wrap( 'autodescription-meta[tw_title]' );
246
  ?>
247
  </th>
248
  <td>
259
  </label>
260
  <?php
261
  $this->get_option( 'display_character_counter' )
262
+ and Form::output_character_counter_wrap( 'autodescription-meta[tw_description]' );
263
  ?>
264
  </th>
265
  <td>
273
  <strong><?php esc_html_e( 'Social Image URL', 'autodescription' ); ?></strong>
274
  <?php
275
  echo ' ';
276
+ HTML::make_info(
277
  __( "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.", 'autodescription' ),
278
  'https://developers.facebook.com/docs/sharing/best-practices#images'
279
  );
286
  <div class="hide-if-no-tsf-js tsf-term-button-wrap">
287
  <?php
288
  // phpcs:ignore, WordPress.Security.EscapeOutput -- Already escaped.
289
+ echo Form::get_image_uploader_form( [ 'id' => 'autodescription_meta_socialimage' ] );
290
  ?>
291
  </div>
292
  </td>
304
  <strong><?php esc_html_e( 'Canonical URL', 'autodescription' ); ?></strong>
305
  <?php
306
  echo ' ';
307
+ HTML::make_info(
308
  __( 'This urges search engines to go to the outputted URL.', 'autodescription' ),
309
+ 'https://developers.google.com/search/docs/advanced/crawling/consolidate-duplicate-urls'
310
  );
311
  ?>
312
  </label>
321
  <?php
322
  esc_html_e( 'Robots Meta Settings', 'autodescription' );
323
  echo ' ';
324
+ HTML::make_info(
325
  __( 'These directives may urge robots not to display, follow links on, or create a cached copy of this term.', 'autodescription' ),
326
+ 'https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#directives'
327
  );
328
  ?>
329
  </th>
331
  <?php
332
  foreach ( $robots_settings as $_s ) :
333
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
334
+ echo Form::make_single_select_form( [
335
  'id' => $_s['id'],
336
  'class' => 'tsf-term-select-wrap',
337
  'name' => $_s['name'],
362
  <strong><?php esc_html_e( '301 Redirect URL', 'autodescription' ); ?></strong>
363
  <?php
364
  echo ' ';
365
+ HTML::make_info(
366
  __( 'This will force visitors to go to another URL.', 'autodescription' ),
367
+ 'https://developers.google.com/search/docs/advanced/crawling/301-redirects'
368
  );
369
  ?>
370
  </label>
inc/views/list/bulk-post.php CHANGED
@@ -6,6 +6,8 @@
6
  * POST index: autodescription-bulk
7
  */
8
 
 
 
9
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
10
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
11
 
@@ -61,7 +63,7 @@ $robots_settings = [
61
  echo '<label class=clear>';
62
  printf( '<span class=title>%s</span>', esc_html( $_setting['label'] ) );
63
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
64
- echo $this->make_single_select_form( [
65
  'id' => $_setting['id'],
66
  'name' => $_setting['name'],
67
  'options' => [
6
  * POST index: autodescription-bulk
7
  */
8
 
9
+ use The_SEO_Framework\Interpreters\Form;
10
+
11
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
12
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
13
 
63
  echo '<label class=clear>';
64
  printf( '<span class=title>%s</span>', esc_html( $_setting['label'] ) );
65
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
66
+ echo Form::make_single_select_form( [
67
  'id' => $_setting['id'],
68
  'name' => $_setting['name'],
69
  'options' => [
inc/views/list/quick-post.php CHANGED
@@ -9,6 +9,8 @@
9
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
10
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
11
 
 
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
14
  $robots_settings = [
@@ -59,9 +61,9 @@ $robots_settings = [
59
  </label>
60
  <?php
61
  $this->get_option( 'display_character_counter' )
62
- and $this->output_character_counter_wrap( 'autodescription-quick[doctitle]' );
63
  $this->get_option( 'display_pixel_counter' )
64
- and $this->output_pixel_counter_wrap( 'autodescription-quick[doctitle]', 'title' );
65
  ?>
66
  <div class="tsf-pad-input tsf-title-wrap">
67
  <input type=text id=autodescription-quick[doctitle] name=autodescription-quick[doctitle] value />
@@ -76,9 +78,9 @@ $robots_settings = [
76
  </label>
77
  <?php
78
  $this->get_option( 'display_character_counter' )
79
- and $this->output_character_counter_wrap( 'autodescription-quick[description]' );
80
  $this->get_option( 'display_pixel_counter' )
81
- and $this->output_pixel_counter_wrap( 'autodescription-quick[description]', 'description' );
82
  ?>
83
  <div class=tsf-pad-input>
84
  <textarea id=autodescription-quick[description] name=autodescription-quick[description] rows=3 cols=22></textarea>
@@ -104,7 +106,7 @@ $robots_settings = [
104
  echo '<label class=clear>';
105
  printf( '<span class=title>%s</span>', esc_html( $_setting['label'] ) );
106
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
107
- echo $this->make_single_select_form( [
108
  'id' => $_setting['id'],
109
  'name' => $_setting['name'],
110
  'options' => [
9
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
10
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
11
 
12
+ use The_SEO_Framework\Interpreters\Form;
13
+
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
16
  $robots_settings = [
61
  </label>
62
  <?php
63
  $this->get_option( 'display_character_counter' )
64
+ and Form::output_character_counter_wrap( 'autodescription-quick[doctitle]' );
65
  $this->get_option( 'display_pixel_counter' )
66
+ and Form::output_pixel_counter_wrap( 'autodescription-quick[doctitle]', 'title' );
67
  ?>
68
  <div class="tsf-pad-input tsf-title-wrap">
69
  <input type=text id=autodescription-quick[doctitle] name=autodescription-quick[doctitle] value />
78
  </label>
79
  <?php
80
  $this->get_option( 'display_character_counter' )
81
+ and Form::output_character_counter_wrap( 'autodescription-quick[description]' );
82
  $this->get_option( 'display_pixel_counter' )
83
+ and Form::output_pixel_counter_wrap( 'autodescription-quick[description]', 'description' );
84
  ?>
85
  <div class=tsf-pad-input>
86
  <textarea id=autodescription-quick[description] name=autodescription-quick[description] rows=3 cols=22></textarea>
106
  echo '<label class=clear>';
107
  printf( '<span class=title>%s</span>', esc_html( $_setting['label'] ) );
108
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
109
+ echo Form::make_single_select_form( [
110
  'id' => $_setting['id'],
111
  'name' => $_setting['name'],
112
  'options' => [
inc/views/list/quick-term.php CHANGED
@@ -9,6 +9,8 @@
9
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
10
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
11
 
 
 
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
14
  $robots_settings = [
@@ -59,9 +61,9 @@ $robots_settings = [
59
  </label>
60
  <?php
61
  $this->get_option( 'display_character_counter' )
62
- and $this->output_character_counter_wrap( 'autodescription-quick[doctitle]' );
63
  $this->get_option( 'display_pixel_counter' )
64
- and $this->output_pixel_counter_wrap( 'autodescription-quick[doctitle]', 'title' );
65
  ?>
66
  <div class="tsf-pad-input tsf-title-wrap">
67
  <input type=text id=autodescription-quick[doctitle] name=autodescription-quick[doctitle] value />
@@ -76,9 +78,9 @@ $robots_settings = [
76
  </label>
77
  <?php
78
  $this->get_option( 'display_character_counter' )
79
- and $this->output_character_counter_wrap( 'autodescription-quick[description]' );
80
  $this->get_option( 'display_pixel_counter' )
81
- and $this->output_pixel_counter_wrap( 'autodescription-quick[description]', 'description' );
82
  ?>
83
  <div class=tsf-pad-input>
84
  <textarea id=autodescription-quick[description] name=autodescription-quick[description] rows=3 cols=22></textarea>
@@ -104,7 +106,7 @@ $robots_settings = [
104
  echo '<label class=clear>';
105
  printf( '<span class="title">%s</span>', esc_html( $_setting['label'] ) );
106
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
107
- echo $this->make_single_select_form( [
108
  'id' => $_setting['id'],
109
  'name' => $_setting['name'],
110
  'options' => [
9
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
10
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
11
 
12
+ use The_SEO_Framework\Interpreters\Form;
13
+
14
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
15
 
16
  $robots_settings = [
61
  </label>
62
  <?php
63
  $this->get_option( 'display_character_counter' )
64
+ and Form::output_character_counter_wrap( 'autodescription-quick[doctitle]' );
65
  $this->get_option( 'display_pixel_counter' )
66
+ and Form::output_pixel_counter_wrap( 'autodescription-quick[doctitle]', 'title' );
67
  ?>
68
  <div class="tsf-pad-input tsf-title-wrap">
69
  <input type=text id=autodescription-quick[doctitle] name=autodescription-quick[doctitle] value />
78
  </label>
79
  <?php
80
  $this->get_option( 'display_character_counter' )
81
+ and Form::output_character_counter_wrap( 'autodescription-quick[description]' );
82
  $this->get_option( 'display_pixel_counter' )
83
+ and Form::output_pixel_counter_wrap( 'autodescription-quick[description]', 'description' );
84
  ?>
85
  <div class=tsf-pad-input>
86
  <textarea id=autodescription-quick[description] name=autodescription-quick[description] rows=3 cols=22></textarea>
106
  echo '<label class=clear>';
107
  printf( '<span class="title">%s</span>', esc_html( $_setting['label'] ) );
108
  // phpcs:disable, WordPress.Security.EscapeOutput -- make_single_select_form() escapes.
109
+ echo Form::make_single_select_form( [
110
  'id' => $_setting['id'],
111
  'name' => $_setting['name'],
112
  'options' => [
inc/views/notice/persistent.php CHANGED
@@ -7,6 +7,8 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
  if ( ! $message ) return;
@@ -23,10 +25,10 @@ $dismiss_title = __( 'Dismiss this notice', 'default' );
23
  $button_js = sprintf(
24
  '<a class="hide-if-no-tsf-js tsf-dismiss" href="javascript:;" title="%s" %s></a>',
25
  esc_attr( $dismiss_title ),
26
- $this->make_data_attributes( [
27
  'key' => $key,
28
  // Is this the best nonce key key? Capability validation already happened. See `output_dismissible_persistent_notices()`.
29
- 'nonce' => wp_create_nonce( $this->get_dismiss_notice_nonce_action( $key ) ),
30
  ] )
31
  );
32
  $button_nojs = vsprintf(
@@ -38,7 +40,7 @@ $button_nojs = vsprintf(
38
  implode(
39
  '',
40
  [
41
- wp_nonce_field( $this->get_dismiss_notice_nonce_action( $key ), 'tsf-notice-nonce', true, false ),
42
  sprintf(
43
  '<button class="tsf-dismiss" type=submit name=tsf-notice-submit id=tsf-notice-submit[%s] value=%s title="%s">%s</button>',
44
  esc_attr( $key ),
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use The_SEO_Framework\Interpreters\HTML;
11
+
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
13
 
14
  if ( ! $message ) return;
25
  $button_js = sprintf(
26
  '<a class="hide-if-no-tsf-js tsf-dismiss" href="javascript:;" title="%s" %s></a>',
27
  esc_attr( $dismiss_title ),
28
+ HTML::make_data_attributes( [
29
  'key' => $key,
30
  // Is this the best nonce key key? Capability validation already happened. See `output_dismissible_persistent_notices()`.
31
+ 'nonce' => wp_create_nonce( $this->_get_dismiss_notice_nonce_action( $key ) ),
32
  ] )
33
  );
34
  $button_nojs = vsprintf(
40
  implode(
41
  '',
42
  [
43
+ wp_nonce_field( $this->_get_dismiss_notice_nonce_action( $key ), 'tsf_notice_nonce', true, false ),
44
  sprintf(
45
  '<button class="tsf-dismiss" type=submit name=tsf-notice-submit id=tsf-notice-submit[%s] value=%s title="%s">%s</button>',
46
  esc_attr( $key ),
inc/views/profile/author.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /**
3
  * @package The_SEO_Framework\Views\Profile
4
- * @subpackage The_SEO_Framework\Admin\Profile
5
  */
6
 
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
@@ -9,6 +9,23 @@
9
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  ?>
13
  <h2><?php esc_html_e( 'Authorial Info', 'autodescription' ); ?></h2>
14
  <table class="form-table">
@@ -17,16 +34,16 @@ foreach ( $fields as $field => $labels ) :
17
  ?>
18
  <tr class="user-<?php echo esc_attr( $field ); ?>-wrap">
19
  <th><label for="<?php echo esc_attr( $field ); ?>">
20
- <?php echo esc_html( $labels->name ); ?>
21
  </label></th>
22
  <td>
23
  <input
24
- type="<?php echo esc_attr( $labels->type ); ?>"
25
  name="<?php echo esc_attr( $field ); ?>"
26
  id="<?php echo esc_attr( $field ); ?>"
27
- value="<?php echo esc_attr( $labels->value ); ?>"
28
- placeholder="<?php echo esc_attr( $labels->placeholder ); ?>"
29
- class="regular-text <?php echo esc_attr( $labels->class ); ?>" />
30
  <p class="description"><?php esc_html_e( 'This may be shown publicly.', 'autodescription' ); ?></p>
31
  </td>
32
  </tr>
1
  <?php
2
  /**
3
  * @package The_SEO_Framework\Views\Profile
4
+ * @subpackage The_SEO_Framework\Admin\Edit\User
5
  */
6
 
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
9
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and the_seo_framework()->_verify_include_secret( $_secret ) or die;
11
 
12
+ $fields = [
13
+ 'tsf-user-meta[facebook_page]' => [
14
+ 'name' => __( 'Facebook profile page', 'autodescription' ),
15
+ 'type' => 'url',
16
+ 'placeholder' => _x( 'https://www.facebook.com/YourPersonalProfile', 'Example Facebook Personal URL', 'autodescription' ),
17
+ 'value' => $this->get_user_meta_item( 'facebook_page', $user->ID ),
18
+ 'class' => '',
19
+ ],
20
+ 'tsf-user-meta[twitter_page]' => [
21
+ 'name' => __( 'Twitter profile name', 'autodescription' ),
22
+ 'type' => 'text',
23
+ 'placeholder' => _x( '@your-personal-username', 'Twitter @username', 'autodescription' ),
24
+ 'value' => $this->get_user_meta_item( 'twitter_page', $user->ID ),
25
+ 'class' => 'ltr',
26
+ ],
27
+ ];
28
+
29
  ?>
30
  <h2><?php esc_html_e( 'Authorial Info', 'autodescription' ); ?></h2>
31
  <table class="form-table">
34
  ?>
35
  <tr class="user-<?php echo esc_attr( $field ); ?>-wrap">
36
  <th><label for="<?php echo esc_attr( $field ); ?>">
37
+ <?php echo esc_html( $labels['name'] ); ?>
38
  </label></th>
39
  <td>
40
  <input
41
+ type="<?php echo esc_attr( $labels['type'] ); ?>"
42
  name="<?php echo esc_attr( $field ); ?>"
43
  id="<?php echo esc_attr( $field ); ?>"
44
+ value="<?php echo esc_attr( $labels['value'] ); ?>"
45
+ placeholder="<?php echo esc_attr( $labels['placeholder'] ); ?>"
46
+ class="regular-text <?php echo esc_attr( $labels['class'] ); ?>" />
47
  <p class="description"><?php esc_html_e( 'This may be shown publicly.', 'autodescription' ); ?></p>
48
  </td>
49
  </tr>
inc/views/templates/inpost/primary-term-selector.php CHANGED
@@ -25,7 +25,7 @@ $tsf = the_seo_framework();
25
  <script type="text/html" id="tmpl-tsf-primary-term-selector-help">
26
  <span class="tsf-primary-term-selector-help-wrap">
27
  <?php
28
- $tsf->make_info(
29
  sprintf(
30
  /* translators: %s = term name */
31
  esc_html__( 'The buttons below are for primary %s selection.', 'autodescription' ),
25
  <script type="text/html" id="tmpl-tsf-primary-term-selector-help">
26
  <span class="tsf-primary-term-selector-help-wrap">
27
  <?php
28
+ \The_SEO_Framework\Interpreters\HTML::make_info(
29
  sprintf(
30
  /* translators: %s = term name */
31
  esc_html__( 'The buttons below are for primary %s selection.', 'autodescription' ),
inc/views/templates/settings/settings.php CHANGED
@@ -7,13 +7,15 @@
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
 
 
10
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::verify( $_secret ) or die;
11
 
12
  ?>
13
  <script type="text/html" id="tmpl-tsf-disabled-post-type-help">
14
  <span class="tsf-post-type-warning">
15
  <?php
16
- the_seo_framework()->make_info(
17
  esc_html__( "This post type is excluded, so this option won't work.", 'autodescription' )
18
  );
19
  ?>
@@ -23,7 +25,7 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::v
23
  <script type="text/html" id="tmpl-tsf-disabled-taxonomy-help">
24
  <span class="tsf-taxonomy-warning">
25
  <?php
26
- the_seo_framework()->make_info(
27
  esc_html__( "This taxonomy is excluded, so this option won't work.", 'autodescription' )
28
  );
29
  ?>
@@ -33,7 +35,7 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::v
33
  <script type="text/html" id="tmpl-tsf-disabled-taxonomy-from-pt-help">
34
  <span class="tsf-taxonomy-from-pt-warning">
35
  <?php
36
- the_seo_framework()->make_info(
37
  esc_html__( "This taxonomy's post types are also excluded, so this option won't have any effect.", 'autodescription' )
38
  );
39
  ?>
@@ -43,7 +45,7 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::v
43
  <script type="text/html" id="tmpl-tsf-disabled-title-additions-help">
44
  <span class="tsf-title-additions-warning">
45
  <?php
46
- the_seo_framework()->make_info(
47
  esc_html__( 'The site title is already removed from meta titles, so this option only affects the homepage.', 'autodescription' )
48
  );
49
  ?>
@@ -53,7 +55,7 @@ defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::v
53
  <script type="text/html" id="tmpl-tsf-robots-pt-help">
54
  <span class="tsf-taxonomy-from-pt-robots-warning">
55
  <?php
56
- the_seo_framework()->make_info(
57
  esc_html__( "This taxonomy inherited the state from the post type, so this option won't have any effect.", 'autodescription' )
58
  );
59
  ?>
7
  // phpcs:disable, VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- includes.
8
  // phpcs:disable, WordPress.WP.GlobalVariablesOverride -- This isn't the global scope.
9
 
10
+ use \The_SEO_Framework\Interpreters\HTML;
11
+
12
  defined( 'THE_SEO_FRAMEWORK_PRESENT' ) and The_SEO_Framework\Builders\Scripts::verify( $_secret ) or die;
13
 
14
  ?>
15
  <script type="text/html" id="tmpl-tsf-disabled-post-type-help">
16
  <span class="tsf-post-type-warning">
17
  <?php
18
+ HTML::make_info(
19
  esc_html__( "This post type is excluded, so this option won't work.", 'autodescription' )
20
  );
21
  ?>
25
  <script type="text/html" id="tmpl-tsf-disabled-taxonomy-help">
26
  <span class="tsf-taxonomy-warning">
27
  <?php
28
+ HTML::make_info(
29
  esc_html__( "This taxonomy is excluded, so this option won't work.", 'autodescription' )
30
  );
31
  ?>
35
  <script type="text/html" id="tmpl-tsf-disabled-taxonomy-from-pt-help">
36
  <span class="tsf-taxonomy-from-pt-warning">
37
  <?php
38
+ HTML::make_info(
39
  esc_html__( "This taxonomy's post types are also excluded, so this option won't have any effect.", 'autodescription' )
40
  );
41
  ?>
45
  <script type="text/html" id="tmpl-tsf-disabled-title-additions-help">
46
  <span class="tsf-title-additions-warning">
47
  <?php
48
+ HTML::make_info(
49
  esc_html__( 'The site title is already removed from meta titles, so this option only affects the homepage.', 'autodescription' )
50
  );
51
  ?>
55
  <script type="text/html" id="tmpl-tsf-robots-pt-help">
56
  <span class="tsf-taxonomy-from-pt-robots-warning">
57
  <?php
58
+ HTML::make_info(
59
  esc_html__( "This taxonomy inherited the state from the post type, so this option won't have any effect.", 'autodescription' )
60
  );
61
  ?>
language/autodescription.pot CHANGED
@@ -1,15 +1,15 @@
1
- # Copyright (C) 2020 The SEO Framework Team
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"
@@ -37,172 +37,184 @@ msgid "Your website has been downgraded successfully to use The SEO Framework at
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
 
156
- #: inc/classes/admin-pages.class.php:62
157
  msgid "SEO Settings"
158
  msgstr ""
159
 
160
- #: inc/classes/admin-pages.class.php:63
161
  msgid "SEO"
162
  msgstr ""
163
 
164
- #: inc/classes/admin-pages.class.php:198
165
  msgid "SEO settings are saved, and the caches have been flushed."
166
  msgstr ""
167
 
168
- #: inc/classes/admin-pages.class.php:203
169
  msgid "No SEO settings were changed, but the caches have been flushed."
170
  msgstr ""
171
 
172
- #: inc/classes/admin-pages.class.php:208
173
  msgid "SEO settings are reset, and the caches have been flushed."
174
  msgstr ""
175
 
176
- #: inc/classes/admin-pages.class.php:213
177
  msgid "An unknown error occurred saving SEO settings."
178
  msgstr ""
179
 
180
- #: inc/classes/admin-pages.class.php:234
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
@@ -219,58 +231,70 @@ msgstr ""
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
 
226
- #: inc/classes/bridges/scripts.class.php:617
 
 
 
 
 
227
  msgid "Change Image"
228
  msgstr ""
229
 
230
- #: inc/classes/bridges/scripts.class.php:618
231
  msgid "Remove Image"
232
  msgstr ""
233
 
234
- #: inc/classes/bridges/scripts.class.php:620
235
  msgctxt "Frame title"
236
  msgid "Select Social Image"
237
  msgstr ""
238
 
239
- #: inc/classes/bridges/scripts.class.php:621
240
- #: inc/classes/bridges/scripts.class.php:630
241
  msgid "Use this image"
242
  msgstr ""
243
 
244
- #: inc/classes/bridges/scripts.class.php:626
 
 
 
 
 
 
 
245
  msgid "Change Logo"
246
  msgstr ""
247
 
248
- #: inc/classes/bridges/scripts.class.php:627
249
  msgid "Remove Logo"
250
  msgstr ""
251
 
252
- #: inc/classes/bridges/scripts.class.php:629
253
  msgctxt "Frame title"
254
  msgid "Select Logo"
255
  msgstr ""
256
 
257
  #. translators: %s = term name
258
- #: inc/classes/bridges/scripts.class.php:850
259
  msgid "Select Primary %s"
260
  msgstr ""
261
 
262
  #. translators: %s = term name
263
- #: inc/classes/bridges/scripts.class.php:855
264
  msgid "Make primary %s"
265
  msgstr ""
266
 
267
  #. translators: %s = term name
268
- #: inc/classes/bridges/scripts.class.php:857
269
  msgid "Primary %s"
270
  msgstr ""
271
 
272
  #. translators: Pixel counter. 1: number (value), 2: number (guideline)
273
- #: inc/classes/bridges/scripts.class.php:952
274
  msgid "%1$d out of %2$d pixels are used."
275
  msgstr ""
276
 
@@ -299,9 +323,9 @@ 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
 
@@ -547,77 +571,68 @@ msgstr ""
547
  msgid "&#8220;%1$s&#8221; is used %2$d times."
548
  msgstr ""
549
 
550
- #: inc/classes/builders/seobar-page.class.php:576
551
- #: inc/classes/builders/seobar-page.class.php:733
552
- #: inc/classes/builders/seobar-page.class.php:864
553
- #: inc/classes/builders/seobar-term.class.php:537
554
- #: inc/classes/builders/seobar-term.class.php:688
555
- #: inc/classes/builders/seobar-term.class.php:805
556
- msgid "The robots.txt file is nonstandard, and may still direct search engines differently."
557
- msgstr ""
558
-
559
  #: inc/classes/builders/seobar-page.class.php:577
560
  #: inc/classes/builders/seobar-page.class.php:734
561
  #: inc/classes/builders/seobar-page.class.php:865
562
  #: inc/classes/builders/seobar-term.class.php:538
563
  #: inc/classes/builders/seobar-term.class.php:689
564
  #: inc/classes/builders/seobar-term.class.php:806
565
- msgid "WordPress discourages crawling via the Reading Settings."
566
  msgstr ""
567
 
568
  #: inc/classes/builders/seobar-page.class.php:578
 
 
569
  #: inc/classes/builders/seobar-term.class.php:539
570
- msgid "Indexing is discouraged for the whole site at the SEO Settings screen."
 
 
571
  msgstr ""
572
 
573
  #: inc/classes/builders/seobar-page.class.php:579
574
- msgid "Indexing is discouraged for this post type at the SEO Settings screen."
 
575
  msgstr ""
576
 
577
  #: inc/classes/builders/seobar-page.class.php:580
578
- msgid "The page is protected, so indexing is discouraged."
579
  msgstr ""
580
 
581
  #: inc/classes/builders/seobar-page.class.php:581
582
- msgid "The page SEO meta input overrides the indexing state."
583
  msgstr ""
584
 
585
  #: inc/classes/builders/seobar-page.class.php:582
586
- #: inc/classes/builders/seobar-term.class.php:545
587
- msgid "A custom canonical URL is set that points to another page."
588
  msgstr ""
589
 
590
- #: inc/classes/builders/seobar-page.class.php:585
591
- #: inc/classes/builders/seobar-page.class.php:741
592
- #: inc/classes/builders/seobar-page.class.php:872
593
- #: inc/classes/builders/seobar-term.class.php:548
594
- #: inc/classes/builders/seobar-term.class.php:697
595
- #: inc/classes/builders/seobar-term.class.php:814
596
- msgid "WordPress overrides the robots directive."
597
  msgstr ""
598
 
599
  #: inc/classes/builders/seobar-page.class.php:586
600
- msgid "The page is protected."
 
 
 
 
 
601
  msgstr ""
602
 
603
  #: inc/classes/builders/seobar-page.class.php:587
604
- #: inc/classes/builders/seobar-page.class.php:742
605
- #: inc/classes/builders/seobar-page.class.php:873
606
- msgid "The page is not published."
607
  msgstr ""
608
 
609
  #: inc/classes/builders/seobar-page.class.php:588
610
- #: inc/classes/builders/seobar-term.class.php:551
611
- msgid "The canonical URL points to another page."
 
612
  msgstr ""
613
 
614
- #: inc/classes/builders/seobar-page.class.php:592
615
- #: inc/classes/builders/seobar-page.class.php:601
616
- #: inc/classes/builders/seobar-page.class.php:610
617
- #: inc/classes/builders/seobar-term.class.php:555
618
- #: inc/classes/builders/seobar-term.class.php:564
619
- msgctxt "Indexing"
620
- msgid "I"
621
  msgstr ""
622
 
623
  #: inc/classes/builders/seobar-page.class.php:593
@@ -625,199 +640,208 @@ msgstr ""
625
  #: inc/classes/builders/seobar-page.class.php:611
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
633
- #: inc/views/list/quick-term.php:20
 
 
 
 
 
 
 
 
 
634
  msgid "Indexing"
635
  msgstr ""
636
 
637
- #: inc/classes/builders/seobar-page.class.php:595
638
  msgid "Page may be indexed."
639
  msgstr ""
640
 
641
- #: inc/classes/builders/seobar-page.class.php:597
642
- #: inc/classes/builders/seobar-term.class.php:560
643
  msgid "The robots meta tag allows indexing."
644
  msgstr ""
645
 
646
- #: inc/classes/builders/seobar-page.class.php:604
647
  msgid "Page may not be indexed."
648
  msgstr ""
649
 
650
- #: inc/classes/builders/seobar-page.class.php:606
651
- #: inc/classes/builders/seobar-term.class.php:569
652
  msgid "The robots meta tag does not allow indexing."
653
  msgstr ""
654
 
655
- #: inc/classes/builders/seobar-page.class.php:613
656
- #: inc/classes/builders/seobar-page.class.php:767
657
- #: inc/classes/builders/seobar-page.class.php:898
658
  msgid "Page is invisible."
659
  msgstr ""
660
 
661
- #: inc/classes/builders/seobar-page.class.php:615
662
- #: inc/classes/builders/seobar-page.class.php:769
663
- #: inc/classes/builders/seobar-page.class.php:900
664
  msgid "This page isn't published and can't be found publicly."
665
  msgstr ""
666
 
667
- #: inc/classes/builders/seobar-page.class.php:670
668
  msgid "Indexing is discouraged for the homepage at the SEO Settings screen."
669
  msgstr ""
670
 
671
- #: inc/classes/builders/seobar-page.class.php:735
672
- #: inc/classes/builders/seobar-term.class.php:690
673
  msgid "Link following is discouraged for the whole site at the SEO Settings screen."
674
  msgstr ""
675
 
676
- #: inc/classes/builders/seobar-page.class.php:736
677
  msgid "Link following is discouraged for this post type at the SEO Settings screen."
678
  msgstr ""
679
 
680
- #: inc/classes/builders/seobar-page.class.php:737
681
  msgid "The page SEO meta input overrides the link following state."
682
  msgstr ""
683
 
684
- #: inc/classes/builders/seobar-page.class.php:738
685
  msgid "The page may not be indexed, this may also discourage link following."
686
  msgstr ""
687
 
688
- #: inc/classes/builders/seobar-page.class.php:746
689
- #: inc/classes/builders/seobar-page.class.php:755
690
- #: inc/classes/builders/seobar-page.class.php:764
691
- #: inc/classes/builders/seobar-term.class.php:701
692
- #: inc/classes/builders/seobar-term.class.php:710
693
- msgctxt "Following"
694
- msgid "F"
695
- msgstr ""
696
-
697
  #: inc/classes/builders/seobar-page.class.php:747
698
  #: inc/classes/builders/seobar-page.class.php:756
699
  #: inc/classes/builders/seobar-page.class.php:765
700
  #: inc/classes/builders/seobar-term.class.php:702
701
  #: inc/classes/builders/seobar-term.class.php:711
702
- #: inc/views/admin/metaboxes/robots-metabox.php:76
703
- #: inc/views/list/quick-term.php:27
 
 
 
 
 
 
 
 
 
704
  msgid "Following"
705
  msgstr ""
706
 
707
- #: inc/classes/builders/seobar-page.class.php:749
708
  msgid "Page links may be followed."
709
  msgstr ""
710
 
711
- #: inc/classes/builders/seobar-page.class.php:751
712
- #: inc/classes/builders/seobar-term.class.php:706
713
  msgid "The robots meta tag allows link following."
714
  msgstr ""
715
 
716
- #: inc/classes/builders/seobar-page.class.php:758
717
  msgid "Page links may not be followed."
718
  msgstr ""
719
 
720
- #: inc/classes/builders/seobar-page.class.php:760
721
- #: inc/classes/builders/seobar-term.class.php:715
722
  msgid "The robots meta tag does not allow link following."
723
  msgstr ""
724
 
725
- #: inc/classes/builders/seobar-page.class.php:812
726
  msgid "Link following is discouraged for the homepage at the SEO Settings screen."
727
  msgstr ""
728
 
729
- #: inc/classes/builders/seobar-page.class.php:866
730
- #: inc/classes/builders/seobar-term.class.php:807
731
  msgid "Archiving is discouraged for the whole site at the SEO Settings screen."
732
  msgstr ""
733
 
734
- #: inc/classes/builders/seobar-page.class.php:867
735
  msgid "Archiving is discouraged for this post type at the SEO Settings screen."
736
  msgstr ""
737
 
738
- #: inc/classes/builders/seobar-page.class.php:868
739
  msgid "The page SEO meta input overrides the archiving state."
740
  msgstr ""
741
 
742
- #: inc/classes/builders/seobar-page.class.php:869
743
  msgid "The page may not be indexed, this may also discourage archiving."
744
  msgstr ""
745
 
746
- #: inc/classes/builders/seobar-page.class.php:877
747
- #: inc/classes/builders/seobar-page.class.php:886
748
- #: inc/classes/builders/seobar-page.class.php:895
749
- #: inc/classes/builders/seobar-term.class.php:818
750
- #: inc/classes/builders/seobar-term.class.php:827
751
- msgctxt "Archiving"
752
- msgid "A"
753
- msgstr ""
754
-
755
  #: inc/classes/builders/seobar-page.class.php:878
756
  #: inc/classes/builders/seobar-page.class.php:887
757
  #: inc/classes/builders/seobar-page.class.php:896
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
765
- #: inc/views/list/quick-term.php:34
 
 
 
 
 
 
 
 
 
766
  msgid "Archiving"
767
  msgstr ""
768
 
769
- #: inc/classes/builders/seobar-page.class.php:880
770
  msgid "Page may be archived."
771
  msgstr ""
772
 
773
- #: inc/classes/builders/seobar-page.class.php:882
774
- #: inc/classes/builders/seobar-term.class.php:823
775
  msgid "The robots meta tag allows archiving."
776
  msgstr ""
777
 
778
- #: inc/classes/builders/seobar-page.class.php:889
779
  msgid "Page may not be archived."
780
  msgstr ""
781
 
782
- #: inc/classes/builders/seobar-page.class.php:891
783
- #: inc/classes/builders/seobar-term.class.php:832
784
  msgid "The robots meta tag does not allow archiving."
785
  msgstr ""
786
 
787
- #: inc/classes/builders/seobar-page.class.php:943
788
  msgid "Archiving is discouraged for the homepage at the SEO Settings screen."
789
  msgstr ""
790
 
791
- #: inc/classes/builders/seobar-page.class.php:994
792
- #: inc/classes/builders/seobar-page.class.php:1010
793
- #: inc/classes/builders/seobar-term.class.php:919
794
- #: inc/classes/builders/seobar-term.class.php:935
795
- msgctxt "Redirect"
796
- msgid "R"
797
- msgstr ""
798
-
799
  #: inc/classes/builders/seobar-page.class.php:995
800
  #: inc/classes/builders/seobar-page.class.php:1011
801
  #: inc/classes/builders/seobar-term.class.php:920
802
  #: inc/classes/builders/seobar-term.class.php:936
 
 
 
 
 
 
 
 
803
  msgid "Redirection"
804
  msgstr ""
805
 
806
- #: inc/classes/builders/seobar-page.class.php:997
807
  msgid "Page does not redirect visitors."
808
  msgstr ""
809
 
810
- #: inc/classes/builders/seobar-page.class.php:999
811
- #: inc/classes/builders/seobar-term.class.php:924
812
  msgid "All visitors and crawlers may access this page."
813
  msgstr ""
814
 
815
- #: inc/classes/builders/seobar-page.class.php:1013
816
  msgid "Page redirects visitors."
817
  msgstr ""
818
 
819
- #: inc/classes/builders/seobar-page.class.php:1015
820
- #: inc/classes/builders/seobar-term.class.php:940
821
  msgid "All visitors and crawlers are being redirected. So, no other SEO enhancements are effective."
822
  msgstr ""
823
 
@@ -857,95 +881,95 @@ msgstr ""
857
  msgid "It's built from the term SEO meta input."
858
  msgstr ""
859
 
860
- #: inc/classes/builders/seobar-term.class.php:540
861
  msgid "Indexing is discouraged for all bound post types to this term at the SEO Settings screen."
862
  msgstr ""
863
 
864
- #: inc/classes/builders/seobar-term.class.php:541
865
  msgid "Indexing is discouraged for this taxonomy at the SEO Settings screen."
866
  msgstr ""
867
 
868
- #: inc/classes/builders/seobar-term.class.php:542
869
  msgid "The term SEO meta input overrides the indexing state."
870
  msgstr ""
871
 
872
- #: inc/classes/builders/seobar-term.class.php:543
873
  msgid "No posts are attached to this term, so indexing is disabled."
874
  msgstr ""
875
 
876
- #: inc/classes/builders/seobar-term.class.php:544
877
  msgid "No posts are attached to this term, so indexing should be disabled."
878
  msgstr ""
879
 
880
- #: inc/classes/builders/seobar-term.class.php:549
881
  msgid "The term is empty."
882
  msgstr ""
883
 
884
- #: inc/classes/builders/seobar-term.class.php:550
885
  msgid "The term is empty yet still indexed."
886
  msgstr ""
887
 
888
- #: inc/classes/builders/seobar-term.class.php:558
889
  msgid "Term may be indexed."
890
  msgstr ""
891
 
892
- #: inc/classes/builders/seobar-term.class.php:567
893
  msgid "Term may not be indexed."
894
  msgstr ""
895
 
896
- #: inc/classes/builders/seobar-term.class.php:691
897
  msgid "Link following is discouraged for all bound post types to this term at the SEO Settings screen."
898
  msgstr ""
899
 
900
- #: inc/classes/builders/seobar-term.class.php:692
901
  msgid "Link following is discouraged for this taxonomy at the SEO Settings screen."
902
  msgstr ""
903
 
904
- #: inc/classes/builders/seobar-term.class.php:693
905
  msgid "The term SEO meta input overrides the link following state."
906
  msgstr ""
907
 
908
- #: inc/classes/builders/seobar-term.class.php:694
909
  msgid "The term may not be indexed, this may also discourage link following."
910
  msgstr ""
911
 
912
- #: inc/classes/builders/seobar-term.class.php:704
913
  msgid "Term links may be followed."
914
  msgstr ""
915
 
916
- #: inc/classes/builders/seobar-term.class.php:713
917
  msgid "Term links may not be followed."
918
  msgstr ""
919
 
920
- #: inc/classes/builders/seobar-term.class.php:808
921
  msgid "Archiving is discouraged for all bound post types to this term at the SEO Settings screen."
922
  msgstr ""
923
 
924
- #: inc/classes/builders/seobar-term.class.php:809
925
  msgid "Archiving is discouraged for this taxonomy at the SEO Settings screen."
926
  msgstr ""
927
 
928
- #: inc/classes/builders/seobar-term.class.php:810
929
  msgid "The term SEO meta input overrides the archiving state."
930
  msgstr ""
931
 
932
- #: inc/classes/builders/seobar-term.class.php:811
933
  msgid "The term may not be indexed, this may also discourage archiving."
934
  msgstr ""
935
 
936
- #: inc/classes/builders/seobar-term.class.php:821
937
  msgid "Term may be archived."
938
  msgstr ""
939
 
940
- #: inc/classes/builders/seobar-term.class.php:830
941
  msgid "Term may not be archived."
942
  msgstr ""
943
 
944
- #: inc/classes/builders/seobar-term.class.php:922
945
  msgid "Term does not redirect visitors."
946
  msgstr ""
947
 
948
- #: inc/classes/builders/seobar-term.class.php:938
949
  msgid "Term redirects visitors."
950
  msgstr ""
951
 
@@ -959,68 +983,37 @@ msgstr ""
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,68 +1033,61 @@ msgctxt "blog page description"
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
 
1069
- #: inc/classes/profile.class.php:94
1070
- msgid "Facebook profile page"
1071
- msgstr ""
1072
-
1073
- #: inc/classes/profile.class.php:96
1074
- #: inc/views/admin/metaboxes/social-metabox.php:204
1075
- msgctxt "Example Facebook Personal URL"
1076
- msgid "https://www.facebook.com/YourPersonalProfile"
1077
- msgstr ""
1078
-
1079
- #: inc/classes/profile.class.php:101
1080
- msgid "Twitter profile name"
1081
- msgstr ""
1082
-
1083
- #: inc/classes/profile.class.php:103
1084
- #: inc/views/admin/metaboxes/social-metabox.php:276
1085
- msgctxt "Twitter @username"
1086
- msgid "@your-personal-username"
1087
  msgstr ""
1088
 
1089
- #: inc/classes/render.class.php:1158
1090
- msgid "by Sybre Waaijer"
1091
  msgstr ""
1092
 
1093
- #: inc/views/admin/metaboxes/description-metabox.php:18
1094
  msgid "Description Settings"
1095
  msgstr ""
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 ""
1103
 
1104
- #: inc/views/admin/metaboxes/description-metabox.php:27
1105
  msgid "Automated Description Settings"
1106
  msgstr ""
1107
 
@@ -1121,19 +1107,19 @@ msgstr ""
1121
  msgid "Automatically generate descriptions?"
1122
  msgstr ""
1123
 
1124
- #: inc/views/admin/metaboxes/feed-metabox.php:18
1125
  msgid "Content Feed Settings"
1126
  msgstr ""
1127
 
1128
- #: inc/views/admin/metaboxes/feed-metabox.php:20
1129
  msgid "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt."
1130
  msgstr ""
1131
 
1132
- #: inc/views/admin/metaboxes/feed-metabox.php:21
1133
  msgid "Adding a backlink below the feed entries will also let the visitors know where the content came from."
1134
  msgstr ""
1135
 
1136
- #: inc/views/admin/metaboxes/feed-metabox.php:26
1137
  msgid "Change Feed Settings"
1138
  msgstr ""
1139
 
@@ -1162,35 +1148,35 @@ msgid "If this site publishes podcasts, enable this option. Otherwise, leave it
1162
  msgstr ""
1163
 
1164
  #. translators: %s = Reading Settings URL. Links are in Markdown!
1165
- #: inc/views/admin/metaboxes/feed-metabox.php:51
1166
  msgid "Note: The feed is already converted into an excerpt through the [Reading Settings](%s)."
1167
  msgstr ""
1168
 
1169
- #: inc/views/admin/metaboxes/feed-metabox.php:64
1170
  msgid "View the main feed."
1171
  msgstr ""
1172
 
1173
- #: inc/views/admin/metaboxes/general-metabox.php:21
1174
  msgid "Layout"
1175
  msgstr ""
1176
 
1177
- #: inc/views/admin/metaboxes/general-metabox.php:26
1178
  msgid "Performance"
1179
  msgstr ""
1180
 
1181
- #: inc/views/admin/metaboxes/general-metabox.php:31
1182
  msgid "Canonical"
1183
  msgstr ""
1184
 
1185
- #: inc/views/admin/metaboxes/general-metabox.php:36
1186
  msgid "Timestamps"
1187
  msgstr ""
1188
 
1189
- #: inc/views/admin/metaboxes/general-metabox.php:41
1190
  msgid "Exclusions"
1191
  msgstr ""
1192
 
1193
- #: inc/views/admin/metaboxes/general-metabox.php:61
1194
  msgid "Administrative Layout Settings"
1195
  msgstr ""
1196
 
@@ -1202,327 +1188,315 @@ msgstr ""
1202
  msgid "SEO Bar Settings"
1203
  msgstr ""
1204
 
1205
- #: inc/views/admin/metaboxes/general-metabox.php:74
1206
  msgid "Display the SEO Bar in overview tables?"
1207
  msgstr ""
1208
 
1209
- #: inc/views/admin/metaboxes/general-metabox.php:80
1210
  msgid "Display the SEO Bar in the SEO Settings metabox?"
1211
  msgstr ""
1212
 
1213
- #: inc/views/admin/metaboxes/general-metabox.php:86
1214
  msgid "Use symbols for warnings?"
1215
  msgstr ""
1216
 
1217
- #: inc/views/admin/metaboxes/general-metabox.php:87
1218
  msgid "If you have difficulty discerning colors, this may help you spot issues more easily."
1219
  msgstr ""
1220
 
1221
- #: inc/views/admin/metaboxes/general-metabox.php:101
1222
  msgid "Counter Settings"
1223
  msgstr ""
1224
 
1225
- #: inc/views/admin/metaboxes/general-metabox.php:105
1226
  msgid "The pixel counter computes whether the input will fit on search engine result pages."
1227
  msgstr ""
1228
 
1229
- #: inc/views/admin/metaboxes/general-metabox.php:111
1230
  msgid "The character counter is based on guidelines."
1231
  msgstr ""
1232
 
1233
- #: inc/views/admin/metaboxes/general-metabox.php:120
1234
  msgid "Display pixel counters?"
1235
  msgstr ""
1236
 
1237
- #: inc/views/admin/metaboxes/general-metabox.php:126
1238
  msgid "Display character counters?"
1239
  msgstr ""
1240
 
1241
- #: inc/views/admin/metaboxes/general-metabox.php:137
1242
  msgid "Performance Settings"
1243
  msgstr ""
1244
 
1245
- #: inc/views/admin/metaboxes/general-metabox.php:139
1246
  msgid "Depending on your server's configuration, adjusting these settings can affect performance."
1247
  msgstr ""
1248
 
1249
- #: inc/views/admin/metaboxes/general-metabox.php:144
1250
  msgid "Query Alteration Settings"
1251
  msgstr ""
1252
 
1253
- #: inc/views/admin/metaboxes/general-metabox.php:147
1254
  msgid "Altering the query allows for more control of the site's hierarchy."
1255
  msgstr ""
1256
 
1257
- #: inc/views/admin/metaboxes/general-metabox.php:149
1258
  msgid "If your website has thousands of pages, these options can greatly affect database performance."
1259
  msgstr ""
1260
 
1261
- #: inc/views/admin/metaboxes/general-metabox.php:153
1262
  msgid "Altering the query in the database is more accurate, but can increase database query time."
1263
  msgstr ""
1264
 
1265
- #: inc/views/admin/metaboxes/general-metabox.php:155
1266
  msgid "Altering the query on the site is much faster, but can lead to inconsistent pagination. It can also lead to 404 error messages if all queried pages have been excluded."
1267
  msgstr ""
1268
 
1269
- #: inc/views/admin/metaboxes/general-metabox.php:161
1270
  msgctxt "Perform query alteration: In the database"
1271
  msgid "In the database"
1272
  msgstr ""
1273
 
1274
- #: inc/views/admin/metaboxes/general-metabox.php:162
1275
  msgctxt "Perform query alteration: On the site"
1276
  msgid "On the site"
1277
  msgstr ""
1278
 
1279
- #: inc/views/admin/metaboxes/general-metabox.php:192
1280
  msgid "Perform alteration:"
1281
  msgstr ""
1282
 
1283
- #: inc/views/admin/metaboxes/general-metabox.php:220
1284
  msgid "Enable search query alteration?"
1285
  msgstr ""
1286
 
1287
- #: inc/views/admin/metaboxes/general-metabox.php:221
1288
  msgid "This allows you to exclude pages from on-site search results."
1289
  msgstr ""
1290
 
1291
- #: inc/views/admin/metaboxes/general-metabox.php:234
1292
  msgid "Enable archive query alteration?"
1293
  msgstr ""
1294
 
1295
- #: inc/views/admin/metaboxes/general-metabox.php:235
1296
  msgid "This allows you to exclude pages from on-site archive listings."
1297
  msgstr ""
1298
 
1299
- #: inc/views/admin/metaboxes/general-metabox.php:246
1300
  msgid "Transient Cache Settings"
1301
  msgstr ""
1302
 
1303
- #: inc/views/admin/metaboxes/general-metabox.php:248
1304
  msgid "To improve performance, generated output can be stored in the database as transient cache."
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
1312
  msgid "Generating the sitemap can use a lot of server resources."
1313
  msgstr ""
1314
 
1315
- #: inc/views/admin/metaboxes/general-metabox.php:265
1316
- msgid "Object Cache Settings"
1317
- msgstr ""
1318
-
1319
- #: inc/views/admin/metaboxes/general-metabox.php:271
1320
- msgid "Enable object cache?"
1321
- msgstr ""
1322
-
1323
- #: inc/views/admin/metaboxes/general-metabox.php:272
1324
- msgid "An object cache handler has been detected."
1325
- msgstr ""
1326
-
1327
- #: inc/views/admin/metaboxes/general-metabox.php:282
1328
  msgid "Canonical URL Settings"
1329
  msgstr ""
1330
 
1331
- #: inc/views/admin/metaboxes/general-metabox.php:284
1332
  msgid "The canonical URL meta tag urges search engines to go to the outputted URL."
1333
  msgstr ""
1334
 
1335
- #: inc/views/admin/metaboxes/general-metabox.php:285
1336
  msgid "If the canonical URL meta tag represents the visited page, then the search engine will crawl the visited page. Otherwise, the search engine may go to the outputted URL."
1337
  msgstr ""
1338
 
1339
- #: inc/views/admin/metaboxes/general-metabox.php:289
1340
  msgid "Scheme Settings"
1341
  msgstr ""
1342
 
1343
- #: inc/views/admin/metaboxes/general-metabox.php:291
1344
  msgid "If your website is accessible via both HTTP as HTTPS, you may want to set this to HTTPS if not detected automatically. Secure connections are preferred by search engines."
1345
  msgstr ""
1346
 
1347
- #: inc/views/admin/metaboxes/general-metabox.php:293
1348
  msgctxt "= Detect Automatically, HTTPS, HTTP"
1349
  msgid "Preferred canonical URL scheme:"
1350
  msgstr ""
1351
 
1352
  #. translators: %s = HTTP or HTTPS
1353
- #: inc/views/admin/metaboxes/general-metabox.php:301
1354
  msgid "Detect automatically (%s)"
1355
  msgstr ""
1356
 
1357
- #: inc/views/admin/metaboxes/general-metabox.php:315
1358
  msgid "Link Relationship Settings"
1359
  msgstr ""
1360
 
1361
- #: inc/views/admin/metaboxes/general-metabox.php:317
1362
  msgid "Some search engines look for relations between the content of your pages. If you have pagination on a post or page, or have archives indexed, these options will help search engines look for the right page to display in the search results."
1363
  msgstr ""
1364
 
1365
- #: inc/views/admin/metaboxes/general-metabox.php:318
1366
  msgid "It's recommended to turn these options on for better SEO consistency and to prevent duplicated content issues."
1367
  msgstr ""
1368
 
1369
  #. translators: the backticks are Markdown! Preserve them as-is!
1370
- #: inc/views/admin/metaboxes/general-metabox.php:324
1371
  msgid "Add `rel` link tags to posts and pages?"
1372
  msgstr ""
1373
 
1374
  #. translators: the backticks are Markdown! Preserve them as-is!
1375
- #: inc/views/admin/metaboxes/general-metabox.php:335
1376
  msgid "Add `rel` link tags to archives?"
1377
  msgstr ""
1378
 
1379
  #. translators: the backticks are Markdown! Preserve them as-is!
1380
- #: inc/views/admin/metaboxes/general-metabox.php:346
1381
  msgid "Add `rel` link tags to the homepage?"
1382
  msgstr ""
1383
 
1384
- #: inc/views/admin/metaboxes/general-metabox.php:367
1385
  msgid "Timestamp Settings"
1386
  msgstr ""
1387
 
1388
- #: inc/views/admin/metaboxes/general-metabox.php:369
1389
  msgid "Timestamps help indicate when a page has been published and modified."
1390
  msgstr ""
1391
 
1392
- #: inc/views/admin/metaboxes/general-metabox.php:375
1393
  msgid "Timestamp Format Settings"
1394
  msgstr ""
1395
 
1396
- #: inc/views/admin/metaboxes/general-metabox.php:377
1397
  msgid "This setting determines how specific the timestamp is."
1398
  msgstr ""
1399
 
1400
- #: inc/views/admin/metaboxes/general-metabox.php:388
1401
  msgid "This outputs the complete date."
1402
  msgstr ""
1403
 
1404
- #: inc/views/admin/metaboxes/general-metabox.php:401
1405
  msgid "This outputs the complete date including hours, minutes, and timezone."
1406
  msgstr ""
1407
 
1408
- #: inc/views/admin/metaboxes/general-metabox.php:416
1409
  msgid "Exclusion Settings"
1410
  msgstr ""
1411
 
1412
- #: inc/views/admin/metaboxes/general-metabox.php:418
1413
  msgid "When checked, these options will remove meta optimizations, SEO suggestions, and sitemap inclusions for the selected post types and taxonomies. This will allow search engines to crawl the post type and taxonomies without advanced restrictions or directions."
1414
  msgstr ""
1415
 
1416
  #. translators: backticks are code wraps. Markdown!
1417
- #: inc/views/admin/metaboxes/general-metabox.php:422
1418
  msgid "These options should not need changing when post types and taxonomies are registered correctly. When they aren't, consider applying `noindex` to purge them from search engines, instead."
1419
  msgstr ""
1420
 
1421
- #: inc/views/admin/metaboxes/general-metabox.php:426
1422
  msgid "Default post types and taxonomies can not be excluded."
1423
  msgstr ""
1424
 
1425
- #: inc/views/admin/metaboxes/general-metabox.php:431
1426
  msgid "Post Type Exclusions"
1427
  msgstr ""
1428
 
1429
- #: inc/views/admin/metaboxes/general-metabox.php:433
1430
  msgid "Select post types which should be excluded."
1431
  msgstr ""
1432
 
1433
- #: inc/views/admin/metaboxes/general-metabox.php:434
1434
- #: inc/views/admin/metaboxes/robots-metabox.php:321
1435
  msgid "These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect."
1436
  msgstr ""
1437
 
1438
- #: inc/views/admin/metaboxes/general-metabox.php:468
1439
  msgid "Taxonomy Exclusions"
1440
  msgstr ""
1441
 
1442
- #: inc/views/admin/metaboxes/general-metabox.php:470
1443
  msgid "Select taxonomies which should be excluded."
1444
  msgstr ""
1445
 
1446
- #: inc/views/admin/metaboxes/general-metabox.php:471
1447
  msgid "When taxonomies have all their bound post types excluded, they will inherit their exclusion status."
1448
  msgstr ""
1449
 
1450
- #: inc/views/admin/metaboxes/homepage-metabox.php:26
1451
  msgid "These settings will take precedence over the settings set within the homepage edit screen, if any."
1452
  msgstr ""
1453
 
1454
- #: inc/views/admin/metaboxes/homepage-metabox.php:33
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
 
1468
- #: inc/views/admin/metaboxes/homepage-metabox.php:43
1469
- #: inc/views/edit/seo-settings-singular.php:34
1470
  msgid "Social"
1471
  msgstr ""
1472
 
1473
- #: inc/views/admin/metaboxes/homepage-metabox.php:48
1474
  msgid "Robots"
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
1482
  msgid "Meta Title"
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 ""
1490
 
1491
- #: inc/views/admin/metaboxes/homepage-metabox.php:108
1492
  msgid "Note: The input value of this field may be used to describe the name of the site elsewhere."
1493
  msgstr ""
1494
 
1495
- #: inc/views/admin/metaboxes/homepage-metabox.php:111
1496
- #: inc/views/admin/metaboxes/homepage-metabox.php:299
1497
- #: inc/views/admin/metaboxes/homepage-metabox.php:348
1498
  msgid "Note: The title placeholder is fetched from the Page SEO Settings on the homepage."
1499
  msgstr ""
1500
 
1501
  #. translators: %s = Homepage URL markdown
1502
- #: inc/views/admin/metaboxes/homepage-metabox.php:124
1503
- #: inc/views/admin/metaboxes/homepage-metabox.php:187
1504
  msgid "A plugin has been detected that suggests to maintain this option on the [homepage](%s)."
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
1512
  msgid "Meta Description"
1513
  msgstr ""
1514
 
1515
- #: inc/views/admin/metaboxes/homepage-metabox.php:173
1516
- #: inc/views/admin/metaboxes/homepage-metabox.php:323
1517
- #: inc/views/admin/metaboxes/homepage-metabox.php:372
1518
  msgid "Note: The description placeholder is fetched from the Page SEO Settings on the homepage."
1519
  msgstr ""
1520
 
1521
- #: inc/views/admin/metaboxes/homepage-metabox.php:216
1522
  msgid "Meta Title Additions"
1523
  msgstr ""
1524
 
1525
- #: inc/views/admin/metaboxes/homepage-metabox.php:228
1526
  msgid "Add Meta Title Additions to the homepage title?"
1527
  msgstr ""
1528
 
@@ -1530,158 +1504,158 @@ msgstr ""
1530
  msgid "Meta Title Additions Location"
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 ""
1566
 
1567
- #: inc/views/admin/metaboxes/homepage-metabox.php:378
1568
- #: inc/views/admin/metaboxes/social-metabox.php:141
1569
  msgid "Social Image Settings"
1570
  msgstr ""
1571
 
1572
- #: inc/views/admin/metaboxes/homepage-metabox.php:380
1573
  msgid "A social image can be displayed when your homepage is shared. It is a great way to grab attention."
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 ""
1587
 
1588
- #: inc/views/admin/metaboxes/homepage-metabox.php:430
1589
  msgctxt "Bear with me: the homepage can be edited globally, or via its page. Thus \"homepage page\"."
1590
  msgid "Edit homepage page settings"
1591
  msgstr ""
1592
 
1593
- #: inc/views/admin/metaboxes/homepage-metabox.php:431
1594
  msgid "Overwritten by page settings"
1595
  msgstr ""
1596
 
1597
  #. translators: 1: Option label, 2: [?] option info note, 3: Optional warning
1598
- #: inc/views/admin/metaboxes/homepage-metabox.php:443
1599
- #: inc/views/admin/metaboxes/homepage-metabox.php:459
1600
- #: inc/views/admin/metaboxes/homepage-metabox.php:475
1601
  msgctxt "robots setting"
1602
  msgid "%1$s %2$s %3$s"
1603
  msgstr ""
1604
 
1605
  #. translators: the backticks are Markdown! Preserve them as-is!
1606
- #: inc/views/admin/metaboxes/homepage-metabox.php:446
1607
  msgid "Apply `noindex` to the homepage?"
1608
  msgstr ""
1609
 
1610
- #: inc/views/admin/metaboxes/homepage-metabox.php:450
1611
  msgid "This tells search engines not to show this page in their search results."
1612
  msgstr ""
1613
 
1614
  #. translators: the backticks are Markdown! Preserve them as-is!
1615
- #: inc/views/admin/metaboxes/homepage-metabox.php:462
1616
  msgid "Apply `nofollow` to the homepage?"
1617
  msgstr ""
1618
 
1619
- #: inc/views/admin/metaboxes/homepage-metabox.php:466
1620
  msgid "This tells search engines not to follow links on this page."
1621
  msgstr ""
1622
 
1623
  #. translators: the backticks are Markdown! Preserve them as-is!
1624
- #: inc/views/admin/metaboxes/homepage-metabox.php:478
1625
  msgid "Apply `noarchive` to the homepage?"
1626
  msgstr ""
1627
 
1628
- #: inc/views/admin/metaboxes/homepage-metabox.php:482
1629
  msgid "This tells search engines not to save a cached copy of this page."
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
 
1637
  #. translators: %s = Homepage URL markdown
1638
- #: inc/views/admin/metaboxes/homepage-metabox.php:520
1639
  msgid "Note: These options may be overwritten by the [page settings](%s)."
1640
  msgstr ""
1641
 
1642
- #: inc/views/admin/metaboxes/homepage-metabox.php:532
1643
  msgid "Homepage Pagination Robots Settings"
1644
  msgstr ""
1645
 
1646
- #: inc/views/admin/metaboxes/homepage-metabox.php:534
1647
  msgid "If your homepage is paginated and outputs content that's also found elsewhere on the website, enabling this option may prevent duplicate content."
1648
  msgstr ""
1649
 
1650
  #. translators: the backticks are Markdown! Preserve them as-is!
1651
- #: inc/views/admin/metaboxes/homepage-metabox.php:542
1652
  msgid "Apply `noindex` to every second or later page on the homepage?"
1653
  msgstr ""
1654
 
1655
- #: inc/views/admin/metaboxes/robots-metabox.php:21
1656
  msgid "Author pages"
1657
  msgstr ""
1658
 
1659
- #: inc/views/admin/metaboxes/robots-metabox.php:25
1660
  msgid "Date archives"
1661
  msgstr ""
1662
 
1663
- #: inc/views/admin/metaboxes/robots-metabox.php:29
1664
  msgid "Search pages"
1665
  msgstr ""
1666
 
1667
- #: inc/views/admin/metaboxes/robots-metabox.php:33
1668
  msgctxt "...for the entire site"
1669
  msgid "the entire site"
1670
  msgstr ""
1671
 
1672
- #: inc/views/admin/metaboxes/robots-metabox.php:45
1673
  msgid "These options most likely prevent indexing of the selected archives and pages. If you enable this, the selected archives or pages will urge to be removed from search engine results pages."
1674
  msgstr ""
1675
 
1676
- #: inc/views/admin/metaboxes/robots-metabox.php:49
1677
  msgid "These options most likely prevent links from being followed on the selected archives and pages. If you enable this, the selected archives or pages in-page links will gain no SEO value, including your internal links."
1678
  msgstr ""
1679
 
1680
- #: inc/views/admin/metaboxes/robots-metabox.php:53
1681
  msgid "These options most likely prevent caching of the selected archives and pages. If you enable this, bots are urged not create a cached copy of the selected archives or pages."
1682
  msgstr ""
1683
 
1684
- #: inc/views/admin/metaboxes/robots-metabox.php:113
1685
  msgid "Advanced Query Protection"
1686
  msgstr ""
1687
 
@@ -1693,159 +1667,159 @@ msgstr ""
1693
  msgid "Enable advanced query protection?"
1694
  msgstr ""
1695
 
1696
- #: inc/views/admin/metaboxes/robots-metabox.php:129
1697
  msgid "Paginated Archive Settings"
1698
  msgstr ""
1699
 
1700
- #: inc/views/admin/metaboxes/robots-metabox.php:131
1701
  msgid "Indexing the second or later page of any archive might cause duplication errors. Search engines look down upon them; therefore, it's recommended to disable indexing of those pages."
1702
  msgstr ""
1703
 
1704
  #. translators: the backticks are Markdown! Preserve them as-is!
1705
- #: inc/views/admin/metaboxes/robots-metabox.php:138
1706
  msgid "Apply `noindex` to every second or later archive page?"
1707
  msgstr ""
1708
 
1709
- #: inc/views/admin/metaboxes/robots-metabox.php:149
1710
  msgid "Copyright Directive Settings"
1711
  msgstr ""
1712
 
1713
- #: inc/views/admin/metaboxes/robots-metabox.php:151
1714
  msgid "Some search engines allow you to control copyright directives on the content they aggregate. It's best to allow some content to be taken by these aggregators, as that can improve contextualized exposure via snippets and previews. When left unspecified, regional regulations may apply. It is up to the aggregator to honor these requests."
1715
  msgstr ""
1716
 
1717
- #: inc/views/admin/metaboxes/robots-metabox.php:156
1718
  msgid "Specify aggregator copyright compliance directives?"
1719
  msgstr ""
1720
 
1721
- #: inc/views/admin/metaboxes/robots-metabox.php:164
1722
  msgid "Unlimited"
1723
  msgstr ""
1724
 
1725
- #: inc/views/admin/metaboxes/robots-metabox.php:165
1726
  msgctxt "quanity: zero"
1727
  msgid "None, disallow snippet"
1728
  msgstr ""
1729
 
1730
  #. translators: %d = number
1731
- #: inc/views/admin/metaboxes/robots-metabox.php:169
1732
  msgid "%d character"
1733
  msgid_plural "%d characters"
1734
  msgstr[0] ""
1735
  msgstr[1] ""
1736
 
1737
- #: inc/views/admin/metaboxes/robots-metabox.php:175
1738
- #: inc/views/admin/metaboxes/robots-metabox.php:261
1739
  msgid "Standard directive"
1740
  msgstr ""
1741
 
1742
- #: inc/views/admin/metaboxes/robots-metabox.php:176
1743
- #: inc/views/admin/metaboxes/robots-metabox.php:262
1744
  msgid "Granular directive"
1745
  msgstr ""
1746
 
1747
- #: inc/views/admin/metaboxes/robots-metabox.php:199
1748
  msgid "Maximum text snippet length"
1749
  msgstr ""
1750
 
1751
- #: inc/views/admin/metaboxes/robots-metabox.php:203
1752
  msgid "This may limit the text snippet length for all pages on this site."
1753
  msgstr ""
1754
 
1755
- #: inc/views/admin/metaboxes/robots-metabox.php:207
1756
  msgid "This directive also imposes a limit on meta descriptions and structured data, which unintentionally restricts the amount of information you can share. Therefore, it's best to use at least a 320 character limit."
1757
  msgstr ""
1758
 
1759
- #: inc/views/admin/metaboxes/robots-metabox.php:216
1760
  msgctxt "quanity: zero"
1761
  msgid "None, disallow preview"
1762
  msgstr ""
1763
 
1764
- #: inc/views/admin/metaboxes/robots-metabox.php:217
1765
  msgid "Thumbnail or standard size"
1766
  msgstr ""
1767
 
1768
- #: inc/views/admin/metaboxes/robots-metabox.php:218
1769
  msgid "Large or full size"
1770
  msgstr ""
1771
 
1772
- #: inc/views/admin/metaboxes/robots-metabox.php:236
1773
  msgid "Maximum image preview size"
1774
  msgstr ""
1775
 
1776
- #: inc/views/admin/metaboxes/robots-metabox.php:240
1777
  msgid "This may limit the image preview size for all images from this site."
1778
  msgstr ""
1779
 
1780
- #: inc/views/admin/metaboxes/robots-metabox.php:250
1781
  msgid "Full video preview"
1782
  msgstr ""
1783
 
1784
- #: inc/views/admin/metaboxes/robots-metabox.php:251
1785
  msgctxt "quanity: zero"
1786
  msgid "None, still image only"
1787
  msgstr ""
1788
 
1789
  #. translators: %d = number
1790
- #: inc/views/admin/metaboxes/robots-metabox.php:255
1791
  msgid "%d second"
1792
  msgid_plural "%d seconds"
1793
  msgstr[0] ""
1794
  msgstr[1] ""
1795
 
1796
- #: inc/views/admin/metaboxes/robots-metabox.php:284
1797
  msgid "Maximum video preview length"
1798
  msgstr ""
1799
 
1800
- #: inc/views/admin/metaboxes/robots-metabox.php:288
1801
  msgid "This may limit the video preview length for all videos on this site."
1802
  msgstr ""
1803
 
1804
  #. translators: SINGULAR. 1 = noindex/nofollow/noarchive, 2 = The entire site
1805
- #: inc/views/admin/metaboxes/robots-metabox.php:303
1806
  msgctxt "singular"
1807
  msgid "Apply %1$s to %2$s?"
1808
  msgstr ""
1809
 
1810
  #. translators: PLURAL. 1 = noindex/nofollow/noarchive, 2 = Archives, Posts, Pages, etc.
1811
- #: inc/views/admin/metaboxes/robots-metabox.php:305
1812
  msgctxt "plural"
1813
  msgid "Apply %1$s to %2$s?"
1814
  msgstr ""
1815
 
1816
- #: inc/views/admin/metaboxes/robots-metabox.php:313
1817
  msgid "Robots Settings"
1818
  msgstr ""
1819
 
1820
- #: inc/views/admin/metaboxes/robots-metabox.php:319
1821
  msgid "Post Type Settings"
1822
  msgstr ""
1823
 
1824
- #: inc/views/admin/metaboxes/robots-metabox.php:327
1825
  msgid "Warning: No site should enable these options for Posts and Pages."
1826
  msgstr ""
1827
 
1828
- #: inc/views/admin/metaboxes/robots-metabox.php:358
1829
  msgid "Taxonomy Settings"
1830
  msgstr ""
1831
 
1832
- #: inc/views/admin/metaboxes/robots-metabox.php:360
1833
  msgid "These settings apply to the taxonomies of post types. When taxonomies have all their bound post types' options checked, they will inherit their status."
1834
  msgstr ""
1835
 
1836
- #: inc/views/admin/metaboxes/robots-metabox.php:394
1837
  msgid "Global Settings"
1838
  msgstr ""
1839
 
1840
- #: inc/views/admin/metaboxes/robots-metabox.php:396
1841
  msgid "These settings apply to other globally registered content types."
1842
  msgstr ""
1843
 
1844
- #: inc/views/admin/metaboxes/robots-metabox.php:416
1845
  msgid "Warning: No public site should ever enable this option."
1846
  msgstr ""
1847
 
1848
- #: inc/views/admin/metaboxes/schema-metabox.php:20
1849
  msgid "Schema.org Output Settings"
1850
  msgstr ""
1851
 
@@ -1873,187 +1847,187 @@ msgstr ""
1873
  msgid "Presence"
1874
  msgstr ""
1875
 
1876
- #: inc/views/admin/metaboxes/schema-metabox.php:57
1877
  msgid "Site Structure Options"
1878
  msgstr ""
1879
 
1880
- #: inc/views/admin/metaboxes/schema-metabox.php:59
1881
  msgid "The site structure Schema.org output allows search engines to gain knowledge on how your website is built."
1882
  msgstr ""
1883
 
1884
- #: inc/views/admin/metaboxes/schema-metabox.php:60
1885
  msgid "For example, search engines display your pages' URLs when listed in the search results. These options allow you to enhance those URLs output."
1886
  msgstr ""
1887
 
1888
- #: inc/views/admin/metaboxes/schema-metabox.php:63
1889
  msgid "Breadcrumbs"
1890
  msgstr ""
1891
 
1892
- #: inc/views/admin/metaboxes/schema-metabox.php:65
1893
  msgid "Breadcrumb trails indicate page positions in the site's hierarchy. Using the following option will show the hierarchy within the search results when available."
1894
  msgstr ""
1895
 
1896
- #: inc/views/admin/metaboxes/schema-metabox.php:68
1897
- #: inc/views/admin/metaboxes/schema-metabox.php:89
1898
- #: inc/views/admin/metaboxes/schema-metabox.php:111
1899
- #: inc/views/admin/metaboxes/schema-metabox.php:161
1900
  msgid "Learn how this data is used."
1901
  msgstr ""
1902
 
1903
- #: inc/views/admin/metaboxes/schema-metabox.php:75
1904
  msgid "Enable Breadcrumbs?"
1905
  msgstr ""
1906
 
1907
- #: inc/views/admin/metaboxes/schema-metabox.php:84
1908
  msgctxt "Product name"
1909
  msgid "Sitelinks Searchbox"
1910
  msgstr ""
1911
 
1912
- #: inc/views/admin/metaboxes/schema-metabox.php:86
1913
  msgid "When Search users search for your brand name, the following option allows them to search through this website directly from the search results."
1914
  msgstr ""
1915
 
1916
- #: inc/views/admin/metaboxes/schema-metabox.php:96
1917
  msgctxt "Sitelinks Searchbox is a Product name"
1918
  msgid "Enable Sitelinks Searchbox?"
1919
  msgstr ""
1920
 
1921
- #: inc/views/admin/metaboxes/schema-metabox.php:106
1922
  msgid "Authorized Presence Options"
1923
  msgstr ""
1924
 
1925
- #: inc/views/admin/metaboxes/schema-metabox.php:108
1926
  msgid "The authorized presence Schema.org output helps search engine users find ways to interact with this website."
1927
  msgstr ""
1928
 
1929
- #: inc/views/admin/metaboxes/schema-metabox.php:119
1930
  msgid "Output Authorized Presence?"
1931
  msgstr ""
1932
 
1933
- #: inc/views/admin/metaboxes/schema-metabox.php:128
1934
  msgid "About this website"
1935
  msgstr ""
1936
 
1937
- #: inc/views/admin/metaboxes/schema-metabox.php:130
1938
  msgctxt "...Organization or Person."
1939
  msgid "This website represents:"
1940
  msgstr ""
1941
 
1942
- #: inc/views/admin/metaboxes/schema-metabox.php:136
1943
  msgid "An Organization"
1944
  msgstr ""
1945
 
1946
- #: inc/views/admin/metaboxes/schema-metabox.php:137
1947
  msgid "A Person"
1948
  msgstr ""
1949
 
1950
- #: inc/views/admin/metaboxes/schema-metabox.php:149
1951
  msgid "The organization or personal name"
1952
  msgstr ""
1953
 
1954
- #: inc/views/admin/metaboxes/schema-metabox.php:157
1955
  msgid "Website logo"
1956
  msgstr ""
1957
 
1958
- #: inc/views/admin/metaboxes/schema-metabox.php:159
1959
  msgid "These options are used when this site represents an organization. When no logo is outputted, search engine will look elsewhere."
1960
  msgstr ""
1961
 
1962
- #: inc/views/admin/metaboxes/schema-metabox.php:168
1963
  msgid "Enable logo?"
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
 
1976
- #: inc/views/admin/metaboxes/schema-metabox.php:194
1977
  msgctxt "No spaces. E.g. https://facebook.com/RelatedProfile"
1978
  msgid "RelatedProfile"
1979
  msgstr ""
1980
 
1981
- #: inc/views/admin/metaboxes/schema-metabox.php:202
1982
  msgid "Facebook Page"
1983
  msgstr ""
1984
 
1985
- #: inc/views/admin/metaboxes/schema-metabox.php:209
1986
  msgid "Twitter Profile"
1987
  msgstr ""
1988
 
1989
- #: inc/views/admin/metaboxes/schema-metabox.php:216
1990
  msgctxt "Google+ is dead. &#8224; is a cross, indicating that."
1991
  msgid "Google+ Profile&#8224;"
1992
  msgstr ""
1993
 
1994
- #: inc/views/admin/metaboxes/schema-metabox.php:223
1995
  msgid "Instagram Profile"
1996
  msgstr ""
1997
 
1998
- #: inc/views/admin/metaboxes/schema-metabox.php:230
1999
  msgid "Youtube Profile"
2000
  msgstr ""
2001
 
2002
- #: inc/views/admin/metaboxes/schema-metabox.php:237
2003
  msgid "LinkedIn Profile"
2004
  msgstr ""
2005
 
2006
- #: inc/views/admin/metaboxes/schema-metabox.php:248
2007
  msgid "Pinterest Profile"
2008
  msgstr ""
2009
 
2010
- #: inc/views/admin/metaboxes/schema-metabox.php:255
2011
  msgid "SoundCloud Profile"
2012
  msgstr ""
2013
 
2014
- #: inc/views/admin/metaboxes/schema-metabox.php:262
2015
  msgid "Tumblr Blog"
2016
  msgstr ""
2017
 
2018
- #: inc/views/admin/metaboxes/schema-metabox.php:281
2019
  msgid "Connected Social Pages"
2020
  msgstr ""
2021
 
2022
- #: inc/views/admin/metaboxes/schema-metabox.php:283
2023
  msgid "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway."
2024
  msgstr ""
2025
 
2026
- #: inc/views/admin/metaboxes/schema-metabox.php:284
2027
  msgid "Add links that lead directly to the connected social pages of this website."
2028
  msgstr ""
2029
 
2030
- #: inc/views/admin/metaboxes/schema-metabox.php:285
2031
  msgid "These settings do not affect sharing behavior with the social networks."
2032
  msgstr ""
2033
 
2034
  #. translators: %s = Learn more URL. Markdown!
2035
- #: inc/views/admin/metaboxes/schema-metabox.php:290
2036
  msgid "These settings are marked for removal. When you clear a field, it will be hidden forever. [Learn more](%s)."
2037
  msgstr ""
2038
 
2039
- #: inc/views/admin/metaboxes/schema-metabox.php:309
2040
  msgid "View your profile."
2041
  msgstr ""
2042
 
2043
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:31
2044
  msgid "Metadata"
2045
  msgstr ""
2046
 
2047
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:36
2048
  msgctxt "Ping or notify search engine"
2049
  msgid "Ping"
2050
  msgstr ""
2051
 
2052
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:41
2053
  msgid "Style"
2054
  msgstr ""
2055
 
2056
- #: inc/views/admin/metaboxes/sitemaps-metabox.php:65
2057
  msgid "Sitemap Integration Settings"
2058
  msgstr ""
2059
 
@@ -2078,187 +2052,187 @@ msgstr ""
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
 
2257
- #: inc/views/admin/metaboxes/social-metabox.php:41
2258
  msgid "Post Dates"
2259
  msgstr ""
2260
 
2261
- #: inc/views/admin/metaboxes/social-metabox.php:61
2262
  msgid "Social Meta Tags Settings"
2263
  msgstr ""
2264
 
@@ -2274,262 +2248,274 @@ msgstr ""
2274
  msgid "Facebook, Twitter, Pinterest and many other social sites make use of these meta tags."
2275
  msgstr ""
2276
 
2277
- #: inc/views/admin/metaboxes/social-metabox.php:80
2278
  msgid "Note: Another Open Graph plugin has been detected. These meta tags might conflict."
2279
  msgstr ""
2280
 
2281
- #: inc/views/admin/metaboxes/social-metabox.php:86
2282
  msgid "Output Facebook meta tags?"
2283
  msgstr ""
2284
 
2285
- #: inc/views/admin/metaboxes/social-metabox.php:87
2286
  msgid "Output various meta tags targeted at Facebook."
2287
  msgstr ""
2288
 
2289
- #: inc/views/admin/metaboxes/social-metabox.php:97
2290
  msgid "Output Twitter meta tags?"
2291
  msgstr ""
2292
 
2293
- #: inc/views/admin/metaboxes/social-metabox.php:98
2294
  msgid "Output various meta tags targeted at Twitter."
2295
  msgstr ""
2296
 
2297
- #: inc/views/admin/metaboxes/social-metabox.php:104
2298
  msgid "Note: Another Twitter Card plugin has been detected. These meta tags might conflict."
2299
  msgstr ""
2300
 
2301
- #: inc/views/admin/metaboxes/social-metabox.php:110
2302
  msgid "Output oEmbed scripts?"
2303
  msgstr ""
2304
 
2305
- #: inc/views/admin/metaboxes/social-metabox.php:111
2306
  msgid "WordPress, Discord, Drupal, Squarespace, and many other clients can make use of these scripts."
2307
  msgstr ""
2308
 
2309
- #: inc/views/admin/metaboxes/social-metabox.php:119
2310
  msgid "Social Title Settings"
2311
  msgstr ""
2312
 
2313
- #: inc/views/admin/metaboxes/social-metabox.php:121
2314
  msgid "Most social sites and third-party services automatically include the website URL inside their embeds. When the site title is described well in the site URL, including it in the social title will be redundant."
2315
  msgstr ""
2316
 
2317
- #: inc/views/admin/metaboxes/social-metabox.php:124
2318
  msgid "When you provide a custom Open Graph or Twitter title, the site title will be omitted automatically."
2319
  msgstr ""
2320
 
2321
- #: inc/views/admin/metaboxes/social-metabox.php:132
2322
  msgid "Remove site title from generated social titles?"
2323
  msgstr ""
2324
 
2325
- #: inc/views/admin/metaboxes/social-metabox.php:143
2326
  msgid "A social image can be displayed when your website is shared. It is a great way to grab attention."
2327
  msgstr ""
2328
 
2329
- #: inc/views/admin/metaboxes/social-metabox.php:148
2330
  msgid "Output multiple Open Graph image tags?"
2331
  msgstr ""
2332
 
2333
- #: inc/views/admin/metaboxes/social-metabox.php:149
2334
  msgid "This enables users to select any image attached to the page shared on social networks, like Facebook."
2335
  msgstr ""
2336
 
2337
- #: inc/views/admin/metaboxes/social-metabox.php:157
2338
  msgid "Social Image Fallback URL"
2339
  msgstr ""
2340
 
2341
- #: inc/views/admin/metaboxes/social-metabox.php:158
2342
  msgid "When no image is available from the page or term, this fallback image will be used instead."
2343
  msgstr ""
2344
 
2345
- #: inc/views/admin/metaboxes/social-metabox.php:173
2346
  msgid "Theme Color Settings"
2347
  msgstr ""
2348
 
2349
- #: inc/views/admin/metaboxes/social-metabox.php:175
2350
  msgid "Discord styles embeds with the theme color. The theme color can also affect the tab-color in some browsers."
2351
  msgstr ""
2352
 
2353
- #: inc/views/admin/metaboxes/social-metabox.php:179
2354
  msgid "Theme Color"
2355
  msgstr ""
2356
 
2357
- #: inc/views/admin/metaboxes/social-metabox.php:187
2358
  msgid "Site Shortlink Settings"
2359
  msgstr ""
2360
 
2361
- #: inc/views/admin/metaboxes/social-metabox.php:189
2362
  msgid "The shortlink tag can be manually used for microblogging services like Twitter, but it has no SEO value whatsoever."
2363
  msgstr ""
2364
 
2365
- #: inc/views/admin/metaboxes/social-metabox.php:194
2366
  msgid "Output shortlink tag?"
2367
  msgstr ""
2368
 
2369
- #: inc/views/admin/metaboxes/social-metabox.php:207
 
 
 
 
 
 
2370
  msgctxt "Example Facebook Business URL"
2371
  msgid "https://www.facebook.com/YourBusinessProfile"
2372
  msgstr ""
2373
 
2374
- #: inc/views/admin/metaboxes/social-metabox.php:213
2375
  msgid "Facebook Integration Settings"
2376
  msgstr ""
2377
 
2378
- #: inc/views/admin/metaboxes/social-metabox.php:215
2379
  msgid "Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options."
2380
  msgstr ""
2381
 
2382
- #: inc/views/admin/metaboxes/social-metabox.php:216
2383
  msgid "When these options are filled in, Facebook might link the Facebook profile to be followed and liked when your post or page is shared."
2384
  msgstr ""
2385
 
2386
- #: inc/views/admin/metaboxes/social-metabox.php:222
2387
  msgid "Facebook App ID"
2388
  msgstr ""
2389
 
2390
- #: inc/views/admin/metaboxes/social-metabox.php:226
2391
  msgid "Get Facebook App ID."
2392
  msgstr ""
2393
 
2394
- #: inc/views/admin/metaboxes/social-metabox.php:238
2395
  msgid "Facebook Publisher page"
2396
  msgstr ""
2397
 
2398
- #: inc/views/admin/metaboxes/social-metabox.php:242
2399
  msgid "Only Facebook Business Pages are accepted."
2400
  msgstr ""
2401
 
2402
- #: inc/views/admin/metaboxes/social-metabox.php:254
2403
  msgid "Facebook Author Fallback Page"
2404
  msgstr ""
2405
 
2406
- #: inc/views/admin/metaboxes/social-metabox.php:258
2407
  msgid "Your Facebook profile."
2408
  msgstr ""
2409
 
2410
- #: inc/views/admin/metaboxes/social-metabox.php:264
2411
- #: inc/views/admin/metaboxes/social-metabox.php:358
2412
  msgid "Authors can override this option on their profile page."
2413
  msgstr ""
2414
 
2415
- #: inc/views/admin/metaboxes/social-metabox.php:273
2416
  msgctxt "Twitter @username"
2417
  msgid "@your-site-username"
2418
  msgstr ""
2419
 
2420
- #: inc/views/admin/metaboxes/social-metabox.php:281
 
 
 
 
 
 
2421
  msgid "Twitter Integration Settings"
2422
  msgstr ""
2423
 
2424
- #: inc/views/admin/metaboxes/social-metabox.php:283
2425
  msgid "Twitter post sharing works mostly through Twitter Cards, and may fall back to use Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options."
2426
  msgstr ""
2427
 
2428
- #: inc/views/admin/metaboxes/social-metabox.php:289
2429
  msgid "Twitter Card Type"
2430
  msgstr ""
2431
 
2432
- #: inc/views/admin/metaboxes/social-metabox.php:292
2433
  msgid "The Twitter Card type may have the image highlighted, either small at the side or large above."
2434
  msgstr ""
2435
 
2436
- #: inc/views/admin/metaboxes/social-metabox.php:308
2437
  msgid "Learn more about this card."
2438
  msgstr ""
2439
 
2440
- #: inc/views/admin/metaboxes/social-metabox.php:323
2441
  msgid "Card and Content Attribution"
2442
  msgstr ""
2443
 
2444
- #: inc/views/admin/metaboxes/social-metabox.php:326
2445
  msgid "Twitter claims users will be able to follow and view the profiles of attributed accounts directly from the card when these fields are filled in."
2446
  msgstr ""
2447
 
2448
- #: inc/views/admin/metaboxes/social-metabox.php:327
2449
  msgid "However, for now, these fields seem to have no discernible effect."
2450
  msgstr ""
2451
 
2452
- #: inc/views/admin/metaboxes/social-metabox.php:332
2453
  msgid "Website Twitter Profile"
2454
  msgstr ""
2455
 
2456
- #: inc/views/admin/metaboxes/social-metabox.php:336
2457
- #: inc/views/admin/metaboxes/social-metabox.php:352
2458
  msgid "Find your @username."
2459
  msgstr ""
2460
 
2461
- #: inc/views/admin/metaboxes/social-metabox.php:348
2462
  msgid "Twitter Author Fallback Profile"
2463
  msgstr ""
2464
 
2465
- #: inc/views/admin/metaboxes/social-metabox.php:367
2466
  msgid "oEmbed Settings"
2467
  msgstr ""
2468
 
2469
- #: inc/views/admin/metaboxes/social-metabox.php:369
2470
  msgid "Some social sharing services and clients, like WordPress, LinkedIn, and Discord, obtain the linked page information via oEmbed."
2471
  msgstr ""
2472
 
2473
- #: inc/views/admin/metaboxes/social-metabox.php:378
2474
  msgid "Use Open Graph title?"
2475
  msgstr ""
2476
 
2477
- #: inc/views/admin/metaboxes/social-metabox.php:379
2478
  msgid "Check this option if you want to replace page titles with Open Graph titles in embeds."
2479
  msgstr ""
2480
 
2481
- #: inc/views/admin/metaboxes/social-metabox.php:385
2482
  msgid "Only custom social images that are selected via the Media Library are considered."
2483
  msgstr ""
2484
 
2485
- #: inc/views/admin/metaboxes/social-metabox.php:392
2486
  msgid "Use social image?"
2487
  msgstr ""
2488
 
2489
- #: inc/views/admin/metaboxes/social-metabox.php:393
2490
  msgid "LinkedIn displays the post's featured image in embeds. Check this option if you want to replace it with the social image."
2491
  msgstr ""
2492
 
2493
- #: inc/views/admin/metaboxes/social-metabox.php:401
2494
  msgid "Remove author name?"
2495
  msgstr ""
2496
 
2497
- #: inc/views/admin/metaboxes/social-metabox.php:402
2498
  msgid "Discord shows the page author's name above the sharing embed. Check this option if you find this undesirable."
2499
  msgstr ""
2500
 
2501
- #: inc/views/admin/metaboxes/social-metabox.php:410
2502
  msgid "Posts"
2503
  msgstr ""
2504
 
2505
- #: inc/views/admin/metaboxes/social-metabox.php:413
2506
  msgid "Post Date Settings"
2507
  msgstr ""
2508
 
2509
- #: inc/views/admin/metaboxes/social-metabox.php:415
2510
  msgid "Some social sites output the shared post's publishing and modified data in the sharing snippet."
2511
  msgstr ""
2512
 
2513
  #. translators: the backticks are Markdown! Preserve them as-is!
2514
- #: inc/views/admin/metaboxes/social-metabox.php:426
2515
  msgid "Add `article:published_time` to posts?"
2516
  msgstr ""
2517
 
2518
  #. translators: the backticks are Markdown! Preserve them as-is!
2519
- #: inc/views/admin/metaboxes/social-metabox.php:436
2520
  msgid "Add `article:modified_time` to posts?"
2521
  msgstr ""
2522
 
2523
- #: inc/views/admin/metaboxes/title-metabox.php:30
2524
  msgid "Example Post"
2525
  msgstr ""
2526
 
2527
- #: inc/views/admin/metaboxes/title-metabox.php:34
2528
  msgid "Example Category"
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
 
@@ -2537,7 +2523,7 @@ msgstr ""
2537
  msgid "The page title is prominently shown within the browser tab as well as within the search engine results pages."
2538
  msgstr ""
2539
 
2540
- #: inc/views/admin/metaboxes/title-metabox.php:54
2541
  msgid "Example Page Title Output"
2542
  msgstr ""
2543
 
@@ -2559,271 +2545,279 @@ msgstr ""
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
 
2635
- #: inc/views/admin/metaboxes/webmaster-metabox.php:27
2636
  msgid "Google Search Console Verification Code"
2637
  msgstr ""
2638
 
2639
- #: inc/views/admin/metaboxes/webmaster-metabox.php:29
2640
  msgid "Get the Google verification code."
2641
  msgstr ""
2642
 
2643
- #: inc/views/admin/metaboxes/webmaster-metabox.php:37
2644
  msgid "Bing Webmaster Verification Code"
2645
  msgstr ""
2646
 
2647
- #: inc/views/admin/metaboxes/webmaster-metabox.php:39
2648
  msgid "Get the Bing verification code."
2649
  msgstr ""
2650
 
2651
- #: inc/views/admin/metaboxes/webmaster-metabox.php:47
2652
  msgid "Yandex Webmaster Verification Code"
2653
  msgstr ""
2654
 
2655
- #: inc/views/admin/metaboxes/webmaster-metabox.php:49
2656
  msgid "Get the Yandex verification code."
2657
  msgstr ""
2658
 
2659
  #. translators: literal translation from '百度搜索资源平台'-Code
2660
- #: inc/views/admin/metaboxes/webmaster-metabox.php:58
2661
  msgid "Baidu Search Resource Platform Code"
2662
  msgstr ""
2663
 
2664
- #: inc/views/admin/metaboxes/webmaster-metabox.php:60
2665
  msgid "Get the Baidu verification code."
2666
  msgstr ""
2667
 
2668
- #: inc/views/admin/metaboxes/webmaster-metabox.php:68
2669
  msgid "Pinterest Analytics Verification Code"
2670
  msgstr ""
2671
 
2672
- #: inc/views/admin/metaboxes/webmaster-metabox.php:70
2673
  msgid "Get the Pinterest verification code."
2674
  msgstr ""
2675
 
2676
- #: inc/views/admin/metaboxes/webmaster-metabox.php:79
2677
  msgid "Webmaster Integration Settings"
2678
  msgstr ""
2679
 
2680
- #: inc/views/admin/metaboxes/webmaster-metabox.php:81
2681
  msgid "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes."
2682
  msgstr ""
2683
 
2684
- #: inc/views/admin/metaboxes/webmaster-metabox.php:82
2685
  msgid "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content."
2686
  msgstr ""
2687
 
2688
- #: inc/views/admin/seo-settings-wrap.php:12
2689
  msgid "Are you sure you want to reset all SEO settings to their defaults?"
2690
  msgstr ""
2691
 
2692
- #: inc/views/admin/seo-settings-wrap.php:15
2693
  msgid "Save Settings"
2694
  msgstr ""
2695
 
2696
- #: inc/views/admin/seo-settings-wrap.php:22
2697
  msgid "Reset Settings"
2698
  msgstr ""
2699
 
2700
- #: inc/views/edit/seo-settings-singular.php:39
2701
  msgid "Visibility"
2702
  msgstr ""
2703
 
2704
- #: inc/views/edit/seo-settings-singular.php:68
2705
- #: inc/views/edit/seo-settings-tt.php:101
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
 
2785
- #: inc/views/edit/seo-settings-tt.php:62
2786
  msgid "This tells search engines not to show this term in their search results."
2787
  msgstr ""
2788
 
2789
- #: inc/views/edit/seo-settings-tt.php:75
2790
  msgid "This tells search engines not to follow links on this term."
2791
  msgstr ""
2792
 
2793
- #: inc/views/edit/seo-settings-tt.php:88
2794
  msgid "This tells search engines not to save a cached copy of this term."
2795
  msgstr ""
2796
 
2797
- #: inc/views/edit/seo-settings-tt.php:95
2798
- #: inc/views/list/quick-post.php:55
2799
- #: inc/views/list/quick-term.php:55
2800
  msgid "General SEO Settings"
2801
  msgstr ""
2802
 
2803
- #: inc/views/edit/seo-settings-tt.php:200
2804
  msgid "Social SEO Settings"
2805
  msgstr ""
2806
 
2807
- #: inc/views/edit/seo-settings-tt.php:295
2808
- #: inc/views/list/bulk-post.php:55
2809
- #: inc/views/list/quick-post.php:92
2810
- #: inc/views/list/quick-term.php:92
2811
  msgid "Visibility SEO Settings"
2812
  msgstr ""
2813
 
2814
- #: inc/views/edit/seo-settings-tt.php:323
2815
  msgid "These directives may urge robots not to display, follow links on, or create a cached copy of this term."
2816
  msgstr ""
2817
 
2818
- #: inc/views/list/bulk-post.php:69
2819
  msgid "Default (unknown)"
2820
  msgstr ""
2821
 
2822
- #: inc/views/profile/author.php:13
2823
- msgid "Authorial Info"
 
 
 
 
2824
  msgstr ""
2825
 
2826
  #: inc/views/profile/author.php:30
 
 
 
 
2827
  msgid "This may be shown publicly."
2828
  msgstr ""
2829
 
@@ -2872,22 +2866,22 @@ msgstr ""
2872
  msgid "The buttons below are for primary %s selection."
2873
  msgstr ""
2874
 
2875
- #: inc/views/templates/settings/settings.php:17
2876
  msgid "This post type is excluded, so this option won't work."
2877
  msgstr ""
2878
 
2879
- #: inc/views/templates/settings/settings.php:27
2880
  msgid "This taxonomy is excluded, so this option won't work."
2881
  msgstr ""
2882
 
2883
- #: inc/views/templates/settings/settings.php:37
2884
  msgid "This taxonomy's post types are also excluded, so this option won't have any effect."
2885
  msgstr ""
2886
 
2887
- #: inc/views/templates/settings/settings.php:47
2888
  msgid "The site title is already removed from meta titles, so this option only affects the homepage."
2889
  msgstr ""
2890
 
2891
- #: inc/views/templates/settings/settings.php:57
2892
  msgid "This taxonomy inherited the state from the post type, so this option won't have any effect."
2893
  msgstr ""
1
+ # Copyright (C) 2021 The SEO Framework Team
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.4\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: 2021-07-19T04:20:10+00: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"
37
  msgstr ""
38
 
39
  #. translators: %s = Version number, surrounded in markdown-backticks.
40
+ #: bootstrap/upgrade.php:422
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:462
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:466
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:596
54
  msgid "Twitter Photo Cards have been deprecated. Your site now uses Summary Cards when applicable."
55
  msgstr ""
56
 
57
+ #: bootstrap/upgrade.php:621
58
  msgid "The previous sitemap timestamp settings have been converted into new global timestamp settings."
59
  msgstr ""
60
 
61
+ #: bootstrap/upgrade.php:716
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:729
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:78
70
  msgid "No Search"
71
  msgstr ""
72
 
73
+ #: inc/classes/admin-init.class.php:81
74
  msgid "No Archive"
75
  msgstr ""
76
 
77
+ #: inc/classes/admin-init.class.php:315
78
  msgid "There's no content."
79
  msgstr ""
80
 
81
+ #: inc/classes/admin-init.class.php:316
82
  msgid "It's too short and it should have more information."
83
  msgstr ""
84
 
85
+ #: inc/classes/admin-init.class.php:317
86
  msgid "It's short and it could have more information."
87
  msgstr ""
88
 
89
+ #: inc/classes/admin-init.class.php:318
90
  msgid "It's long and it might get truncated in search."
91
  msgstr ""
92
 
93
+ #: inc/classes/admin-init.class.php:319
94
  msgid "It's too long and it will get truncated in search."
95
  msgstr ""
96
 
97
+ #: inc/classes/admin-init.class.php:320
98
  msgid "Length is good."
99
  msgstr ""
100
 
101
+ #: inc/classes/admin-init.class.php:323
102
  msgctxt "The string is empty"
103
  msgid "Empty"
104
  msgstr ""
105
 
106
+ #: inc/classes/admin-init.class.php:324
107
  msgid "Far too short"
108
  msgstr ""
109
 
110
+ #: inc/classes/admin-init.class.php:325
111
  msgid "Too short"
112
  msgstr ""
113
 
114
+ #: inc/classes/admin-init.class.php:326
115
  msgid "Too long"
116
  msgstr ""
117
 
118
+ #: inc/classes/admin-init.class.php:327
119
  msgid "Far too long"
120
  msgstr ""
121
 
122
+ #: inc/classes/admin-init.class.php:328
123
  msgid "Good"
124
  msgstr ""
125
 
126
+ #: inc/classes/admin-init.class.php:331
127
  msgctxt "The string is empty"
128
  msgid "Empty."
129
  msgstr ""
130
 
131
+ #: inc/classes/admin-init.class.php:332
132
  msgid "Far too short."
133
  msgstr ""
134
 
135
+ #: inc/classes/admin-init.class.php:333
136
  msgid "Too short."
137
  msgstr ""
138
 
139
+ #: inc/classes/admin-init.class.php:334
140
  msgid "Too long."
141
  msgstr ""
142
 
143
+ #: inc/classes/admin-init.class.php:335
144
  msgid "Far too long."
145
  msgstr ""
146
 
147
+ #: inc/classes/admin-init.class.php:336
148
  msgid "Good."
149
  msgstr ""
150
 
151
  #. translators: %s = Redirect URL markdown
152
+ #: inc/classes/admin-init.class.php:432
153
  msgid "There has been an error redirecting. Refresh the page or follow [this link](%s)."
154
  msgstr ""
155
 
156
+ #: inc/classes/admin-pages.class.php:56
157
  msgid "SEO Settings"
158
  msgstr ""
159
 
160
+ #: inc/classes/admin-pages.class.php:57
161
  msgid "SEO"
162
  msgstr ""
163
 
164
+ #: inc/classes/admin-pages.class.php:209
165
  msgid "SEO settings are saved, and the caches have been flushed."
166
  msgstr ""
167
 
168
+ #: inc/classes/admin-pages.class.php:214
169
  msgid "No SEO settings were changed, but the caches have been flushed."
170
  msgstr ""
171
 
172
+ #: inc/classes/admin-pages.class.php:219
173
  msgid "SEO settings are reset, and the caches have been flushed."
174
  msgstr ""
175
 
176
+ #: inc/classes/admin-pages.class.php:224
177
  msgid "An unknown error occurred saving SEO settings."
178
  msgstr ""
179
 
180
+ #: inc/classes/admin-pages.class.php:245
181
  msgid "Multiple SEO tools have been detected. You should only use one."
182
  msgstr ""
183
 
184
+ #: inc/classes/bridges/feed.class.php:190
185
+ msgctxt "The content source"
186
+ msgid "Source"
187
  msgstr ""
188
 
189
+ #: inc/classes/bridges/plugintable.class.php:57
190
+ msgid "Settings"
 
191
  msgstr ""
192
 
193
+ #: inc/classes/bridges/plugintable.class.php:64
194
+ msgctxt "Plugin extensions"
195
+ msgid "Extensions"
196
  msgstr ""
197
 
198
+ #: inc/classes/bridges/plugintable.class.php:69
199
+ msgctxt "Plugin pricing"
200
+ msgid "Pricing"
201
  msgstr ""
202
 
203
+ #: inc/classes/bridges/plugintable.class.php:103
204
+ msgid "Get support"
205
+ msgstr ""
206
+
207
+ #: inc/classes/bridges/plugintable.class.php:110
208
+ msgid "View documentation"
209
+ msgstr ""
210
+
211
+ #: inc/classes/bridges/plugintable.class.php:117
212
+ msgid "View API docs"
213
+ msgstr ""
214
+
215
+ #: inc/classes/bridges/plugintable.class.php:124
216
+ msgctxt "Extension Manager is a product name; do not translate it."
217
+ msgid "Get Extension Manager"
218
  msgstr ""
219
 
220
  #: inc/classes/bridges/postsettings.class.php:73
231
  msgid "%s SEO Settings"
232
  msgstr ""
233
 
234
+ #: inc/classes/bridges/scripts.class.php:356
235
  msgid "The changes you made will be lost if you navigate away from this page."
236
  msgstr ""
237
 
238
+ #: inc/classes/bridges/scripts.class.php:640
239
+ #: inc/classes/interpreters/form.class.php:518
240
+ msgid "Select Image"
241
+ msgstr ""
242
+
243
+ #: inc/classes/bridges/scripts.class.php:642
244
  msgid "Change Image"
245
  msgstr ""
246
 
247
+ #: inc/classes/bridges/scripts.class.php:643
248
  msgid "Remove Image"
249
  msgstr ""
250
 
251
+ #: inc/classes/bridges/scripts.class.php:645
252
  msgctxt "Frame title"
253
  msgid "Select Social Image"
254
  msgstr ""
255
 
256
+ #: inc/classes/bridges/scripts.class.php:646
257
+ #: inc/classes/bridges/scripts.class.php:655
258
  msgid "Use this image"
259
  msgstr ""
260
 
261
+ #: inc/classes/bridges/scripts.class.php:649
262
+ #: inc/classes/deprecated.class.php:513
263
+ #: inc/views/admin/metaboxes/schema-metabox.php:191
264
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:414
265
+ msgid "Select Logo"
266
+ msgstr ""
267
+
268
+ #: inc/classes/bridges/scripts.class.php:651
269
  msgid "Change Logo"
270
  msgstr ""
271
 
272
+ #: inc/classes/bridges/scripts.class.php:652
273
  msgid "Remove Logo"
274
  msgstr ""
275
 
276
+ #: inc/classes/bridges/scripts.class.php:654
277
  msgctxt "Frame title"
278
  msgid "Select Logo"
279
  msgstr ""
280
 
281
  #. translators: %s = term name
282
+ #: inc/classes/bridges/scripts.class.php:875
283
  msgid "Select Primary %s"
284
  msgstr ""
285
 
286
  #. translators: %s = term name
287
+ #: inc/classes/bridges/scripts.class.php:880
288
  msgid "Make primary %s"
289
  msgstr ""
290
 
291
  #. translators: %s = term name
292
+ #: inc/classes/bridges/scripts.class.php:882
293
  msgid "Primary %s"
294
  msgstr ""
295
 
296
  #. translators: Pixel counter. 1: number (value), 2: number (guideline)
297
+ #: inc/classes/bridges/scripts.class.php:977
298
  msgid "%1$d out of %2$d pixels are used."
299
  msgstr ""
300
 
323
  msgstr ""
324
 
325
  #: inc/classes/bridges/seosettings.class.php:138
326
+ #: inc/views/admin/metaboxes/homepage-metabox.php:435
327
+ #: inc/views/edit/seo-settings-singular.php:292
328
+ #: inc/views/edit/seo-settings-tt.php:322
329
  msgid "Robots Meta Settings"
330
  msgstr ""
331
 
571
  msgid "&#8220;%1$s&#8221; is used %2$d times."
572
  msgstr ""
573
 
 
 
 
 
 
 
 
 
 
574
  #: inc/classes/builders/seobar-page.class.php:577
575
  #: inc/classes/builders/seobar-page.class.php:734
576
  #: inc/classes/builders/seobar-page.class.php:865
577
  #: inc/classes/builders/seobar-term.class.php:538
578
  #: inc/classes/builders/seobar-term.class.php:689
579
  #: inc/classes/builders/seobar-term.class.php:806
580
+ msgid "The robots.txt file is nonstandard, and may still direct search engines differently."
581
  msgstr ""
582
 
583
  #: inc/classes/builders/seobar-page.class.php:578
584
+ #: inc/classes/builders/seobar-page.class.php:735
585
+ #: inc/classes/builders/seobar-page.class.php:866
586
  #: inc/classes/builders/seobar-term.class.php:539
587
+ #: inc/classes/builders/seobar-term.class.php:690
588
+ #: inc/classes/builders/seobar-term.class.php:807
589
+ msgid "WordPress discourages crawling via the Reading Settings."
590
  msgstr ""
591
 
592
  #: inc/classes/builders/seobar-page.class.php:579
593
+ #: inc/classes/builders/seobar-term.class.php:540
594
+ msgid "Indexing is discouraged for the whole site at the SEO Settings screen."
595
  msgstr ""
596
 
597
  #: inc/classes/builders/seobar-page.class.php:580
598
+ msgid "Indexing is discouraged for this post type at the SEO Settings screen."
599
  msgstr ""
600
 
601
  #: inc/classes/builders/seobar-page.class.php:581
602
+ msgid "The page is protected, so indexing is discouraged."
603
  msgstr ""
604
 
605
  #: inc/classes/builders/seobar-page.class.php:582
606
+ msgid "The page SEO meta input overrides the indexing state."
 
607
  msgstr ""
608
 
609
+ #: inc/classes/builders/seobar-page.class.php:583
610
+ #: inc/classes/builders/seobar-term.class.php:546
611
+ msgid "A custom canonical URL is set that points to another page."
 
 
 
 
612
  msgstr ""
613
 
614
  #: inc/classes/builders/seobar-page.class.php:586
615
+ #: inc/classes/builders/seobar-page.class.php:742
616
+ #: inc/classes/builders/seobar-page.class.php:873
617
+ #: inc/classes/builders/seobar-term.class.php:549
618
+ #: inc/classes/builders/seobar-term.class.php:698
619
+ #: inc/classes/builders/seobar-term.class.php:815
620
+ msgid "WordPress overrides the robots directive."
621
  msgstr ""
622
 
623
  #: inc/classes/builders/seobar-page.class.php:587
624
+ msgid "The page is protected."
 
 
625
  msgstr ""
626
 
627
  #: inc/classes/builders/seobar-page.class.php:588
628
+ #: inc/classes/builders/seobar-page.class.php:743
629
+ #: inc/classes/builders/seobar-page.class.php:874
630
+ msgid "The page is not published."
631
  msgstr ""
632
 
633
+ #: inc/classes/builders/seobar-page.class.php:589
634
+ #: inc/classes/builders/seobar-term.class.php:552
635
+ msgid "The canonical URL points to another page."
 
 
 
 
636
  msgstr ""
637
 
638
  #: inc/classes/builders/seobar-page.class.php:593
640
  #: inc/classes/builders/seobar-page.class.php:611
641
  #: inc/classes/builders/seobar-term.class.php:556
642
  #: inc/classes/builders/seobar-term.class.php:565
643
+ msgctxt "Indexing"
644
+ msgid "I"
645
+ msgstr ""
646
+
647
+ #: inc/classes/builders/seobar-page.class.php:594
648
+ #: inc/classes/builders/seobar-page.class.php:603
649
+ #: inc/classes/builders/seobar-page.class.php:612
650
+ #: inc/classes/builders/seobar-term.class.php:557
651
+ #: inc/classes/builders/seobar-term.class.php:566
652
+ #: inc/views/admin/metaboxes/robots-metabox.php:67
653
+ #: inc/views/edit/seo-settings-singular.php:245
654
+ #: inc/views/edit/seo-settings-tt.php:60
655
+ #: inc/views/list/bulk-post.php:22
656
+ #: inc/views/list/quick-post.php:22
657
+ #: inc/views/list/quick-term.php:22
658
  msgid "Indexing"
659
  msgstr ""
660
 
661
+ #: inc/classes/builders/seobar-page.class.php:596
662
  msgid "Page may be indexed."
663
  msgstr ""
664
 
665
+ #: inc/classes/builders/seobar-page.class.php:598
666
+ #: inc/classes/builders/seobar-term.class.php:561
667
  msgid "The robots meta tag allows indexing."
668
  msgstr ""
669
 
670
+ #: inc/classes/builders/seobar-page.class.php:605
671
  msgid "Page may not be indexed."
672
  msgstr ""
673
 
674
+ #: inc/classes/builders/seobar-page.class.php:607
675
+ #: inc/classes/builders/seobar-term.class.php:570
676
  msgid "The robots meta tag does not allow indexing."
677
  msgstr ""
678
 
679
+ #: inc/classes/builders/seobar-page.class.php:614
680
+ #: inc/classes/builders/seobar-page.class.php:768
681
+ #: inc/classes/builders/seobar-page.class.php:899
682
  msgid "Page is invisible."
683
  msgstr ""
684
 
685
+ #: inc/classes/builders/seobar-page.class.php:616
686
+ #: inc/classes/builders/seobar-page.class.php:770
687
+ #: inc/classes/builders/seobar-page.class.php:901
688
  msgid "This page isn't published and can't be found publicly."
689
  msgstr ""
690
 
691
+ #: inc/classes/builders/seobar-page.class.php:671
692
  msgid "Indexing is discouraged for the homepage at the SEO Settings screen."
693
  msgstr ""
694
 
695
+ #: inc/classes/builders/seobar-page.class.php:736
696
+ #: inc/classes/builders/seobar-term.class.php:691
697
  msgid "Link following is discouraged for the whole site at the SEO Settings screen."
698
  msgstr ""
699
 
700
+ #: inc/classes/builders/seobar-page.class.php:737
701
  msgid "Link following is discouraged for this post type at the SEO Settings screen."
702
  msgstr ""
703
 
704
+ #: inc/classes/builders/seobar-page.class.php:738
705
  msgid "The page SEO meta input overrides the link following state."
706
  msgstr ""
707
 
708
+ #: inc/classes/builders/seobar-page.class.php:739
709
  msgid "The page may not be indexed, this may also discourage link following."
710
  msgstr ""
711
 
 
 
 
 
 
 
 
 
 
712
  #: inc/classes/builders/seobar-page.class.php:747
713
  #: inc/classes/builders/seobar-page.class.php:756
714
  #: inc/classes/builders/seobar-page.class.php:765
715
  #: inc/classes/builders/seobar-term.class.php:702
716
  #: inc/classes/builders/seobar-term.class.php:711
717
+ msgctxt "Following"
718
+ msgid "F"
719
+ msgstr ""
720
+
721
+ #: inc/classes/builders/seobar-page.class.php:748
722
+ #: inc/classes/builders/seobar-page.class.php:757
723
+ #: inc/classes/builders/seobar-page.class.php:766
724
+ #: inc/classes/builders/seobar-term.class.php:703
725
+ #: inc/classes/builders/seobar-term.class.php:712
726
+ #: inc/views/admin/metaboxes/robots-metabox.php:78
727
+ #: inc/views/list/quick-term.php:29
728
  msgid "Following"
729
  msgstr ""
730
 
731
+ #: inc/classes/builders/seobar-page.class.php:750
732
  msgid "Page links may be followed."
733
  msgstr ""
734
 
735
+ #: inc/classes/builders/seobar-page.class.php:752
736
+ #: inc/classes/builders/seobar-term.class.php:707
737
  msgid "The robots meta tag allows link following."
738
  msgstr ""
739
 
740
+ #: inc/classes/builders/seobar-page.class.php:759
741
  msgid "Page links may not be followed."
742
  msgstr ""
743
 
744
+ #: inc/classes/builders/seobar-page.class.php:761
745
+ #: inc/classes/builders/seobar-term.class.php:716
746
  msgid "The robots meta tag does not allow link following."
747
  msgstr ""
748
 
749
+ #: inc/classes/builders/seobar-page.class.php:813
750
  msgid "Link following is discouraged for the homepage at the SEO Settings screen."
751
  msgstr ""
752
 
753
+ #: inc/classes/builders/seobar-page.class.php:867
754
+ #: inc/classes/builders/seobar-term.class.php:808
755
  msgid "Archiving is discouraged for the whole site at the SEO Settings screen."
756
  msgstr ""
757
 
758
+ #: inc/classes/builders/seobar-page.class.php:868
759
  msgid "Archiving is discouraged for this post type at the SEO Settings screen."
760
  msgstr ""
761
 
762
+ #: inc/classes/builders/seobar-page.class.php:869
763
  msgid "The page SEO meta input overrides the archiving state."
764
  msgstr ""
765
 
766
+ #: inc/classes/builders/seobar-page.class.php:870
767
  msgid "The page may not be indexed, this may also discourage archiving."
768
  msgstr ""
769
 
 
 
 
 
 
 
 
 
 
770
  #: inc/classes/builders/seobar-page.class.php:878
771
  #: inc/classes/builders/seobar-page.class.php:887
772
  #: inc/classes/builders/seobar-page.class.php:896
773
  #: inc/classes/builders/seobar-term.class.php:819
774
  #: inc/classes/builders/seobar-term.class.php:828
775
+ msgctxt "Archiving"
776
+ msgid "A"
777
+ msgstr ""
778
+
779
+ #: inc/classes/builders/seobar-page.class.php:879
780
+ #: inc/classes/builders/seobar-page.class.php:888
781
+ #: inc/classes/builders/seobar-page.class.php:897
782
+ #: inc/classes/builders/seobar-term.class.php:820
783
+ #: inc/classes/builders/seobar-term.class.php:829
784
+ #: inc/views/admin/metaboxes/robots-metabox.php:89
785
+ #: inc/views/edit/seo-settings-singular.php:261
786
+ #: inc/views/edit/seo-settings-tt.php:86
787
+ #: inc/views/list/bulk-post.php:36
788
+ #: inc/views/list/quick-post.php:36
789
+ #: inc/views/list/quick-term.php:36
790
  msgid "Archiving"
791
  msgstr ""
792
 
793
+ #: inc/classes/builders/seobar-page.class.php:881
794
  msgid "Page may be archived."
795
  msgstr ""
796
 
797
+ #: inc/classes/builders/seobar-page.class.php:883
798
+ #: inc/classes/builders/seobar-term.class.php:824
799
  msgid "The robots meta tag allows archiving."
800
  msgstr ""
801
 
802
+ #: inc/classes/builders/seobar-page.class.php:890
803
  msgid "Page may not be archived."
804
  msgstr ""
805
 
806
+ #: inc/classes/builders/seobar-page.class.php:892
807
+ #: inc/classes/builders/seobar-term.class.php:833
808
  msgid "The robots meta tag does not allow archiving."
809
  msgstr ""
810
 
811
+ #: inc/classes/builders/seobar-page.class.php:944
812
  msgid "Archiving is discouraged for the homepage at the SEO Settings screen."
813
  msgstr ""
814
 
 
 
 
 
 
 
 
 
815
  #: inc/classes/builders/seobar-page.class.php:995
816
  #: inc/classes/builders/seobar-page.class.php:1011
817
  #: inc/classes/builders/seobar-term.class.php:920
818
  #: inc/classes/builders/seobar-term.class.php:936
819
+ msgctxt "Redirect"
820
+ msgid "R"
821
+ msgstr ""
822
+
823
+ #: inc/classes/builders/seobar-page.class.php:996
824
+ #: inc/classes/builders/seobar-page.class.php:1012
825
+ #: inc/classes/builders/seobar-term.class.php:921
826
+ #: inc/classes/builders/seobar-term.class.php:937
827
  msgid "Redirection"
828
  msgstr ""
829
 
830
+ #: inc/classes/builders/seobar-page.class.php:998
831
  msgid "Page does not redirect visitors."
832
  msgstr ""
833
 
834
+ #: inc/classes/builders/seobar-page.class.php:1000
835
+ #: inc/classes/builders/seobar-term.class.php:925
836
  msgid "All visitors and crawlers may access this page."
837
  msgstr ""
838
 
839
+ #: inc/classes/builders/seobar-page.class.php:1014
840
  msgid "Page redirects visitors."
841
  msgstr ""
842
 
843
+ #: inc/classes/builders/seobar-page.class.php:1016
844
+ #: inc/classes/builders/seobar-term.class.php:941
845
  msgid "All visitors and crawlers are being redirected. So, no other SEO enhancements are effective."
846
  msgstr ""
847
 
881
  msgid "It's built from the term SEO meta input."
882
  msgstr ""
883
 
884
+ #: inc/classes/builders/seobar-term.class.php:541
885
  msgid "Indexing is discouraged for all bound post types to this term at the SEO Settings screen."
886
  msgstr ""
887
 
888
+ #: inc/classes/builders/seobar-term.class.php:542
889
  msgid "Indexing is discouraged for this taxonomy at the SEO Settings screen."
890
  msgstr ""
891
 
892
+ #: inc/classes/builders/seobar-term.class.php:543
893
  msgid "The term SEO meta input overrides the indexing state."
894
  msgstr ""
895
 
896
+ #: inc/classes/builders/seobar-term.class.php:544
897
  msgid "No posts are attached to this term, so indexing is disabled."
898
  msgstr ""
899
 
900
+ #: inc/classes/builders/seobar-term.class.php:545
901
  msgid "No posts are attached to this term, so indexing should be disabled."
902
  msgstr ""
903
 
904
+ #: inc/classes/builders/seobar-term.class.php:550
905
  msgid "The term is empty."
906
  msgstr ""
907
 
908
+ #: inc/classes/builders/seobar-term.class.php:551
909
  msgid "The term is empty yet still indexed."
910
  msgstr ""
911
 
912
+ #: inc/classes/builders/seobar-term.class.php:559
913
  msgid "Term may be indexed."
914
  msgstr ""
915
 
916
+ #: inc/classes/builders/seobar-term.class.php:568
917
  msgid "Term may not be indexed."
918
  msgstr ""
919
 
920
+ #: inc/classes/builders/seobar-term.class.php:692
921
  msgid "Link following is discouraged for all bound post types to this term at the SEO Settings screen."
922
  msgstr ""
923
 
924
+ #: inc/classes/builders/seobar-term.class.php:693
925
  msgid "Link following is discouraged for this taxonomy at the SEO Settings screen."
926
  msgstr ""
927
 
928
+ #: inc/classes/builders/seobar-term.class.php:694
929
  msgid "The term SEO meta input overrides the link following state."
930
  msgstr ""
931
 
932
+ #: inc/classes/builders/seobar-term.class.php:695
933
  msgid "The term may not be indexed, this may also discourage link following."
934
  msgstr ""
935
 
936
+ #: inc/classes/builders/seobar-term.class.php:705
937
  msgid "Term links may be followed."
938
  msgstr ""
939
 
940
+ #: inc/classes/builders/seobar-term.class.php:714
941
  msgid "Term links may not be followed."
942
  msgstr ""
943
 
944
+ #: inc/classes/builders/seobar-term.class.php:809
945
  msgid "Archiving is discouraged for all bound post types to this term at the SEO Settings screen."
946
  msgstr ""
947
 
948
+ #: inc/classes/builders/seobar-term.class.php:810
949
  msgid "Archiving is discouraged for this taxonomy at the SEO Settings screen."
950
  msgstr ""
951
 
952
+ #: inc/classes/builders/seobar-term.class.php:811
953
  msgid "The term SEO meta input overrides the archiving state."
954
  msgstr ""
955
 
956
+ #: inc/classes/builders/seobar-term.class.php:812
957
  msgid "The term may not be indexed, this may also discourage archiving."
958
  msgstr ""
959
 
960
+ #: inc/classes/builders/seobar-term.class.php:822
961
  msgid "Term may be archived."
962
  msgstr ""
963
 
964
+ #: inc/classes/builders/seobar-term.class.php:831
965
  msgid "Term may not be archived."
966
  msgstr ""
967
 
968
+ #: inc/classes/builders/seobar-term.class.php:923
969
  msgid "Term does not redirect visitors."
970
  msgstr ""
971
 
972
+ #: inc/classes/builders/seobar-term.class.php:939
973
  msgid "Term redirects visitors."
974
  msgstr ""
975
 
983
  msgid "Sitemap is generated on %s"
984
  msgstr ""
985
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
986
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification, 4: Replacement function
987
+ #: inc/classes/debug.class.php:133
988
  msgid "%1$s is %2$s since version %3$s of The SEO Framework! Use %4$s instead."
989
  msgstr ""
990
 
991
+ #: inc/classes/debug.class.php:135
992
+ #: inc/classes/debug.class.php:144
993
  msgid "deprecated"
994
  msgstr ""
995
 
996
  #. translators: 1: Function name, 2: 'Deprecated', 3: Plugin Version notification
997
+ #: inc/classes/debug.class.php:142
998
  msgid "%1$s is %2$s since version %3$s of The SEO Framework with no alternative available."
999
  msgstr ""
1000
 
1001
  #. translators: 1: plugin version
1002
+ #: inc/classes/debug.class.php:195
1003
  msgid "(This message was added in version %s of The SEO Framework.)"
1004
  msgstr ""
1005
 
1006
  #. translators: 1: Function name, 2: 'Incorrectly', 3: Error message 4: Plugin Version notification
1007
+ #: inc/classes/debug.class.php:199
1008
  msgid "%1$s was called %2$s. %3$s %4$s"
1009
  msgstr ""
1010
 
1011
+ #: inc/classes/debug.class.php:201
1012
  msgid "incorrectly"
1013
  msgstr ""
1014
 
1015
  #. translators: 1: Method or Property name, 2: The SEO Framework class. 3: Message
1016
+ #: inc/classes/debug.class.php:249
1017
  msgid "%1$s is not accessible in %2$s. %3$s"
1018
  msgstr ""
1019
 
1033
  msgid "%1$s %2$s %3$s"
1034
  msgstr ""
1035
 
1036
+ #: inc/classes/interpreters/form.class.php:430
1037
+ msgid "Click to change the counter type"
1038
+ msgstr ""
1039
+
1040
+ #. translators: %s = number
1041
+ #: inc/classes/interpreters/form.class.php:433
1042
+ msgid "Characters: %s"
1043
+ msgstr ""
1044
+
1045
  #. translators: 1 = SEO Bar type title, 2 = Status reason. 3 = Assessments
1046
+ #: inc/classes/interpreters/seobar.class.php:309
1047
  msgctxt "SEO Bar ARIA assessment enumeration"
1048
  msgid "%1$s: %2$s %3$s"
1049
  msgstr ""
1050
 
1051
  #. translators: 1 = Assessment number (mind the %d (D)), 2 = Assessment explanation
1052
+ #: inc/classes/interpreters/seobar.class.php:353
1053
  msgctxt "assessment enumeration"
1054
  msgid "%1$d: %2$s"
1055
  msgstr ""
1056
 
1057
  #. translators: 1 = 'Assessment(s)', 2 = A list of assessments.
1058
+ #: inc/classes/interpreters/seobar.class.php:355
1059
  msgctxt "assessment list"
1060
  msgid "%1$s: %2$s"
1061
  msgstr ""
1062
 
1063
+ #: inc/classes/interpreters/seobar.class.php:356
1064
  msgid "Assessment"
1065
  msgstr ""
1066
 
1067
+ #: inc/classes/interpreters/seobar.class.php:357
1068
  msgid "Assessments"
1069
  msgstr ""
1070
 
1071
+ #: inc/classes/render.class.php:1281
1072
+ msgid "by Sybre Waaijer"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  msgstr ""
1074
 
1075
+ #: inc/compat/plugin-woocommerce.php:257
1076
+ msgid "WooCommerce recommends not indexing this dynamic page."
1077
  msgstr ""
1078
 
1079
+ #: inc/views/admin/metaboxes/description-metabox.php:20
1080
  msgid "Description Settings"
1081
  msgstr ""
1082
 
1083
+ #: inc/views/admin/metaboxes/description-metabox.php:22
1084
+ #: inc/views/admin/metaboxes/homepage-metabox.php:143
1085
+ #: inc/views/edit/seo-settings-singular.php:195
1086
+ #: inc/views/edit/seo-settings-tt.php:171
1087
  msgid "The meta description can be used to determine the text used under the title on search engine results pages."
1088
  msgstr ""
1089
 
1090
+ #: inc/views/admin/metaboxes/description-metabox.php:28
1091
  msgid "Automated Description Settings"
1092
  msgstr ""
1093
 
1107
  msgid "Automatically generate descriptions?"
1108
  msgstr ""
1109
 
1110
+ #: inc/views/admin/metaboxes/feed-metabox.php:20
1111
  msgid "Content Feed Settings"
1112
  msgstr ""
1113
 
1114
+ #: inc/views/admin/metaboxes/feed-metabox.php:21
1115
  msgid "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt."
1116
  msgstr ""
1117
 
1118
+ #: inc/views/admin/metaboxes/feed-metabox.php:22
1119
  msgid "Adding a backlink below the feed entries will also let the visitors know where the content came from."
1120
  msgstr ""
1121
 
1122
+ #: inc/views/admin/metaboxes/feed-metabox.php:27
1123
  msgid "Change Feed Settings"
1124
  msgstr ""
1125
 
1148
  msgstr ""
1149
 
1150
  #. translators: %s = Reading Settings URL. Links are in Markdown!
1151
+ #: inc/views/admin/metaboxes/feed-metabox.php:63
1152
  msgid "Note: The feed is already converted into an excerpt through the [Reading Settings](%s)."
1153
  msgstr ""
1154
 
1155
+ #: inc/views/admin/metaboxes/feed-metabox.php:76
1156
  msgid "View the main feed."
1157
  msgstr ""
1158
 
1159
+ #: inc/views/admin/metaboxes/general-metabox.php:23
1160
  msgid "Layout"
1161
  msgstr ""
1162
 
1163
+ #: inc/views/admin/metaboxes/general-metabox.php:28
1164
  msgid "Performance"
1165
  msgstr ""
1166
 
1167
+ #: inc/views/admin/metaboxes/general-metabox.php:33
1168
  msgid "Canonical"
1169
  msgstr ""
1170
 
1171
+ #: inc/views/admin/metaboxes/general-metabox.php:38
1172
  msgid "Timestamps"
1173
  msgstr ""
1174
 
1175
+ #: inc/views/admin/metaboxes/general-metabox.php:43
1176
  msgid "Exclusions"
1177
  msgstr ""
1178
 
1179
+ #: inc/views/admin/metaboxes/general-metabox.php:62
1180
  msgid "Administrative Layout Settings"
1181
  msgstr ""
1182
 
1188
  msgid "SEO Bar Settings"
1189
  msgstr ""
1190
 
1191
+ #: inc/views/admin/metaboxes/general-metabox.php:73
1192
  msgid "Display the SEO Bar in overview tables?"
1193
  msgstr ""
1194
 
1195
+ #: inc/views/admin/metaboxes/general-metabox.php:78
1196
  msgid "Display the SEO Bar in the SEO Settings metabox?"
1197
  msgstr ""
1198
 
1199
+ #: inc/views/admin/metaboxes/general-metabox.php:83
1200
  msgid "Use symbols for warnings?"
1201
  msgstr ""
1202
 
1203
+ #: inc/views/admin/metaboxes/general-metabox.php:84
1204
  msgid "If you have difficulty discerning colors, this may help you spot issues more easily."
1205
  msgstr ""
1206
 
1207
+ #: inc/views/admin/metaboxes/general-metabox.php:97
1208
  msgid "Counter Settings"
1209
  msgstr ""
1210
 
1211
+ #: inc/views/admin/metaboxes/general-metabox.php:100
1212
  msgid "The pixel counter computes whether the input will fit on search engine result pages."
1213
  msgstr ""
1214
 
1215
+ #: inc/views/admin/metaboxes/general-metabox.php:106
1216
  msgid "The character counter is based on guidelines."
1217
  msgstr ""
1218
 
1219
+ #: inc/views/admin/metaboxes/general-metabox.php:115
1220
  msgid "Display pixel counters?"
1221
  msgstr ""
1222
 
1223
+ #: inc/views/admin/metaboxes/general-metabox.php:120
1224
  msgid "Display character counters?"
1225
  msgstr ""
1226
 
1227
+ #: inc/views/admin/metaboxes/general-metabox.php:129
1228
  msgid "Performance Settings"
1229
  msgstr ""
1230
 
1231
+ #: inc/views/admin/metaboxes/general-metabox.php:130
1232
  msgid "Depending on your server's configuration, adjusting these settings can affect performance."
1233
  msgstr ""
1234
 
1235
+ #: inc/views/admin/metaboxes/general-metabox.php:135
1236
  msgid "Query Alteration Settings"
1237
  msgstr ""
1238
 
1239
+ #: inc/views/admin/metaboxes/general-metabox.php:137
1240
  msgid "Altering the query allows for more control of the site's hierarchy."
1241
  msgstr ""
1242
 
1243
+ #: inc/views/admin/metaboxes/general-metabox.php:139
1244
  msgid "If your website has thousands of pages, these options can greatly affect database performance."
1245
  msgstr ""
1246
 
1247
+ #: inc/views/admin/metaboxes/general-metabox.php:143
1248
  msgid "Altering the query in the database is more accurate, but can increase database query time."
1249
  msgstr ""
1250
 
1251
+ #: inc/views/admin/metaboxes/general-metabox.php:145
1252
  msgid "Altering the query on the site is much faster, but can lead to inconsistent pagination. It can also lead to 404 error messages if all queried pages have been excluded."
1253
  msgstr ""
1254
 
1255
+ #: inc/views/admin/metaboxes/general-metabox.php:151
1256
  msgctxt "Perform query alteration: In the database"
1257
  msgid "In the database"
1258
  msgstr ""
1259
 
1260
+ #: inc/views/admin/metaboxes/general-metabox.php:152
1261
  msgctxt "Perform query alteration: On the site"
1262
  msgid "On the site"
1263
  msgstr ""
1264
 
1265
+ #: inc/views/admin/metaboxes/general-metabox.php:182
1266
  msgid "Perform alteration:"
1267
  msgstr ""
1268
 
1269
+ #: inc/views/admin/metaboxes/general-metabox.php:210
1270
  msgid "Enable search query alteration?"
1271
  msgstr ""
1272
 
1273
+ #: inc/views/admin/metaboxes/general-metabox.php:211
1274
  msgid "This allows you to exclude pages from on-site search results."
1275
  msgstr ""
1276
 
1277
+ #: inc/views/admin/metaboxes/general-metabox.php:223
1278
  msgid "Enable archive query alteration?"
1279
  msgstr ""
1280
 
1281
+ #: inc/views/admin/metaboxes/general-metabox.php:224
1282
  msgid "This allows you to exclude pages from on-site archive listings."
1283
  msgstr ""
1284
 
1285
+ #: inc/views/admin/metaboxes/general-metabox.php:234
1286
  msgid "Transient Cache Settings"
1287
  msgstr ""
1288
 
1289
+ #: inc/views/admin/metaboxes/general-metabox.php:235
1290
  msgid "To improve performance, generated output can be stored in the database as transient cache."
1291
  msgstr ""
1292
 
1293
+ #: inc/views/admin/metaboxes/general-metabox.php:240
1294
  msgid "Enable optimized sitemap generation cache?"
1295
  msgstr ""
1296
 
1297
+ #: inc/views/admin/metaboxes/general-metabox.php:241
1298
  msgid "Generating the sitemap can use a lot of server resources."
1299
  msgstr ""
1300
 
1301
+ #: inc/views/admin/metaboxes/general-metabox.php:249
 
 
 
 
 
 
 
 
 
 
 
 
1302
  msgid "Canonical URL Settings"
1303
  msgstr ""
1304
 
1305
+ #: inc/views/admin/metaboxes/general-metabox.php:250
1306
  msgid "The canonical URL meta tag urges search engines to go to the outputted URL."
1307
  msgstr ""
1308
 
1309
+ #: inc/views/admin/metaboxes/general-metabox.php:251
1310
  msgid "If the canonical URL meta tag represents the visited page, then the search engine will crawl the visited page. Otherwise, the search engine may go to the outputted URL."
1311
  msgstr ""
1312
 
1313
+ #: inc/views/admin/metaboxes/general-metabox.php:255
1314
  msgid "Scheme Settings"
1315
  msgstr ""
1316
 
1317
+ #: inc/views/admin/metaboxes/general-metabox.php:256
1318
  msgid "If your website is accessible via both HTTP as HTTPS, you may want to set this to HTTPS if not detected automatically. Secure connections are preferred by search engines."
1319
  msgstr ""
1320
 
1321
+ #: inc/views/admin/metaboxes/general-metabox.php:258
1322
  msgctxt "= Detect Automatically, HTTPS, HTTP"
1323
  msgid "Preferred canonical URL scheme:"
1324
  msgstr ""
1325
 
1326
  #. translators: %s = HTTP or HTTPS
1327
+ #: inc/views/admin/metaboxes/general-metabox.php:266
1328
  msgid "Detect automatically (%s)"
1329
  msgstr ""
1330
 
1331
+ #: inc/views/admin/metaboxes/general-metabox.php:280
1332
  msgid "Link Relationship Settings"
1333
  msgstr ""
1334
 
1335
+ #: inc/views/admin/metaboxes/general-metabox.php:281
1336
  msgid "Some search engines look for relations between the content of your pages. If you have pagination on a post or page, or have archives indexed, these options will help search engines look for the right page to display in the search results."
1337
  msgstr ""
1338
 
1339
+ #: inc/views/admin/metaboxes/general-metabox.php:282
1340
  msgid "It's recommended to turn these options on for better SEO consistency and to prevent duplicated content issues."
1341
  msgstr ""
1342
 
1343
  #. translators: the backticks are Markdown! Preserve them as-is!
1344
+ #: inc/views/admin/metaboxes/general-metabox.php:288
1345
  msgid "Add `rel` link tags to posts and pages?"
1346
  msgstr ""
1347
 
1348
  #. translators: the backticks are Markdown! Preserve them as-is!
1349
+ #: inc/views/admin/metaboxes/general-metabox.php:298
1350
  msgid "Add `rel` link tags to archives?"
1351
  msgstr ""
1352
 
1353
  #. translators: the backticks are Markdown! Preserve them as-is!
1354
+ #: inc/views/admin/metaboxes/general-metabox.php:308
1355
  msgid "Add `rel` link tags to the homepage?"
1356
  msgstr ""
1357
 
1358
+ #: inc/views/admin/metaboxes/general-metabox.php:321
1359
  msgid "Timestamp Settings"
1360
  msgstr ""
1361
 
1362
+ #: inc/views/admin/metaboxes/general-metabox.php:322
1363
  msgid "Timestamps help indicate when a page has been published and modified."
1364
  msgstr ""
1365
 
1366
+ #: inc/views/admin/metaboxes/general-metabox.php:327
1367
  msgid "Timestamp Format Settings"
1368
  msgstr ""
1369
 
1370
+ #: inc/views/admin/metaboxes/general-metabox.php:328
1371
  msgid "This setting determines how specific the timestamp is."
1372
  msgstr ""
1373
 
1374
+ #: inc/views/admin/metaboxes/general-metabox.php:337
1375
  msgid "This outputs the complete date."
1376
  msgstr ""
1377
 
1378
+ #: inc/views/admin/metaboxes/general-metabox.php:348
1379
  msgid "This outputs the complete date including hours, minutes, and timezone."
1380
  msgstr ""
1381
 
1382
+ #: inc/views/admin/metaboxes/general-metabox.php:362
1383
  msgid "Exclusion Settings"
1384
  msgstr ""
1385
 
1386
+ #: inc/views/admin/metaboxes/general-metabox.php:363
1387
  msgid "When checked, these options will remove meta optimizations, SEO suggestions, and sitemap inclusions for the selected post types and taxonomies. This will allow search engines to crawl the post type and taxonomies without advanced restrictions or directions."
1388
  msgstr ""
1389
 
1390
  #. translators: backticks are code wraps. Markdown!
1391
+ #: inc/views/admin/metaboxes/general-metabox.php:367
1392
  msgid "These options should not need changing when post types and taxonomies are registered correctly. When they aren't, consider applying `noindex` to purge them from search engines, instead."
1393
  msgstr ""
1394
 
1395
+ #: inc/views/admin/metaboxes/general-metabox.php:371
1396
  msgid "Default post types and taxonomies can not be excluded."
1397
  msgstr ""
1398
 
1399
+ #: inc/views/admin/metaboxes/general-metabox.php:376
1400
  msgid "Post Type Exclusions"
1401
  msgstr ""
1402
 
1403
+ #: inc/views/admin/metaboxes/general-metabox.php:377
1404
  msgid "Select post types which should be excluded."
1405
  msgstr ""
1406
 
1407
+ #: inc/views/admin/metaboxes/general-metabox.php:378
1408
+ #: inc/views/admin/metaboxes/robots-metabox.php:311
1409
  msgid "These settings apply to the post type pages and their terms. When terms are shared between post types, all their post types should be checked for this to have an effect."
1410
  msgstr ""
1411
 
1412
+ #: inc/views/admin/metaboxes/general-metabox.php:412
1413
  msgid "Taxonomy Exclusions"
1414
  msgstr ""
1415
 
1416
+ #: inc/views/admin/metaboxes/general-metabox.php:413
1417
  msgid "Select taxonomies which should be excluded."
1418
  msgstr ""
1419
 
1420
+ #: inc/views/admin/metaboxes/general-metabox.php:414
1421
  msgid "When taxonomies have all their bound post types excluded, they will inherit their exclusion status."
1422
  msgstr ""
1423
 
1424
+ #: inc/views/admin/metaboxes/homepage-metabox.php:28
1425
  msgid "These settings will take precedence over the settings set within the homepage edit screen, if any."
1426
  msgstr ""
1427
 
1428
+ #: inc/views/admin/metaboxes/homepage-metabox.php:35
1429
+ #: inc/views/admin/metaboxes/robots-metabox.php:61
1430
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:23
1431
+ #: inc/views/admin/metaboxes/social-metabox.php:23
1432
  #: inc/views/admin/metaboxes/title-metabox.php:118
1433
+ #: inc/views/edit/seo-settings-singular.php:31
1434
  msgid "General"
1435
  msgstr ""
1436
 
1437
+ #: inc/views/admin/metaboxes/homepage-metabox.php:40
1438
  #: inc/views/admin/metaboxes/title-metabox.php:123
1439
  msgid "Additions"
1440
  msgstr ""
1441
 
1442
+ #: inc/views/admin/metaboxes/homepage-metabox.php:45
1443
+ #: inc/views/edit/seo-settings-singular.php:36
1444
  msgid "Social"
1445
  msgstr ""
1446
 
1447
+ #: inc/views/admin/metaboxes/homepage-metabox.php:50
1448
  msgid "Robots"
1449
  msgstr ""
1450
 
1451
+ #: inc/views/admin/metaboxes/homepage-metabox.php:72
1452
+ #: inc/views/edit/seo-settings-singular.php:121
1453
+ #: inc/views/edit/seo-settings-tt.php:116
1454
+ #: inc/views/list/quick-post.php:60
1455
+ #: inc/views/list/quick-term.php:60
1456
  msgid "Meta Title"
1457
  msgstr ""
1458
 
1459
+ #: inc/views/admin/metaboxes/homepage-metabox.php:76
1460
+ #: inc/views/edit/seo-settings-singular.php:125
1461
+ #: inc/views/edit/seo-settings-tt.php:120
1462
  msgid "The meta title can be used to determine the title used on search engine result pages."
1463
  msgstr ""
1464
 
1465
+ #: inc/views/admin/metaboxes/homepage-metabox.php:110
1466
  msgid "Note: The input value of this field may be used to describe the name of the site elsewhere."
1467
  msgstr ""
1468
 
1469
+ #: inc/views/admin/metaboxes/homepage-metabox.php:113
1470
+ #: inc/views/admin/metaboxes/homepage-metabox.php:298
1471
+ #: inc/views/admin/metaboxes/homepage-metabox.php:347
1472
  msgid "Note: The title placeholder is fetched from the Page SEO Settings on the homepage."
1473
  msgstr ""
1474
 
1475
  #. translators: %s = Homepage URL markdown
1476
+ #: inc/views/admin/metaboxes/homepage-metabox.php:126
1477
+ #: inc/views/admin/metaboxes/homepage-metabox.php:189
1478
  msgid "A plugin has been detected that suggests to maintain this option on the [homepage](%s)."
1479
  msgstr ""
1480
 
1481
+ #: inc/views/admin/metaboxes/homepage-metabox.php:139
1482
+ #: inc/views/edit/seo-settings-singular.php:191
1483
+ #: inc/views/edit/seo-settings-tt.php:167
1484
+ #: inc/views/list/quick-post.php:77
1485
+ #: inc/views/list/quick-term.php:77
1486
  msgid "Meta Description"
1487
  msgstr ""
1488
 
1489
+ #: inc/views/admin/metaboxes/homepage-metabox.php:175
1490
+ #: inc/views/admin/metaboxes/homepage-metabox.php:322
1491
+ #: inc/views/admin/metaboxes/homepage-metabox.php:371
1492
  msgid "Note: The description placeholder is fetched from the Page SEO Settings on the homepage."
1493
  msgstr ""
1494
 
1495
+ #: inc/views/admin/metaboxes/homepage-metabox.php:219
1496
  msgid "Meta Title Additions"
1497
  msgstr ""
1498
 
1499
+ #: inc/views/admin/metaboxes/homepage-metabox.php:231
1500
  msgid "Add Meta Title Additions to the homepage title?"
1501
  msgstr ""
1502
 
1504
  msgid "Meta Title Additions Location"
1505
  msgstr ""
1506
 
1507
+ #: inc/views/admin/metaboxes/homepage-metabox.php:247
1508
+ #: inc/views/admin/metaboxes/title-metabox.php:225
1509
  msgid "Left:"
1510
  msgstr ""
1511
 
1512
+ #: inc/views/admin/metaboxes/homepage-metabox.php:257
1513
+ #: inc/views/admin/metaboxes/title-metabox.php:235
1514
  msgid "Right:"
1515
  msgstr ""
1516
 
1517
+ #: inc/views/admin/metaboxes/homepage-metabox.php:283
1518
+ #: inc/views/edit/seo-settings-singular.php:435
1519
+ #: inc/views/edit/seo-settings-tt.php:209
1520
  msgid "Open Graph Title"
1521
  msgstr ""
1522
 
1523
+ #: inc/views/admin/metaboxes/homepage-metabox.php:307
1524
+ #: inc/views/edit/seo-settings-singular.php:458
1525
+ #: inc/views/edit/seo-settings-tt.php:226
1526
  msgid "Open Graph Description"
1527
  msgstr ""
1528
 
1529
+ #: inc/views/admin/metaboxes/homepage-metabox.php:332
1530
+ #: inc/views/edit/seo-settings-singular.php:479
1531
+ #: inc/views/edit/seo-settings-tt.php:241
1532
  msgid "Twitter Title"
1533
  msgstr ""
1534
 
1535
+ #: inc/views/admin/metaboxes/homepage-metabox.php:356
1536
+ #: inc/views/edit/seo-settings-singular.php:502
1537
+ #: inc/views/edit/seo-settings-tt.php:258
1538
  msgid "Twitter Description"
1539
  msgstr ""
1540
 
1541
+ #: inc/views/admin/metaboxes/homepage-metabox.php:377
1542
+ #: inc/views/admin/metaboxes/social-metabox.php:135
1543
  msgid "Social Image Settings"
1544
  msgstr ""
1545
 
1546
+ #: inc/views/admin/metaboxes/homepage-metabox.php:378
1547
  msgid "A social image can be displayed when your homepage is shared. It is a great way to grab attention."
1548
  msgstr ""
1549
 
1550
+ #: inc/views/admin/metaboxes/homepage-metabox.php:387
1551
+ #: inc/views/edit/seo-settings-singular.php:535
1552
+ #: inc/views/edit/seo-settings-tt.php:273
1553
  msgid "Social Image URL"
1554
  msgstr ""
1555
 
1556
+ #: inc/views/admin/metaboxes/homepage-metabox.php:390
1557
+ #: inc/views/edit/seo-settings-singular.php:539
1558
+ #: inc/views/edit/seo-settings-tt.php:277
1559
  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."
1560
  msgstr ""
1561
 
1562
+ #: inc/views/admin/metaboxes/homepage-metabox.php:428
1563
  msgctxt "Bear with me: the homepage can be edited globally, or via its page. Thus \"homepage page\"."
1564
  msgid "Edit homepage page settings"
1565
  msgstr ""
1566
 
1567
+ #: inc/views/admin/metaboxes/homepage-metabox.php:429
1568
  msgid "Overwritten by page settings"
1569
  msgstr ""
1570
 
1571
  #. translators: 1: Option label, 2: [?] option info note, 3: Optional warning
1572
+ #: inc/views/admin/metaboxes/homepage-metabox.php:439
1573
+ #: inc/views/admin/metaboxes/homepage-metabox.php:455
1574
+ #: inc/views/admin/metaboxes/homepage-metabox.php:471
1575
  msgctxt "robots setting"
1576
  msgid "%1$s %2$s %3$s"
1577
  msgstr ""
1578
 
1579
  #. translators: the backticks are Markdown! Preserve them as-is!
1580
+ #: inc/views/admin/metaboxes/homepage-metabox.php:442
1581
  msgid "Apply `noindex` to the homepage?"
1582
  msgstr ""
1583
 
1584
+ #: inc/views/admin/metaboxes/homepage-metabox.php:446
1585
  msgid "This tells search engines not to show this page in their search results."
1586
  msgstr ""
1587
 
1588
  #. translators: the backticks are Markdown! Preserve them as-is!
1589
+ #: inc/views/admin/metaboxes/homepage-metabox.php:458
1590
  msgid "Apply `nofollow` to the homepage?"
1591
  msgstr ""
1592
 
1593
+ #: inc/views/admin/metaboxes/homepage-metabox.php:462
1594
  msgid "This tells search engines not to follow links on this page."
1595
  msgstr ""
1596
 
1597
  #. translators: the backticks are Markdown! Preserve them as-is!
1598
+ #: inc/views/admin/metaboxes/homepage-metabox.php:474
1599
  msgid "Apply `noarchive` to the homepage?"
1600
  msgstr ""
1601
 
1602
+ #: inc/views/admin/metaboxes/homepage-metabox.php:478
1603
  msgid "This tells search engines not to save a cached copy of this page."
1604
  msgstr ""
1605
 
1606
+ #: inc/views/admin/metaboxes/homepage-metabox.php:485
1607
+ #: inc/views/edit/seo-settings-singular.php:306
1608
  msgid "Warning: No public site should ever apply \"noindex\" or \"nofollow\" to the homepage."
1609
  msgstr ""
1610
 
1611
  #. translators: %s = Homepage URL markdown
1612
+ #: inc/views/admin/metaboxes/homepage-metabox.php:513
1613
  msgid "Note: These options may be overwritten by the [page settings](%s)."
1614
  msgstr ""
1615
 
1616
+ #: inc/views/admin/metaboxes/homepage-metabox.php:525
1617
  msgid "Homepage Pagination Robots Settings"
1618
  msgstr ""
1619
 
1620
+ #: inc/views/admin/metaboxes/homepage-metabox.php:526
1621
  msgid "If your homepage is paginated and outputs content that's also found elsewhere on the website, enabling this option may prevent duplicate content."
1622
  msgstr ""
1623
 
1624
  #. translators: the backticks are Markdown! Preserve them as-is!
1625
+ #: inc/views/admin/metaboxes/homepage-metabox.php:534
1626
  msgid "Apply `noindex` to every second or later page on the homepage?"
1627
  msgstr ""
1628
 
1629
+ #: inc/views/admin/metaboxes/robots-metabox.php:23
1630
  msgid "Author pages"
1631
  msgstr ""
1632
 
1633
+ #: inc/views/admin/metaboxes/robots-metabox.php:27
1634
  msgid "Date archives"
1635
  msgstr ""
1636
 
1637
+ #: inc/views/admin/metaboxes/robots-metabox.php:31
1638
  msgid "Search pages"
1639
  msgstr ""
1640
 
1641
+ #: inc/views/admin/metaboxes/robots-metabox.php:35
1642
  msgctxt "...for the entire site"
1643
  msgid "the entire site"
1644
  msgstr ""
1645
 
1646
+ #: inc/views/admin/metaboxes/robots-metabox.php:47
1647
  msgid "These options most likely prevent indexing of the selected archives and pages. If you enable this, the selected archives or pages will urge to be removed from search engine results pages."
1648
  msgstr ""
1649
 
1650
+ #: inc/views/admin/metaboxes/robots-metabox.php:51
1651
  msgid "These options most likely prevent links from being followed on the selected archives and pages. If you enable this, the selected archives or pages in-page links will gain no SEO value, including your internal links."
1652
  msgstr ""
1653
 
1654
+ #: inc/views/admin/metaboxes/robots-metabox.php:55
1655
  msgid "These options most likely prevent caching of the selected archives and pages. If you enable this, bots are urged not create a cached copy of the selected archives or pages."
1656
  msgstr ""
1657
 
1658
+ #: inc/views/admin/metaboxes/robots-metabox.php:114
1659
  msgid "Advanced Query Protection"
1660
  msgstr ""
1661
 
1667
  msgid "Enable advanced query protection?"
1668
  msgstr ""
1669
 
1670
+ #: inc/views/admin/metaboxes/robots-metabox.php:127
1671
  msgid "Paginated Archive Settings"
1672
  msgstr ""
1673
 
1674
+ #: inc/views/admin/metaboxes/robots-metabox.php:128
1675
  msgid "Indexing the second or later page of any archive might cause duplication errors. Search engines look down upon them; therefore, it's recommended to disable indexing of those pages."
1676
  msgstr ""
1677
 
1678
  #. translators: the backticks are Markdown! Preserve them as-is!
1679
+ #: inc/views/admin/metaboxes/robots-metabox.php:135
1680
  msgid "Apply `noindex` to every second or later archive page?"
1681
  msgstr ""
1682
 
1683
+ #: inc/views/admin/metaboxes/robots-metabox.php:145
1684
  msgid "Copyright Directive Settings"
1685
  msgstr ""
1686
 
1687
+ #: inc/views/admin/metaboxes/robots-metabox.php:146
1688
  msgid "Some search engines allow you to control copyright directives on the content they aggregate. It's best to allow some content to be taken by these aggregators, as that can improve contextualized exposure via snippets and previews. When left unspecified, regional regulations may apply. It is up to the aggregator to honor these requests."
1689
  msgstr ""
1690
 
1691
+ #: inc/views/admin/metaboxes/robots-metabox.php:151
1692
  msgid "Specify aggregator copyright compliance directives?"
1693
  msgstr ""
1694
 
1695
+ #: inc/views/admin/metaboxes/robots-metabox.php:157
1696
  msgid "Unlimited"
1697
  msgstr ""
1698
 
1699
+ #: inc/views/admin/metaboxes/robots-metabox.php:158
1700
  msgctxt "quanity: zero"
1701
  msgid "None, disallow snippet"
1702
  msgstr ""
1703
 
1704
  #. translators: %d = number
1705
+ #: inc/views/admin/metaboxes/robots-metabox.php:162
1706
  msgid "%d character"
1707
  msgid_plural "%d characters"
1708
  msgstr[0] ""
1709
  msgstr[1] ""
1710
 
1711
+ #: inc/views/admin/metaboxes/robots-metabox.php:168
1712
+ #: inc/views/admin/metaboxes/robots-metabox.php:254
1713
  msgid "Standard directive"
1714
  msgstr ""
1715
 
1716
+ #: inc/views/admin/metaboxes/robots-metabox.php:169
1717
+ #: inc/views/admin/metaboxes/robots-metabox.php:255
1718
  msgid "Granular directive"
1719
  msgstr ""
1720
 
1721
+ #: inc/views/admin/metaboxes/robots-metabox.php:192
1722
  msgid "Maximum text snippet length"
1723
  msgstr ""
1724
 
1725
+ #: inc/views/admin/metaboxes/robots-metabox.php:196
1726
  msgid "This may limit the text snippet length for all pages on this site."
1727
  msgstr ""
1728
 
1729
+ #: inc/views/admin/metaboxes/robots-metabox.php:200
1730
  msgid "This directive also imposes a limit on meta descriptions and structured data, which unintentionally restricts the amount of information you can share. Therefore, it's best to use at least a 320 character limit."
1731
  msgstr ""
1732
 
1733
+ #: inc/views/admin/metaboxes/robots-metabox.php:209
1734
  msgctxt "quanity: zero"
1735
  msgid "None, disallow preview"
1736
  msgstr ""
1737
 
1738
+ #: inc/views/admin/metaboxes/robots-metabox.php:210
1739
  msgid "Thumbnail or standard size"
1740
  msgstr ""
1741
 
1742
+ #: inc/views/admin/metaboxes/robots-metabox.php:211
1743
  msgid "Large or full size"
1744
  msgstr ""
1745
 
1746
+ #: inc/views/admin/metaboxes/robots-metabox.php:229
1747
  msgid "Maximum image preview size"
1748
  msgstr ""
1749
 
1750
+ #: inc/views/admin/metaboxes/robots-metabox.php:233
1751
  msgid "This may limit the image preview size for all images from this site."
1752
  msgstr ""
1753
 
1754
+ #: inc/views/admin/metaboxes/robots-metabox.php:243
1755
  msgid "Full video preview"
1756
  msgstr ""
1757
 
1758
+ #: inc/views/admin/metaboxes/robots-metabox.php:244
1759
  msgctxt "quanity: zero"
1760
  msgid "None, still image only"
1761
  msgstr ""
1762
 
1763
  #. translators: %d = number
1764
+ #: inc/views/admin/metaboxes/robots-metabox.php:248
1765
  msgid "%d second"
1766
  msgid_plural "%d seconds"
1767
  msgstr[0] ""
1768
  msgstr[1] ""
1769
 
1770
+ #: inc/views/admin/metaboxes/robots-metabox.php:277
1771
  msgid "Maximum video preview length"
1772
  msgstr ""
1773
 
1774
+ #: inc/views/admin/metaboxes/robots-metabox.php:281
1775
  msgid "This may limit the video preview length for all videos on this site."
1776
  msgstr ""
1777
 
1778
  #. translators: SINGULAR. 1 = noindex/nofollow/noarchive, 2 = The entire site
1779
+ #: inc/views/admin/metaboxes/robots-metabox.php:296
1780
  msgctxt "singular"
1781
  msgid "Apply %1$s to %2$s?"
1782
  msgstr ""
1783
 
1784
  #. translators: PLURAL. 1 = noindex/nofollow/noarchive, 2 = Archives, Posts, Pages, etc.
1785
+ #: inc/views/admin/metaboxes/robots-metabox.php:298
1786
  msgctxt "plural"
1787
  msgid "Apply %1$s to %2$s?"
1788
  msgstr ""
1789
 
1790
+ #: inc/views/admin/metaboxes/robots-metabox.php:305
1791
  msgid "Robots Settings"
1792
  msgstr ""
1793
 
1794
+ #: inc/views/admin/metaboxes/robots-metabox.php:310
1795
  msgid "Post Type Settings"
1796
  msgstr ""
1797
 
1798
+ #: inc/views/admin/metaboxes/robots-metabox.php:317
1799
  msgid "Warning: No site should enable these options for Posts and Pages."
1800
  msgstr ""
1801
 
1802
+ #: inc/views/admin/metaboxes/robots-metabox.php:348
1803
  msgid "Taxonomy Settings"
1804
  msgstr ""
1805
 
1806
+ #: inc/views/admin/metaboxes/robots-metabox.php:349
1807
  msgid "These settings apply to the taxonomies of post types. When taxonomies have all their bound post types' options checked, they will inherit their status."
1808
  msgstr ""
1809
 
1810
+ #: inc/views/admin/metaboxes/robots-metabox.php:383
1811
  msgid "Global Settings"
1812
  msgstr ""
1813
 
1814
+ #: inc/views/admin/metaboxes/robots-metabox.php:384
1815
  msgid "These settings apply to other globally registered content types."
1816
  msgstr ""
1817
 
1818
+ #: inc/views/admin/metaboxes/robots-metabox.php:404
1819
  msgid "Warning: No public site should ever enable this option."
1820
  msgstr ""
1821
 
1822
+ #: inc/views/admin/metaboxes/schema-metabox.php:21
1823
  msgid "Schema.org Output Settings"
1824
  msgstr ""
1825
 
1847
  msgid "Presence"
1848
  msgstr ""
1849
 
1850
+ #: inc/views/admin/metaboxes/schema-metabox.php:56
1851
  msgid "Site Structure Options"
1852
  msgstr ""
1853
 
1854
+ #: inc/views/admin/metaboxes/schema-metabox.php:57
1855
  msgid "The site structure Schema.org output allows search engines to gain knowledge on how your website is built."
1856
  msgstr ""
1857
 
1858
+ #: inc/views/admin/metaboxes/schema-metabox.php:58
1859
  msgid "For example, search engines display your pages' URLs when listed in the search results. These options allow you to enhance those URLs output."
1860
  msgstr ""
1861
 
1862
+ #: inc/views/admin/metaboxes/schema-metabox.php:61
1863
  msgid "Breadcrumbs"
1864
  msgstr ""
1865
 
1866
+ #: inc/views/admin/metaboxes/schema-metabox.php:62
1867
  msgid "Breadcrumb trails indicate page positions in the site's hierarchy. Using the following option will show the hierarchy within the search results when available."
1868
  msgstr ""
1869
 
1870
+ #: inc/views/admin/metaboxes/schema-metabox.php:65
1871
+ #: inc/views/admin/metaboxes/schema-metabox.php:85
1872
+ #: inc/views/admin/metaboxes/schema-metabox.php:104
1873
+ #: inc/views/admin/metaboxes/schema-metabox.php:152
1874
  msgid "Learn how this data is used."
1875
  msgstr ""
1876
 
1877
+ #: inc/views/admin/metaboxes/schema-metabox.php:72
1878
  msgid "Enable Breadcrumbs?"
1879
  msgstr ""
1880
 
1881
+ #: inc/views/admin/metaboxes/schema-metabox.php:80
1882
  msgctxt "Product name"
1883
  msgid "Sitelinks Searchbox"
1884
  msgstr ""
1885
 
1886
+ #: inc/views/admin/metaboxes/schema-metabox.php:82
1887
  msgid "When Search users search for your brand name, the following option allows them to search through this website directly from the search results."
1888
  msgstr ""
1889
 
1890
+ #: inc/views/admin/metaboxes/schema-metabox.php:92
1891
  msgctxt "Sitelinks Searchbox is a Product name"
1892
  msgid "Enable Sitelinks Searchbox?"
1893
  msgstr ""
1894
 
1895
+ #: inc/views/admin/metaboxes/schema-metabox.php:100
1896
  msgid "Authorized Presence Options"
1897
  msgstr ""
1898
 
1899
+ #: inc/views/admin/metaboxes/schema-metabox.php:101
1900
  msgid "The authorized presence Schema.org output helps search engine users find ways to interact with this website."
1901
  msgstr ""
1902
 
1903
+ #: inc/views/admin/metaboxes/schema-metabox.php:112
1904
  msgid "Output Authorized Presence?"
1905
  msgstr ""
1906
 
1907
+ #: inc/views/admin/metaboxes/schema-metabox.php:120
1908
  msgid "About this website"
1909
  msgstr ""
1910
 
1911
+ #: inc/views/admin/metaboxes/schema-metabox.php:122
1912
  msgctxt "...Organization or Person."
1913
  msgid "This website represents:"
1914
  msgstr ""
1915
 
1916
+ #: inc/views/admin/metaboxes/schema-metabox.php:128
1917
  msgid "An Organization"
1918
  msgstr ""
1919
 
1920
+ #: inc/views/admin/metaboxes/schema-metabox.php:129
1921
  msgid "A Person"
1922
  msgstr ""
1923
 
1924
+ #: inc/views/admin/metaboxes/schema-metabox.php:141
1925
  msgid "The organization or personal name"
1926
  msgstr ""
1927
 
1928
+ #: inc/views/admin/metaboxes/schema-metabox.php:149
1929
  msgid "Website logo"
1930
  msgstr ""
1931
 
1932
+ #: inc/views/admin/metaboxes/schema-metabox.php:150
1933
  msgid "These options are used when this site represents an organization. When no logo is outputted, search engine will look elsewhere."
1934
  msgstr ""
1935
 
1936
+ #: inc/views/admin/metaboxes/schema-metabox.php:159
1937
  msgid "Enable logo?"
1938
  msgstr ""
1939
 
1940
+ #: inc/views/admin/metaboxes/schema-metabox.php:168
1941
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:391
1942
  msgid "Logo URL"
1943
  msgstr ""
1944
 
1945
+ #: inc/views/admin/metaboxes/schema-metabox.php:172
1946
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:395
1947
  msgid "Setting a logo requires JavaScript."
1948
  msgstr ""
1949
 
1950
+ #: inc/views/admin/metaboxes/schema-metabox.php:198
1951
  msgctxt "No spaces. E.g. https://facebook.com/RelatedProfile"
1952
  msgid "RelatedProfile"
1953
  msgstr ""
1954
 
1955
+ #: inc/views/admin/metaboxes/schema-metabox.php:206
1956
  msgid "Facebook Page"
1957
  msgstr ""
1958
 
1959
+ #: inc/views/admin/metaboxes/schema-metabox.php:213
1960
  msgid "Twitter Profile"
1961
  msgstr ""
1962
 
1963
+ #: inc/views/admin/metaboxes/schema-metabox.php:220
1964
  msgctxt "Google+ is dead. &#8224; is a cross, indicating that."
1965
  msgid "Google+ Profile&#8224;"
1966
  msgstr ""
1967
 
1968
+ #: inc/views/admin/metaboxes/schema-metabox.php:227
1969
  msgid "Instagram Profile"
1970
  msgstr ""
1971
 
1972
+ #: inc/views/admin/metaboxes/schema-metabox.php:234
1973
  msgid "Youtube Profile"
1974
  msgstr ""
1975
 
1976
+ #: inc/views/admin/metaboxes/schema-metabox.php:241
1977
  msgid "LinkedIn Profile"
1978
  msgstr ""
1979
 
1980
+ #: inc/views/admin/metaboxes/schema-metabox.php:252
1981
  msgid "Pinterest Profile"
1982
  msgstr ""
1983
 
1984
+ #: inc/views/admin/metaboxes/schema-metabox.php:259
1985
  msgid "SoundCloud Profile"
1986
  msgstr ""
1987
 
1988
+ #: inc/views/admin/metaboxes/schema-metabox.php:266
1989
  msgid "Tumblr Blog"
1990
  msgstr ""
1991
 
1992
+ #: inc/views/admin/metaboxes/schema-metabox.php:285
1993
  msgid "Connected Social Pages"
1994
  msgstr ""
1995
 
1996
+ #: inc/views/admin/metaboxes/schema-metabox.php:286
1997
  msgid "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway."
1998
  msgstr ""
1999
 
2000
+ #: inc/views/admin/metaboxes/schema-metabox.php:287
2001
  msgid "Add links that lead directly to the connected social pages of this website."
2002
  msgstr ""
2003
 
2004
+ #: inc/views/admin/metaboxes/schema-metabox.php:288
2005
  msgid "These settings do not affect sharing behavior with the social networks."
2006
  msgstr ""
2007
 
2008
  #. translators: %s = Learn more URL. Markdown!
2009
+ #: inc/views/admin/metaboxes/schema-metabox.php:293
2010
  msgid "These settings are marked for removal. When you clear a field, it will be hidden forever. [Learn more](%s)."
2011
  msgstr ""
2012
 
2013
+ #: inc/views/admin/metaboxes/schema-metabox.php:312
2014
  msgid "View your profile."
2015
  msgstr ""
2016
 
2017
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:33
2018
  msgid "Metadata"
2019
  msgstr ""
2020
 
2021
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:38
2022
  msgctxt "Ping or notify search engine"
2023
  msgid "Ping"
2024
  msgstr ""
2025
 
2026
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:43
2027
  msgid "Style"
2028
  msgstr ""
2029
 
2030
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:66
2031
  msgid "Sitemap Integration Settings"
2032
  msgstr ""
2033
 
2052
  msgid "Sitemap Output"
2053
  msgstr ""
2054
 
2055
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:97
2056
  msgid "Output optimized sitemap?"
2057
  msgstr ""
2058
 
2059
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:99
2060
  msgid "This sitemap is processed quicker by search engines."
2061
  msgstr ""
2062
 
2063
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:114
2064
  msgid "View the base sitemap."
2065
  msgstr ""
2066
 
2067
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:126
2068
  msgid "View the sitemap index."
2069
  msgstr ""
2070
 
2071
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:137
2072
  msgid "Sitemap Query Limit"
2073
  msgstr ""
2074
 
2075
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:141
2076
  msgid "This setting affects how many pages are requested from the database per query."
2077
  msgstr ""
2078
 
2079
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:157
2080
  msgid "Consider lowering this value when the sitemap shows a white screen or notifies you of memory exhaustion."
2081
  msgstr ""
2082
 
2083
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:164
2084
  msgid "Robots.txt Settings"
2085
  msgstr ""
2086
 
2087
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:168
2088
  msgid "Note: A robots.txt file has been detected in the root folder of your website. This means these settings have no effect."
2089
  msgstr ""
2090
 
2091
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:174
2092
  msgid "Note: robots.txt files can't be generated or used on subdirectory installations."
2093
  msgstr ""
2094
 
2095
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:179
2096
  msgid "Note: You're using the plain permalink structure; so, no robots.txt file can be generated."
2097
  msgstr ""
2098
 
2099
  #. translators: 1 = Link to settings, Markdown. 2 = example input, also markdown! Preserve the Markdown as-is!
2100
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:185
2101
  msgid "Change your [Permalink Settings](%1$s). Recommended structure: `%2$s`."
2102
  msgstr ""
2103
 
2104
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:197
2105
  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."
2106
  msgstr ""
2107
 
2108
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:198
2109
  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."
2110
  msgstr ""
2111
 
2112
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:205
2113
  msgid "Sitemap Hinting"
2114
  msgstr ""
2115
 
2116
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:210
2117
  msgid "Add sitemap location to robots.txt?"
2118
  msgstr ""
2119
 
2120
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:223
2121
  msgid "View the robots.txt output."
2122
  msgstr ""
2123
 
2124
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:230
2125
  msgid "Timestamps Settings"
2126
  msgstr ""
2127
 
2128
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:231
2129
  msgid "The modified time suggests to search engines where to look for content changes first."
2130
  msgstr ""
2131
 
2132
  #. translators: the backticks are Markdown! Preserve them as-is!
2133
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:239
2134
  msgid "Add `<lastmod>` to the sitemap?"
2135
  msgstr ""
2136
 
2137
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:251
2138
  msgid "Priority Settings"
2139
  msgstr ""
2140
 
2141
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:252
2142
  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."
2143
  msgstr ""
2144
 
2145
  #. translators: the backticks are Markdown! Preserve them as-is!
2146
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:260
2147
  msgid "Add `<priority>` to the optimized sitemap?"
2148
  msgstr ""
2149
 
2150
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:271
2151
  msgid "Ping Settings"
2152
  msgstr ""
2153
 
2154
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:272
2155
  msgid "Notifying search engines of a sitemap change is helpful to get your content indexed as soon as possible."
2156
  msgstr ""
2157
 
2158
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:273
2159
  msgid "By default this will happen at most once an hour."
2160
  msgstr ""
2161
 
2162
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:279
2163
  msgid "Use cron for pinging?"
2164
  msgstr ""
2165
 
2166
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:281
2167
  msgid "This speeds up post and term saving processes, by offsetting pinging to a later time."
2168
  msgstr ""
2169
 
2170
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:289
2171
  msgid "Prerender optimized sitemap before pinging via cron?"
2172
  msgstr ""
2173
 
2174
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:291
2175
  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."
2176
  msgstr ""
2177
 
2178
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:295
2179
  msgid "Only enable prerendering when generating the sitemap takes over 60 seconds."
2180
  msgstr ""
2181
 
2182
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:305
2183
  msgid "Notify Search Engines"
2184
  msgstr ""
2185
 
2186
  #. translators: %s = Google
2187
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:316
2188
  msgid "Notify %s about sitemap changes?"
2189
  msgstr ""
2190
 
2191
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:328
2192
  msgid "Optimized Sitemap Styling Settings"
2193
  msgstr ""
2194
 
2195
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:329
2196
  msgid "You can style the optimized sitemap to give it a more personal look for your visitors. Search engines do not use these styles."
2197
  msgstr ""
2198
 
2199
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:330
2200
  msgid "Note: Changes may not appear to have an effect directly because the stylesheet is cached in the browser for 30 minutes."
2201
  msgstr ""
2202
 
2203
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:334
2204
  msgid "Enable Styling"
2205
  msgstr ""
2206
 
2207
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:339
2208
  msgid "Style sitemap?"
2209
  msgstr ""
2210
 
2211
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:339
2212
  msgid "This makes the sitemap more readable for humans."
2213
  msgstr ""
2214
 
2215
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:355
2216
  msgid "Sitemap Header Background Color"
2217
  msgstr ""
2218
 
2219
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:364
2220
  msgid "Sitemap Title and Lines Color"
2221
  msgstr ""
2222
 
2223
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:373
2224
  msgid "Header Title Logo"
2225
  msgstr ""
2226
 
2227
+ #: inc/views/admin/metaboxes/sitemaps-metabox.php:378
2228
  msgid "Show logo next to sitemap header title?"
2229
  msgstr ""
2230
 
2231
+ #: inc/views/admin/metaboxes/social-metabox.php:43
2232
  msgid "Post Dates"
2233
  msgstr ""
2234
 
2235
+ #: inc/views/admin/metaboxes/social-metabox.php:62
2236
  msgid "Social Meta Tags Settings"
2237
  msgstr ""
2238
 
2248
  msgid "Facebook, Twitter, Pinterest and many other social sites make use of these meta tags."
2249
  msgstr ""
2250
 
2251
+ #: inc/views/admin/metaboxes/social-metabox.php:79
2252
  msgid "Note: Another Open Graph plugin has been detected. These meta tags might conflict."
2253
  msgstr ""
2254
 
2255
+ #: inc/views/admin/metaboxes/social-metabox.php:85
2256
  msgid "Output Facebook meta tags?"
2257
  msgstr ""
2258
 
2259
+ #: inc/views/admin/metaboxes/social-metabox.php:86
2260
  msgid "Output various meta tags targeted at Facebook."
2261
  msgstr ""
2262
 
2263
+ #: inc/views/admin/metaboxes/social-metabox.php:95
2264
  msgid "Output Twitter meta tags?"
2265
  msgstr ""
2266
 
2267
+ #: inc/views/admin/metaboxes/social-metabox.php:96
2268
  msgid "Output various meta tags targeted at Twitter."
2269
  msgstr ""
2270
 
2271
+ #: inc/views/admin/metaboxes/social-metabox.php:101
2272
  msgid "Note: Another Twitter Card plugin has been detected. These meta tags might conflict."
2273
  msgstr ""
2274
 
2275
+ #: inc/views/admin/metaboxes/social-metabox.php:107
2276
  msgid "Output oEmbed scripts?"
2277
  msgstr ""
2278
 
2279
+ #: inc/views/admin/metaboxes/social-metabox.php:108
2280
  msgid "WordPress, Discord, Drupal, Squarespace, and many other clients can make use of these scripts."
2281
  msgstr ""
2282
 
2283
+ #: inc/views/admin/metaboxes/social-metabox.php:115
2284
  msgid "Social Title Settings"
2285
  msgstr ""
2286
 
2287
+ #: inc/views/admin/metaboxes/social-metabox.php:116
2288
  msgid "Most social sites and third-party services automatically include the website URL inside their embeds. When the site title is described well in the site URL, including it in the social title will be redundant."
2289
  msgstr ""
2290
 
2291
+ #: inc/views/admin/metaboxes/social-metabox.php:119
2292
  msgid "When you provide a custom Open Graph or Twitter title, the site title will be omitted automatically."
2293
  msgstr ""
2294
 
2295
+ #: inc/views/admin/metaboxes/social-metabox.php:127
2296
  msgid "Remove site title from generated social titles?"
2297
  msgstr ""
2298
 
2299
+ #: inc/views/admin/metaboxes/social-metabox.php:136
2300
  msgid "A social image can be displayed when your website is shared. It is a great way to grab attention."
2301
  msgstr ""
2302
 
2303
+ #: inc/views/admin/metaboxes/social-metabox.php:141
2304
  msgid "Output multiple Open Graph image tags?"
2305
  msgstr ""
2306
 
2307
+ #: inc/views/admin/metaboxes/social-metabox.php:142
2308
  msgid "This enables users to select any image attached to the page shared on social networks, like Facebook."
2309
  msgstr ""
2310
 
2311
+ #: inc/views/admin/metaboxes/social-metabox.php:149
2312
  msgid "Social Image Fallback URL"
2313
  msgstr ""
2314
 
2315
+ #: inc/views/admin/metaboxes/social-metabox.php:150
2316
  msgid "When no image is available from the page or term, this fallback image will be used instead."
2317
  msgstr ""
2318
 
2319
+ #: inc/views/admin/metaboxes/social-metabox.php:165
2320
  msgid "Theme Color Settings"
2321
  msgstr ""
2322
 
2323
+ #: inc/views/admin/metaboxes/social-metabox.php:166
2324
  msgid "Discord styles embeds with the theme color. The theme color can also affect the tab-color in some browsers."
2325
  msgstr ""
2326
 
2327
+ #: inc/views/admin/metaboxes/social-metabox.php:170
2328
  msgid "Theme Color"
2329
  msgstr ""
2330
 
2331
+ #: inc/views/admin/metaboxes/social-metabox.php:178
2332
  msgid "Site Shortlink Settings"
2333
  msgstr ""
2334
 
2335
+ #: inc/views/admin/metaboxes/social-metabox.php:179
2336
  msgid "The shortlink tag can be manually used for microblogging services like Twitter, but it has no SEO value whatsoever."
2337
  msgstr ""
2338
 
2339
+ #: inc/views/admin/metaboxes/social-metabox.php:184
2340
  msgid "Output shortlink tag?"
2341
  msgstr ""
2342
 
2343
+ #: inc/views/admin/metaboxes/social-metabox.php:192
2344
+ #: inc/views/profile/author.php:16
2345
+ msgctxt "Example Facebook Personal URL"
2346
+ msgid "https://www.facebook.com/YourPersonalProfile"
2347
+ msgstr ""
2348
+
2349
+ #: inc/views/admin/metaboxes/social-metabox.php:195
2350
  msgctxt "Example Facebook Business URL"
2351
  msgid "https://www.facebook.com/YourBusinessProfile"
2352
  msgstr ""
2353
 
2354
+ #: inc/views/admin/metaboxes/social-metabox.php:200
2355
  msgid "Facebook Integration Settings"
2356
  msgstr ""
2357
 
2358
+ #: inc/views/admin/metaboxes/social-metabox.php:201
2359
  msgid "Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options."
2360
  msgstr ""
2361
 
2362
+ #: inc/views/admin/metaboxes/social-metabox.php:202
2363
  msgid "When these options are filled in, Facebook might link the Facebook profile to be followed and liked when your post or page is shared."
2364
  msgstr ""
2365
 
2366
+ #: inc/views/admin/metaboxes/social-metabox.php:208
2367
  msgid "Facebook App ID"
2368
  msgstr ""
2369
 
2370
+ #: inc/views/admin/metaboxes/social-metabox.php:212
2371
  msgid "Get Facebook App ID."
2372
  msgstr ""
2373
 
2374
+ #: inc/views/admin/metaboxes/social-metabox.php:224
2375
  msgid "Facebook Publisher page"
2376
  msgstr ""
2377
 
2378
+ #: inc/views/admin/metaboxes/social-metabox.php:228
2379
  msgid "Only Facebook Business Pages are accepted."
2380
  msgstr ""
2381
 
2382
+ #: inc/views/admin/metaboxes/social-metabox.php:240
2383
  msgid "Facebook Author Fallback Page"
2384
  msgstr ""
2385
 
2386
+ #: inc/views/admin/metaboxes/social-metabox.php:244
2387
  msgid "Your Facebook profile."
2388
  msgstr ""
2389
 
2390
+ #: inc/views/admin/metaboxes/social-metabox.php:250
2391
+ #: inc/views/admin/metaboxes/social-metabox.php:341
2392
  msgid "Authors can override this option on their profile page."
2393
  msgstr ""
2394
 
2395
+ #: inc/views/admin/metaboxes/social-metabox.php:259
2396
  msgctxt "Twitter @username"
2397
  msgid "@your-site-username"
2398
  msgstr ""
2399
 
2400
+ #: inc/views/admin/metaboxes/social-metabox.php:262
2401
+ #: inc/views/profile/author.php:23
2402
+ msgctxt "Twitter @username"
2403
+ msgid "@your-personal-username"
2404
+ msgstr ""
2405
+
2406
+ #: inc/views/admin/metaboxes/social-metabox.php:266
2407
  msgid "Twitter Integration Settings"
2408
  msgstr ""
2409
 
2410
+ #: inc/views/admin/metaboxes/social-metabox.php:267
2411
  msgid "Twitter post sharing works mostly through Twitter Cards, and may fall back to use Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options."
2412
  msgstr ""
2413
 
2414
+ #: inc/views/admin/metaboxes/social-metabox.php:273
2415
  msgid "Twitter Card Type"
2416
  msgstr ""
2417
 
2418
+ #: inc/views/admin/metaboxes/social-metabox.php:276
2419
  msgid "The Twitter Card type may have the image highlighted, either small at the side or large above."
2420
  msgstr ""
2421
 
2422
+ #: inc/views/admin/metaboxes/social-metabox.php:292
2423
  msgid "Learn more about this card."
2424
  msgstr ""
2425
 
2426
+ #: inc/views/admin/metaboxes/social-metabox.php:307
2427
  msgid "Card and Content Attribution"
2428
  msgstr ""
2429
 
2430
+ #: inc/views/admin/metaboxes/social-metabox.php:309
2431
  msgid "Twitter claims users will be able to follow and view the profiles of attributed accounts directly from the card when these fields are filled in."
2432
  msgstr ""
2433
 
2434
+ #: inc/views/admin/metaboxes/social-metabox.php:310
2435
  msgid "However, for now, these fields seem to have no discernible effect."
2436
  msgstr ""
2437
 
2438
+ #: inc/views/admin/metaboxes/social-metabox.php:315
2439
  msgid "Website Twitter Profile"
2440
  msgstr ""
2441
 
2442
+ #: inc/views/admin/metaboxes/social-metabox.php:319
2443
+ #: inc/views/admin/metaboxes/social-metabox.php:335
2444
  msgid "Find your @username."
2445
  msgstr ""
2446
 
2447
+ #: inc/views/admin/metaboxes/social-metabox.php:331
2448
  msgid "Twitter Author Fallback Profile"
2449
  msgstr ""
2450
 
2451
+ #: inc/views/admin/metaboxes/social-metabox.php:349
2452
  msgid "oEmbed Settings"
2453
  msgstr ""
2454
 
2455
+ #: inc/views/admin/metaboxes/social-metabox.php:350
2456
  msgid "Some social sharing services and clients, like WordPress, LinkedIn, and Discord, obtain the linked page information via oEmbed."
2457
  msgstr ""
2458
 
2459
+ #: inc/views/admin/metaboxes/social-metabox.php:359
2460
  msgid "Use Open Graph title?"
2461
  msgstr ""
2462
 
2463
+ #: inc/views/admin/metaboxes/social-metabox.php:360
2464
  msgid "Check this option if you want to replace page titles with Open Graph titles in embeds."
2465
  msgstr ""
2466
 
2467
+ #: inc/views/admin/metaboxes/social-metabox.php:365
2468
  msgid "Only custom social images that are selected via the Media Library are considered."
2469
  msgstr ""
2470
 
2471
+ #: inc/views/admin/metaboxes/social-metabox.php:372
2472
  msgid "Use social image?"
2473
  msgstr ""
2474
 
2475
+ #: inc/views/admin/metaboxes/social-metabox.php:373
2476
  msgid "LinkedIn displays the post's featured image in embeds. Check this option if you want to replace it with the social image."
2477
  msgstr ""
2478
 
2479
+ #: inc/views/admin/metaboxes/social-metabox.php:381
2480
  msgid "Remove author name?"
2481
  msgstr ""
2482
 
2483
+ #: inc/views/admin/metaboxes/social-metabox.php:382
2484
  msgid "Discord shows the page author's name above the sharing embed. Check this option if you find this undesirable."
2485
  msgstr ""
2486
 
2487
+ #: inc/views/admin/metaboxes/social-metabox.php:389
2488
  msgid "Posts"
2489
  msgstr ""
2490
 
2491
+ #: inc/views/admin/metaboxes/social-metabox.php:391
2492
  msgid "Post Date Settings"
2493
  msgstr ""
2494
 
2495
+ #: inc/views/admin/metaboxes/social-metabox.php:392
2496
  msgid "Some social sites output the shared post's publishing and modified data in the sharing snippet."
2497
  msgstr ""
2498
 
2499
  #. translators: the backticks are Markdown! Preserve them as-is!
2500
+ #: inc/views/admin/metaboxes/social-metabox.php:403
2501
  msgid "Add `article:published_time` to posts?"
2502
  msgstr ""
2503
 
2504
  #. translators: the backticks are Markdown! Preserve them as-is!
2505
+ #: inc/views/admin/metaboxes/social-metabox.php:412
2506
  msgid "Add `article:modified_time` to posts?"
2507
  msgstr ""
2508
 
2509
+ #: inc/views/admin/metaboxes/title-metabox.php:32
2510
  msgid "Example Post"
2511
  msgstr ""
2512
 
2513
+ #: inc/views/admin/metaboxes/title-metabox.php:36
2514
  msgid "Example Category"
2515
  msgstr ""
2516
 
2517
+ #: inc/views/admin/metaboxes/title-metabox.php:50
2518
+ #: inc/views/admin/metaboxes/title-metabox.php:187
2519
  msgid "Automated Title Settings"
2520
  msgstr ""
2521
 
2523
  msgid "The page title is prominently shown within the browser tab as well as within the search engine results pages."
2524
  msgstr ""
2525
 
2526
+ #: inc/views/admin/metaboxes/title-metabox.php:53
2527
  msgid "Example Page Title Output"
2528
  msgstr ""
2529
 
2545
  msgid "Prefixes"
2546
  msgstr ""
2547
 
2548
+ #: inc/views/admin/metaboxes/title-metabox.php:161
2549
  msgid "Title Separator"
2550
  msgstr ""
2551
 
2552
+ #: inc/views/admin/metaboxes/title-metabox.php:163
2553
  msgid "If the title consists of multiple parts, then the separator will go in-between them."
2554
  msgstr ""
2555
 
2556
+ #: inc/views/admin/metaboxes/title-metabox.php:188
2557
  msgid "A title is generated for every page."
2558
  msgstr ""
2559
 
2560
+ #: inc/views/admin/metaboxes/title-metabox.php:189
2561
  msgid "Some titles may have HTML tags inserted by the author for styling."
2562
  msgstr ""
2563
 
2564
  #. translators: %s = HTML tag example
2565
+ #: inc/views/admin/metaboxes/title-metabox.php:194
2566
  msgid "This strips HTML tags, like %s, from the title. Disable this option to display generated HTML tags as plain text in meta titles."
2567
  msgstr ""
2568
 
2569
+ #: inc/views/admin/metaboxes/title-metabox.php:203
2570
  msgid "Strip HTML tags from generated titles?"
2571
  msgstr ""
2572
 
2573
+ #: inc/views/admin/metaboxes/title-metabox.php:209
2574
  msgid "Tip: It is a bad practice to style page titles with HTML as inconsistent behavior might occur."
2575
  msgstr ""
2576
 
2577
+ #: inc/views/admin/metaboxes/title-metabox.php:216
2578
  msgid "This option does not affect the homepage; it uses a different one."
2579
  msgstr ""
2580
 
2581
+ #: inc/views/admin/metaboxes/title-metabox.php:220
2582
  msgid "Site Title Location"
2583
  msgstr ""
2584
 
2585
+ #: inc/views/admin/metaboxes/title-metabox.php:248
2586
  msgid "Site Title"
2587
  msgstr ""
2588
 
2589
+ #: inc/views/admin/metaboxes/title-metabox.php:252
2590
  msgid "Always brand your titles. Search engines may ignore your titles with this feature enabled."
2591
  msgstr ""
2592
 
2593
+ #: inc/views/admin/metaboxes/title-metabox.php:260
2594
  msgid "Remove site title from the title?"
2595
  msgstr ""
2596
 
2597
+ #: inc/views/admin/metaboxes/title-metabox.php:268
2598
  msgid "Note: Only use this option if you are aware of its SEO effects."
2599
  msgstr ""
2600
 
2601
+ #: inc/views/admin/metaboxes/title-metabox.php:274
2602
  msgid "Title Prefix Options"
2603
  msgstr ""
2604
 
2605
+ #: inc/views/admin/metaboxes/title-metabox.php:275
2606
  msgid "For archives, a descriptive prefix may be added to generated titles."
2607
  msgstr ""
2608
 
2609
+ #: inc/views/admin/metaboxes/title-metabox.php:280
2610
  msgid "Archive Title Prefixes"
2611
  msgstr ""
2612
 
2613
+ #: inc/views/admin/metaboxes/title-metabox.php:284
2614
  msgid "The prefix helps visitors and search engines determine what kind of page they're visiting."
2615
  msgstr ""
2616
 
2617
+ #: inc/views/admin/metaboxes/title-metabox.php:291
2618
  msgid "Remove term type prefixes from generated archive titles?"
2619
  msgstr ""
2620
 
2621
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:30
2622
  msgid "Google Search Console Verification Code"
2623
  msgstr ""
2624
 
2625
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:32
2626
  msgid "Get the Google verification code."
2627
  msgstr ""
2628
 
2629
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:40
2630
  msgid "Bing Webmaster Verification Code"
2631
  msgstr ""
2632
 
2633
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:42
2634
  msgid "Get the Bing verification code."
2635
  msgstr ""
2636
 
2637
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:50
2638
  msgid "Yandex Webmaster Verification Code"
2639
  msgstr ""
2640
 
2641
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:52
2642
  msgid "Get the Yandex verification code."
2643
  msgstr ""
2644
 
2645
  #. translators: literal translation from '百度搜索资源平台'-Code
2646
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:61
2647
  msgid "Baidu Search Resource Platform Code"
2648
  msgstr ""
2649
 
2650
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:63
2651
  msgid "Get the Baidu verification code."
2652
  msgstr ""
2653
 
2654
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:71
2655
  msgid "Pinterest Analytics Verification Code"
2656
  msgstr ""
2657
 
2658
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:73
2659
  msgid "Get the Pinterest verification code."
2660
  msgstr ""
2661
 
2662
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:81
2663
  msgid "Webmaster Integration Settings"
2664
  msgstr ""
2665
 
2666
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:82
2667
  msgid "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes."
2668
  msgstr ""
2669
 
2670
+ #: inc/views/admin/metaboxes/webmaster-metabox.php:83
2671
  msgid "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content."
2672
  msgstr ""
2673
 
2674
+ #: inc/views/admin/seo-settings-wrap.php:15
2675
  msgid "Are you sure you want to reset all SEO settings to their defaults?"
2676
  msgstr ""
2677
 
2678
+ #: inc/views/admin/seo-settings-wrap.php:18
2679
  msgid "Save Settings"
2680
  msgstr ""
2681
 
2682
+ #: inc/views/admin/seo-settings-wrap.php:25
2683
  msgid "Reset Settings"
2684
  msgstr ""
2685
 
2686
+ #: inc/views/edit/seo-settings-singular.php:41
2687
  msgid "Visibility"
2688
  msgstr ""
2689
 
2690
+ #: inc/views/edit/seo-settings-singular.php:70
2691
+ #: inc/views/edit/seo-settings-tt.php:103
2692
  msgid "Doing it Right"
2693
  msgstr ""
2694
 
2695
+ #: inc/views/edit/seo-settings-singular.php:170
2696
+ #: inc/views/edit/seo-settings-singular.php:177
2697
+ #: inc/views/edit/seo-settings-tt.php:156
2698
  msgid "Remove the site title?"
2699
  msgstr ""
2700
 
2701
+ #: inc/views/edit/seo-settings-singular.php:172
2702
  msgid "For the homepage, this option must be managed on the SEO Settings page."
2703
  msgstr ""
2704
 
2705
+ #: inc/views/edit/seo-settings-singular.php:179
2706
+ #: inc/views/edit/seo-settings-tt.php:158
2707
  msgid "Use this when you want to rearrange the title parts manually."
2708
  msgstr ""
2709
 
2710
+ #: inc/views/edit/seo-settings-singular.php:253
2711
+ #: inc/views/edit/seo-settings-tt.php:73
2712
+ #: inc/views/list/bulk-post.php:29
2713
+ #: inc/views/list/quick-post.php:29
2714
  msgid "Link following"
2715
  msgstr ""
2716
 
2717
+ #: inc/views/edit/seo-settings-singular.php:271
2718
+ #: inc/views/edit/seo-settings-tt.php:304
2719
+ #: inc/views/list/quick-post.php:97
2720
+ #: inc/views/list/quick-term.php:97
2721
  msgid "Canonical URL"
2722
  msgstr ""
2723
 
2724
+ #: inc/views/edit/seo-settings-singular.php:275
2725
+ #: inc/views/edit/seo-settings-tt.php:308
2726
  msgid "This urges search engines to go to the outputted URL."
2727
  msgstr ""
2728
 
2729
+ #: inc/views/edit/seo-settings-singular.php:296
2730
  msgid "These directives may urge robots not to display, follow links on, or create a cached copy of this page."
2731
  msgstr ""
2732
 
2733
+ #: inc/views/edit/seo-settings-singular.php:310
2734
  msgid "Note: A non-default selection here will overwrite the global homepage SEO settings."
2735
  msgstr ""
2736
 
2737
  #. translators: %s = default option value
2738
+ #: inc/views/edit/seo-settings-singular.php:338
2739
+ #: inc/views/edit/seo-settings-singular.php:346
2740
+ #: inc/views/edit/seo-settings-tt.php:341
2741
+ #: inc/views/edit/seo-settings-tt.php:350
2742
+ #: inc/views/list/quick-post.php:114
2743
+ #: inc/views/list/quick-term.php:114
2744
  msgid "Default (%s)"
2745
  msgstr ""
2746
 
2747
+ #: inc/views/edit/seo-settings-singular.php:369
2748
  msgid "Archive Settings"
2749
  msgstr ""
2750
 
2751
+ #: inc/views/edit/seo-settings-singular.php:378
2752
  msgid "Exclude this page from all search queries on this site."
2753
  msgstr ""
2754
 
2755
+ #: inc/views/edit/seo-settings-singular.php:387
2756
  msgid "Exclude this page from all archive queries on this site."
2757
  msgstr ""
2758
 
2759
+ #: inc/views/edit/seo-settings-singular.php:401
2760
+ #: inc/views/edit/seo-settings-tt.php:362
2761
+ #: inc/views/list/quick-post.php:127
2762
+ #: inc/views/list/quick-term.php:127
2763
  msgid "301 Redirect URL"
2764
  msgstr ""
2765
 
2766
+ #: inc/views/edit/seo-settings-singular.php:406
2767
+ #: inc/views/edit/seo-settings-tt.php:366
2768
  msgid "This will force visitors to go to another URL."
2769
  msgstr ""
2770
 
2771
+ #: inc/views/edit/seo-settings-tt.php:64
2772
  msgid "This tells search engines not to show this term in their search results."
2773
  msgstr ""
2774
 
2775
+ #: inc/views/edit/seo-settings-tt.php:77
2776
  msgid "This tells search engines not to follow links on this term."
2777
  msgstr ""
2778
 
2779
+ #: inc/views/edit/seo-settings-tt.php:90
2780
  msgid "This tells search engines not to save a cached copy of this term."
2781
  msgstr ""
2782
 
2783
+ #: inc/views/edit/seo-settings-tt.php:97
2784
+ #: inc/views/list/quick-post.php:57
2785
+ #: inc/views/list/quick-term.php:57
2786
  msgid "General SEO Settings"
2787
  msgstr ""
2788
 
2789
+ #: inc/views/edit/seo-settings-tt.php:202
2790
  msgid "Social SEO Settings"
2791
  msgstr ""
2792
 
2793
+ #: inc/views/edit/seo-settings-tt.php:297
2794
+ #: inc/views/list/bulk-post.php:57
2795
+ #: inc/views/list/quick-post.php:94
2796
+ #: inc/views/list/quick-term.php:94
2797
  msgid "Visibility SEO Settings"
2798
  msgstr ""
2799
 
2800
+ #: inc/views/edit/seo-settings-tt.php:325
2801
  msgid "These directives may urge robots not to display, follow links on, or create a cached copy of this term."
2802
  msgstr ""
2803
 
2804
+ #: inc/views/list/bulk-post.php:71
2805
  msgid "Default (unknown)"
2806
  msgstr ""
2807
 
2808
+ #: inc/views/profile/author.php:14
2809
+ msgid "Facebook profile page"
2810
+ msgstr ""
2811
+
2812
+ #: inc/views/profile/author.php:21
2813
+ msgid "Twitter profile name"
2814
  msgstr ""
2815
 
2816
  #: inc/views/profile/author.php:30
2817
+ msgid "Authorial Info"
2818
+ msgstr ""
2819
+
2820
+ #: inc/views/profile/author.php:47
2821
  msgid "This may be shown publicly."
2822
  msgstr ""
2823
 
2866
  msgid "The buttons below are for primary %s selection."
2867
  msgstr ""
2868
 
2869
+ #: inc/views/templates/settings/settings.php:19
2870
  msgid "This post type is excluded, so this option won't work."
2871
  msgstr ""
2872
 
2873
+ #: inc/views/templates/settings/settings.php:29
2874
  msgid "This taxonomy is excluded, so this option won't work."
2875
  msgstr ""
2876
 
2877
+ #: inc/views/templates/settings/settings.php:39
2878
  msgid "This taxonomy's post types are also excluded, so this option won't have any effect."
2879
  msgstr ""
2880
 
2881
+ #: inc/views/templates/settings/settings.php:49
2882
  msgid "The site title is already removed from meta titles, so this option only affects the homepage."
2883
  msgstr ""
2884
 
2885
+ #: inc/views/templates/settings/settings.php:59
2886
  msgid "This taxonomy inherited the state from the post type, so this option won't have any effect."
2887
  msgstr ""
lib/css/le.css CHANGED
@@ -51,7 +51,7 @@ body.rtl .tsf-inline-input {
51
  width: 40%;
52
  }
53
  #wpbody-content .inline-edit-row .tsf-inline-edit-col-normal {
54
- width: 20%;
55
  }
56
 
57
  @media screen and (max-width: 782px) {
51
  width: 40%;
52
  }
53
  #wpbody-content .inline-edit-row .tsf-inline-edit-col-normal {
54
+ width: 30%;
55
  }
56
 
57
  @media screen and (max-width: 782px) {
lib/css/le.min.css CHANGED
@@ -1 +1 @@
1
- .tsf-quick-edit-columns{width:100%;display:block;clear:both}.inline-edit-row .tsf-quick-edit-columns fieldset label span.title{width:8em}.tsf-inline-input{display:block;margin-left:8em}body.rtl .tsf-inline-input{margin-left:0;margin-right:8em}.tsf-inline-input input,.tsf-le-wide-complex-column input,.tsf-le-wide-complex-column textarea{width:100%}.tsf-le-wide-complex-column{display:block;width:100%;margin-bottom:1em;box-sizing:border-box}.inline-edit-row .tsf-quick-edit-columns fieldset .tsf-le-wide-complex-column label span.title{font-weight:600;float:none;width:100%;line-height:initial}.tsf-pad-input input,.tsf-pad-input textarea{margin-top:1em;margin-right:0;margin-left:0}#wpbody-content .inline-edit-row .tsf-inline-edit-col-wide{width:40%}#wpbody-content .inline-edit-row .tsf-inline-edit-col-normal{width:20%}@media screen and (max-width:782px){.tsf-inline-input{margin-left:0}#wpbody-content .inline-edit-row .tsf-inline-edit-col-normal,#wpbody-content .inline-edit-row .tsf-inline-edit-col-wide{width:100%}}
1
+ .tsf-quick-edit-columns{width:100%;display:block;clear:both}.inline-edit-row .tsf-quick-edit-columns fieldset label span.title{width:8em}.tsf-inline-input{display:block;margin-left:8em}body.rtl .tsf-inline-input{margin-left:0;margin-right:8em}.tsf-inline-input input,.tsf-le-wide-complex-column input,.tsf-le-wide-complex-column textarea{width:100%}.tsf-le-wide-complex-column{display:block;width:100%;margin-bottom:1em;box-sizing:border-box}.inline-edit-row .tsf-quick-edit-columns fieldset .tsf-le-wide-complex-column label span.title{font-weight:600;float:none;width:100%;line-height:initial}.tsf-pad-input input,.tsf-pad-input textarea{margin-top:1em;margin-right:0;margin-left:0}#wpbody-content .inline-edit-row .tsf-inline-edit-col-wide{width:40%}#wpbody-content .inline-edit-row .tsf-inline-edit-col-normal{width:30%}@media screen and (max-width:782px){.tsf-inline-input{margin-left:0}#wpbody-content .inline-edit-row .tsf-inline-edit-col-normal,#wpbody-content .inline-edit-row .tsf-inline-edit-col-wide{width:100%}}
lib/css/tsf.css CHANGED
@@ -334,7 +334,7 @@ input.tsf-disabled {
334
  margin-left: 3px;
335
  }
336
 
337
- body.rtl .tsf-counter .tsf-aja {
338
  margin-left: 0;
339
  margin-right: 3px;
340
  }
334
  margin-left: 3px;
335
  }
336
 
337
+ body.rtl .tsf-counter .tsf-ajax {
338
  margin-left: 0;
339
  margin-right: 3px;
340
  }
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-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)}}@keyframes tsf-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes tsf-fade-out{0%{opacity:1}100%{opacity:0}}@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-ajax{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)}}@keyframes tsf-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes tsf-fade-out{0%{opacity:1}100%{opacity:0}}@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/js/ays.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/c.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -311,7 +311,7 @@ window.tsfC = function( $ ) {
311
  url: ajaxurl,
312
  datatype: 'json',
313
  data: {
314
- action: 'the_seo_framework_update_counter',
315
  nonce: tsf.l10n.nonces.edit_posts,
316
  val: counterType,
317
  },
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
311
  url: ajaxurl,
312
  datatype: 'json',
313
  data: {
314
+ action: 'tsf_update_counter',
315
  nonce: tsf.l10n.nonces.edit_posts,
316
  val: counterType,
317
  },
lib/js/c.min.js CHANGED
@@ -1 +1 @@
1
- 'use strict';window.tsfC=function(a){const b="undefined"!=typeof tsfCL10n&&tsfCL10n;let c=+(b.counterType||0);const d=()=>c,e={0:"tsf-counter-zero",1:"tsf-counter-one",2:"tsf-counter-two",3:"tsf-counter-three"},f=a=>{let d=a.e,f=tsf.decodeEntities(a.text),g=b.guidelines[a.field][a.type].chars,h=tsf.getStringLength(f),i="",j="",k={bad:"tsf-count-bad",okay:"tsf-count-okay",good:"tsf-count-good",unknown:"tsf-count-unknown"};switch(h?h<g.lower?(i=k.bad,j=b.i18n.guidelines.short.farTooShort):h<g.goodLower?(i=k.okay,j=b.i18n.guidelines.short.tooShort):h>g.upper?(i=k.bad,j=b.i18n.guidelines.short.farTooLong):h>g.goodUpper?(i=k.okay,j=b.i18n.guidelines.short.tooLong):(i=k.good,j=b.i18n.guidelines.short.good):(i=k.unknown,j=b.i18n.guidelines.short.empty),c){case 3:j=h.toString()+" - "+j;break;case 2:break;case 1:default:j=h.toString();}for(let b in d.innerHTML=j,k)d.classList.remove(k[b]);for(let b in e)d.classList.remove(e[b]);d.classList.add(i),d.classList.add(e[c])},g=()=>{window.dispatchEvent(new CustomEvent("tsf-counter-updated"))},h=a=>{a&&++c,3<c&&(c=0),g()},i=()=>{h(!0);let b=".tsf-counter-wrap .tsf-ajax",d=0;tsf.resetAjaxLoader(b),tsf.setAjaxLoader(b);let e={method:"POST",url:ajaxurl,datatype:"json",data:{action:"the_seo_framework_update_counter",nonce:tsf.l10n.nonces.edit_posts,val:c},async:!0,success:a=>{switch(a=tsf.convertJSONResponse(a),"success"===a.type&&(d=1),d){case 0:tsf.unsetAjaxLoader(b,!1);break;case 1:tsf.unsetAjaxLoader(b,!0);break;default:tsf.resetAjaxLoader(b);}},error:()=>{tsf.unsetAjaxLoader(b,!1)}};a.ajax(e)},j=()=>document.querySelectorAll(".tsf-counter").forEach(a=>a.addEventListener("click",i)),k=()=>{j()};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",k)}},{updatePixelCounter:a=>{let c=a.e,d=tsf.decodeEntities(a.text),e=b.guidelines[a.field][a.type].pixels,f=c.parentElement;if(!f)return;let g=f.querySelector(".tsf-pixel-counter-bar"),h=f.querySelector(".tsf-pixel-counter-shadow");if(!g||!h)return;h.innerHTML=tsf.escapeString(d);let i=h.offsetWidth,j="",k="",l="",m={bad:"tsf-pixel-counter-bad",okay:"tsf-pixel-counter-okay",good:"tsf-pixel-counter-good",unknown:"tsf-pixel-counter-unknown"};k=100*(i/e.goodUpper)+"%",i?i<e.lower?(j=m.bad,l=b.i18n.guidelines.long.farTooShort):i<e.goodLower?(j=m.okay,l=b.i18n.guidelines.long.tooShort):i>e.upper?(k=100*(e.upper/(i+2*(i-e.upper)/3))+"%",j=m.bad,l=b.i18n.guidelines.long.farTooLong):i>e.goodUpper?(j=m.okay,l=b.i18n.guidelines.long.tooLong,k="100%"):(j=m.good,l=b.i18n.guidelines.long.good):(j=m.unknown,k="100%",l=b.i18n.guidelines.long.empty);let n,o=g.querySelector(".tsf-pixel-counter-fluid");n=b.i18n.pixelsUsed.replace(/%1\$d/g,i),n=n.replace(/%2\$d/g,e.goodUpper),n+="<br>"+l,g.classList.remove(...Object.values(m)),g.classList.add(j),o.style.width=k,g.dataset.desc=n,g.setAttribute("aria-label",tsf.escapeString(n.replace(/(<([^>]+)?>?)/ig," "))),tsfTT.triggerUpdate(g)},updateCharacterCounter:f,triggerCounterUpdate:g,resetCounterListener:j,getCounterType:d},{counterClasses:e,l10n:b})}(jQuery),window.tsfC.load();
1
+ 'use strict';window.tsfC=function(a){const b="undefined"!=typeof tsfCL10n&&tsfCL10n;let c=+(b.counterType||0);const d=()=>c,e={0:"tsf-counter-zero",1:"tsf-counter-one",2:"tsf-counter-two",3:"tsf-counter-three"},f=a=>{let d=a.e,f=tsf.decodeEntities(a.text),g=b.guidelines[a.field][a.type].chars,h=tsf.getStringLength(f),i="",j="",k={bad:"tsf-count-bad",okay:"tsf-count-okay",good:"tsf-count-good",unknown:"tsf-count-unknown"};switch(h?h<g.lower?(i=k.bad,j=b.i18n.guidelines.short.farTooShort):h<g.goodLower?(i=k.okay,j=b.i18n.guidelines.short.tooShort):h>g.upper?(i=k.bad,j=b.i18n.guidelines.short.farTooLong):h>g.goodUpper?(i=k.okay,j=b.i18n.guidelines.short.tooLong):(i=k.good,j=b.i18n.guidelines.short.good):(i=k.unknown,j=b.i18n.guidelines.short.empty),c){case 3:j=h.toString()+" - "+j;break;case 2:break;case 1:default:j=h.toString();}for(let b in d.innerHTML=j,k)d.classList.remove(k[b]);for(let b in e)d.classList.remove(e[b]);d.classList.add(i),d.classList.add(e[c])},g=()=>{window.dispatchEvent(new CustomEvent("tsf-counter-updated"))},h=a=>{a&&++c,3<c&&(c=0),g()},i=()=>{h(!0);let b=".tsf-counter-wrap .tsf-ajax",d=0;tsf.resetAjaxLoader(b),tsf.setAjaxLoader(b);let e={method:"POST",url:ajaxurl,datatype:"json",data:{action:"tsf_update_counter",nonce:tsf.l10n.nonces.edit_posts,val:c},async:!0,success:a=>{switch(a=tsf.convertJSONResponse(a),"success"===a.type&&(d=1),d){case 0:tsf.unsetAjaxLoader(b,!1);break;case 1:tsf.unsetAjaxLoader(b,!0);break;default:tsf.resetAjaxLoader(b);}},error:()=>{tsf.unsetAjaxLoader(b,!1)}};a.ajax(e)},j=()=>document.querySelectorAll(".tsf-counter").forEach(a=>a.addEventListener("click",i)),k=()=>{j()};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",k)}},{updatePixelCounter:a=>{let c=a.e,d=tsf.decodeEntities(a.text),e=b.guidelines[a.field][a.type].pixels,f=c.parentElement;if(!f)return;let g=f.querySelector(".tsf-pixel-counter-bar"),h=f.querySelector(".tsf-pixel-counter-shadow");if(!g||!h)return;h.innerHTML=tsf.escapeString(d);let i=h.offsetWidth,j="",k="",l="",m={bad:"tsf-pixel-counter-bad",okay:"tsf-pixel-counter-okay",good:"tsf-pixel-counter-good",unknown:"tsf-pixel-counter-unknown"};k=100*(i/e.goodUpper)+"%",i?i<e.lower?(j=m.bad,l=b.i18n.guidelines.long.farTooShort):i<e.goodLower?(j=m.okay,l=b.i18n.guidelines.long.tooShort):i>e.upper?(k=100*(e.upper/(i+2*(i-e.upper)/3))+"%",j=m.bad,l=b.i18n.guidelines.long.farTooLong):i>e.goodUpper?(j=m.okay,l=b.i18n.guidelines.long.tooLong,k="100%"):(j=m.good,l=b.i18n.guidelines.long.good):(j=m.unknown,k="100%",l=b.i18n.guidelines.long.empty);let n,o=g.querySelector(".tsf-pixel-counter-fluid");n=b.i18n.pixelsUsed.replace(/%1\$d/g,i),n=n.replace(/%2\$d/g,e.goodUpper),n+="<br>"+l,g.classList.remove(...Object.values(m)),g.classList.add(j),o.style.width=k,g.dataset.desc=n,g.setAttribute("aria-label",tsf.escapeString(n.replace(/(<([^>]+)?>?)/ig," "))),tsfTT.triggerUpdate(g)},updateCharacterCounter:f,triggerCounterUpdate:g,resetCounterListener:j,getCounterType:d},{counterClasses:e,l10n:b})}(jQuery),window.tsfC.load();
lib/js/description.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/gbc.js CHANGED
@@ -11,7 +11,7 @@
11
 
12
  /**
13
  * The SEO Framework plugin
14
- * Copyright (C) 2019 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
15
  *
16
  * This program is free software: you can redistribute it and/or modify
17
  * it under the terms of the GNU General Public License version 3 as published
11
 
12
  /**
13
  * The SEO Framework plugin
14
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
15
  *
16
  * This program is free software: you can redistribute it and/or modify
17
  * it under the terms of the GNU General Public License version 3 as published
lib/js/le.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -128,6 +128,27 @@ window.tsfLe = function( $ ) {
128
  */
129
  const _setInlineTermValues = id => _setInlinePostValues( id );
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  /**
132
  * Sets private/protected visibility state.
133
  *
@@ -174,6 +195,7 @@ window.tsfLe = function( $ ) {
174
  * Sets default title state.
175
  *
176
  * @since 4.1.0
 
177
  * @access private
178
  *
179
  * @function
@@ -249,13 +271,16 @@ window.tsfLe = function( $ ) {
249
  element.addEventListener( 'click', _setTitleVisibilityPrefix );
250
  element.dispatchEvent( new CustomEvent( 'click' ) );
251
  } );
 
252
  // Post titles. TODO Should we use class "ptitle" instead?
253
  inlineEdit.querySelectorAll( '[name=post_title]' ).forEach( element => {
254
  element.dataset.tsfTitleId = titleId;
255
  // tsfTitle shouldn't be aware of this--since we remove the prefix on-input.
256
  // element.dataset.termPrefix = termPrefix;
257
- element.addEventListener( 'input', _setDefaultTitle );
258
- element.dispatchEvent( new CustomEvent( 'input' ) );
 
 
259
  } );
260
  // Term titles. TODO Should we use class "ptitle" instead?
261
  inlineEdit.querySelectorAll( '[name=name]' ).forEach( element => {
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
128
  */
129
  const _setInlineTermValues = id => _setInlinePostValues( id );
130
 
131
+ /**
132
+ * Returns the postdata element, or an empty object.
133
+ *
134
+ * @since 4.1.4
135
+ * @access private
136
+ *
137
+ * @function
138
+ * @param {string} id
139
+ * @return {object}
140
+ */
141
+ const _getPostData = id => {
142
+ let dataElement = document.getElementById( 'tsfLePostData[' + id + ']' ),
143
+ data = void 0;
144
+
145
+ try {
146
+ data = JSON.parse( dataElement.dataset.lePostData ) || void 0;
147
+ } catch( e ) {}
148
+
149
+ return data || {};
150
+ }
151
+
152
  /**
153
  * Sets private/protected visibility state.
154
  *
195
  * Sets default title state.
196
  *
197
  * @since 4.1.0
198
+ * @since 4.1.4 Is now considerate of additionsForcedDisabled/additionsForceEnabled
199
  * @access private
200
  *
201
  * @function
271
  element.addEventListener( 'click', _setTitleVisibilityPrefix );
272
  element.dispatchEvent( new CustomEvent( 'click' ) );
273
  } );
274
+ //= The homepage listens to a static preset value. Update all others.
275
  // Post titles. TODO Should we use class "ptitle" instead?
276
  inlineEdit.querySelectorAll( '[name=post_title]' ).forEach( element => {
277
  element.dataset.tsfTitleId = titleId;
278
  // tsfTitle shouldn't be aware of this--since we remove the prefix on-input.
279
  // element.dataset.termPrefix = termPrefix;
280
+ if ( ! _getPostData( id ).isFront ) {
281
+ element.addEventListener( 'input', _setDefaultTitle );
282
+ element.dispatchEvent( new CustomEvent( 'input' ) );
283
+ }
284
  } );
285
  // Term titles. TODO Should we use class "ptitle" instead?
286
  inlineEdit.querySelectorAll( '[name=name]' ).forEach( element => {
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,c=document.getElementById("tsfLePostData["+a+"]");try{b=JSON.parse(c.dataset.lePostData)||void 0}catch(a){}return b||{}},h=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)},i=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)},j=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",h),a.dispatchEvent(new CustomEvent("input"))}),f.querySelectorAll("[name=keep_private]").forEach(a=>{a.dataset.tsfTitleId=d,a.addEventListener("click",h),a.dispatchEvent(new CustomEvent("click"))}),f.querySelectorAll("[name=post_title]").forEach(b=>{b.dataset.tsfTitleId=d,g(a).isFront||(b.addEventListener("input",i),b.dispatchEvent(new CustomEvent("input")))}),f.querySelectorAll("[name=name]").forEach(a=>{a.dataset.tsfTitleId=d,a.dataset.termPrefix=b.termPrefix||"",a.addEventListener("input",i),a.dispatchEvent(new CustomEvent("input"))})),tsfTT.triggerReset()},k=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()}},l=()=>{document.addEventListener("tsfLeDispatchUpdate",c),document.addEventListener("tsfLeUpdated",d)},m=()=>{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),j(b),k(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),j(b),k(b),"tsfC"in window&&tsfC.resetCounterListener(),c)}))};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",l),document.body.addEventListener("tsf-onload",m)}},{},{l10n:a})}(jQuery),window.tsfLe.load();
lib/js/media.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2018-2019 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
@@ -242,6 +242,7 @@ window.tsfMedia = function( $ ) {
242
 
243
  if ( animate ) {
244
  // What if we don't do this? Mind blown.
 
245
  $( button ).css( 'opacity', 0 ).animate(
246
  { opacity: 1 },
247
  { queue: true, duration: 1000 }
@@ -282,6 +283,7 @@ window.tsfMedia = function( $ ) {
282
  inputRemove.disabled = true;
283
  inputRemove.classList.add( 'disabled' );
284
 
 
285
  $( inputRemove ).fadeOut( 250, () => {
286
  inputRemove.remove();
287
 
@@ -429,7 +431,7 @@ window.tsfMedia = function( $ ) {
429
  }
430
 
431
  return wp.ajax.post(
432
- 'tsf-crop-image',
433
  {
434
  nonce: l10n.nonce,
435
  id: attachment.get( 'id' ),
@@ -597,9 +599,9 @@ window.tsfMedia = function( $ ) {
597
  const _checkImageEditorInput = () => {
598
 
599
  document.querySelectorAll( '.tsf-set-image-button' ).forEach( element => {
600
- const imageId = element.dataset.inputId || '',
601
- inputId = imageId && document.getElementById( `${imageId}-id` ),
602
- inputUrl = imageId && document.getElementById( `${imageId}-url` );
603
 
604
  if ( inputId && inputId.value > 0 ) {
605
  if ( inputUrl ) inputUrl.readOnly = true;
@@ -661,6 +663,9 @@ window.tsfMedia = function( $ ) {
661
  el.disabled = false;
662
  el.classList.remove( 'tsf-enable-media-if-js' );
663
  } );
 
 
 
664
  }
665
 
666
  let _debounceActionReset = void 0;
@@ -680,88 +685,92 @@ window.tsfMedia = function( $ ) {
680
  _debounceActionReset = setTimeout( _setupImageEditorActions, 500 );
681
  }
682
 
 
683
  /**
684
- * Sets up image input tooltip handler.
685
  *
686
- * @since 4.0.0
687
- * @access private
688
  *
 
689
  * @function
690
  * @return {(undefined|null)}
691
  */
692
- const _prepareTooltip = () => {
693
-
694
- let _updateToolTipBuffer = {};
695
- /**
696
- * Updates the input's parentNode tooltip input.
697
- *
698
- * @param {Event} event
699
- */
700
- const _updateToolTip = event => {
701
-
702
- const imageId = _inferImageId( event.target.id || '' ),
703
- preview = imageId && document.getElementById( `${imageId}-preview` );
704
-
705
- if ( ! preview ) return;
706
 
707
- ( imageId in _updateToolTipBuffer ) && clearTimeout( _updateToolTipBuffer[ imageId ] );
708
 
709
- let pageLoaded = preview.dataset.tsfLoaded || false;
710
- preview.dataset.tsfLoaded = 1;
711
 
712
- let src = event.target.value || event.target.placeholder || '';
 
713
 
714
- _updateToolTipBuffer[ imageId ] = setTimeout(
715
- () => {
716
 
717
- // The maxWidth is defined at tsfTT.doTooltip(), where the tooltip has 12px padding.
718
- // Remove 1 to account for floating point errors.
719
- // let maxWidth = 250 - ( 12 * 2 ) - 1 + 'px'; // this is just 225px...
 
 
720
 
721
- let // style = `max-width:${maxWidth};max-height:${maxWidth};min-width:60px;min-height:60px;border-radius:3px;display:block;`;
722
- style = `max-width:225px;max-height:225px;min-width:60px;min-height:60px;border-radius:3px;display:block;`;
723
- // We set min-height and width as that will prevent jumping. Also, those are the absolute-minimum for sharing/schema images.
724
-
725
- if ( ! src.length ) {
726
- if ( pageLoaded ) {
727
- $( preview ).not( ':hidden' ).fadeOut( 250 );
728
- } else {
729
- $( preview ).hide();
730
- }
731
- return;
732
- }
733
-
734
- /**
735
- * XSS tests that passed (i.e., no issue), because the --browser-- must (and does) block these:
736
- * - data:text/html;base64,amF2YXNjcmlwdDphbGVydCgnaGknKTs=
737
- * - svg loading with scripts attached (CORB blocks, good. Thank you for bringing attention, Meltdown & Spectr)
738
- *
739
- * CSRF should be blocked by the browser, as well. Otherwise, Authors and Editors are able to execute
740
- * these via the default WordPress editor, already.
741
- *
742
- * URLs that aren't trusted are also filtered via sanitization on save, using `the_seo_framework()->s_url_query()`.
743
- *
744
- * We are NOT creating a document node here, that's something we leave for the tooltip.
745
- */
746
- preview.dataset.desc = "<img src='" + tsf.escapeString( src ) + "' style=" + style + " />";
747
 
 
748
  if ( pageLoaded ) {
749
- $( preview ).not( ':visible' ).fadeIn( 250 );
 
750
  } else {
751
- $( preview ).show();
752
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
- // Preload image. The same security notes apply as above. Moreover, the Image object escapes:
755
- // ( new Image() ).src = '"/><script>alert(\'XSS\');</script>';
756
- ( new Image() ).src = src;
757
 
758
- tsfTT.triggerUpdate( preview );
759
- },
760
- // High timeout: Don't DoS the inputted URL, plus the delay is quite nice.
761
- // Also invoke instantly when removing, otherwise it lags behind the removal button's animation
762
- pageLoaded && src.length ? 500 : 0
763
- );
764
- }
 
 
 
 
 
 
 
 
 
 
 
765
 
766
  // Prepare tooltip updates.
767
  document.querySelectorAll( '.tsf-image-preview' ).forEach( el => {
@@ -799,12 +808,6 @@ window.tsfMedia = function( $ ) {
799
  load: () => {
800
  // Initialize image uploader button cache.
801
  document.body.addEventListener( 'tsf-ready', _setupImageEditorActions );
802
-
803
- // Determine image editor button input states.
804
- document.body.addEventListener( 'tsf-ready', _checkImageEditorInput );
805
-
806
- // Prepares image input tooltips.
807
- document.body.addEventListener( 'tsf-ready', _prepareTooltip );
808
  }
809
  }, {
810
  resetImageEditorActions,
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2018 - 2021 Sybre Waaijer, CyberWire B.V. (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
242
 
243
  if ( animate ) {
244
  // What if we don't do this? Mind blown.
245
+ // TODO use tsf-fade-in CSS?
246
  $( button ).css( 'opacity', 0 ).animate(
247
  { opacity: 1 },
248
  { queue: true, duration: 1000 }
283
  inputRemove.disabled = true;
284
  inputRemove.classList.add( 'disabled' );
285
 
286
+ // TODO use tsf-fade-out CSS?
287
  $( inputRemove ).fadeOut( 250, () => {
288
  inputRemove.remove();
289
 
431
  }
432
 
433
  return wp.ajax.post(
434
+ 'tsf_crop_image',
435
  {
436
  nonce: l10n.nonce,
437
  id: attachment.get( 'id' ),
599
  const _checkImageEditorInput = () => {
600
 
601
  document.querySelectorAll( '.tsf-set-image-button' ).forEach( element => {
602
+ const imageId = element.dataset.inputId || '',
603
+ inputId = imageId && document.getElementById( `${imageId}-id` ),
604
+ inputUrl = imageId && document.getElementById( `${imageId}-url` );
605
 
606
  if ( inputId && inputId.value > 0 ) {
607
  if ( inputUrl ) inputUrl.readOnly = true;
663
  el.disabled = false;
664
  el.classList.remove( 'tsf-enable-media-if-js' );
665
  } );
666
+
667
+ _checkImageEditorInput(); // This fires a change event... is that desired?
668
+ _prepareTooltip();
669
  }
670
 
671
  let _debounceActionReset = void 0;
685
  _debounceActionReset = setTimeout( _setupImageEditorActions, 500 );
686
  }
687
 
688
+ let _updateToolTipBuffer = {};
689
  /**
690
+ * Updates the input's parentNode tooltip input.
691
  *
692
+ * @since 4.1.4
 
693
  *
694
+ * @param {Event} event
695
  * @function
696
  * @return {(undefined|null)}
697
  */
698
+ const _updateToolTip = event => {
699
+ const imageId = _inferImageId( event.target.id || '' ),
700
+ preview = imageId && document.getElementById( `${imageId}-preview` );
 
 
 
 
 
 
 
 
 
 
 
701
 
702
+ if ( ! preview ) return;
703
 
704
+ ( imageId in _updateToolTipBuffer ) && clearTimeout( _updateToolTipBuffer[ imageId ] );
 
705
 
706
+ let pageLoaded = preview.dataset.tsfLoaded || false;
707
+ preview.dataset.tsfLoaded = 1;
708
 
709
+ let src = event.target.value || event.target.placeholder || '';
 
710
 
711
+ _updateToolTipBuffer[ imageId ] = setTimeout(
712
+ () => {
713
+ // The maxWidth is defined at tsfTT.doTooltip(), where the tooltip has 12px padding.
714
+ // Remove 1 to account for floating point errors.
715
+ // let maxWidth = 250 - ( 12 * 2 ) - 1 + 'px'; // this is just 225px...
716
 
717
+ let // style = `max-width:${maxWidth};max-height:${maxWidth};min-width:60px;min-height:60px;border-radius:3px;display:block;`;
718
+ style = `max-width:225px;max-height:225px;min-width:60px;min-height:60px;border-radius:3px;display:block;`;
719
+ // We set min-height and width as that will prevent jumping. Also, those are the absolute-minimum for sharing/schema images.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
 
721
+ if ( ! src.length ) {
722
  if ( pageLoaded ) {
723
+ // TODO use tsf-fade-out CSS?
724
+ $( preview ).not( ':hidden' ).fadeOut( 250 );
725
  } else {
726
+ $( preview ).hide();
727
  }
728
+ return;
729
+ }
730
+
731
+ /**
732
+ * XSS tests that passed (i.e., no issue), because the --browser-- must (and does) block these:
733
+ * - data:text/html;base64,amF2YXNjcmlwdDphbGVydCgnaGknKTs=
734
+ * - svg loading with scripts attached (CORB blocks, good. Thank you for bringing attention, Meltdown & Spectr)
735
+ *
736
+ * CSRF should be blocked by the browser, as well. Otherwise, Authors and Editors are able to execute
737
+ * these via the default WordPress editor, already.
738
+ *
739
+ * URLs that aren't trusted are also filtered via sanitization on save, using `the_seo_framework()->s_url_query()`.
740
+ *
741
+ * We are NOT creating a document node here, that's something we leave for the tooltip.
742
+ */
743
+ preview.dataset.desc = "<img src='" + tsf.escapeString( src ) + "' style=" + style + " />";
744
+
745
+ if ( pageLoaded ) {
746
+ // TODO use tsf-fade-out CSS?
747
+ $( preview ).not( ':visible' ).fadeIn( 250 );
748
+ } else {
749
+ $( preview ).show();
750
+ }
751
 
752
+ // Preload image. The same security notes apply as above. Moreover, the Image object escapes:
753
+ // ( new Image() ).src = '"/><script>alert(\'XSS\');</script>';
754
+ ( new Image() ).src = src;
755
 
756
+ tsfTT.triggerUpdate( preview );
757
+ },
758
+ // High timeout: Don't DoS the inputted URL, plus the delay is quite nice.
759
+ // Also invoke instantly when removing, otherwise it lags behind the removal button's animation
760
+ pageLoaded && src.length ? 500 : 0
761
+ );
762
+ }
763
+
764
+ /**
765
+ * Sets up image input tooltip handler.
766
+ *
767
+ * @since 4.0.0
768
+ * @access private
769
+ *
770
+ * @function
771
+ * @return {(undefined|null)}
772
+ */
773
+ const _prepareTooltip = () => {
774
 
775
  // Prepare tooltip updates.
776
  document.querySelectorAll( '.tsf-image-preview' ).forEach( el => {
808
  load: () => {
809
  // Initialize image uploader button cache.
810
  document.body.addEventListener( 'tsf-ready', _setupImageEditorActions );
 
 
 
 
 
 
811
  }
812
  }, {
813
  resetImageEditorActions,
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;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();
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")}),k(),s()};let o;const p=()=>{clearTimeout(o),o=setTimeout(n,500)};let q={};const r=b=>{const c=t(b.target.id||""),d=c&&document.getElementById(`${c}-preview`);if(!d)return;c in q&&clearTimeout(q[c]);let e=d.dataset.tsfLoaded||!1;d.dataset.tsfLoaded=1;let f=b.target.value||b.target.placeholder||"";q[c]=setTimeout(()=>{return f.length?void(d.dataset.desc="<img src='"+tsf.escapeString(f)+"' style="+`max-width:225px;max-height:225px;min-width:60px;min-height:60px;border-radius:3px;display:block;`+" />",e?a(d).not(":visible").fadeIn(250):a(d).show(),new Image().src=f,tsfTT.triggerUpdate(d)):void(e?a(d).not(":hidden").fadeOut(250):a(d).hide())},e&&f.length?500:0)},s=()=>{document.querySelectorAll(".tsf-image-preview").forEach(a=>{const b=document.getElementById(`${a.dataset.for}-url`);b&&(b.addEventListener("input",r),b.addEventListener("change",r),b.dispatchEvent(new Event("change")))})},t=a=>a.replace(/-[a-z]+$/,"");return Object.assign({load:()=>{document.body.addEventListener("tsf-ready",n)}},{resetImageEditorActions:p},{l10n:b})}(jQuery),window.tsfMedia.load();
lib/js/post.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
@@ -212,6 +212,7 @@ window.tsfPost = function( $ ) {
212
  *
213
  * @since 4.0.0
214
  * @since 4.1.2 Changed name from _initCanonicalInput
 
215
  * @access private
216
  *
217
  * @function
@@ -219,12 +220,13 @@ window.tsfPost = function( $ ) {
219
  */
220
  const _initVisibilityListeners = () => {
221
 
 
 
 
222
  // Prefix with B because I don't trust using 'protected' (might become reserved).
223
  const BPROTECTED = 0b01,
224
  BNOINDEX = 0b10;
225
 
226
- const indexSelect = document.getElementById( 'autodescription_noindex' );
227
-
228
  let canonicalPhState = 0b00,
229
  canonicalUrl = '';
230
 
@@ -682,9 +684,11 @@ window.tsfPost = function( $ ) {
682
  tsfSocial.updateState( 'twDescPlaceholder', response.data.twdescription.trim() );
683
  }
684
 
685
- // Is this necessary? It's safer, though :)
686
- imageUrl.placeholder = tsf.decodeEntities( response.data.imageurl );
687
- imageUrl.dispatchEvent( new Event( 'change' ) );
 
 
688
 
689
  'tsfAys' in window && tsfAys.reset();
690
  }, fadeTime );
@@ -724,7 +728,7 @@ window.tsfPost = function( $ ) {
724
  url: ajaxurl,
725
  datatype: 'json',
726
  data: {
727
- action: 'the_seo_framework_update_post_data',
728
  nonce: tsf.l10n.nonces.edit_posts,
729
  post_id: l10n.states.id,
730
  get: getData,
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
212
  *
213
  * @since 4.0.0
214
  * @since 4.1.2 Changed name from _initCanonicalInput
215
+ * @since 4.1.4 Now no longer proceeds on absence of element ID 'autodescription_noindex'.
216
  * @access private
217
  *
218
  * @function
220
  */
221
  const _initVisibilityListeners = () => {
222
 
223
+ const indexSelect = document.getElementById( 'autodescription_noindex' );
224
+ if ( ! indexSelect ) return;
225
+
226
  // Prefix with B because I don't trust using 'protected' (might become reserved).
227
  const BPROTECTED = 0b01,
228
  BNOINDEX = 0b10;
229
 
 
 
230
  let canonicalPhState = 0b00,
231
  canonicalUrl = '';
232
 
684
  tsfSocial.updateState( 'twDescPlaceholder', response.data.twdescription.trim() );
685
  }
686
 
687
+ if ( imageUrl ) {
688
+ // Is this necessary? It's safer than assuming, though :)
689
+ imageUrl.placeholder = tsf.decodeEntities( response.data.imageurl );
690
+ imageUrl.dispatchEvent( new Event( 'change' ) );
691
+ }
692
 
693
  'tsfAys' in window && tsfAys.reset();
694
  }, fadeTime );
728
  url: ajaxurl,
729
  datatype: 'json',
730
  data: {
731
+ action: 'tsf_update_post_data',
732
  nonce: tsf.l10n.nonces.edit_posts,
733
  post_id: l10n.states.id,
734
  get: getData,
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=()=>{tsfTabs.initStack("tsfSettings",{tabToggledEvent:new CustomEvent("tsf-flex-tab-toggled"),HTMLClasses:{wrapper:"tsf-flex-nav-tab-wrapper",tabRadio:"tsf-flex-nav-tab-radio",tabLabel:"tsf-flex-nav-tab-label",activeTab:"tsf-flex-tab-active",activeTabContent:"tsf-flex-tab-content-active"},fixHistory:!0})},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();
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=()=>{tsfTabs.initStack("tsfSettings",{tabToggledEvent:new CustomEvent("tsf-flex-tab-toggled"),HTMLClasses:{wrapper:"tsf-flex-nav-tab-wrapper",tabRadio:"tsf-flex-nav-tab-radio",tabLabel:"tsf-flex-nav-tab-label",activeTab:"tsf-flex-tab-active",activeTabContent:"tsf-flex-tab-content-active"},fixHistory:!0})},g=()=>{const c=document.getElementById("autodescription_noindex");if(!c)return;const d=1,e=2;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&d||g&e?"":h)},50)},j=a=>{h=a,i()};a(document).on("tsf-updated-gutenberg-link",(a,b)=>j(b));const k=a=>{let b=c.querySelector("[value=\"0\"]"),e="";switch(a){case"password":case"private":e="noindex",g|=d;break;default:case"public":e=c.dataset.defaultUnprotected,g&=~d;}b&&(b.innerHTML=c.dataset.defaultI18n.replace("%s",tsf.decodeEntities(e))),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=c.dataset.defaultUnprotected;break;case-1:b="index";break;case 1:b="noindex";}"noindex"===b?g|=e:g&=~e,i()};c.addEventListener("change",a=>m(a.target.value)),m(c.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&&(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:"tsf_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
@@ -7,7 +7,7 @@
7
 
8
  /**
9
  * The SEO Framework plugin
10
- * Copyright (C) 2019 - 2020 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
11
  *
12
  * This program is free software: you can redistribute it and/or modify
13
  * it under the terms of the GNU General Public License version 3 as published
7
 
8
  /**
9
  * The SEO Framework plugin
10
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (https://cyberwire.nl/)
11
  *
12
  * This program is free software: you can redistribute it and/or modify
13
  * it under the terms of the GNU General Public License version 3 as published
lib/js/pt.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/settings.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/social.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/tabs.js CHANGED
@@ -8,7 +8,7 @@
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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2020 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/term.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/title.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2019 - 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
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
lib/js/tsf.js CHANGED
@@ -8,7 +8,7 @@
8
 
9
  /**
10
  * The SEO Framework plugin
11
- * Copyright (C) 2015 - 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
@@ -461,10 +461,10 @@ window.tsf = function( $ ) {
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
  }
8
 
9
  /**
10
  * The SEO Framework plugin
11
+ * Copyright (C) 2015 - 2021 Sybre Waaijer, CyberWire B.V. (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
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
  }
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{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();
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
@@ -9,7 +9,7 @@
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
@@ -244,9 +244,6 @@ window.tsfTT = function( $ ) {
244
  * Doesn't test whether a tooltip is present, since that happens asynchronously--often (yet _not always_) after the click finishes.
245
  * If we set a datapoint where we tell the tooltip is still building, we might be able to read that out (e.g. instigatingTooltip).
246
  *
247
- * FIXME: When a tooltip is injected via another script (via tsfTT.doTooltip()) that has a toolTipWrapName,
248
- * this might cause a click on the object to become unnatural.
249
- *
250
  * @function
251
  * @param {Event} event
252
  */
9
 
10
  /**
11
  * The SEO Framework plugin
12
+ * Copyright (C) 2019 - 2021 Sybre Waaijer, CyberWire B.V. (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
244
  * Doesn't test whether a tooltip is present, since that happens asynchronously--often (yet _not always_) after the click finishes.
245
  * If we set a datapoint where we tell the tooltip is still building, we might be able to read that out (e.g. instigatingTooltip).
246
  *
 
 
 
247
  * @function
248
  * @param {Event} event
249
  */
readme.txt CHANGED
@@ -1,11 +1,11 @@
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.3
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -126,6 +126,7 @@ The SEO Framework works on many things without notifying you, because the best s
126
  * Translation plugins like WPML, Polylang, WPGlobus, and MultilingualPress.
127
  * E-commerce plugins, like WooCommerce and Easy Digital Downloads.
128
  * Editing posts and terms via WordPress's native bulk-and-quick-edit interfaces.
 
129
 
130
  = Copyright legislation notice =
131
 
@@ -226,7 +227,7 @@ In the meantime, you can disable SEO for the unwanted entries via the "General S
226
 
227
  = Why aren't focus keywords included? =
228
 
229
- [Google warns](https://support.google.com/webmasters/answer/66358) about the keyword stuffing approach implemented by some other SEO plugins. It forces users to write unnatural content, and it can have adverse effects on your site's ranking.
230
 
231
  Modern search engines use AI to understand the context of your articles. This means that as long as you write relevant content, you shouldn't have to worry about keywords.
232
 
@@ -246,11 +247,15 @@ If you wish to display breadcrumbs, then your theme should provide this. Alterna
246
 
247
  == Changelog ==
248
 
 
 
 
 
249
  = 4.1.3 =
250
 
251
  Before heading into 2021, we wanted to [set free four bugs](https://theseoframework.com/?p=3660#detailed).
252
 
253
- Pro tip: If you can no longer switch TSF's settings tabs, try hitting 'CMD+SHIFT+R' (Mac) or 'CTRL+SHIFT+R' (Windows); these keyboard shortcuts will force-fetch the latest scripts from your server.
254
 
255
  = 4.1.2 =
256
 
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, headless
5
  Requires at least: 5.1.0
6
+ Tested up to: 5.8
7
  Requires PHP: 5.6.0
8
+ Stable tag: 4.1.4
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
126
  * Translation plugins like WPML, Polylang, WPGlobus, and MultilingualPress.
127
  * E-commerce plugins, like WooCommerce and Easy Digital Downloads.
128
  * Editing posts and terms via WordPress's native bulk-and-quick-edit interfaces.
129
+ * Headless mode via a single [constant definition](https://kb.theseoframework.com/?p=136).
130
 
131
  = Copyright legislation notice =
132
 
227
 
228
  = Why aren't focus keywords included? =
229
 
230
+ [Google warns](https://developers.google.com/search/docs/advanced/guidelines/irrelevant-keywords) about the keyword stuffing approach implemented by some other SEO plugins. It forces users to write unnatural content, and it can have adverse effects on your site's ranking.
231
 
232
  Modern search engines use AI to understand the context of your articles. This means that as long as you write relevant content, you shouldn't have to worry about keywords.
233
 
247
 
248
  == Changelog ==
249
 
250
+ = 4.1.4 =
251
+
252
+ This minor update packs a major punch. TSF now supports [headless mode](https://kb.theseoframework.com/?p=136), cementing itself as a turnkey solution. We defenestrated the pernicious object caching mechanism, and we updated some options' defaults effective only on new sites. We improved performance iterably, fixed about 12 bugs, and enjoyed the weather. Lastly, we introduced a new API for user meta handling, among other things --- developers that wrote software interfacing with TSF are employed well reading the [detailed changelog](https://theseoframework.com/?p=3727#detailed).
253
+
254
  = 4.1.3 =
255
 
256
  Before heading into 2021, we wanted to [set free four bugs](https://theseoframework.com/?p=3660#detailed).
257
 
258
+ Pro tip: If you can no longer switch TSF's settings tabs, try hitting 'CMD+OPTION+R' (Safari Mac), 'CMD+SHIFT+R' (Chrome/Firefox Mac), or 'CTRL+SHIFT+R' (Windows); these keyboard shortcuts will force-fetch the latest scripts from your server.
259
 
260
  = 4.1.2 =
261