PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | inc/class-wpseo-admin-bar-menu.php +486 -161 18.6 → trunk View file →
@@ -4,8 +4,18 @@
4 4 *
5 5 * @package WPSEO
6 6 */
7 7
8 +use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
9 +use Yoast\WP\SEO\Helpers\Product_Helper;
10 +use Yoast\WP\SEO\Helpers\Score_Icon_Helper;
11 +use Yoast\WP\SEO\Integrations\Admin\Brand_Insights_Page;
12 +use Yoast\WP\SEO\Integrations\Support_Integration;
13 +use Yoast\WP\SEO\Models\Indexable;
14 +use Yoast\WP\SEO\Presenters\Admin\Premium_Badge_Presenter;
15 +use Yoast\WP\SEO\Promotions\Application\Promotion_Manager;
16 +use Yoast\WP\SEO\Repositories\Indexable_Repository;
17 +
8 18 /**
9 19 * Class for the Yoast SEO admin bar menu.
10 20 */
11 21 class WPSEO_Admin_Bar_Menu implements WPSEO_WordPress_Integration {
@@ -14,9 +24,9 @@
14 24 * The identifier used for the menu.
15 25 *
16 26 * @var string
17 27 */
18 - const MENU_IDENTIFIER = 'wpseo-menu';
28 + public const MENU_IDENTIFIER = 'wpseo-menu';
19 29
20 30 /**
21 31 * The identifier used for the Keyword Research submenu.
22 32 *
@@ -21,9 +31,9 @@
21 31 * The identifier used for the Keyword Research submenu.
22 32 *
23 33 * @var string
24 34 */
25 - const KEYWORD_RESEARCH_SUBMENU_IDENTIFIER = 'wpseo-kwresearch';
35 + public const KEYWORD_RESEARCH_SUBMENU_IDENTIFIER = 'wpseo-kwresearch';
26 36
27 37 /**
28 38 * The identifier used for the Analysis submenu.
29 39 *
@@ -28,9 +38,9 @@
28 38 * The identifier used for the Analysis submenu.
29 39 *
30 40 * @var string
31 41 */
32 - const ANALYSIS_SUBMENU_IDENTIFIER = 'wpseo-analysis';
42 + public const ANALYSIS_SUBMENU_IDENTIFIER = 'wpseo-analysis';
33 43
34 44 /**
35 45 * The identifier used for the Settings submenu.
36 46 *
@@ -35,9 +45,9 @@
35 45 * The identifier used for the Settings submenu.
36 46 *
37 47 * @var string
38 48 */
39 - const SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-settings';
49 + public const SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-settings';
40 50
41 51 /**
42 52 * The identifier used for the Network Settings submenu.
43 53 *
@@ -42,9 +52,9 @@
42 52 * The identifier used for the Network Settings submenu.
43 53 *
44 54 * @var string
45 55 */
46 - const NETWORK_SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-network-settings';
56 + public const NETWORK_SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-network-settings';
47 57
48 58 /**
49 59 * Asset manager instance.
50 60 *
@@ -52,23 +62,129 @@
52 62 */
53 63 protected $asset_manager;
54 64
55 65 /**
56 - * Constructor.
66 + * Holds the Score_Icon_Helper instance.
57 67 *
58 - * Sets the asset manager to use.
68 + * @var Score_Icon_Helper
69 + */
70 + protected $indexable_repository;
71 +
72 + /**
73 + * Holds the Score_Icon_Helper instance.
59 74 *
60 - * @param WPSEO_Admin_Asset_Manager|null $asset_manager Optional. Asset manager to use.
75 + * @var Score_Icon_Helper
61 76 */
62 - public function __construct( WPSEO_Admin_Asset_Manager $asset_manager = null ) {
77 + protected $score_icon_helper;
78 +
79 + /**
80 + * Holds the Product_Helper instance.
81 + *
82 + * @var Product_Helper
83 + */
84 + protected $product_helper;
85 +
86 + /**
87 + * Holds the shortlinker instance.
88 + *
89 + * @var WPSEO_Shortlinker
90 + */
91 + protected $shortlinker;
92 +
93 + /**
94 + * Whether SEO Score is enabled.
95 + *
96 + * @var bool|null
97 + */
98 + protected $is_seo_enabled = null;
99 +
100 + /**
101 + * Whether readability is enabled.
102 + *
103 + * @var bool|null
104 + */
105 + protected $is_readability_enabled = null;
106 +
107 + /**
108 + * The indexable for the current WordPress page, if found.
109 + *
110 + * @var Indexable|bool|null
111 + */
112 + protected $current_indexable = null;
113 +
114 + /**
115 + * Constructs the WPSEO_Admin_Bar_Menu.
116 + *
117 + * @param WPSEO_Admin_Asset_Manager|null $asset_manager Optional. Asset manager to use.
118 + * @param Indexable_Repository|null $indexable_repository Optional. The Indexable_Repository.
119 + * @param Score_Icon_Helper|null $score_icon_helper Optional. The Score_Icon_Helper.
120 + * @param Product_Helper|null $product_helper Optional. The product helper.
121 + * @param WPSEO_Shortlinker|null $shortlinker The shortlinker.
122 + */
123 + public function __construct(
124 + ?WPSEO_Admin_Asset_Manager $asset_manager = null,
125 + ?Indexable_Repository $indexable_repository = null,
126 + ?Score_Icon_Helper $score_icon_helper = null,
127 + ?Product_Helper $product_helper = null,
128 + ?WPSEO_Shortlinker $shortlinker = null
129 + ) {
63 130 if ( ! $asset_manager ) {
64 131 $asset_manager = new WPSEO_Admin_Asset_Manager();
65 132 }
133 + if ( ! $indexable_repository ) {
134 + $indexable_repository = YoastSEO()->classes->get( Indexable_Repository::class );
135 + }
136 + if ( ! $score_icon_helper ) {
137 + $score_icon_helper = YoastSEO()->helpers->score_icon;
138 + }
139 + if ( ! $product_helper ) {
140 + $product_helper = YoastSEO()->helpers->product;
141 + }
142 + if ( ! $shortlinker ) {
143 + $shortlinker = new WPSEO_Shortlinker();
144 + }
66 145
67 - $this->asset_manager = $asset_manager;
146 + $this->product_helper = $product_helper;
147 + $this->asset_manager = $asset_manager;
148 + $this->indexable_repository = $indexable_repository;
149 + $this->score_icon_helper = $score_icon_helper;
150 + $this->shortlinker = $shortlinker;
68 151 }
69 152
70 153 /**
154 + * Gets whether SEO score is enabled, with cache applied.
155 + *
156 + * @return bool True if SEO score is enabled, false otherwise.
157 + */
158 + protected function get_is_seo_enabled() {
159 + $this->is_seo_enabled ??= ( new WPSEO_Metabox_Analysis_SEO() )->is_enabled();
160 +
161 + return $this->is_seo_enabled;
162 + }
163 +
164 + /**
165 + * Gets whether readability is enabled, with cache applied.
166 + *
167 + * @return bool True if readability is enabled, false otherwise.
168 + */
169 + protected function get_is_readability_enabled() {
170 + $this->is_readability_enabled ??= ( new WPSEO_Metabox_Analysis_Readability() )->is_enabled();
171 +
172 + return $this->is_readability_enabled;
173 + }
174 +
175 + /**
176 + * Returns the indexable for the current WordPress page, with cache applied.
177 + *
178 + * @return bool|Indexable The indexable, false if none could be found.
179 + */
180 + protected function get_current_indexable() {
181 + $this->current_indexable ??= $this->indexable_repository->for_current_page();
182 +
183 + return $this->current_indexable;
184 + }
185 +
186 + /**
71 187 * Adds the admin bar menu.
72 188 *
73 189 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
74 190 *
@@ -74,9 +190,8 @@
74 190 *
75 191 * @return void
76 192 */
77 193 public function add_menu( WP_Admin_Bar $wp_admin_bar ) {
78 -
79 194 // On block editor pages, the admin bar only shows on mobile, where having this menu icon is not very helpful.
80 195 if ( is_admin() ) {
81 196 $screen = get_current_screen();
82 197 if ( isset( $screen ) && $screen->is_block_editor() ) {
@@ -89,12 +204,78 @@
89 204 return;
90 205 }
91 206
92 207 $this->add_root_menu( $wp_admin_bar );
93 - $this->add_keyword_research_submenu( $wp_admin_bar );
94 208
209 + /**
210 + * Adds a submenu item in the top of the adminbar.
211 + *
212 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
213 + * @param string $menu_identifier The menu identifier.
214 + */
215 + do_action( 'wpseo_add_adminbar_submenu', $wp_admin_bar, self::MENU_IDENTIFIER );
216 +
95 217 if ( ! is_admin() ) {
218 +
219 + if ( is_singular() || is_tag() || is_tax() || is_category() ) {
220 + $is_seo_enabled = $this->get_is_seo_enabled();
221 + $is_readability_enabled = $this->get_is_readability_enabled();
222 +
223 + $indexable = $this->get_current_indexable();
224 +
225 + if ( $is_seo_enabled ) {
226 + $focus_keyword = ( ! is_a( $indexable, 'Yoast\WP\SEO\Models\Indexable' ) || $indexable->primary_focus_keyword === null ) ? __( 'not set', 'wordpress-seo' ) : $indexable->primary_focus_keyword;
227 +
228 + $wp_admin_bar->add_menu(
229 + [
230 + 'parent' => self::MENU_IDENTIFIER,
231 + 'id' => 'wpseo-seo-focus-keyword',
232 + 'title' => __( 'Focus keyphrase: ', 'wordpress-seo' ) . '<span class="wpseo-focus-keyword">' . $focus_keyword . '</span>',
233 + 'meta' => [ 'tabindex' => '0' ],
234 + ],
235 + );
236 + $wp_admin_bar->add_menu(
237 + [
238 + 'parent' => self::MENU_IDENTIFIER,
239 + 'id' => 'wpseo-seo-score',
240 + 'title' => __( 'SEO score', 'wordpress-seo' ) . ': ' . $this->score_icon_helper->for_seo( $indexable, 'adminbar-sub-menu-score' )
241 + ->present(),
242 + 'meta' => [ 'tabindex' => '0' ],
243 + ],
244 + );
245 + }
246 +
247 + if ( $is_readability_enabled ) {
248 + $wp_admin_bar->add_menu(
249 + [
250 + 'parent' => self::MENU_IDENTIFIER,
251 + 'id' => 'wpseo-readability-score',
252 + 'title' => __( 'Readability', 'wordpress-seo' ) . ': ' . $this->score_icon_helper->for_readability( $indexable->readability_score, 'adminbar-sub-menu-score' )
253 + ->present(),
254 + 'meta' => [ 'tabindex' => '0' ],
255 + ],
256 + );
257 + }
258 +
259 + if ( ! $this->product_helper->is_premium() ) {
260 + $wp_admin_bar->add_menu(
261 + [
262 + 'parent' => self::MENU_IDENTIFIER,
263 + 'id' => 'wpseo-frontend-inspector',
264 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-frontend-inspector' ),
265 + 'title' => __( 'Front-end SEO inspector', 'wordpress-seo' ) . new Premium_Badge_Presenter( 'wpseo-frontend-inspector-badge' ),
266 + 'meta' => [
267 + 'tabindex' => '0',
268 + 'target' => '_blank',
269 + ],
270 + ],
271 + );
272 + }
273 + }
96 274 $this->add_analysis_submenu( $wp_admin_bar );
275 + $this->add_seo_tools_submenu( $wp_admin_bar );
276 + $this->add_how_to_submenu( $wp_admin_bar );
277 + $this->add_get_help_submenu( $wp_admin_bar );
97 278 }
98 279
99 280 if ( ! is_admin() || is_blog_admin() ) {
100 281 $this->add_settings_submenu( $wp_admin_bar );
@@ -101,8 +282,12 @@
101 282 }
102 283 elseif ( is_network_admin() ) {
103 284 $this->add_network_settings_submenu( $wp_admin_bar );
104 285 }
286 +
287 + $this->add_academy_link( $wp_admin_bar );
288 + $this->add_premium_link( $wp_admin_bar );
289 + $this->add_brand_insights_link( $wp_admin_bar );
105 290 }
106 291
107 292 /**
108 293 * Enqueues admin bar assets.
@@ -169,8 +354,9 @@
169 354 $score = '';
170 355 $settings_url = '';
171 356 $counter = '';
172 357 $notification_popup = '';
358 + $notification_count = 0;
173 359
174 360 $post = $this->get_singular_post();
175 361 if ( $post ) {
176 362 $score = $this->get_post_score( $post );
@@ -187,9 +373,12 @@
187 373 $settings_url = $this->get_settings_page_url();
188 374 }
189 375
190 376 if ( empty( $score ) && ! is_network_admin() && $can_manage_options ) {
191 - $counter = $this->get_notification_counter();
377 + $notification_center = Yoast_Notification_Center::get();
378 + $notification_count = $notification_center->get_notification_count();
379 +
380 + $counter = $this->get_notification_counter( $notification_count );
192 381 $notification_popup = $this->get_notification_popup();
193 382 }
194 383
195 384 $admin_bar_menu_args = [
@@ -199,14 +388,14 @@
199 388 'meta' => [ 'tabindex' => ! empty( $settings_url ) ? false : '0' ],
200 389 ];
201 390 $wp_admin_bar->add_menu( $admin_bar_menu_args );
202 391
203 - if ( ! empty( $counter ) ) {
392 + if ( $notification_count > 0 ) {
204 393 $admin_bar_menu_args = [
205 394 'parent' => self::MENU_IDENTIFIER,
206 395 'id' => 'wpseo-notifications',
207 396 'title' => __( 'Notifications', 'wordpress-seo' ) . $counter,
208 - 'href' => $settings_url,
397 + 'href' => empty( $settings_url ) ? '' : $settings_url . '#/alert-center',
209 398 'meta' => [ 'tabindex' => ! empty( $settings_url ) ? false : '0' ],
210 399 ];
211 400 $wp_admin_bar->add_menu( $admin_bar_menu_args );
212 401 }
@@ -212,164 +401,284 @@
212 401 }
213 402 }
214 403
215 404 /**
216 - * Adds the admin bar keyword research submenu.
405 + * Adds the admin bar analysis submenu.
217 406 *
218 407 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
219 408 *
220 409 * @return void
221 410 */
222 - protected function add_keyword_research_submenu( WP_Admin_Bar $wp_admin_bar ) {
223 - $adwords_url = 'https://yoa.st/keywordplanner';
224 - $trends_url = 'https://yoa.st/google-trends';
411 + protected function add_analysis_submenu( WP_Admin_Bar $wp_admin_bar ) {
412 + try {
413 + $url = YoastSEO()->meta->for_current_page()->canonical;
414 + } catch ( Exception $e ) {
415 + // This is not the type of error we can handle here.
416 + return;
417 + }
225 418
226 - $post = $this->get_singular_post();
227 - if ( $post ) {
228 - $focus_keyword = $this->get_post_focus_keyword( $post );
229 -
230 - if ( ! empty( $focus_keyword ) ) {
231 - $trends_url .= '#q=' . urlencode( $focus_keyword );
232 - }
419 + if ( ! $url ) {
420 + return;
233 421 }
234 422
235 423 $menu_args = [
236 424 'parent' => self::MENU_IDENTIFIER,
237 - 'id' => self::KEYWORD_RESEARCH_SUBMENU_IDENTIFIER,
238 - 'title' => __( 'Keyword Research', 'wordpress-seo' ),
425 + 'id' => self::ANALYSIS_SUBMENU_IDENTIFIER,
426 + 'title' => __( 'Analyze this page', 'wordpress-seo' ),
239 427 'meta' => [ 'tabindex' => '0' ],
240 428 ];
241 429 $wp_admin_bar->add_menu( $menu_args );
242 430
431 + $encoded_url = rawurlencode( $url );
243 432 $submenu_items = [
244 433 [
245 - 'id' => 'wpseo-kwresearchtraining',
246 - 'title' => __( 'Keyword research training', 'wordpress-seo' ),
247 - 'href' => WPSEO_Shortlinker::get( 'https://yoa.st/wp-admin-bar' ),
434 + 'id' => 'wpseo-inlinks',
435 + 'title' => __( 'Check links to this URL', 'wordpress-seo' ),
436 + 'href' => 'https://search.google.com/search-console/links/drilldown?resource_id=' . rawurlencode( get_option( 'siteurl' ) ) . '&type=EXTERNAL&target=' . $encoded_url . '&domain=',
248 437 ],
249 438 [
250 - 'id' => 'wpseo-adwordsexternal',
251 - 'title' => __( 'Google Ads', 'wordpress-seo' ),
252 - 'href' => $adwords_url,
439 + 'id' => 'wpseo-structureddata',
440 + 'title' => __( 'Google Rich Results Test', 'wordpress-seo' ),
441 + 'href' => 'https://search.google.com/test/rich-results?url=' . $encoded_url,
253 442 ],
254 443 [
255 - 'id' => 'wpseo-googleinsights',
256 - 'title' => __( 'Google Trends', 'wordpress-seo' ),
257 - 'href' => $trends_url,
444 + 'id' => 'wpseo-facebookdebug',
445 + 'title' => __( 'Facebook Debugger', 'wordpress-seo' ),
446 + 'href' => '//developers.facebook.com/tools/debug/?q=' . $encoded_url,
258 447 ],
448 + [
449 + 'id' => 'wpseo-pagespeed',
450 + 'title' => __( 'Google Page Speed Test', 'wordpress-seo' ),
451 + 'href' => '//developers.google.com/speed/pagespeed/insights/?url=' . $encoded_url,
452 + ],
259 453 ];
260 454
261 - foreach ( $submenu_items as $menu_item ) {
262 - $menu_args = [
263 - 'parent' => self::KEYWORD_RESEARCH_SUBMENU_IDENTIFIER,
264 - 'id' => $menu_item['id'],
265 - 'title' => $menu_item['title'],
266 - 'href' => $menu_item['href'],
267 - 'meta' => [ 'target' => '_blank' ],
268 - ];
269 - $wp_admin_bar->add_menu( $menu_args );
270 - }
455 + $this->add_submenu_items( $submenu_items, $wp_admin_bar, self::ANALYSIS_SUBMENU_IDENTIFIER );
271 456 }
272 457
273 458 /**
274 - * Adds the admin bar analysis submenu.
459 + * Adds the admin bar tools submenu.
275 460 *
276 461 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
277 462 *
278 463 * @return void
279 464 */
280 - protected function add_analysis_submenu( WP_Admin_Bar $wp_admin_bar ) {
281 - try {
282 - $url = YoastSEO()->meta->for_current_page()->canonical;
283 - } catch ( Exception $e ) {
284 - // This is not the type of error we can handle here.
285 - return;
286 - }
465 + protected function add_seo_tools_submenu( WP_Admin_Bar $wp_admin_bar ) {
466 + $menu_args = [
467 + 'parent' => self::MENU_IDENTIFIER,
468 + 'id' => 'wpseo-sub-tools',
469 + 'title' => __( 'SEO Tools', 'wordpress-seo' ),
470 + 'meta' => [ 'tabindex' => '0' ],
471 + ];
472 + $wp_admin_bar->add_menu( $menu_args );
287 473
288 - $focus_keyword = '';
474 + $submenu_items = [
475 + [
476 + 'id' => 'wpseo-semrush',
477 + 'title' => 'Semrush',
478 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-semrush' ),
479 + ],
480 + [
481 + 'id' => 'wpseo-wincher',
482 + 'title' => 'Wincher',
483 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-wincher' ),
484 + ],
485 + [
486 + 'id' => 'wpseo-google-trends',
487 + 'title' => 'Google trends',
488 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-gtrends' ),
489 + ],
490 + ];
289 491
290 - if ( ! $url ) {
291 - return;
292 - }
492 + $this->add_submenu_items( $submenu_items, $wp_admin_bar, 'wpseo-sub-tools' );
493 + }
293 494
294 - $post = $this->get_singular_post();
295 - if ( $post ) {
296 - $focus_keyword = $this->get_post_focus_keyword( $post );
297 - }
298 -
495 + /**
496 + * Adds the admin bar How To submenu.
497 + *
498 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
499 + *
500 + * @return void
501 + */
502 + protected function add_how_to_submenu( WP_Admin_Bar $wp_admin_bar ) {
299 503 $menu_args = [
300 504 'parent' => self::MENU_IDENTIFIER,
301 - 'id' => self::ANALYSIS_SUBMENU_IDENTIFIER,
302 - 'title' => __( 'Analyze this page', 'wordpress-seo' ),
505 + 'id' => 'wpseo-sub-howto',
506 + 'title' => __( 'How to', 'wordpress-seo' ),
303 507 'meta' => [ 'tabindex' => '0' ],
304 508 ];
305 509 $wp_admin_bar->add_menu( $menu_args );
306 510
307 - $encoded_url = urlencode( $url );
308 511 $submenu_items = [
309 512 [
310 - 'id' => 'wpseo-inlinks',
311 - 'title' => __( 'Check links to this URL', 'wordpress-seo' ),
312 - 'href' => 'https://search.google.com/search-console/links/drilldown?resource_id=' . urlencode( get_option( 'siteurl' ) ) . '&type=EXTERNAL&target=' . $encoded_url . '&domain=',
513 + 'id' => 'wpseo-learn-seo',
514 + 'title' => __( 'Learn more SEO', 'wordpress-seo' ),
515 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-learn-more-seo' ),
313 516 ],
314 517 [
315 - 'id' => 'wpseo-kwdensity',
316 - 'title' => __( 'Check Keyphrase Density', 'wordpress-seo' ),
317 - // HTTPS not available.
318 - 'href' => 'http://www.zippy.co.uk/keyworddensity/index.php?url=' . $encoded_url . '&keyword=' . urlencode( $focus_keyword ),
518 + 'id' => 'wpseo-improve-blogpost',
519 + 'title' => __( 'Improve your blog post', 'wordpress-seo' ),
520 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-improve-blog-post' ),
319 521 ],
320 522 [
321 - 'id' => 'wpseo-cache',
322 - 'title' => __( 'Check Google Cache', 'wordpress-seo' ),
323 - 'href' => '//webcache.googleusercontent.com/search?strip=1&q=cache:' . $encoded_url,
523 + 'id' => 'wpseo-write-better-content',
524 + 'title' => __( 'Write better content', 'wordpress-seo' ),
525 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-write-better' ),
324 526 ],
527 + ];
528 +
529 + $this->add_submenu_items( $submenu_items, $wp_admin_bar, 'wpseo-sub-howto' );
530 + }
531 +
532 + /**
533 + * Adds the admin bar How To submenu.
534 + *
535 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
536 + *
537 + * @return void
538 + */
539 + protected function add_get_help_submenu( WP_Admin_Bar $wp_admin_bar ) {
540 + $menu_args = [
541 + 'parent' => self::MENU_IDENTIFIER,
542 + 'id' => 'wpseo-sub-get-help',
543 + 'title' => __( 'Help', 'wordpress-seo' ),
544 + 'meta' => [ 'tabindex' => '0' ],
545 + ];
546 +
547 + if ( current_user_can( Support_Integration::CAPABILITY ) ) {
548 + $menu_args['href'] = admin_url( 'admin.php?page=' . Support_Integration::PAGE );
549 + $wp_admin_bar->add_menu( $menu_args );
550 +
551 + return;
552 + }
553 + $wp_admin_bar->add_menu( $menu_args );
554 +
555 + $submenu_items = [
325 556 [
326 - 'id' => 'wpseo-structureddata',
327 - 'title' => __( 'Google Rich Results Test', 'wordpress-seo' ),
328 - 'href' => 'https://search.google.com/test/rich-results?url=' . $encoded_url,
557 + 'id' => 'wpseo-yoast-help',
558 + 'title' => __( 'Yoast.com help section', 'wordpress-seo' ),
559 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-yoast-help' ),
329 560 ],
330 561 [
331 - 'id' => 'wpseo-facebookdebug',
332 - 'title' => __( 'Facebook Debugger', 'wordpress-seo' ),
333 - 'href' => '//developers.facebook.com/tools/debug/?q=' . $encoded_url,
562 + 'id' => 'wpseo-premium-support',
563 + 'title' => __( 'Yoast Premium support', 'wordpress-seo' ),
564 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-premium-support' ),
334 565 ],
335 566 [
336 - 'id' => 'wpseo-pinterestvalidator',
337 - 'title' => __( 'Pinterest Rich Pins Validator', 'wordpress-seo' ),
338 - 'href' => 'https://developers.pinterest.com/tools/url-debugger/?link=' . $encoded_url,
567 + 'id' => 'wpseo-wp-support-forums',
568 + 'title' => __( 'WordPress.org support forums', 'wordpress-seo' ),
569 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-wp-support-forums' ),
339 570 ],
340 571 [
341 - 'id' => 'wpseo-htmlvalidation',
342 - 'title' => __( 'HTML Validator', 'wordpress-seo' ),
343 - 'href' => '//validator.w3.org/check?uri=' . $encoded_url,
572 + 'id' => 'wpseo-learn-seo-2',
573 + 'title' => __( 'Learn more SEO', 'wordpress-seo' ),
574 + 'href' => $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-learn-more-seo-help' ),
344 575 ],
576 + ];
577 +
578 + $this->add_submenu_items( $submenu_items, $wp_admin_bar, 'wpseo-sub-get-help' );
579 + }
580 +
581 + /**
582 + * Adds the Academy link to the admin bar.
583 + *
584 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
585 + *
586 + * @return void
587 + */
588 + protected function add_academy_link( WP_Admin_Bar $wp_admin_bar ) {
589 + $wp_admin_bar->add_menu(
345 590 [
346 - 'id' => 'wpseo-cssvalidation',
347 - 'title' => __( 'CSS Validator', 'wordpress-seo' ),
348 - 'href' => '//jigsaw.w3.org/css-validator/validator?uri=' . $encoded_url,
591 + 'parent' => self::MENU_IDENTIFIER,
592 + 'id' => 'wpseo-academy',
593 + 'title' => __( 'Academy', 'wordpress-seo' ),
594 + 'href' => admin_url( 'admin.php?page=wpseo_page_academy' ),
595 + 'meta' => [
596 + 'tabindex' => '0',
597 + ],
349 598 ],
599 + );
600 + }
601 +
602 + /**
603 + * Adds the Upgrade link to the admin bar.
604 + *
605 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
606 + *
607 + * @return void
608 + */
609 + protected function add_premium_link( WP_Admin_Bar $wp_admin_bar ) {
610 + // Don't show the Upgrade button if Yoast SEO WooCommerce addon is active.
611 + $addon_manager = new WPSEO_Addon_Manager();
612 + if ( $addon_manager->is_installed( WPSEO_Addon_Manager::WOOCOMMERCE_SLUG ) ) {
613 + return;
614 + }
615 +
616 + $has_woocommerce = ( new Woocommerce_Conditional() )->is_met();
617 +
618 + // Don't show the Upgrade button if Premium is active without the WooCommerce plugin.
619 + if ( $this->product_helper->is_premium() && ! $has_woocommerce ) {
620 + return;
621 + }
622 +
623 + $link = $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-get-premium' );
624 +
625 + if ( $has_woocommerce ) {
626 + $link = $this->shortlinker->build_shortlink( 'https://yoa.st/admin-bar-get-premium-woocommerce' );
627 + }
628 +
629 + $button_label = esc_html__( 'Upgrade', 'wordpress-seo' );
630 +
631 + if ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-promotion' ) ) {
632 + $button_label = esc_html__( '30% off - BF Sale', 'wordpress-seo' );
633 + }
634 + $wp_admin_bar->add_menu(
350 635 [
351 - 'id' => 'wpseo-pagespeed',
352 - 'title' => __( 'Google Page Speed Test', 'wordpress-seo' ),
353 - 'href' => '//developers.google.com/speed/pagespeed/insights/?url=' . $encoded_url,
636 + 'parent' => self::MENU_IDENTIFIER,
637 + 'id' => 'wpseo-get-premium',
638 + // Circumvent an issue in the WP admin bar API in order to pass `data` attributes. See https://core.trac.wordpress.org/ticket/38636.
639 + 'title' => sprintf(
640 + '<a href="%1$s" target="_blank" data-action="load-nfd-ctb" data-ctb-id="f6a84663-465f-4cb5-8ba5-f7a6d72224b2">%2$s</a>',
641 + esc_url( $link ),
642 + $button_label,
643 + ),
644 + 'meta' => [
645 + 'tabindex' => '0',
646 + ],
354 647 ],
648 + );
649 + }
650 +
651 + /**
652 + * Adds the Brand Insights link to the admin bar.
653 + *
654 + * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
655 + *
656 + * @return void
657 + */
658 + protected function add_brand_insights_link( WP_Admin_Bar $wp_admin_bar ) {
659 + $page = $this->product_helper->is_premium() ? 'wpseo_brand_insights_premium' : 'wpseo_brand_insights';
660 +
661 + $button_content = 'AI Brand Insights';
662 +
663 + $menu_title = '<span class="yoast-brand-insights-gradient-border">'
664 + . '<span class="yoast-brand-insights-content">'
665 + . $button_content
666 + . Brand_Insights_Page::EXTERNAL_LINK_ICON
667 + . '</span></span>';
668 +
669 + $wp_admin_bar->add_menu(
355 670 [
356 - 'id' => 'wpseo-google-mobile-friendly',
357 - 'title' => __( 'Mobile-Friendly Test', 'wordpress-seo' ),
358 - 'href' => 'https://www.google.com/webmasters/tools/mobile-friendly/?url=' . $encoded_url,
671 + 'parent' => self::MENU_IDENTIFIER,
672 + 'id' => $page,
673 + 'title' => $menu_title,
674 + 'href' => admin_url( 'admin.php?page=' . $page ),
675 + 'meta' => [
676 + 'tabindex' => '0',
677 + 'target' => '_blank',
678 + ],
359 679 ],
360 - ];
361 -
362 - foreach ( $submenu_items as $menu_item ) {
363 - $menu_args = [
364 - 'parent' => self::ANALYSIS_SUBMENU_IDENTIFIER,
365 - 'id' => $menu_item['id'],
366 - 'title' => $menu_item['title'],
367 - 'href' => $menu_item['href'],
368 - 'meta' => [ 'target' => '_blank' ],
369 - ];
370 - $wp_admin_bar->add_menu( $menu_args );
371 - }
680 + );
372 681 }
373 682
374 683 /**
375 684 * Adds the admin bar settings submenu.
@@ -403,8 +712,18 @@
403 712 if ( $submenu_page[4] === 'wpseo_search_console' ) {
404 713 continue;
405 714 }
406 715
716 + // Don't add the Academy menu item (it's now in the main menu).
717 + if ( $submenu_page[4] === 'wpseo_page_academy' ) {
718 + continue;
719 + }
720 +
721 + // Don't add the Brand Insights menu items (they're now in the main menu).
722 + if ( $submenu_page[4] === 'wpseo_brand_insights' || $submenu_page[4] === 'wpseo_brand_insights_premium' ) {
723 + continue;
724 + }
725 +
407 726 $id = 'wpseo-' . str_replace( '_', '-', str_replace( 'wpseo_', '', $submenu_page[4] ) );
408 727 if ( $id === 'wpseo-dashboard' ) {
409 728 $id = 'wpseo-general';
410 729 }
@@ -412,9 +731,9 @@
412 731 $menu_args = [
413 732 'parent' => self::SETTINGS_SUBMENU_IDENTIFIER,
414 733 'id' => $id,
415 734 'title' => $submenu_page[2],
416 - 'href' => admin_url( 'admin.php?page=' . urlencode( $submenu_page[4] ) ),
735 + 'href' => admin_url( 'admin.php?page=' . rawurlencode( $submenu_page[4] ) ),
417 736 ];
418 737 $wp_admin_bar->add_menu( $menu_args );
419 738 }
420 739 }
@@ -455,9 +774,9 @@
455 774 $menu_args = [
456 775 'parent' => self::NETWORK_SETTINGS_SUBMENU_IDENTIFIER,
457 776 'id' => $id,
458 777 'title' => $submenu_page[2],
459 - 'href' => network_admin_url( 'admin.php?page=' . urlencode( $submenu_page[4] ) ),
778 + 'href' => network_admin_url( 'admin.php?page=' . rawurlencode( $submenu_page[4] ) ),
460 779 ];
461 780 $wp_admin_bar->add_menu( $menu_args );
462 781 }
463 782 }
@@ -507,9 +826,9 @@
507 826
508 827 /**
509 828 * Filter: 'wpseo_use_page_analysis' Determines if the analysis should be enabled.
510 829 *
511 - * @api bool Determines if the analysis should be enabled.
830 + * @param bool $enabled Determines if the analysis should be enabled.
512 831 */
513 832 if ( apply_filters( 'wpseo_use_page_analysis', true ) !== true ) {
514 833 return '';
515 834 }
@@ -532,20 +851,9 @@
532 851 if ( apply_filters( 'wpseo_use_page_analysis', true ) !== true ) {
533 852 return '';
534 853 }
535 854
536 - $analysis_seo = new WPSEO_Metabox_Analysis_SEO();
537 - $analysis_readability = new WPSEO_Metabox_Analysis_Readability();
538 -
539 - if ( $analysis_seo->is_enabled() ) {
540 - return $this->get_score( WPSEO_Meta::get_value( 'linkdex', $post->ID ) );
541 - }
542 -
543 - if ( $analysis_readability->is_enabled() ) {
544 - return $this->get_score( WPSEO_Meta::get_value( 'content_score', $post->ID ) );
545 - }
546 -
547 - return '';
855 + return $this->get_score_icon();
548 856 }
549 857
550 858 /**
551 859 * Gets the current term if in a singular term context.
@@ -581,38 +889,32 @@
581 889 if ( ! is_object( $term ) || ! property_exists( $term, 'term_id' ) || ! property_exists( $term, 'taxonomy' ) ) {
582 890 return '';
583 891 }
584 892
585 - $analysis_seo = new WPSEO_Metabox_Analysis_SEO();
586 - $analysis_readability = new WPSEO_Metabox_Analysis_Readability();
587 -
588 - if ( $analysis_seo->is_enabled() ) {
589 - return $this->get_score( WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $term->taxonomy, 'linkdex' ) );
590 - }
591 -
592 - if ( $analysis_readability->is_enabled() ) {
593 - return $this->get_score( WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $term->taxonomy, 'content_score' ) );
594 - }
595 -
596 - return '';
893 + return $this->get_score_icon();
597 894 }
598 895
599 896 /**
600 - * Takes the SEO score and makes the score icon for the admin bar for it.
897 + * Create the score icon.
601 898 *
602 - * @param int $score The 0-100 rating of the score. Can be either SEO score or content score.
603 - *
604 - * @return string Score markup.
899 + * @return string The score icon, or empty string.
605 900 */
606 - protected function get_score( $score ) {
607 - $score_class = WPSEO_Utils::translate_score( $score );
608 - $translated_score = WPSEO_Utils::translate_score( $score, false );
609 - /* translators: %s expands to the SEO score. */
610 - $screen_reader_text = sprintf( __( 'SEO score: %s', 'wordpress-seo' ), $translated_score );
901 + protected function get_score_icon() {
902 + $is_seo_enabled = $this->get_is_seo_enabled();
903 + $is_readability_enabled = $this->get_is_readability_enabled();
611 904
612 - $score_adminbar_element = '<div class="wpseo-score-icon adminbar-seo-score ' . $score_class . '"><span class="adminbar-seo-score-text screen-reader-text">' . $screen_reader_text . '</span></div>';
905 + $indexable = $this->get_current_indexable();
613 906
614 - return $score_adminbar_element;
907 + if ( $is_seo_enabled ) {
908 + return $this->score_icon_helper->for_seo( $indexable, 'adminbar-seo-score' )->present();
909 + }
910 +
911 + if ( $is_readability_enabled ) {
912 + return $this->score_icon_helper->for_readability( $indexable->readability_score, 'adminbar-seo-score' )
913 + ->present();
914 + }
915 +
916 + return '';
615 917 }
616 918
617 919 /**
618 920 * Gets the URL to the main admin settings page.
@@ -625,22 +927,22 @@
625 927
626 928 /**
627 929 * Gets the notification counter if in a valid context.
628 930 *
931 + * @param int $notification_count Number of notifications.
932 + *
629 933 * @return string Notification counter markup, or empty string if not available.
630 934 */
631 - protected function get_notification_counter() {
632 - $notification_center = Yoast_Notification_Center::get();
633 - $notification_count = $notification_center->get_notification_count();
634 -
635 - if ( ! $notification_count ) {
636 - return '';
637 - }
638 -
639 - /* translators: %s: number of notifications */
935 + protected function get_notification_counter( $notification_count ) {
936 + /* translators: Hidden accessibility text; %s: number of notifications. */
640 937 $counter_screen_reader_text = sprintf( _n( '%s notification', '%s notifications', $notification_count, 'wordpress-seo' ), number_format_i18n( $notification_count ) );
641 938
642 - return sprintf( ' <div class="wp-core-ui wp-ui-notification yoast-issue-counter"><span aria-hidden="true">%d</span><span class="screen-reader-text">%s</span></div>', $notification_count, $counter_screen_reader_text );
939 + return sprintf(
940 + ' <div class="wp-core-ui wp-ui-notification yoast-issue-counter%s"><span class="yoast-issues-count" aria-hidden="true">%d</span><span class="screen-reader-text">%s</span></div>',
941 + ( $notification_count ) ? '' : ' wpseo-no-adminbar-notifications',
942 + $notification_count,
943 + $counter_screen_reader_text,
944 + );
643 945 }
644 946
645 947 /**
646 948 * Gets the notification popup if in a valid context.
@@ -660,11 +962,11 @@
660 962 _n(
661 963 'There is a new notification.',
662 964 'There are new notifications.',
663 965 $new_notifications_count,
664 - 'wordpress-seo'
966 + 'wordpress-seo',
665 967 ),
666 - $new_notifications_count
968 + $new_notifications_count,
667 969 );
668 970
669 971 return '<div class="yoast-issue-added">' . $notification . '</div>';
670 972 }
@@ -674,7 +976,30 @@
674 976 *
675 977 * @return bool True if capabilities are sufficient, false otherwise.
676 978 */
677 979 protected function can_manage_options() {
678 - return is_network_admin() && current_user_can( 'wpseo_manage_network_options' ) || ! is_network_admin() && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' );
980 + return ( is_network_admin() && current_user_can( 'wpseo_manage_network_options' ) )
981 + || ( ! is_network_admin() && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) );
982 + }
983 +
984 + /**
985 + * Add submenu items to a menu item.
986 + *
987 + * @param array<array{id: string, title: string, href: string}> $submenu_items Submenu items array.
988 + * @param WP_Admin_Bar $wp_admin_bar Admin bar object.
989 + * @param string $parent_id Parent menu item ID.
990 + *
991 + * @return void
992 + */
993 + protected function add_submenu_items( array $submenu_items, WP_Admin_Bar $wp_admin_bar, $parent_id ) {
994 + foreach ( $submenu_items as $menu_item ) {
995 + $menu_args = [
996 + 'parent' => $parent_id,
997 + 'id' => $menu_item['id'],
998 + 'title' => $menu_item['title'],
999 + 'href' => $menu_item['href'],
1000 + 'meta' => [ 'target' => '_blank' ],
1001 + ];
1002 + $wp_admin_bar->add_menu( $menu_args );
1003 + }
679 1004 }
680 1005 }