PluginProbe ʕ •ᴥ•ʔ
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.0
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-yoast-network-admin.php
wordpress-seo / admin Last commit date
ajax 5 years ago capabilities 4 years ago endpoints 5 years ago exceptions 7 years ago filters 4 years ago formatter 4 years ago google_search_console 5 years ago import 4 years ago listeners 8 years ago menu 4 years ago metabox 4 years ago notifiers 4 years ago pages 4 years ago roles 5 years ago ryte 5 years ago services 5 years ago statistics 5 years ago taxonomy 4 years ago tracking 4 years ago views 4 years ago watchers 5 years ago admin-settings-changed-listener.php 5 years ago ajax.php 4 years ago class-admin-asset-analysis-worker-location.php 5 years ago class-admin-asset-dev-server-location.php 5 years ago class-admin-asset-location.php 8 years ago class-admin-asset-manager.php 4 years ago class-admin-asset-seo-location.php 4 years ago class-admin-asset-yoast-components-l10n.php 5 years ago class-admin-editor-specific-replace-vars.php 5 years ago class-admin-gutenberg-compatibility-notification.php 5 years ago class-admin-help-panel.php 5 years ago class-admin-init.php 4 years ago class-admin-recommended-replace-vars.php 6 years ago class-admin-user-profile.php 6 years ago class-admin-utils.php 5 years ago class-admin.php 4 years ago class-asset.php 5 years ago class-bulk-description-editor-list-table.php 5 years ago class-bulk-editor-list-table.php 4 years ago class-bulk-title-editor-list-table.php 6 years ago class-collector.php 6 years ago class-config.php 4 years ago class-customizer.php 5 years ago class-database-proxy.php 5 years ago class-export.php 5 years ago class-expose-shortlinks.php 4 years ago class-gutenberg-compatibility.php 4 years ago class-helpscout.php 5 years ago class-meta-columns.php 5 years ago class-my-yoast-proxy.php 5 years ago class-option-tab.php 4 years ago class-option-tabs-formatter.php 5 years ago class-option-tabs.php 5 years ago class-paper-presenter.php 5 years ago class-plugin-availability.php 5 years ago class-plugin-conflict.php 4 years ago class-premium-popup.php 5 years ago class-premium-upsell-admin-block.php 4 years ago class-primary-term-admin.php 5 years ago class-product-upsell-notice.php 5 years ago class-remote-request.php 5 years ago class-schema-person-upgrade-notification.php 4 years ago class-suggested-plugins.php 4 years ago class-yoast-columns.php 5 years ago class-yoast-dashboard-widget.php 4 years ago class-yoast-form.php 4 years ago class-yoast-input-validation.php 5 years ago class-yoast-network-admin.php 5 years ago class-yoast-network-settings-api.php 4 years ago class-yoast-notification-center.php 4 years ago class-yoast-notification.php 5 years ago class-yoast-notifications.php 5 years ago class-yoast-plugin-conflict.php 4 years ago index.php 10 years ago interface-collection.php 7 years ago interface-installable.php 8 years ago
class-yoast-network-admin.php
325 lines
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Internals
6 */
7
8 /**
9 * Multisite utility class for network admin functionality.
10 */
11 class Yoast_Network_Admin implements WPSEO_WordPress_AJAX_Integration, WPSEO_WordPress_Integration {
12
13 /**
14 * Action identifier for updating plugin network options.
15 *
16 * @var string
17 */
18 const UPDATE_OPTIONS_ACTION = 'yoast_handle_network_options';
19
20 /**
21 * Action identifier for restoring a site.
22 *
23 * @var string
24 */
25 const RESTORE_SITE_ACTION = 'yoast_restore_site';
26
27 /**
28 * Gets the available sites as choices, e.g. for a dropdown.
29 *
30 * @param bool $include_empty Optional. Whether to include an initial placeholder choice.
31 * Default false.
32 * @param bool $show_title Optional. Whether to show the title for each site. This requires
33 * switching through the sites, so has performance implications for
34 * sites that do not use a persistent cache.
35 * Default false.
36 *
37 * @return array Choices as $site_id => $site_label pairs.
38 */
39 public function get_site_choices( $include_empty = false, $show_title = false ) {
40 $choices = [];
41
42 if ( $include_empty ) {
43 $choices['-'] = __( 'None', 'wordpress-seo' );
44 }
45
46 $criteria = [
47 'deleted' => 0,
48 'network_id' => get_current_network_id(),
49 ];
50 $sites = get_sites( $criteria );
51
52 foreach ( $sites as $site ) {
53 $site_name = $site->domain . $site->path;
54 if ( $show_title ) {
55 $site_name = $site->blogname . ' (' . $site->domain . $site->path . ')';
56 }
57 $choices[ $site->blog_id ] = $site->blog_id . ': ' . $site_name;
58
59 $site_states = $this->get_site_states( $site );
60 if ( ! empty( $site_states ) ) {
61 $choices[ $site->blog_id ] .= ' [' . implode( ', ', $site_states ) . ']';
62 }
63 }
64
65 return $choices;
66 }
67
68 /**
69 * Gets the states of a site.
70 *
71 * @param WP_Site $site Site object.
72 *
73 * @return array Array of $state_slug => $state_label pairs.
74 */
75 public function get_site_states( $site ) {
76 $available_states = [
77 'public' => __( 'public', 'wordpress-seo' ),
78 'archived' => __( 'archived', 'wordpress-seo' ),
79 'mature' => __( 'mature', 'wordpress-seo' ),
80 'spam' => __( 'spam', 'wordpress-seo' ),
81 'deleted' => __( 'deleted', 'wordpress-seo' ),
82 ];
83
84 $site_states = [];
85 foreach ( $available_states as $state_slug => $state_label ) {
86 if ( $site->$state_slug === '1' ) {
87 $site_states[ $state_slug ] = $state_label;
88 }
89 }
90
91 return $site_states;
92 }
93
94 /**
95 * Handles a request to update plugin network options.
96 *
97 * This method works similar to how option updates are handled in `wp-admin/options.php` and
98 * `wp-admin/network/settings.php`.
99 *
100 * @return void
101 */
102 public function handle_update_options_request() {
103 $option_group = filter_input( INPUT_POST, 'network_option_group', FILTER_SANITIZE_STRING );
104
105 $this->verify_request( "{$option_group}-network-options" );
106
107 $whitelist_options = Yoast_Network_Settings_API::get()->get_whitelist_options( $option_group );
108
109 if ( empty( $whitelist_options ) ) {
110 add_settings_error( $option_group, 'settings_updated', __( 'You are not allowed to modify unregistered network settings.', 'wordpress-seo' ), 'error' );
111
112 $this->terminate_request();
113 return;
114 }
115
116 // phpcs:disable WordPress.Security.NonceVerification -- Nonce verified via `verify_request()` above.
117 foreach ( $whitelist_options as $option_name ) {
118 $value = null;
119 if ( isset( $_POST[ $option_name ] ) ) {
120 // Adding sanitize_text_field around this will break the saving of settings because it expects a string: https://github.com/Yoast/wordpress-seo/issues/12440.
121 $value = wp_unslash( $_POST[ $option_name ] );
122 }
123
124 WPSEO_Options::update_site_option( $option_name, $value );
125 }
126 // phpcs:enable WordPress.Security.NonceVerification
127
128 $settings_errors = get_settings_errors();
129 if ( empty( $settings_errors ) ) {
130 add_settings_error( $option_group, 'settings_updated', __( 'Settings Updated.', 'wordpress-seo' ), 'updated' );
131 }
132
133 $this->terminate_request();
134 }
135
136 /**
137 * Handles a request to restore a site's default settings.
138 *
139 * @return void
140 */
141 public function handle_restore_site_request() {
142 $this->verify_request( 'wpseo-network-restore', 'restore_site_nonce' );
143
144 $option_group = 'wpseo_ms';
145
146 // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified via `verify_request()` above.
147 $site_id = ! empty( $_POST[ $option_group ]['site_id'] ) ? (int) $_POST[ $option_group ]['site_id'] : 0;
148 if ( ! $site_id ) {
149 add_settings_error( $option_group, 'settings_updated', __( 'No site has been selected to restore.', 'wordpress-seo' ), 'error' );
150
151 $this->terminate_request();
152 return;
153 }
154
155 $site = get_site( $site_id );
156 if ( ! $site ) {
157 /* translators: %s expands to the ID of a site within a multisite network. */
158 add_settings_error( $option_group, 'settings_updated', sprintf( __( 'Site with ID %d not found.', 'wordpress-seo' ), $site_id ), 'error' );
159 }
160 else {
161 WPSEO_Options::reset_ms_blog( $site_id );
162
163 /* translators: %s expands to the name of a site within a multisite network. */
164 add_settings_error( $option_group, 'settings_updated', sprintf( __( '%s restored to default SEO settings.', 'wordpress-seo' ), esc_html( $site->blogname ) ), 'updated' );
165 }
166
167 $this->terminate_request();
168 }
169
170 /**
171 * Outputs nonce, action and option group fields for a network settings page in the plugin.
172 *
173 * @param string $option_group Option group name for the current page.
174 *
175 * @return void
176 */
177 public function settings_fields( $option_group ) {
178 ?>
179 <input type="hidden" name="network_option_group" value="<?php echo esc_attr( $option_group ); ?>" />
180 <input type="hidden" name="action" value="<?php echo esc_attr( self::UPDATE_OPTIONS_ACTION ); ?>" />
181 <?php
182 wp_nonce_field( "$option_group-network-options" );
183 }
184
185 /**
186 * Enqueues network admin assets.
187 *
188 * @return void
189 */
190 public function enqueue_assets() {
191 $asset_manager = new WPSEO_Admin_Asset_Manager();
192 $asset_manager->enqueue_script( 'network-admin' );
193
194 $translations = [
195 /* translators: %s: success message */
196 'success_prefix' => __( 'Success: %s', 'wordpress-seo' ),
197 /* translators: %s: error message */
198 'error_prefix' => __( 'Error: %s', 'wordpress-seo' ),
199 ];
200 $asset_manager->localize_script(
201 'network-admin',
202 'wpseoNetworkAdminGlobalL10n',
203 $translations
204 );
205 }
206
207 /**
208 * Hooks in the necessary actions and filters.
209 *
210 * @return void
211 */
212 public function register_hooks() {
213
214 if ( ! $this->meets_requirements() ) {
215 return;
216 }
217
218 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
219
220 add_action( 'admin_action_' . self::UPDATE_OPTIONS_ACTION, [ $this, 'handle_update_options_request' ] );
221 add_action( 'admin_action_' . self::RESTORE_SITE_ACTION, [ $this, 'handle_restore_site_request' ] );
222 }
223
224 /**
225 * Hooks in the necessary AJAX actions.
226 *
227 * @return void
228 */
229 public function register_ajax_hooks() {
230 add_action( 'wp_ajax_' . self::UPDATE_OPTIONS_ACTION, [ $this, 'handle_update_options_request' ] );
231 add_action( 'wp_ajax_' . self::RESTORE_SITE_ACTION, [ $this, 'handle_restore_site_request' ] );
232 }
233
234 /**
235 * Checks whether the requirements to use this class are met.
236 *
237 * @return bool True if requirements are met, false otherwise.
238 */
239 public function meets_requirements() {
240 return is_multisite() && is_network_admin();
241 }
242
243 /**
244 * Verifies that the current request is valid.
245 *
246 * @param string $action Nonce action.
247 * @param string $query_arg Optional. Nonce query argument. Default '_wpnonce'.
248 *
249 * @return void
250 */
251 public function verify_request( $action, $query_arg = '_wpnonce' ) {
252 $has_access = current_user_can( 'wpseo_manage_network_options' );
253
254 if ( wp_doing_ajax() ) {
255 check_ajax_referer( $action, $query_arg );
256
257 if ( ! $has_access ) {
258 wp_die( -1, 403 );
259 }
260 return;
261 }
262
263 check_admin_referer( $action, $query_arg );
264
265 if ( ! $has_access ) {
266 wp_die( esc_html__( 'You are not allowed to perform this action.', 'wordpress-seo' ) );
267 }
268 }
269
270 /**
271 * Terminates the current request by either redirecting back or sending an AJAX response.
272 *
273 * @return void
274 */
275 public function terminate_request() {
276 if ( wp_doing_ajax() ) {
277 $settings_errors = get_settings_errors();
278
279 if ( ! empty( $settings_errors ) && $settings_errors[0]['type'] === 'updated' ) {
280 wp_send_json_success( $settings_errors, 200 );
281 }
282
283 wp_send_json_error( $settings_errors, 400 );
284 }
285
286 $this->persist_settings_errors();
287 $this->redirect_back( [ 'settings-updated' => 'true' ] );
288 }
289
290 /**
291 * Persists settings errors.
292 *
293 * Settings errors are stored in a transient for 30 seconds so that this transient
294 * can be retrieved on the next page load.
295 *
296 * @return void
297 */
298 protected function persist_settings_errors() {
299 /*
300 * A regular transient is used here, since it is automatically cleared right after the redirect.
301 * A network transient would be cleaner, but would require a lot of copied code from core for
302 * just a minor adjustment when displaying settings errors.
303 */
304 set_transient( 'settings_errors', get_settings_errors(), 30 );
305 }
306
307 /**
308 * Redirects back to the referer URL, with optional query arguments.
309 *
310 * @param array $query_args Optional. Query arguments to add to the redirect URL. Default none.
311 *
312 * @return void
313 */
314 protected function redirect_back( $query_args = [] ) {
315 $sendback = wp_get_referer();
316
317 if ( ! empty( $query_args ) ) {
318 $sendback = add_query_arg( $query_args, $sendback );
319 }
320
321 wp_safe_redirect( $sendback );
322 exit;
323 }
324 }
325