PluginProbe ʕ •ᴥ•ʔ
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.5
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 19.1 19.10 19.11 19.12 19.13 19.14 19.2 19.3 19.4 19.5 19.5.1 19.6 19.6.1 19.7 19.7.1 19.7.2 19.8 19.9 20.0 20.1 20.10 20.11 20.12 20.13 20.2 20.2.1 20.3 20.4 20.5 20.6 20.7 20.8 20.9 21.0 21.1 21.2 21.3 21.4 21.5 21.6 21.7 21.8 21.8.1 21.9 21.9.1 22.0 22.1 22.2 22.3 22.4 22.5 22.6 22.7 22.8 22.9 23.0 23.1 23.2 23.3 23.4 23.5 23.6 23.7 23.8 23.9 24.0 24.1 24.2 24.3 24.4 24.5 24.6 24.7 24.8 24.8.1 24.9 25.0 25.1 25.2 25.3 25.3.1 25.4 25.5 25.6 25.7 25.8 25.9 26.0 26.1 26.1.1 26.2 26.3 26.4 26.5 26.6 26.7 26.8 26.9 27.0 27.1 27.1.1 27.2 27.3 27.4
wordpress-seo / admin / class-admin.php
wordpress-seo / admin Last commit date
ajax 2 years ago capabilities 1 year ago endpoints 2 years ago exceptions 3 months ago filters 3 months ago formatter 1 year ago google_search_console 3 months ago import 3 months ago listeners 8 years ago menu 3 months ago metabox 3 months ago notifiers 3 months ago pages 3 months ago roles 3 months ago services 3 months ago statistics 3 months ago taxonomy 3 months ago tracking 3 months ago views 3 months ago watchers 3 months ago admin-settings-changed-listener.php 2 years ago ajax.php 3 months ago class-admin-asset-analysis-worker-location.php 3 months ago class-admin-asset-dev-server-location.php 3 months ago class-admin-asset-location.php 8 years ago class-admin-asset-manager.php 3 months ago class-admin-asset-seo-location.php 4 years ago class-admin-editor-specific-replace-vars.php 3 months ago class-admin-gutenberg-compatibility-notification.php 3 months ago class-admin-help-panel.php 3 months ago class-admin-init.php 3 months ago class-admin-recommended-replace-vars.php 2 years ago class-admin-user-profile.php 7 months ago class-admin-utils.php 3 months ago class-admin.php 3 months ago class-asset.php 1 year ago class-bulk-description-editor-list-table.php 3 months ago class-bulk-editor-list-table.php 3 months ago class-bulk-title-editor-list-table.php 3 months ago class-collector.php 1 year ago class-config.php 3 months ago class-database-proxy.php 3 months ago class-export.php 3 months ago class-expose-shortlinks.php 7 months ago class-gutenberg-compatibility.php 1 month ago class-meta-columns.php 3 months ago class-my-yoast-proxy.php 3 months ago class-option-tab.php 4 years ago class-option-tabs-formatter.php 3 months ago class-option-tabs.php 2 years ago class-paper-presenter.php 5 years ago class-plugin-availability.php 3 months ago class-plugin-conflict.php 2 years ago class-premium-popup.php 1 year ago class-premium-upsell-admin-block.php 3 months ago class-primary-term-admin.php 3 months ago class-product-upsell-notice.php 3 months ago class-remote-request.php 2 years ago class-schema-person-upgrade-notification.php 3 months ago class-suggested-plugins.php 3 months ago class-wincher-dashboard-widget.php 3 months ago class-yoast-columns.php 3 months ago class-yoast-dashboard-widget.php 3 months ago class-yoast-form.php 3 months ago class-yoast-input-validation.php 3 months ago class-yoast-network-admin.php 3 months ago class-yoast-network-settings-api.php 3 months ago class-yoast-notification-center.php 3 months ago class-yoast-notification.php 3 months ago class-yoast-notifications.php 3 months ago class-yoast-plugin-conflict.php 3 months ago index.php 10 years ago interface-collection.php 7 years ago interface-installable.php 8 years ago
class-admin.php
386 lines
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Admin
6 */
7
8 use Yoast\WP\SEO\Integrations\Settings_Integration;
9
10 /**
11 * Class that holds most of the admin functionality for Yoast SEO.
12 */
13 class WPSEO_Admin {
14
15 /**
16 * The page identifier used in WordPress to register the admin page.
17 *
18 * !DO NOT CHANGE THIS!
19 *
20 * @var string
21 */
22 public const PAGE_IDENTIFIER = 'wpseo_dashboard';
23
24 /**
25 * Array of classes that add admin functionality.
26 *
27 * @var array
28 */
29 protected $admin_features;
30
31 /**
32 * Class constructor.
33 */
34 public function __construct() {
35 $integrations = [];
36
37 global $pagenow;
38
39 $wpseo_menu = new WPSEO_Menu();
40 $wpseo_menu->register_hooks();
41
42 if ( is_multisite() ) {
43 WPSEO_Options::maybe_set_multisite_defaults( false );
44 }
45
46 add_action( 'created_category', [ $this, 'schedule_rewrite_flush' ] );
47 add_action( 'edited_category', [ $this, 'schedule_rewrite_flush' ] );
48 add_action( 'delete_category', [ $this, 'schedule_rewrite_flush' ] );
49
50 add_filter( 'wpseo_accessible_post_types', [ 'WPSEO_Post_Type', 'filter_attachment_post_type' ] );
51
52 add_filter( 'plugin_action_links_' . WPSEO_BASENAME, [ $this, 'add_action_link' ], 10, 2 );
53 add_filter( 'network_admin_plugin_action_links_' . WPSEO_BASENAME, [ $this, 'add_action_link' ], 10, 2 );
54
55 add_action( 'admin_enqueue_scripts', [ $this, 'config_page_scripts' ] );
56 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_global_style' ] );
57
58 add_action( 'after_switch_theme', [ $this, 'switch_theme' ] );
59 add_action( 'switch_theme', [ $this, 'switch_theme' ] );
60
61 add_filter( 'set-screen-option', [ $this, 'save_bulk_edit_options' ], 10, 3 );
62
63 add_action( 'admin_init', [ 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ], 10, 1 );
64
65 add_action( 'admin_init', [ $this, 'map_manage_options_cap' ] );
66
67 WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' );
68 WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' );
69
70 $this->initialize_cornerstone_content();
71
72 if ( WPSEO_Utils::is_plugin_network_active() ) {
73 $integrations[] = new Yoast_Network_Admin();
74 }
75
76 $this->admin_features = [
77 'dashboard_widget' => new Yoast_Dashboard_Widget(),
78 'wincher_dashboard_widget' => new Wincher_Dashboard_Widget(),
79 ];
80
81 if ( WPSEO_Metabox::is_post_overview( $pagenow ) || WPSEO_Metabox::is_post_edit( $pagenow ) ) {
82 $this->admin_features['primary_category'] = new WPSEO_Primary_Term_Admin();
83 }
84
85 $integrations[] = new WPSEO_Yoast_Columns();
86 $integrations[] = new WPSEO_Statistic_Integration();
87 $integrations[] = new WPSEO_Capability_Manager_Integration( WPSEO_Capability_Manager_Factory::get() );
88 $integrations[] = new WPSEO_Admin_Gutenberg_Compatibility_Notification();
89 $integrations[] = new WPSEO_Expose_Shortlinks();
90 $integrations[] = new WPSEO_MyYoast_Proxy();
91 $integrations[] = new WPSEO_Schema_Person_Upgrade_Notification();
92 $integrations[] = new WPSEO_Tracking( 'https://tracking.yoast.com/stats', ( WEEK_IN_SECONDS * 2 ) );
93 $integrations[] = new WPSEO_Admin_Settings_Changed_Listener();
94
95 $integrations = array_merge(
96 $integrations,
97 $this->get_admin_features(),
98 $this->initialize_cornerstone_content(),
99 );
100
101 foreach ( $integrations as $integration ) {
102 $integration->register_hooks();
103 }
104 }
105
106 /**
107 * Schedules a rewrite flush to happen at shutdown.
108 *
109 * @return void
110 */
111 public function schedule_rewrite_flush() {
112 if ( WPSEO_Options::get( 'stripcategorybase' ) !== true ) {
113 return;
114 }
115
116 // Bail if this is a multisite installation and the site has been switched.
117 if ( is_multisite() && ms_is_switched() ) {
118 return;
119 }
120
121 add_action( 'shutdown', 'flush_rewrite_rules' );
122 }
123
124 /**
125 * Returns all the classes for the admin features.
126 *
127 * @return array
128 */
129 public function get_admin_features() {
130 return $this->admin_features;
131 }
132
133 /**
134 * Register assets needed on admin pages.
135 *
136 * @deprecated 25.5
137 * @codeCoverageIgnore
138 *
139 * @return void
140 */
141 public function enqueue_assets() {
142 _deprecated_function( __METHOD__, 'Yoast SEO 25.5' );
143 }
144
145 /**
146 * Returns the manage_options capability.
147 *
148 * @return string The capability to use.
149 */
150 public function get_manage_options_cap() {
151 /**
152 * Filter: 'wpseo_manage_options_capability' - Allow changing the capability users need to view the settings pages.
153 *
154 * @param string $capability The capability.
155 */
156 return apply_filters( 'wpseo_manage_options_capability', 'wpseo_manage_options' );
157 }
158
159 /**
160 * Maps the manage_options cap on saving an options page to wpseo_manage_options.
161 *
162 * @return void
163 */
164 public function map_manage_options_cap() {
165 // phpcs:ignore WordPress.Security -- The variable is only used in strpos and thus safe to not unslash or sanitize.
166 $option_page = ! empty( $_POST['option_page'] ) ? $_POST['option_page'] : '';
167
168 if ( strpos( $option_page, 'yoast_wpseo' ) === 0 || strpos( $option_page, Settings_Integration::PAGE ) === 0 ) {
169 add_filter( 'option_page_capability_' . $option_page, [ $this, 'get_manage_options_cap' ] );
170 }
171 }
172
173 /**
174 * Adds the ability to choose how many posts are displayed per page
175 * on the bulk edit pages.
176 *
177 * @return void
178 */
179 public function bulk_edit_options() {
180 $option = 'per_page';
181 $args = [
182 'label' => __( 'Posts', 'wordpress-seo' ),
183 'default' => 10,
184 'option' => 'wpseo_posts_per_page',
185 ];
186 add_screen_option( $option, $args );
187 }
188
189 /**
190 * Saves the posts per page limit for bulk edit pages.
191 *
192 * @param int $status Status value to pass through.
193 * @param string $option Option name.
194 * @param int $value Count value to check.
195 *
196 * @return int
197 */
198 public function save_bulk_edit_options( $status, $option, $value ) {
199 if ( $option && ( $value > 0 && $value < 1000 ) === 'wpseo_posts_per_page' ) {
200 return $value;
201 }
202
203 return $status;
204 }
205
206 /**
207 * Adds links to Premium Support and FAQ under the plugin in the plugin overview page.
208 *
209 * @param array $links Array of links for the plugins, adapted when the current plugin is found.
210 * @param string $file The filename for the current plugin, which the filter loops through.
211 *
212 * @return array
213 */
214 public function add_action_link( $links, $file ) {
215 $first_time_configuration_notice_helper = YoastSEO()->helpers->first_time_configuration_notice;
216
217 if ( $file === WPSEO_BASENAME && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
218 if ( is_network_admin() ) {
219 $settings_url = network_admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER );
220 }
221 else {
222 $settings_url = admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER );
223 }
224 $settings_link = '<a href="' . esc_url( $settings_url ) . '">' . __( 'Settings', 'wordpress-seo' ) . '</a>';
225 array_unshift( $links, $settings_link );
226 }
227
228 // Add link to docs.
229 $faq_link = '<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yc' ) ) . '" target="_blank">' . __( 'FAQ', 'wordpress-seo' ) . '</a>';
230 array_unshift( $links, $faq_link );
231
232 if ( $first_time_configuration_notice_helper->first_time_configuration_not_finished() && ! is_network_admin() ) {
233 $configuration_title = ( ! $first_time_configuration_notice_helper->should_show_alternate_message() ) ? 'first-time configuration' : 'SEO configuration';
234 /* translators: CTA to finish the first time configuration. %s: Either first-time SEO configuration or SEO configuration. */
235 $message = sprintf( __( 'Finish your %s', 'wordpress-seo' ), $configuration_title );
236 $ftc_page = 'admin.php?page=wpseo_dashboard#/first-time-configuration';
237 $ftc_link = '<a href="' . esc_url( admin_url( $ftc_page ) ) . '" target="_blank">' . $message . '</a>';
238 array_unshift( $links, $ftc_link );
239 }
240
241 $addon_manager = new WPSEO_Addon_Manager();
242 if ( YoastSEO()->helpers->product->is_premium() ) {
243
244 // Remove Free 'deactivate' link if Premium is active as well. We don't want users to deactivate Free when Premium is active.
245 unset( $links['deactivate'] );
246 $no_deactivation_explanation = '<span style="color: #32373c">' . sprintf(
247 /* translators: %s expands to Yoast SEO Premium. */
248 __( 'Required by %s', 'wordpress-seo' ),
249 'Yoast SEO Premium',
250 ) . '</span>';
251
252 array_unshift( $links, $no_deactivation_explanation );
253
254 if ( $addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) {
255 return $links;
256 }
257
258 // Add link to where premium can be activated.
259 $activation_link = '<a style="font-weight: bold;" href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/activate-my-yoast' ) ) . '" target="_blank">' . __( 'Activate your subscription', 'wordpress-seo' ) . '</a>';
260 array_unshift( $links, $activation_link );
261
262 return $links;
263 }
264
265 // Add link to premium landing page.
266 $premium_link = '<a style="font-weight: bold;" href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yb' ) ) . '" target="_blank" data-action="load-nfd-ctb" data-ctb-id="f6a84663-465f-4cb5-8ba5-f7a6d72224b2">' . __( 'Get Premium', 'wordpress-seo' ) . '</a>';
267 array_unshift( $links, $premium_link );
268
269 return $links;
270 }
271
272 /**
273 * Enqueues the (tiny) global JS needed for the plugin.
274 *
275 * @return void
276 */
277 public function config_page_scripts() {
278 $asset_manager = new WPSEO_Admin_Asset_Manager();
279 $asset_manager->enqueue_script( 'admin-global' );
280 $asset_manager->localize_script( 'admin-global', 'wpseoAdminGlobalL10n', $this->localize_admin_global_script() );
281 }
282
283 /**
284 * Enqueues the (tiny) global stylesheet needed for the plugin.
285 *
286 * @return void
287 */
288 public function enqueue_global_style() {
289 $asset_manager = new WPSEO_Admin_Asset_Manager();
290 $asset_manager->enqueue_style( 'admin-global' );
291 }
292
293 /**
294 * Filter the $contactmethods array and add a set of social profiles.
295 *
296 * These are used with the Facebook author, rel="author" and Twitter cards implementation.
297 *
298 * @deprecated 22.6
299 * @codeCoverageIgnore
300 *
301 * @param array<string, string> $contactmethods Currently set contactmethods.
302 *
303 * @return array<string, string> Contactmethods with added contactmethods.
304 */
305 public function update_contactmethods( $contactmethods ) {
306 _deprecated_function( __METHOD__, 'Yoast SEO 22.6' );
307
308 $contactmethods['facebook'] = __( 'Facebook profile URL', 'wordpress-seo' );
309 $contactmethods['instagram'] = __( 'Instagram profile URL', 'wordpress-seo' );
310 $contactmethods['linkedin'] = __( 'LinkedIn profile URL', 'wordpress-seo' );
311 $contactmethods['myspace'] = __( 'MySpace profile URL', 'wordpress-seo' );
312 $contactmethods['pinterest'] = __( 'Pinterest profile URL', 'wordpress-seo' );
313 $contactmethods['soundcloud'] = __( 'SoundCloud profile URL', 'wordpress-seo' );
314 $contactmethods['tumblr'] = __( 'Tumblr profile URL', 'wordpress-seo' );
315 $contactmethods['twitter'] = __( 'X username (without @)', 'wordpress-seo' );
316 $contactmethods['youtube'] = __( 'YouTube profile URL', 'wordpress-seo' );
317 $contactmethods['wikipedia'] = __( 'Wikipedia page about you', 'wordpress-seo' ) . '<br/><small>' . __( '(if one exists)', 'wordpress-seo' ) . '</small>';
318
319 return $contactmethods;
320 }
321
322 /**
323 * Log the updated timestamp for user profiles when theme is changed.
324 *
325 * @return void
326 */
327 public function switch_theme() {
328
329 $users = get_users( [ 'capability' => [ 'edit_posts' ] ] );
330
331 if ( is_array( $users ) && $users !== [] ) {
332 foreach ( $users as $user ) {
333 update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', time() );
334 }
335 }
336 }
337
338 /**
339 * Localization for the dismiss urls.
340 *
341 * @return array
342 */
343 private function localize_admin_global_script() {
344 return array_merge(
345 [
346 'isRtl' => is_rtl(),
347 'variable_warning' => sprintf(
348 /* translators: %1$s: '%%term_title%%' variable used in titles and meta's template that's not compatible with the given template, %2$s: expands to 'HelpScout beacon' */
349 __( 'Warning: the variable %1$s cannot be used in this template. See the %2$s for more info.', 'wordpress-seo' ),
350 '<code>%s</code>',
351 'HelpScout beacon',
352 ),
353 /* translators: %s: expends to Yoast SEO */
354 'help_video_iframe_title' => sprintf( __( '%s video tutorial', 'wordpress-seo' ), 'Yoast SEO' ),
355 'scrollable_table_hint' => __( 'Scroll to see the table content.', 'wordpress-seo' ),
356 'wincher_is_logged_in' => WPSEO_Options::get( 'wincher_integration_active', true ) ? YoastSEO()->helpers->wincher->login_status() : false,
357 ],
358 YoastSEO()->helpers->wincher->get_admin_global_links(),
359 );
360 }
361
362 /**
363 * Whether we are on the admin dashboard page.
364 *
365 * @return bool
366 */
367 protected function on_dashboard_page() {
368 return $GLOBALS['pagenow'] === 'index.php';
369 }
370
371 /**
372 * Loads the cornerstone filter.
373 *
374 * @return WPSEO_WordPress_Integration[] The integrations to initialize.
375 */
376 protected function initialize_cornerstone_content() {
377 if ( ! WPSEO_Options::get( 'enable_cornerstone_content' ) ) {
378 return [];
379 }
380
381 return [
382 'cornerstone_filter' => new WPSEO_Cornerstone_Filter(),
383 ];
384 }
385 }
386