PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.9
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.9
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 / admin / class-admin-init.php

class-admin-init.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.9, at admin/class-admin-init.php

486 lines 13.9 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\Admin
6 */
7
8 /**
9 * Performs the load on admin side.
10 */
11 class WPSEO_Admin_Init {
12
13 /**
14 * Holds the global `$pagenow` variable's value.
15 *
16 * @var string
17 */
18 private $pagenow;
19
20 /**
21 * Holds the asset manager.
22 *
23 * @var WPSEO_Admin_Asset_Manager
24 */
25 private $asset_manager;
26
27 /**
28 * Class constructor.
29 */
30 public function __construct() {
31 $GLOBALS['wpseo_admin'] = new WPSEO_Admin();
32
33 $this->pagenow = $GLOBALS['pagenow'];
34
35 $this->asset_manager = new WPSEO_Admin_Asset_Manager();
36
37 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_dismissible' ] );
38 add_action( 'admin_init', [ $this, 'unsupported_php_notice' ], 15 );
39 add_action( 'admin_init', [ $this->asset_manager, 'register_assets' ] );
40 add_action( 'admin_init', [ $this, 'show_hook_deprecation_warnings' ] );
41 add_action( 'admin_init', [ 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ] );
42 add_action( 'admin_notices', [ $this, 'permalink_settings_notice' ] );
43 add_action( 'post_submitbox_misc_actions', [ $this, 'add_publish_box_section' ] );
44
45 /*
46 * The `admin_notices` hook fires on single site admin pages vs.
47 * `network_admin_notices` which fires on multisite admin pages and
48 * `user_admin_notices` which fires on multisite user admin pagss.
49 */
50 add_action( 'admin_notices', [ $this, 'search_engines_discouraged_notice' ] );
51
52 $this->load_meta_boxes();
53 $this->load_taxonomy_class();
54 $this->load_admin_page_class();
55 $this->load_admin_user_class();
56 $this->load_xml_sitemaps_admin();
57 $this->load_plugin_suggestions();
58 }
59
60 /**
61 * Enqueue our styling for dismissible yoast notifications.
62 */
63 public function enqueue_dismissible() {
64 $this->asset_manager->enqueue_style( 'dismissible' );
65 }
66
67 /**
68 * Creates an unsupported PHP version notification in the notification center.
69 *
70 * @return void
71 */
72 public function unsupported_php_notice() {
73 $notification_center = Yoast_Notification_Center::get();
74 $notification_center->remove_notification_by_id( 'wpseo-dismiss-unsupported-php' );
75 }
76
77 /**
78 * Gets the latest released major WordPress version from the WordPress stable-check api.
79 *
80 * @return float|int The latest released major WordPress version. 0 when the stable-check API doesn't respond.
81 */
82 private function get_latest_major_wordpress_version() {
83 $core_updates = get_core_updates( [ 'dismissed' => true ] );
84
85 if ( $core_updates === false ) {
86 return 0;
87 }
88
89 $wp_version_latest = get_bloginfo( 'version' );
90 foreach ( $core_updates as $update ) {
91 if ( $update->response === 'upgrade' && version_compare( $update->version, $wp_version_latest, '>' ) ) {
92 $wp_version_latest = $update->version;
93 }
94 }
95
96 // Strip the patch version and convert to a float.
97 return (float) $wp_version_latest;
98 }
99
100 /**
101 * Helper to verify if the user is currently visiting one of our admin pages.
102 *
103 * @return bool
104 */
105 private function on_wpseo_admin_page() {
106 return $this->pagenow === 'admin.php' && strpos( filter_input( INPUT_GET, 'page' ), 'wpseo' ) === 0;
107 }
108
109 /**
110 * Determine whether we should load the meta box class and if so, load it.
111 */
112 private function load_meta_boxes() {
113
114 $is_editor = WPSEO_Metabox::is_post_overview( $this->pagenow ) || WPSEO_Metabox::is_post_edit( $this->pagenow );
115 $is_inline_save = filter_input( INPUT_POST, 'action' ) === 'inline-save';
116
117 /**
118 * Filter: 'wpseo_always_register_metaboxes_on_admin' - Allow developers to change whether
119 * the WPSEO metaboxes are only registered on the typical pages (lean loading) or always
120 * registered when in admin.
121 *
122 * @api bool Whether to always register the metaboxes or not. Defaults to false.
123 */
124 if ( $is_editor || $is_inline_save || apply_filters( 'wpseo_always_register_metaboxes_on_admin', false )
125 ) {
126 $GLOBALS['wpseo_metabox'] = new WPSEO_Metabox();
127 $GLOBALS['wpseo_meta_columns'] = new WPSEO_Meta_Columns();
128 }
129 }
130
131 /**
132 * Determine if we should load our taxonomy edit class and if so, load it.
133 */
134 private function load_taxonomy_class() {
135 if (
136 WPSEO_Taxonomy::is_term_edit( $this->pagenow )
137 || WPSEO_Taxonomy::is_term_overview( $this->pagenow )
138 ) {
139 new WPSEO_Taxonomy();
140 }
141 }
142
143 /**
144 * Determine if we should load our admin pages class and if so, load it.
145 *
146 * Loads admin page class for all admin pages starting with `wpseo_`.
147 */
148 private function load_admin_user_class() {
149 if ( in_array( $this->pagenow, [ 'user-edit.php', 'profile.php' ], true )
150 && current_user_can( 'edit_users' )
151 ) {
152 new WPSEO_Admin_User_Profile();
153 }
154 }
155
156 /**
157 * Determine if we should load our admin pages class and if so, load it.
158 *
159 * Loads admin page class for all admin pages starting with `wpseo_`.
160 */
161 private function load_admin_page_class() {
162
163 if ( $this->on_wpseo_admin_page() ) {
164 // For backwards compatabilty, this still needs a global, for now...
165 $GLOBALS['wpseo_admin_pages'] = new WPSEO_Admin_Pages();
166
167 $page = filter_input( INPUT_GET, 'page' );
168 // Only register the yoast i18n when the page is a Yoast SEO page.
169 if ( WPSEO_Utils::is_yoast_seo_free_page( $page ) ) {
170 $this->register_i18n_promo_class();
171 if ( $page !== 'wpseo_titles' ) {
172 $this->register_premium_upsell_admin_block();
173 }
174 }
175 }
176 }
177
178 /**
179 * Loads the plugin suggestions.
180 */
181 private function load_plugin_suggestions() {
182 $suggestions = new WPSEO_Suggested_Plugins( new WPSEO_Plugin_Availability(), Yoast_Notification_Center::get() );
183 $suggestions->register_hooks();
184 }
185
186 /**
187 * Registers the Premium Upsell Admin Block.
188 *
189 * @return void
190 */
191 private function register_premium_upsell_admin_block() {
192 if ( ! YoastSEO()->helpers->product->is_premium() ) {
193 $upsell_block = new WPSEO_Premium_Upsell_Admin_Block( 'wpseo_admin_promo_footer' );
194 $upsell_block->register_hooks();
195 }
196 }
197
198 /**
199 * Registers the promotion class for our GlotPress instance, then creates a notification with the i18n promo.
200 *
201 * @link https://github.com/Yoast/i18n-module
202 */
203 private function register_i18n_promo_class() {
204 // BC, because an older version of the i18n-module didn't have this class.
205 $i18n_module = new Yoast_I18n_WordPressOrg_v3(
206 [
207 'textdomain' => 'wordpress-seo',
208 'plugin_name' => 'Yoast SEO',
209 'hook' => 'wpseo_admin_promo_footer',
210 ],
211 false
212 );
213
214 $message = $i18n_module->get_promo_message();
215
216 if ( $message !== '' ) {
217 $message .= $i18n_module->get_dismiss_i18n_message_button();
218 }
219
220 $notification_center = Yoast_Notification_Center::get();
221
222 $notification = new Yoast_Notification(
223 $message,
224 [
225 'type' => Yoast_Notification::WARNING,
226 'id' => 'i18nModuleTranslationAssistance',
227 ]
228 );
229
230 if ( $message ) {
231 $notification_center->add_notification( $notification );
232
233 return;
234 }
235
236 $notification_center->remove_notification( $notification );
237 }
238
239 /**
240 * See if we should start our XML Sitemaps Admin class.
241 */
242 private function load_xml_sitemaps_admin() {
243 if ( WPSEO_Options::get( 'enable_xml_sitemap', false ) ) {
244 new WPSEO_Sitemaps_Admin();
245 }
246 }
247
248 /**
249 * Checks whether search engines are discouraged from indexing the site.
250 *
251 * @return bool Whether search engines are discouraged from indexing the site.
252 */
253 private function are_search_engines_discouraged() {
254 return (string) get_option( 'blog_public' ) === '0';
255 }
256
257 /**
258 * Shows deprecation warnings to the user if a plugin has registered a filter we have deprecated.
259 */
260 public function show_hook_deprecation_warnings() {
261 global $wp_filter;
262
263 if ( wp_doing_ajax() ) {
264 return;
265 }
266
267 // WordPress hooks that have been deprecated since a Yoast SEO version.
268 $deprecated_filters = [
269 'wpseo_genesis_force_adjacent_rel_home' => [
270 'version' => '9.4',
271 'alternative' => null,
272 ],
273 'wpseo_opengraph' => [
274 'version' => '14.0',
275 'alternative' => null,
276 ],
277 'wpseo_twitter' => [
278 'version' => '14.0',
279 'alternative' => null,
280 ],
281 'wpseo_twitter_taxonomy_image' => [
282 'version' => '14.0',
283 'alternative' => null,
284 ],
285 'wpseo_twitter_metatag_key' => [
286 'version' => '14.0',
287 'alternative' => null,
288 ],
289 'wp_seo_get_bc_ancestors' => [
290 'version' => '14.0',
291 'alternative' => 'wpseo_breadcrumb_links',
292 ],
293 'validate_facebook_app_id_api_response_code' => [
294 'version' => '15.5',
295 'alternative' => null,
296 ],
297 'validate_facebook_app_id_api_response_body' => [
298 'version' => '15.5',
299 'alternative' => null,
300 ],
301 ];
302
303 // Determine which filters have been registered.
304 $deprecated_notices = array_intersect(
305 array_keys( $deprecated_filters ),
306 array_keys( $wp_filter )
307 );
308
309 // Show notice for each deprecated filter or action that has been registered.
310 foreach ( $deprecated_notices as $deprecated_filter ) {
311 $deprecation_info = $deprecated_filters[ $deprecated_filter ];
312 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Only uses the hardcoded values from above.
313 _deprecated_hook(
314 $deprecated_filter,
315 'WPSEO ' . $deprecation_info['version'],
316 $deprecation_info['alternative']
317 );
318 // phpcs:enable
319 }
320 }
321
322 /**
323 * Check if the permalink uses %postname%.
324 *
325 * @return bool
326 */
327 private function has_postname_in_permalink() {
328 return ( strpos( get_option( 'permalink_structure' ), '%postname%' ) !== false );
329 }
330
331 /**
332 * Shows a notice on the permalink settings page.
333 */
334 public function permalink_settings_notice() {
335 global $pagenow;
336
337 if ( $pagenow === 'options-permalink.php' ) {
338 printf(
339 '<div class="notice notice-warning"><p><strong>%1$s</strong><br>%2$s<br><a href="%3$s" target="_blank">%4$s</a></p></div>',
340 esc_html__( 'WARNING:', 'wordpress-seo' ),
341 sprintf(
342 /* translators: %1$s and %2$s expand to <em> items to emphasize the word in the middle. */
343 esc_html__( 'Changing your permalinks settings can seriously impact your search engine visibility. It should almost %1$s never %2$s be done on a live website.', 'wordpress-seo' ),
344 '<em>',
345 '</em>'
346 ),
347 esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/why-permalinks/' ) ),
348 // The link's content.
349 esc_html__( 'Learn about why permalinks are important for SEO.', 'wordpress-seo' )
350 );
351 }
352 }
353
354 /**
355 * Determines whether and where the "search engines discouraged" admin notice should be displayed.
356 *
357 * @return bool Whether the "search engines discouraged" admin notice should be displayed.
358 */
359 private function should_display_search_engines_discouraged_notice() {
360 $discouraged_pages = [
361 'index.php',
362 'plugins.php',
363 'update-core.php',
364 ];
365
366 return (
367 $this->are_search_engines_discouraged()
368 && WPSEO_Capability_Utils::current_user_can( 'manage_options' )
369 && WPSEO_Options::get( 'ignore_search_engines_discouraged_notice', false ) === false
370 && (
371 $this->on_wpseo_admin_page()
372 || in_array( $this->pagenow, $discouraged_pages, true )
373 )
374 );
375 }
376
377 /**
378 * Displays an admin notice when WordPress is set to discourage search engines from indexing the site.
379 *
380 * @return void
381 */
382 public function search_engines_discouraged_notice() {
383 if ( ! $this->should_display_search_engines_discouraged_notice() ) {
384 return;
385 }
386
387 printf(
388 '<div id="robotsmessage" class="notice notice-error"><p><strong>%1$s</strong> %2$s <button type="button" id="robotsmessage-dismiss-button" class="button-link hide-if-no-js" data-nonce="%3$s">%4$s</button></p></div>',
389 esc_html__( 'Huge SEO Issue: You\'re blocking access to robots.', 'wordpress-seo' ),
390 sprintf(
391 /* translators: 1: Link start tag to the WordPress Reading Settings page, 2: Link closing tag. */
392 esc_html__( 'If you want search engines to show this site in their results, you must %1$sgo to your Reading Settings%2$s and uncheck the box for Search Engine Visibility.', 'wordpress-seo' ),
393 '<a href="' . esc_url( admin_url( 'options-reading.php' ) ) . '">',
394 '</a>'
395 ),
396 esc_js( wp_create_nonce( 'wpseo-ignore' ) ),
397 esc_html__( 'I don\'t want this site to show in the search results.', 'wordpress-seo' )
398 );
399 }
400
401 /**
402 * Adds a custom Yoast section within the Classic Editor publish box.
403 *
404 * @param \WP_Post $post The current post object.
405 *
406 * @return void
407 */
408 public function add_publish_box_section( $post ) {
409 if ( in_array( $this->pagenow, [ 'post.php', 'post-new.php' ], true ) ) {
410 ?>
411 <div id="yoast-seo-publishbox-section"></div>
412 <?php
413 /**
414 * Fires after the post time/date setting in the Publish meta box.
415 *
416 * @api \WP_Post The current post object.
417 */
418 do_action( 'wpseo_publishbox_misc_actions', $post );
419 }
420 }
421
422 /* ********************* DEPRECATED METHODS ********************* */
423
424 /**
425 * Notify about the default tagline if the user hasn't changed it.
426 *
427 * @deprecated 13.2
428 * @codeCoverageIgnore
429 */
430 public function tagline_notice() {
431 _deprecated_function( __METHOD__, 'WPSEO 13.2' );
432 }
433
434 /**
435 * Returns whether or not the site has the default tagline.
436 *
437 * @deprecated 13.2
438 * @codeCoverageIgnore
439 *
440 * @return bool
441 */
442 public function has_default_tagline() {
443 _deprecated_function( __METHOD__, 'WPSEO 13.2' );
444
445 $blog_description = get_bloginfo( 'description' );
446 $default_blog_description = 'Just another WordPress site';
447
448 // We are using the WordPress internal translation.
449 $translated_blog_description = __( 'Just another WordPress site', 'default' );
450
451 return $translated_blog_description === $blog_description || $default_blog_description === $blog_description;
452 }
453
454 /**
455 * Shows an alert when the permalink doesn't contain %postname%.
456 *
457 * @deprecated 13.2
458 * @codeCoverageIgnore
459 */
460 public function permalink_notice() {
461 _deprecated_function( __METHOD__, 'WPSEO 13.2' );
462 }
463
464 /**
465 * Add an alert if the blog is not publicly visible.
466 *
467 * @deprecated 14.1
468 * @codeCoverageIgnore
469 */
470 public function blog_public_notice() {
471 _deprecated_function( __METHOD__, 'WPSEO 14.1' );
472 }
473
474 /**
475 * Handles the notifiers for the dashboard page.
476 *
477 * @deprecated 14.1
478 * @codeCoverageIgnore
479 *
480 * @return void
481 */
482 public function handle_notifications() {
483 _deprecated_function( __METHOD__, 'WPSEO 14.1' );
484 }
485 }
486