Yoast SEO - Version 7.7

Version Description

Download this release

Release Info

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

Code changes from version 7.6.1 to 7.7

Files changed (83) hide show
  1. admin/banner/class-admin-banner-sidebar.php +1 -1
  2. admin/capabilities/class-register-capabilities.php +40 -7
  3. admin/class-admin-asset-dev-server-location.php +1 -0
  4. admin/class-admin-asset-manager.php +9 -0
  5. admin/class-admin-asset-yoast-components-l10n.php +11 -7
  6. admin/class-admin-init.php +2 -1
  7. admin/class-admin-media-purge-notification.php +1 -21
  8. admin/class-admin-recommended-replace-vars.php +203 -0
  9. admin/class-admin.php +3 -2
  10. admin/class-config.php +28 -4
  11. admin/class-database-proxy.php +33 -8
  12. admin/class-help-center.php +9 -6
  13. admin/class-yoast-alerts.php +2 -1
  14. admin/class-yoast-notification-center.php +128 -17
  15. admin/class-yoast-notification.php +39 -1
  16. admin/formatter/class-metabox-formatter.php +0 -1
  17. admin/formatter/class-post-metabox-formatter.php +8 -2
  18. admin/formatter/class-term-metabox-formatter.php +8 -2
  19. admin/menu/class-replacevar-editor.php +77 -0
  20. admin/menu/class-replacevar-field.php +67 -0
  21. admin/metabox/class-metabox.php +19 -11
  22. admin/taxonomy/class-taxonomy-content-fields.php +1 -6
  23. admin/taxonomy/class-taxonomy.php +20 -9
  24. admin/views/class-view-utils.php +10 -16
  25. admin/views/tabs/metas/archives.php +9 -10
  26. admin/views/tabs/metas/general/homepage.php +3 -2
  27. admin/views/tabs/metas/post-types.php +64 -42
  28. admin/views/tabs/metas/taxonomies.php +7 -2
  29. admin/watchers/class-slug-change-watcher.php +89 -29
  30. css/dist/{admin-global-761-rtl.min.css → admin-global-770-rtl.min.css} +1 -1
  31. css/dist/{admin-global-761.min.css → admin-global-770.min.css} +1 -1
  32. css/dist/{adminbar-761-rtl.min.css → adminbar-770-rtl.min.css} +0 -0
  33. css/dist/{adminbar-761.min.css → adminbar-770.min.css} +0 -0
  34. css/dist/{alerts-761-rtl.min.css → alerts-770-rtl.min.css} +0 -0
  35. css/dist/{alerts-761.min.css → alerts-770.min.css} +0 -0
  36. css/dist/{dashboard-761-rtl.min.css → dashboard-770-rtl.min.css} +0 -0
  37. css/dist/{dashboard-761.min.css → dashboard-770.min.css} +0 -0
  38. css/dist/{edit-page-761-rtl.min.css → edit-page-770-rtl.min.css} +0 -0
  39. css/dist/{edit-page-761.min.css → edit-page-770.min.css} +0 -0
  40. css/dist/{featured-image-761-rtl.min.css → featured-image-770-rtl.min.css} +0 -0
  41. css/dist/{featured-image-761.min.css → featured-image-770.min.css} +0 -0
  42. css/dist/{filter-explanation-761-rtl.min.css → filter-explanation-770-rtl.min.css} +0 -0
  43. css/dist/{filter-explanation-761.min.css → filter-explanation-770.min.css} +0 -0
  44. css/dist/{inside-editor-761-rtl.min.css → inside-editor-770-rtl.min.css} +0 -0
  45. css/dist/{inside-editor-761.min.css → inside-editor-770.min.css} +0 -0
  46. css/dist/{metabox-761-rtl.min.css → metabox-770-rtl.min.css} +1 -1
  47. css/dist/{metabox-761.min.css → metabox-770.min.css} +1 -1
  48. css/dist/{metabox-primary-category-761-rtl.min.css → metabox-primary-category-770-rtl.min.css} +0 -0
  49. css/dist/{metabox-primary-category-761.min.css → metabox-primary-category-770.min.css} +0 -0
  50. css/dist/search-appearance-770-rtl.min.css +1 -0
  51. css/dist/search-appearance-770.min.css +1 -0
  52. css/dist/{snippet-761-rtl.min.css → snippet-770-rtl.min.css} +0 -0
  53. css/dist/{snippet-761.min.css → snippet-770.min.css} +0 -0
  54. css/dist/{toggle-switch-761-rtl.min.css → toggle-switch-770-rtl.min.css} +1 -1
  55. css/dist/{toggle-switch-761.min.css → toggle-switch-770.min.css} +1 -1
  56. css/dist/{wpseo-dismissible-761-rtl.min.css → wpseo-dismissible-770-rtl.min.css} +0 -0
  57. css/dist/{wpseo-dismissible-761.min.css → wpseo-dismissible-770.min.css} +0 -0
  58. css/dist/{yoast-components-761-rtl.min.css → yoast-components-770-rtl.min.css} +0 -0
  59. css/dist/{yoast-components-761.min.css → yoast-components-770.min.css} +0 -0
  60. css/dist/{yoast-extensions-761-rtl.min.css → yoast-extensions-770-rtl.min.css} +0 -0
  61. css/dist/{yoast-extensions-761.min.css → yoast-extensions-770.min.css} +0 -0
  62. css/dist/yst_plugin_tools-761-rtl.min.css +0 -1
  63. css/dist/yst_plugin_tools-761.min.css +0 -1
  64. css/dist/yst_plugin_tools-770-rtl.min.css +1 -0
  65. css/dist/yst_plugin_tools-770.min.css +1 -0
  66. css/dist/{yst_seo_score-761-rtl.min.css → yst_seo_score-770-rtl.min.css} +0 -0
  67. css/dist/{yst_seo_score-761.min.css → yst_seo_score-770.min.css} +0 -0
  68. frontend/class-breadcrumbs.php +42 -23
  69. frontend/class-frontend.php +16 -1
  70. frontend/class-json-ld.php +45 -0
  71. frontend/class-opengraph-image.php +15 -8
  72. images/banner/yoast-seo-for-wordpress-training-2018.png +0 -0
  73. images/banner/yoast-seo-for-wordpress-training.png +0 -0
  74. images/pinterest-23x30.png +0 -0
  75. inc/class-rewrite.php +1 -0
  76. inc/class-upgrade.php +14 -0
  77. inc/class-wpseo-content-images.php +10 -52
  78. inc/class-wpseo-image-utils.php +36 -1
  79. inc/class-wpseo-replace-vars.php +195 -69
  80. inc/class-wpseo-replacement-variable.php +73 -0
  81. inc/sitemaps/class-sitemaps-router.php +1 -0
  82. js/dist/{commons-761.min.js → commons-770.min.js} +7 -7
  83. js/dist/configuration-wizard-761.min.js +0 -12
