balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

misc.php (44002B)


      1 <?php
      2 /**
      3  * Misc WordPress Administration API.
      4  *
      5  * @package WordPress
      6  * @subpackage Administration
      7  */
      8 
      9 /**
     10  * Returns whether the server is running Apache with the mod_rewrite module loaded.
     11  *
     12  * @since 2.0.0
     13  *
     14  * @return bool Whether the server is running Apache with the mod_rewrite module loaded.
     15  */
     16 function got_mod_rewrite() {
     17 	$got_rewrite = apache_mod_loaded( 'mod_rewrite', true );
     18 
     19 	/**
     20 	 * Filters whether Apache and mod_rewrite are present.
     21 	 *
     22 	 * This filter was previously used to force URL rewriting for other servers,
     23 	 * like nginx. Use the {@see 'got_url_rewrite'} filter in got_url_rewrite() instead.
     24 	 *
     25 	 * @since 2.5.0
     26 	 *
     27 	 * @see got_url_rewrite()
     28 	 *
     29 	 * @param bool $got_rewrite Whether Apache and mod_rewrite are present.
     30 	 */
     31 	return apply_filters( 'got_rewrite', $got_rewrite );
     32 }
     33 
     34 /**
     35  * Returns whether the server supports URL rewriting.
     36  *
     37  * Detects Apache's mod_rewrite, IIS 7.0+ permalink support, and nginx.
     38  *
     39  * @since 3.7.0
     40  *
     41  * @global bool $is_nginx
     42  *
     43  * @return bool Whether the server supports URL rewriting.
     44  */
     45 function got_url_rewrite() {
     46 	$got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || iis7_supports_permalinks() );
     47 
     48 	/**
     49 	 * Filters whether URL rewriting is available.
     50 	 *
     51 	 * @since 3.7.0
     52 	 *
     53 	 * @param bool $got_url_rewrite Whether URL rewriting is available.
     54 	 */
     55 	return apply_filters( 'got_url_rewrite', $got_url_rewrite );
     56 }
     57 
     58 /**
     59  * Extracts strings from between the BEGIN and END markers in the .htaccess file.
     60  *
     61  * @since 1.5.0
     62  *
     63  * @param string $filename Filename to extract the strings from.
     64  * @param string $marker   The marker to extract the strings from.
     65  * @return string[] An array of strings from a file (.htaccess) from between BEGIN and END markers.
     66  */
     67 function extract_from_markers( $filename, $marker ) {
     68 	$result = array();
     69 
     70 	if ( ! file_exists( $filename ) ) {
     71 		return $result;
     72 	}
     73 
     74 	$markerdata = explode( "\n", implode( '', file( $filename ) ) );
     75 
     76 	$state = false;
     77 	foreach ( $markerdata as $markerline ) {
     78 		if ( false !== strpos( $markerline, '# END ' . $marker ) ) {
     79 			$state = false;
     80 		}
     81 		if ( $state ) {
     82 			if ( '#' === substr( $markerline, 0, 1 ) ) {
     83 				continue;
     84 			}
     85 			$result[] = $markerline;
     86 		}
     87 		if ( false !== strpos( $markerline, '# BEGIN ' . $marker ) ) {
     88 			$state = true;
     89 		}
     90 	}
     91 
     92 	return $result;
     93 }
     94 
     95 /**
     96  * Inserts an array of strings into a file (.htaccess), placing it between
     97  * BEGIN and END markers.
     98  *
     99  * Replaces existing marked info. Retains surrounding
    100  * data. Creates file if none exists.
    101  *
    102  * @since 1.5.0
    103  *
    104  * @param string       $filename  Filename to alter.
    105  * @param string       $marker    The marker to alter.
    106  * @param array|string $insertion The new content to insert.
    107  * @return bool True on write success, false on failure.
    108  */
    109 function insert_with_markers( $filename, $marker, $insertion ) {
    110 	if ( ! file_exists( $filename ) ) {
    111 		if ( ! is_writable( dirname( $filename ) ) ) {
    112 			return false;
    113 		}
    114 
    115 		if ( ! touch( $filename ) ) {
    116 			return false;
    117 		}
    118 
    119 		// Make sure the file is created with a minimum set of permissions.
    120 		$perms = fileperms( $filename );
    121 		if ( $perms ) {
    122 			chmod( $filename, $perms | 0644 );
    123 		}
    124 	} elseif ( ! is_writable( $filename ) ) {
    125 		return false;
    126 	}
    127 
    128 	if ( ! is_array( $insertion ) ) {
    129 		$insertion = explode( "\n", $insertion );
    130 	}
    131 
    132 	$switched_locale = switch_to_locale( get_locale() );
    133 
    134 	$instructions = sprintf(
    135 		/* translators: 1: Marker. */
    136 		__(
    137 			'The directives (lines) between "BEGIN %1$s" and "END %1$s" are
    138 dynamically generated, and should only be modified via WordPress filters.
    139 Any changes to the directives between these markers will be overwritten.'
    140 		),
    141 		$marker
    142 	);
    143 
    144 	$instructions = explode( "\n", $instructions );
    145 	foreach ( $instructions as $line => $text ) {
    146 		$instructions[ $line ] = '# ' . $text;
    147 	}
    148 
    149 	/**
    150 	 * Filters the inline instructions inserted before the dynamically generated content.
    151 	 *
    152 	 * @since 5.3.0
    153 	 *
    154 	 * @param string[] $instructions Array of lines with inline instructions.
    155 	 * @param string   $marker       The marker being inserted.
    156 	 */
    157 	$instructions = apply_filters( 'insert_with_markers_inline_instructions', $instructions, $marker );
    158 
    159 	if ( $switched_locale ) {
    160 		restore_previous_locale();
    161 	}
    162 
    163 	$insertion = array_merge( $instructions, $insertion );
    164 
    165 	$start_marker = "# BEGIN {$marker}";
    166 	$end_marker   = "# END {$marker}";
    167 
    168 	$fp = fopen( $filename, 'r+' );
    169 	if ( ! $fp ) {
    170 		return false;
    171 	}
    172 
    173 	// Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired.
    174 	flock( $fp, LOCK_EX );
    175 
    176 	$lines = array();
    177 	while ( ! feof( $fp ) ) {
    178 		$lines[] = rtrim( fgets( $fp ), "\r\n" );
    179 	}
    180 
    181 	// Split out the existing file into the preceding lines, and those that appear after the marker.
    182 	$pre_lines        = array();
    183 	$post_lines       = array();
    184 	$existing_lines   = array();
    185 	$found_marker     = false;
    186 	$found_end_marker = false;
    187 	foreach ( $lines as $line ) {
    188 		if ( ! $found_marker && false !== strpos( $line, $start_marker ) ) {
    189 			$found_marker = true;
    190 			continue;
    191 		} elseif ( ! $found_end_marker && false !== strpos( $line, $end_marker ) ) {
    192 			$found_end_marker = true;
    193 			continue;
    194 		}
    195 		if ( ! $found_marker ) {
    196 			$pre_lines[] = $line;
    197 		} elseif ( $found_marker && $found_end_marker ) {
    198 			$post_lines[] = $line;
    199 		} else {
    200 			$existing_lines[] = $line;
    201 		}
    202 	}
    203 
    204 	// Check to see if there was a change.
    205 	if ( $existing_lines === $insertion ) {
    206 		flock( $fp, LOCK_UN );
    207 		fclose( $fp );
    208 
    209 		return true;
    210 	}
    211 
    212 	// Generate the new file data.
    213 	$new_file_data = implode(
    214 		"\n",
    215 		array_merge(
    216 			$pre_lines,
    217 			array( $start_marker ),
    218 			$insertion,
    219 			array( $end_marker ),
    220 			$post_lines
    221 		)
    222 	);
    223 
    224 	// Write to the start of the file, and truncate it to that length.
    225 	fseek( $fp, 0 );
    226 	$bytes = fwrite( $fp, $new_file_data );
    227 	if ( $bytes ) {
    228 		ftruncate( $fp, ftell( $fp ) );
    229 	}
    230 	fflush( $fp );
    231 	flock( $fp, LOCK_UN );
    232 	fclose( $fp );
    233 
    234 	return (bool) $bytes;
    235 }
    236 
    237 /**
    238  * Updates the htaccess file with the current rules if it is writable.
    239  *
    240  * Always writes to the file if it exists and is writable to ensure that we
    241  * blank out old rules.
    242  *
    243  * @since 1.5.0
    244  *
    245  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
    246  *
    247  * @return bool|null True on write success, false on failure. Null in multisite.
    248  */
    249 function save_mod_rewrite_rules() {
    250 	if ( is_multisite() ) {
    251 		return;
    252 	}
    253 
    254 	global $wp_rewrite;
    255 
    256 	// Ensure get_home_path() is declared.
    257 	require_once ABSPATH . 'wp-admin/includes/file.php';
    258 
    259 	$home_path     = get_home_path();
    260 	$htaccess_file = $home_path . '.htaccess';
    261 
    262 	/*
    263 	 * If the file doesn't already exist check for write access to the directory
    264 	 * and whether we have some rules. Else check for write access to the file.
    265 	 */
    266 	if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) && $wp_rewrite->using_mod_rewrite_permalinks() ) || is_writable( $htaccess_file ) ) {
    267 		if ( got_mod_rewrite() ) {
    268 			$rules = explode( "\n", $wp_rewrite->mod_rewrite_rules() );
    269 			return insert_with_markers( $htaccess_file, 'WordPress', $rules );
    270 		}
    271 	}
    272 
    273 	return false;
    274 }
    275 
    276 /**
    277  * Updates the IIS web.config file with the current rules if it is writable.
    278  * If the permalinks do not require rewrite rules then the rules are deleted from the web.config file.
    279  *
    280  * @since 2.8.0
    281  *
    282  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
    283  *
    284  * @return bool|null True on write success, false on failure. Null in multisite.
    285  */
    286 function iis7_save_url_rewrite_rules() {
    287 	if ( is_multisite() ) {
    288 		return;
    289 	}
    290 
    291 	global $wp_rewrite;
    292 
    293 	// Ensure get_home_path() is declared.
    294 	require_once ABSPATH . 'wp-admin/includes/file.php';
    295 
    296 	$home_path       = get_home_path();
    297 	$web_config_file = $home_path . 'web.config';
    298 
    299 	// Using win_is_writable() instead of is_writable() because of a bug in Windows PHP.
    300 	if ( iis7_supports_permalinks() && ( ( ! file_exists( $web_config_file ) && win_is_writable( $home_path ) && $wp_rewrite->using_mod_rewrite_permalinks() ) || win_is_writable( $web_config_file ) ) ) {
    301 		$rule = $wp_rewrite->iis7_url_rewrite_rules( false );
    302 		if ( ! empty( $rule ) ) {
    303 			return iis7_add_rewrite_rule( $web_config_file, $rule );
    304 		} else {
    305 			return iis7_delete_rewrite_rule( $web_config_file );
    306 		}
    307 	}
    308 	return false;
    309 }
    310 
    311 /**
    312  * Update the "recently-edited" file for the plugin or theme editor.
    313  *
    314  * @since 1.5.0
    315  *
    316  * @param string $file
    317  */
    318 function update_recently_edited( $file ) {
    319 	$oldfiles = (array) get_option( 'recently_edited' );
    320 	if ( $oldfiles ) {
    321 		$oldfiles   = array_reverse( $oldfiles );
    322 		$oldfiles[] = $file;
    323 		$oldfiles   = array_reverse( $oldfiles );
    324 		$oldfiles   = array_unique( $oldfiles );
    325 		if ( 5 < count( $oldfiles ) ) {
    326 			array_pop( $oldfiles );
    327 		}
    328 	} else {
    329 		$oldfiles[] = $file;
    330 	}
    331 	update_option( 'recently_edited', $oldfiles );
    332 }
    333 
    334 /**
    335  * Makes a tree structure for the theme editor's file list.
    336  *
    337  * @since 4.9.0
    338  * @access private
    339  *
    340  * @param array $allowed_files List of theme file paths.
    341  * @return array Tree structure for listing theme files.
    342  */
    343 function wp_make_theme_file_tree( $allowed_files ) {
    344 	$tree_list = array();
    345 	foreach ( $allowed_files as $file_name => $absolute_filename ) {
    346 		$list     = explode( '/', $file_name );
    347 		$last_dir = &$tree_list;
    348 		foreach ( $list as $dir ) {
    349 			$last_dir =& $last_dir[ $dir ];
    350 		}
    351 		$last_dir = $file_name;
    352 	}
    353 	return $tree_list;
    354 }
    355 
    356 /**
    357  * Outputs the formatted file list for the theme editor.
    358  *
    359  * @since 4.9.0
    360  * @access private
    361  *
    362  * @global string $relative_file Name of the file being edited relative to the
    363  *                               theme directory.
    364  * @global string $stylesheet    The stylesheet name of the theme being edited.
    365  *
    366  * @param array|string $tree  List of file/folder paths, or filename.
    367  * @param int          $level The aria-level for the current iteration.
    368  * @param int          $size  The aria-setsize for the current iteration.
    369  * @param int          $index The aria-posinset for the current iteration.
    370  */
    371 function wp_print_theme_file_tree( $tree, $level = 2, $size = 1, $index = 1 ) {
    372 	global $relative_file, $stylesheet;
    373 
    374 	if ( is_array( $tree ) ) {
    375 		$index = 0;
    376 		$size  = count( $tree );
    377 		foreach ( $tree as $label => $theme_file ) :
    378 			$index++;
    379 			if ( ! is_array( $theme_file ) ) {
    380 				wp_print_theme_file_tree( $theme_file, $level, $index, $size );
    381 				continue;
    382 			}
    383 			?>
    384 			<li role="treeitem" aria-expanded="true" tabindex="-1"
    385 				aria-level="<?php echo esc_attr( $level ); ?>"
    386 				aria-setsize="<?php echo esc_attr( $size ); ?>"
    387 				aria-posinset="<?php echo esc_attr( $index ); ?>">
    388 				<span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"><?php _e( 'folder' ); ?></span><span aria-hidden="true" class="icon"></span></span>
    389 				<ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, $level + 1, $index, $size ); ?></ul>
    390 			</li>
    391 			<?php
    392 		endforeach;
    393 	} else {
    394 		$filename = $tree;
    395 		$url      = add_query_arg(
    396 			array(
    397 				'file'  => rawurlencode( $tree ),
    398 				'theme' => rawurlencode( $stylesheet ),
    399 			),
    400 			self_admin_url( 'theme-editor.php' )
    401 		);
    402 		?>
    403 		<li role="none" class="<?php echo esc_attr( $relative_file === $filename ? 'current-file' : '' ); ?>">
    404 			<a role="treeitem" tabindex="<?php echo esc_attr( $relative_file === $filename ? '0' : '-1' ); ?>"
    405 				href="<?php echo esc_url( $url ); ?>"
    406 				aria-level="<?php echo esc_attr( $level ); ?>"
    407 				aria-setsize="<?php echo esc_attr( $size ); ?>"
    408 				aria-posinset="<?php echo esc_attr( $index ); ?>">
    409 				<?php
    410 				$file_description = esc_html( get_file_description( $filename ) );
    411 				if ( $file_description !== $filename && wp_basename( $filename ) !== $file_description ) {
    412 					$file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>';
    413 				}
    414 
    415 				if ( $relative_file === $filename ) {
    416 					echo '<span class="notice notice-info">' . $file_description . '</span>';
    417 				} else {
    418 					echo $file_description;
    419 				}
    420 				?>
    421 			</a>
    422 		</li>
    423 		<?php
    424 	}
    425 }
    426 
    427 /**
    428  * Makes a tree structure for the plugin editor's file list.
    429  *
    430  * @since 4.9.0
    431  * @access private
    432  *
    433  * @param array $plugin_editable_files List of plugin file paths.
    434  * @return array Tree structure for listing plugin files.
    435  */
    436 function wp_make_plugin_file_tree( $plugin_editable_files ) {
    437 	$tree_list = array();
    438 	foreach ( $plugin_editable_files as $plugin_file ) {
    439 		$list     = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) );
    440 		$last_dir = &$tree_list;
    441 		foreach ( $list as $dir ) {
    442 			$last_dir =& $last_dir[ $dir ];
    443 		}
    444 		$last_dir = $plugin_file;
    445 	}
    446 	return $tree_list;
    447 }
    448 
    449 /**
    450  * Outputs the formatted file list for the plugin editor.
    451  *
    452  * @since 4.9.0
    453  * @access private
    454  *
    455  * @param array|string $tree  List of file/folder paths, or filename.
    456  * @param string       $label Name of file or folder to print.
    457  * @param int          $level The aria-level for the current iteration.
    458  * @param int          $size  The aria-setsize for the current iteration.
    459  * @param int          $index The aria-posinset for the current iteration.
    460  */
    461 function wp_print_plugin_file_tree( $tree, $label = '', $level = 2, $size = 1, $index = 1 ) {
    462 	global $file, $plugin;
    463 	if ( is_array( $tree ) ) {
    464 		$index = 0;
    465 		$size  = count( $tree );
    466 		foreach ( $tree as $label => $plugin_file ) :
    467 			$index++;
    468 			if ( ! is_array( $plugin_file ) ) {
    469 				wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size );
    470 				continue;
    471 			}
    472 			?>
    473 			<li role="treeitem" aria-expanded="true" tabindex="-1"
    474 				aria-level="<?php echo esc_attr( $level ); ?>"
    475 				aria-setsize="<?php echo esc_attr( $size ); ?>"
    476 				aria-posinset="<?php echo esc_attr( $index ); ?>">
    477 				<span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"><?php _e( 'folder' ); ?></span><span aria-hidden="true" class="icon"></span></span>
    478 				<ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, '', $level + 1, $index, $size ); ?></ul>
    479 			</li>
    480 			<?php
    481 		endforeach;
    482 	} else {
    483 		$url = add_query_arg(
    484 			array(
    485 				'file'   => rawurlencode( $tree ),
    486 				'plugin' => rawurlencode( $plugin ),
    487 			),
    488 			self_admin_url( 'plugin-editor.php' )
    489 		);
    490 		?>
    491 		<li role="none" class="<?php echo esc_attr( $file === $tree ? 'current-file' : '' ); ?>">
    492 			<a role="treeitem" tabindex="<?php echo esc_attr( $file === $tree ? '0' : '-1' ); ?>"
    493 				href="<?php echo esc_url( $url ); ?>"
    494 				aria-level="<?php echo esc_attr( $level ); ?>"
    495 				aria-setsize="<?php echo esc_attr( $size ); ?>"
    496 				aria-posinset="<?php echo esc_attr( $index ); ?>">
    497 				<?php
    498 				if ( $file === $tree ) {
    499 					echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>';
    500 				} else {
    501 					echo esc_html( $label );
    502 				}
    503 				?>
    504 			</a>
    505 		</li>
    506 		<?php
    507 	}
    508 }
    509 
    510 /**
    511  * Flushes rewrite rules if siteurl, home or page_on_front changed.
    512  *
    513  * @since 2.1.0
    514  *
    515  * @param string $old_value
    516  * @param string $value
    517  */
    518 function update_home_siteurl( $old_value, $value ) {
    519 	if ( wp_installing() ) {
    520 		return;
    521 	}
    522 
    523 	if ( is_multisite() && ms_is_switched() ) {
    524 		delete_option( 'rewrite_rules' );
    525 	} else {
    526 		flush_rewrite_rules();
    527 	}
    528 }
    529 
    530 
    531 /**
    532  * Resets global variables based on $_GET and $_POST
    533  *
    534  * This function resets global variables based on the names passed
    535  * in the $vars array to the value of $_POST[$var] or $_GET[$var] or ''
    536  * if neither is defined.
    537  *
    538  * @since 2.0.0
    539  *
    540  * @param array $vars An array of globals to reset.
    541  */
    542 function wp_reset_vars( $vars ) {
    543 	foreach ( $vars as $var ) {
    544 		if ( empty( $_POST[ $var ] ) ) {
    545 			if ( empty( $_GET[ $var ] ) ) {
    546 				$GLOBALS[ $var ] = '';
    547 			} else {
    548 				$GLOBALS[ $var ] = $_GET[ $var ];
    549 			}
    550 		} else {
    551 			$GLOBALS[ $var ] = $_POST[ $var ];
    552 		}
    553 	}
    554 }
    555 
    556 /**
    557  * Displays the given administration message.
    558  *
    559  * @since 2.1.0
    560  *
    561  * @param string|WP_Error $message
    562  */
    563 function show_message( $message ) {
    564 	if ( is_wp_error( $message ) ) {
    565 		if ( $message->get_error_data() && is_string( $message->get_error_data() ) ) {
    566 			$message = $message->get_error_message() . ': ' . $message->get_error_data();
    567 		} else {
    568 			$message = $message->get_error_message();
    569 		}
    570 	}
    571 	echo "<p>$message</p>\n";
    572 	wp_ob_end_flush_all();
    573 	flush();
    574 }
    575 
    576 /**
    577  * @since 2.8.0
    578  *
    579  * @param string $content
    580  * @return array
    581  */
    582 function wp_doc_link_parse( $content ) {
    583 	if ( ! is_string( $content ) || empty( $content ) ) {
    584 		return array();
    585 	}
    586 
    587 	if ( ! function_exists( 'token_get_all' ) ) {
    588 		return array();
    589 	}
    590 
    591 	$tokens           = token_get_all( $content );
    592 	$count            = count( $tokens );
    593 	$functions        = array();
    594 	$ignore_functions = array();
    595 	for ( $t = 0; $t < $count - 2; $t++ ) {
    596 		if ( ! is_array( $tokens[ $t ] ) ) {
    597 			continue;
    598 		}
    599 
    600 		if ( T_STRING == $tokens[ $t ][0] && ( '(' === $tokens[ $t + 1 ] || '(' === $tokens[ $t + 2 ] ) ) {
    601 			// If it's a function or class defined locally, there's not going to be any docs available.
    602 			if ( ( isset( $tokens[ $t - 2 ][1] ) && in_array( $tokens[ $t - 2 ][1], array( 'function', 'class' ), true ) )
    603 				|| ( isset( $tokens[ $t - 2 ][0] ) && T_OBJECT_OPERATOR == $tokens[ $t - 1 ][0] )
    604 			) {
    605 				$ignore_functions[] = $tokens[ $t ][1];
    606 			}
    607 			// Add this to our stack of unique references.
    608 			$functions[] = $tokens[ $t ][1];
    609 		}
    610 	}
    611 
    612 	$functions = array_unique( $functions );
    613 	sort( $functions );
    614 
    615 	/**
    616 	 * Filters the list of functions and classes to be ignored from the documentation lookup.
    617 	 *
    618 	 * @since 2.8.0
    619 	 *
    620 	 * @param string[] $ignore_functions Array of names of functions and classes to be ignored.
    621 	 */
    622 	$ignore_functions = apply_filters( 'documentation_ignore_functions', $ignore_functions );
    623 
    624 	$ignore_functions = array_unique( $ignore_functions );
    625 
    626 	$out = array();
    627 	foreach ( $functions as $function ) {
    628 		if ( in_array( $function, $ignore_functions, true ) ) {
    629 			continue;
    630 		}
    631 		$out[] = $function;
    632 	}
    633 
    634 	return $out;
    635 }
    636 
    637 /**
    638  * Saves option for number of rows when listing posts, pages, comments, etc.
    639  *
    640  * @since 2.8.0
    641  */
    642 function set_screen_options() {
    643 
    644 	if ( isset( $_POST['wp_screen_options'] ) && is_array( $_POST['wp_screen_options'] ) ) {
    645 		check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
    646 
    647 		$user = wp_get_current_user();
    648 		if ( ! $user ) {
    649 			return;
    650 		}
    651 		$option = $_POST['wp_screen_options']['option'];
    652 		$value  = $_POST['wp_screen_options']['value'];
    653 
    654 		if ( sanitize_key( $option ) != $option ) {
    655 			return;
    656 		}
    657 
    658 		$map_option = $option;
    659 		$type       = str_replace( 'edit_', '', $map_option );
    660 		$type       = str_replace( '_per_page', '', $type );
    661 		if ( in_array( $type, get_taxonomies(), true ) ) {
    662 			$map_option = 'edit_tags_per_page';
    663 		} elseif ( in_array( $type, get_post_types(), true ) ) {
    664 			$map_option = 'edit_per_page';
    665 		} else {
    666 			$option = str_replace( '-', '_', $option );
    667 		}
    668 
    669 		switch ( $map_option ) {
    670 			case 'edit_per_page':
    671 			case 'users_per_page':
    672 			case 'edit_comments_per_page':
    673 			case 'upload_per_page':
    674 			case 'edit_tags_per_page':
    675 			case 'plugins_per_page':
    676 			case 'export_personal_data_requests_per_page':
    677 			case 'remove_personal_data_requests_per_page':
    678 				// Network admin.
    679 			case 'sites_network_per_page':
    680 			case 'users_network_per_page':
    681 			case 'site_users_network_per_page':
    682 			case 'plugins_network_per_page':
    683 			case 'themes_network_per_page':
    684 			case 'site_themes_network_per_page':
    685 				$value = (int) $value;
    686 				if ( $value < 1 || $value > 999 ) {
    687 					return;
    688 				}
    689 				break;
    690 			default:
    691 				$screen_option = false;
    692 
    693 				if ( '_page' === substr( $option, -5 ) || 'layout_columns' === $option ) {
    694 					/**
    695 					 * Filters a screen option value before it is set.
    696 					 *
    697 					 * The filter can also be used to modify non-standard [items]_per_page
    698 					 * settings. See the parent function for a full list of standard options.
    699 					 *
    700 					 * Returning false from the filter will skip saving the current option.
    701 					 *
    702 					 * @since 2.8.0
    703 					 * @since 5.4.2 Only applied to options ending with '_page',
    704 					 *              or the 'layout_columns' option.
    705 					 *
    706 					 * @see set_screen_options()
    707 					 *
    708 					 * @param mixed  $screen_option The value to save instead of the option value.
    709 					 *                              Default false (to skip saving the current option).
    710 					 * @param string $option        The option name.
    711 					 * @param int    $value         The option value.
    712 					 */
    713 					$screen_option = apply_filters( 'set-screen-option', $screen_option, $option, $value ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
    714 				}
    715 
    716 				/**
    717 				 * Filters a screen option value before it is set.
    718 				 *
    719 				 * The dynamic portion of the hook, `$option`, refers to the option name.
    720 				 *
    721 				 * Returning false from the filter will skip saving the current option.
    722 				 *
    723 				 * @since 5.4.2
    724 				 *
    725 				 * @see set_screen_options()
    726 				 *
    727 				 * @param mixed   $screen_option The value to save instead of the option value.
    728 				 *                               Default false (to skip saving the current option).
    729 				 * @param string  $option        The option name.
    730 				 * @param int     $value         The option value.
    731 				 */
    732 				$value = apply_filters( "set_screen_option_{$option}", $screen_option, $option, $value );
    733 
    734 				if ( false === $value ) {
    735 					return;
    736 				}
    737 				break;
    738 		}
    739 
    740 		update_user_meta( $user->ID, $option, $value );
    741 
    742 		$url = remove_query_arg( array( 'pagenum', 'apage', 'paged' ), wp_get_referer() );
    743 		if ( isset( $_POST['mode'] ) ) {
    744 			$url = add_query_arg( array( 'mode' => $_POST['mode'] ), $url );
    745 		}
    746 
    747 		wp_safe_redirect( $url );
    748 		exit;
    749 	}
    750 }
    751 
    752 /**
    753  * Check if rewrite rule for WordPress already exists in the IIS 7+ configuration file
    754  *
    755  * @since 2.8.0
    756  *
    757  * @return bool
    758  * @param string $filename The file path to the configuration file
    759  */
    760 function iis7_rewrite_rule_exists( $filename ) {
    761 	if ( ! file_exists( $filename ) ) {
    762 		return false;
    763 	}
    764 	if ( ! class_exists( 'DOMDocument', false ) ) {
    765 		return false;
    766 	}
    767 
    768 	$doc = new DOMDocument();
    769 	if ( $doc->load( $filename ) === false ) {
    770 		return false;
    771 	}
    772 	$xpath = new DOMXPath( $doc );
    773 	$rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' );
    774 	if ( 0 == $rules->length ) {
    775 		return false;
    776 	} else {
    777 		return true;
    778 	}
    779 }
    780 
    781 /**
    782  * Delete WordPress rewrite rule from web.config file if it exists there
    783  *
    784  * @since 2.8.0
    785  *
    786  * @param string $filename Name of the configuration file
    787  * @return bool
    788  */
    789 function iis7_delete_rewrite_rule( $filename ) {
    790 	// If configuration file does not exist then rules also do not exist, so there is nothing to delete.
    791 	if ( ! file_exists( $filename ) ) {
    792 		return true;
    793 	}
    794 
    795 	if ( ! class_exists( 'DOMDocument', false ) ) {
    796 		return false;
    797 	}
    798 
    799 	$doc                     = new DOMDocument();
    800 	$doc->preserveWhiteSpace = false;
    801 
    802 	if ( $doc->load( $filename ) === false ) {
    803 		return false;
    804 	}
    805 	$xpath = new DOMXPath( $doc );
    806 	$rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' );
    807 	if ( $rules->length > 0 ) {
    808 		$child  = $rules->item( 0 );
    809 		$parent = $child->parentNode;
    810 		$parent->removeChild( $child );
    811 		$doc->formatOutput = true;
    812 		saveDomDocument( $doc, $filename );
    813 	}
    814 	return true;
    815 }
    816 
    817 /**
    818  * Add WordPress rewrite rule to the IIS 7+ configuration file.
    819  *
    820  * @since 2.8.0
    821  *
    822  * @param string $filename The file path to the configuration file
    823  * @param string $rewrite_rule The XML fragment with URL Rewrite rule
    824  * @return bool
    825  */
    826 function iis7_add_rewrite_rule( $filename, $rewrite_rule ) {
    827 	if ( ! class_exists( 'DOMDocument', false ) ) {
    828 		return false;
    829 	}
    830 
    831 	// If configuration file does not exist then we create one.
    832 	if ( ! file_exists( $filename ) ) {
    833 		$fp = fopen( $filename, 'w' );
    834 		fwrite( $fp, '<configuration/>' );
    835 		fclose( $fp );
    836 	}
    837 
    838 	$doc                     = new DOMDocument();
    839 	$doc->preserveWhiteSpace = false;
    840 
    841 	if ( $doc->load( $filename ) === false ) {
    842 		return false;
    843 	}
    844 
    845 	$xpath = new DOMXPath( $doc );
    846 
    847 	// First check if the rule already exists as in that case there is no need to re-add it.
    848 	$wordpress_rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' );
    849 	if ( $wordpress_rules->length > 0 ) {
    850 		return true;
    851 	}
    852 
    853 	// Check the XPath to the rewrite rule and create XML nodes if they do not exist.
    854 	$xmlnodes = $xpath->query( '/configuration/system.webServer/rewrite/rules' );
    855 	if ( $xmlnodes->length > 0 ) {
    856 		$rules_node = $xmlnodes->item( 0 );
    857 	} else {
    858 		$rules_node = $doc->createElement( 'rules' );
    859 
    860 		$xmlnodes = $xpath->query( '/configuration/system.webServer/rewrite' );
    861 		if ( $xmlnodes->length > 0 ) {
    862 			$rewrite_node = $xmlnodes->item( 0 );
    863 			$rewrite_node->appendChild( $rules_node );
    864 		} else {
    865 			$rewrite_node = $doc->createElement( 'rewrite' );
    866 			$rewrite_node->appendChild( $rules_node );
    867 
    868 			$xmlnodes = $xpath->query( '/configuration/system.webServer' );
    869 			if ( $xmlnodes->length > 0 ) {
    870 				$system_webServer_node = $xmlnodes->item( 0 );
    871 				$system_webServer_node->appendChild( $rewrite_node );
    872 			} else {
    873 				$system_webServer_node = $doc->createElement( 'system.webServer' );
    874 				$system_webServer_node->appendChild( $rewrite_node );
    875 
    876 				$xmlnodes = $xpath->query( '/configuration' );
    877 				if ( $xmlnodes->length > 0 ) {
    878 					$config_node = $xmlnodes->item( 0 );
    879 					$config_node->appendChild( $system_webServer_node );
    880 				} else {
    881 					$config_node = $doc->createElement( 'configuration' );
    882 					$doc->appendChild( $config_node );
    883 					$config_node->appendChild( $system_webServer_node );
    884 				}
    885 			}
    886 		}
    887 	}
    888 
    889 	$rule_fragment = $doc->createDocumentFragment();
    890 	$rule_fragment->appendXML( $rewrite_rule );
    891 	$rules_node->appendChild( $rule_fragment );
    892 
    893 	$doc->encoding     = 'UTF-8';
    894 	$doc->formatOutput = true;
    895 	saveDomDocument( $doc, $filename );
    896 
    897 	return true;
    898 }
    899 
    900 /**
    901  * Saves the XML document into a file
    902  *
    903  * @since 2.8.0
    904  *
    905  * @param DOMDocument $doc
    906  * @param string      $filename
    907  */
    908 function saveDomDocument( $doc, $filename ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
    909 	$config = $doc->saveXML();
    910 	$config = preg_replace( "/([^\r])\n/", "$1\r\n", $config );
    911 	$fp     = fopen( $filename, 'w' );
    912 	fwrite( $fp, $config );
    913 	fclose( $fp );
    914 }
    915 
    916 /**
    917  * Display the default admin color scheme picker (Used in user-edit.php)
    918  *
    919  * @since 3.0.0
    920  *
    921  * @global array $_wp_admin_css_colors
    922  *
    923  * @param int $user_id User ID.
    924  */
    925 function admin_color_scheme_picker( $user_id ) {
    926 	global $_wp_admin_css_colors;
    927 
    928 	ksort( $_wp_admin_css_colors );
    929 
    930 	if ( isset( $_wp_admin_css_colors['fresh'] ) ) {
    931 		// Set Default ('fresh') and Light should go first.
    932 		$_wp_admin_css_colors = array_filter(
    933 			array_merge(
    934 				array(
    935 					'fresh'  => '',
    936 					'light'  => '',
    937 					'modern' => '',
    938 				),
    939 				$_wp_admin_css_colors
    940 			)
    941 		);
    942 	}
    943 
    944 	$current_color = get_user_option( 'admin_color', $user_id );
    945 
    946 	if ( empty( $current_color ) || ! isset( $_wp_admin_css_colors[ $current_color ] ) ) {
    947 		$current_color = 'fresh';
    948 	}
    949 
    950 	?>
    951 	<fieldset id="color-picker" class="scheme-list">
    952 		<legend class="screen-reader-text"><span><?php _e( 'Admin Color Scheme' ); ?></span></legend>
    953 		<?php
    954 		wp_nonce_field( 'save-color-scheme', 'color-nonce', false );
    955 		foreach ( $_wp_admin_css_colors as $color => $color_info ) :
    956 
    957 			?>
    958 			<div class="color-option <?php echo ( $color == $current_color ) ? 'selected' : ''; ?>">
    959 				<input name="admin_color" id="admin_color_<?php echo esc_attr( $color ); ?>" type="radio" value="<?php echo esc_attr( $color ); ?>" class="tog" <?php checked( $color, $current_color ); ?> />
    960 				<input type="hidden" class="css_url" value="<?php echo esc_url( $color_info->url ); ?>" />
    961 				<input type="hidden" class="icon_colors" value="<?php echo esc_attr( wp_json_encode( array( 'icons' => $color_info->icon_colors ) ) ); ?>" />
    962 				<label for="admin_color_<?php echo esc_attr( $color ); ?>"><?php echo esc_html( $color_info->name ); ?></label>
    963 				<table class="color-palette">
    964 					<tr>
    965 					<?php
    966 
    967 					foreach ( $color_info->colors as $html_color ) {
    968 						?>
    969 						<td style="background-color: <?php echo esc_attr( $html_color ); ?>">&nbsp;</td>
    970 						<?php
    971 					}
    972 
    973 					?>
    974 					</tr>
    975 				</table>
    976 			</div>
    977 			<?php
    978 
    979 		endforeach;
    980 
    981 		?>
    982 	</fieldset>
    983 	<?php
    984 }
    985 
    986 /**
    987  *
    988  * @global array $_wp_admin_css_colors
    989  */
    990 function wp_color_scheme_settings() {
    991 	global $_wp_admin_css_colors;
    992 
    993 	$color_scheme = get_user_option( 'admin_color' );
    994 
    995 	// It's possible to have a color scheme set that is no longer registered.
    996 	if ( empty( $_wp_admin_css_colors[ $color_scheme ] ) ) {
    997 		$color_scheme = 'fresh';
    998 	}
    999 
   1000 	if ( ! empty( $_wp_admin_css_colors[ $color_scheme ]->icon_colors ) ) {
   1001 		$icon_colors = $_wp_admin_css_colors[ $color_scheme ]->icon_colors;
   1002 	} elseif ( ! empty( $_wp_admin_css_colors['fresh']->icon_colors ) ) {
   1003 		$icon_colors = $_wp_admin_css_colors['fresh']->icon_colors;
   1004 	} else {
   1005 		// Fall back to the default set of icon colors if the default scheme is missing.
   1006 		$icon_colors = array(
   1007 			'base'    => '#a7aaad',
   1008 			'focus'   => '#72aee6',
   1009 			'current' => '#fff',
   1010 		);
   1011 	}
   1012 
   1013 	echo '<script type="text/javascript">var _wpColorScheme = ' . wp_json_encode( array( 'icons' => $icon_colors ) ) . ";</script>\n";
   1014 }
   1015 
   1016 /**
   1017  * Displays the viewport meta in the admin.
   1018  *
   1019  * @since 5.5.0
   1020  */
   1021 function wp_admin_viewport_meta() {
   1022 	/**
   1023 	 * Filters the viewport meta in the admin.
   1024 	 *
   1025 	 * @since 5.5.0
   1026 	 *
   1027 	 * @param string $viewport_meta The viewport meta.
   1028 	 */
   1029 	$viewport_meta = apply_filters( 'admin_viewport_meta', 'width=device-width,initial-scale=1.0' );
   1030 
   1031 	if ( empty( $viewport_meta ) ) {
   1032 		return;
   1033 	}
   1034 
   1035 	echo '<meta name="viewport" content="' . esc_attr( $viewport_meta ) . '">';
   1036 }
   1037 
   1038 /**
   1039  * Adds viewport meta for mobile in Customizer.
   1040  *
   1041  * Hooked to the {@see 'admin_viewport_meta'} filter.
   1042  *
   1043  * @since 5.5.0
   1044  *
   1045  * @param string $viewport_meta The viewport meta.
   1046  * @return string Filtered viewport meta.
   1047  */
   1048 function _customizer_mobile_viewport_meta( $viewport_meta ) {
   1049 	return trim( $viewport_meta, ',' ) . ',minimum-scale=0.5,maximum-scale=1.2';
   1050 }
   1051 
   1052 /**
   1053  * Check lock status for posts displayed on the Posts screen
   1054  *
   1055  * @since 3.6.0
   1056  *
   1057  * @param array  $response  The Heartbeat response.
   1058  * @param array  $data      The $_POST data sent.
   1059  * @param string $screen_id The screen ID.
   1060  * @return array The Heartbeat response.
   1061  */
   1062 function wp_check_locked_posts( $response, $data, $screen_id ) {
   1063 	$checked = array();
   1064 
   1065 	if ( array_key_exists( 'wp-check-locked-posts', $data ) && is_array( $data['wp-check-locked-posts'] ) ) {
   1066 		foreach ( $data['wp-check-locked-posts'] as $key ) {
   1067 			$post_id = absint( substr( $key, 5 ) );
   1068 			if ( ! $post_id ) {
   1069 				continue;
   1070 			}
   1071 
   1072 			$user_id = wp_check_post_lock( $post_id );
   1073 			if ( $user_id ) {
   1074 				$user = get_userdata( $user_id );
   1075 				if ( $user && current_user_can( 'edit_post', $post_id ) ) {
   1076 					$send = array(
   1077 						/* translators: %s: User's display name. */
   1078 						'text' => sprintf( __( '%s is currently editing' ), $user->display_name ),
   1079 					);
   1080 
   1081 					if ( get_option( 'show_avatars' ) ) {
   1082 						$send['avatar_src']    = get_avatar_url( $user->ID, array( 'size' => 18 ) );
   1083 						$send['avatar_src_2x'] = get_avatar_url( $user->ID, array( 'size' => 36 ) );
   1084 					}
   1085 
   1086 					$checked[ $key ] = $send;
   1087 				}
   1088 			}
   1089 		}
   1090 	}
   1091 
   1092 	if ( ! empty( $checked ) ) {
   1093 		$response['wp-check-locked-posts'] = $checked;
   1094 	}
   1095 
   1096 	return $response;
   1097 }
   1098 
   1099 /**
   1100  * Check lock status on the New/Edit Post screen and refresh the lock
   1101  *
   1102  * @since 3.6.0
   1103  *
   1104  * @param array  $response  The Heartbeat response.
   1105  * @param array  $data      The $_POST data sent.
   1106  * @param string $screen_id The screen ID.
   1107  * @return array The Heartbeat response.
   1108  */
   1109 function wp_refresh_post_lock( $response, $data, $screen_id ) {
   1110 	if ( array_key_exists( 'wp-refresh-post-lock', $data ) ) {
   1111 		$received = $data['wp-refresh-post-lock'];
   1112 		$send     = array();
   1113 
   1114 		$post_id = absint( $received['post_id'] );
   1115 		if ( ! $post_id ) {
   1116 			return $response;
   1117 		}
   1118 
   1119 		if ( ! current_user_can( 'edit_post', $post_id ) ) {
   1120 			return $response;
   1121 		}
   1122 
   1123 		$user_id = wp_check_post_lock( $post_id );
   1124 		$user    = get_userdata( $user_id );
   1125 		if ( $user ) {
   1126 			$error = array(
   1127 				/* translators: %s: User's display name. */
   1128 				'text' => sprintf( __( '%s has taken over and is currently editing.' ), $user->display_name ),
   1129 			);
   1130 
   1131 			if ( get_option( 'show_avatars' ) ) {
   1132 				$error['avatar_src']    = get_avatar_url( $user->ID, array( 'size' => 64 ) );
   1133 				$error['avatar_src_2x'] = get_avatar_url( $user->ID, array( 'size' => 128 ) );
   1134 			}
   1135 
   1136 			$send['lock_error'] = $error;
   1137 		} else {
   1138 			$new_lock = wp_set_post_lock( $post_id );
   1139 			if ( $new_lock ) {
   1140 				$send['new_lock'] = implode( ':', $new_lock );
   1141 			}
   1142 		}
   1143 
   1144 		$response['wp-refresh-post-lock'] = $send;
   1145 	}
   1146 
   1147 	return $response;
   1148 }
   1149 
   1150 /**
   1151  * Check nonce expiration on the New/Edit Post screen and refresh if needed
   1152  *
   1153  * @since 3.6.0
   1154  *
   1155  * @param array  $response  The Heartbeat response.
   1156  * @param array  $data      The $_POST data sent.
   1157  * @param string $screen_id The screen ID.
   1158  * @return array The Heartbeat response.
   1159  */
   1160 function wp_refresh_post_nonces( $response, $data, $screen_id ) {
   1161 	if ( array_key_exists( 'wp-refresh-post-nonces', $data ) ) {
   1162 		$received                           = $data['wp-refresh-post-nonces'];
   1163 		$response['wp-refresh-post-nonces'] = array( 'check' => 1 );
   1164 
   1165 		$post_id = absint( $received['post_id'] );
   1166 		if ( ! $post_id ) {
   1167 			return $response;
   1168 		}
   1169 
   1170 		if ( ! current_user_can( 'edit_post', $post_id ) ) {
   1171 			return $response;
   1172 		}
   1173 
   1174 		$response['wp-refresh-post-nonces'] = array(
   1175 			'replace' => array(
   1176 				'getpermalinknonce'    => wp_create_nonce( 'getpermalink' ),
   1177 				'samplepermalinknonce' => wp_create_nonce( 'samplepermalink' ),
   1178 				'closedpostboxesnonce' => wp_create_nonce( 'closedpostboxes' ),
   1179 				'_ajax_linking_nonce'  => wp_create_nonce( 'internal-linking' ),
   1180 				'_wpnonce'             => wp_create_nonce( 'update-post_' . $post_id ),
   1181 			),
   1182 		);
   1183 	}
   1184 
   1185 	return $response;
   1186 }
   1187 
   1188 /**
   1189  * Add the latest Heartbeat and REST-API nonce to the Heartbeat response.
   1190  *
   1191  * @since 5.0.0
   1192  *
   1193  * @param array $response The Heartbeat response.
   1194  * @return array The Heartbeat response.
   1195  */
   1196 function wp_refresh_heartbeat_nonces( $response ) {
   1197 	// Refresh the Rest API nonce.
   1198 	$response['rest_nonce'] = wp_create_nonce( 'wp_rest' );
   1199 
   1200 	// Refresh the Heartbeat nonce.
   1201 	$response['heartbeat_nonce'] = wp_create_nonce( 'heartbeat-nonce' );
   1202 	return $response;
   1203 }
   1204 
   1205 /**
   1206  * Disable suspension of Heartbeat on the Add/Edit Post screens.
   1207  *
   1208  * @since 3.8.0
   1209  *
   1210  * @global string $pagenow
   1211  *
   1212  * @param array $settings An array of Heartbeat settings.
   1213  * @return array Filtered Heartbeat settings.
   1214  */
   1215 function wp_heartbeat_set_suspension( $settings ) {
   1216 	global $pagenow;
   1217 
   1218 	if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) {
   1219 		$settings['suspension'] = 'disable';
   1220 	}
   1221 
   1222 	return $settings;
   1223 }
   1224 
   1225 /**
   1226  * Autosave with heartbeat
   1227  *
   1228  * @since 3.9.0
   1229  *
   1230  * @param array $response The Heartbeat response.
   1231  * @param array $data     The $_POST data sent.
   1232  * @return array The Heartbeat response.
   1233  */
   1234 function heartbeat_autosave( $response, $data ) {
   1235 	if ( ! empty( $data['wp_autosave'] ) ) {
   1236 		$saved = wp_autosave( $data['wp_autosave'] );
   1237 
   1238 		if ( is_wp_error( $saved ) ) {
   1239 			$response['wp_autosave'] = array(
   1240 				'success' => false,
   1241 				'message' => $saved->get_error_message(),
   1242 			);
   1243 		} elseif ( empty( $saved ) ) {
   1244 			$response['wp_autosave'] = array(
   1245 				'success' => false,
   1246 				'message' => __( 'Error while saving.' ),
   1247 			);
   1248 		} else {
   1249 			/* translators: Draft saved date format, see https://www.php.net/manual/datetime.format.php */
   1250 			$draft_saved_date_format = __( 'g:i:s a' );
   1251 			$response['wp_autosave'] = array(
   1252 				'success' => true,
   1253 				/* translators: %s: Date and time. */
   1254 				'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ),
   1255 			);
   1256 		}
   1257 	}
   1258 
   1259 	return $response;
   1260 }
   1261 
   1262 /**
   1263  * Remove single-use URL parameters and create canonical link based on new URL.
   1264  *
   1265  * Remove specific query string parameters from a URL, create the canonical link,
   1266  * put it in the admin header, and change the current URL to match.
   1267  *
   1268  * @since 4.2.0
   1269  */
   1270 function wp_admin_canonical_url() {
   1271 	$removable_query_args = wp_removable_query_args();
   1272 
   1273 	if ( empty( $removable_query_args ) ) {
   1274 		return;
   1275 	}
   1276 
   1277 	// Ensure we're using an absolute URL.
   1278 	$current_url  = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
   1279 	$filtered_url = remove_query_arg( $removable_query_args, $current_url );
   1280 	?>
   1281 	<link id="wp-admin-canonical" rel="canonical" href="<?php echo esc_url( $filtered_url ); ?>" />
   1282 	<script>
   1283 		if ( window.history.replaceState ) {
   1284 			window.history.replaceState( null, null, document.getElementById( 'wp-admin-canonical' ).href + window.location.hash );
   1285 		}
   1286 	</script>
   1287 	<?php
   1288 }
   1289 
   1290 /**
   1291  * Send a referrer policy header so referrers are not sent externally from administration screens.
   1292  *
   1293  * @since 4.9.0
   1294  */
   1295 function wp_admin_headers() {
   1296 	$policy = 'strict-origin-when-cross-origin';
   1297 
   1298 	/**
   1299 	 * Filters the admin referrer policy header value.
   1300 	 *
   1301 	 * @since 4.9.0
   1302 	 * @since 4.9.5 The default value was changed to 'strict-origin-when-cross-origin'.
   1303 	 *
   1304 	 * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
   1305 	 *
   1306 	 * @param string $policy The admin referrer policy header value. Default 'strict-origin-when-cross-origin'.
   1307 	 */
   1308 	$policy = apply_filters( 'admin_referrer_policy', $policy );
   1309 
   1310 	header( sprintf( 'Referrer-Policy: %s', $policy ) );
   1311 }
   1312 
   1313 /**
   1314  * Outputs JS that reloads the page if the user navigated to it with the Back or Forward button.
   1315  *
   1316  * Used on the Edit Post and Add New Post screens. Needed to ensure the page is not loaded from browser cache,
   1317  * so the post title and editor content are the last saved versions. Ideally this script should run first in the head.
   1318  *
   1319  * @since 4.6.0
   1320  */
   1321 function wp_page_reload_on_back_button_js() {
   1322 	?>
   1323 	<script>
   1324 		if ( typeof performance !== 'undefined' && performance.navigation && performance.navigation.type === 2 ) {
   1325 			document.location.reload( true );
   1326 		}
   1327 	</script>
   1328 	<?php
   1329 }
   1330 
   1331 /**
   1332  * Send a confirmation request email when a change of site admin email address is attempted.
   1333  *
   1334  * The new site admin address will not become active until confirmed.
   1335  *
   1336  * @since 3.0.0
   1337  * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
   1338  *
   1339  * @param string $old_value The old site admin email address.
   1340  * @param string $value     The proposed new site admin email address.
   1341  */
   1342 function update_option_new_admin_email( $old_value, $value ) {
   1343 	if ( get_option( 'admin_email' ) === $value || ! is_email( $value ) ) {
   1344 		return;
   1345 	}
   1346 
   1347 	$hash            = md5( $value . time() . wp_rand() );
   1348 	$new_admin_email = array(
   1349 		'hash'     => $hash,
   1350 		'newemail' => $value,
   1351 	);
   1352 	update_option( 'adminhash', $new_admin_email );
   1353 
   1354 	$switched_locale = switch_to_locale( get_user_locale() );
   1355 
   1356 	/* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
   1357 	$email_text = __(
   1358 		'Howdy ###USERNAME###,
   1359 
   1360 You recently requested to have the administration email address on
   1361 your site changed.
   1362 
   1363 If this is correct, please click on the following link to change it:
   1364 ###ADMIN_URL###
   1365 
   1366 You can safely ignore and delete this email if you do not want to
   1367 take this action.
   1368 
   1369 This email has been sent to ###EMAIL###
   1370 
   1371 Regards,
   1372 All at ###SITENAME###
   1373 ###SITEURL###'
   1374 	);
   1375 
   1376 	/**
   1377 	 * Filters the text of the email sent when a change of site admin email address is attempted.
   1378 	 *
   1379 	 * The following strings have a special meaning and will get replaced dynamically:
   1380 	 * ###USERNAME###  The current user's username.
   1381 	 * ###ADMIN_URL### The link to click on to confirm the email change.
   1382 	 * ###EMAIL###     The proposed new site admin email address.
   1383 	 * ###SITENAME###  The name of the site.
   1384 	 * ###SITEURL###   The URL to the site.
   1385 	 *
   1386 	 * @since MU (3.0.0)
   1387 	 * @since 4.9.0 This filter is no longer Multisite specific.
   1388 	 *
   1389 	 * @param string $email_text      Text in the email.
   1390 	 * @param array  $new_admin_email {
   1391 	 *     Data relating to the new site admin email address.
   1392 	 *
   1393 	 *     @type string $hash     The secure hash used in the confirmation link URL.
   1394 	 *     @type string $newemail The proposed new site admin email address.
   1395 	 * }
   1396 	 */
   1397 	$content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email );
   1398 
   1399 	$current_user = wp_get_current_user();
   1400 	$content      = str_replace( '###USERNAME###', $current_user->user_login, $content );
   1401 	$content      = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash=' . $hash ) ), $content );
   1402 	$content      = str_replace( '###EMAIL###', $value, $content );
   1403 	$content      = str_replace( '###SITENAME###', wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $content );
   1404 	$content      = str_replace( '###SITEURL###', home_url(), $content );
   1405 
   1406 	wp_mail(
   1407 		$value,
   1408 		sprintf(
   1409 			/* translators: New admin email address notification email subject. %s: Site title. */
   1410 			__( '[%s] New Admin Email Address' ),
   1411 			wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES )
   1412 		),
   1413 		$content
   1414 	);
   1415 
   1416 	if ( $switched_locale ) {
   1417 		restore_previous_locale();
   1418 	}
   1419 }
   1420 
   1421 /**
   1422  * Appends '(Draft)' to draft page titles in the privacy page dropdown
   1423  * so that unpublished content is obvious.
   1424  *
   1425  * @since 4.9.8
   1426  * @access private
   1427  *
   1428  * @param string  $title Page title.
   1429  * @param WP_Post $page  Page data object.
   1430  * @return string Page title.
   1431  */
   1432 function _wp_privacy_settings_filter_draft_page_titles( $title, $page ) {
   1433 	if ( 'draft' === $page->post_status && 'privacy' === get_current_screen()->id ) {
   1434 		/* translators: %s: Page title. */
   1435 		$title = sprintf( __( '%s (Draft)' ), $title );
   1436 	}
   1437 
   1438 	return $title;
   1439 }
   1440 
   1441 /**
   1442  * Checks if the user needs to update PHP.
   1443  *
   1444  * @since 5.1.0
   1445  * @since 5.1.1 Added the {@see 'wp_is_php_version_acceptable'} filter.
   1446  *
   1447  * @return array|false Array of PHP version data. False on failure.
   1448  */
   1449 function wp_check_php_version() {
   1450 	$version = phpversion();
   1451 	$key     = md5( $version );
   1452 
   1453 	$response = get_site_transient( 'php_check_' . $key );
   1454 	if ( false === $response ) {
   1455 		$url = 'http://api.wordpress.org/core/serve-happy/1.0/';
   1456 		if ( wp_http_supports( array( 'ssl' ) ) ) {
   1457 			$url = set_url_scheme( $url, 'https' );
   1458 		}
   1459 
   1460 		$url = add_query_arg( 'php_version', $version, $url );
   1461 
   1462 		$response = wp_remote_get( $url );
   1463 
   1464 		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
   1465 			return false;
   1466 		}
   1467 
   1468 		/**
   1469 		 * Response should be an array with:
   1470 		 *  'recommended_version' - string - The PHP version recommended by WordPress.
   1471 		 *  'is_supported' - boolean - Whether the PHP version is actively supported.
   1472 		 *  'is_secure' - boolean - Whether the PHP version receives security updates.
   1473 		 *  'is_acceptable' - boolean - Whether the PHP version is still acceptable for WordPress.
   1474 		 */
   1475 		$response = json_decode( wp_remote_retrieve_body( $response ), true );
   1476 
   1477 		if ( ! is_array( $response ) ) {
   1478 			return false;
   1479 		}
   1480 
   1481 		set_site_transient( 'php_check_' . $key, $response, WEEK_IN_SECONDS );
   1482 	}
   1483 
   1484 	if ( isset( $response['is_acceptable'] ) && $response['is_acceptable'] ) {
   1485 		/**
   1486 		 * Filters whether the active PHP version is considered acceptable by WordPress.
   1487 		 *
   1488 		 * Returning false will trigger a PHP version warning to show up in the admin dashboard to administrators.
   1489 		 *
   1490 		 * This filter is only run if the wordpress.org Serve Happy API considers the PHP version acceptable, ensuring
   1491 		 * that this filter can only make this check stricter, but not loosen it.
   1492 		 *
   1493 		 * @since 5.1.1
   1494 		 *
   1495 		 * @param bool   $is_acceptable Whether the PHP version is considered acceptable. Default true.
   1496 		 * @param string $version       PHP version checked.
   1497 		 */
   1498 		$response['is_acceptable'] = (bool) apply_filters( 'wp_is_php_version_acceptable', true, $version );
   1499 	}
   1500 
   1501 	return $response;
   1502 }