PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.2
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.2
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
wordpress-seo / inc / class-wpseo-admin-bar-menu.php

class-wpseo-admin-bar-menu.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.2, at inc/class-wpseo-admin-bar-menu.php

1,006 lines 28.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO
6 */
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
18 /**
19 * Class for the Yoast SEO admin bar menu.
20 */
21 class WPSEO_Admin_Bar_Menu implements WPSEO_WordPress_Integration {
22
23 /**
24 * The identifier used for the menu.
25 *
26 * @var string
27 */
28 public const MENU_IDENTIFIER = 'wpseo-menu';
29
30 /**
31 * The identifier used for the Keyword Research submenu.
32 *
33 * @var string
34 */
35 public const KEYWORD_RESEARCH_SUBMENU_IDENTIFIER = 'wpseo-kwresearch';
36
37 /**
38 * The identifier used for the Analysis submenu.
39 *
40 * @var string
41 */
42 public const ANALYSIS_SUBMENU_IDENTIFIER = 'wpseo-analysis';
43
44 /**
45 * The identifier used for the Settings submenu.
46 *
47 * @var string
48 */
49 public const SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-settings';
50
51 /**
52 * The identifier used for the Network Settings submenu.
53 *
54 * @var string
55 */
56 public const NETWORK_SETTINGS_SUBMENU_IDENTIFIER = 'wpseo-network-settings';
57
58 /**
59 * Asset manager instance.
60 *
61 * @var WPSEO_Admin_Asset_Manager
62 */
63 protected $asset_manager;
64
65 /**
66 * Holds the Score_Icon_Helper instance.
67 *
68 * @var Score_Icon_Helper
69 */
70 protected $indexable_repository;
71
72 /**
73 * Holds the Score_Icon_Helper instance.
74 *
75 * @var Score_Icon_Helper
76 */
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 ) {
130 if ( ! $asset_manager ) {
131 $asset_manager = new WPSEO_Admin_Asset_Manager();
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 }
145
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;
151 }
152
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 /**
187 * Adds the admin bar menu.
188 *
189 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
190 *
191 * @return void
192 */
193 public function add_menu( WP_Admin_Bar $wp_admin_bar ) {
194 // On block editor pages, the admin bar only shows on mobile, where having this menu icon is not very helpful.
195 if ( is_admin() ) {
196 $screen = get_current_screen();
197 if ( isset( $screen ) && $screen->is_block_editor() ) {
198 return;
199 }
200 }
201
202 // If the current user can't write posts, this is all of no use, so let's not output an admin menu.
203 if ( ! current_user_can( 'edit_posts' ) ) {
204 return;
205 }
206
207 $this->add_root_menu( $wp_admin_bar );
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
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 }
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 );
278 }
279
280 if ( ! is_admin() || is_blog_admin() ) {
281 $this->add_settings_submenu( $wp_admin_bar );
282 }
283 elseif ( is_network_admin() ) {
284 $this->add_network_settings_submenu( $wp_admin_bar );
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 );
290 }
291
292 /**
293 * Enqueues admin bar assets.
294 *
295 * @return void
296 */
297 public function enqueue_assets() {
298 if ( ! is_admin_bar_showing() ) {
299 return;
300 }
301
302 // If the current user can't write posts, this is all of no use, so let's not output an admin menu.
303 if ( ! current_user_can( 'edit_posts' ) ) {
304 return;
305 }
306
307 $this->asset_manager->register_assets();
308 $this->asset_manager->enqueue_style( 'adminbar' );
309 }
310
311 /**
312 * Registers the hooks.
313 *
314 * @return void
315 */
316 public function register_hooks() {
317 if ( ! $this->meets_requirements() ) {
318 return;
319 }
320
321 add_action( 'admin_bar_menu', [ $this, 'add_menu' ], 95 );
322
323 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
324 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
325 }
326
327 /**
328 * Checks whether the requirements to use this class are met.
329 *
330 * @return bool True if requirements are met, false otherwise.
331 */
332 public function meets_requirements() {
333 if ( is_network_admin() ) {
334 return WPSEO_Utils::is_plugin_network_active();
335 }
336
337 if ( WPSEO_Options::get( 'enable_admin_bar_menu' ) !== true ) {
338 return false;
339 }
340
341 return ! is_admin() || is_blog_admin();
342 }
343
344 /**
345 * Adds the admin bar root menu.
346 *
347 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
348 *
349 * @return void
350 */
351 protected function add_root_menu( WP_Admin_Bar $wp_admin_bar ) {
352 $title = $this->get_title();
353
354 $score = '';
355 $settings_url = '';
356 $counter = '';
357 $notification_popup = '';
358 $notification_count = 0;
359
360 $post = $this->get_singular_post();
361 if ( $post ) {
362 $score = $this->get_post_score( $post );
363 }
364
365 $term = $this->get_singular_term();
366 if ( $term ) {
367 $score = $this->get_term_score( $term );
368 }
369
370 $can_manage_options = $this->can_manage_options();
371
372 if ( $can_manage_options ) {
373 $settings_url = $this->get_settings_page_url();
374 }
375
376 if ( empty( $score ) && ! is_network_admin() && $can_manage_options ) {
377 $notification_center = Yoast_Notification_Center::get();
378 $notification_count = $notification_center->get_notification_count();
379
380 $counter = $this->get_notification_counter( $notification_count );
381 $notification_popup = $this->get_notification_popup();
382 }
383
384 $admin_bar_menu_args = [
385 'id' => self::MENU_IDENTIFIER,
386 'title' => $title . $score . $counter . $notification_popup,
387 'href' => $settings_url,
388 'meta' => [ 'tabindex' => ! empty( $settings_url ) ? false : '0' ],
389 ];
390 $wp_admin_bar->add_menu( $admin_bar_menu_args );
391
392 if ( $notification_count > 0 ) {
393 $admin_bar_menu_args = [
394 'parent' => self::MENU_IDENTIFIER,
395 'id' => 'wpseo-notifications',
396 'title' => __( 'Notifications', 'wordpress-seo' ) . $counter,
397 'href' => empty( $settings_url ) ? '' : $settings_url . '#/alert-center',
398 'meta' => [ 'tabindex' => ! empty( $settings_url ) ? false : '0' ],
399 ];
400 $wp_admin_bar->add_menu( $admin_bar_menu_args );
401 }
402 }
403
404 /**
405 * Adds the admin bar analysis submenu.
406 *
407 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
408 *
409 * @return void
410 */
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 }
418
419 if ( ! $url ) {
420 return;
421 }
422
423 $menu_args = [
424 'parent' => self::MENU_IDENTIFIER,
425 'id' => self::ANALYSIS_SUBMENU_IDENTIFIER,
426 'title' => __( 'Analyze this page', 'wordpress-seo' ),
427 'meta' => [ 'tabindex' => '0' ],
428 ];
429 $wp_admin_bar->add_menu( $menu_args );
430
431 $encoded_url = rawurlencode( $url );
432 $submenu_items = [
433 [
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=',
437 ],
438 [
439 'id' => 'wpseo-structureddata',
440 'title' => __( 'Google Rich Results Test', 'wordpress-seo' ),
441 'href' => 'https://search.google.com/test/rich-results?url=' . $encoded_url,
442 ],
443 [
444 'id' => 'wpseo-facebookdebug',
445 'title' => __( 'Facebook Debugger', 'wordpress-seo' ),
446 'href' => '//developers.facebook.com/tools/debug/?q=' . $encoded_url,
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 ],
453 ];
454
455 $this->add_submenu_items( $submenu_items, $wp_admin_bar, self::ANALYSIS_SUBMENU_IDENTIFIER );
456 }
457
458 /**
459 * Adds the admin bar tools submenu.
460 *
461 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
462 *
463 * @return void
464 */
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 );
473
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 ];
491
492 $this->add_submenu_items( $submenu_items, $wp_admin_bar, 'wpseo-sub-tools' );
493 }
494
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 ) {
503 $menu_args = [
504 'parent' => self::MENU_IDENTIFIER,
505 'id' => 'wpseo-sub-howto',
506 'title' => __( 'How to', 'wordpress-seo' ),
507 'meta' => [ 'tabindex' => '0' ],
508 ];
509 $wp_admin_bar->add_menu( $menu_args );
510
511 $submenu_items = [
512 [
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' ),
516 ],
517 [
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' ),
521 ],
522 [
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' ),
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 = [
556 [
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' ),
560 ],
561 [
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' ),
565 ],
566 [
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' ),
570 ],
571 [
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' ),
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(
590 [
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 ],
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(
635 [
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 ],
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(
670 [
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 ],
679 ],
680 );
681 }
682
683 /**
684 * Adds the admin bar settings submenu.
685 *
686 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
687 *
688 * @return void
689 */
690 protected function add_settings_submenu( WP_Admin_Bar $wp_admin_bar ) {
691 if ( ! $this->can_manage_options() ) {
692 return;
693 }
694
695 $admin_menu = new WPSEO_Admin_Menu( new WPSEO_Menu() );
696 $submenu_pages = $admin_menu->get_submenu_pages();
697
698 $menu_args = [
699 'parent' => self::MENU_IDENTIFIER,
700 'id' => self::SETTINGS_SUBMENU_IDENTIFIER,
701 'title' => __( 'SEO Settings', 'wordpress-seo' ),
702 'meta' => [ 'tabindex' => '0' ],
703 ];
704 $wp_admin_bar->add_menu( $menu_args );
705
706 foreach ( $submenu_pages as $submenu_page ) {
707 if ( ! current_user_can( $submenu_page[3] ) ) {
708 continue;
709 }
710
711 // Don't add the Google Search Console menu item.
712 if ( $submenu_page[4] === 'wpseo_search_console' ) {
713 continue;
714 }
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
726 $id = 'wpseo-' . str_replace( '_', '-', str_replace( 'wpseo_', '', $submenu_page[4] ) );
727 if ( $id === 'wpseo-dashboard' ) {
728 $id = 'wpseo-general';
729 }
730
731 $menu_args = [
732 'parent' => self::SETTINGS_SUBMENU_IDENTIFIER,
733 'id' => $id,
734 'title' => $submenu_page[2],
735 'href' => admin_url( 'admin.php?page=' . rawurlencode( $submenu_page[4] ) ),
736 ];
737 $wp_admin_bar->add_menu( $menu_args );
738 }
739 }
740
741 /**
742 * Adds the admin bar network settings submenu.
743 *
744 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance to add the menu to.
745 *
746 * @return void
747 */
748 protected function add_network_settings_submenu( WP_Admin_Bar $wp_admin_bar ) {
749 if ( ! $this->can_manage_options() ) {
750 return;
751 }
752
753 $network_admin_menu = new WPSEO_Network_Admin_Menu( new WPSEO_Menu() );
754 $submenu_pages = $network_admin_menu->get_submenu_pages();
755
756 $menu_args = [
757 'parent' => self::MENU_IDENTIFIER,
758 'id' => self::NETWORK_SETTINGS_SUBMENU_IDENTIFIER,
759 'title' => __( 'SEO Settings', 'wordpress-seo' ),
760 'meta' => [ 'tabindex' => '0' ],
761 ];
762 $wp_admin_bar->add_menu( $menu_args );
763
764 foreach ( $submenu_pages as $submenu_page ) {
765 if ( ! current_user_can( $submenu_page[3] ) ) {
766 continue;
767 }
768
769 $id = 'wpseo-' . str_replace( '_', '-', str_replace( 'wpseo_', '', $submenu_page[4] ) );
770 if ( $id === 'wpseo-dashboard' ) {
771 $id = 'wpseo-general';
772 }
773
774 $menu_args = [
775 'parent' => self::NETWORK_SETTINGS_SUBMENU_IDENTIFIER,
776 'id' => $id,
777 'title' => $submenu_page[2],
778 'href' => network_admin_url( 'admin.php?page=' . rawurlencode( $submenu_page[4] ) ),
779 ];
780 $wp_admin_bar->add_menu( $menu_args );
781 }
782 }
783
784 /**
785 * Gets the menu title markup.
786 *
787 * @return string Admin bar title markup.
788 */
789 protected function get_title() {
790 return '<div id="yoast-ab-icon" class="ab-item yoast-logo svg"><span class="screen-reader-text">' . __( 'SEO', 'wordpress-seo' ) . '</span></div>';
791 }
792
793 /**
794 * Gets the current post if in a singular post context.
795 *
796 * @global string $pagenow Current page identifier.
797 * @global WP_Post|null $post Current post object, or null if none available.
798 *
799 * @return WP_Post|null Post object, or null if not in singular context.
800 */
801 protected function get_singular_post() {
802 global $pagenow, $post;
803
804 if ( ! is_singular() && ( ! is_blog_admin() || ! WPSEO_Metabox::is_post_edit( $pagenow ) ) ) {
805 return null;
806 }
807
808 if ( ! isset( $post ) || ! is_object( $post ) || ! $post instanceof WP_Post ) {
809 return null;
810 }
811
812 return $post;
813 }
814
815 /**
816 * Gets the focus keyword for a given post.
817 *
818 * @param WP_Post $post Post object to get its focus keyword.
819 *
820 * @return string Focus keyword, or empty string if none available.
821 */
822 protected function get_post_focus_keyword( $post ) {
823 if ( ! is_object( $post ) || ! property_exists( $post, 'ID' ) ) {
824 return '';
825 }
826
827 /**
828 * Filter: 'wpseo_use_page_analysis' Determines if the analysis should be enabled.
829 *
830 * @param bool $enabled Determines if the analysis should be enabled.
831 */
832 if ( apply_filters( 'wpseo_use_page_analysis', true ) !== true ) {
833 return '';
834 }
835
836 return WPSEO_Meta::get_value( 'focuskw', $post->ID );
837 }
838
839 /**
840 * Gets the score for a given post.
841 *
842 * @param WP_Post $post Post object to get its score.
843 *
844 * @return string Score markup, or empty string if none available.
845 */
846 protected function get_post_score( $post ) {
847 if ( ! is_object( $post ) || ! property_exists( $post, 'ID' ) ) {
848 return '';
849 }
850
851 if ( apply_filters( 'wpseo_use_page_analysis', true ) !== true ) {
852 return '';
853 }
854
855 return $this->get_score_icon();
856 }
857
858 /**
859 * Gets the current term if in a singular term context.
860 *
861 * @global string $pagenow Current page identifier.
862 * @global WP_Query $wp_query Current query object.
863 * @global WP_Term|null $tag Current term object, or null if none available.
864 *
865 * @return WP_Term|null Term object, or null if not in singular context.
866 */
867 protected function get_singular_term() {
868 global $pagenow, $wp_query, $tag;
869
870 if ( is_category() || is_tag() || is_tax() ) {
871 return $wp_query->get_queried_object();
872 }
873
874 if ( WPSEO_Taxonomy::is_term_edit( $pagenow ) && ! WPSEO_Taxonomy::is_term_overview( $pagenow ) && isset( $tag ) && is_object( $tag ) && ! is_wp_error( $tag ) ) {
875 return get_term( $tag->term_id );
876 }
877
878 return null;
879 }
880
881 /**
882 * Gets the score for a given term.
883 *
884 * @param WP_Term $term Term object to get its score.
885 *
886 * @return string Score markup, or empty string if none available.
887 */
888 protected function get_term_score( $term ) {
889 if ( ! is_object( $term ) || ! property_exists( $term, 'term_id' ) || ! property_exists( $term, 'taxonomy' ) ) {
890 return '';
891 }
892
893 return $this->get_score_icon();
894 }
895
896 /**
897 * Create the score icon.
898 *
899 * @return string The score icon, or empty string.
900 */
901 protected function get_score_icon() {
902 $is_seo_enabled = $this->get_is_seo_enabled();
903 $is_readability_enabled = $this->get_is_readability_enabled();
904
905 $indexable = $this->get_current_indexable();
906
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 '';
917 }
918
919 /**
920 * Gets the URL to the main admin settings page.
921 *
922 * @return string Admin settings page URL.
923 */
924 protected function get_settings_page_url() {
925 return self_admin_url( 'admin.php?page=' . WPSEO_Admin::PAGE_IDENTIFIER );
926 }
927
928 /**
929 * Gets the notification counter if in a valid context.
930 *
931 * @param int $notification_count Number of notifications.
932 *
933 * @return string Notification counter markup, or empty string if not available.
934 */
935 protected function get_notification_counter( $notification_count ) {
936 /* translators: Hidden accessibility text; %s: number of notifications. */
937 $counter_screen_reader_text = sprintf( _n( '%s notification', '%s notifications', $notification_count, 'wordpress-seo' ), number_format_i18n( $notification_count ) );
938
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 );
945 }
946
947 /**
948 * Gets the notification popup if in a valid context.
949 *
950 * @return string Notification popup markup, or empty string if not available.
951 */
952 protected function get_notification_popup() {
953 $notification_center = Yoast_Notification_Center::get();
954 $new_notifications = $notification_center->get_new_notifications();
955 $new_notifications_count = count( $new_notifications );
956
957 if ( ! $new_notifications_count ) {
958 return '';
959 }
960
961 $notification = sprintf(
962 _n(
963 'There is a new notification.',
964 'There are new notifications.',
965 $new_notifications_count,
966 'wordpress-seo',
967 ),
968 $new_notifications_count,
969 );
970
971 return '<div class="yoast-issue-added">' . $notification . '</div>';
972 }
973
974 /**
975 * Checks whether the current user can manage options in the current context.
976 *
977 * @return bool True if capabilities are sufficient, false otherwise.
978 */
979 protected function can_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 }
1004 }
1005 }
1006