admin/banner/class-admin-banner-sidebar.php CHANGED
@@ -270,7 +270,7 @@ class WPSEO_Admin_Banner_Sidebar {
270
  $courses_spot->add_banner(
271
  new WPSEO_Admin_Banner(
272
  WPSEO_Shortlinker::get( 'https://yoa.st/jv' ),
273
- 'yoast-seo-for-wordpress-training.png',
274
  261,
275
  152,
276
  sprintf(
270
  $courses_spot->add_banner(
271
  new WPSEO_Admin_Banner(
272
  WPSEO_Shortlinker::get( 'https://yoa.st/jv' ),
273
+ 'yoast-seo-for-wordpress-training-2018.png',
274
  261,
275
  152,
276
  sprintf(
admin/capabilities/class-register-capabilities.php CHANGED
@@ -16,6 +16,10 @@ class WPSEO_Register_Capabilities implements WPSEO_WordPress_Integration {
16
  */
17
  public function register_hooks() {
18
  add_action( 'wpseo_register_capabilities', array( $this, 'register' ) );
 
 
 
 
19
  }
20
 
21
  /**
@@ -29,14 +33,43 @@ class WPSEO_Register_Capabilities implements WPSEO_WordPress_Integration {
29
  $manager->register( 'wpseo_bulk_edit', array( 'editor', 'wpseo_editor', 'wpseo_manager' ) );
30
  $manager->register( 'wpseo_edit_advanced_metadata', array( 'wpseo_editor', 'wpseo_manager' ) );
31
 
32
- $manager->register( 'wpseo_manage_options', array( 'wpseo_manager' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- /*
35
- * Respect MultiSite 'access' setting if set to 'super admins only'.
36
- * This means that local admins do not get the `wpseo_manage_options` capability.
37
- */
38
- if ( WPSEO_Options::get( 'access' ) !== 'superadmins' ) {
39
- $manager->register( 'wpseo_manage_options', array( 'administrator' ) );
40
  }
 
 
41
  }
42
  }
16
  */
17
  public function register_hooks() {
18
  add_action( 'wpseo_register_capabilities', array( $this, 'register' ) );
19
+
20
+ if ( is_multisite() ) {
21
+ add_action( 'user_has_cap', array( $this, 'filter_user_has_wpseo_manage_options_cap' ), 10, 4 );
22
+ }
23
  }
24
 
25
  /**
33
  $manager->register( 'wpseo_bulk_edit', array( 'editor', 'wpseo_editor', 'wpseo_manager' ) );
34
  $manager->register( 'wpseo_edit_advanced_metadata', array( 'wpseo_editor', 'wpseo_manager' ) );
35
 
36
+ $manager->register( 'wpseo_manage_options', array( 'administrator', 'wpseo_manager' ) );
37
+ }
38
+
39
+ /**
40
+ * Revokes the 'wpseo_manage_options' capability from administrator users if it should only
41
+ * only be granted to network administrators.
42
+ *
43
+ * @param array $allcaps An array of all the user's capabilities.
44
+ * @param array $caps Actual capabilities being checked.
45
+ * @param array $args Optional parameters passed to has_cap(), typically object ID.
46
+ * @param WP_User $user The user object.
47
+ *
48
+ * @return array Possibly modified array of the user's capabilities.
49
+ */
50
+ public function filter_user_has_wpseo_manage_options_cap( $allcaps, $caps, $args, $user ) {
51
+
52
+ // We only need to do something if 'wpseo_manage_options' is being checked.
53
+ if ( ! in_array( 'wpseo_manage_options', $caps, true ) ) {
54
+ return $allcaps;
55
+ }
56
+
57
+ // If the user does not have 'wpseo_manage_options' anyway, we don't need to revoke access.
58
+ if ( empty( $allcaps['wpseo_manage_options'] ) ) {
59
+ return $allcaps;
60
+ }
61
+
62
+ // If the user does not have 'delete_users', they are not an administrator.
63
+ if ( empty( $allcaps['delete_users'] ) ) {
64
+ return $allcaps;
65
+ }
66
+
67
+ $options = WPSEO_Options::get_instance();
68
 
69
+ if ( $options->get( 'access' ) === 'superadmin' && ! is_super_admin( $user->ID ) ) {
70
+ unset( $allcaps['wpseo_manage_options'] );
 
 
 
 
71
  }
72
+
73
+ return $allcaps;
74
  }
75
  }
admin/class-admin-asset-dev-server-location.php CHANGED
@@ -17,6 +17,7 @@ final class WPSEO_Admin_Asset_Dev_Server_Location implements WPSEO_Admin_Asset_L
17
  private static $dev_server_script = array(
18
  'commons',
19
  'configuration-wizard',
 
20
  'wp-seo-dashboard-widget',
21
  'wp-seo-help-center',
22
  'wp-seo-metabox',
17
  private static $dev_server_script = array(
18
  'commons',
19
  'configuration-wizard',
20
+ 'search-appearance',
21
  'wp-seo-dashboard-widget',
22
  'wp-seo-help-center',
23
  'wp-seo-metabox',
admin/class-admin-asset-manager.php CHANGED
@@ -220,6 +220,11 @@ class WPSEO_Admin_Asset_Manager {
220
  self::PREFIX . 'babel-polyfill',
221
  ),
222
  ),
 
 
 
 
 
223
  array(
224
  'name' => 'wp-globals-backport',
225
  'src' => 'wp-seo-wp-globals-backport-' . $flat_version,
@@ -520,6 +525,10 @@ class WPSEO_Admin_Asset_Manager {
520
  'name' => 'filter-explanation',
521
  'src' => 'filter-explanation-' . $flat_version,
522
  ),
 
 
 
 
523
  );
524
  }
525
  }
220
  self::PREFIX . 'babel-polyfill',
221
  ),
222
  ),
223
+ array(
224
+ 'name' => 'search-appearance',
225
+ 'src' => 'search-appearance-' . $flat_version,
226
+ 'deps' => 'react-dependencies',
227
+ ),
228
  array(
229
  'name' => 'wp-globals-backport',
230
  'src' => 'wp-seo-wp-globals-backport-' . $flat_version,
525
  'name' => 'filter-explanation',
526
  'src' => 'filter-explanation-' . $flat_version,
527
  ),
528
+ array(
529
+ 'name' => 'search-appearance',
530
+ 'src' => 'search-appearance-' . $flat_version,
531
+ ),
532
  );
533
  }
534
  }
admin/class-admin-asset-yoast-components-l10n.php CHANGED
@@ -4,29 +4,33 @@
4
  */
5
 
6
  /**
7
- * Localizes yoast-components.
8
  */
