wp/wp-admin/includes/plugin.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     7  */
     7  */
     8 
     8 
     9 /**
     9 /**
    10  * Parses the plugin contents to retrieve plugin's metadata.
    10  * Parses the plugin contents to retrieve plugin's metadata.
    11  *
    11  *
    12  * The metadata of the plugin's data searches for the following in the plugin's
    12  * All plugin headers must be on their own line. Plugin description must not have
    13  * header. All plugin data must be on its own line. For plugin description, it
    13  * any newlines, otherwise only parts of the description will be displayed.
    14  * must not have any newlines or only parts of the description will be displayed
    14  * The below is formatted for printing.
    15  * and the same goes for the plugin data. The below is formatted for printing.
       
    16  *
    15  *
    17  *     /*
    16  *     /*
    18  *     Plugin Name: Name of Plugin
    17  *     Plugin Name: Name of the plugin.
    19  *     Plugin URI: Link to plugin information
    18  *     Plugin URI: The home page of the plugin.
    20  *     Description: Plugin Description
    19  *     Description: Plugin description.
    21  *     Author: Plugin author's name
    20  *     Author: Plugin author's name.
    22  *     Author URI: Link to the author's web site
    21  *     Author URI: Link to the author's website.
    23  *     Version: Must be set in the plugin for WordPress 2.3+
    22  *     Version: Plugin version.
    24  *     Text Domain: Optional. Unique identifier, should be same as the one used in
    23  *     Text Domain: Optional. Unique identifier, should be same as the one used in
    25  *          load_plugin_textdomain()
    24  *          load_plugin_textdomain().
    26  *     Domain Path: Optional. Only useful if the translations are located in a
    25  *     Domain Path: Optional. Only useful if the translations are located in a
    27  *          folder above the plugin's base path. For example, if .mo files are
    26  *          folder above the plugin's base path. For example, if .mo files are
    28  *          located in the locale folder then Domain Path will be "/locale/" and
    27  *          located in the locale folder then Domain Path will be "/locale/" and
    29  *          must have the first slash. Defaults to the base folder the plugin is
    28  *          must have the first slash. Defaults to the base folder the plugin is
    30  *          located in.
    29  *          located in.
    31  *     Network: Optional. Specify "Network: true" to require that a plugin is activated
    30  *     Network: Optional. Specify "Network: true" to require that a plugin is activated
    32  *          across all sites in an installation. This will prevent a plugin from being
    31  *          across all sites in an installation. This will prevent a plugin from being
    33  *          activated on a single site when Multisite is enabled.
    32  *          activated on a single site when Multisite is enabled.
    34  *      * / # Remove the space to close comment
    33  *     Requires at least: Optional. Specify the minimum required WordPress version.
    35  *
    34  *     Requires PHP: Optional. Specify the minimum required PHP version.
    36  * Some users have issues with opening large files and manipulating the contents
    35  *     * / # Remove the space to close comment.
    37  * for want is usually the first 1kiB or 2kiB. This function stops pulling in
    36  *
    38  * the plugin contents when it has all of the required plugin data.
    37  * The first 8 KB of the file will be pulled in and if the plugin data is not
    39  *
    38  * within that first 8 KB, then the plugin author should correct their plugin
    40  * The first 8kiB of the file will be pulled in and if the plugin data is not
       
    41  * within that first 8kiB, then the plugin author should correct their plugin
       
    42  * and move the plugin data headers to the top.
    39  * and move the plugin data headers to the top.
    43  *
    40  *
    44  * The plugin file is assumed to have permissions to allow for scripts to read
    41  * The plugin file is assumed to have permissions to allow for scripts to read
    45  * the file. This is not checked however and the file is only opened for
    42  * the file. This is not checked however and the file is only opened for
    46  * reading.
    43  * reading.
    47  *
    44  *
    48  * @since 1.5.0
    45  * @since 1.5.0
       
    46  * @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers.
    49  *
    47  *
    50  * @param string $plugin_file Absolute path to the main plugin file.
    48  * @param string $plugin_file Absolute path to the main plugin file.
       
    49  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
       
    50  *                            Default true.
       
    51  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
       
    52  * @return array {
       
    53  *     Plugin data. Values will be empty if not supplied by the plugin.
       
    54  *
       
    55  *     @type string $Name        Name of the plugin. Should be unique.
       
    56  *     @type string $Title       Title of the plugin and link to the plugin's site (if set).
       
    57  *     @type string $Description Plugin description.
       
    58  *     @type string $Author      Author's name.
       
    59  *     @type string $AuthorURI   Author's website address (if set).
       
    60  *     @type string $Version     Plugin version.
       
    61  *     @type string $TextDomain  Plugin textdomain.
       
    62  *     @type string $DomainPath  Plugins relative directory path to .mo files.
       
    63  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
       
    64  *     @type string $RequiresWP  Minimum required version of WordPress.
       
    65  *     @type string $RequiresPHP Minimum required version of PHP.
       
    66  * }
       
    67  */
       
    68 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
       
    69 
       
    70 	$default_headers = array(
       
    71 		'Name'        => 'Plugin Name',
       
    72 		'PluginURI'   => 'Plugin URI',
       
    73 		'Version'     => 'Version',
       
    74 		'Description' => 'Description',
       
    75 		'Author'      => 'Author',
       
    76 		'AuthorURI'   => 'Author URI',
       
    77 		'TextDomain'  => 'Text Domain',
       
    78 		'DomainPath'  => 'Domain Path',
       
    79 		'Network'     => 'Network',
       
    80 		'RequiresWP'  => 'Requires at least',
       
    81 		'RequiresPHP' => 'Requires PHP',
       
    82 		// Site Wide Only is deprecated in favor of Network.
       
    83 		'_sitewide'   => 'Site Wide Only',
       
    84 	);
       
    85 
       
    86 	$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
       
    87 
       
    88 	// Site Wide Only is the old header for Network.
       
    89 	if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) {
       
    90 		/* translators: 1: Site Wide Only: true, 2: Network: true */
       
    91 		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), '<code>Site Wide Only: true</code>', '<code>Network: true</code>' ) );
       
    92 		$plugin_data['Network'] = $plugin_data['_sitewide'];
       
    93 	}
       
    94 	$plugin_data['Network'] = ( 'true' === strtolower( $plugin_data['Network'] ) );
       
    95 	unset( $plugin_data['_sitewide'] );
       
    96 
       
    97 	// If no text domain is defined fall back to the plugin slug.
       
    98 	if ( ! $plugin_data['TextDomain'] ) {
       
    99 		$plugin_slug = dirname( plugin_basename( $plugin_file ) );
       
   100 		if ( '.' !== $plugin_slug && false === strpos( $plugin_slug, '/' ) ) {
       
   101 			$plugin_data['TextDomain'] = $plugin_slug;
       
   102 		}
       
   103 	}
       
   104 
       
   105 	if ( $markup || $translate ) {
       
   106 		$plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate );
       
   107 	} else {
       
   108 		$plugin_data['Title']      = $plugin_data['Name'];
       
   109 		$plugin_data['AuthorName'] = $plugin_data['Author'];
       
   110 	}
       
   111 
       
   112 	return $plugin_data;
       
   113 }
       
   114 
       
   115 /**
       
   116  * Sanitizes plugin data, optionally adds markup, optionally translates.
       
   117  *
       
   118  * @since 2.7.0
       
   119  *
       
   120  * @see get_plugin_data()
       
   121  *
       
   122  * @access private
       
   123  *
       
   124  * @param string $plugin_file Path to the main plugin file.
       
   125  * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
    51  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
   126  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
    52  *                            Default true.
   127  *                            Default true.
    53  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
   128  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
    54  * @return array {
   129  * @return array {
    55  *     Plugin data. Values will be empty if not supplied by the plugin.
   130  *     Plugin data. Values will be empty if not supplied by the plugin.
    63  *     @type string $TextDomain  Plugin textdomain.
   138  *     @type string $TextDomain  Plugin textdomain.
    64  *     @type string $DomainPath  Plugins relative directory path to .mo files.
   139  *     @type string $DomainPath  Plugins relative directory path to .mo files.
    65  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
   140  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
    66  * }
   141  * }
    67  */
   142  */
    68 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
       
    69 
       
    70 	$default_headers = array(
       
    71 		'Name'        => 'Plugin Name',
       
    72 		'PluginURI'   => 'Plugin URI',
       
    73 		'Version'     => 'Version',
       
    74 		'Description' => 'Description',
       
    75 		'Author'      => 'Author',
       
    76 		'AuthorURI'   => 'Author URI',
       
    77 		'TextDomain'  => 'Text Domain',
       
    78 		'DomainPath'  => 'Domain Path',
       
    79 		'Network'     => 'Network',
       
    80 		// Site Wide Only is deprecated in favor of Network.
       
    81 		'_sitewide'   => 'Site Wide Only',
       
    82 	);
       
    83 
       
    84 	$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
       
    85 
       
    86 	// Site Wide Only is the old header for Network
       
    87 	if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) {
       
    88 		/* translators: 1: Site Wide Only: true, 2: Network: true */
       
    89 		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), '<code>Site Wide Only: true</code>', '<code>Network: true</code>' ) );
       
    90 		$plugin_data['Network'] = $plugin_data['_sitewide'];
       
    91 	}
       
    92 	$plugin_data['Network'] = ( 'true' == strtolower( $plugin_data['Network'] ) );
       
    93 	unset( $plugin_data['_sitewide'] );
       
    94 
       
    95 	// If no text domain is defined fall back to the plugin slug.
       
    96 	if ( ! $plugin_data['TextDomain'] ) {
       
    97 		$plugin_slug = dirname( plugin_basename( $plugin_file ) );
       
    98 		if ( '.' !== $plugin_slug && false === strpos( $plugin_slug, '/' ) ) {
       
    99 			$plugin_data['TextDomain'] = $plugin_slug;
       
   100 		}
       
   101 	}
       
   102 
       
   103 	if ( $markup || $translate ) {
       
   104 		$plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate );
       
   105 	} else {
       
   106 		$plugin_data['Title']      = $plugin_data['Name'];
       
   107 		$plugin_data['AuthorName'] = $plugin_data['Author'];
       
   108 	}
       
   109 
       
   110 	return $plugin_data;
       
   111 }
       
   112 
       
   113 /**
       
   114  * Sanitizes plugin data, optionally adds markup, optionally translates.
       
   115  *
       
   116  * @since 2.7.0
       
   117  *
       
   118  * @see get_plugin_data()
       
   119  *
       
   120  * @access private
       
   121  *
       
   122  * @param string $plugin_file Path to the main plugin file.
       
   123  * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
       
   124  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
       
   125  *                            Default true.
       
   126  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
       
   127  * @return array {
       
   128  *     Plugin data. Values will be empty if not supplied by the plugin.
       
   129  *
       
   130  *     @type string $Name        Name of the plugin. Should be unique.
       
   131  *     @type string $Title       Title of the plugin and link to the plugin's site (if set).
       
   132  *     @type string $Description Plugin description.
       
   133  *     @type string $Author      Author's name.
       
   134  *     @type string $AuthorURI   Author's website address (if set).
       
   135  *     @type string $Version     Plugin version.
       
   136  *     @type string $TextDomain  Plugin textdomain.
       
   137  *     @type string $DomainPath  Plugins relative directory path to .mo files.
       
   138  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
       
   139  * }
       
   140  */
       
   141 function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
   143 function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
   142 
   144 
   143 	// Sanitize the plugin filename to a WP_PLUGIN_DIR relative path
   145 	// Sanitize the plugin filename to a WP_PLUGIN_DIR relative path.
   144 	$plugin_file = plugin_basename( $plugin_file );
   146 	$plugin_file = plugin_basename( $plugin_file );
   145 
   147 
   146 	// Translate fields
   148 	// Translate fields.
   147 	if ( $translate ) {
   149 	if ( $translate ) {
   148 		if ( $textdomain = $plugin_data['TextDomain'] ) {
   150 		$textdomain = $plugin_data['TextDomain'];
       
   151 		if ( $textdomain ) {
   149 			if ( ! is_textdomain_loaded( $textdomain ) ) {
   152 			if ( ! is_textdomain_loaded( $textdomain ) ) {
   150 				if ( $plugin_data['DomainPath'] ) {
   153 				if ( $plugin_data['DomainPath'] ) {
   151 					load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] );
   154 					load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] );
   152 				} else {
   155 				} else {
   153 					load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) );
   156 					load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) );
   154 				}
   157 				}
   155 			}
   158 			}
   156 		} elseif ( 'hello.php' == basename( $plugin_file ) ) {
   159 		} elseif ( 'hello.php' === basename( $plugin_file ) ) {
   157 			$textdomain = 'default';
   160 			$textdomain = 'default';
   158 		}
   161 		}
   159 		if ( $textdomain ) {
   162 		if ( $textdomain ) {
   160 			foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field ) {
   163 			foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field ) {
   161 				// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
   164 				// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
   162 				$plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
   165 				$plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
   163 			}
   166 			}
   164 		}
   167 		}
   165 	}
   168 	}
   166 
   169 
   167 	// Sanitize fields
   170 	// Sanitize fields.
   168 	$allowed_tags_in_links = array(
   171 	$allowed_tags_in_links = array(
   169 		'abbr'    => array( 'title' => true ),
   172 		'abbr'    => array( 'title' => true ),
   170 		'acronym' => array( 'title' => true ),
   173 		'acronym' => array( 'title' => true ),
   171 		'code'    => true,
   174 		'code'    => true,
   172 		'em'      => true,
   175 		'em'      => true,
   191 	$plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] );
   194 	$plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] );
   192 
   195 
   193 	$plugin_data['Title']      = $plugin_data['Name'];
   196 	$plugin_data['Title']      = $plugin_data['Name'];
   194 	$plugin_data['AuthorName'] = $plugin_data['Author'];
   197 	$plugin_data['AuthorName'] = $plugin_data['Author'];
   195 
   198 
   196 	// Apply markup
   199 	// Apply markup.
   197 	if ( $markup ) {
   200 	if ( $markup ) {
   198 		if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) {
   201 		if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) {
   199 			$plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
   202 			$plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
   200 		}
   203 		}
   201 
   204 
   204 		}
   207 		}
   205 
   208 
   206 		$plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
   209 		$plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
   207 
   210 
   208 		if ( $plugin_data['Author'] ) {
   211 		if ( $plugin_data['Author'] ) {
   209 			$plugin_data['Description'] .= ' <cite>' . sprintf( __( 'By %s.' ), $plugin_data['Author'] ) . '</cite>';
   212 			$plugin_data['Description'] .= sprintf(
       
   213 				/* translators: %s: Plugin author. */
       
   214 				' <cite>' . __( 'By %s.' ) . '</cite>',
       
   215 				$plugin_data['Author']
       
   216 			);
   210 		}
   217 		}
   211 	}
   218 	}
   212 
   219 
   213 	return $plugin_data;
   220 	return $plugin_data;
   214 }
   221 }
   217  * Get a list of a plugin's files.
   224  * Get a list of a plugin's files.
   218  *
   225  *
   219  * @since 2.8.0
   226  * @since 2.8.0
   220  *
   227  *
   221  * @param string $plugin Path to the plugin file relative to the plugins directory.
   228  * @param string $plugin Path to the plugin file relative to the plugins directory.
   222  * @return array List of files relative to the plugin root.
   229  * @return string[] Array of file names relative to the plugin root.
   223  */
   230  */
   224 function get_plugin_files( $plugin ) {
   231 function get_plugin_files( $plugin ) {
   225 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
   232 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
   226 	$dir         = dirname( $plugin_file );
   233 	$dir         = dirname( $plugin_file );
   227 
   234 
   264  * optimization purposes.
   271  * optimization purposes.
   265  *
   272  *
   266  * @since 1.5.0
   273  * @since 1.5.0
   267  *
   274  *
   268  * @param string $plugin_folder Optional. Relative path to single plugin folder.
   275  * @param string $plugin_folder Optional. Relative path to single plugin folder.
   269  * @return array Key is the plugin file path and the value is an array of the plugin data.
   276  * @return array[] Array of arrays of plugin data, keyed by plugin file name. See `get_plugin_data()`.
   270  */
   277  */
   271 function get_plugins( $plugin_folder = '' ) {
   278 function get_plugins( $plugin_folder = '' ) {
   272 
   279 
   273 	$cache_plugins = wp_cache_get( 'plugins', 'plugins' );
   280 	$cache_plugins = wp_cache_get( 'plugins', 'plugins' );
   274 	if ( ! $cache_plugins ) {
   281 	if ( ! $cache_plugins ) {
   283 	$plugin_root = WP_PLUGIN_DIR;
   290 	$plugin_root = WP_PLUGIN_DIR;
   284 	if ( ! empty( $plugin_folder ) ) {
   291 	if ( ! empty( $plugin_folder ) ) {
   285 		$plugin_root .= $plugin_folder;
   292 		$plugin_root .= $plugin_folder;
   286 	}
   293 	}
   287 
   294 
   288 	// Files in wp-content/plugins directory
   295 	// Files in wp-content/plugins directory.
   289 	$plugins_dir  = @ opendir( $plugin_root );
   296 	$plugins_dir  = @ opendir( $plugin_root );
   290 	$plugin_files = array();
   297 	$plugin_files = array();
       
   298 
   291 	if ( $plugins_dir ) {
   299 	if ( $plugins_dir ) {
   292 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   300 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   293 			if ( substr( $file, 0, 1 ) == '.' ) {
   301 			if ( '.' === substr( $file, 0, 1 ) ) {
   294 				continue;
   302 				continue;
   295 			}
   303 			}
       
   304 
   296 			if ( is_dir( $plugin_root . '/' . $file ) ) {
   305 			if ( is_dir( $plugin_root . '/' . $file ) ) {
   297 				$plugins_subdir = @ opendir( $plugin_root . '/' . $file );
   306 				$plugins_subdir = @ opendir( $plugin_root . '/' . $file );
       
   307 
   298 				if ( $plugins_subdir ) {
   308 				if ( $plugins_subdir ) {
   299 					while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
   309 					while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
   300 						if ( substr( $subfile, 0, 1 ) == '.' ) {
   310 						if ( '.' === substr( $subfile, 0, 1 ) ) {
   301 							continue;
   311 							continue;
   302 						}
   312 						}
   303 						if ( substr( $subfile, -4 ) == '.php' ) {
   313 
       
   314 						if ( '.php' === substr( $subfile, -4 ) ) {
   304 							$plugin_files[] = "$file/$subfile";
   315 							$plugin_files[] = "$file/$subfile";
   305 						}
   316 						}
   306 					}
   317 					}
       
   318 
   307 					closedir( $plugins_subdir );
   319 					closedir( $plugins_subdir );
   308 				}
   320 				}
   309 			} else {
   321 			} else {
   310 				if ( substr( $file, -4 ) == '.php' ) {
   322 				if ( '.php' === substr( $file, -4 ) ) {
   311 					$plugin_files[] = $file;
   323 					$plugin_files[] = $file;
   312 				}
   324 				}
   313 			}
   325 			}
   314 		}
   326 		}
       
   327 
   315 		closedir( $plugins_dir );
   328 		closedir( $plugins_dir );
   316 	}
   329 	}
   317 
   330 
   318 	if ( empty( $plugin_files ) ) {
   331 	if ( empty( $plugin_files ) ) {
   319 		return $wp_plugins;
   332 		return $wp_plugins;
   322 	foreach ( $plugin_files as $plugin_file ) {
   335 	foreach ( $plugin_files as $plugin_file ) {
   323 		if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
   336 		if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
   324 			continue;
   337 			continue;
   325 		}
   338 		}
   326 
   339 
   327 		$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   340 		// Do not apply markup/translate as it will be cached.
       
   341 		$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false );
   328 
   342 
   329 		if ( empty( $plugin_data['Name'] ) ) {
   343 		if ( empty( $plugin_data['Name'] ) ) {
   330 			continue;
   344 			continue;
   331 		}
   345 		}
   332 
   346 
   345  * Check the mu-plugins directory and retrieve all mu-plugin files with any plugin data.
   359  * Check the mu-plugins directory and retrieve all mu-plugin files with any plugin data.
   346  *
   360  *
   347  * WordPress only includes mu-plugin files in the base mu-plugins directory (wp-content/mu-plugins).
   361  * WordPress only includes mu-plugin files in the base mu-plugins directory (wp-content/mu-plugins).
   348  *
   362  *
   349  * @since 3.0.0
   363  * @since 3.0.0
   350  * @return array Key is the mu-plugin file path and the value is an array of the mu-plugin data.
   364  * @return array[] Array of arrays of mu-plugin data, keyed by plugin file name. See `get_plugin_data()`.
   351  */
   365  */
   352 function get_mu_plugins() {
   366 function get_mu_plugins() {
   353 	$wp_plugins = array();
   367 	$wp_plugins   = array();
   354 	// Files in wp-content/mu-plugins directory
       
   355 	$plugin_files = array();
   368 	$plugin_files = array();
   356 
   369 
   357 	if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
   370 	if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
   358 		return $wp_plugins;
   371 		return $wp_plugins;
   359 	}
   372 	}
   360 	if ( $plugins_dir = @ opendir( WPMU_PLUGIN_DIR ) ) {
   373 
       
   374 	// Files in wp-content/mu-plugins directory.
       
   375 	$plugins_dir = @opendir( WPMU_PLUGIN_DIR );
       
   376 	if ( $plugins_dir ) {
   361 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   377 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   362 			if ( substr( $file, -4 ) == '.php' ) {
   378 			if ( '.php' === substr( $file, -4 ) ) {
   363 				$plugin_files[] = $file;
   379 				$plugin_files[] = $file;
   364 			}
   380 			}
   365 		}
   381 		}
   366 	} else {
   382 	} else {
   367 		return $wp_plugins;
   383 		return $wp_plugins;
   368 	}
   384 	}
   369 
   385 
   370 	@closedir( $plugins_dir );
   386 	closedir( $plugins_dir );
   371 
   387 
   372 	if ( empty( $plugin_files ) ) {
   388 	if ( empty( $plugin_files ) ) {
   373 		return $wp_plugins;
   389 		return $wp_plugins;
   374 	}
   390 	}
   375 
   391 
   376 	foreach ( $plugin_files as $plugin_file ) {
   392 	foreach ( $plugin_files as $plugin_file ) {
   377 		if ( ! is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) ) {
   393 		if ( ! is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) ) {
   378 			continue;
   394 			continue;
   379 		}
   395 		}
   380 
   396 
   381 		$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   397 		// Do not apply markup/translate as it will be cached.
       
   398 		$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false );
   382 
   399 
   383 		if ( empty( $plugin_data['Name'] ) ) {
   400 		if ( empty( $plugin_data['Name'] ) ) {
   384 			$plugin_data['Name'] = $plugin_file;
   401 			$plugin_data['Name'] = $plugin_file;
   385 		}
   402 		}
   386 
   403 
   387 		$wp_plugins[ $plugin_file ] = $plugin_data;
   404 		$wp_plugins[ $plugin_file ] = $plugin_data;
   388 	}
   405 	}
   389 
   406 
   390 	if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php' ) <= 30 ) { // silence is golden
   407 	if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php' ) <= 30 ) {
       
   408 		// Silence is golden.
   391 		unset( $wp_plugins['index.php'] );
   409 		unset( $wp_plugins['index.php'] );
   392 	}
   410 	}
   393 
   411 
   394 	uasort( $wp_plugins, '_sort_uname_callback' );
   412 	uasort( $wp_plugins, '_sort_uname_callback' );
   395 
   413 
   413 
   431 
   414 /**
   432 /**
   415  * Check the wp-content directory and retrieve all drop-ins with any plugin data.
   433  * Check the wp-content directory and retrieve all drop-ins with any plugin data.
   416  *
   434  *
   417  * @since 3.0.0
   435  * @since 3.0.0
   418  * @return array Key is the file path and the value is an array of the plugin data.
   436  * @return array[] Array of arrays of dropin plugin data, keyed by plugin file name. See `get_plugin_data()`.
   419  */
   437  */
   420 function get_dropins() {
   438 function get_dropins() {
   421 	$dropins      = array();
   439 	$dropins      = array();
   422 	$plugin_files = array();
   440 	$plugin_files = array();
   423 
   441 
   424 	$_dropins = _get_dropins();
   442 	$_dropins = _get_dropins();
   425 
   443 
   426 	// These exist in the wp-content directory
   444 	// Files in wp-content directory.
   427 	if ( $plugins_dir = @ opendir( WP_CONTENT_DIR ) ) {
   445 	$plugins_dir = @opendir( WP_CONTENT_DIR );
       
   446 	if ( $plugins_dir ) {
   428 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   447 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   429 			if ( isset( $_dropins[ $file ] ) ) {
   448 			if ( isset( $_dropins[ $file ] ) ) {
   430 				$plugin_files[] = $file;
   449 				$plugin_files[] = $file;
   431 			}
   450 			}
   432 		}
   451 		}
   433 	} else {
   452 	} else {
   434 		return $dropins;
   453 		return $dropins;
   435 	}
   454 	}
   436 
   455 
   437 	@closedir( $plugins_dir );
   456 	closedir( $plugins_dir );
   438 
   457 
   439 	if ( empty( $plugin_files ) ) {
   458 	if ( empty( $plugin_files ) ) {
   440 		return $dropins;
   459 		return $dropins;
   441 	}
   460 	}
   442 
   461 
   443 	foreach ( $plugin_files as $plugin_file ) {
   462 	foreach ( $plugin_files as $plugin_file ) {
   444 		if ( ! is_readable( WP_CONTENT_DIR . "/$plugin_file" ) ) {
   463 		if ( ! is_readable( WP_CONTENT_DIR . "/$plugin_file" ) ) {
   445 			continue;
   464 			continue;
   446 		}
   465 		}
   447 		$plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   466 
       
   467 		// Do not apply markup/translate as it will be cached.
       
   468 		$plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false );
       
   469 
   448 		if ( empty( $plugin_data['Name'] ) ) {
   470 		if ( empty( $plugin_data['Name'] ) ) {
   449 			$plugin_data['Name'] = $plugin_file;
   471 			$plugin_data['Name'] = $plugin_file;
   450 		}
   472 		}
       
   473 
   451 		$dropins[ $plugin_file ] = $plugin_data;
   474 		$dropins[ $plugin_file ] = $plugin_data;
   452 	}
   475 	}
   453 
   476 
   454 	uksort( $dropins, 'strnatcasecmp' );
   477 	uksort( $dropins, 'strnatcasecmp' );
   455 
   478 
   460  * Returns drop-ins that WordPress uses.
   483  * Returns drop-ins that WordPress uses.
   461  *
   484  *
   462  * Includes Multisite drop-ins only when is_multisite()
   485  * Includes Multisite drop-ins only when is_multisite()
   463  *
   486  *
   464  * @since 3.0.0
   487  * @since 3.0.0
   465  * @return array Key is file name. The value is an array, with the first value the
   488  * @return array[] Key is file name. The value is an array, with the first value the
   466  *  purpose of the drop-in and the second value the name of the constant that must be
   489  *  purpose of the drop-in and the second value the name of the constant that must be
   467  *  true for the drop-in to be used, or true if no constant is required.
   490  *  true for the drop-in to be used, or true if no constant is required.
   468  */
   491  */
   469 function _get_dropins() {
   492 function _get_dropins() {
   470 	$dropins = array(
   493 	$dropins = array(
   471 		'advanced-cache.php'      => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE
   494 		'advanced-cache.php'      => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ),  // WP_CACHE
   472 		'db.php'                  => array( __( 'Custom database class.' ), true ), // auto on load
   495 		'db.php'                  => array( __( 'Custom database class.' ), true ),          // Auto on load.
   473 		'db-error.php'            => array( __( 'Custom database error message.' ), true ), // auto on error
   496 		'db-error.php'            => array( __( 'Custom database error message.' ), true ),  // Auto on error.
   474 		'install.php'             => array( __( 'Custom installation script.' ), true ), // auto on installation
   497 		'install.php'             => array( __( 'Custom installation script.' ), true ),     // Auto on installation.
   475 		'maintenance.php'         => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance
   498 		'maintenance.php'         => array( __( 'Custom maintenance message.' ), true ),     // Auto on maintenance.
   476 		'object-cache.php'        => array( __( 'External object cache.' ), true ), // auto on load
   499 		'object-cache.php'        => array( __( 'External object cache.' ), true ),          // Auto on load.
   477 		'php-error.php'           => array( __( 'Custom PHP error message.' ), true ), // auto on error
   500 		'php-error.php'           => array( __( 'Custom PHP error message.' ), true ),       // Auto on error.
   478 		'fatal-error-handler.php' => array( __( 'Custom PHP fatal error handler.' ), true ), // auto on error
   501 		'fatal-error-handler.php' => array( __( 'Custom PHP fatal error handler.' ), true ), // Auto on error.
   479 	);
   502 	);
   480 
   503 
   481 	if ( is_multisite() ) {
   504 	if ( is_multisite() ) {
   482 		$dropins['sunrise.php']        = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE
   505 		$dropins['sunrise.php']        = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE
   483 		$dropins['blog-deleted.php']   = array( __( 'Custom site deleted message.' ), true ); // auto on deleted blog
   506 		$dropins['blog-deleted.php']   = array( __( 'Custom site deleted message.' ), true );   // Auto on deleted blog.
   484 		$dropins['blog-inactive.php']  = array( __( 'Custom site inactive message.' ), true ); // auto on inactive blog
   507 		$dropins['blog-inactive.php']  = array( __( 'Custom site inactive message.' ), true );  // Auto on inactive blog.
   485 		$dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // auto on archived or spammed blog
   508 		$dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // Auto on archived or spammed blog.
   486 	}
   509 	}
   487 
   510 
   488 	return $dropins;
   511 	return $dropins;
   489 }
   512 }
   490 
   513 
   504  *
   527  *
   505  * @param string $plugin Path to the plugin file relative to the plugins directory.
   528  * @param string $plugin Path to the plugin file relative to the plugins directory.
   506  * @return bool True, if in the active plugins list. False, not in the list.
   529  * @return bool True, if in the active plugins list. False, not in the list.
   507  */
   530  */
   508 function is_plugin_active( $plugin ) {
   531 function is_plugin_active( $plugin ) {
   509 	return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin );
   532 	return in_array( $plugin, (array) get_option( 'active_plugins', array() ), true ) || is_plugin_active_for_network( $plugin );
   510 }
   533 }
   511 
   534 
   512 /**
   535 /**
   513  * Determines whether the plugin is inactive.
   536  * Determines whether the plugin is inactive.
   514  *
   537  *
   517  * For more information on this and similar theme functions, check out
   540  * For more information on this and similar theme functions, check out
   518  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
   541  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
   519  * Conditional Tags} article in the Theme Developer Handbook.
   542  * Conditional Tags} article in the Theme Developer Handbook.
   520  *
   543  *
   521  * @since 3.1.0
   544  * @since 3.1.0
       
   545  *
   522  * @see is_plugin_active()
   546  * @see is_plugin_active()
   523  *
   547  *
   524  * @param string $plugin Path to the plugin file relative to the plugins directory.
   548  * @param string $plugin Path to the plugin file relative to the plugins directory.
   525  * @return bool True if inactive. False if active.
   549  * @return bool True if inactive. False if active.
   526  */
   550  */
   602  * @param string $plugin       Path to the plugin file relative to the plugins directory.
   626  * @param string $plugin       Path to the plugin file relative to the plugins directory.
   603  * @param string $redirect     Optional. URL to redirect to.
   627  * @param string $redirect     Optional. URL to redirect to.
   604  * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network
   628  * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network
   605  *                             or just the current site. Multisite only. Default false.
   629  *                             or just the current site. Multisite only. Default false.
   606  * @param bool   $silent       Optional. Whether to prevent calling activation hooks. Default false.
   630  * @param bool   $silent       Optional. Whether to prevent calling activation hooks. Default false.
   607  * @return WP_Error|null WP_Error on invalid file or null on success.
   631  * @return null|WP_Error Null on success, WP_Error on invalid file.
   608  */
   632  */
   609 function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
   633 function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
   610 	$plugin = plugin_basename( trim( $plugin ) );
   634 	$plugin = plugin_basename( trim( $plugin ) );
   611 
   635 
   612 	if ( is_multisite() && ( $network_wide || is_network_only_plugin( $plugin ) ) ) {
   636 	if ( is_multisite() && ( $network_wide || is_network_only_plugin( $plugin ) ) ) {
   625 	$requirements = validate_plugin_requirements( $plugin );
   649 	$requirements = validate_plugin_requirements( $plugin );
   626 	if ( is_wp_error( $requirements ) ) {
   650 	if ( is_wp_error( $requirements ) ) {
   627 		return $requirements;
   651 		return $requirements;
   628 	}
   652 	}
   629 
   653 
   630 	if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current ) ) ) {
   654 	if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current, true ) ) ) {
   631 		if ( ! empty( $redirect ) ) {
   655 		if ( ! empty( $redirect ) ) {
   632 			wp_redirect( add_query_arg( '_error_nonce', wp_create_nonce( 'plugin-activation-error_' . $plugin ), $redirect ) ); // we'll override this later if the plugin can be included without fatal error
   656 			// We'll override this later if the plugin can be included without fatal error.
       
   657 			wp_redirect( add_query_arg( '_error_nonce', wp_create_nonce( 'plugin-activation-error_' . $plugin ), $redirect ) );
   633 		}
   658 		}
   634 
   659 
   635 		ob_start();
   660 		ob_start();
       
   661 
       
   662 		if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
       
   663 			define( 'WP_SANDBOX_SCRAPING', true );
       
   664 		}
       
   665 
   636 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
   666 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
   637 		$_wp_plugin_file = $plugin;
   667 		$_wp_plugin_file = $plugin;
   638 		if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
   668 		include_once WP_PLUGIN_DIR . '/' . $plugin;
   639 			define( 'WP_SANDBOX_SCRAPING', true );
       
   640 		}
       
   641 		include_once( WP_PLUGIN_DIR . '/' . $plugin );
       
   642 		$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
   669 		$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
   643 
   670 
   644 		if ( ! $silent ) {
   671 		if ( ! $silent ) {
   645 			/**
   672 			/**
   646 			 * Fires before a plugin is activated.
   673 			 * Fires before a plugin is activated.
   650 			 *
   677 			 *
   651 			 * @since 2.9.0
   678 			 * @since 2.9.0
   652 			 *
   679 			 *
   653 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   680 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   654 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   681 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   655 			 *                             or just the current site. Multisite only. Default is false.
   682 			 *                             or just the current site. Multisite only. Default false.
   656 			 */
   683 			 */
   657 			do_action( 'activate_plugin', $plugin, $network_wide );
   684 			do_action( 'activate_plugin', $plugin, $network_wide );
   658 
   685 
   659 			/**
   686 			/**
   660 			 * Fires as a specific plugin is being activated.
   687 			 * Fires as a specific plugin is being activated.
   665 			 * If a plugin is silently activated (such as during an update), this hook does not fire.
   692 			 * If a plugin is silently activated (such as during an update), this hook does not fire.
   666 			 *
   693 			 *
   667 			 * @since 2.0.0
   694 			 * @since 2.0.0
   668 			 *
   695 			 *
   669 			 * @param bool $network_wide Whether to enable the plugin for all sites in the network
   696 			 * @param bool $network_wide Whether to enable the plugin for all sites in the network
   670 			 *                           or just the current site. Multisite only. Default is false.
   697 			 *                           or just the current site. Multisite only. Default false.
   671 			 */
   698 			 */
   672 			do_action( "activate_{$plugin}", $network_wide );
   699 			do_action( "activate_{$plugin}", $network_wide );
   673 		}
   700 		}
   674 
   701 
   675 		if ( $network_wide ) {
   702 		if ( $network_wide ) {
   692 			 *
   719 			 *
   693 			 * @since 2.9.0
   720 			 * @since 2.9.0
   694 			 *
   721 			 *
   695 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   722 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   696 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   723 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   697 			 *                             or just the current site. Multisite only. Default is false.
   724 			 *                             or just the current site. Multisite only. Default false.
   698 			 */
   725 			 */
   699 			do_action( 'activated_plugin', $plugin, $network_wide );
   726 			do_action( 'activated_plugin', $plugin, $network_wide );
   700 		}
   727 		}
   701 
   728 
   702 		if ( ob_get_length() > 0 ) {
   729 		if ( ob_get_length() > 0 ) {
   715  * The deactivation hook is disabled by the plugin upgrader by using the $silent
   742  * The deactivation hook is disabled by the plugin upgrader by using the $silent
   716  * parameter.
   743  * parameter.
   717  *
   744  *
   718  * @since 2.5.0
   745  * @since 2.5.0
   719  *
   746  *
   720  * @param string|array $plugins Single plugin or list of plugins to deactivate.
   747  * @param string|string[] $plugins      Single plugin or list of plugins to deactivate.
   721  * @param bool $silent Prevent calling deactivation hooks. Default is false.
   748  * @param bool            $silent       Prevent calling deactivation hooks. Default false.
   722  * @param mixed $network_wide Whether to deactivate the plugin for all sites in the network.
   749  * @param bool|null       $network_wide Whether to deactivate the plugin for all sites in the network.
   723  *  A value of null (the default) will deactivate plugins for both the site and the network.
   750  *                                      A value of null will deactivate plugins for both the network
       
   751  *                                      and the current site. Multisite only. Default null.
   724  */
   752  */
   725 function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) {
   753 function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) {
   726 	if ( is_multisite() ) {
   754 	if ( is_multisite() ) {
   727 		$network_current = get_site_option( 'active_sitewide_plugins', array() );
   755 		$network_current = get_site_option( 'active_sitewide_plugins', array() );
   728 	}
   756 	}
   729 	$current = get_option( 'active_plugins', array() );
   757 	$current    = get_option( 'active_plugins', array() );
   730 	$do_blog = $do_network = false;
   758 	$do_blog    = false;
       
   759 	$do_network = false;
   731 
   760 
   732 	foreach ( (array) $plugins as $plugin ) {
   761 	foreach ( (array) $plugins as $plugin ) {
   733 		$plugin = plugin_basename( trim( $plugin ) );
   762 		$plugin = plugin_basename( trim( $plugin ) );
   734 		if ( ! is_plugin_active( $plugin ) ) {
   763 		if ( ! is_plugin_active( $plugin ) ) {
   735 			continue;
   764 			continue;
   746 			 *
   775 			 *
   747 			 * @since 2.9.0
   776 			 * @since 2.9.0
   748 			 *
   777 			 *
   749 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   778 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   750 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
   779 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
   751 			 *                                     or just the current site. Multisite only. Default is false.
   780 			 *                                     or just the current site. Multisite only. Default false.
   752 			 */
   781 			 */
   753 			do_action( 'deactivate_plugin', $plugin, $network_deactivating );
   782 			do_action( 'deactivate_plugin', $plugin, $network_deactivating );
   754 		}
   783 		}
   755 
   784 
   756 		if ( false !== $network_wide ) {
   785 		if ( false !== $network_wide ) {
   761 				continue;
   790 				continue;
   762 			}
   791 			}
   763 		}
   792 		}
   764 
   793 
   765 		if ( true !== $network_wide ) {
   794 		if ( true !== $network_wide ) {
   766 			$key = array_search( $plugin, $current );
   795 			$key = array_search( $plugin, $current, true );
   767 			if ( false !== $key ) {
   796 			if ( false !== $key ) {
   768 				$do_blog = true;
   797 				$do_blog = true;
   769 				unset( $current[ $key ] );
   798 				unset( $current[ $key ] );
   770 			}
   799 			}
   771 		}
   800 		}
   785 			 * If a plugin is silently deactivated (such as during an update), this hook does not fire.
   814 			 * If a plugin is silently deactivated (such as during an update), this hook does not fire.
   786 			 *
   815 			 *
   787 			 * @since 2.0.0
   816 			 * @since 2.0.0
   788 			 *
   817 			 *
   789 			 * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network
   818 			 * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network
   790 			 *                                   or just the current site. Multisite only. Default is false.
   819 			 *                                   or just the current site. Multisite only. Default false.
   791 			 */
   820 			 */
   792 			do_action( "deactivate_{$plugin}", $network_deactivating );
   821 			do_action( "deactivate_{$plugin}", $network_deactivating );
   793 
   822 
   794 			/**
   823 			/**
   795 			 * Fires after a plugin is deactivated.
   824 			 * Fires after a plugin is deactivated.
   798 			 * this hook does not fire.
   827 			 * this hook does not fire.
   799 			 *
   828 			 *
   800 			 * @since 2.9.0
   829 			 * @since 2.9.0
   801 			 *
   830 			 *
   802 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   831 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   803 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network.
   832 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
   804 			 *                                     or just the current site. Multisite only. Default false.
   833 			 *                                     or just the current site. Multisite only. Default false.
   805 			 */
   834 			 */
   806 			do_action( 'deactivated_plugin', $plugin, $network_deactivating );
   835 			do_action( 'deactivated_plugin', $plugin, $network_deactivating );
   807 		}
   836 		}
   808 	}
   837 	}
   817 
   846 
   818 /**
   847 /**
   819  * Activate multiple plugins.
   848  * Activate multiple plugins.
   820  *
   849  *
   821  * When WP_Error is returned, it does not mean that one of the plugins had
   850  * When WP_Error is returned, it does not mean that one of the plugins had
   822  * errors. It means that one or more of the plugins file path was invalid.
   851  * errors. It means that one or more of the plugin file paths were invalid.
   823  *
   852  *
   824  * The execution will be halted as soon as one of the plugins has an error.
   853  * The execution will be halted as soon as one of the plugins has an error.
   825  *
   854  *
   826  * @since 2.6.0
   855  * @since 2.6.0
   827  *
   856  *
   828  * @param string|array $plugins Single plugin or list of plugins to activate.
   857  * @param string|string[] $plugins      Single plugin or list of plugins to activate.
   829  * @param string $redirect Redirect to page after successful activation.
   858  * @param string          $redirect     Redirect to page after successful activation.
   830  * @param bool $network_wide Whether to enable the plugin for all sites in the network.
   859  * @param bool            $network_wide Whether to enable the plugin for all sites in the network.
   831  * @param bool $silent Prevent calling activation hooks. Default is false.
   860  *                                      Default false.
       
   861  * @param bool            $silent       Prevent calling activation hooks. Default false.
   832  * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation.
   862  * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation.
   833  */
   863  */
   834 function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) {
   864 function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) {
   835 	if ( ! is_array( $plugins ) ) {
   865 	if ( ! is_array( $plugins ) ) {
   836 		$plugins = array( $plugins );
   866 		$plugins = array( $plugins );
   884 	$credentials = request_filesystem_credentials( $url );
   914 	$credentials = request_filesystem_credentials( $url );
   885 	$data        = ob_get_clean();
   915 	$data        = ob_get_clean();
   886 
   916 
   887 	if ( false === $credentials ) {
   917 	if ( false === $credentials ) {
   888 		if ( ! empty( $data ) ) {
   918 		if ( ! empty( $data ) ) {
   889 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
   919 			require_once ABSPATH . 'wp-admin/admin-header.php';
   890 			echo $data;
   920 			echo $data;
   891 			include( ABSPATH . 'wp-admin/admin-footer.php' );
   921 			require_once ABSPATH . 'wp-admin/admin-footer.php';
   892 			exit;
   922 			exit;
   893 		}
   923 		}
   894 		return;
   924 		return;
   895 	}
   925 	}
   896 
   926 
   897 	if ( ! WP_Filesystem( $credentials ) ) {
   927 	if ( ! WP_Filesystem( $credentials ) ) {
   898 		ob_start();
   928 		ob_start();
   899 		request_filesystem_credentials( $url, '', true ); // Failed to connect, Error and request again.
   929 		// Failed to connect. Error and request again.
       
   930 		request_filesystem_credentials( $url, '', true );
   900 		$data = ob_get_clean();
   931 		$data = ob_get_clean();
   901 
   932 
   902 		if ( ! empty( $data ) ) {
   933 		if ( ! empty( $data ) ) {
   903 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
   934 			require_once ABSPATH . 'wp-admin/admin-header.php';
   904 			echo $data;
   935 			echo $data;
   905 			include( ABSPATH . 'wp-admin/admin-footer.php' );
   936 			require_once ABSPATH . 'wp-admin/admin-footer.php';
   906 			exit;
   937 			exit;
   907 		}
   938 		}
   908 		return;
   939 		return;
   909 	}
   940 	}
   910 
   941 
   944 		do_action( 'delete_plugin', $plugin_file );
   975 		do_action( 'delete_plugin', $plugin_file );
   945 
   976 
   946 		$this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );
   977 		$this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );
   947 
   978 
   948 		// If plugin is in its own directory, recursively delete the directory.
   979 		// If plugin is in its own directory, recursively delete the directory.
   949 		if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir ) { //base check on if plugin includes directory separator AND that it's not the root plugin folder
   980 		// Base check on if plugin includes directory separator AND that it's not the root plugin folder.
       
   981 		if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir ) {
   950 			$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
   982 			$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
   951 		} else {
   983 		} else {
   952 			$deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file );
   984 			$deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file );
   953 		}
   985 		}
   954 
   986 
   983 			}
  1015 			}
   984 		}
  1016 		}
   985 	}
  1017 	}
   986 
  1018 
   987 	// Remove deleted plugins from the plugin updates list.
  1019 	// Remove deleted plugins from the plugin updates list.
   988 	if ( $current = get_site_transient( 'update_plugins' ) ) {
  1020 	$current = get_site_transient( 'update_plugins' );
       
  1021 	if ( $current ) {
   989 		// Don't remove the plugins that weren't deleted.
  1022 		// Don't remove the plugins that weren't deleted.
   990 		$deleted = array_diff( $plugins, $errors );
  1023 		$deleted = array_diff( $plugins, $errors );
   991 
  1024 
   992 		foreach ( $deleted as $plugin_file ) {
  1025 		foreach ( $deleted as $plugin_file ) {
   993 			unset( $current->response[ $plugin_file ] );
  1026 			unset( $current->response[ $plugin_file ] );
   996 		set_site_transient( 'update_plugins', $current );
  1029 		set_site_transient( 'update_plugins', $current );
   997 	}
  1030 	}
   998 
  1031 
   999 	if ( ! empty( $errors ) ) {
  1032 	if ( ! empty( $errors ) ) {
  1000 		if ( 1 === count( $errors ) ) {
  1033 		if ( 1 === count( $errors ) ) {
  1001 			/* translators: %s: plugin filename */
  1034 			/* translators: %s: Plugin filename. */
  1002 			$message = __( 'Could not fully remove the plugin %s.' );
  1035 			$message = __( 'Could not fully remove the plugin %s.' );
  1003 		} else {
  1036 		} else {
  1004 			/* translators: %s: comma-separated list of plugin filenames */
  1037 			/* translators: %s: Comma-separated list of plugin filenames. */
  1005 			$message = __( 'Could not fully remove the plugins %s.' );
  1038 			$message = __( 'Could not fully remove the plugins %s.' );
  1006 		}
  1039 		}
  1007 
  1040 
  1008 		return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) );
  1041 		return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) );
  1009 	}
  1042 	}
  1016  *
  1049  *
  1017  * Validate all active plugins, deactivates invalid and
  1050  * Validate all active plugins, deactivates invalid and
  1018  * returns an array of deactivated ones.
  1051  * returns an array of deactivated ones.
  1019  *
  1052  *
  1020  * @since 2.5.0
  1053  * @since 2.5.0
  1021  * @return array invalid plugins, plugin as key, error as value
  1054  * @return WP_Error[] Array of plugin errors keyed by plugin file name.
  1022  */
  1055  */
  1023 function validate_active_plugins() {
  1056 function validate_active_plugins() {
  1024 	$plugins = get_option( 'active_plugins', array() );
  1057 	$plugins = get_option( 'active_plugins', array() );
  1025 	// Validate vartype: array.
  1058 	// Validate vartype: array.
  1026 	if ( ! is_array( $plugins ) ) {
  1059 	if ( ! is_array( $plugins ) ) {
  1056  * Checks that the main plugin file exists and is a valid plugin. See validate_file().
  1089  * Checks that the main plugin file exists and is a valid plugin. See validate_file().
  1057  *
  1090  *
  1058  * @since 2.5.0
  1091  * @since 2.5.0
  1059  *
  1092  *
  1060  * @param string $plugin Path to the plugin file relative to the plugins directory.
  1093  * @param string $plugin Path to the plugin file relative to the plugins directory.
  1061  * @return WP_Error|int 0 on success, WP_Error on failure.
  1094  * @return int|WP_Error 0 on success, WP_Error on failure.
  1062  */
  1095  */
  1063 function validate_plugin( $plugin ) {
  1096 function validate_plugin( $plugin ) {
  1064 	if ( validate_file( $plugin ) ) {
  1097 	if ( validate_file( $plugin ) ) {
  1065 		return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) );
  1098 		return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) );
  1066 	}
  1099 	}
  1074 	}
  1107 	}
  1075 	return 0;
  1108 	return 0;
  1076 }
  1109 }
  1077 
  1110 
  1078 /**
  1111 /**
  1079  * Validate the plugin requirements for WP version and PHP version.
  1112  * Validates the plugin requirements for WordPress version and PHP version.
       
  1113  *
       
  1114  * Uses the information from `Requires at least` and `Requires PHP` headers
       
  1115  * defined in the plugin's main PHP file.
       
  1116  *
       
  1117  * If the headers are not present in the plugin's main PHP file,
       
  1118  * `readme.txt` is also checked as a fallback.
  1080  *
  1119  *
  1081  * @since 5.2.0
  1120  * @since 5.2.0
       
  1121  * @since 5.3.0 Added support for reading the headers from the plugin's
       
  1122  *              main PHP file, with `readme.txt` as a fallback.
  1082  *
  1123  *
  1083  * @param string $plugin Path to the plugin file relative to the plugins directory.
  1124  * @param string $plugin Path to the plugin file relative to the plugins directory.
  1084  * @return true|WP_Error True if requirements are met, WP_Error on failure.
  1125  * @return true|WP_Error True if requirements are met, WP_Error on failure.
  1085  */
  1126  */
  1086 function validate_plugin_requirements( $plugin ) {
  1127 function validate_plugin_requirements( $plugin ) {
       
  1128 	$plugin_headers = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
       
  1129 
       
  1130 	$requirements = array(
       
  1131 		'requires'     => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '',
       
  1132 		'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '',
       
  1133 	);
       
  1134 
  1087 	$readme_file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/readme.txt';
  1135 	$readme_file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/readme.txt';
  1088 
  1136 
  1089 	if ( file_exists( $readme_file ) ) {
  1137 	if ( file_exists( $readme_file ) ) {
  1090 		$plugin_data = get_file_data(
  1138 		$readme_headers = get_file_data(
  1091 			$readme_file,
  1139 			$readme_file,
  1092 			array(
  1140 			array(
  1093 				'requires'     => 'Requires at least',
  1141 				'requires'     => 'Requires at least',
  1094 				'requires_php' => 'Requires PHP',
  1142 				'requires_php' => 'Requires PHP',
  1095 			),
  1143 			),
  1096 			'plugin'
  1144 			'plugin'
  1097 		);
  1145 		);
  1098 	} else {
  1146 
  1099 		return true;
  1147 		$requirements = array_merge( $readme_headers, $requirements );
  1100 	}
  1148 	}
  1101 
  1149 
  1102 	$plugin_data['wp_compatible']  = is_wp_version_compatible( $plugin_data['requires'] );
  1150 	$compatible_wp  = is_wp_version_compatible( $requirements['requires'] );
  1103 	$plugin_data['php_compatible'] = is_php_version_compatible( $plugin_data['requires_php'] );
  1151 	$compatible_php = is_php_version_compatible( $requirements['requires_php'] );
  1104 
  1152 
  1105 	$plugin_data = array_merge( $plugin_data, get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ) );
  1153 	/* translators: %s: URL to Update PHP page. */
  1106 
  1154 	$php_update_message = '</p><p>' . sprintf(
  1107 	if ( ! $plugin_data['wp_compatible'] && ! $plugin_data['php_compatible'] ) {
  1155 		__( '<a href="%s">Learn more about updating PHP</a>.' ),
       
  1156 		esc_url( wp_get_update_php_url() )
       
  1157 	);
       
  1158 
       
  1159 	$annotation = wp_get_update_php_annotation();
       
  1160 
       
  1161 	if ( $annotation ) {
       
  1162 		$php_update_message .= '</p><p><em>' . $annotation . '</em>';
       
  1163 	}
       
  1164 
       
  1165 	if ( ! $compatible_wp && ! $compatible_php ) {
  1108 		return new WP_Error(
  1166 		return new WP_Error(
  1109 			'plugin_wp_php_incompatible',
  1167 			'plugin_wp_php_incompatible',
  1110 			sprintf(
  1168 			'<p>' . sprintf(
  1111 				/* translators: %s: plugin name */
  1169 				/* translators: 1: Current WordPress version, 2: Current PHP version, 3: Plugin name, 4: Required WordPress version, 5: Required PHP version. */
  1112 				__( '<strong>Error:</strong> Current WordPress and PHP versions do not meet minimum requirements for %s.' ),
  1170 				_x( '<strong>Error:</strong> Current versions of WordPress (%1$s) and PHP (%2$s) do not meet minimum requirements for %3$s. The plugin requires WordPress %4$s and PHP %5$s.', 'plugin' ),
  1113 				$plugin_data['Name']
  1171 				get_bloginfo( 'version' ),
  1114 			)
  1172 				phpversion(),
       
  1173 				$plugin_headers['Name'],
       
  1174 				$requirements['requires'],
       
  1175 				$requirements['requires_php']
       
  1176 			) . $php_update_message . '</p>'
  1115 		);
  1177 		);
  1116 	} elseif ( ! $plugin_data['php_compatible'] ) {
  1178 	} elseif ( ! $compatible_php ) {
  1117 		return new WP_Error(
  1179 		return new WP_Error(
  1118 			'plugin_php_incompatible',
  1180 			'plugin_php_incompatible',
  1119 			sprintf(
  1181 			'<p>' . sprintf(
  1120 				/* translators: %s: plugin name */
  1182 				/* translators: 1: Current PHP version, 2: Plugin name, 3: Required PHP version. */
  1121 				__( '<strong>Error:</strong> Current PHP version does not meet minimum requirements for %s.' ),
  1183 				_x( '<strong>Error:</strong> Current PHP version (%1$s) does not meet minimum requirements for %2$s. The plugin requires PHP %3$s.', 'plugin' ),
  1122 				$plugin_data['Name']
  1184 				phpversion(),
  1123 			)
  1185 				$plugin_headers['Name'],
       
  1186 				$requirements['requires_php']
       
  1187 			) . $php_update_message . '</p>'
  1124 		);
  1188 		);
  1125 	} elseif ( ! $plugin_data['wp_compatible'] ) {
  1189 	} elseif ( ! $compatible_wp ) {
  1126 		return new WP_Error(
  1190 		return new WP_Error(
  1127 			'plugin_wp_incompatible',
  1191 			'plugin_wp_incompatible',
  1128 			sprintf(
  1192 			'<p>' . sprintf(
  1129 				/* translators: %s: plugin name */
  1193 				/* translators: 1: Current WordPress version, 2: Plugin name, 3: Required WordPress version. */
  1130 				__( '<strong>Error:</strong> Current WordPress version does not meet minimum requirements for %s.' ),
  1194 				_x( '<strong>Error:</strong> Current WordPress version (%1$s) does not meet minimum requirements for %2$s. The plugin requires WordPress %3$s.', 'plugin' ),
  1131 				$plugin_data['Name']
  1195 				get_bloginfo( 'version' ),
  1132 			)
  1196 				$plugin_headers['Name'],
       
  1197 				$requirements['requires']
       
  1198 			) . '</p>'
  1133 		);
  1199 		);
  1134 	}
  1200 	}
  1135 
  1201 
  1136 	return true;
  1202 	return true;
  1137 }
  1203 }
  1186 			update_option( 'uninstall_plugins', $uninstallable_plugins );
  1252 			update_option( 'uninstall_plugins', $uninstallable_plugins );
  1187 		}
  1253 		}
  1188 		unset( $uninstallable_plugins );
  1254 		unset( $uninstallable_plugins );
  1189 
  1255 
  1190 		define( 'WP_UNINSTALL_PLUGIN', $file );
  1256 		define( 'WP_UNINSTALL_PLUGIN', $file );
       
  1257 
  1191 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1258 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1192 		include( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' );
  1259 		include_once WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php';
  1193 
  1260 
  1194 		return true;
  1261 		return true;
  1195 	}
  1262 	}
  1196 
  1263 
  1197 	if ( isset( $uninstallable_plugins[ $file ] ) ) {
  1264 	if ( isset( $uninstallable_plugins[ $file ] ) ) {
  1199 		unset( $uninstallable_plugins[ $file ] );
  1266 		unset( $uninstallable_plugins[ $file ] );
  1200 		update_option( 'uninstall_plugins', $uninstallable_plugins );
  1267 		update_option( 'uninstall_plugins', $uninstallable_plugins );
  1201 		unset( $uninstallable_plugins );
  1268 		unset( $uninstallable_plugins );
  1202 
  1269 
  1203 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1270 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1204 		include( WP_PLUGIN_DIR . '/' . $file );
  1271 		include_once WP_PLUGIN_DIR . '/' . $file;
  1205 
  1272 
  1206 		add_action( "uninstall_{$file}", $callable );
  1273 		add_action( "uninstall_{$file}", $callable );
  1207 
  1274 
  1208 		/**
  1275 		/**
  1209 		 * Fires in uninstall_plugin() once the plugin has been uninstalled.
  1276 		 * Fires in uninstall_plugin() once the plugin has been uninstalled.
  1216 		do_action( "uninstall_{$file}" );
  1283 		do_action( "uninstall_{$file}" );
  1217 	}
  1284 	}
  1218 }
  1285 }
  1219 
  1286 
  1220 //
  1287 //
  1221 // Menu
  1288 // Menu.
  1222 //
  1289 //
  1223 
  1290 
  1224 /**
  1291 /**
  1225  * Add a top-level menu page.
  1292  * Add a top-level menu page.
  1226  *
  1293  *
  1248  *                             * Pass a base64-encoded SVG using a data URI, which will be colored to match
  1315  *                             * Pass a base64-encoded SVG using a data URI, which will be colored to match
  1249  *                               the color scheme. This should begin with 'data:image/svg+xml;base64,'.
  1316  *                               the color scheme. This should begin with 'data:image/svg+xml;base64,'.
  1250  *                             * Pass the name of a Dashicons helper class to use a font icon,
  1317  *                             * Pass the name of a Dashicons helper class to use a font icon,
  1251  *                               e.g. 'dashicons-chart-pie'.
  1318  *                               e.g. 'dashicons-chart-pie'.
  1252  *                             * Pass 'none' to leave div.wp-menu-image empty so an icon can be added via CSS.
  1319  *                             * Pass 'none' to leave div.wp-menu-image empty so an icon can be added via CSS.
  1253  * @param int      $position   The position in the menu order this one should appear.
  1320  * @param int      $position   The position in the menu order this item should appear.
  1254  * @return string The resulting page's hook_suffix.
  1321  * @return string The resulting page's hook_suffix.
  1255  */
  1322  */
  1256 function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
  1323 function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
  1257 	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;
  1324 	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;
  1258 
  1325 
  1285 		$menu[ $position ] = $new_menu;
  1352 		$menu[ $position ] = $new_menu;
  1286 	}
  1353 	}
  1287 
  1354 
  1288 	$_registered_pages[ $hookname ] = true;
  1355 	$_registered_pages[ $hookname ] = true;
  1289 
  1356 
  1290 	// No parent as top level
  1357 	// No parent as top level.
  1291 	$_parent_pages[ $menu_slug ] = false;
  1358 	$_parent_pages[ $menu_slug ] = false;
  1292 
  1359 
  1293 	return $hookname;
  1360 	return $hookname;
  1294 }
  1361 }
  1295 
  1362 
  1301  *
  1368  *
  1302  * The function which is hooked in to handle the output of the page must check
  1369  * The function which is hooked in to handle the output of the page must check
  1303  * that the user has the required capability as well.
  1370  * that the user has the required capability as well.
  1304  *
  1371  *
  1305  * @since 1.5.0
  1372  * @since 1.5.0
       
  1373  * @since 5.3.0 Added the `$position` parameter.
  1306  *
  1374  *
  1307  * @global array $submenu
  1375  * @global array $submenu
  1308  * @global array $menu
  1376  * @global array $menu
  1309  * @global array $_wp_real_parent_file
  1377  * @global array $_wp_real_parent_file
  1310  * @global bool  $_wp_submenu_nopriv
  1378  * @global bool  $_wp_submenu_nopriv
  1319  * @param string   $capability  The capability required for this menu to be displayed to the user.
  1387  * @param string   $capability  The capability required for this menu to be displayed to the user.
  1320  * @param string   $menu_slug   The slug name to refer to this menu by. Should be unique for this menu
  1388  * @param string   $menu_slug   The slug name to refer to this menu by. Should be unique for this menu
  1321  *                              and only include lowercase alphanumeric, dashes, and underscores characters
  1389  *                              and only include lowercase alphanumeric, dashes, and underscores characters
  1322  *                              to be compatible with sanitize_key().
  1390  *                              to be compatible with sanitize_key().
  1323  * @param callable $function    The function to be called to output the content for this page.
  1391  * @param callable $function    The function to be called to output the content for this page.
  1324  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1392  * @param int      $position    The position in the menu order this item should appear.
  1325  */
  1393  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1326 function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1394  */
       
  1395 function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
  1327 	global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv,
  1396 	global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv,
  1328 		$_registered_pages, $_parent_pages;
  1397 		$_registered_pages, $_parent_pages;
  1329 
  1398 
  1330 	$menu_slug   = plugin_basename( $menu_slug );
  1399 	$menu_slug   = plugin_basename( $menu_slug );
  1331 	$parent_slug = plugin_basename( $parent_slug );
  1400 	$parent_slug = plugin_basename( $parent_slug );
  1351 				$submenu[ $parent_slug ][] = array_slice( $parent_menu, 0, 4 );
  1420 				$submenu[ $parent_slug ][] = array_slice( $parent_menu, 0, 4 );
  1352 			}
  1421 			}
  1353 		}
  1422 		}
  1354 	}
  1423 	}
  1355 
  1424 
  1356 	$submenu[ $parent_slug ][] = array( $menu_title, $capability, $menu_slug, $page_title );
  1425 	$new_sub_menu = array( $menu_title, $capability, $menu_slug, $page_title );
       
  1426 	if ( ! is_int( $position ) ) {
       
  1427 		if ( null !== $position ) {
       
  1428 			_doing_it_wrong(
       
  1429 				__FUNCTION__,
       
  1430 				sprintf(
       
  1431 					/* translators: %s: add_submenu_page() */
       
  1432 					__( 'The seventh parameter passed to %s should be an integer representing menu position.' ),
       
  1433 					'<code>add_submenu_page()</code>'
       
  1434 				),
       
  1435 				'5.3.0'
       
  1436 			);
       
  1437 		}
       
  1438 
       
  1439 		$submenu[ $parent_slug ][] = $new_sub_menu;
       
  1440 	} else {
       
  1441 		// Append the submenu if the parent item is not present in the submenu,
       
  1442 		// or if position is equal or higher than the number of items in the array.
       
  1443 		if ( ! isset( $submenu[ $parent_slug ] ) || $position >= count( $submenu[ $parent_slug ] ) ) {
       
  1444 			$submenu[ $parent_slug ][] = $new_sub_menu;
       
  1445 		} else {
       
  1446 			// Test for a negative position.
       
  1447 			$position = max( $position, 0 );
       
  1448 			if ( 0 === $position ) {
       
  1449 				// For negative or `0` positions, prepend the submenu.
       
  1450 				array_unshift( $submenu[ $parent_slug ], $new_sub_menu );
       
  1451 			} else {
       
  1452 				// Grab all of the items before the insertion point.
       
  1453 				$before_items = array_slice( $submenu[ $parent_slug ], 0, $position, true );
       
  1454 				// Grab all of the items after the insertion point.
       
  1455 				$after_items = array_slice( $submenu[ $parent_slug ], $position, null, true );
       
  1456 				// Add the new item.
       
  1457 				$before_items[] = $new_sub_menu;
       
  1458 				// Merge the items.
       
  1459 				$submenu[ $parent_slug ] = array_merge( $before_items, $after_items );
       
  1460 			}
       
  1461 		}
       
  1462 	}
       
  1463 	// Sort the parent array.
       
  1464 	ksort( $submenu[ $parent_slug ] );
  1357 
  1465 
  1358 	$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
  1466 	$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
  1359 	if ( ! empty( $function ) && ! empty( $hookname ) ) {
  1467 	if ( ! empty( $function ) && ! empty( $hookname ) ) {
  1360 		add_action( $hookname, $function );
  1468 		add_action( $hookname, $function );
  1361 	}
  1469 	}
  1364 
  1472 
  1365 	/*
  1473 	/*
  1366 	 * Backward-compatibility for plugins using add_management_page().
  1474 	 * Backward-compatibility for plugins using add_management_page().
  1367 	 * See wp-admin/admin.php for redirect from edit.php to tools.php.
  1475 	 * See wp-admin/admin.php for redirect from edit.php to tools.php.
  1368 	 */
  1476 	 */
  1369 	if ( 'tools.php' == $parent_slug ) {
  1477 	if ( 'tools.php' === $parent_slug ) {
  1370 		$_registered_pages[ get_plugin_page_hookname( $menu_slug, 'edit.php' ) ] = true;
  1478 		$_registered_pages[ get_plugin_page_hookname( $menu_slug, 'edit.php' ) ] = true;
  1371 	}
  1479 	}
  1372 
  1480 
  1373 	// No parent as top level.
  1481 	// No parent as top level.
  1374 	$_parent_pages[ $menu_slug ] = $parent_slug;
  1482 	$_parent_pages[ $menu_slug ] = $parent_slug;
  1384  *
  1492  *
  1385  * The function which is hooked in to handle the output of the page must check
  1493  * The function which is hooked in to handle the output of the page must check
  1386  * that the user has the required capability as well.
  1494  * that the user has the required capability as well.
  1387  *
  1495  *
  1388  * @since 1.5.0
  1496  * @since 1.5.0
       
  1497  * @since 5.3.0 Added the `$position` parameter.
  1389  *
  1498  *
  1390  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1499  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1391  * @param string   $menu_title The text to be used for the menu.
  1500  * @param string   $menu_title The text to be used for the menu.
  1392  * @param string   $capability The capability required for this menu to be displayed to the user.
  1501  * @param string   $capability The capability required for this menu to be displayed to the user.
  1393  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1502  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1394  * @param callable $function   The function to be called to output the content for this page.
  1503  * @param callable $function   The function to be called to output the content for this page.
  1395  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1504  * @param int      $position   The position in the menu order this item should appear.
  1396  */
  1505  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1397 function add_management_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1506  */
  1398 	return add_submenu_page( 'tools.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1507 function add_management_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1508 	return add_submenu_page( 'tools.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1399 }
  1509 }
  1400 
  1510 
  1401 /**
  1511 /**
  1402  * Add submenu page to the Settings main menu.
  1512  * Add submenu page to the Settings main menu.
  1403  *
  1513  *
  1406  *
  1516  *
  1407  * The function which is hooked in to handle the output of the page must check
  1517  * The function which is hooked in to handle the output of the page must check
  1408  * that the user has the required capability as well.
  1518  * that the user has the required capability as well.
  1409  *
  1519  *
  1410  * @since 1.5.0
  1520  * @since 1.5.0
       
  1521  * @since 5.3.0 Added the `$position` parameter.
  1411  *
  1522  *
  1412  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1523  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1413  * @param string   $menu_title The text to be used for the menu.
  1524  * @param string   $menu_title The text to be used for the menu.
  1414  * @param string   $capability The capability required for this menu to be displayed to the user.
  1525  * @param string   $capability The capability required for this menu to be displayed to the user.
  1415  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1526  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1416  * @param callable $function   The function to be called to output the content for this page.
  1527  * @param callable $function   The function to be called to output the content for this page.
  1417  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1528  * @param int      $position   The position in the menu order this item should appear.
  1418  */
  1529  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1419 function add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1530  */
  1420 	return add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1531 function add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1532 	return add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1421 }
  1533 }
  1422 
  1534 
  1423 /**
  1535 /**
  1424  * Add submenu page to the Appearance main menu.
  1536  * Add submenu page to the Appearance main menu.
  1425  *
  1537  *
  1428  *
  1540  *
  1429  * The function which is hooked in to handle the output of the page must check
  1541  * The function which is hooked in to handle the output of the page must check
  1430  * that the user has the required capability as well.
  1542  * that the user has the required capability as well.
  1431  *
  1543  *
  1432  * @since 2.0.0
  1544  * @since 2.0.0
       
  1545  * @since 5.3.0 Added the `$position` parameter.
  1433  *
  1546  *
  1434  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1547  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1435  * @param string   $menu_title The text to be used for the menu.
  1548  * @param string   $menu_title The text to be used for the menu.
  1436  * @param string   $capability The capability required for this menu to be displayed to the user.
  1549  * @param string   $capability The capability required for this menu to be displayed to the user.
  1437  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1550  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1438  * @param callable $function   The function to be called to output the content for this page.
  1551  * @param callable $function   The function to be called to output the content for this page.
  1439  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1552  * @param int      $position   The position in the menu order this item should appear.
  1440  */
  1553  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1441 function add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1554  */
  1442 	return add_submenu_page( 'themes.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1555 function add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1556 	return add_submenu_page( 'themes.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1443 }
  1557 }
  1444 
  1558 
  1445 /**
  1559 /**
  1446  * Add submenu page to the Plugins main menu.
  1560  * Add submenu page to the Plugins main menu.
  1447  *
  1561  *
  1450  *
  1564  *
  1451  * The function which is hooked in to handle the output of the page must check
  1565  * The function which is hooked in to handle the output of the page must check
  1452  * that the user has the required capability as well.
  1566  * that the user has the required capability as well.
  1453  *
  1567  *
  1454  * @since 3.0.0
  1568  * @since 3.0.0
       
  1569  * @since 5.3.0 Added the `$position` parameter.
  1455  *
  1570  *
  1456  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1571  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1457  * @param string   $menu_title The text to be used for the menu.
  1572  * @param string   $menu_title The text to be used for the menu.
  1458  * @param string   $capability The capability required for this menu to be displayed to the user.
  1573  * @param string   $capability The capability required for this menu to be displayed to the user.
  1459  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1574  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1460  * @param callable $function   The function to be called to output the content for this page.
  1575  * @param callable $function   The function to be called to output the content for this page.
  1461  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1576  * @param int      $position   The position in the menu order this item should appear.
  1462  */
  1577  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1463 function add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1578  */
  1464 	return add_submenu_page( 'plugins.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1579 function add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1580 	return add_submenu_page( 'plugins.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1465 }
  1581 }
  1466 
  1582 
  1467 /**
  1583 /**
  1468  * Add submenu page to the Users/Profile main menu.
  1584  * Add submenu page to the Users/Profile main menu.
  1469  *
  1585  *
  1472  *
  1588  *
  1473  * The function which is hooked in to handle the output of the page must check
  1589  * The function which is hooked in to handle the output of the page must check
  1474  * that the user has the required capability as well.
  1590  * that the user has the required capability as well.
  1475  *
  1591  *
  1476  * @since 2.1.3
  1592  * @since 2.1.3
       
  1593  * @since 5.3.0 Added the `$position` parameter.
  1477  *
  1594  *
  1478  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1595  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1479  * @param string   $menu_title The text to be used for the menu.
  1596  * @param string   $menu_title The text to be used for the menu.
  1480  * @param string   $capability The capability required for this menu to be displayed to the user.
  1597  * @param string   $capability The capability required for this menu to be displayed to the user.
  1481  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1598  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1482  * @param callable $function   The function to be called to output the content for this page.
  1599  * @param callable $function   The function to be called to output the content for this page.
  1483  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1600  * @param int      $position   The position in the menu order this item should appear.
  1484  */
  1601  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1485 function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1602  */
       
  1603 function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
  1486 	if ( current_user_can( 'edit_users' ) ) {
  1604 	if ( current_user_can( 'edit_users' ) ) {
  1487 		$parent = 'users.php';
  1605 		$parent = 'users.php';
  1488 	} else {
  1606 	} else {
  1489 		$parent = 'profile.php';
  1607 		$parent = 'profile.php';
  1490 	}
  1608 	}
  1491 	return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function );
  1609 	return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1492 }
  1610 }
       
  1611 
  1493 /**
  1612 /**
  1494  * Add submenu page to the Dashboard main menu.
  1613  * Add submenu page to the Dashboard main menu.
  1495  *
  1614  *
  1496  * This function takes a capability which will be used to determine whether
  1615  * This function takes a capability which will be used to determine whether
  1497  * or not a page is included in the menu.
  1616  * or not a page is included in the menu.
  1498  *
  1617  *
  1499  * The function which is hooked in to handle the output of the page must check
  1618  * The function which is hooked in to handle the output of the page must check
  1500  * that the user has the required capability as well.
  1619  * that the user has the required capability as well.
  1501  *
  1620  *
  1502  * @since 2.7.0
  1621  * @since 2.7.0
       
  1622  * @since 5.3.0 Added the `$position` parameter.
  1503  *
  1623  *
  1504  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1624  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1505  * @param string   $menu_title The text to be used for the menu.
  1625  * @param string   $menu_title The text to be used for the menu.
  1506  * @param string   $capability The capability required for this menu to be displayed to the user.
  1626  * @param string   $capability The capability required for this menu to be displayed to the user.
  1507  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1627  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1508  * @param callable $function   The function to be called to output the content for this page.
  1628  * @param callable $function   The function to be called to output the content for this page.
  1509  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1629  * @param int      $position   The position in the menu order this item should appear.
  1510  */
  1630  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1511 function add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1631  */
  1512 	return add_submenu_page( 'index.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1632 function add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1633 	return add_submenu_page( 'index.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1513 }
  1634 }
  1514 
  1635 
  1515 /**
  1636 /**
  1516  * Add submenu page to the Posts main menu.
  1637  * Add submenu page to the Posts main menu.
  1517  *
  1638  *
  1520  *
  1641  *
  1521  * The function which is hooked in to handle the output of the page must check
  1642  * The function which is hooked in to handle the output of the page must check
  1522  * that the user has the required capability as well.
  1643  * that the user has the required capability as well.
  1523  *
  1644  *
  1524  * @since 2.7.0
  1645  * @since 2.7.0
       
  1646  * @since 5.3.0 Added the `$position` parameter.
  1525  *
  1647  *
  1526  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1648  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1527  * @param string   $menu_title The text to be used for the menu.
  1649  * @param string   $menu_title The text to be used for the menu.
  1528  * @param string   $capability The capability required for this menu to be displayed to the user.
  1650  * @param string   $capability The capability required for this menu to be displayed to the user.
  1529  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1651  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1530  * @param callable $function   The function to be called to output the content for this page.
  1652  * @param callable $function   The function to be called to output the content for this page.
  1531  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1653  * @param int      $position   The position in the menu order this item should appear.
  1532  */
  1654  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1533 function add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1655  */
  1534 	return add_submenu_page( 'edit.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1656 function add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1657 	return add_submenu_page( 'edit.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1535 }
  1658 }
  1536 
  1659 
  1537 /**
  1660 /**
  1538  * Add submenu page to the Media main menu.
  1661  * Add submenu page to the Media main menu.
  1539  *
  1662  *
  1542  *
  1665  *
  1543  * The function which is hooked in to handle the output of the page must check
  1666  * The function which is hooked in to handle the output of the page must check
  1544  * that the user has the required capability as well.
  1667  * that the user has the required capability as well.
  1545  *
  1668  *
  1546  * @since 2.7.0
  1669  * @since 2.7.0
       
  1670  * @since 5.3.0 Added the `$position` parameter.
  1547  *
  1671  *
  1548  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1672  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1549  * @param string   $menu_title The text to be used for the menu.
  1673  * @param string   $menu_title The text to be used for the menu.
  1550  * @param string   $capability The capability required for this menu to be displayed to the user.
  1674  * @param string   $capability The capability required for this menu to be displayed to the user.
  1551  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1675  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1552  * @param callable $function   The function to be called to output the content for this page.
  1676  * @param callable $function   The function to be called to output the content for this page.
  1553  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1677  * @param int      $position   The position in the menu order this item should appear.
  1554  */
  1678  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1555 function add_media_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1679  */
  1556 	return add_submenu_page( 'upload.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1680 function add_media_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1681 	return add_submenu_page( 'upload.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1557 }
  1682 }
  1558 
  1683 
  1559 /**
  1684 /**
  1560  * Add submenu page to the Links main menu.
  1685  * Add submenu page to the Links main menu.
  1561  *
  1686  *
  1564  *
  1689  *
  1565  * The function which is hooked in to handle the output of the page must check
  1690  * The function which is hooked in to handle the output of the page must check
  1566  * that the user has the required capability as well.
  1691  * that the user has the required capability as well.
  1567  *
  1692  *
  1568  * @since 2.7.0
  1693  * @since 2.7.0
       
  1694  * @since 5.3.0 Added the `$position` parameter.
  1569  *
  1695  *
  1570  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1696  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1571  * @param string   $menu_title The text to be used for the menu.
  1697  * @param string   $menu_title The text to be used for the menu.
  1572  * @param string   $capability The capability required for this menu to be displayed to the user.
  1698  * @param string   $capability The capability required for this menu to be displayed to the user.
  1573  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1699  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1574  * @param callable $function   The function to be called to output the content for this page.
  1700  * @param callable $function   The function to be called to output the content for this page.
  1575  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1701  * @param int      $position   The position in the menu order this item should appear.
  1576  */
  1702  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1577 function add_links_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1703  */
  1578 	return add_submenu_page( 'link-manager.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1704 function add_links_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1705 	return add_submenu_page( 'link-manager.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1579 }
  1706 }
  1580 
  1707 
  1581 /**
  1708 /**
  1582  * Add submenu page to the Pages main menu.
  1709  * Add submenu page to the Pages main menu.
  1583  *
  1710  *
  1586  *
  1713  *
  1587  * The function which is hooked in to handle the output of the page must check
  1714  * The function which is hooked in to handle the output of the page must check
  1588  * that the user has the required capability as well.
  1715  * that the user has the required capability as well.
  1589  *
  1716  *
  1590  * @since 2.7.0
  1717  * @since 2.7.0
       
  1718  * @since 5.3.0 Added the `$position` parameter.
  1591  *
  1719  *
  1592  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1720  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1593  * @param string   $menu_title The text to be used for the menu.
  1721  * @param string   $menu_title The text to be used for the menu.
  1594  * @param string   $capability The capability required for this menu to be displayed to the user.
  1722  * @param string   $capability The capability required for this menu to be displayed to the user.
  1595  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1723  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1596  * @param callable $function   The function to be called to output the content for this page.
  1724  * @param callable $function   The function to be called to output the content for this page.
  1597  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1725  * @param int      $position   The position in the menu order this item should appear.
  1598  */
  1726  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1599 function add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1727  */
  1600 	return add_submenu_page( 'edit.php?post_type=page', $page_title, $menu_title, $capability, $menu_slug, $function );
  1728 function add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1729 	return add_submenu_page( 'edit.php?post_type=page', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1601 }
  1730 }
  1602 
  1731 
  1603 /**
  1732 /**
  1604  * Add submenu page to the Comments main menu.
  1733  * Add submenu page to the Comments main menu.
  1605  *
  1734  *
  1608  *
  1737  *
  1609  * The function which is hooked in to handle the output of the page must check
  1738  * The function which is hooked in to handle the output of the page must check
  1610  * that the user has the required capability as well.
  1739  * that the user has the required capability as well.
  1611  *
  1740  *
  1612  * @since 2.7.0
  1741  * @since 2.7.0
       
  1742  * @since 5.3.0 Added the `$position` parameter.
  1613  *
  1743  *
  1614  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1744  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1615  * @param string   $menu_title The text to be used for the menu.
  1745  * @param string   $menu_title The text to be used for the menu.
  1616  * @param string   $capability The capability required for this menu to be displayed to the user.
  1746  * @param string   $capability The capability required for this menu to be displayed to the user.
  1617  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1747  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1618  * @param callable $function   The function to be called to output the content for this page.
  1748  * @param callable $function   The function to be called to output the content for this page.
  1619  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1749  * @param int      $position   The position in the menu order this item should appear.
  1620  */
  1750  * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required.
  1621 function add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1751  */
  1622 	return add_submenu_page( 'edit-comments.php', $page_title, $menu_title, $capability, $menu_slug, $function );
  1752 function add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $position = null ) {
       
  1753 	return add_submenu_page( 'edit-comments.php', $page_title, $menu_title, $capability, $menu_slug, $function, $position );
  1623 }
  1754 }
  1624 
  1755 
  1625 /**
  1756 /**
  1626  * Remove a top-level admin menu.
  1757  * Remove a top-level admin menu.
  1627  *
  1758  *
  1672 
  1803 
  1673 	return false;
  1804 	return false;
  1674 }
  1805 }
  1675 
  1806 
  1676 /**
  1807 /**
  1677  * Get the url to access a particular menu page based on the slug it was registered with.
  1808  * Get the URL to access a particular menu page based on the slug it was registered with.
  1678  *
  1809  *
  1679  * If the slug hasn't been registered properly no url will be returned
  1810  * If the slug hasn't been registered properly, no URL will be returned.
  1680  *
  1811  *
  1681  * @since 3.0.0
  1812  * @since 3.0.0
  1682  *
  1813  *
  1683  * @global array $_parent_pages
  1814  * @global array $_parent_pages
  1684  *
  1815  *
  1685  * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu)
  1816  * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu).
  1686  * @param bool $echo Whether or not to echo the url - default is true
  1817  * @param bool   $echo      Whether or not to echo the URL. Default true.
  1687  * @return string the url
  1818  * @return string The menu page URL.
  1688  */
  1819  */
  1689 function menu_page_url( $menu_slug, $echo = true ) {
  1820 function menu_page_url( $menu_slug, $echo = true ) {
  1690 	global $_parent_pages;
  1821 	global $_parent_pages;
  1691 
  1822 
  1692 	if ( isset( $_parent_pages[ $menu_slug ] ) ) {
  1823 	if ( isset( $_parent_pages[ $menu_slug ] ) ) {
  1708 
  1839 
  1709 	return $url;
  1840 	return $url;
  1710 }
  1841 }
  1711 
  1842 
  1712 //
  1843 //
  1713 // Pluggable Menu Support -- Private
  1844 // Pluggable Menu Support -- Private.
  1714 //
  1845 //
  1715 /**
  1846 /**
       
  1847  * Gets the parent file of the current admin page.
       
  1848  *
       
  1849  * @since 1.5.0
       
  1850  *
  1716  * @global string $parent_file
  1851  * @global string $parent_file
  1717  * @global array $menu
  1852  * @global array  $menu
  1718  * @global array $submenu
  1853  * @global array  $submenu
  1719  * @global string $pagenow
  1854  * @global string $pagenow
  1720  * @global string $typenow
  1855  * @global string $typenow
  1721  * @global string $plugin_page
  1856  * @global string $plugin_page
  1722  * @global array $_wp_real_parent_file
  1857  * @global array  $_wp_real_parent_file
  1723  * @global array $_wp_menu_nopriv
  1858  * @global array  $_wp_menu_nopriv
  1724  * @global array $_wp_submenu_nopriv
  1859  * @global array  $_wp_submenu_nopriv
  1725  *
  1860  *
  1726  * @return string
  1861  * @param string $parent The slug name for the parent menu (or the file name of a standard
       
  1862  *                       WordPress admin page). Default empty string.
       
  1863  * @return string The parent file of the current admin page.
  1727  */
  1864  */
  1728 function get_admin_page_parent( $parent = '' ) {
  1865 function get_admin_page_parent( $parent = '' ) {
  1729 	global $parent_file, $menu, $submenu, $pagenow, $typenow,
  1866 	global $parent_file, $menu, $submenu, $pagenow, $typenow,
  1730 		$plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv;
  1867 		$plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv;
  1731 
  1868 
  1732 	if ( ! empty( $parent ) && 'admin.php' != $parent ) {
  1869 	if ( ! empty( $parent ) && 'admin.php' !== $parent ) {
  1733 		if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1870 		if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1734 			$parent = $_wp_real_parent_file[ $parent ];
  1871 			$parent = $_wp_real_parent_file[ $parent ];
  1735 		}
  1872 		}
  1736 		return $parent;
  1873 		return $parent;
  1737 	}
  1874 	}
  1738 
  1875 
  1739 	if ( $pagenow == 'admin.php' && isset( $plugin_page ) ) {
  1876 	if ( 'admin.php' === $pagenow && isset( $plugin_page ) ) {
  1740 		foreach ( (array) $menu as $parent_menu ) {
  1877 		foreach ( (array) $menu as $parent_menu ) {
  1741 			if ( $parent_menu[2] == $plugin_page ) {
  1878 			if ( $parent_menu[2] == $plugin_page ) {
  1742 				$parent_file = $plugin_page;
  1879 				$parent_file = $plugin_page;
  1743 				if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
  1880 				if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
  1744 					$parent_file = $_wp_real_parent_file[ $parent_file ];
  1881 					$parent_file = $_wp_real_parent_file[ $parent_file ];
  1766 	foreach ( array_keys( (array) $submenu ) as $parent ) {
  1903 	foreach ( array_keys( (array) $submenu ) as $parent ) {
  1767 		foreach ( $submenu[ $parent ] as $submenu_array ) {
  1904 		foreach ( $submenu[ $parent ] as $submenu_array ) {
  1768 			if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1905 			if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1769 				$parent = $_wp_real_parent_file[ $parent ];
  1906 				$parent = $_wp_real_parent_file[ $parent ];
  1770 			}
  1907 			}
  1771 			if ( ! empty( $typenow ) && ( $submenu_array[2] == "$pagenow?post_type=$typenow" ) ) {
  1908 			if ( ! empty( $typenow ) && ( "$pagenow?post_type=$typenow" === $submenu_array[2] ) ) {
  1772 				$parent_file = $parent;
  1909 				$parent_file = $parent;
  1773 				return $parent;
  1910 				return $parent;
  1774 			} elseif ( $submenu_array[2] == $pagenow && empty( $typenow ) && ( empty( $parent_file ) || false === strpos( $parent_file, '?' ) ) ) {
  1911 			} elseif ( $submenu_array[2] == $pagenow && empty( $typenow ) && ( empty( $parent_file ) || false === strpos( $parent_file, '?' ) ) ) {
  1775 				$parent_file = $parent;
  1912 				$parent_file = $parent;
  1776 				return $parent;
  1913 				return $parent;
  1786 	}
  1923 	}
  1787 	return '';
  1924 	return '';
  1788 }
  1925 }
  1789 
  1926 
  1790 /**
  1927 /**
       
  1928  * Gets the title of the current admin page.
       
  1929  *
       
  1930  * @since 1.5.0
       
  1931  *
  1791  * @global string $title
  1932  * @global string $title
  1792  * @global array $menu
  1933  * @global array $menu
  1793  * @global array $submenu
  1934  * @global array $submenu
  1794  * @global string $pagenow
  1935  * @global string $pagenow
  1795  * @global string $plugin_page
  1936  * @global string $plugin_page
  1796  * @global string $typenow
  1937  * @global string $typenow
  1797  *
  1938  *
  1798  * @return string
  1939  * @return string The title of the current admin page.
  1799  */
  1940  */
  1800 function get_admin_page_title() {
  1941 function get_admin_page_title() {
  1801 	global $title, $menu, $submenu, $pagenow, $plugin_page, $typenow;
  1942 	global $title, $menu, $submenu, $pagenow, $plugin_page, $typenow;
  1802 
  1943 
  1803 	if ( ! empty( $title ) ) {
  1944 	if ( ! empty( $title ) ) {
  1804 		return $title;
  1945 		return $title;
  1805 	}
  1946 	}
  1806 
  1947 
  1807 	$hook = get_plugin_page_hook( $plugin_page, $pagenow );
  1948 	$hook = get_plugin_page_hook( $plugin_page, $pagenow );
  1808 
  1949 
  1809 	$parent = $parent1 = get_admin_page_parent();
  1950 	$parent  = get_admin_page_parent();
       
  1951 	$parent1 = $parent;
  1810 
  1952 
  1811 	if ( empty( $parent ) ) {
  1953 	if ( empty( $parent ) ) {
  1812 		foreach ( (array) $menu as $menu_array ) {
  1954 		foreach ( (array) $menu as $menu_array ) {
  1813 			if ( isset( $menu_array[3] ) ) {
  1955 			if ( isset( $menu_array[3] ) ) {
  1814 				if ( $menu_array[2] == $pagenow ) {
  1956 				if ( $menu_array[2] == $pagenow ) {
  1815 					$title = $menu_array[3];
  1957 					$title = $menu_array[3];
  1816 					return $menu_array[3];
  1958 					return $menu_array[3];
  1817 				} elseif ( isset( $plugin_page ) && ( $plugin_page == $menu_array[2] ) && ( $hook == $menu_array[3] ) ) {
  1959 				} elseif ( isset( $plugin_page ) && ( $plugin_page == $menu_array[2] ) && ( $hook == $menu_array[5] ) ) {
  1818 					$title = $menu_array[3];
  1960 					$title = $menu_array[3];
  1819 					return $menu_array[3];
  1961 					return $menu_array[3];
  1820 				}
  1962 				}
  1821 			} else {
  1963 			} else {
  1822 				$title = $menu_array[0];
  1964 				$title = $menu_array[0];
  1830 					( $plugin_page == $submenu_array[2] ) &&
  1972 					( $plugin_page == $submenu_array[2] ) &&
  1831 					(
  1973 					(
  1832 						( $parent == $pagenow ) ||
  1974 						( $parent == $pagenow ) ||
  1833 						( $parent == $plugin_page ) ||
  1975 						( $parent == $plugin_page ) ||
  1834 						( $plugin_page == $hook ) ||
  1976 						( $plugin_page == $hook ) ||
  1835 						( $pagenow == 'admin.php' && $parent1 != $submenu_array[2] ) ||
  1977 						( 'admin.php' === $pagenow && $parent1 != $submenu_array[2] ) ||
  1836 						( ! empty( $typenow ) && $parent == $pagenow . '?post_type=' . $typenow )
  1978 						( ! empty( $typenow ) && $parent == $pagenow . '?post_type=' . $typenow )
  1837 					)
  1979 					)
  1838 					) {
  1980 					) {
  1839 						$title = $submenu_array[3];
  1981 						$title = $submenu_array[3];
  1840 						return $submenu_array[3];
  1982 						return $submenu_array[3];
  1841 				}
  1983 				}
  1842 
  1984 
  1843 				if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) { // not the current page
  1985 				if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) { // Not the current page.
  1844 					continue;
  1986 					continue;
  1845 				}
  1987 				}
  1846 
  1988 
  1847 				if ( isset( $submenu_array[3] ) ) {
  1989 				if ( isset( $submenu_array[3] ) ) {
  1848 					$title = $submenu_array[3];
  1990 					$title = $submenu_array[3];
  1855 		}
  1997 		}
  1856 		if ( empty( $title ) ) {
  1998 		if ( empty( $title ) ) {
  1857 			foreach ( $menu as $menu_array ) {
  1999 			foreach ( $menu as $menu_array ) {
  1858 				if ( isset( $plugin_page ) &&
  2000 				if ( isset( $plugin_page ) &&
  1859 					( $plugin_page == $menu_array[2] ) &&
  2001 					( $plugin_page == $menu_array[2] ) &&
  1860 					( $pagenow == 'admin.php' ) &&
  2002 					( 'admin.php' === $pagenow ) &&
  1861 					( $parent1 == $menu_array[2] ) ) {
  2003 					( $parent1 == $menu_array[2] ) ) {
  1862 						$title = $menu_array[3];
  2004 						$title = $menu_array[3];
  1863 						return $menu_array[3];
  2005 						return $menu_array[3];
  1864 				}
  2006 				}
  1865 			}
  2007 			}
  1868 
  2010 
  1869 	return $title;
  2011 	return $title;
  1870 }
  2012 }
  1871 
  2013 
  1872 /**
  2014 /**
  1873  * @since 2.3.0
  2015  * Gets the hook attached to the administrative page of a plugin.
       
  2016  *
       
  2017  * @since 1.5.0
  1874  *
  2018  *
  1875  * @param string $plugin_page The slug name of the plugin page.
  2019  * @param string $plugin_page The slug name of the plugin page.
  1876  * @param string $parent_page The slug name for the parent menu (or the file name of a standard
  2020  * @param string $parent_page The slug name for the parent menu (or the file name of a standard
  1877  *                            WordPress admin page).
  2021  *                            WordPress admin page).
  1878  * @return string|null Hook attached to the plugin page, null otherwise.
  2022  * @return string|null Hook attached to the plugin page, null otherwise.
  1885 		return null;
  2029 		return null;
  1886 	}
  2030 	}
  1887 }
  2031 }
  1888 
  2032 
  1889 /**
  2033 /**
       
  2034  * Gets the hook name for the administrative page of a plugin.
       
  2035  *
       
  2036  * @since 1.5.0
       
  2037  *
  1890  * @global array $admin_page_hooks
  2038  * @global array $admin_page_hooks
  1891  *
  2039  *
  1892  * @param string $plugin_page The slug name of the plugin page.
  2040  * @param string $plugin_page The slug name of the plugin page.
  1893  * @param string $parent_page The slug name for the parent menu (or the file name of a standard
  2041  * @param string $parent_page The slug name for the parent menu (or the file name of a standard
  1894  *                            WordPress admin page).
  2042  *                            WordPress admin page).
  1898 	global $admin_page_hooks;
  2046 	global $admin_page_hooks;
  1899 
  2047 
  1900 	$parent = get_admin_page_parent( $parent_page );
  2048 	$parent = get_admin_page_parent( $parent_page );
  1901 
  2049 
  1902 	$page_type = 'admin';
  2050 	$page_type = 'admin';
  1903 	if ( empty( $parent_page ) || 'admin.php' == $parent_page || isset( $admin_page_hooks[ $plugin_page ] ) ) {
  2051 	if ( empty( $parent_page ) || 'admin.php' === $parent_page || isset( $admin_page_hooks[ $plugin_page ] ) ) {
  1904 		if ( isset( $admin_page_hooks[ $plugin_page ] ) ) {
  2052 		if ( isset( $admin_page_hooks[ $plugin_page ] ) ) {
  1905 			$page_type = 'toplevel';
  2053 			$page_type = 'toplevel';
  1906 		} elseif ( isset( $admin_page_hooks[ $parent ] ) ) {
  2054 		} elseif ( isset( $admin_page_hooks[ $parent ] ) ) {
  1907 			$page_type = $admin_page_hooks[ $parent ];
  2055 			$page_type = $admin_page_hooks[ $parent ];
  1908 		}
  2056 		}
  1914 
  2062 
  1915 	return $page_type . '_page_' . $plugin_name;
  2063 	return $page_type . '_page_' . $plugin_name;
  1916 }
  2064 }
  1917 
  2065 
  1918 /**
  2066 /**
       
  2067  * Determines whether the current user can access the current admin page.
       
  2068  *
       
  2069  * @since 1.5.0
       
  2070  *
  1919  * @global string $pagenow
  2071  * @global string $pagenow
  1920  * @global array $menu
  2072  * @global array  $menu
  1921  * @global array $submenu
  2073  * @global array  $submenu
  1922  * @global array $_wp_menu_nopriv
  2074  * @global array  $_wp_menu_nopriv
  1923  * @global array $_wp_submenu_nopriv
  2075  * @global array  $_wp_submenu_nopriv
  1924  * @global string $plugin_page
  2076  * @global string $plugin_page
  1925  * @global array $_registered_pages
  2077  * @global array  $_registered_pages
  1926  *
  2078  *
  1927  * @return bool Whether the current user can access the current admin page.
  2079  * @return bool True if the current user can access the admin page, false otherwise.
  1928  */
  2080  */
  1929 function user_can_access_admin_page() {
  2081 function user_can_access_admin_page() {
  1930 	global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv,
  2082 	global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv,
  1931 		$plugin_page, $_registered_pages;
  2083 		$plugin_page, $_registered_pages;
  1932 
  2084 
  2005 	}
  2157 	}
  2006 
  2158 
  2007 	return true;
  2159 	return true;
  2008 }
  2160 }
  2009 
  2161 
  2010 /* Whitelist functions */
  2162 /* Allowed list functions */
  2011 
  2163 
  2012 /**
  2164 /**
  2013  * Refreshes the value of the options whitelist available via the 'whitelist_options' hook.
  2165  * Refreshes the value of the allowed options list available via the 'allowed_options' hook.
  2014  *
  2166  *
  2015  * See the {@see 'whitelist_options'} filter.
  2167  * See the {@see 'allowed_options'} filter.
  2016  *
  2168  *
  2017  * @since 2.7.0
  2169  * @since 2.7.0
  2018  *
  2170  * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`.
  2019  * @global array $new_whitelist_options
  2171  *              Please consider writing more inclusive code.
       
  2172  *
       
  2173  * @global array $new_allowed_options
  2020  *
  2174  *
  2021  * @param array $options
  2175  * @param array $options
  2022  * @return array
  2176  * @return array
  2023  */
  2177  */
  2024 function option_update_filter( $options ) {
  2178 function option_update_filter( $options ) {
  2025 	global $new_whitelist_options;
  2179 	global $new_allowed_options;
  2026 
  2180 
  2027 	if ( is_array( $new_whitelist_options ) ) {
  2181 	if ( is_array( $new_allowed_options ) ) {
  2028 		$options = add_option_whitelist( $new_whitelist_options, $options );
  2182 		$options = add_allowed_options( $new_allowed_options, $options );
  2029 	}
  2183 	}
  2030 
  2184 
  2031 	return $options;
  2185 	return $options;
  2032 }
  2186 }
  2033 
  2187 
  2034 /**
  2188 /**
  2035  * Adds an array of options to the options whitelist.
  2189  * Adds an array of options to the list of allowed options.
  2036  *
  2190  *
  2037  * @since 2.7.0
  2191  * @since 2.7.0
  2038  *
  2192  *
  2039  * @global array $whitelist_options
  2193  * @global array $allowed_options
  2040  *
  2194  *
  2041  * @param array        $new_options
  2195  * @param array        $new_options
  2042  * @param string|array $options
  2196  * @param string|array $options
  2043  * @return array
  2197  * @return array
  2044  */
  2198  */
  2045 function add_option_whitelist( $new_options, $options = '' ) {
  2199 function add_allowed_options( $new_options, $options = '' ) {
  2046 	if ( $options == '' ) {
  2200 	if ( '' === $options ) {
  2047 		global $whitelist_options;
  2201 		global $allowed_options;
  2048 	} else {
  2202 	} else {
  2049 		$whitelist_options = $options;
  2203 		$allowed_options = $options;
  2050 	}
  2204 	}
  2051 
  2205 
  2052 	foreach ( $new_options as $page => $keys ) {
  2206 	foreach ( $new_options as $page => $keys ) {
  2053 		foreach ( $keys as $key ) {
  2207 		foreach ( $keys as $key ) {
  2054 			if ( ! isset( $whitelist_options[ $page ] ) || ! is_array( $whitelist_options[ $page ] ) ) {
  2208 			if ( ! isset( $allowed_options[ $page ] ) || ! is_array( $allowed_options[ $page ] ) ) {
  2055 				$whitelist_options[ $page ]   = array();
  2209 				$allowed_options[ $page ]   = array();
  2056 				$whitelist_options[ $page ][] = $key;
  2210 				$allowed_options[ $page ][] = $key;
  2057 			} else {
  2211 			} else {
  2058 				$pos = array_search( $key, $whitelist_options[ $page ] );
  2212 				$pos = array_search( $key, $allowed_options[ $page ], true );
  2059 				if ( $pos === false ) {
  2213 				if ( false === $pos ) {
  2060 					$whitelist_options[ $page ][] = $key;
  2214 					$allowed_options[ $page ][] = $key;
  2061 				}
  2215 				}
  2062 			}
  2216 			}
  2063 		}
  2217 		}
  2064 	}
  2218 	}
  2065 
  2219 
  2066 	return $whitelist_options;
  2220 	return $allowed_options;
  2067 }
  2221 }
  2068 
  2222 
  2069 /**
  2223 /**
  2070  * Removes a list of options from the options whitelist.
  2224  * Removes a list of options from the allowed options list.
  2071  *
  2225  *
  2072  * @since 2.7.0
  2226  * @since 5.5.0
  2073  *
  2227  *
  2074  * @global array $whitelist_options
  2228  * @global array $allowed_options
  2075  *
  2229  *
  2076  * @param array        $del_options
  2230  * @param array        $del_options
  2077  * @param string|array $options
  2231  * @param string|array $options
  2078  * @return array
  2232  * @return array
  2079  */
  2233  */
  2080 function remove_option_whitelist( $del_options, $options = '' ) {
  2234 function remove_allowed_options( $del_options, $options = '' ) {
  2081 	if ( $options == '' ) {
  2235 	if ( '' === $options ) {
  2082 		global $whitelist_options;
  2236 		global $allowed_options;
  2083 	} else {
  2237 	} else {
  2084 		$whitelist_options = $options;
  2238 		$allowed_options = $options;
  2085 	}
  2239 	}
  2086 
  2240 
  2087 	foreach ( $del_options as $page => $keys ) {
  2241 	foreach ( $del_options as $page => $keys ) {
  2088 		foreach ( $keys as $key ) {
  2242 		foreach ( $keys as $key ) {
  2089 			if ( isset( $whitelist_options[ $page ] ) && is_array( $whitelist_options[ $page ] ) ) {
  2243 			if ( isset( $allowed_options[ $page ] ) && is_array( $allowed_options[ $page ] ) ) {
  2090 				$pos = array_search( $key, $whitelist_options[ $page ] );
  2244 				$pos = array_search( $key, $allowed_options[ $page ], true );
  2091 				if ( $pos !== false ) {
  2245 				if ( false !== $pos ) {
  2092 					unset( $whitelist_options[ $page ][ $pos ] );
  2246 					unset( $allowed_options[ $page ][ $pos ] );
  2093 				}
  2247 				}
  2094 			}
  2248 			}
  2095 		}
  2249 		}
  2096 	}
  2250 	}
  2097 
  2251 
  2098 	return $whitelist_options;
  2252 	return $allowed_options;
  2099 }
  2253 }
  2100 
  2254 
  2101 /**
  2255 /**
  2102  * Output nonce, action, and option_page fields for a settings page.
  2256  * Output nonce, action, and option_page fields for a settings page.
  2103  *
  2257  *
  2104  * @since 2.7.0
  2258  * @since 2.7.0
  2105  *
  2259  *
  2106  * @param string $option_group A settings group name. This should match the group name used in register_setting().
  2260  * @param string $option_group A settings group name. This should match the group name
       
  2261  *                             used in register_setting().
  2107  */
  2262  */
  2108 function settings_fields( $option_group ) {
  2263 function settings_fields( $option_group ) {
  2109 	echo "<input type='hidden' name='option_page' value='" . esc_attr( $option_group ) . "' />";
  2264 	echo "<input type='hidden' name='option_page' value='" . esc_attr( $option_group ) . "' />";
  2110 	echo '<input type="hidden" name="action" value="update" />';
  2265 	echo '<input type="hidden" name="action" value="update" />';
  2111 	wp_nonce_field( "$option_group-options" );
  2266 	wp_nonce_field( "$option_group-options" );
  2112 }
  2267 }
  2113 
  2268 
  2114 /**
  2269 /**
  2115  * Clears the Plugins cache used by get_plugins() and by default, the Plugin Update cache.
  2270  * Clears the plugins cache used by get_plugins() and by default, the plugin updates cache.
  2116  *
  2271  *
  2117  * @since 3.7.0
  2272  * @since 3.7.0
  2118  *
  2273  *
  2119  * @param bool $clear_update_cache Whether to clear the Plugin updates cache
  2274  * @param bool $clear_update_cache Whether to clear the plugin updates cache. Default true.
  2120  */
  2275  */
  2121 function wp_clean_plugins_cache( $clear_update_cache = true ) {
  2276 function wp_clean_plugins_cache( $clear_update_cache = true ) {
  2122 	if ( $clear_update_cache ) {
  2277 	if ( $clear_update_cache ) {
  2123 		delete_site_transient( 'update_plugins' );
  2278 		delete_site_transient( 'update_plugins' );
  2124 	}
  2279 	}
  2135  */
  2290  */
  2136 function plugin_sandbox_scrape( $plugin ) {
  2291 function plugin_sandbox_scrape( $plugin ) {
  2137 	if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
  2292 	if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
  2138 		define( 'WP_SANDBOX_SCRAPING', true );
  2293 		define( 'WP_SANDBOX_SCRAPING', true );
  2139 	}
  2294 	}
       
  2295 
  2140 	wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
  2296 	wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
  2141 	include( WP_PLUGIN_DIR . '/' . $plugin );
  2297 	include WP_PLUGIN_DIR . '/' . $plugin;
  2142 }
  2298 }
  2143 
  2299 
  2144 /**
  2300 /**
  2145  * Helper function for adding content to the Privacy Policy Guide.
  2301  * Helper function for adding content to the Privacy Policy Guide.
  2146  *
  2302  *
  2152  * the suggested policy content. For example modular plugins such as WooCommerse or Jetpack
  2308  * the suggested policy content. For example modular plugins such as WooCommerse or Jetpack
  2153  * can add or remove suggested content depending on the modules/extensions that are enabled.
  2309  * can add or remove suggested content depending on the modules/extensions that are enabled.
  2154  * For more information see the Plugin Handbook:
  2310  * For more information see the Plugin Handbook:
  2155  * https://developer.wordpress.org/plugins/privacy/suggesting-text-for-the-site-privacy-policy/.
  2311  * https://developer.wordpress.org/plugins/privacy/suggesting-text-for-the-site-privacy-policy/.
  2156  *
  2312  *
       
  2313  * The HTML contents of the `$policy_text` supports use of a specialized `.privacy-policy-tutorial`
       
  2314  * CSS class which can be used to provide supplemental information. Any content contained within
       
  2315  * HTML elements that have the `.privacy-policy-tutorial` CSS class applied will be omitted
       
  2316  * from the clipboard when the section content is copied.
       
  2317  *
  2157  * Intended for use with the `'admin_init'` action.
  2318  * Intended for use with the `'admin_init'` action.
  2158  *
  2319  *
  2159  * @since 4.9.6
  2320  * @since 4.9.6
  2160  *
  2321  *
  2161  * @param string $plugin_name The name of the plugin or theme that is suggesting content for the site's privacy policy.
  2322  * @param string $plugin_name The name of the plugin or theme that is suggesting content
       
  2323  *                            for the site's privacy policy.
  2162  * @param string $policy_text The suggested content for inclusion in the policy.
  2324  * @param string $policy_text The suggested content for inclusion in the policy.
  2163  */
  2325  */
  2164 function wp_add_privacy_policy_content( $plugin_name, $policy_text ) {
  2326 function wp_add_privacy_policy_content( $plugin_name, $policy_text ) {
  2165 	if ( ! is_admin() ) {
  2327 	if ( ! is_admin() ) {
  2166 		_doing_it_wrong(
  2328 		_doing_it_wrong(
  2185 		);
  2347 		);
  2186 		return;
  2348 		return;
  2187 	}
  2349 	}
  2188 
  2350 
  2189 	if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) {
  2351 	if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) {
  2190 		require_once( ABSPATH . 'wp-admin/includes/misc.php' );
  2352 		require_once ABSPATH . 'wp-admin/includes/class-wp-privacy-policy-content.php';
  2191 	}
  2353 	}
  2192 
  2354 
  2193 	WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
  2355 	WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
  2194 }
  2356 }
  2195 
  2357 
  2202  * Conditional Tags} article in the Theme Developer Handbook.
  2364  * Conditional Tags} article in the Theme Developer Handbook.
  2203  *
  2365  *
  2204  * @since 5.2.0
  2366  * @since 5.2.0
  2205  *
  2367  *
  2206  * @param string $plugin Path to the plugin file relative to the plugins directory.
  2368  * @param string $plugin Path to the plugin file relative to the plugins directory.
  2207  * @return bool True, if in the list of paused plugins. False, not in the list.
  2369  * @return bool True, if in the list of paused plugins. False, if not in the list.
  2208  */
  2370  */
  2209 function is_plugin_paused( $plugin ) {
  2371 function is_plugin_paused( $plugin ) {
  2210 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
  2372 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
  2211 		return false;
  2373 		return false;
  2212 	}
  2374 	}
  2223 /**
  2385 /**
  2224  * Gets the error that was recorded for a paused plugin.
  2386  * Gets the error that was recorded for a paused plugin.
  2225  *
  2387  *
  2226  * @since 5.2.0
  2388  * @since 5.2.0
  2227  *
  2389  *
  2228  * @param string $plugin Path to the plugin file relative to the plugins
  2390  * @param string $plugin Path to the plugin file relative to the plugins directory.
  2229  *                       directory.
  2391  * @return array|false Array of error information as returned by `error_get_last()`,
  2230  * @return array|false Array of error information as it was returned by
  2392  *                     or false if none was recorded.
  2231  *                     `error_get_last()`, or false if none was recorded.
       
  2232  */
  2393  */
  2233 function wp_get_plugin_error( $plugin ) {
  2394 function wp_get_plugin_error( $plugin ) {
  2234 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
  2395 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
  2235 		return false;
  2396 		return false;
  2236 	}
  2397 	}
  2254  * include the plugin file. If the plugin fails, then the redirection will not
  2415  * include the plugin file. If the plugin fails, then the redirection will not
  2255  * be overwritten with the success message and the plugin will not be resumed.
  2416  * be overwritten with the success message and the plugin will not be resumed.
  2256  *
  2417  *
  2257  * @since 5.2.0
  2418  * @since 5.2.0
  2258  *
  2419  *
  2259  * @param string $plugin       Single plugin to resume.
  2420  * @param string $plugin   Single plugin to resume.
  2260  * @param string $redirect     Optional. URL to redirect to. Default empty string.
  2421  * @param string $redirect Optional. URL to redirect to. Default empty string.
  2261  * @return bool|WP_Error True on success, false if `$plugin` was not paused,
  2422  * @return bool|WP_Error True on success, false if `$plugin` was not paused,
  2262  *                       `WP_Error` on failure.
  2423  *                       `WP_Error` on failure.
  2263  */
  2424  */
  2264 function resume_plugin( $plugin, $redirect = '' ) {
  2425 function resume_plugin( $plugin, $redirect = '' ) {
  2265 	/*
  2426 	/*