* Misc WordPress Administration API.
* @subpackage Administration
* Returns whether the server is running Apache with the mod_rewrite module loaded.
* @return bool Whether the server is running Apache with the mod_rewrite module loaded.
function got_mod_rewrite() {
$got_rewrite = apache_mod_loaded( 'mod_rewrite', true );
* Filters whether Apache and mod_rewrite are present.
* This filter was previously used to force URL rewriting for other servers,
* like nginx. Use the {@see 'got_url_rewrite'} filter in got_url_rewrite() instead.
* @param bool $got_rewrite Whether Apache and mod_rewrite are present.
return apply_filters( 'got_rewrite', $got_rewrite );
* Returns whether the server supports URL rewriting.
* Detects Apache's mod_rewrite, IIS 7.0+ permalink support, and nginx.
* @return bool Whether the server supports URL rewriting.
function got_url_rewrite() {
$got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || iis7_supports_permalinks() );
* Filters whether URL rewriting is available.
* @param bool $got_url_rewrite Whether URL rewriting is available.
return apply_filters( 'got_url_rewrite', $got_url_rewrite );
* Extracts strings from between the BEGIN and END markers in the .htaccess file.
* @param string $filename Filename to extract the strings from.
* @param string $marker The marker to extract the strings from.
* @return string[] An array of strings from a file (.htaccess) from between BEGIN and END markers.
function extract_from_markers( $filename, $marker ) {
if ( ! file_exists( $filename ) ) {
$markerdata = explode( "\n", implode( '', file( $filename ) ) );
foreach ( $markerdata as $markerline ) {
if ( false !== strpos( $markerline, '# END ' . $marker ) ) {
if ( '#' === substr( $markerline, 0, 1 ) ) {
if ( false !== strpos( $markerline, '# BEGIN ' . $marker ) ) {
* Inserts an array of strings into a file (.htaccess), placing it between
* Replaces existing marked info. Retains surrounding
* data. Creates file if none exists.
* @param string $filename Filename to alter.
* @param string $marker The marker to alter.
* @param array|string $insertion The new content to insert.
* @return bool True on write success, false on failure.
function insert_with_markers( $filename, $marker, $insertion ) {
if ( ! file_exists( $filename ) ) {
if ( ! is_writable( dirname( $filename ) ) ) {
if ( ! touch( $filename ) ) {
// Make sure the file is created with a minimum set of permissions.
$perms = fileperms( $filename );
chmod( $filename, $perms | 0644 );
} elseif ( ! is_writable( $filename ) ) {
if ( ! is_array( $insertion ) ) {
$insertion = explode( "\n", $insertion );
$switched_locale = switch_to_locale( get_locale() );
/* translators: 1: Marker. */
'The directives (lines) between "BEGIN %1$s" and "END %1$s" are
dynamically generated, and should only be modified via WordPress filters.
Any changes to the directives between these markers will be overwritten.'
$instructions = explode( "\n", $instructions );
foreach ( $instructions as $line => $text ) {
$instructions[ $line ] = '# ' . $text;
* Filters the inline instructions inserted before the dynamically generated content.
* @param string[] $instructions Array of lines with inline instructions.
* @param string $marker The marker being inserted.
$instructions = apply_filters( 'insert_with_markers_inline_instructions', $instructions, $marker );
if ( $switched_locale ) {
restore_previous_locale();
$insertion = array_merge( $instructions, $insertion );
$start_marker = "# BEGIN {$marker}";
$end_marker = "# END {$marker}";
$fp = fopen( $filename, 'r+' );
// Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired.
while ( ! feof( $fp ) ) {
$lines[] = rtrim( fgets( $fp ), "\r\n" );
// Split out the existing file into the preceding lines, and those that appear after the marker.
$existing_lines = array();
$found_end_marker = false;
foreach ( $lines as $line ) {
if ( ! $found_marker && false !== strpos( $line, $start_marker ) ) {
} elseif ( ! $found_end_marker && false !== strpos( $line, $end_marker ) ) {
$found_end_marker = true;
} elseif ( $found_marker && $found_end_marker ) {
$existing_lines[] = $line;
// Check to see if there was a change.
if ( $existing_lines === $insertion ) {
// Generate the new file data.
$new_file_data = implode(
// Write to the start of the file, and truncate it to that length.
$bytes = fwrite( $fp, $new_file_data );
ftruncate( $fp, ftell( $fp ) );
* Updates the htaccess file with the current rules if it is writable.
* Always writes to the file if it exists and is writable to ensure that we
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
* @return bool|null True on write success, false on failure. Null in multisite.
function save_mod_rewrite_rules() {
// Ensure get_home_path() is declared.
require_once ABSPATH . 'wp-admin/includes/file.php';
$home_path = get_home_path();
$htaccess_file = $home_path . '.htaccess';
* If the file doesn't already exist check for write access to the directory
* and whether we have some rules. Else check for write access to the file.
if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) && $wp_rewrite->using_mod_rewrite_permalinks() ) || is_writable( $htaccess_file ) ) {
if ( got_mod_rewrite() ) {
$rules = explode( "\n", $wp_rewrite->mod_rewrite_rules() );
return insert_with_markers( $htaccess_file, 'WordPress', $rules );
* Updates the IIS web.config file with the current rules if it is writable.
* If the permalinks do not require rewrite rules then the rules are deleted from the web.config file.
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
* @return bool|null True on write success, false on failure. Null in multisite.
function iis7_save_url_rewrite_rules() {
// Ensure get_home_path() is declared.
require_once ABSPATH . 'wp-admin/includes/file.php';
$home_path = get_home_path();
$web_config_file = $home_path . 'web.config';
// Using win_is_writable() instead of is_writable() because of a bug in Windows PHP.
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 ) ) ) {
$rule = $wp_rewrite->iis7_url_rewrite_rules( false );
if ( ! empty( $rule ) ) {
return iis7_add_rewrite_rule( $web_config_file, $rule );
return iis7_delete_rewrite_rule( $web_config_file );
* Update the "recently-edited" file for the plugin or theme editor.
function update_recently_edited( $file ) {
$oldfiles = (array) get_option( 'recently_edited' );
$oldfiles = array_reverse( $oldfiles );
$oldfiles = array_reverse( $oldfiles );
$oldfiles = array_unique( $oldfiles );
if ( 5 < count( $oldfiles ) ) {
update_option( 'recently_edited', $oldfiles );
* Makes a tree structure for the theme editor's file list.
* @param array $allowed_files List of theme file paths.
* @return array Tree structure for listing theme files.
function wp_make_theme_file_tree( $allowed_files ) {
foreach ( $allowed_files as $file_name => $absolute_filename ) {
$list = explode( '/', $file_name );
foreach ( $list as $dir ) {
$last_dir =& $last_dir[ $dir ];
* Outputs the formatted file list for the theme editor.
* @global string $relative_file Name of the file being edited relative to the
* @global string $stylesheet The stylesheet name of the theme being edited.
* @param array|string $tree List of file/folder paths, or filename.
* @param int $level The aria-level for the current iteration.
* @param int $size The aria-setsize for the current iteration.
* @param int $index The aria-posinset for the current iteration.
function wp_print_theme_file_tree( $tree, $level = 2, $size = 1, $index = 1 ) {
global $relative_file, $stylesheet;
if ( is_array( $tree ) ) {
foreach ( $tree as $label => $theme_file ) :
if ( ! is_array( $theme_file ) ) {
wp_print_theme_file_tree( $theme_file, $level, $index, $size );
<li role="treeitem" aria-expanded="true" tabindex="-1"
aria-level="<?php echo esc_attr( $level ); ?>"
aria-setsize="<?php echo esc_attr( $size ); ?>"
aria-posinset="<?php echo esc_attr( $index ); ?>">
<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>
<ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, $level + 1, $index, $size ); ?></ul>
'file' => rawurlencode( $tree ),
'theme' => rawurlencode( $stylesheet ),
self_admin_url( 'theme-editor.php' )
<li role="none" class="<?php echo esc_attr( $relative_file === $filename ? 'current-file' : '' ); ?>">
<a role="treeitem" tabindex="<?php echo esc_attr( $relative_file === $filename ? '0' : '-1' ); ?>"
href="<?php echo esc_url( $url ); ?>"
aria-level="<?php echo esc_attr( $level ); ?>"
aria-setsize="<?php echo esc_attr( $size ); ?>"
aria-posinset="<?php echo esc_attr( $index ); ?>">
$file_description = esc_html( get_file_description( $filename ) );
if ( $file_description !== $filename && wp_basename( $filename ) !== $file_description ) {
$file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>';
if ( $relative_file === $filename ) {
echo '<span class="notice notice-info">' . $file_description . '</span>';
* Makes a tree structure for the plugin editor's file list.
* @param array $plugin_editable_files List of plugin file paths.
* @return array Tree structure for listing plugin files.
function wp_make_plugin_file_tree( $plugin_editable_files ) {
foreach ( $plugin_editable_files as $plugin_file ) {
$list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) );
foreach ( $list as $dir ) {
$last_dir =& $last_dir[ $dir ];
$last_dir = $plugin_file;
* Outputs the formatted file list for the plugin editor.
* @param array|string $tree List of file/folder paths, or filename.
* @param string $label Name of file or folder to print.
* @param int $level The aria-level for the current iteration.
* @param int $size The aria-setsize for the current iteration.
* @param int $index The aria-posinset for the current iteration.
function wp_print_plugin_file_tree( $tree, $label = '', $level = 2, $size = 1, $index = 1 ) {
if ( is_array( $tree ) ) {
foreach ( $tree as $label => $plugin_file ) :
if ( ! is_array( $plugin_file ) ) {
wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size );
<li role="treeitem" aria-expanded="true" tabindex="-1"
aria-level="<?php echo esc_attr( $level ); ?>"
aria-setsize="<?php echo esc_attr( $size ); ?>"
aria-posinset="<?php echo esc_attr( $index ); ?>">
<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>
<ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, '', $level + 1, $index, $size ); ?></ul>
'file' => rawurlencode( $tree ),
'plugin' => rawurlencode( $plugin ),
self_admin_url( 'plugin-editor.php' )
<li role="none" class="<?php echo esc_attr( $file === $tree ? 'current-file' : '' ); ?>">
<a role="treeitem" tabindex="<?php echo esc_attr( $file === $tree ? '0' : '-1' ); ?>"
href="<?php echo esc_url( $url ); ?>"
aria-level="<?php echo esc_attr( $level ); ?>"
aria-setsize="<?php echo esc_attr( $size ); ?>"
aria-posinset="<?php echo esc_attr( $index ); ?>">
echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>';