9
  final class WPSEO_Admin_Asset_Yoast_Components_l10n {
10
  /**
11
- * Localizes the given script with the yoast-components translations.
12
  *
13
  * @param string $script_handle The script handle to localize for.
14
  *
15
  * @return void
16
  */
17
  public function localize_script( $script_handle ) {
18
- wp_localize_script( $script_handle, 'wpseoYoastComponentsL10n', $this->get_translations() );
 
 
 
19
  }
20
 
21
  /**
22
- * Returns translations necessary for yoast-components.
23
  *
24
- * @return object The translations in a Jed format for yoast-components.
 
25
  */
26
- protected function get_translations() {
27
  $locale = WPSEO_Utils::get_user_locale();
28
 
29
- $file = plugin_dir_path( WPSEO_FILE ) . 'languages/yoast-components-' . $locale . '.json';
30
  if ( file_exists( $file ) ) {
31
  $file = file_get_contents( $file );
32
  if ( is_string( $file ) && $file !== '' ) {
4
  */
5
 
6
  /**
7
+ * Localizes JavaScript files.
8
  */
9
  final class WPSEO_Admin_Asset_Yoast_Components_l10n {
10
  /**
11
+ * Localizes the given script with the JavaScript translations.
12
  *
13
  * @param string $script_handle The script handle to localize for.
14
  *
15
  * @return void
16
  */
17
  public function localize_script( $script_handle ) {
18
+ wp_localize_script( $script_handle, 'wpseoYoastJSL10n', array(
19
+ 'yoast-components' => $this->get_translations( 'yoast-components' ),
20
+ 'wordpress-seo' => $this->get_translations( 'wordpress-seojs' ),
21
+ ) );
22
  }
23
 
24
  /**
25
+ * Returns translations necessary for JS files.
26
  *
27
+ * @param string $component The component to retrieve the translations for.
28
+ * @return object The translations in a Jed format for JS files.
29
  */
30
+ protected function get_translations( $component ) {
31
  $locale = WPSEO_Utils::get_user_locale();
32
 
33
+ $file = plugin_dir_path( WPSEO_FILE ) . 'languages/' . $component . '-' . $locale . '.json';
34
  if ( file_exists( $file ) ) {
35
  $file = file_get_contents( $file );
36
  if ( is_string( $file ) && $file !== '' ) {
admin/class-admin-init.php CHANGED
@@ -83,7 +83,8 @@ class WPSEO_Admin_Init {
83
  $current_url = ( is_ssl() ? 'https://' : 'http://' );
84
  $current_url .= sanitize_text_field( $_SERVER['SERVER_NAME'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
85
  $customize_url = add_query_arg( array(
86
- 'url' => urlencode( $current_url ),
 
87
  ), wp_customize_url() );
88
 
89
  $info_message = sprintf(
83
  $current_url = ( is_ssl() ? 'https://' : 'http://' );
84
  $current_url .= sanitize_text_field( $_SERVER['SERVER_NAME'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
85
  $customize_url = add_query_arg( array(
86
+ 'autofocus[control]' => 'blogdescription',
87
+ 'url' => urlencode( $current_url ),
88
  ), wp_customize_url() );
89
 
90
  $info_message = sprintf(
admin/class-admin-media-purge-notification.php CHANGED
@@ -55,27 +55,7 @@ class WPSEO_Admin_Media_Purge_Notification implements WPSEO_WordPress_Integratio
55
  * @return void
56
  */
57
  public function manage_notification() {
58
- if ( ! WPSEO_Options::get( 'is-media-purge-relevant' ) ) {
59
- $this->remove_notification();
60
-
61
- return;
62
- }
63
-
64
- // This is the desired behaviour, remove any notifications and carry on.
65
- if ( WPSEO_Options::get( 'disable-attachment' ) === true ) {
66
- $this->remove_notification();
67
-
68
- return;
69
- }
70
-
71
- // When the attachments are no-indexed, this is acceptable.
72
- if ( WPSEO_Options::get( 'noindex-attachment' ) === true ) {
73
- $this->remove_notification();
74
-
75
- return;
76
- }
77
-
78
- $this->add_notification();
79
  }
80
 
81
  /**
55
  * @return void
56
  */
57
  public function manage_notification() {
58
+ $this->remove_notification();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
 
61
  /**
admin/class-admin-recommended-replace-vars.php ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * Determines the recommended replacement variables based on the context.
10
+ */
11
+ class WPSEO_Admin_Recommended_Replace_Vars {
12
+
13
+ /**
14
+ * @var array The recommended replacement variables.
15
+ */
16
+ protected $recommended_replace_vars = array(
17
+ // Posts types.
18
+ 'page' => array( 'sitename', 'title', 'sep', 'primary_category' ),
19
+ 'post' => array( 'sitename', 'title', 'sep', 'primary_category' ),
20
+ // Homepage.
21
+ 'homepage' => array( 'sitename', 'sitedesc', 'sep' ),
22
+ // Custom post type.
23
+ 'custom_post_type' => array( 'sitename', 'title', 'sep' ),
24
+
25
+ // Taxonomies.
26
+ 'category' => array( 'sitename', 'term_title', 'sep' ),
27
+ 'post_tag' => array( 'sitename', 'term_title', 'sep' ),
28
+ 'post_format' => array( 'sitename', 'term_title', 'sep', 'page' ),
29
+
30
+ // Custom taxonomy.
31
+ 'term-in-custom-taxomomy' => array( 'sitename', 'term_title', 'sep' ),
32
+
33
+ // Settings - archive pages.
34
+ 'author_archive' => array( 'sitename', 'title', 'sep', 'page' ),
35
+ 'date_archive' => array( 'sitename', 'sep', 'date', 'page' ),
36
+ 'custom-post-type_archive' => array( 'sitename', 'title', 'sep', 'page' ),
37
+
38
+ // Settings - special pages.
39
+ 'search' => array( 'sitename', 'searchphrase', 'sep', 'page' ),
40
+ '404' => array( 'sitename', 'sep' ),
41
+ );
42
+
43
+ /**
44
+ * Determines the page type of the current term.
45
+ *
46
+ * @param string $taxonomy The taxonomy name.
47
+ *
48
+ * @return string The page type.
49
+ */
50
+ public function determine_for_term( $taxonomy ) {
51
+ $recommended_replace_vars = $this->get_recommended_replacevars();
52
+ if ( array_key_exists( $taxonomy, $recommended_replace_vars ) ) {
53
+ return $taxonomy;
54
+ }
55
+
56
+ return 'term-in-custom-taxomomy';
57
+ }
58
+
59
+ /**
60
+ * Determines the page type of the current post.
61
+ *
62
+ * @param WP_Post $post The WordPress global post object.
63
+ *
64
+ * @return string The page type.
65
+ */
66
+ public function determine_for_post( $post ) {
67
+ if ( $post instanceof WP_Post === false ) {
68
+ return 'post';
69
+ }
70
+
71
+ if ( $post->post_type === 'page' && $this->is_homepage( $post ) ) {
72
+ return 'homepage';
73
+ }
74
+
75
+ $recommended_replace_vars = $this->get_recommended_replacevars();
76
+ if ( array_key_exists( $post->post_type, $recommended_replace_vars ) ) {
77
+ return $post->post_type;
78
+ }
79
+
80
+ return 'custom_post_type';
81
+ }
82
+
83
+ /**
84
+ * Determines the page type for a post type.
85
+ *
86
+ * @param string $post_type The name of the post_type.
87
+ * @param string $fallback The page type to fall back to.
88
+ *
89
+ * @return string The page type.
90
+ */
91
+ public function determine_for_post_type( $post_type, $fallback = 'custom_post_type' ) {
92
+ $page_type = $post_type;
93
+ $recommended_replace_vars = $this->get_recommended_replacevars();
94
+ $has_recommended_replacevars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type );
95
+
96
+ if ( ! $has_recommended_replacevars ) {
97
+ return $fallback;
98
+ }
99
+
100
+ return $page_type;
101
+ }
102
+
103
+ /**
104
+ * Determines the page type for an archive page.
105
+ *
106
+ * @param string $name The name of the archive.
107
+ * @param string $fallback The page type to fall back to.
108
+ *
109
+ * @return string The page type.
110
+ */
111
+ public function determine_for_archive( $name, $fallback = 'custom-post-type_archive' ) {
112
+ $page_type = $name . '_archive';
113
+ $recommended_replace_vars = $this->get_recommended_replacevars();
114
+ $has_recommended_replacevars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type );
115
+
116
+ if ( ! $has_recommended_replacevars ) {
117
+ return $fallback;
118
+ }
119
+
120
+ return $page_type;
121
+ }
122
+
123
+ /**
124
+ * Retrieves the recommended replacement variables for the given page type.
125
+ *
126
+ * @param string $page_type The page type.
127
+ *
128
+ * @return array The recommended replacement variables.
129
+ */
130
+ public function get_recommended_replacevars_for( $page_type ) {
131
+ $recommended_replace_vars = $this->get_recommended_replacevars();
132
+ $has_recommended_replace_vars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type );
133
+
134
+ if ( ! $has_recommended_replace_vars ) {
135
+ return array();
136
+ }
137
+
138
+ return $recommended_replace_vars[ $page_type ];
139
+ }
140
+
141
+ /**
142
+ * Retrieves the recommended replacement variables.
143
+ *
144
+ * @return array The recommended replacement variables.
145
+ */
146
+ public function get_recommended_replacevars() {
147
+ /**
148
+ * Filter: Adds the possibility to add extra recommended replacement variables.
149
+ *
150
+ * @api array $additional_replace_vars Empty array to add the replacevars to.
151
+ */
152
+ $recommended_replace_vars = apply_filters( 'wpseo_recommended_replace_vars', $this->recommended_replace_vars );
153
+
154
+ if ( ! is_array( $recommended_replace_vars ) ) {
155
+ return $this->recommended_replace_vars;
156
+ }
157
+
158
+ return $recommended_replace_vars;
159
+ }
160
+
161
+ /**
162
+ * Returns whether the given page type has recommended replace vars.
163
+ *
164
+ * @param array $recommended_replace_vars The recommended replace vars
165
+ * to check in.
166
+ * @param string $page_type The page type to check.
167
+ *
168
+ * @return bool True if there are associated recommended replace vars.
169
+ */
170
+ private function has_recommended_replace_vars( $recommended_replace_vars, $page_type ) {
171
+ if ( ! isset( $recommended_replace_vars[ $page_type ] ) ) {
172
+ return false;
173
+ }
174
+
175
+ if ( ! is_array( $recommended_replace_vars[ $page_type ] ) ) {
176
+ return false;
177
+ }
178
+
179
+ return true;
180
+ }
181
+
182
+ /**
183
+ * Determines whether or not a post is the homepage.
184
+ *
185
+ * @param WP_Post $post The WordPress global post object.
186
+ *
187
+ * @return bool True if the given post is the homepage.
188
+ */
189
+ private function is_homepage( $post ) {
190
+ if ( $post instanceof WP_Post === false ) {
191
+ return false;
192
+ }
193
+
194
+ /*
195
+ * The page on front returns a string with normal WordPress interaction, while the post ID is an int.
196
+ * This way we make sure we always compare strings.
197
+ */
198
+ $post_id = (int) $post->ID;
199
+ $page_on_front = (int) get_option( 'page_on_front' );
200
+
201
+ return get_option( 'show_on_front' ) === 'page' && $page_on_front === $post_id;
202
+ }
203
+ }
admin/class-admin.php CHANGED
@@ -71,6 +71,8 @@ class WPSEO_Admin {
71
 
72
  add_action( 'admin_init', array( $this, 'map_manage_options_cap' ) );
73
 
 
 
74
  WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' );
75
  WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' );
76
 
@@ -86,7 +88,6 @@ class WPSEO_Admin {
86
 
87
  $this->set_upsell_notice();
88
 
89
- $this->check_php_version();
90
  $this->initialize_cornerstone_content();
91
 
92
  new Yoast_Modal();
@@ -322,7 +323,7 @@ class WPSEO_Admin {
322
  *
323
  * @return void
324
  */
325
- protected function check_php_version() {
326
  // If the user isn't an admin, don't display anything.
327
  if ( ! current_user_can( 'manage_options' ) ) {
328
  return;
71
 
72
  add_action( 'admin_init', array( $this, 'map_manage_options_cap' ) );
73
 
74
+ add_action( 'admin_init', array( $this, 'check_php_version' ) );
75
+
76
  WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' );
77
  WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' );
78
 
88
 
89
  $this->set_upsell_notice();
90
 
 
91
  $this->initialize_cornerstone_content();
92
 
93
  new Yoast_Modal();
323
  *
324
  * @return void
325
  */
326
+ public function check_php_version() {
327
  // If the user isn't an admin, don't display anything.
328
  if ( ! current_user_can( 'manage_options' ) ) {
329
  return;
admin/class-config.php CHANGED
@@ -80,11 +80,20 @@ class WPSEO_Admin_Pages {
80
  $this->asset_manager->enqueue_script( 'admin-script' );
81
  $this->asset_manager->enqueue_script( 'help-center' );
82
 
 
 
 
 
 
 
 
 
 
 
 
83
  wp_enqueue_script( 'dashboard' );
84
  wp_enqueue_script( 'thickbox' );
85
 
86
- $page = filter_input( INPUT_GET, 'page' );
87
-
88
  wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-script', 'wpseoSelect2Locale', WPSEO_Utils::get_language( WPSEO_Utils::get_user_locale() ) );
89
 
90
  if ( in_array( $page, array( 'wpseo_social', WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_titles' ), true ) ) {
@@ -100,9 +109,9 @@ class WPSEO_Admin_Pages {
100
  }
101
 
102
  /**
103
- * Pass some variables to js for upload module.
104
  *
105
- * @return array
106
  */
107
  public function localize_media_script() {
108
  return array(
@@ -110,6 +119,21 @@ class WPSEO_Admin_Pages {
110
  );
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  /**
114
  * Enqueues and handles all the tool dependencies.
115
  */
80
  $this->asset_manager->enqueue_script( 'admin-script' );
81
  $this->asset_manager->enqueue_script( 'help-center' );
82
 
83
+ $page = filter_input( INPUT_GET, 'page' );
84
+
85
+ if ( $page === 'wpseo_titles' ) {
86
+ wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'search-appearance', 'wpseoReplaceVarsL10n', $this->localize_replace_vars_script() );
87
+ $this->asset_manager->enqueue_script( 'search-appearance' );
88
+ $this->asset_manager->enqueue_style( 'search-appearance' );
89
+
90
+ $yoast_components_l10n = new WPSEO_Admin_Asset_Yoast_Components_l10n();
91
+ $yoast_components_l10n->localize_script( 'search-appearance' );
92
+ }
93
+
94
  wp_enqueue_script( 'dashboard' );
95
  wp_enqueue_script( 'thickbox' );
96
 
 
 
97
  wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-script', 'wpseoSelect2Locale', WPSEO_Utils::get_language( WPSEO_Utils::get_user_locale() ) );
98
 
99
  if ( in_array( $page, array( 'wpseo_social', WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_titles' ), true ) ) {
109
  }
110
 
111
  /**
112
+ * Retrieves some variables that are needed for the upload module in JS.
113
  *
114
+ * @return array The upload module variables.
115
  */
116
  public function localize_media_script() {
117
  return array(
119
  );
120
  }
121
 
122
+ /**
123
+ * Retrieves some variables that are needed for replacing variables in JS.
124
+ *
125
+ * @return array The replacement and recommended replacement variables.
126
+ */
127
+ public function localize_replace_vars_script() {
128
+ $replace_vars = new WPSEO_Replace_Vars();
129
+ $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
130
+
131
+ return array(
132
+ 'replace_vars' => $replace_vars->get_replacement_variables_list(),
133
+ 'recommended_replace_vars' => $recommended_replace_vars->get_recommended_replacevars(),
134
+ );
135
+ }
136
+
137
  /**
138
  * Enqueues and handles all the tool dependencies.
139
  */
admin/class-database-proxy.php CHANGED
@@ -91,22 +91,47 @@ class WPSEO_Database_Proxy {
91
  /**
92
  * Upserts data in the database.
93
  *
94
- * Tries to insert the data first, if this fails an update is attempted.
95
  *
96
  * @param array $data Data to update on the table.
97
- * @param array $where Where condition as key => value array.
98
- * @param null $format Optional. data prepare format.
99
- * @param null $where_format Optional. Where prepare format.
100
  *
101
  * @return false|int False when the upsert request is invalid, int on number of rows changed.
102
  */
103
- public function upsert( array $data, array $where, $format = null, $where_format = null ) {
104
- $result = $this->insert( $data, $format );
 
 
105
 
106
- if ( false === $result ) {
107
- $result = $this->update( $data, $where, $format, $where_format );
 
 
 
 
 
 
108
  }
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  return $result;
111
  }
112
 
91
  /**
92
  * Upserts data in the database.
93
  *
94
+ * Performs an insert into and if key is duplicate it will update the existing record.
95
  *
96
  * @param array $data Data to update on the table.
97
+ * @param array $where Unused. Where condition as key => value array.
98
+ * @param null $format Optional. Data prepare format.
99
+ * @param null $where_format Deprecated. Where prepare format.
100
  *
101
  * @return false|int False when the upsert request is invalid, int on number of rows changed.
102
  */
103
+ public function upsert( array $data, array $where = null, $format = null, $where_format = null ) {
104
+ if ( $where_format !== null ) {
105
+ _deprecated_argument( __METHOD__, '7.7.0', 'The where_format argument is deprecated' );
106
+ }
107
 
108
+ $this->pre_execution();
109
+
110
+ $update = array();
111
+ $keys = array();
112
+ $columns = array_keys( $data );
113
+ foreach ( $columns as $column ) {
114
+ $keys[] = '`' . $column . '`';
115
+ $update[] = sprintf( '`%1$s` = VALUES(`%1$s`)', $column );
116
  }
117
 
118
+ $query = sprintf(
119
+ 'INSERT INTO `%1$s` (%2$s) VALUES ( %3$s ) ON DUPLICATE KEY UPDATE %4$s',
120
+ $this->get_table_name(),
121
+ implode( ', ', $keys ),
122
+ implode( ', ', array_fill( 0, count( $data ), '%s' ) ),
123
+ implode( ', ', $update )
124
+ );
125
+
126
+ $result = $this->database->query(
127
+ $this->database->prepare(
128
+ $query,
129
+ array_values( $data )
130
+ )
131
+ );
132
+
133
+ $this->post_execution();
134
+
135
  return $result;
136
  }
137
 
admin/class-help-center.php CHANGED
@@ -104,20 +104,23 @@ class WPSEO_Help_Center {
104
 
105
  $formatted_data['translations'] = self::get_translated_texts();
106
 
107
- $formatted_data['videoDescriptions'] = array(
108
- array(
 
 
109
  'title' => __( 'Need some help?', 'wordpress-seo' ),
110
  'description' => __( 'Go Premium and our experts will be there for you to answer any questions you might have about the setup and use of the plugin.', 'wordpress-seo' ),
111
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/seo-premium-vt' ),
112
  'linkText' => __( 'Get Yoast SEO Premium now »', 'wordpress-seo' ),
113
- ),
114
- array(
 
115
  'title' => __( 'Want to be a Yoast SEO Expert?', 'wordpress-seo' ),
116
  'description' => __( 'Follow our Yoast SEO for WordPress training and become a certified Yoast SEO Expert!', 'wordpress-seo' ),
117
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/wordpress-training-vt' ),
118
  'linkText' => __( 'Enroll in the Yoast SEO for WordPress training »', 'wordpress-seo' ),
119
- ),
120
- );
121
 
122
  $formatted_data['contactSupportParagraphs'] = array(
123
  array(
104
 
105
  $formatted_data['translations'] = self::get_translated_texts();
106
 
107
+ $formatted_data['videoDescriptions'] = array();
108
+
109
+ if ( $is_premium === false ) {
110
+ $formatted_data['videoDescriptions'][] = array(
111
  'title' => __( 'Need some help?', 'wordpress-seo' ),
112
  'description' => __( 'Go Premium and our experts will be there for you to answer any questions you might have about the setup and use of the plugin.', 'wordpress-seo' ),
113
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/seo-premium-vt' ),
114
  'linkText' => __( 'Get Yoast SEO Premium now »', 'wordpress-seo' ),
115
+ );
116
+
117
+ $formatted_data['videoDescriptions'][] = array(
118
  'title' => __( 'Want to be a Yoast SEO Expert?', 'wordpress-seo' ),
119
  'description' => __( 'Follow our Yoast SEO for WordPress training and become a certified Yoast SEO Expert!', 'wordpress-seo' ),
120
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/wordpress-training-vt' ),
121
  'linkText' => __( 'Enroll in the Yoast SEO for WordPress training »', 'wordpress-seo' ),
122
+ );
123
+ }
124
 
125
  $formatted_data['contactSupportParagraphs'] = array(
126
  array(
admin/class-yoast-alerts.php CHANGED
@@ -87,7 +87,8 @@ class Yoast_Alerts {
87
 
88
  $notification = $this->get_notification_from_ajax_request();
89
  if ( $notification ) {
90
- delete_user_meta( get_current_user_id(), $notification->get_dismissal_key() );
 
91
 
92
  $this->output_ajax_response( $notification->get_type() );
93
  }
87
 
88
  $notification = $this->get_notification_from_ajax_request();
89
  if ( $notification ) {
90
+ $notification_center = Yoast_Notification_Center::get();
91
+ $notification_center->restore_notification( $notification );
92
 
93
  $this->output_ajax_response( $notification->get_type() );
94
  }
admin/class-yoast-notification-center.php CHANGED
@@ -25,12 +25,18 @@ class Yoast_Notification_Center {
25
  /** @var array Notifications that were resolved this execution */
26
  private $resolved = 0;
27
 
 
 
 
 
 
 
28
  /**
29
  * Construct
30
  */
31
  private function __construct() {
32
 
33
- $this->retrieve_notifications_from_storage();
34
 
35
  add_action( 'all_admin_notices', array( $this, 'display_notifications' ) );
36
 
@@ -96,7 +102,15 @@ class Yoast_Notification_Center {
96
  $user_id = ( ! is_null( $user_id ) ? $user_id : get_current_user_id() );
97
  $dismissal_key = $notification->get_dismissal_key();
98
 
99
- $current_value = get_user_meta( $user_id, $dismissal_key, true );
 
 
 
 
 
 
 
 
100
 
101
  return ! empty( $current_value );
102
  }
@@ -146,6 +160,42 @@ class Yoast_Notification_Center {
146
  return self::dismiss_notification( $notification, $meta_value );
147
  }
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  /**
150
  * Clear dismissal information for the specified Notification
151
  *
@@ -158,6 +208,8 @@ class Yoast_Notification_Center {
158
  */
159
  public function clear_dismissal( $notification ) {
160
 
 
 
161
  if ( $notification instanceof Yoast_Notification ) {
162
  $dismissal_key = $notification->get_dismissal_key();
163
  }
@@ -171,11 +223,36 @@ class Yoast_Notification_Center {
171
  }
172
 
173
  // Remove notification dismissal for all users.
174
- $deleted = delete_metadata( 'user', 0, $dismissal_key, '', true );
 
 
 
175
 
176
  return $deleted;
177
  }
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  /**
180
  * Add notification to the cookie
181
  *
@@ -183,6 +260,12 @@ class Yoast_Notification_Center {
183
  */
184
  public function add_notification( Yoast_Notification $notification ) {
185
 
 
 
 
 
 
 
186
  // Don't add if the user can't see it.
187
  if ( ! $notification->display_for_current_user() ) {
188
  return;
@@ -273,6 +356,12 @@ class Yoast_Notification_Center {
273
  */
274
  public function remove_notification( Yoast_Notification $notification, $resolve = true ) {
275
 
 
 
 
 
 
 
276
  $index = false;
277
 
278
  // Match persistent Notifications by ID, non persistent by item in the array.
@@ -448,6 +537,12 @@ class Yoast_Notification_Center {
448
  */
449
  private function retrieve_notifications_from_storage() {
450
 
 
 
 
 
 
 
451
  $stored_notifications = get_user_option( self::STORAGE_KEY, get_current_user_id() );
452
 
453
  // Check if notifications are stored.
@@ -492,19 +587,6 @@ class Yoast_Notification_Center {
492
  return 0;
493
  }
494
 
495
- /**
496
- * Dismiss the notification
497
- *
498
- * @param Yoast_Notification $notification Notification to dismiss.
499
- * @param string $meta_value Value to save in the dismissal.
500
- *
501
- * @return bool
502
- */
503
- private static function dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
504
- // Dismiss notification.
505
- return ( false !== update_user_meta( get_current_user_id(), $notification->get_dismissal_key(), $meta_value ) );
506
- }
507
-
508
  /**
509
  * Remove all notifications from storage
510
  */
@@ -518,7 +600,8 @@ class Yoast_Notification_Center {
518
  */
519
  private function clear_notifications() {
520
 
521
- $this->notifications = array();
 
522
  }
523
 
524
  /**
@@ -597,4 +680,32 @@ class Yoast_Notification_Center {
597
  private function is_notification_persistent( Yoast_Notification $notification ) {
598
  return ! $notification->is_persistent();
599
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  }
25
  /** @var array Notifications that were resolved this execution */
26
  private $resolved = 0;
27
 
28
+ /** @var array Internal storage for transaction before notifications have been retrieved from storage. */
29
+ private $queued_transactions = array();
30
+
31
+ /** @var bool Internal flag for whether notifications have been retrieved from storage. */
32
+ private $notifications_retrieved = false;
33
+
34
  /**
35
  * Construct
36
  */
37
  private function __construct() {
38
 
39
+ add_action( 'init', array( $this, 'setup_current_notifications' ), 1 );
40
 
41
  add_action( 'all_admin_notices', array( $this, 'display_notifications' ) );
42
 
102
  $user_id = ( ! is_null( $user_id ) ? $user_id : get_current_user_id() );
103
  $dismissal_key = $notification->get_dismissal_key();
104
 
105
+ // This checks both the site-specific user option and the meta value.
106
+ $current_value = get_user_option( $dismissal_key, $user_id );
107
+
108
+ // Migrate old user meta to user option on-the-fly.
109
+ if ( ! empty( $current_value )
110
+ && metadata_exists( 'user', $user_id, $dismissal_key )
111
+ && update_user_option( $user_id, $dismissal_key, $current_value ) ) {
112
+ delete_user_meta( $user_id, $dismissal_key );
113
+ }
114
 
115
  return ! empty( $current_value );
116
  }
160
  return self::dismiss_notification( $notification, $meta_value );
161
  }
162
 
163
+ /**
164
+ * Dismisses a notification.
165
+ *
166
+ * @param Yoast_Notification $notification Notification to dismiss.
167
+ * @param string $meta_value Value to save in the dismissal.
168
+ *
169
+ * @return bool True if dismissed, false otherwise.
170
+ */
171
+ public static function dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
172
+ // Dismiss notification.
173
+ return update_user_option( get_current_user_id(), $notification->get_dismissal_key(), $meta_value ) !== false;
174
+ }
175
+
176
+ /**
177
+ * Restores a notification.
178
+ *
179
+ * @param Yoast_Notification $notification Notification to restore.
180
+ *
181
+ * @return bool True if restored, false otherwise.
182
+ */
183
+ public static function restore_notification( Yoast_Notification $notification ) {
184
+
185
+ $user_id = get_current_user_id();
186
+ $dismissal_key = $notification->get_dismissal_key();
187
+
188
+ // Restore notification.
189
+ $restored = delete_user_option( $user_id, $dismissal_key );
190
+
191
+ // Delete unprefixed user meta too for backward-compatibility.
192
+ if ( metadata_exists( 'user', $user_id, $dismissal_key ) ) {
193
+ $restored = delete_user_meta( $user_id, $dismissal_key ) && $restored;
194
+ }
195
+
196
+ return $restored;
197
+ }
198
+
199
  /**
200
  * Clear dismissal information for the specified Notification
201
  *
208
  */
209
  public function clear_dismissal( $notification ) {
210
 
211
+ global $wpdb;
212
+
213
  if ( $notification instanceof Yoast_Notification ) {
214
  $dismissal_key = $notification->get_dismissal_key();
215
  }
223
  }
224
 
225
  // Remove notification dismissal for all users.
226
+ $deleted = delete_metadata( 'user', 0, $wpdb->get_blog_prefix() . $dismissal_key, '', true );
227
+
228
+ // Delete unprefixed user meta too for backward-compatibility.
229
+ $deleted = delete_metadata( 'user', 0, $dismissal_key, '', true ) || $deleted;
230
 
231
  return $deleted;
232
  }
233
 
234
+ /**
235
+ * Retrieves notifications from the storage and merges in previous notification changes.
236
+ *
237
+ * The current user in WordPress is not loaded shortly before the 'init' hook, but the plugin
238
+ * sometimes needs to add or remove notifications before that. In such cases, the transactions
239
+ * are not actually executed, but added to a queue. That queue is then handled in this method,
240
+ * after notifications for the current user have been set up.
241
+ *
242
+ * @return void
243
+ */
244
+ public function setup_current_notifications() {
245
+ $this->retrieve_notifications_from_storage();
246
+
247
+ foreach ( $this->queued_transactions as $transaction ) {
248
+ list( $callback, $args ) = $transaction;
249
+
250
+ call_user_func_array( $callback, $args );
251
+ }
252
+
253
+ $this->queued_transactions = array();
254
+ }
255
+
256
  /**
257
  * Add notification to the cookie
258
  *
260
  */
261
  public function add_notification( Yoast_Notification $notification ) {
262
 
263
+ $callback = array( $this, __METHOD__ );
264
+ $args = func_get_args();
265
+ if ( $this->queue_transaction( $callback, $args ) ) {
266
+ return;
267
+ }
268
+
269
  // Don't add if the user can't see it.
270
  if ( ! $notification->display_for_current_user() ) {
271
  return;
356
  */
357
  public function remove_notification( Yoast_Notification $notification, $resolve = true ) {
358
 
359
+ $callback = array( $this, __METHOD__ );
360
+ $args = func_get_args();
361
+ if ( $this->queue_transaction( $callback, $args ) ) {
362
+ return;
363
+ }
364
+
365
  $index = false;
366
 
367
  // Match persistent Notifications by ID, non persistent by item in the array.
537
  */
538
  private function retrieve_notifications_from_storage() {
539
 
540
+ if ( $this->notifications_retrieved ) {
541
+ return;
542
+ }
543
+
544
+ $this->notifications_retrieved = true;
545
+
546
  $stored_notifications = get_user_option( self::STORAGE_KEY, get_current_user_id() );
547
 
548
  // Check if notifications are stored.
587
  return 0;
588
  }
589
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  /**
591
  * Remove all notifications from storage
592
  */
600
  */
601
  private function clear_notifications() {
602
 
603
+ $this->notifications = array();
604
+ $this->notifications_retrieved = false;
605
  }
606
 
607
  /**
680
  private function is_notification_persistent( Yoast_Notification $notification ) {
681
  return ! $notification->is_persistent();
682
  }
683
+
684
+ /**
685
+ * Queues a notification transaction for later execution if notifications are not yet set up.
686
+ *
687
+ * @param callable $callback Callback that performs the transaction.
688
+ * @param array $args Arguments to pass to the callback.
689
+ *
690
+ * @return bool True if transaction was queued, false if it can be performed immediately.
691
+ */
692
+ private function queue_transaction( $callback, $args ) {
693
+ if ( $this->notifications_retrieved ) {
694
+ return false;
695
+ }
696
+
697
+ $this->add_transaction_to_queue( $callback, $args );
698
+
699
+ return true;
700
+ }
701
+
702
+ /**
703
+ * Adds a notification transaction to the queue for later execution.
704
+ *
705
+ * @param callable $callback Callback that performs the transaction.
706
+ * @param array $args Arguments to pass to the callback.
707
+ */
708
+ private function add_transaction_to_queue( $callback, $args ) {
709
+ $this->queued_transactions[] = array( $callback, $args );
710
+ }
711
  }
admin/class-yoast-notification.php CHANGED
@@ -62,8 +62,16 @@ class Yoast_Notification {
62
  'dismissal_key' => null,
63
  'capabilities' => array(),
64
  'capability_check' => self::MATCH_ALL,
 
65
  );
66
 
 
 
 
 
 
 
 
67
  /**
68
  * Notification class constructor.
69
  *
@@ -282,8 +290,38 @@ class Yoast_Notification {
282
  // Combined attribute key and value into a string.
283
  array_walk( $attributes, array( $this, 'parse_attributes' ) );
284
 
 
 
 
 
 
 
 
 
 
285
  // Build the output DIV.
286
- return '<div ' . implode( ' ', $attributes ) . '>' . wpautop( $this->message ) . '</div>' . PHP_EOL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  }
288
 
289
  /**
62
  'dismissal_key' => null,
63
  'capabilities' => array(),
64
  'capability_check' => self::MATCH_ALL,
65
+ 'yoast_branding' => false,
66
  );
67
 
68
+ /**
69
+ * The message for the notification.
70
+ *
71
+ * @var string
72
+ */
73
+ private $message;
74
+
75
  /**
76
  * Notification class constructor.
77
  *
290
  // Combined attribute key and value into a string.
291
  array_walk( $attributes, array( $this, 'parse_attributes' ) );
292
 
293
+ $message = null;
294
+ if ( $this->options['yoast_branding'] ) {
295
+ $message = $this->wrap_yoast_seo_icon( $this->message );
296
+ }
297
+
298
+ if ( $message === null ) {
299
+ $message = wpautop( $this->message );
300
+ }
301
+
302
  // Build the output DIV.
303
+ return '<div ' . implode( ' ', $attributes ) . '>' . $message . '</div>' . PHP_EOL;
304
+ }
305
+
306
+ /**
307
+ * Wraps the message with a Yoast SEO icon.
308
+ *
309
+ * @param string $message The message to wrap.
310
+ *
311
+ * @return string The wrapped message.
312
+ */
313
+ private function wrap_yoast_seo_icon( $message ) {
314
+ $out = sprintf(
315
+ '<img src="%1$s" height="%2$d" width="%3$d" class="yoast-seo-icon" />',
316
+ esc_url( plugin_dir_url( WPSEO_FILE ) . 'images/Yoast_SEO_Icon.svg' ),
317
+ 60,
318
+ 60
319
+ );
320
+ $out .= '<div class="yoast-seo-icon-wrap">';
321
+ $out .= $message;
322
+ $out .= '</div>';
323
+
324
+ return $out;
325
  }
326
 
327
  /**
admin/formatter/class-metabox-formatter.php CHANGED
@@ -64,7 +64,6 @@ class WPSEO_Metabox_Formatter {
64
  'contentAnalysisActive' => $analysis_readability->is_enabled() ? 1 : 0,
65
  'keywordAnalysisActive' => $analysis_seo->is_enabled() ? 1 : 0,
66
  'intl' => $this->get_content_analysis_component_translations(),
67
- 'reactSnippetPreview' => defined( 'YOAST_FEATURE_SNIPPET_PREVIEW' ) && YOAST_FEATURE_SNIPPET_PREVIEW,
68
 
69
  /**
70
  * Filter to determine if the markers should be enabled or not.
64
  'contentAnalysisActive' => $analysis_readability->is_enabled() ? 1 : 0,
65
  'keywordAnalysisActive' => $analysis_seo->is_enabled() ? 1 : 0,
66
  'intl' => $this->get_content_analysis_component_translations(),
 
67
 
68
  /**
69
  * Filter to determine if the markers should be enabled or not.
admin/formatter/class-post-metabox-formatter.php CHANGED
@@ -153,10 +153,16 @@ class WPSEO_Post_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface
153
  /**
154
  * Retrieves the title template.
155
  *
156
- * @return string
157
  */
158
  private function get_title_template() {
159
- return $this->get_template( 'title' );
 
 
 
 
 
 
160
  }
161
 
162
  /**
153
  /**
154
  * Retrieves the title template.
155
  *
156
+ * @return string The title template.
157
  */
158
  private function get_title_template() {
159
+ $title = $this->get_template( 'title' );
160
+
161
+ if ( $title === '' ) {
162
+ return '%%title%% %%sep%% %%sitename%%';
163
+ }
164
+
165
+ return $title;
166
  }
167
 
168
  /**
admin/formatter/class-term-metabox-formatter.php CHANGED
@@ -109,10 +109,16 @@ class WPSEO_Term_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface
109
  /**
110
  * Retrieves the title template.
111
  *
112
- * @return string
113
  */
114
  private function get_title_template() {
115
- return $this->get_template( 'title' );
 
 
 
 
 
 
116
  }
117
 
118
  /**
109
  /**
110
  * Retrieves the title template.
111
  *
112
+ * @return string The title template.
113
  */
114
  private function get_title_template() {
115
+ $title = $this->get_template( 'title' );
116
+
117
+ if ( $title === '' ) {
118
+ return '%%title%% %%sep%% %%sitename%%';
119
+ }
120
+
121
+ return $title;
122
  }
123
 
124
  /**
admin/menu/class-replacevar-editor.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Menu
6
+ */
7
+
8
+ /**
9
+ * Renders a replacement variable editor.
10
+ */
11
+ class WPSEO_Replacevar_Editor {
12
+ /**
13
+ * @var Yoast_Form Yoast Forms instance.
14
+ */
15
+ private $yform;
16
+
17
+ /**
18
+ * @var string The id for the hidden title field.
19
+ */
20
+ private $title;
21
+
22
+ /**
23
+ * @var string The id for the hidden description field.
24
+ */
25
+ private $description;
26
+
27
+ /**
28
+ * @var string The page type for context.
29
+ */
30
+ private $page_type;
31
+
32
+ /**
33
+ * @var bool Whether the editor has paper style.
34
+ */
35
+ private $paper_style;
36
+
37
+ /**
38
+ * Constructs the object.
39
+ *
40
+ * @param Yoast_Form $yform Yoast forms.
41
+ * @param string $title The title field id.
42
+ * @param string $description The description field id.
43
+ * @param string $page_type The page type for context.
44
+ * @param bool $paper_style Whether the editor has paper style.
45
+ */
46
+ public function __construct( Yoast_Form $yform, $title, $description, $page_type, $paper_style = true ) {
47
+ $this->yform = $yform;
48
+ $this->title = (string) $title;
49
+ $this->description = (string) $description;
50
+ $this->page_type = (string) $page_type;
51
+ $this->paper_style = (bool) $paper_style;
52
+ }
53
+
54
+ /**
55
+ * Renders a div for the react application to mount to, and hidden inputs where
56
+ * the app should store it's value so they will be properly saved when the form
57
+ * is submitted.
58
+ *
59
+ * @return void
60
+ */
61
+ public function render() {
62
+ $this->yform->hidden( $this->title, $this->title );
63
+ $this->yform->hidden( $this->description, $this->description );
64
+
65
+ printf( '<div
66
+ data-react-replacevar-editor
67
+ data-react-replacevar-title-field-id="%1$s"
68
+ data-react-replacevar-metadesc-field-id="%2$s"
69
+ data-react-replacevar-page-type="%3$s"
70
+ data-react-replacevar-paper-style="%4$s"></div>',
71
+ esc_attr( $this->title ),
72
+ esc_attr( $this->description ),
73
+ esc_attr( $this->page_type ),
74
+ esc_attr( $this->paper_style )
75
+ );
76
+ }
77
+ }
admin/menu/class-replacevar-field.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Menu
6
+ */
7
+
8
+ /**
9
+ * Renders a single replacement variable field.
10
+ */
11
+ class WPSEO_Replacevar_Field {
12
+ /**
13
+ * @var Yoast_Form Yoast Forms instance.
14
+ */
15
+ private $yform;
16
+
17
+ /**
18
+ * @var string The id for the hidden field.
19
+ */
20
+ private $field_id;
21
+
22
+ /**
23
+ * @var string The label for the field.
24
+ */
25
+ private $label;
26
+
27
+ /**
28
+ * @var string The page type for context.
29
+ */
30
+ private $page_type;
31
+
32
+ /**
33
+ * Constructs the object.
34
+ *
35
+ * @param Yoast_Form $yform Yoast forms.
36
+ * @param string $field_id The field id.
37
+ * @param string $label The field label.
38
+ * @param string $page_type The page type for context.
39
+ */
40
+ public function __construct( Yoast_Form $yform, $field_id, $label, $page_type ) {
41
+ $this->yform = $yform;
42
+ $this->field_id = $field_id;
43
+ $this->label = $label;
44
+ $this->page_type = $page_type;
45
+ }
46
+
47
+ /**
48
+ * Renders a div for the react application to mount to, and hidden inputs where
49
+ * the app should store it's value so they will be properly saved when the form
50
+ * is submitted.
51
+ *
52
+ * @return void
53
+ */
54
+ public function render() {
55
+ $this->yform->hidden( $this->field_id, $this->field_id );
56
+
57
+ printf( '<div
58
+ data-react-replacevar-field
59
+ data-react-replacevar-field-id="%1$s"
60
+ data-react-replacevar-field-label="%2$s",
61
+ data-react-replacevar-page-type="%3$s"></div>',
62
+ esc_attr( $this->field_id ),
63
+ esc_attr( $this->label ),
64
+ esc_attr( $this->page_type )
65
+ );
66
+ }
67
+ }
admin/metabox/class-metabox.php CHANGED
@@ -60,9 +60,6 @@ class WPSEO_Metabox extends WPSEO_Meta {
60
  */
61
  public static function translate_meta_boxes() {
62
  self::$meta_fields['general']['snippetpreview']['title'] = __( 'Snippet editor', 'wordpress-seo' );
63
- /* translators: 1: link open tag; 2: link close tag. */
64
- self::$meta_fields['general']['snippetpreview']['help'] = sprintf( __( 'This is a rendering of what this post might look like in Google\'s search results. %1$sLearn more about the Snippet Preview%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/snippet-preview' ) . '">', '</a>' );
65
- self::$meta_fields['general']['snippetpreview']['help-button'] = __( 'Show information about the snippet editor', 'wordpress-seo' );
66
 
67
  self::$meta_fields['general']['pageanalysis']['title'] = __( 'Analysis', 'wordpress-seo' );
68
  /* translators: 1: link open tag; 2: link close tag. */
@@ -257,9 +254,10 @@ class WPSEO_Metabox extends WPSEO_Meta {
257
  */
258
  public function localize_replace_vars_script() {
259
  return array(
260
- 'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
261
- 'replace_vars' => $this->get_replace_vars(),
262
- 'scope' => $this->determine_scope(),
 
263
  );
264
  }
265
 
@@ -985,11 +983,6 @@ class WPSEO_Metabox extends WPSEO_Meta {
985
  'sitedesc',
986
  'sep',
987
  'page',
988
- 'currenttime',
989
- 'currentdate',
990
- 'currentday',
991
- 'currentmonth',
992
- 'currentyear',
993
  );
994
 
995
  foreach ( $vars_to_cache as $var ) {
@@ -1000,6 +993,21 @@ class WPSEO_Metabox extends WPSEO_Meta {
1000
  return array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $post ) );
1001
  }
1002
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1003
  /**
1004
  * Gets the custom replace variables for custom taxonomies and fields.
1005
  *
60
  */
61
  public static function translate_meta_boxes() {
62
  self::$meta_fields['general']['snippetpreview']['title'] = __( 'Snippet editor', 'wordpress-seo' );
 
 
 
63
 
64
  self::$meta_fields['general']['pageanalysis']['title'] = __( 'Analysis', 'wordpress-seo' );
65
  /* translators: 1: link open tag; 2: link close tag. */
254
  */
255
  public function localize_replace_vars_script() {
256
  return array(
257
+ 'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
258
+ 'replace_vars' => $this->get_replace_vars(),
259
+ 'recommended_replace_vars' => $this->get_recommended_replace_vars(),
260
+ 'scope' => $this->determine_scope(),
261
  );
262
  }
263
 
983
  'sitedesc',
984
  'sep',
985
  'page',
 
 
 
 
 
986
  );
987
 
988
  foreach ( $vars_to_cache as $var ) {
993
  return array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $post ) );
994
  }
995
 
996
+ /**
997
+ * Prepares the recommended replace vars for localization.
998
+ *
999
+ * @return array Recommended replacement variables.
1000
+ */
1001
+ private function get_recommended_replace_vars() {
1002
+ $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
1003
+ $post = $this->get_metabox_post();
1004
+
1005
+ // What is recommended depends on the current context.
1006
+ $post_type = $recommended_replace_vars->determine_for_post( $post );
1007
+
1008
+ return $recommended_replace_vars->get_recommended_replacevars_for( $post_type );
1009
+ }
1010
+
1011
  /**
1012
  * Gets the custom replace variables for custom taxonomies and fields.
1013
  *
admin/taxonomy/class-taxonomy-content-fields.php CHANGED
@@ -20,12 +20,7 @@ class WPSEO_Taxonomy_Content_Fields extends WPSEO_Taxonomy_Fields {
20
  'snippet' => $this->get_field_config(
21
  __( 'Snippet editor', 'wordpress-seo' ),
22
  '',
23
- 'snippetpreview',
24
- array(
25
- 'help-button' => __( 'Show information about the snippet editor', 'wordpress-seo' ),
26
- /* translators: 1: link open tag; 2: link close tag. */
27
- 'help' => sprintf( __( 'This is a rendering of what this post might look like in Google\'s search results. %1$sLearn more about the Snippet Preview%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/snippet-preview' ) . '">', '</a>' ),
28
- )
29
  ),
30
  'focuskw' => $this->get_field_config(
31
  __( 'Focus keyword', 'wordpress-seo' ),
20
  'snippet' => $this->get_field_config(
21
  __( 'Snippet editor', 'wordpress-seo' ),
22
  '',
23
+ 'snippetpreview'
 
 
 
 
 
24
  ),
25
  'focuskw' => $this->get_field_config(
26
  __( 'Focus keyword', 'wordpress-seo' ),
admin/taxonomy/class-taxonomy.php CHANGED
@@ -234,9 +234,10 @@ class WPSEO_Taxonomy {
234
  */
235
  public function localize_replace_vars_script() {
236
  return array(
237
- 'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
238
- 'replace_vars' => $this->get_replace_vars(),
239
- 'scope' => $this->determine_scope(),
 
240
  );
241
  }
242
 
@@ -302,7 +303,7 @@ class WPSEO_Taxonomy {
302
  /**
303
  * Prepares the replace vars for localization.
304
  *
305
- * @return array replace vars.
306
  */
307
  private function get_replace_vars() {
308
  $term_id = filter_input( INPUT_GET, 'tag_ID' );
@@ -317,11 +318,6 @@ class WPSEO_Taxonomy {
317
  'sitedesc',
318
  'sep',
319
  'page',
320
- 'currenttime',
321
- 'currentdate',
322
- 'currentday',
323
- 'currentmonth',
324
- 'currentyear',
325
  'term_title',
326
  'term_description',
327
  'category_description',
@@ -336,6 +332,21 @@ class WPSEO_Taxonomy {
336
  return $cached_replacement_vars;
337
  }
338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  /**
340
  * Adds custom category description editor.
341
  * Needs a hook that runs before the description field. Prior to WP version 4.5 we need to use edit_form as
234
  */
235
  public function localize_replace_vars_script() {
236
  return array(
237
+ 'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
238
+ 'replace_vars' => $this->get_replace_vars(),
239
+ 'recommended_replace_vars' => $this->get_recommended_replace_vars(),
240
+ 'scope' => $this->determine_scope(),
241
  );
242
  }
243
 
303
  /**
304
  * Prepares the replace vars for localization.
305
  *
306
+ * @return array The replacement variables.
307
  */
308
  private function get_replace_vars() {
309
  $term_id = filter_input( INPUT_GET, 'tag_ID' );
318
  'sitedesc',
319
  'sep',
320
  'page',
 
 
 
 
 
321
  'term_title',
322
  'term_description',
323
  'category_description',
332
  return $cached_replacement_vars;
333
  }
334
 
335
+ /**
336
+ * Prepares the recommended replace vars for localization.
337
+ *
338
+ * @return array The recommended replacement variables.
339
+ */
340
+ private function get_recommended_replace_vars() {
341
+ $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
342
+ $taxonomy = filter_input( INPUT_GET, 'taxonomy' );
343
+
344
+ // What is recommended depends on the current context.
345
+ $page_type = $recommended_replace_vars->determine_for_term( $taxonomy );
346
+
347
+ return $recommended_replace_vars->get_recommended_replacevars_for( $page_type );
348
+ }
349
+
350
  /**
351
  * Adds custom category description editor.
352
  * Needs a hook that runs before the description field. Prior to WP version 4.5 we need to use edit_form as
admin/views/class-view-utils.php CHANGED
@@ -72,13 +72,7 @@ class Yoast_View_Utils {
72
  }
73
 
74
  $show_post_type_help = $this->search_results_setting_help( $post_type );
75
-
76
  $noindex_option_name = 'noindex-' . $post_type->name;
77
- $this->form->index_switch(
78
- $noindex_option_name,
79
- $post_type->labels->name,
80
- $show_post_type_help->get_button_html() . $show_post_type_help->get_panel_html()
81
- );
82
 
83
  if ( WPSEO_Options::get( 'is-media-purge-relevant' ) ) {
84
  if ( $post_type->name === 'attachment' && WPSEO_Options::get( $noindex_option_name ) === false ) {
@@ -97,16 +91,10 @@ you want more information about the impact of showing media in search results.',
97
  }
98
  }
99
 
100
- $this->form->textinput(
101
- 'title-' . $post_type->name,
102
- __( 'Title template', 'wordpress-seo' ),
103
- 'template posttype-template'
104
- );
105
-
106
- $this->form->textarea(
107
- 'metadesc-' . $post_type->name,
108
- __( 'Meta description template', 'wordpress-seo' ),
109
- array( 'class' => 'template posttype-template' )
110
  );
111
 
112
  $this->form->show_hide_switch(
@@ -119,5 +107,11 @@ you want more information about the impact of showing media in search results.',
119
  /* translators: %1$s expands to Yoast SEO */
120
  sprintf( __( '%1$s Meta Box', 'wordpress-seo' ), 'Yoast SEO' )
121
  );
 
 
 
 
 
 
122
  }
123
  }
72
  }
73
 
74
  $show_post_type_help = $this->search_results_setting_help( $post_type );
 
75
  $noindex_option_name = 'noindex-' . $post_type->name;
 
 
 
 
 
76
 
77
  if ( WPSEO_Options::get( 'is-media-purge-relevant' ) ) {
78
  if ( $post_type->name === 'attachment' && WPSEO_Options::get( $noindex_option_name ) === false ) {
91
  }
92
  }
93
 
94
+ $this->form->index_switch(
95
+ $noindex_option_name,
96
+ $post_type->labels->name,
97
+ $show_post_type_help->get_button_html() . $show_post_type_help->get_panel_html()
 
 
 
 
 
 
98
  );
99
 
100
  $this->form->show_hide_switch(
107
  /* translators: %1$s expands to Yoast SEO */
108
  sprintf( __( '%1$s Meta Box', 'wordpress-seo' ), 'Yoast SEO' )
109
  );
110
+
111
+ $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
112
+ $page_type = $recommended_replace_vars->determine_for_post_type( $post_type->name );
113
+
114
+ $editor = new WPSEO_Replacevar_Editor( $this->form, 'title-' . $post_type->name, 'metadesc-' . $post_type->name, $page_type, false );
115
+ $editor->render();
116
  }
117
  }
admin/views/tabs/metas/archives.php CHANGED
@@ -87,8 +87,8 @@ $yform->index_switch(
87
  $author_archives_no_posts_help->get_button_html() . $author_archives_no_posts_help->get_panel_html()
88
  );
89
 
90
- $yform->textinput( 'title-author-wpseo', __( 'Title template', 'wordpress-seo' ), 'template author-template' );
91
- $yform->textarea( 'metadesc-author-wpseo', __( 'Meta description template', 'wordpress-seo' ), array( 'class' => 'template author-template' ) );
92
  echo '</div>';
93
  echo '</div>';
94
 
@@ -119,8 +119,8 @@ $yform->index_switch(
119
  $date_archives_help->get_button_html() . $date_archives_help->get_panel_html()
120
  );
121
 
122
- $yform->textinput( 'title-archive-wpseo', __( 'Title template', 'wordpress-seo' ), 'template date-template' );
123
- $yform->textarea( 'metadesc-archive-wpseo', __( 'Meta description template', 'wordpress-seo' ), array( 'class' => 'template date-template' ) );
124
  echo '</div>';
125
  echo '</div>';
126
 
@@ -139,10 +139,9 @@ echo '<div class="tab-block" id="special-pages-titles-metas">';
139
  echo '<h2 class="help-button-inline">' . esc_html__( 'Special Pages', 'wordpress-seo' ) . $spcia_pages_help->get_button_html() . '</h2>';
140
  echo $spcia_pages_help->get_panel_html();
141
 
142
- echo '<p><strong>' . esc_html__( 'Search pages', 'wordpress-seo' ) . '</strong><br/>';
143
- $yform->textinput( 'title-search-wpseo', __( 'Title template', 'wordpress-seo' ), 'template search-template' );
144
- echo '</p>';
145
- echo '<p><strong>' . esc_html__( '404 pages', 'wordpress-seo' ) . '</strong><br/>';
146
- $yform->textinput( 'title-404-wpseo', __( 'Title template', 'wordpress-seo' ), 'template error404-template' );
147
- echo '</p>';
148
  echo '</div>';
87
  $author_archives_no_posts_help->get_button_html() . $author_archives_no_posts_help->get_panel_html()
88
  );
89
 
90
+ $editor = new WPSEO_Replacevar_Editor( $yform, 'title-author-wpseo', 'metadesc-author-wpseo', 'author_archive' );
91
+ $editor->render();
92
  echo '</div>';
93
  echo '</div>';
94
 
119
  $date_archives_help->get_button_html() . $date_archives_help->get_panel_html()
120
  );
121
 
122
+ $editor = new WPSEO_Replacevar_Editor( $yform, 'title-archive-wpseo', 'metadesc-archive-wpseo', 'date_archive' );
123
+ $editor->render();
124
  echo '</div>';
125
  echo '</div>';
126
 
139
  echo '<h2 class="help-button-inline">' . esc_html__( 'Special Pages', 'wordpress-seo' ) . $spcia_pages_help->get_button_html() . '</h2>';
140
  echo $spcia_pages_help->get_panel_html();
141
 
142
+ $editor = new WPSEO_Replacevar_Field( $yform, 'title-search-wpseo', __( 'Search pages', 'wordpress-seo' ), 'search' );
143
+ $editor->render();
144
+ echo '<br/>';
145
+ $editor = new WPSEO_Replacevar_Field( $yform, 'title-404-wpseo', __( '404 pages', 'wordpress-seo' ), '404' );
146
+ $editor->render();
 
147
  echo '</div>';
admin/views/tabs/metas/general/homepage.php CHANGED
@@ -20,8 +20,9 @@
20
 
21
  echo '<h2 class="help-button-inline">', esc_html__( 'Homepage', 'wordpress-seo' ), $homepage_help->get_button_html(), '</h2>';
22
  echo $homepage_help->get_panel_html();
23
- $yform->textinput( 'title-home-wpseo', __( 'Title', 'wordpress-seo' ), 'template homepage-template' );
24
- $yform->textarea( 'metadesc-home-wpseo', __( 'Meta description', 'wordpress-seo' ), array( 'class' => 'template homepage-template' ) );
 
25
  }
26
  else {
27
  echo '<h2>', esc_html__( 'Homepage &amp; Front page', 'wordpress-seo' ), '</h2>';
20
 
21
  echo '<h2 class="help-button-inline">', esc_html__( 'Homepage', 'wordpress-seo' ), $homepage_help->get_button_html(), '</h2>';
22
  echo $homepage_help->get_panel_html();
23
+
24
+ $editor = new WPSEO_Replacevar_Editor( $yform, 'title-home-wpseo', 'metadesc-home-wpseo', 'homepage' );
25
+ $editor->render();
26
  }
27
  else {
28
  echo '<h2>', esc_html__( 'Homepage &amp; Front page', 'wordpress-seo' ), '</h2>';
admin/views/tabs/metas/post-types.php CHANGED
@@ -6,6 +6,8 @@
6
  */
7
 
8
  /**
 
 
9
  * @var Yoast_Form $yform
10
  */
11
 
@@ -27,12 +29,69 @@ $post_types = WPSEO_Post_Type::filter_attachment_post_type( $post_types );
27
 
28
  $view_utils = new Yoast_View_Utils();
29
 
 
 
 
 
30
  if ( is_array( $post_types ) && $post_types !== array() ) {
31
- foreach ( $post_types as $post_type ) {
32
- echo '<div class="tab-block" id="' . esc_attr( $post_type->name . '-titles-metas' ) . '">';
33
- echo '<h2 id="' . esc_attr( $post_type->name ) . '">' . esc_html( $post_type->labels->name ) . ' (<code>' . esc_html( $post_type->name ) . '</code>)</h2>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  $view_utils->show_post_type_settings( $post_type );
35
- echo '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  /**
37
  * Allow adding a custom checkboxes to the admin meta page - Post Types tab
38
  *
@@ -40,45 +99,8 @@ if ( is_array( $post_types ) && $post_types !== array() ) {
40
  * @api String $name The post type name
41
  */
42
  do_action( 'wpseo_admin_page_meta_post_types', $yform, $post_type->name );
43
- }
44
- unset( $post_type );
45
- }
46
-
47
- $post_types = get_post_types(
48
- array(
49
- '_builtin' => false,
50
- 'has_archive' => true,
51
- ),
52
- 'objects'
53
- );
54
 
55
- if ( is_array( $post_types ) && $post_types !== array() ) {
56
- echo '<h2>' . esc_html__( 'Custom Content Type Archives', 'wordpress-seo' ) . '</h2>';
57
- echo '<p>' . esc_html__( 'Note: instead of templates these are the actual titles and meta descriptions for these custom content type archive pages.', 'wordpress-seo' ) . '</p>';
58
- foreach ( $post_types as $post_type ) {
59
- $name = $post_type->name;
60
- echo '<div class="tab-block">';
61
- echo '<h3>' . esc_html( ucfirst( $post_type->labels->name ) ) . '</h3>';
62
-
63
- $custom_post_type_archive_help = $view_utils->search_results_setting_help( $post_type, 'archive' );
64
-
65
- $yform->index_switch(
66
- 'noindex-ptarchive-' . $name,
67
- sprintf(
68
- /* translators: %s expands to the post type's name. */
69
- __( 'the archive for %s', 'wordpress-seo' ),
70
- $post_type->labels->name
71
- ),
72
- $custom_post_type_archive_help->get_button_html() . $custom_post_type_archive_help->get_panel_html()
73
- );
74
-
75
- $yform->textinput( 'title-ptarchive-' . $name, __( 'Title', 'wordpress-seo' ), 'template posttype-template' );
76
- $yform->textarea( 'metadesc-ptarchive-' . $name, __( 'Meta description', 'wordpress-seo' ), array( 'class' => 'template posttype-template' ) );
77
- if ( WPSEO_Options::get( 'breadcrumbs-enable' ) === true ) {
78
- $yform->textinput( 'bctitle-ptarchive-' . $name, __( 'Breadcrumbs title', 'wordpress-seo' ) );
79
- }
80
  echo '</div>';
81
  }
82
- unset( $post_type );
83
  }
84
- unset( $post_types );
<
6
  */
7
 
8
  /**
9
+ * Form object.
10
+ *
11
  * @var Yoast_Form $yform
12
  */
13
 
29
 
30
  $view_utils = new Yoast_View_Utils();
31
 
32
+ echo '<p>';
33
+ esc_html_e( 'The settings on this page allow you to specify what the default search appearance should be for any type of content you have. You can choose which content types appear in search results and what their default description should be.', 'wordpress-seo' );
34
+ echo '</p>';
35
+
36
  if ( is_array( $post_types ) && $post_types !== array() ) {
37
+ foreach ( $post_types as $id => $post_type ) {
38
+ $single_label = $post_type->labels->singular_name;
39
+ $plural_label = $post_type->labels->name;
40
+
41
+ echo '<div class="paper tab-block" id="' . esc_attr( $post_type->name . '-titles-metas' ) . '">';
42
+
43
+ $toggle_icon = 'dashicons-arrow-up-alt2';
44
+ $class = 'toggleable-container';
45
+
46
+ if ( $id !== 'post' ) {
47
+ $toggle_icon = 'dashicons-arrow-down-alt2';
48
+ $class .= ' toggleable-container-hidden';
49
+ }
50
+
51
+ printf(
52
+ '<h2 id="%s">%s (<code>%s</code>) <button class="toggleable-container-trigger"><span class="toggleable-container-icon dashicons %s"></span></button></h2>',
53
+ esc_attr( $post_type->name ),
54
+ esc_html( $plural_label ),
55
+ esc_html( $post_type->name ),
56
+ $toggle_icon
57
+ );
58
+
59
+ echo '<div class="' . $class . '">';
60
+
61
+ // translators: %s is the singular version of the post type's name.
62
+ echo '<h3>' . esc_html( sprintf( __( 'Settings for single %s URLs', 'wordpress-seo' ), $single_label ) ) . '</h3>';
63
+
64
  $view_utils->show_post_type_settings( $post_type );
65
+
66
+ if ( $post_type->has_archive === true ) {
67
+ // translators: %s is the plural version of the post type's name.
68
+ echo '<h3>' . esc_html( sprintf( __( 'Settings for %s archive', 'wordpress-seo' ), $plural_label ) ) . '</h3>';
69
+
70
+ $custom_post_type_archive_help = $view_utils->search_results_setting_help( $post_type, 'archive' );
71
+
72
+ $yform->index_switch(
73
+ 'noindex-ptarchive-' . $post_type->name,
74
+ sprintf(
75
+ /* translators: %s expands to the post type's name. */
76
+ __( 'the archive for %s', 'wordpress-seo' ),
77
+ $plural_label
78
+ ),
79
+ $custom_post_type_archive_help->get_button_html() . $custom_post_type_archive_help->get_panel_html()
80
+ );
81
+
82
+ $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars();
83
+ $page_type = $recommended_replace_vars->determine_for_archive( $post_type->name );
84
+
85
+ $editor = new WPSEO_Replacevar_Editor( $yform, 'title-ptarchive-' . $post_type->name, 'metadesc-ptarchive-' . $post_type->name, $page_type, false );
86
+ $editor->render();
87
+
88
+ if ( WPSEO_Options::get( 'breadcrumbs-enable' ) === true ) {
89
+ // translators: %s is the plural version of the post type's name.
90
+ echo '<h4>' . esc_html( sprintf( __( 'Breadcrumb settings for %s archive', 'wordpress-seo' ), $plural_label ) ) . '</h4>';
91
+ $yform->textinput( 'bctitle-ptarchive-' . $post_type->name, __( 'Breadcrumbs title', 'wordpress-seo' ) );
92
+ }
93
+ }
94
+
95
  /**
96
  * Allow adding a custom checkboxes to the admin meta page - Post Types tab
97
  *
99
  * @api String $name The post type name
100
  */
101
  do_action( 'wpseo_admin_page_meta_post_types', $yform, $post_type->name );
 
 
 
 
 
 
 
 
 
 
 
102
 
103
+ echo '</div>';