wp/wp-admin/includes/plugin.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    20  *     Description: Plugin Description
    20  *     Description: Plugin Description
    21  *     Author: Plugin author's name
    21  *     Author: Plugin author's name
    22  *     Author URI: Link to the author's web site
    22  *     Author URI: Link to the author's web site
    23  *     Version: Must be set in the plugin for WordPress 2.3+
    23  *     Version: Must be set in the plugin for WordPress 2.3+
    24  *     Text Domain: Optional. Unique identifier, should be same as the one used in
    24  *     Text Domain: Optional. Unique identifier, should be same as the one used in
    25  *    		load_plugin_textdomain()
    25  *          load_plugin_textdomain()
    26  *     Domain Path: Optional. Only useful if the translations are located in a
    26  *     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
    27  *          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
    28  *          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
    29  *          must have the first slash. Defaults to the base folder the plugin is
    30  *    		located in.
    30  *          located in.
    31  *     Network: Optional. Specify "Network: true" to require that a plugin is activated
    31  *     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
    32  *          across all sites in an installation. This will prevent a plugin from being
    33  *    		activated on a single site when Multisite is enabled.
    33  *          activated on a single site when Multisite is enabled.
    34  *      * / # Remove the space to close comment
    34  *      * / # Remove the space to close comment
    35  *
    35  *
    36  * Some users have issues with opening large files and manipulating the contents
    36  * Some users have issues with opening large files and manipulating the contents
    37  * for want is usually the first 1kiB or 2kiB. This function stops pulling in
    37  * for want is usually the first 1kiB or 2kiB. This function stops pulling in
    38  * the plugin contents when it has all of the required plugin data.
    38  * the plugin contents when it has all of the required plugin data.
    45  * the file. This is not checked however and the file is only opened for
    45  * the file. This is not checked however and the file is only opened for
    46  * reading.
    46  * reading.
    47  *
    47  *
    48  * @since 1.5.0
    48  * @since 1.5.0
    49  *
    49  *
    50  * @param string $plugin_file Path to the main plugin file.
    50  * @param string $plugin_file Absolute path to the main plugin file.
    51  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
    51  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
    52  *                            Default true.
    52  *                            Default true.
    53  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
    53  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
    54  * @return array {
    54  * @return array {
    55  *     Plugin data. Values will be empty if not supplied by the plugin.
    55  *     Plugin data. Values will be empty if not supplied by the plugin.
    66  * }
    66  * }
    67  */
    67  */
    68 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
    68 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
    69 
    69 
    70 	$default_headers = array(
    70 	$default_headers = array(
    71 		'Name' => 'Plugin Name',
    71 		'Name'        => 'Plugin Name',
    72 		'PluginURI' => 'Plugin URI',
    72 		'PluginURI'   => 'Plugin URI',
    73 		'Version' => 'Version',
    73 		'Version'     => 'Version',
    74 		'Description' => 'Description',
    74 		'Description' => 'Description',
    75 		'Author' => 'Author',
    75 		'Author'      => 'Author',
    76 		'AuthorURI' => 'Author URI',
    76 		'AuthorURI'   => 'Author URI',
    77 		'TextDomain' => 'Text Domain',
    77 		'TextDomain'  => 'Text Domain',
    78 		'DomainPath' => 'Domain Path',
    78 		'DomainPath'  => 'Domain Path',
    79 		'Network' => 'Network',
    79 		'Network'     => 'Network',
    80 		// Site Wide Only is deprecated in favor of Network.
    80 		// Site Wide Only is deprecated in favor of Network.
    81 		'_sitewide' => 'Site Wide Only',
    81 		'_sitewide'   => 'Site Wide Only',
    82 	);
    82 	);
    83 
    83 
    84 	$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
    84 	$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
    85 
    85 
    86 	// Site Wide Only is the old header for Network
    86 	// Site Wide Only is the old header for Network
   112 
   112 
   113 /**
   113 /**
   114  * Sanitizes plugin data, optionally adds markup, optionally translates.
   114  * Sanitizes plugin data, optionally adds markup, optionally translates.
   115  *
   115  *
   116  * @since 2.7.0
   116  * @since 2.7.0
       
   117  *
       
   118  * @see get_plugin_data()
       
   119  *
   117  * @access private
   120  * @access private
   118  * @see get_plugin_data()
   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  * }
   119  */
   140  */
   120 function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
   141 function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
   121 
   142 
   122 	// Sanitize the plugin filename to a WP_PLUGIN_DIR relative path
   143 	// Sanitize the plugin filename to a WP_PLUGIN_DIR relative path
   123 	$plugin_file = plugin_basename( $plugin_file );
   144 	$plugin_file = plugin_basename( $plugin_file );
   134 			}
   155 			}
   135 		} elseif ( 'hello.php' == basename( $plugin_file ) ) {
   156 		} elseif ( 'hello.php' == basename( $plugin_file ) ) {
   136 			$textdomain = 'default';
   157 			$textdomain = 'default';
   137 		}
   158 		}
   138 		if ( $textdomain ) {
   159 		if ( $textdomain ) {
   139 			foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field )
   160 			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
   140 				$plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
   162 				$plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
       
   163 			}
   141 		}
   164 		}
   142 	}
   165 	}
   143 
   166 
   144 	// Sanitize fields
   167 	// Sanitize fields
   145 	$allowed_tags = $allowed_tags_in_links = array(
   168 	$allowed_tags_in_links = array(
   146 		'abbr'    => array( 'title' => true ),
   169 		'abbr'    => array( 'title' => true ),
   147 		'acronym' => array( 'title' => true ),
   170 		'acronym' => array( 'title' => true ),
   148 		'code'    => true,
   171 		'code'    => true,
   149 		'em'      => true,
   172 		'em'      => true,
   150 		'strong'  => true,
   173 		'strong'  => true,
   151 	);
   174 	);
   152 	$allowed_tags['a'] = array( 'href' => true, 'title' => true );
   175 
       
   176 	$allowed_tags      = $allowed_tags_in_links;
       
   177 	$allowed_tags['a'] = array(
       
   178 		'href'  => true,
       
   179 		'title' => true,
       
   180 	);
   153 
   181 
   154 	// Name is marked up inside <a> tags. Don't allow these.
   182 	// Name is marked up inside <a> tags. Don't allow these.
   155 	// Author is too, but some plugins have used <a> here (omitting Author URI).
   183 	// Author is too, but some plugins have used <a> here (omitting Author URI).
   156 	$plugin_data['Name']        = wp_kses( $plugin_data['Name'],        $allowed_tags_in_links );
   184 	$plugin_data['Name']   = wp_kses( $plugin_data['Name'], $allowed_tags_in_links );
   157 	$plugin_data['Author']      = wp_kses( $plugin_data['Author'],      $allowed_tags );
   185 	$plugin_data['Author'] = wp_kses( $plugin_data['Author'], $allowed_tags );
   158 
   186 
   159 	$plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags );
   187 	$plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags );
   160 	$plugin_data['Version']     = wp_kses( $plugin_data['Version'],     $allowed_tags );
   188 	$plugin_data['Version']     = wp_kses( $plugin_data['Version'], $allowed_tags );
   161 
   189 
   162 	$plugin_data['PluginURI']   = esc_url( $plugin_data['PluginURI'] );
   190 	$plugin_data['PluginURI'] = esc_url( $plugin_data['PluginURI'] );
   163 	$plugin_data['AuthorURI']   = esc_url( $plugin_data['AuthorURI'] );
   191 	$plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] );
   164 
   192 
   165 	$plugin_data['Title']      = $plugin_data['Name'];
   193 	$plugin_data['Title']      = $plugin_data['Name'];
   166 	$plugin_data['AuthorName'] = $plugin_data['Author'];
   194 	$plugin_data['AuthorName'] = $plugin_data['Author'];
   167 
   195 
   168 	// Apply markup
   196 	// Apply markup
   169 	if ( $markup ) {
   197 	if ( $markup ) {
   170 		if ( $plugin_data['PluginURI'] && $plugin_data['Name'] )
   198 		if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) {
   171 			$plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
   199 			$plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
   172 
   200 		}
   173 		if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] )
   201 
       
   202 		if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] ) {
   174 			$plugin_data['Author'] = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
   203 			$plugin_data['Author'] = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
       
   204 		}
   175 
   205 
   176 		$plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
   206 		$plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
   177 
   207 
   178 		if ( $plugin_data['Author'] )
   208 		if ( $plugin_data['Author'] ) {
   179 			$plugin_data['Description'] .= ' <cite>' . sprintf( __('By %s.'), $plugin_data['Author'] ) . '</cite>';
   209 			$plugin_data['Description'] .= ' <cite>' . sprintf( __( 'By %s.' ), $plugin_data['Author'] ) . '</cite>';
       
   210 		}
   180 	}
   211 	}
   181 
   212 
   182 	return $plugin_data;
   213 	return $plugin_data;
   183 }
   214 }
   184 
   215 
   185 /**
   216 /**
   186  * Get a list of a plugin's files.
   217  * Get a list of a plugin's files.
   187  *
   218  *
   188  * @since 2.8.0
   219  * @since 2.8.0
   189  *
   220  *
   190  * @param string $plugin Path to the main plugin file from plugins directory.
   221  * @param string $plugin Path to the plugin file relative to the plugins directory.
   191  * @return array List of files relative to the plugin root.
   222  * @return array List of files relative to the plugin root.
   192  */
   223  */
   193 function get_plugin_files( $plugin ) {
   224 function get_plugin_files( $plugin ) {
   194 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
   225 	$plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
   195 	$dir = dirname( $plugin_file );
   226 	$dir         = dirname( $plugin_file );
   196 
   227 
   197 	$plugin_files = array( plugin_basename( $plugin_file ) );
   228 	$plugin_files = array( plugin_basename( $plugin_file ) );
   198 
   229 
   199 	if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) {
   230 	if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) {
   200 
   231 
   201 		/**
   232 		/**
   202 		 * Filters the array of excluded directories and files while scanning the folder.
   233 		 * Filters the array of excluded directories and files while scanning the folder.
   203 		 *
   234 		 *
   204 		 * @since 4.9.0
   235 		 * @since 4.9.0
   205 		 *
   236 		 *
   206 		 * @param array $exclusions Array of excluded directories and files.
   237 		 * @param string[] $exclusions Array of excluded directories and files.
   207 		 */
   238 		 */
   208 		$exclusions = (array) apply_filters( 'plugin_files_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
   239 		$exclusions = (array) apply_filters( 'plugin_files_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
   209 
   240 
   210 		$list_files = list_files( $dir, 100, $exclusions );
   241 		$list_files = list_files( $dir, 100, $exclusions );
   211 		$list_files = array_map( 'plugin_basename', $list_files );
   242 		$list_files = array_map( 'plugin_basename', $list_files );
   235  * @since 1.5.0
   266  * @since 1.5.0
   236  *
   267  *
   237  * @param string $plugin_folder Optional. Relative path to single plugin folder.
   268  * @param string $plugin_folder Optional. Relative path to single plugin folder.
   238  * @return array Key is the plugin file path and the value is an array of the plugin data.
   269  * @return array Key is the plugin file path and the value is an array of the plugin data.
   239  */
   270  */
   240 function get_plugins($plugin_folder = '') {
   271 function get_plugins( $plugin_folder = '' ) {
   241 
   272 
   242 	if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') )
   273 	$cache_plugins = wp_cache_get( 'plugins', 'plugins' );
       
   274 	if ( ! $cache_plugins ) {
   243 		$cache_plugins = array();
   275 		$cache_plugins = array();
   244 
   276 	}
   245 	if ( isset($cache_plugins[ $plugin_folder ]) )
   277 
       
   278 	if ( isset( $cache_plugins[ $plugin_folder ] ) ) {
   246 		return $cache_plugins[ $plugin_folder ];
   279 		return $cache_plugins[ $plugin_folder ];
   247 
   280 	}
   248 	$wp_plugins = array ();
   281 
       
   282 	$wp_plugins  = array();
   249 	$plugin_root = WP_PLUGIN_DIR;
   283 	$plugin_root = WP_PLUGIN_DIR;
   250 	if ( !empty($plugin_folder) )
   284 	if ( ! empty( $plugin_folder ) ) {
   251 		$plugin_root .= $plugin_folder;
   285 		$plugin_root .= $plugin_folder;
       
   286 	}
   252 
   287 
   253 	// Files in wp-content/plugins directory
   288 	// Files in wp-content/plugins directory
   254 	$plugins_dir = @ opendir( $plugin_root);
   289 	$plugins_dir  = @ opendir( $plugin_root );
   255 	$plugin_files = array();
   290 	$plugin_files = array();
   256 	if ( $plugins_dir ) {
   291 	if ( $plugins_dir ) {
   257 		while (($file = readdir( $plugins_dir ) ) !== false ) {
   292 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   258 			if ( substr($file, 0, 1) == '.' )
   293 			if ( substr( $file, 0, 1 ) == '.' ) {
   259 				continue;
   294 				continue;
   260 			if ( is_dir( $plugin_root.'/'.$file ) ) {
   295 			}
   261 				$plugins_subdir = @ opendir( $plugin_root.'/'.$file );
   296 			if ( is_dir( $plugin_root . '/' . $file ) ) {
       
   297 				$plugins_subdir = @ opendir( $plugin_root . '/' . $file );
   262 				if ( $plugins_subdir ) {
   298 				if ( $plugins_subdir ) {
   263 					while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
   299 					while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
   264 						if ( substr($subfile, 0, 1) == '.' )
   300 						if ( substr( $subfile, 0, 1 ) == '.' ) {
   265 							continue;
   301 							continue;
   266 						if ( substr($subfile, -4) == '.php' )
   302 						}
       
   303 						if ( substr( $subfile, -4 ) == '.php' ) {
   267 							$plugin_files[] = "$file/$subfile";
   304 							$plugin_files[] = "$file/$subfile";
       
   305 						}
   268 					}
   306 					}
   269 					closedir( $plugins_subdir );
   307 					closedir( $plugins_subdir );
   270 				}
   308 				}
   271 			} else {
   309 			} else {
   272 				if ( substr($file, -4) == '.php' )
   310 				if ( substr( $file, -4 ) == '.php' ) {
   273 					$plugin_files[] = $file;
   311 					$plugin_files[] = $file;
       
   312 				}
   274 			}
   313 			}
   275 		}
   314 		}
   276 		closedir( $plugins_dir );
   315 		closedir( $plugins_dir );
   277 	}
   316 	}
   278 
   317 
   279 	if ( empty($plugin_files) )
   318 	if ( empty( $plugin_files ) ) {
   280 		return $wp_plugins;
   319 		return $wp_plugins;
       
   320 	}
   281 
   321 
   282 	foreach ( $plugin_files as $plugin_file ) {
   322 	foreach ( $plugin_files as $plugin_file ) {
   283 		if ( !is_readable( "$plugin_root/$plugin_file" ) )
   323 		if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
   284 			continue;
   324 			continue;
       
   325 		}
   285 
   326 
   286 		$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   327 		$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   287 
   328 
   288 		if ( empty ( $plugin_data['Name'] ) )
   329 		if ( empty( $plugin_data['Name'] ) ) {
   289 			continue;
   330 			continue;
   290 
   331 		}
   291 		$wp_plugins[plugin_basename( $plugin_file )] = $plugin_data;
   332 
       
   333 		$wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data;
   292 	}
   334 	}
   293 
   335 
   294 	uasort( $wp_plugins, '_sort_uname_callback' );
   336 	uasort( $wp_plugins, '_sort_uname_callback' );
   295 
   337 
   296 	$cache_plugins[ $plugin_folder ] = $wp_plugins;
   338 	$cache_plugins[ $plugin_folder ] = $wp_plugins;
   297 	wp_cache_set('plugins', $cache_plugins, 'plugins');
   339 	wp_cache_set( 'plugins', $cache_plugins, 'plugins' );
   298 
   340 
   299 	return $wp_plugins;
   341 	return $wp_plugins;
   300 }
   342 }
   301 
   343 
   302 /**
   344 /**
   310 function get_mu_plugins() {
   352 function get_mu_plugins() {
   311 	$wp_plugins = array();
   353 	$wp_plugins = array();
   312 	// Files in wp-content/mu-plugins directory
   354 	// Files in wp-content/mu-plugins directory
   313 	$plugin_files = array();
   355 	$plugin_files = array();
   314 
   356 
   315 	if ( ! is_dir( WPMU_PLUGIN_DIR ) )
   357 	if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
   316 		return $wp_plugins;
   358 		return $wp_plugins;
       
   359 	}
   317 	if ( $plugins_dir = @ opendir( WPMU_PLUGIN_DIR ) ) {
   360 	if ( $plugins_dir = @ opendir( WPMU_PLUGIN_DIR ) ) {
   318 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   361 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   319 			if ( substr( $file, -4 ) == '.php' )
   362 			if ( substr( $file, -4 ) == '.php' ) {
   320 				$plugin_files[] = $file;
   363 				$plugin_files[] = $file;
       
   364 			}
   321 		}
   365 		}
   322 	} else {
   366 	} else {
   323 		return $wp_plugins;
   367 		return $wp_plugins;
   324 	}
   368 	}
   325 
   369 
   326 	@closedir( $plugins_dir );
   370 	@closedir( $plugins_dir );
   327 
   371 
   328 	if ( empty($plugin_files) )
   372 	if ( empty( $plugin_files ) ) {
   329 		return $wp_plugins;
   373 		return $wp_plugins;
       
   374 	}
   330 
   375 
   331 	foreach ( $plugin_files as $plugin_file ) {
   376 	foreach ( $plugin_files as $plugin_file ) {
   332 		if ( !is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) )
   377 		if ( ! is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) ) {
   333 			continue;
   378 			continue;
       
   379 		}
   334 
   380 
   335 		$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   381 		$plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   336 
   382 
   337 		if ( empty ( $plugin_data['Name'] ) )
   383 		if ( empty( $plugin_data['Name'] ) ) {
   338 			$plugin_data['Name'] = $plugin_file;
   384 			$plugin_data['Name'] = $plugin_file;
       
   385 		}
   339 
   386 
   340 		$wp_plugins[ $plugin_file ] = $plugin_data;
   387 		$wp_plugins[ $plugin_file ] = $plugin_data;
   341 	}
   388 	}
   342 
   389 
   343 	if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php') <= 30 ) // silence is golden
   390 	if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php' ) <= 30 ) { // silence is golden
   344 		unset( $wp_plugins['index.php'] );
   391 		unset( $wp_plugins['index.php'] );
       
   392 	}
   345 
   393 
   346 	uasort( $wp_plugins, '_sort_uname_callback' );
   394 	uasort( $wp_plugins, '_sort_uname_callback' );
   347 
   395 
   348 	return $wp_plugins;
   396 	return $wp_plugins;
   349 }
   397 }
   350 
   398 
   351 /**
   399 /**
   352  * Callback to sort array by a 'Name' key.
   400  * Callback to sort array by a 'Name' key.
   353  *
   401  *
   354  * @since 3.1.0
   402  * @since 3.1.0
       
   403  *
   355  * @access private
   404  * @access private
       
   405  *
       
   406  * @param array $a array with 'Name' key.
       
   407  * @param array $b array with 'Name' key.
       
   408  * @return int Return 0 or 1 based on two string comparison.
   356  */
   409  */
   357 function _sort_uname_callback( $a, $b ) {
   410 function _sort_uname_callback( $a, $b ) {
   358 	return strnatcasecmp( $a['Name'], $b['Name'] );
   411 	return strnatcasecmp( $a['Name'], $b['Name'] );
   359 }
   412 }
   360 
   413 
   363  *
   416  *
   364  * @since 3.0.0
   417  * @since 3.0.0
   365  * @return array Key is the file path and the value is an array of the plugin data.
   418  * @return array Key is the file path and the value is an array of the plugin data.
   366  */
   419  */
   367 function get_dropins() {
   420 function get_dropins() {
   368 	$dropins = array();
   421 	$dropins      = array();
   369 	$plugin_files = array();
   422 	$plugin_files = array();
   370 
   423 
   371 	$_dropins = _get_dropins();
   424 	$_dropins = _get_dropins();
   372 
   425 
   373 	// These exist in the wp-content directory
   426 	// These exist in the wp-content directory
   374 	if ( $plugins_dir = @ opendir( WP_CONTENT_DIR ) ) {
   427 	if ( $plugins_dir = @ opendir( WP_CONTENT_DIR ) ) {
   375 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   428 		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
   376 			if ( isset( $_dropins[ $file ] ) )
   429 			if ( isset( $_dropins[ $file ] ) ) {
   377 				$plugin_files[] = $file;
   430 				$plugin_files[] = $file;
       
   431 			}
   378 		}
   432 		}
   379 	} else {
   433 	} else {
   380 		return $dropins;
   434 		return $dropins;
   381 	}
   435 	}
   382 
   436 
   383 	@closedir( $plugins_dir );
   437 	@closedir( $plugins_dir );
   384 
   438 
   385 	if ( empty($plugin_files) )
   439 	if ( empty( $plugin_files ) ) {
   386 		return $dropins;
   440 		return $dropins;
       
   441 	}
   387 
   442 
   388 	foreach ( $plugin_files as $plugin_file ) {
   443 	foreach ( $plugin_files as $plugin_file ) {
   389 		if ( !is_readable( WP_CONTENT_DIR . "/$plugin_file" ) )
   444 		if ( ! is_readable( WP_CONTENT_DIR . "/$plugin_file" ) ) {
   390 			continue;
   445 			continue;
       
   446 		}
   391 		$plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   447 		$plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
   392 		if ( empty( $plugin_data['Name'] ) )
   448 		if ( empty( $plugin_data['Name'] ) ) {
   393 			$plugin_data['Name'] = $plugin_file;
   449 			$plugin_data['Name'] = $plugin_file;
       
   450 		}
   394 		$dropins[ $plugin_file ] = $plugin_data;
   451 		$dropins[ $plugin_file ] = $plugin_data;
   395 	}
   452 	}
   396 
   453 
   397 	uksort( $dropins, 'strnatcasecmp' );
   454 	uksort( $dropins, 'strnatcasecmp' );
   398 
   455 
   404  *
   461  *
   405  * Includes Multisite drop-ins only when is_multisite()
   462  * Includes Multisite drop-ins only when is_multisite()
   406  *
   463  *
   407  * @since 3.0.0
   464  * @since 3.0.0
   408  * @return array Key is file name. The value is an array, with the first value the
   465  * @return array Key is file name. The value is an array, with the first value the
   409  *	purpose of the drop-in and the second value the name of the constant that must be
   466  *  purpose of the drop-in and the second value the name of the constant that must be
   410  *	true for the drop-in to be used, or true if no constant is required.
   467  *  true for the drop-in to be used, or true if no constant is required.
   411  */
   468  */
   412 function _get_dropins() {
   469 function _get_dropins() {
   413 	$dropins = array(
   470 	$dropins = array(
   414 		'advanced-cache.php' => array( __( 'Advanced caching plugin.'       ), 'WP_CACHE' ), // WP_CACHE
   471 		'advanced-cache.php'      => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE
   415 		'db.php'             => array( __( 'Custom database class.'         ), true ), // auto on load
   472 		'db.php'                  => array( __( 'Custom database class.' ), true ), // auto on load
   416 		'db-error.php'       => array( __( 'Custom database error message.' ), true ), // auto on error
   473 		'db-error.php'            => array( __( 'Custom database error message.' ), true ), // auto on error
   417 		'install.php'        => array( __( 'Custom installation script.'    ), true ), // auto on installation
   474 		'install.php'             => array( __( 'Custom installation script.' ), true ), // auto on installation
   418 		'maintenance.php'    => array( __( 'Custom maintenance message.'    ), true ), // auto on maintenance
   475 		'maintenance.php'         => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance
   419 		'object-cache.php'   => array( __( 'External object cache.'         ), true ), // auto on load
   476 		'object-cache.php'        => array( __( 'External object cache.' ), true ), // auto on load
       
   477 		'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
   420 	);
   479 	);
   421 
   480 
   422 	if ( is_multisite() ) {
   481 	if ( is_multisite() ) {
   423 		$dropins['sunrise.php'       ] = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE
   482 		$dropins['sunrise.php']        = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE
   424 		$dropins['blog-deleted.php'  ] = array( __( 'Custom site deleted message.'   ), true ); // auto on deleted blog
   483 		$dropins['blog-deleted.php']   = array( __( 'Custom site deleted message.' ), true ); // auto on deleted blog
   425 		$dropins['blog-inactive.php' ] = array( __( 'Custom site inactive message.'  ), true ); // auto on inactive blog
   484 		$dropins['blog-inactive.php']  = array( __( 'Custom site inactive message.' ), true ); // auto on inactive blog
   426 		$dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // auto on archived or spammed blog
   485 		$dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // auto on archived or spammed blog
   427 	}
   486 	}
   428 
   487 
   429 	return $dropins;
   488 	return $dropins;
   430 }
   489 }
   431 
   490 
   432 /**
   491 /**
   433  * Check whether a plugin is active.
   492  * Determines whether a plugin is active.
   434  *
   493  *
   435  * Only plugins installed in the plugins/ folder can be active.
   494  * Only plugins installed in the plugins/ folder can be active.
   436  *
   495  *
   437  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
   496  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
   438  * return false for those plugins.
   497  * return false for those plugins.
   439  *
   498  *
       
   499  * For more information on this and similar theme functions, check out
       
   500  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
       
   501  * Conditional Tags} article in the Theme Developer Handbook.
       
   502  *
   440  * @since 2.5.0
   503  * @since 2.5.0
   441  *
   504  *
   442  * @param string $plugin Path to the main plugin file from plugins directory.
   505  * @param string $plugin Path to the plugin file relative to the plugins directory.
   443  * @return bool True, if in the active plugins list. False, not in the list.
   506  * @return bool True, if in the active plugins list. False, not in the list.
   444  */
   507  */
   445 function is_plugin_active( $plugin ) {
   508 function is_plugin_active( $plugin ) {
   446 	return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin );
   509 	return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin );
   447 }
   510 }
   448 
   511 
   449 /**
   512 /**
   450  * Check whether the plugin is inactive.
   513  * Determines whether the plugin is inactive.
   451  *
   514  *
   452  * Reverse of is_plugin_active(). Used as a callback.
   515  * Reverse of is_plugin_active(). Used as a callback.
       
   516  *
       
   517  * For more information on this and similar theme functions, check out
       
   518  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
       
   519  * Conditional Tags} article in the Theme Developer Handbook.
   453  *
   520  *
   454  * @since 3.1.0
   521  * @since 3.1.0
   455  * @see is_plugin_active()
   522  * @see is_plugin_active()
   456  *
   523  *
   457  * @param string $plugin Path to the main plugin file from plugins directory.
   524  * @param string $plugin Path to the plugin file relative to the plugins directory.
   458  * @return bool True if inactive. False if active.
   525  * @return bool True if inactive. False if active.
   459  */
   526  */
   460 function is_plugin_inactive( $plugin ) {
   527 function is_plugin_inactive( $plugin ) {
   461 	return ! is_plugin_active( $plugin );
   528 	return ! is_plugin_active( $plugin );
   462 }
   529 }
   463 
   530 
   464 /**
   531 /**
   465  * Check whether the plugin is active for the entire network.
   532  * Determines whether the plugin is active for the entire network.
   466  *
   533  *
   467  * Only plugins installed in the plugins/ folder can be active.
   534  * Only plugins installed in the plugins/ folder can be active.
   468  *
   535  *
   469  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
   536  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
   470  * return false for those plugins.
   537  * return false for those plugins.
   471  *
   538  *
       
   539  * For more information on this and similar theme functions, check out
       
   540  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
       
   541  * Conditional Tags} article in the Theme Developer Handbook.
       
   542  *
   472  * @since 3.0.0
   543  * @since 3.0.0
   473  *
   544  *
   474  * @param string $plugin Path to the main plugin file from plugins directory.
   545  * @param string $plugin Path to the plugin file relative to the plugins directory.
   475  * @return bool True, if active for the network, otherwise false.
   546  * @return bool True if active for the network, otherwise false.
   476  */
   547  */
   477 function is_plugin_active_for_network( $plugin ) {
   548 function is_plugin_active_for_network( $plugin ) {
   478 	if ( !is_multisite() )
   549 	if ( ! is_multisite() ) {
   479 		return false;
   550 		return false;
   480 
   551 	}
   481 	$plugins = get_site_option( 'active_sitewide_plugins');
   552 
   482 	if ( isset($plugins[$plugin]) )
   553 	$plugins = get_site_option( 'active_sitewide_plugins' );
       
   554 	if ( isset( $plugins[ $plugin ] ) ) {
   483 		return true;
   555 		return true;
       
   556 	}
   484 
   557 
   485 	return false;
   558 	return false;
   486 }
   559 }
   487 
   560 
   488 /**
   561 /**
   492  *
   565  *
   493  * Checks for "Site Wide Only: true" for backward compatibility.
   566  * Checks for "Site Wide Only: true" for backward compatibility.
   494  *
   567  *
   495  * @since 3.0.0
   568  * @since 3.0.0
   496  *
   569  *
   497  * @param string $plugin Path to the main plugin file from plugins directory.
   570  * @param string $plugin Path to the plugin file relative to the plugins directory.
   498  * @return bool True if plugin is network only, false otherwise.
   571  * @return bool True if plugin is network only, false otherwise.
   499  */
   572  */
   500 function is_network_only_plugin( $plugin ) {
   573 function is_network_only_plugin( $plugin ) {
   501 	$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
   574 	$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
   502 	if ( $plugin_data )
   575 	if ( $plugin_data ) {
   503 		return $plugin_data['Network'];
   576 		return $plugin_data['Network'];
       
   577 	}
   504 	return false;
   578 	return false;
   505 }
   579 }
   506 
   580 
   507 /**
   581 /**
   508  * Attempts activation of plugin in a "sandbox" and redirects on success.
   582  * Attempts activation of plugin in a "sandbox" and redirects on success.
   521  *
   595  *
   522  * If any errors are found or text is outputted, then it will be captured to
   596  * If any errors are found or text is outputted, then it will be captured to
   523  * ensure that the success redirection will update the error redirection.
   597  * ensure that the success redirection will update the error redirection.
   524  *
   598  *
   525  * @since 2.5.0
   599  * @since 2.5.0
   526  *
   600  * @since 5.2.0 Test for WordPress version and PHP version compatibility.
   527  * @param string $plugin       Path to the main plugin file from plugins directory.
   601  *
       
   602  * @param string $plugin       Path to the plugin file relative to the plugins directory.
   528  * @param string $redirect     Optional. URL to redirect to.
   603  * @param string $redirect     Optional. URL to redirect to.
   529  * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network
   604  * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network
   530  *                             or just the current site. Multisite only. Default false.
   605  *                             or just the current site. Multisite only. Default false.
   531  * @param bool   $silent       Optional. Whether to prevent calling activation hooks. Default false.
   606  * @param bool   $silent       Optional. Whether to prevent calling activation hooks. Default false.
   532  * @return WP_Error|null WP_Error on invalid file or null on success.
   607  * @return WP_Error|null WP_Error on invalid file or null on success.
   533  */
   608  */
   534 function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
   609 function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
   535 	$plugin = plugin_basename( trim( $plugin ) );
   610 	$plugin = plugin_basename( trim( $plugin ) );
   536 
   611 
   537 	if ( is_multisite() && ( $network_wide || is_network_only_plugin($plugin) ) ) {
   612 	if ( is_multisite() && ( $network_wide || is_network_only_plugin( $plugin ) ) ) {
   538 		$network_wide = true;
   613 		$network_wide        = true;
   539 		$current = get_site_option( 'active_sitewide_plugins', array() );
   614 		$current             = get_site_option( 'active_sitewide_plugins', array() );
   540 		$_GET['networkwide'] = 1; // Back compat for plugins looking for this value.
   615 		$_GET['networkwide'] = 1; // Back compat for plugins looking for this value.
   541 	} else {
   616 	} else {
   542 		$current = get_option( 'active_plugins', array() );
   617 		$current = get_option( 'active_plugins', array() );
   543 	}
   618 	}
   544 
   619 
   545 	$valid = validate_plugin($plugin);
   620 	$valid = validate_plugin( $plugin );
   546 	if ( is_wp_error($valid) )
   621 	if ( is_wp_error( $valid ) ) {
   547 		return $valid;
   622 		return $valid;
       
   623 	}
       
   624 
       
   625 	$requirements = validate_plugin_requirements( $plugin );
       
   626 	if ( is_wp_error( $requirements ) ) {
       
   627 		return $requirements;
       
   628 	}
   548 
   629 
   549 	if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current ) ) ) {
   630 	if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current ) ) ) {
   550 		if ( !empty($redirect) )
   631 		if ( ! empty( $redirect ) ) {
   551 			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
   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
       
   633 		}
       
   634 
   552 		ob_start();
   635 		ob_start();
   553 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
   636 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
   554 		$_wp_plugin_file = $plugin;
   637 		$_wp_plugin_file = $plugin;
       
   638 		if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
       
   639 			define( 'WP_SANDBOX_SCRAPING', true );
       
   640 		}
   555 		include_once( WP_PLUGIN_DIR . '/' . $plugin );
   641 		include_once( WP_PLUGIN_DIR . '/' . $plugin );
   556 		$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
   642 		$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
   557 
   643 
   558 		if ( ! $silent ) {
   644 		if ( ! $silent ) {
   559 			/**
   645 			/**
   562 			 * If a plugin is silently activated (such as during an update),
   648 			 * If a plugin is silently activated (such as during an update),
   563 			 * this hook does not fire.
   649 			 * this hook does not fire.
   564 			 *
   650 			 *
   565 			 * @since 2.9.0
   651 			 * @since 2.9.0
   566 			 *
   652 			 *
   567 			 * @param string $plugin       Path to the main plugin file from plugins directory.
   653 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   568 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   654 			 * @param bool   $network_wide Whether to enable the plugin for all sites in the network
   569 			 *                             or just the current site. Multisite only. Default is false.
   655 			 *                             or just the current site. Multisite only. Default is false.
   570 			 */
   656 			 */
   571 			do_action( 'activate_plugin', $plugin, $network_wide );
   657 			do_action( 'activate_plugin', $plugin, $network_wide );
   572 
   658 
   585 			 */
   671 			 */
   586 			do_action( "activate_{$plugin}", $network_wide );
   672 			do_action( "activate_{$plugin}", $network_wide );
   587 		}
   673 		}
   588 
   674 
   589 		if ( $network_wide ) {
   675 		if ( $network_wide ) {
   590 			$current = get_site_option( 'active_sitewide_plugins', array() );
   676 			$current            = get_site_option( 'active_sitewide_plugins', array() );
   591 			$current[$plugin] = time();
   677 			$current[ $plugin ] = time();
   592 			update_site_option( 'active_sitewide_plugins', $current );
   678 			update_site_option( 'active_sitewide_plugins', $current );
   593 		} else {
   679 		} else {
   594 			$current = get_option( 'active_plugins', array() );
   680 			$current   = get_option( 'active_plugins', array() );
   595 			$current[] = $plugin;
   681 			$current[] = $plugin;
   596 			sort($current);
   682 			sort( $current );
   597 			update_option('active_plugins', $current);
   683 			update_option( 'active_plugins', $current );
   598 		}
   684 		}
   599 
   685 
   600 		if ( ! $silent ) {
   686 		if ( ! $silent ) {
   601 			/**
   687 			/**
   602 			 * Fires after a plugin has been activated.
   688 			 * Fires after a plugin has been activated.
   604 			 * If a plugin is silently activated (such as during an update),
   690 			 * If a plugin is silently activated (such as during an update),
   605 			 * this hook does not fire.
   691 			 * this hook does not fire.
   606 			 *
   692 			 *
   607 			 * @since 2.9.0
   693 			 * @since 2.9.0
   608 			 *
   694 			 *
   609 			 * @param string $plugin       Path to the main plugin file from plugins directory.
   695 			 * @param string $plugin       Path to the plugin file relative to the plugins directory.
   610 			 * @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
   611 			 *                             or just the current site. Multisite only. Default is false.
   697 			 *                             or just the current site. Multisite only. Default is false.
   612 			 */
   698 			 */
   613 			do_action( 'activated_plugin', $plugin, $network_wide );
   699 			do_action( 'activated_plugin', $plugin, $network_wide );
   614 		}
   700 		}
   615 
   701 
   616 		if ( ob_get_length() > 0 ) {
   702 		if ( ob_get_length() > 0 ) {
   617 			$output = ob_get_clean();
   703 			$output = ob_get_clean();
   618 			return new WP_Error('unexpected_output', __('The plugin generated unexpected output.'), $output);
   704 			return new WP_Error( 'unexpected_output', __( 'The plugin generated unexpected output.' ), $output );
   619 		}
   705 		}
   620 		ob_end_clean();
   706 		ob_end_clean();
   621 	}
   707 	}
   622 
   708 
   623 	return null;
   709 	return null;
   632  * @since 2.5.0
   718  * @since 2.5.0
   633  *
   719  *
   634  * @param string|array $plugins Single plugin or list of plugins to deactivate.
   720  * @param string|array $plugins Single plugin or list of plugins to deactivate.
   635  * @param bool $silent Prevent calling deactivation hooks. Default is false.
   721  * @param bool $silent Prevent calling deactivation hooks. Default is false.
   636  * @param mixed $network_wide Whether to deactivate the plugin for all sites in the network.
   722  * @param mixed $network_wide Whether to deactivate the plugin for all sites in the network.
   637  * 	A value of null (the default) will deactivate plugins for both the site and the network.
   723  *  A value of null (the default) will deactivate plugins for both the site and the network.
   638  */
   724  */
   639 function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) {
   725 function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) {
   640 	if ( is_multisite() )
   726 	if ( is_multisite() ) {
   641 		$network_current = get_site_option( 'active_sitewide_plugins', array() );
   727 		$network_current = get_site_option( 'active_sitewide_plugins', array() );
       
   728 	}
   642 	$current = get_option( 'active_plugins', array() );
   729 	$current = get_option( 'active_plugins', array() );
   643 	$do_blog = $do_network = false;
   730 	$do_blog = $do_network = false;
   644 
   731 
   645 	foreach ( (array) $plugins as $plugin ) {
   732 	foreach ( (array) $plugins as $plugin ) {
   646 		$plugin = plugin_basename( trim( $plugin ) );
   733 		$plugin = plugin_basename( trim( $plugin ) );
   647 		if ( ! is_plugin_active($plugin) )
   734 		if ( ! is_plugin_active( $plugin ) ) {
   648 			continue;
   735 			continue;
       
   736 		}
   649 
   737 
   650 		$network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin );
   738 		$network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin );
   651 
   739 
   652 		if ( ! $silent ) {
   740 		if ( ! $silent ) {
   653 			/**
   741 			/**
   656 			 * If a plugin is silently deactivated (such as during an update),
   744 			 * If a plugin is silently deactivated (such as during an update),
   657 			 * this hook does not fire.
   745 			 * this hook does not fire.
   658 			 *
   746 			 *
   659 			 * @since 2.9.0
   747 			 * @since 2.9.0
   660 			 *
   748 			 *
   661 			 * @param string $plugin               Path to the main plugin file from plugins directory.
   749 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   662 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
   750 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
   663 			 *                                     or just the current site. Multisite only. Default is false.
   751 			 *                                     or just the current site. Multisite only. Default is false.
   664 			 */
   752 			 */
   665 			do_action( 'deactivate_plugin', $plugin, $network_deactivating );
   753 			do_action( 'deactivate_plugin', $plugin, $network_deactivating );
   666 		}
   754 		}
   678 			$key = array_search( $plugin, $current );
   766 			$key = array_search( $plugin, $current );
   679 			if ( false !== $key ) {
   767 			if ( false !== $key ) {
   680 				$do_blog = true;
   768 				$do_blog = true;
   681 				unset( $current[ $key ] );
   769 				unset( $current[ $key ] );
   682 			}
   770 			}
       
   771 		}
       
   772 
       
   773 		if ( $do_blog && wp_is_recovery_mode() ) {
       
   774 			list( $extension ) = explode( '/', $plugin );
       
   775 			wp_paused_plugins()->delete( $extension );
   683 		}
   776 		}
   684 
   777 
   685 		if ( ! $silent ) {
   778 		if ( ! $silent ) {
   686 			/**
   779 			/**
   687 			 * Fires as a specific plugin is being deactivated.
   780 			 * Fires as a specific plugin is being deactivated.
   704 			 * If a plugin is silently deactivated (such as during an update),
   797 			 * If a plugin is silently deactivated (such as during an update),
   705 			 * this hook does not fire.
   798 			 * this hook does not fire.
   706 			 *
   799 			 *
   707 			 * @since 2.9.0
   800 			 * @since 2.9.0
   708 			 *
   801 			 *
   709 			 * @param string $plugin               Path to the main plugin file from plugins directory.
   802 			 * @param string $plugin               Path to the plugin file relative to the plugins directory.
   710 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network.
   803 			 * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network.
   711 			 *                                     or just the current site. Multisite only. Default false.
   804 			 *                                     or just the current site. Multisite only. Default false.
   712 			 */
   805 			 */
   713 			do_action( 'deactivated_plugin', $plugin, $network_deactivating );
   806 			do_action( 'deactivated_plugin', $plugin, $network_deactivating );
   714 		}
   807 		}
   715 	}
   808 	}
   716 
   809 
   717 	if ( $do_blog )
   810 	if ( $do_blog ) {
   718 		update_option('active_plugins', $current);
   811 		update_option( 'active_plugins', $current );
   719 	if ( $do_network )
   812 	}
       
   813 	if ( $do_network ) {
   720 		update_site_option( 'active_sitewide_plugins', $network_current );
   814 		update_site_option( 'active_sitewide_plugins', $network_current );
       
   815 	}
   721 }
   816 }
   722 
   817 
   723 /**
   818 /**
   724  * Activate multiple plugins.
   819  * Activate multiple plugins.
   725  *
   820  *
   735  * @param bool $network_wide Whether to enable the plugin for all sites in the network.
   830  * @param bool $network_wide Whether to enable the plugin for all sites in the network.
   736  * @param bool $silent Prevent calling activation hooks. Default is false.
   831  * @param bool $silent Prevent calling activation hooks. Default is false.
   737  * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation.
   832  * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation.
   738  */
   833  */
   739 function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) {
   834 function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) {
   740 	if ( !is_array($plugins) )
   835 	if ( ! is_array( $plugins ) ) {
   741 		$plugins = array($plugins);
   836 		$plugins = array( $plugins );
       
   837 	}
   742 
   838 
   743 	$errors = array();
   839 	$errors = array();
   744 	foreach ( $plugins as $plugin ) {
   840 	foreach ( $plugins as $plugin ) {
   745 		if ( !empty($redirect) )
   841 		if ( ! empty( $redirect ) ) {
   746 			$redirect = add_query_arg('plugin', $plugin, $redirect);
   842 			$redirect = add_query_arg( 'plugin', $plugin, $redirect );
   747 		$result = activate_plugin($plugin, $redirect, $network_wide, $silent);
   843 		}
   748 		if ( is_wp_error($result) )
   844 		$result = activate_plugin( $plugin, $redirect, $network_wide, $silent );
   749 			$errors[$plugin] = $result;
   845 		if ( is_wp_error( $result ) ) {
   750 	}
   846 			$errors[ $plugin ] = $result;
   751 
   847 		}
   752 	if ( !empty($errors) )
   848 	}
   753 		return new WP_Error('plugins_invalid', __('One of the plugins is invalid.'), $errors);
   849 
       
   850 	if ( ! empty( $errors ) ) {
       
   851 		return new WP_Error( 'plugins_invalid', __( 'One of the plugins is invalid.' ), $errors );
       
   852 	}
   754 
   853 
   755 	return true;
   854 	return true;
   756 }
   855 }
   757 
   856 
   758 /**
   857 /**
   759  * Remove directory and files of a plugin for a list of plugins.
   858  * Remove directory and files of a plugin for a list of plugins.
   760  *
   859  *
   761  * @since 2.6.0
   860  * @since 2.6.0
   762  *
   861  *
   763  * @global WP_Filesystem_Base $wp_filesystem
   862  * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
   764  *
   863  *
   765  * @param array  $plugins    List of plugins to delete.
   864  * @param string[] $plugins    List of plugin paths to delete, relative to the plugins directory.
   766  * @param string $deprecated Deprecated.
   865  * @param string   $deprecated Not used.
   767  * @return bool|null|WP_Error True on success, false is $plugins is empty, WP_Error on failure.
   866  * @return bool|null|WP_Error True on success, false if `$plugins` is empty, `WP_Error` on failure.
   768  *                            Null if filesystem credentials are required to proceed.
   867  *                            `null` if filesystem credentials are required to proceed.
   769  */
   868  */
   770 function delete_plugins( $plugins, $deprecated = '' ) {
   869 function delete_plugins( $plugins, $deprecated = '' ) {
   771 	global $wp_filesystem;
   870 	global $wp_filesystem;
   772 
   871 
   773 	if ( empty($plugins) )
   872 	if ( empty( $plugins ) ) {
   774 		return false;
   873 		return false;
       
   874 	}
   775 
   875 
   776 	$checked = array();
   876 	$checked = array();
   777 	foreach ( $plugins as $plugin )
   877 	foreach ( $plugins as $plugin ) {
   778 		$checked[] = 'checked[]=' . $plugin;
   878 		$checked[] = 'checked[]=' . $plugin;
   779 
   879 	}
   780 	$url = wp_nonce_url('plugins.php?action=delete-selected&verify-delete=1&' . implode('&', $checked), 'bulk-plugins');
   880 
       
   881 	$url = wp_nonce_url( 'plugins.php?action=delete-selected&verify-delete=1&' . implode( '&', $checked ), 'bulk-plugins' );
   781 
   882 
   782 	ob_start();
   883 	ob_start();
   783 	$credentials = request_filesystem_credentials( $url );
   884 	$credentials = request_filesystem_credentials( $url );
   784 	$data = ob_get_clean();
   885 	$data        = ob_get_clean();
   785 
   886 
   786 	if ( false === $credentials ) {
   887 	if ( false === $credentials ) {
   787 		if ( ! empty($data) ){
   888 		if ( ! empty( $data ) ) {
   788 			include_once( ABSPATH . 'wp-admin/admin-header.php');
   889 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
   789 			echo $data;
   890 			echo $data;
   790 			include( ABSPATH . 'wp-admin/admin-footer.php');
   891 			include( ABSPATH . 'wp-admin/admin-footer.php' );
   791 			exit;
   892 			exit;
   792 		}
   893 		}
   793 		return;
   894 		return;
   794 	}
   895 	}
   795 
   896 
   796 	if ( ! WP_Filesystem( $credentials ) ) {
   897 	if ( ! WP_Filesystem( $credentials ) ) {
   797 		ob_start();
   898 		ob_start();
   798 		request_filesystem_credentials( $url, '', true ); // Failed to connect, Error and request again.
   899 		request_filesystem_credentials( $url, '', true ); // Failed to connect, Error and request again.
   799 		$data = ob_get_clean();
   900 		$data = ob_get_clean();
   800 
   901 
   801 		if ( ! empty($data) ){
   902 		if ( ! empty( $data ) ) {
   802 			include_once( ABSPATH . 'wp-admin/admin-header.php');
   903 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
   803 			echo $data;
   904 			echo $data;
   804 			include( ABSPATH . 'wp-admin/admin-footer.php');
   905 			include( ABSPATH . 'wp-admin/admin-footer.php' );
   805 			exit;
   906 			exit;
   806 		}
   907 		}
   807 		return;
   908 		return;
   808 	}
   909 	}
   809 
   910 
   810 	if ( ! is_object($wp_filesystem) )
   911 	if ( ! is_object( $wp_filesystem ) ) {
   811 		return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
   912 		return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
   812 
   913 	}
   813 	if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
   914 
   814 		return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors);
   915 	if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
       
   916 		return new WP_Error( 'fs_error', __( 'Filesystem error.' ), $wp_filesystem->errors );
       
   917 	}
   815 
   918 
   816 	// Get the base plugin folder.
   919 	// Get the base plugin folder.
   817 	$plugins_dir = $wp_filesystem->wp_plugins_dir();
   920 	$plugins_dir = $wp_filesystem->wp_plugins_dir();
   818 	if ( empty( $plugins_dir ) ) {
   921 	if ( empty( $plugins_dir ) ) {
   819 		return new WP_Error( 'fs_no_plugins_dir', __( 'Unable to locate WordPress plugin directory.' ) );
   922 		return new WP_Error( 'fs_no_plugins_dir', __( 'Unable to locate WordPress plugin directory.' ) );
   826 	$errors = array();
   929 	$errors = array();
   827 
   930 
   828 	foreach ( $plugins as $plugin_file ) {
   931 	foreach ( $plugins as $plugin_file ) {
   829 		// Run Uninstall hook.
   932 		// Run Uninstall hook.
   830 		if ( is_uninstallable_plugin( $plugin_file ) ) {
   933 		if ( is_uninstallable_plugin( $plugin_file ) ) {
   831 			uninstall_plugin($plugin_file);
   934 			uninstall_plugin( $plugin_file );
   832 		}
   935 		}
   833 
   936 
   834 		/**
   937 		/**
   835 		 * Fires immediately before a plugin deletion attempt.
   938 		 * Fires immediately before a plugin deletion attempt.
   836 		 *
   939 		 *
   837 		 * @since 4.4.0
   940 		 * @since 4.4.0
   838 		 *
   941 		 *
   839 		 * @param string $plugin_file Plugin file name.
   942 		 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   840 		 */
   943 		 */
   841 		do_action( 'delete_plugin', $plugin_file );
   944 		do_action( 'delete_plugin', $plugin_file );
   842 
   945 
   843 		$this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );
   946 		$this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );
   844 
   947 
   852 		/**
   955 		/**
   853 		 * Fires immediately after a plugin deletion attempt.
   956 		 * Fires immediately after a plugin deletion attempt.
   854 		 *
   957 		 *
   855 		 * @since 4.4.0
   958 		 * @since 4.4.0
   856 		 *
   959 		 *
   857 		 * @param string $plugin_file Plugin file name.
   960 		 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   858 		 * @param bool   $deleted     Whether the plugin deletion was successful.
   961 		 * @param bool   $deleted     Whether the plugin deletion was successful.
   859 		 */
   962 		 */
   860 		do_action( 'deleted_plugin', $plugin_file, $deleted );
   963 		do_action( 'deleted_plugin', $plugin_file, $deleted );
   861 
   964 
   862 		if ( ! $deleted ) {
   965 		if ( ! $deleted ) {
   870 			$translations = $plugin_translations[ $plugin_slug ];
   973 			$translations = $plugin_translations[ $plugin_slug ];
   871 
   974 
   872 			foreach ( $translations as $translation => $data ) {
   975 			foreach ( $translations as $translation => $data ) {
   873 				$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' );
   976 				$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' );
   874 				$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' );
   977 				$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' );
       
   978 
       
   979 				$json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' );
       
   980 				if ( $json_translation_files ) {
       
   981 					array_map( array( $wp_filesystem, 'delete' ), $json_translation_files );
       
   982 				}
   875 			}
   983 			}
   876 		}
   984 		}
   877 	}
   985 	}
   878 
   986 
   879 	// Remove deleted plugins from the plugin updates list.
   987 	// Remove deleted plugins from the plugin updates list.
   880 	if ( $current = get_site_transient('update_plugins') ) {
   988 	if ( $current = get_site_transient( 'update_plugins' ) ) {
   881 		// Don't remove the plugins that weren't deleted.
   989 		// Don't remove the plugins that weren't deleted.
   882 		$deleted = array_diff( $plugins, $errors );
   990 		$deleted = array_diff( $plugins, $errors );
   883 
   991 
   884 		foreach ( $deleted as $plugin_file ) {
   992 		foreach ( $deleted as $plugin_file ) {
   885 			unset( $current->response[ $plugin_file ] );
   993 			unset( $current->response[ $plugin_file ] );
   920 		$plugins = array();
  1028 		$plugins = array();
   921 	}
  1029 	}
   922 
  1030 
   923 	if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) {
  1031 	if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) {
   924 		$network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
  1032 		$network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
   925 		$plugins = array_merge( $plugins, array_keys( $network_plugins ) );
  1033 		$plugins         = array_merge( $plugins, array_keys( $network_plugins ) );
   926 	}
  1034 	}
   927 
  1035 
   928 	if ( empty( $plugins ) )
  1036 	if ( empty( $plugins ) ) {
   929 		return array();
  1037 		return array();
       
  1038 	}
   930 
  1039 
   931 	$invalid = array();
  1040 	$invalid = array();
   932 
  1041 
   933 	// Invalid plugins get deactivated.
  1042 	// Invalid plugins get deactivated.
   934 	foreach ( $plugins as $plugin ) {
  1043 	foreach ( $plugins as $plugin ) {
   935 		$result = validate_plugin( $plugin );
  1044 		$result = validate_plugin( $plugin );
   936 		if ( is_wp_error( $result ) ) {
  1045 		if ( is_wp_error( $result ) ) {
   937 			$invalid[$plugin] = $result;
  1046 			$invalid[ $plugin ] = $result;
   938 			deactivate_plugins( $plugin, true );
  1047 			deactivate_plugins( $plugin, true );
   939 		}
  1048 		}
   940 	}
  1049 	}
   941 	return $invalid;
  1050 	return $invalid;
   942 }
  1051 }
   946  *
  1055  *
   947  * Checks that the main plugin file exists and is a valid plugin. See validate_file().
  1056  * Checks that the main plugin file exists and is a valid plugin. See validate_file().
   948  *
  1057  *
   949  * @since 2.5.0
  1058  * @since 2.5.0
   950  *
  1059  *
   951  * @param string $plugin Path to the main plugin file from plugins directory.
  1060  * @param string $plugin Path to the plugin file relative to the plugins directory.
   952  * @return WP_Error|int 0 on success, WP_Error on failure.
  1061  * @return WP_Error|int 0 on success, WP_Error on failure.
   953  */
  1062  */
   954 function validate_plugin($plugin) {
  1063 function validate_plugin( $plugin ) {
   955 	if ( validate_file($plugin) )
  1064 	if ( validate_file( $plugin ) ) {
   956 		return new WP_Error('plugin_invalid', __('Invalid plugin path.'));
  1065 		return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) );
   957 	if ( ! file_exists(WP_PLUGIN_DIR . '/' . $plugin) )
  1066 	}
   958 		return new WP_Error('plugin_not_found', __('Plugin file does not exist.'));
  1067 	if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
       
  1068 		return new WP_Error( 'plugin_not_found', __( 'Plugin file does not exist.' ) );
       
  1069 	}
   959 
  1070 
   960 	$installed_plugins = get_plugins();
  1071 	$installed_plugins = get_plugins();
   961 	if ( ! isset($installed_plugins[$plugin]) )
  1072 	if ( ! isset( $installed_plugins[ $plugin ] ) ) {
   962 		return new WP_Error('no_plugin_header', __('The plugin does not have a valid header.'));
  1073 		return new WP_Error( 'no_plugin_header', __( 'The plugin does not have a valid header.' ) );
       
  1074 	}
   963 	return 0;
  1075 	return 0;
   964 }
  1076 }
   965 
  1077 
   966 /**
  1078 /**
       
  1079  * Validate the plugin requirements for WP version and PHP version.
       
  1080  *
       
  1081  * @since 5.2.0
       
  1082  *
       
  1083  * @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.
       
  1085  */
       
  1086 function validate_plugin_requirements( $plugin ) {
       
  1087 	$readme_file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/readme.txt';
       
  1088 
       
  1089 	if ( file_exists( $readme_file ) ) {
       
  1090 		$plugin_data = get_file_data(
       
  1091 			$readme_file,
       
  1092 			array(
       
  1093 				'requires'     => 'Requires at least',
       
  1094 				'requires_php' => 'Requires PHP',
       
  1095 			),
       
  1096 			'plugin'
       
  1097 		);
       
  1098 	} else {
       
  1099 		return true;
       
  1100 	}
       
  1101 
       
  1102 	$plugin_data['wp_compatible']  = is_wp_version_compatible( $plugin_data['requires'] );
       
  1103 	$plugin_data['php_compatible'] = is_php_version_compatible( $plugin_data['requires_php'] );
       
  1104 
       
  1105 	$plugin_data = array_merge( $plugin_data, get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ) );
       
  1106 
       
  1107 	if ( ! $plugin_data['wp_compatible'] && ! $plugin_data['php_compatible'] ) {
       
  1108 		return new WP_Error(
       
  1109 			'plugin_wp_php_incompatible',
       
  1110 			sprintf(
       
  1111 				/* translators: %s: plugin name */
       
  1112 				__( '<strong>Error:</strong> Current WordPress and PHP versions do not meet minimum requirements for %s.' ),
       
  1113 				$plugin_data['Name']
       
  1114 			)
       
  1115 		);
       
  1116 	} elseif ( ! $plugin_data['php_compatible'] ) {
       
  1117 		return new WP_Error(
       
  1118 			'plugin_php_incompatible',
       
  1119 			sprintf(
       
  1120 				/* translators: %s: plugin name */
       
  1121 				__( '<strong>Error:</strong> Current PHP version does not meet minimum requirements for %s.' ),
       
  1122 				$plugin_data['Name']
       
  1123 			)
       
  1124 		);
       
  1125 	} elseif ( ! $plugin_data['wp_compatible'] ) {
       
  1126 		return new WP_Error(
       
  1127 			'plugin_wp_incompatible',
       
  1128 			sprintf(
       
  1129 				/* translators: %s: plugin name */
       
  1130 				__( '<strong>Error:</strong> Current WordPress version does not meet minimum requirements for %s.' ),
       
  1131 				$plugin_data['Name']
       
  1132 			)
       
  1133 		);
       
  1134 	}
       
  1135 
       
  1136 	return true;
       
  1137 }
       
  1138 
       
  1139 /**
   967  * Whether the plugin can be uninstalled.
  1140  * Whether the plugin can be uninstalled.
   968  *
  1141  *
   969  * @since 2.7.0
  1142  * @since 2.7.0
   970  *
  1143  *
   971  * @param string $plugin Path to the main plugin file from plugins directory.
  1144  * @param string $plugin Path to the plugin file relative to the plugins directory.
   972  * @return bool Whether plugin can be uninstalled.
  1145  * @return bool Whether plugin can be uninstalled.
   973  */
  1146  */
   974 function is_uninstallable_plugin($plugin) {
  1147 function is_uninstallable_plugin( $plugin ) {
   975 	$file = plugin_basename($plugin);
  1148 	$file = plugin_basename( $plugin );
   976 
  1149 
   977 	$uninstallable_plugins = (array) get_option('uninstall_plugins');
  1150 	$uninstallable_plugins = (array) get_option( 'uninstall_plugins' );
   978 	if ( isset( $uninstallable_plugins[$file] ) || file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) )
  1151 	if ( isset( $uninstallable_plugins[ $file ] ) || file_exists( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' ) ) {
   979 		return true;
  1152 		return true;
       
  1153 	}
   980 
  1154 
   981 	return false;
  1155 	return false;
   982 }
  1156 }
   983 
  1157 
   984 /**
  1158 /**
   986  *
  1160  *
   987  * Calls the uninstall hook, if it is available.
  1161  * Calls the uninstall hook, if it is available.
   988  *
  1162  *
   989  * @since 2.7.0
  1163  * @since 2.7.0
   990  *
  1164  *
   991  * @param string $plugin Path to the main plugin file from plugins directory.
  1165  * @param string $plugin Path to the plugin file relative to the plugins directory.
   992  * @return true True if a plugin's uninstall.php file has been found and included.
  1166  * @return true True if a plugin's uninstall.php file has been found and included.
   993  */
  1167  */
   994 function uninstall_plugin($plugin) {
  1168 function uninstall_plugin( $plugin ) {
   995 	$file = plugin_basename($plugin);
  1169 	$file = plugin_basename( $plugin );
   996 
  1170 
   997 	$uninstallable_plugins = (array) get_option('uninstall_plugins');
  1171 	$uninstallable_plugins = (array) get_option( 'uninstall_plugins' );
   998 
  1172 
   999 	/**
  1173 	/**
  1000 	 * Fires in uninstall_plugin() immediately before the plugin is uninstalled.
  1174 	 * Fires in uninstall_plugin() immediately before the plugin is uninstalled.
  1001 	 *
  1175 	 *
  1002 	 * @since 4.5.0
  1176 	 * @since 4.5.0
  1003 	 *
  1177 	 *
  1004 	 * @param string $plugin                Path to the main plugin file from plugins directory.
  1178 	 * @param string $plugin                Path to the plugin file relative to the plugins directory.
  1005 	 * @param array  $uninstallable_plugins Uninstallable plugins.
  1179 	 * @param array  $uninstallable_plugins Uninstallable plugins.
  1006 	 */
  1180 	 */
  1007 	do_action( 'pre_uninstall_plugin', $plugin, $uninstallable_plugins );
  1181 	do_action( 'pre_uninstall_plugin', $plugin, $uninstallable_plugins );
  1008 
  1182 
  1009 	if ( file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) ) {
  1183 	if ( file_exists( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' ) ) {
  1010 		if ( isset( $uninstallable_plugins[$file] ) ) {
  1184 		if ( isset( $uninstallable_plugins[ $file ] ) ) {
  1011 			unset($uninstallable_plugins[$file]);
  1185 			unset( $uninstallable_plugins[ $file ] );
  1012 			update_option('uninstall_plugins', $uninstallable_plugins);
  1186 			update_option( 'uninstall_plugins', $uninstallable_plugins );
  1013 		}
  1187 		}
  1014 		unset($uninstallable_plugins);
  1188 		unset( $uninstallable_plugins );
  1015 
  1189 
  1016 		define('WP_UNINSTALL_PLUGIN', $file);
  1190 		define( 'WP_UNINSTALL_PLUGIN', $file );
  1017 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1191 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1018 		include( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' );
  1192 		include( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' );
  1019 
  1193 
  1020 		return true;
  1194 		return true;
  1021 	}
  1195 	}
  1022 
  1196 
  1023 	if ( isset( $uninstallable_plugins[$file] ) ) {
  1197 	if ( isset( $uninstallable_plugins[ $file ] ) ) {
  1024 		$callable = $uninstallable_plugins[$file];
  1198 		$callable = $uninstallable_plugins[ $file ];
  1025 		unset($uninstallable_plugins[$file]);
  1199 		unset( $uninstallable_plugins[ $file ] );
  1026 		update_option('uninstall_plugins', $uninstallable_plugins);
  1200 		update_option( 'uninstall_plugins', $uninstallable_plugins );
  1027 		unset($uninstallable_plugins);
  1201 		unset( $uninstallable_plugins );
  1028 
  1202 
  1029 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1203 		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
  1030 		include( WP_PLUGIN_DIR . '/' . $file );
  1204 		include( WP_PLUGIN_DIR . '/' . $file );
  1031 
  1205 
  1032 		add_action( "uninstall_{$file}", $callable );
  1206 		add_action( "uninstall_{$file}", $callable );
  1053  * This function takes a capability which will be used to determine whether
  1227  * This function takes a capability which will be used to determine whether
  1054  * or not a page is included in the menu.
  1228  * or not a page is included in the menu.
  1055  *
  1229  *
  1056  * The function which is hooked in to handle the output of the page must check
  1230  * The function which is hooked in to handle the output of the page must check
  1057  * that the user has the required capability as well.
  1231  * that the user has the required capability as well.
       
  1232  *
       
  1233  * @since 1.5.0
  1058  *
  1234  *
  1059  * @global array $menu
  1235  * @global array $menu
  1060  * @global array $admin_page_hooks
  1236  * @global array $admin_page_hooks
  1061  * @global array $_registered_pages
  1237  * @global array $_registered_pages
  1062  * @global array $_parent_pages
  1238  * @global array $_parent_pages
  1080 function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
  1256 function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
  1081 	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;
  1257 	global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;
  1082 
  1258 
  1083 	$menu_slug = plugin_basename( $menu_slug );
  1259 	$menu_slug = plugin_basename( $menu_slug );
  1084 
  1260 
  1085 	$admin_page_hooks[$menu_slug] = sanitize_title( $menu_title );
  1261 	$admin_page_hooks[ $menu_slug ] = sanitize_title( $menu_title );
  1086 
  1262 
  1087 	$hookname = get_plugin_page_hookname( $menu_slug, '' );
  1263 	$hookname = get_plugin_page_hookname( $menu_slug, '' );
  1088 
  1264 
  1089 	if ( !empty( $function ) && !empty( $hookname ) && current_user_can( $capability ) )
  1265 	if ( ! empty( $function ) && ! empty( $hookname ) && current_user_can( $capability ) ) {
  1090 		add_action( $hookname, $function );
  1266 		add_action( $hookname, $function );
  1091 
  1267 	}
  1092 	if ( empty($icon_url) ) {
  1268 
  1093 		$icon_url = 'dashicons-admin-generic';
  1269 	if ( empty( $icon_url ) ) {
       
  1270 		$icon_url   = 'dashicons-admin-generic';
  1094 		$icon_class = 'menu-icon-generic ';
  1271 		$icon_class = 'menu-icon-generic ';
  1095 	} else {
  1272 	} else {
  1096 		$icon_url = set_url_scheme( $icon_url );
  1273 		$icon_url   = set_url_scheme( $icon_url );
  1097 		$icon_class = '';
  1274 		$icon_class = '';
  1098 	}
  1275 	}
  1099 
  1276 
  1100 	$new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url );
  1277 	$new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url );
  1101 
  1278 
  1102 	if ( null === $position ) {
  1279 	if ( null === $position ) {
  1103 		$menu[] = $new_menu;
  1280 		$menu[] = $new_menu;
  1104 	} elseif ( isset( $menu[ "$position" ] ) ) {
  1281 	} elseif ( isset( $menu[ "$position" ] ) ) {
  1105 	 	$position = $position + substr( base_convert( md5( $menu_slug . $menu_title ), 16, 10 ) , -5 ) * 0.00001;
  1282 		$position            = $position + substr( base_convert( md5( $menu_slug . $menu_title ), 16, 10 ), -5 ) * 0.00001;
  1106 		$menu[ "$position" ] = $new_menu;
  1283 		$menu[ "$position" ] = $new_menu;
  1107 	} else {
  1284 	} else {
  1108 		$menu[ $position ] = $new_menu;
  1285 		$menu[ $position ] = $new_menu;
  1109 	}
  1286 	}
  1110 
  1287 
  1111 	$_registered_pages[$hookname] = true;
  1288 	$_registered_pages[ $hookname ] = true;
  1112 
  1289 
  1113 	// No parent as top level
  1290 	// No parent as top level
  1114 	$_parent_pages[$menu_slug] = false;
  1291 	$_parent_pages[ $menu_slug ] = false;
  1115 
  1292 
  1116 	return $hookname;
  1293 	return $hookname;
  1117 }
  1294 }
  1118 
  1295 
  1119 /**
  1296 /**
  1122  * This function takes a capability which will be used to determine whether
  1299  * This function takes a capability which will be used to determine whether
  1123  * or not a page is included in the menu.
  1300  * or not a page is included in the menu.
  1124  *
  1301  *
  1125  * The function which is hooked in to handle the output of the page must check
  1302  * The function which is hooked in to handle the output of the page must check
  1126  * that the user has the required capability as well.
  1303  * that the user has the required capability as well.
       
  1304  *
       
  1305  * @since 1.5.0
  1127  *
  1306  *
  1128  * @global array $submenu
  1307  * @global array $submenu
  1129  * @global array $menu
  1308  * @global array $menu
  1130  * @global array $_wp_real_parent_file
  1309  * @global array $_wp_real_parent_file
  1131  * @global bool  $_wp_submenu_nopriv
  1310  * @global bool  $_wp_submenu_nopriv
  1146  */
  1325  */
  1147 function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1326 function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1148 	global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv,
  1327 	global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv,
  1149 		$_registered_pages, $_parent_pages;
  1328 		$_registered_pages, $_parent_pages;
  1150 
  1329 
  1151 	$menu_slug = plugin_basename( $menu_slug );
  1330 	$menu_slug   = plugin_basename( $menu_slug );
  1152 	$parent_slug = plugin_basename( $parent_slug);
  1331 	$parent_slug = plugin_basename( $parent_slug );
  1153 
  1332 
  1154 	if ( isset( $_wp_real_parent_file[$parent_slug] ) )
  1333 	if ( isset( $_wp_real_parent_file[ $parent_slug ] ) ) {
  1155 		$parent_slug = $_wp_real_parent_file[$parent_slug];
  1334 		$parent_slug = $_wp_real_parent_file[ $parent_slug ];
  1156 
  1335 	}
  1157 	if ( !current_user_can( $capability ) ) {
  1336 
  1158 		$_wp_submenu_nopriv[$parent_slug][$menu_slug] = true;
  1337 	if ( ! current_user_can( $capability ) ) {
       
  1338 		$_wp_submenu_nopriv[ $parent_slug ][ $menu_slug ] = true;
  1159 		return false;
  1339 		return false;
  1160 	}
  1340 	}
  1161 
  1341 
  1162 	/*
  1342 	/*
  1163 	 * If the parent doesn't already have a submenu, add a link to the parent
  1343 	 * If the parent doesn't already have a submenu, add a link to the parent
  1164 	 * as the first item in the submenu. If the submenu file is the same as the
  1344 	 * as the first item in the submenu. If the submenu file is the same as the
  1165 	 * parent file someone is trying to link back to the parent manually. In
  1345 	 * parent file someone is trying to link back to the parent manually. In
  1166 	 * this case, don't automatically add a link back to avoid duplication.
  1346 	 * this case, don't automatically add a link back to avoid duplication.
  1167 	 */
  1347 	 */
  1168 	if (!isset( $submenu[$parent_slug] ) && $menu_slug != $parent_slug ) {
  1348 	if ( ! isset( $submenu[ $parent_slug ] ) && $menu_slug != $parent_slug ) {
  1169 		foreach ( (array)$menu as $parent_menu ) {
  1349 		foreach ( (array) $menu as $parent_menu ) {
  1170 			if ( $parent_menu[2] == $parent_slug && current_user_can( $parent_menu[1] ) )
  1350 			if ( $parent_menu[2] == $parent_slug && current_user_can( $parent_menu[1] ) ) {
  1171 				$submenu[$parent_slug][] = array_slice( $parent_menu, 0, 4 );
  1351 				$submenu[ $parent_slug ][] = array_slice( $parent_menu, 0, 4 );
  1172 		}
  1352 			}
  1173 	}
  1353 		}
  1174 
  1354 	}
  1175 	$submenu[$parent_slug][] = array ( $menu_title, $capability, $menu_slug, $page_title );
  1355 
  1176 
  1356 	$submenu[ $parent_slug ][] = array( $menu_title, $capability, $menu_slug, $page_title );
  1177 	$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug);
  1357 
  1178 	if (!empty ( $function ) && !empty ( $hookname ))
  1358 	$hookname = get_plugin_page_hookname( $menu_slug, $parent_slug );
       
  1359 	if ( ! empty( $function ) && ! empty( $hookname ) ) {
  1179 		add_action( $hookname, $function );
  1360 		add_action( $hookname, $function );
  1180 
  1361 	}
  1181 	$_registered_pages[$hookname] = true;
  1362 
       
  1363 	$_registered_pages[ $hookname ] = true;
  1182 
  1364 
  1183 	/*
  1365 	/*
  1184 	 * Backward-compatibility for plugins using add_management page.
  1366 	 * Backward-compatibility for plugins using add_management_page().
  1185 	 * See wp-admin/admin.php for redirect from edit.php to tools.php
  1367 	 * See wp-admin/admin.php for redirect from edit.php to tools.php.
  1186 	 */
  1368 	 */
  1187 	if ( 'tools.php' == $parent_slug )
  1369 	if ( 'tools.php' == $parent_slug ) {
  1188 		$_registered_pages[get_plugin_page_hookname( $menu_slug, 'edit.php')] = true;
  1370 		$_registered_pages[ get_plugin_page_hookname( $menu_slug, 'edit.php' ) ] = true;
       
  1371 	}
  1189 
  1372 
  1190 	// No parent as top level.
  1373 	// No parent as top level.
  1191 	$_parent_pages[$menu_slug] = $parent_slug;
  1374 	$_parent_pages[ $menu_slug ] = $parent_slug;
  1192 
  1375 
  1193 	return $hookname;
  1376 	return $hookname;
  1194 }
  1377 }
  1195 
  1378 
  1196 /**
  1379 /**
  1199  * This function takes a capability which will be used to determine whether
  1382  * This function takes a capability which will be used to determine whether
  1200  * or not a page is included in the menu.
  1383  * or not a page is included in the menu.
  1201  *
  1384  *
  1202  * The function which is hooked in to handle the output of the page must check
  1385  * The function which is hooked in to handle the output of the page must check
  1203  * that the user has the required capability as well.
  1386  * that the user has the required capability as well.
       
  1387  *
       
  1388  * @since 1.5.0
  1204  *
  1389  *
  1205  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1390  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1206  * @param string   $menu_title The text to be used for the menu.
  1391  * @param string   $menu_title The text to be used for the menu.
  1207  * @param string   $capability The capability required for this menu to be displayed to the user.
  1392  * @param string   $capability The capability required for this menu to be displayed to the user.
  1208  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1393  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1220  * or not a page is included in the menu.
  1405  * or not a page is included in the menu.
  1221  *
  1406  *
  1222  * The function which is hooked in to handle the output of the page must check
  1407  * The function which is hooked in to handle the output of the page must check
  1223  * that the user has the required capability as well.
  1408  * that the user has the required capability as well.
  1224  *
  1409  *
       
  1410  * @since 1.5.0
       
  1411  *
  1225  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1412  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1226  * @param string   $menu_title The text to be used for the menu.
  1413  * @param string   $menu_title The text to be used for the menu.
  1227  * @param string   $capability The capability required for this menu to be displayed to the user.
  1414  * @param string   $capability The capability required for this menu to be displayed to the user.
  1228  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1415  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1229  * @param callable $function   The function to be called to output the content for this page.
  1416  * @param callable $function   The function to be called to output the content for this page.
  1240  * or not a page is included in the menu.
  1427  * or not a page is included in the menu.
  1241  *
  1428  *
  1242  * The function which is hooked in to handle the output of the page must check
  1429  * The function which is hooked in to handle the output of the page must check
  1243  * that the user has the required capability as well.
  1430  * that the user has the required capability as well.
  1244  *
  1431  *
       
  1432  * @since 2.0.0
       
  1433  *
  1245  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1434  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1246  * @param string   $menu_title The text to be used for the menu.
  1435  * @param string   $menu_title The text to be used for the menu.
  1247  * @param string   $capability The capability required for this menu to be displayed to the user.
  1436  * @param string   $capability The capability required for this menu to be displayed to the user.
  1248  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1437  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1249  * @param callable $function   The function to be called to output the content for this page.
  1438  * @param callable $function   The function to be called to output the content for this page.
  1260  * or not a page is included in the menu.
  1449  * or not a page is included in the menu.
  1261  *
  1450  *
  1262  * The function which is hooked in to handle the output of the page must check
  1451  * The function which is hooked in to handle the output of the page must check
  1263  * that the user has the required capability as well.
  1452  * that the user has the required capability as well.
  1264  *
  1453  *
       
  1454  * @since 3.0.0
       
  1455  *
  1265  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1456  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1266  * @param string   $menu_title The text to be used for the menu.
  1457  * @param string   $menu_title The text to be used for the menu.
  1267  * @param string   $capability The capability required for this menu to be displayed to the user.
  1458  * @param string   $capability The capability required for this menu to be displayed to the user.
  1268  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1459  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1269  * @param callable $function   The function to be called to output the content for this page.
  1460  * @param callable $function   The function to be called to output the content for this page.
  1280  * or not a page is included in the menu.
  1471  * or not a page is included in the menu.
  1281  *
  1472  *
  1282  * The function which is hooked in to handle the output of the page must check
  1473  * The function which is hooked in to handle the output of the page must check
  1283  * that the user has the required capability as well.
  1474  * that the user has the required capability as well.
  1284  *
  1475  *
       
  1476  * @since 2.1.3
       
  1477  *
  1285  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1478  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1286  * @param string   $menu_title The text to be used for the menu.
  1479  * @param string   $menu_title The text to be used for the menu.
  1287  * @param string   $capability The capability required for this menu to be displayed to the user.
  1480  * @param string   $capability The capability required for this menu to be displayed to the user.
  1288  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1481  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1289  * @param callable $function   The function to be called to output the content for this page.
  1482  * @param callable $function   The function to be called to output the content for this page.
  1290  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1483  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
  1291  */
  1484  */
  1292 function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1485 function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
  1293 	if ( current_user_can('edit_users') )
  1486 	if ( current_user_can( 'edit_users' ) ) {
  1294 		$parent = 'users.php';
  1487 		$parent = 'users.php';
  1295 	else
  1488 	} else {
  1296 		$parent = 'profile.php';
  1489 		$parent = 'profile.php';
       
  1490 	}
  1297 	return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function );
  1491 	return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function );
  1298 }
  1492 }
  1299 /**
  1493 /**
  1300  * Add submenu page to the Dashboard main menu.
  1494  * Add submenu page to the Dashboard main menu.
  1301  *
  1495  *
  1302  * This function takes a capability which will be used to determine whether
  1496  * This function takes a capability which will be used to determine whether
  1303  * or not a page is included in the menu.
  1497  * or not a page is included in the menu.
  1304  *
  1498  *
  1305  * The function which is hooked in to handle the output of the page must check
  1499  * The function which is hooked in to handle the output of the page must check
  1306  * that the user has the required capability as well.
  1500  * that the user has the required capability as well.
       
  1501  *
       
  1502  * @since 2.7.0
  1307  *
  1503  *
  1308  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1504  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1309  * @param string   $menu_title The text to be used for the menu.
  1505  * @param string   $menu_title The text to be used for the menu.
  1310  * @param string   $capability The capability required for this menu to be displayed to the user.
  1506  * @param string   $capability The capability required for this menu to be displayed to the user.
  1311  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1507  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1323  * or not a page is included in the menu.
  1519  * or not a page is included in the menu.
  1324  *
  1520  *
  1325  * The function which is hooked in to handle the output of the page must check
  1521  * The function which is hooked in to handle the output of the page must check
  1326  * that the user has the required capability as well.
  1522  * that the user has the required capability as well.
  1327  *
  1523  *
       
  1524  * @since 2.7.0
       
  1525  *
  1328  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1526  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1329  * @param string   $menu_title The text to be used for the menu.
  1527  * @param string   $menu_title The text to be used for the menu.
  1330  * @param string   $capability The capability required for this menu to be displayed to the user.
  1528  * @param string   $capability The capability required for this menu to be displayed to the user.
  1331  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1529  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1332  * @param callable $function   The function to be called to output the content for this page.
  1530  * @param callable $function   The function to be called to output the content for this page.
  1343  * or not a page is included in the menu.
  1541  * or not a page is included in the menu.
  1344  *
  1542  *
  1345  * The function which is hooked in to handle the output of the page must check
  1543  * The function which is hooked in to handle the output of the page must check
  1346  * that the user has the required capability as well.
  1544  * that the user has the required capability as well.
  1347  *
  1545  *
       
  1546  * @since 2.7.0
       
  1547  *
  1348  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1548  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1349  * @param string   $menu_title The text to be used for the menu.
  1549  * @param string   $menu_title The text to be used for the menu.
  1350  * @param string   $capability The capability required for this menu to be displayed to the user.
  1550  * @param string   $capability The capability required for this menu to be displayed to the user.
  1351  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1551  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1352  * @param callable $function   The function to be called to output the content for this page.
  1552  * @param callable $function   The function to be called to output the content for this page.
  1363  * or not a page is included in the menu.
  1563  * or not a page is included in the menu.
  1364  *
  1564  *
  1365  * 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
  1366  * that the user has the required capability as well.
  1566  * that the user has the required capability as well.
  1367  *
  1567  *
       
  1568  * @since 2.7.0
       
  1569  *
  1368  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1570  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1369  * @param string   $menu_title The text to be used for the menu.
  1571  * @param string   $menu_title The text to be used for the menu.
  1370  * @param string   $capability The capability required for this menu to be displayed to the user.
  1572  * @param string   $capability The capability required for this menu to be displayed to the user.
  1371  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1573  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1372  * @param callable $function   The function to be called to output the content for this page.
  1574  * @param callable $function   The function to be called to output the content for this page.
  1383  * or not a page is included in the menu.
  1585  * or not a page is included in the menu.
  1384  *
  1586  *
  1385  * The function which is hooked in to handle the output of the page must check
  1587  * 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.
  1588  * that the user has the required capability as well.
  1387  *
  1589  *
       
  1590  * @since 2.7.0
       
  1591  *
  1388  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1592  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1389  * @param string   $menu_title The text to be used for the menu.
  1593  * @param string   $menu_title The text to be used for the menu.
  1390  * @param string   $capability The capability required for this menu to be displayed to the user.
  1594  * @param string   $capability The capability required for this menu to be displayed to the user.
  1391  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1595  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1392  * @param callable $function   The function to be called to output the content for this page.
  1596  * @param callable $function   The function to be called to output the content for this page.
  1403  * or not a page is included in the menu.
  1607  * or not a page is included in the menu.
  1404  *
  1608  *
  1405  * The function which is hooked in to handle the output of the page must check
  1609  * The function which is hooked in to handle the output of the page must check
  1406  * that the user has the required capability as well.
  1610  * that the user has the required capability as well.
  1407  *
  1611  *
       
  1612  * @since 2.7.0
       
  1613  *
  1408  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1614  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
  1409  * @param string   $menu_title The text to be used for the menu.
  1615  * @param string   $menu_title The text to be used for the menu.
  1410  * @param string   $capability The capability required for this menu to be displayed to the user.
  1616  * @param string   $capability The capability required for this menu to be displayed to the user.
  1411  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1617  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
  1412  * @param callable $function   The function to be called to output the content for this page.
  1618  * @param callable $function   The function to be called to output the content for this page.
  1429 function remove_menu_page( $menu_slug ) {
  1635 function remove_menu_page( $menu_slug ) {
  1430 	global $menu;
  1636 	global $menu;
  1431 
  1637 
  1432 	foreach ( $menu as $i => $item ) {
  1638 	foreach ( $menu as $i => $item ) {
  1433 		if ( $menu_slug == $item[2] ) {
  1639 		if ( $menu_slug == $item[2] ) {
  1434 			unset( $menu[$i] );
  1640 			unset( $menu[ $i ] );
  1435 			return $item;
  1641 			return $item;
  1436 		}
  1642 		}
  1437 	}
  1643 	}
  1438 
  1644 
  1439 	return false;
  1645 	return false;
  1451  * @return array|bool The removed submenu on success, false if not found.
  1657  * @return array|bool The removed submenu on success, false if not found.
  1452  */
  1658  */
  1453 function remove_submenu_page( $menu_slug, $submenu_slug ) {
  1659 function remove_submenu_page( $menu_slug, $submenu_slug ) {
  1454 	global $submenu;
  1660 	global $submenu;
  1455 
  1661 
  1456 	if ( !isset( $submenu[$menu_slug] ) )
  1662 	if ( ! isset( $submenu[ $menu_slug ] ) ) {
  1457 		return false;
  1663 		return false;
  1458 
  1664 	}
  1459 	foreach ( $submenu[$menu_slug] as $i => $item ) {
  1665 
       
  1666 	foreach ( $submenu[ $menu_slug ] as $i => $item ) {
  1460 		if ( $submenu_slug == $item[2] ) {
  1667 		if ( $submenu_slug == $item[2] ) {
  1461 			unset( $submenu[$menu_slug][$i] );
  1668 			unset( $submenu[ $menu_slug ][ $i ] );
  1462 			return $item;
  1669 			return $item;
  1463 		}
  1670 		}
  1464 	}
  1671 	}
  1465 
  1672 
  1466 	return false;
  1673 	return false;
  1477  *
  1684  *
  1478  * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu)
  1685  * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu)
  1479  * @param bool $echo Whether or not to echo the url - default is true
  1686  * @param bool $echo Whether or not to echo the url - default is true
  1480  * @return string the url
  1687  * @return string the url
  1481  */
  1688  */
  1482 function menu_page_url($menu_slug, $echo = true) {
  1689 function menu_page_url( $menu_slug, $echo = true ) {
  1483 	global $_parent_pages;
  1690 	global $_parent_pages;
  1484 
  1691 
  1485 	if ( isset( $_parent_pages[$menu_slug] ) ) {
  1692 	if ( isset( $_parent_pages[ $menu_slug ] ) ) {
  1486 		$parent_slug = $_parent_pages[$menu_slug];
  1693 		$parent_slug = $_parent_pages[ $menu_slug ];
  1487 		if ( $parent_slug && ! isset( $_parent_pages[$parent_slug] ) ) {
  1694 		if ( $parent_slug && ! isset( $_parent_pages[ $parent_slug ] ) ) {
  1488 			$url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) );
  1695 			$url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) );
  1489 		} else {
  1696 		} else {
  1490 			$url = admin_url( 'admin.php?page=' . $menu_slug );
  1697 			$url = admin_url( 'admin.php?page=' . $menu_slug );
  1491 		}
  1698 		}
  1492 	} else {
  1699 	} else {
  1493 		$url = '';
  1700 		$url = '';
  1494 	}
  1701 	}
  1495 
  1702 
  1496 	$url = esc_url($url);
  1703 	$url = esc_url( $url );
  1497 
  1704 
  1498 	if ( $echo )
  1705 	if ( $echo ) {
  1499 		echo $url;
  1706 		echo $url;
       
  1707 	}
  1500 
  1708 
  1501 	return $url;
  1709 	return $url;
  1502 }
  1710 }
  1503 
  1711 
  1504 //
  1712 //
  1505 // Pluggable Menu Support -- Private
  1713 // Pluggable Menu Support -- Private
  1506 //
  1714 //
  1507 /**
  1715 /**
  1508  *
       
  1509  * @global string $parent_file
  1716  * @global string $parent_file
  1510  * @global array $menu
  1717  * @global array $menu
  1511  * @global array $submenu
  1718  * @global array $submenu
  1512  * @global string $pagenow
  1719  * @global string $pagenow
  1513  * @global string $typenow
  1720  * @global string $typenow
  1514  * @global string $plugin_page
  1721  * @global string $plugin_page
  1515  * @global array $_wp_real_parent_file
  1722  * @global array $_wp_real_parent_file
  1516  * @global array $_wp_menu_nopriv
  1723  * @global array $_wp_menu_nopriv
  1517  * @global array $_wp_submenu_nopriv
  1724  * @global array $_wp_submenu_nopriv
       
  1725  *
       
  1726  * @return string
  1518  */
  1727  */
  1519 function get_admin_page_parent( $parent = '' ) {
  1728 function get_admin_page_parent( $parent = '' ) {
  1520 	global $parent_file, $menu, $submenu, $pagenow, $typenow,
  1729 	global $parent_file, $menu, $submenu, $pagenow, $typenow,
  1521 		$plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv;
  1730 		$plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv;
  1522 
  1731 
  1523 	if ( !empty ( $parent ) && 'admin.php' != $parent ) {
  1732 	if ( ! empty( $parent ) && 'admin.php' != $parent ) {
  1524 		if ( isset( $_wp_real_parent_file[$parent] ) )
  1733 		if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1525 			$parent = $_wp_real_parent_file[$parent];
  1734 			$parent = $_wp_real_parent_file[ $parent ];
       
  1735 		}
  1526 		return $parent;
  1736 		return $parent;
  1527 	}
  1737 	}
  1528 
  1738 
  1529 	if ( $pagenow == 'admin.php' && isset( $plugin_page ) ) {
  1739 	if ( $pagenow == 'admin.php' && isset( $plugin_page ) ) {
  1530 		foreach ( (array)$menu as $parent_menu ) {
  1740 		foreach ( (array) $menu as $parent_menu ) {
  1531 			if ( $parent_menu[2] == $plugin_page ) {
  1741 			if ( $parent_menu[2] == $plugin_page ) {
  1532 				$parent_file = $plugin_page;
  1742 				$parent_file = $plugin_page;
  1533 				if ( isset( $_wp_real_parent_file[$parent_file] ) )
  1743 				if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
  1534 					$parent_file = $_wp_real_parent_file[$parent_file];
  1744 					$parent_file = $_wp_real_parent_file[ $parent_file ];
       
  1745 				}
  1535 				return $parent_file;
  1746 				return $parent_file;
  1536 			}
  1747 			}
  1537 		}
  1748 		}
  1538 		if ( isset( $_wp_menu_nopriv[$plugin_page] ) ) {
  1749 		if ( isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
  1539 			$parent_file = $plugin_page;
  1750 			$parent_file = $plugin_page;
  1540 			if ( isset( $_wp_real_parent_file[$parent_file] ) )
  1751 			if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
  1541 					$parent_file = $_wp_real_parent_file[$parent_file];
  1752 					$parent_file = $_wp_real_parent_file[ $parent_file ];
       
  1753 			}
  1542 			return $parent_file;
  1754 			return $parent_file;
  1543 		}
  1755 		}
  1544 	}
  1756 	}
  1545 
  1757 
  1546 	if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) ) {
  1758 	if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $pagenow ][ $plugin_page ] ) ) {
  1547 		$parent_file = $pagenow;
  1759 		$parent_file = $pagenow;
  1548 		if ( isset( $_wp_real_parent_file[$parent_file] ) )
  1760 		if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
  1549 			$parent_file = $_wp_real_parent_file[$parent_file];
  1761 			$parent_file = $_wp_real_parent_file[ $parent_file ];
       
  1762 		}
  1550 		return $parent_file;
  1763 		return $parent_file;
  1551 	}
  1764 	}
  1552 
  1765 
  1553 	foreach (array_keys( (array)$submenu ) as $parent) {
  1766 	foreach ( array_keys( (array) $submenu ) as $parent ) {
  1554 		foreach ( $submenu[$parent] as $submenu_array ) {
  1767 		foreach ( $submenu[ $parent ] as $submenu_array ) {
  1555 			if ( isset( $_wp_real_parent_file[$parent] ) )
  1768 			if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
  1556 				$parent = $_wp_real_parent_file[$parent];
  1769 				$parent = $_wp_real_parent_file[ $parent ];
  1557 			if ( !empty($typenow) && ($submenu_array[2] == "$pagenow?post_type=$typenow") ) {
  1770 			}
       
  1771 			if ( ! empty( $typenow ) && ( $submenu_array[2] == "$pagenow?post_type=$typenow" ) ) {
  1558 				$parent_file = $parent;
  1772 				$parent_file = $parent;
  1559 				return $parent;
  1773 				return $parent;
  1560 			} elseif ( $submenu_array[2] == $pagenow && empty($typenow) && ( empty($parent_file) || false === strpos($parent_file, '?') ) ) {
  1774 			} elseif ( $submenu_array[2] == $pagenow && empty( $typenow ) && ( empty( $parent_file ) || false === strpos( $parent_file, '?' ) ) ) {
  1561 				$parent_file = $parent;
  1775 				$parent_file = $parent;
  1562 				return $parent;
  1776 				return $parent;
  1563 			} elseif ( isset( $plugin_page ) && ($plugin_page == $submenu_array[2] ) ) {
  1777 			} elseif ( isset( $plugin_page ) && ( $plugin_page == $submenu_array[2] ) ) {
  1564 				$parent_file = $parent;
  1778 				$parent_file = $parent;
  1565 				return $parent;
  1779 				return $parent;
  1566 			}
  1780 			}
  1567 		}
  1781 		}
  1568 	}
  1782 	}
  1569 
  1783 
  1570 	if ( empty($parent_file) )
  1784 	if ( empty( $parent_file ) ) {
  1571 		$parent_file = '';
  1785 		$parent_file = '';
       
  1786 	}
  1572 	return '';
  1787 	return '';
  1573 }
  1788 }
  1574 
  1789 
  1575 /**
  1790 /**
  1576  *
       
  1577  * @global string $title
  1791  * @global string $title
  1578  * @global array $menu
  1792  * @global array $menu
  1579  * @global array $submenu
  1793  * @global array $submenu
  1580  * @global string $pagenow
  1794  * @global string $pagenow
  1581  * @global string $plugin_page
  1795  * @global string $plugin_page
  1582  * @global string $typenow
  1796  * @global string $typenow
       
  1797  *
       
  1798  * @return string
  1583  */
  1799  */
  1584 function get_admin_page_title() {
  1800 function get_admin_page_title() {
  1585 	global $title, $menu, $submenu, $pagenow, $plugin_page, $typenow;
  1801 	global $title, $menu, $submenu, $pagenow, $plugin_page, $typenow;
  1586 
  1802 
  1587 	if ( ! empty ( $title ) )
  1803 	if ( ! empty( $title ) ) {
  1588 		return $title;
  1804 		return $title;
       
  1805 	}
  1589 
  1806 
  1590 	$hook = get_plugin_page_hook( $plugin_page, $pagenow );
  1807 	$hook = get_plugin_page_hook( $plugin_page, $pagenow );
  1591 
  1808 
  1592 	$parent = $parent1 = get_admin_page_parent();
  1809 	$parent = $parent1 = get_admin_page_parent();
  1593 
  1810 
  1594 	if ( empty ( $parent) ) {
  1811 	if ( empty( $parent ) ) {
  1595 		foreach ( (array)$menu as $menu_array ) {
  1812 		foreach ( (array) $menu as $menu_array ) {
  1596 			if ( isset( $menu_array[3] ) ) {
  1813 			if ( isset( $menu_array[3] ) ) {
  1597 				if ( $menu_array[2] == $pagenow ) {
  1814 				if ( $menu_array[2] == $pagenow ) {
  1598 					$title = $menu_array[3];
  1815 					$title = $menu_array[3];
  1599 					return $menu_array[3];
  1816 					return $menu_array[3];
  1600 				} elseif ( isset( $plugin_page ) && ($plugin_page == $menu_array[2] ) && ($hook == $menu_array[3] ) ) {
  1817 				} elseif ( isset( $plugin_page ) && ( $plugin_page == $menu_array[2] ) && ( $hook == $menu_array[3] ) ) {
  1601 					$title = $menu_array[3];
  1818 					$title = $menu_array[3];
  1602 					return $menu_array[3];
  1819 					return $menu_array[3];
  1603 				}
  1820 				}
  1604 			} else {
  1821 			} else {
  1605 				$title = $menu_array[0];
  1822 				$title = $menu_array[0];
  1606 				return $title;
  1823 				return $title;
  1607 			}
  1824 			}
  1608 		}
  1825 		}
  1609 	} else {
  1826 	} else {
  1610 		foreach ( array_keys( $submenu ) as $parent ) {
  1827 		foreach ( array_keys( $submenu ) as $parent ) {
  1611 			foreach ( $submenu[$parent] as $submenu_array ) {
  1828 			foreach ( $submenu[ $parent ] as $submenu_array ) {
  1612 				if ( isset( $plugin_page ) &&
  1829 				if ( isset( $plugin_page ) &&
  1613 					( $plugin_page == $submenu_array[2] ) &&
  1830 					( $plugin_page == $submenu_array[2] ) &&
  1614 					(
  1831 					(
  1615 						( $parent == $pagenow ) ||
  1832 						( $parent == $pagenow ) ||
  1616 						( $parent == $plugin_page ) ||
  1833 						( $parent == $plugin_page ) ||
  1617 						( $plugin_page == $hook ) ||
  1834 						( $plugin_page == $hook ) ||
  1618 						( $pagenow == 'admin.php' && $parent1 != $submenu_array[2] ) ||
  1835 						( $pagenow == 'admin.php' && $parent1 != $submenu_array[2] ) ||
  1619 						( !empty($typenow) && $parent == $pagenow . '?post_type=' . $typenow)
  1836 						( ! empty( $typenow ) && $parent == $pagenow . '?post_type=' . $typenow )
  1620 					)
  1837 					)
  1621 					) {
  1838 					) {
  1622 						$title = $submenu_array[3];
  1839 						$title = $submenu_array[3];
  1623 						return $submenu_array[3];
  1840 						return $submenu_array[3];
  1624 					}
  1841 				}
  1625 
  1842 
  1626 				if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) // not the current page
  1843 				if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) { // not the current page
  1627 					continue;
  1844 					continue;
       
  1845 				}
  1628 
  1846 
  1629 				if ( isset( $submenu_array[3] ) ) {
  1847 				if ( isset( $submenu_array[3] ) ) {
  1630 					$title = $submenu_array[3];
  1848 					$title = $submenu_array[3];
  1631 					return $submenu_array[3];
  1849 					return $submenu_array[3];
  1632 				} else {
  1850 				} else {
  1633 					$title = $submenu_array[0];
  1851 					$title = $submenu_array[0];
  1634 					return $title;
  1852 					return $title;
  1635 				}
  1853 				}
  1636 			}
  1854 			}
  1637 		}
  1855 		}
  1638 		if ( empty ( $title ) ) {
  1856 		if ( empty( $title ) ) {
  1639 			foreach ( $menu as $menu_array ) {
  1857 			foreach ( $menu as $menu_array ) {
  1640 				if ( isset( $plugin_page ) &&
  1858 				if ( isset( $plugin_page ) &&
  1641 					( $plugin_page == $menu_array[2] ) &&
  1859 					( $plugin_page == $menu_array[2] ) &&
  1642 					( $pagenow == 'admin.php' ) &&
  1860 					( $pagenow == 'admin.php' ) &&
  1643 					( $parent1 == $menu_array[2] ) )
  1861 					( $parent1 == $menu_array[2] ) ) {
  1644 					{
       
  1645 						$title = $menu_array[3];
  1862 						$title = $menu_array[3];
  1646 						return $menu_array[3];
  1863 						return $menu_array[3];
  1647 					}
  1864 				}
  1648 			}
  1865 			}
  1649 		}
  1866 		}
  1650 	}
  1867 	}
  1651 
  1868 
  1652 	return $title;
  1869 	return $title;
  1653 }
  1870 }
  1654 
  1871 
  1655 /**
  1872 /**
  1656  * @since 2.3.0
  1873  * @since 2.3.0
  1657  *
  1874  *
  1658  * @param string $plugin_page
  1875  * @param string $plugin_page The slug name of the plugin page.
  1659  * @param string $parent_page
  1876  * @param string $parent_page The slug name for the parent menu (or the file name of a standard
  1660  * @return string|null
  1877  *                            WordPress admin page).
       
  1878  * @return string|null Hook attached to the plugin page, null otherwise.
  1661  */
  1879  */
  1662 function get_plugin_page_hook( $plugin_page, $parent_page ) {
  1880 function get_plugin_page_hook( $plugin_page, $parent_page ) {
  1663 	$hook = get_plugin_page_hookname( $plugin_page, $parent_page );
  1881 	$hook = get_plugin_page_hookname( $plugin_page, $parent_page );
  1664 	if ( has_action($hook) )
  1882 	if ( has_action( $hook ) ) {
  1665 		return $hook;
  1883 		return $hook;
  1666 	else
  1884 	} else {
  1667 		return null;
  1885 		return null;
  1668 }
  1886 	}
  1669 
  1887 }
  1670 /**
  1888 
  1671  *
  1889 /**
  1672  * @global array $admin_page_hooks
  1890  * @global array $admin_page_hooks
  1673  * @param string $plugin_page
  1891  *
  1674  * @param string $parent_page
  1892  * @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
       
  1894  *                            WordPress admin page).
       
  1895  * @return string Hook name for the plugin page.
  1675  */
  1896  */
  1676 function get_plugin_page_hookname( $plugin_page, $parent_page ) {
  1897 function get_plugin_page_hookname( $plugin_page, $parent_page ) {
  1677 	global $admin_page_hooks;
  1898 	global $admin_page_hooks;
  1678 
  1899 
  1679 	$parent = get_admin_page_parent( $parent_page );
  1900 	$parent = get_admin_page_parent( $parent_page );
  1680 
  1901 
  1681 	$page_type = 'admin';
  1902 	$page_type = 'admin';
  1682 	if ( empty ( $parent_page ) || 'admin.php' == $parent_page || isset( $admin_page_hooks[$plugin_page] ) ) {
  1903 	if ( empty( $parent_page ) || 'admin.php' == $parent_page || isset( $admin_page_hooks[ $plugin_page ] ) ) {
  1683 		if ( isset( $admin_page_hooks[$plugin_page] ) ) {
  1904 		if ( isset( $admin_page_hooks[ $plugin_page ] ) ) {
  1684 			$page_type = 'toplevel';
  1905 			$page_type = 'toplevel';
  1685 		} elseif ( isset( $admin_page_hooks[$parent] )) {
  1906 		} elseif ( isset( $admin_page_hooks[ $parent ] ) ) {
  1686 			$page_type = $admin_page_hooks[$parent];
  1907 			$page_type = $admin_page_hooks[ $parent ];
  1687 		}
  1908 		}
  1688 	} elseif ( isset( $admin_page_hooks[$parent] ) ) {
  1909 	} elseif ( isset( $admin_page_hooks[ $parent ] ) ) {
  1689 		$page_type = $admin_page_hooks[$parent];
  1910 		$page_type = $admin_page_hooks[ $parent ];
  1690 	}
  1911 	}
  1691 
  1912 
  1692 	$plugin_name = preg_replace( '!\.php!', '', $plugin_page );
  1913 	$plugin_name = preg_replace( '!\.php!', '', $plugin_page );
  1693 
  1914 
  1694 	return $page_type . '_page_' . $plugin_name;
  1915 	return $page_type . '_page_' . $plugin_name;
  1695 }
  1916 }
  1696 
  1917 
  1697 /**
  1918 /**
  1698  *
       
  1699  * @global string $pagenow
  1919  * @global string $pagenow
  1700  * @global array $menu
  1920  * @global array $menu
  1701  * @global array $submenu
  1921  * @global array $submenu
  1702  * @global array $_wp_menu_nopriv
  1922  * @global array $_wp_menu_nopriv
  1703  * @global array $_wp_submenu_nopriv
  1923  * @global array $_wp_submenu_nopriv
  1704  * @global string $plugin_page
  1924  * @global string $plugin_page
  1705  * @global array $_registered_pages
  1925  * @global array $_registered_pages
       
  1926  *
       
  1927  * @return bool Whether the current user can access the current admin page.
  1706  */
  1928  */
  1707 function user_can_access_admin_page() {
  1929 function user_can_access_admin_page() {
  1708 	global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv,
  1930 	global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv,
  1709 		$plugin_page, $_registered_pages;
  1931 		$plugin_page, $_registered_pages;
  1710 
  1932 
  1711 	$parent = get_admin_page_parent();
  1933 	$parent = get_admin_page_parent();
  1712 
  1934 
  1713 	if ( !isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$parent][$pagenow] ) )
  1935 	if ( ! isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $parent ][ $pagenow ] ) ) {
  1714 		return false;
  1936 		return false;
       
  1937 	}
  1715 
  1938 
  1716 	if ( isset( $plugin_page ) ) {
  1939 	if ( isset( $plugin_page ) ) {
  1717 		if ( isset( $_wp_submenu_nopriv[$parent][$plugin_page] ) )
  1940 		if ( isset( $_wp_submenu_nopriv[ $parent ][ $plugin_page ] ) ) {
  1718 			return false;
  1941 			return false;
  1719 
  1942 		}
  1720 		$hookname = get_plugin_page_hookname($plugin_page, $parent);
  1943 
  1721 
  1944 		$hookname = get_plugin_page_hookname( $plugin_page, $parent );
  1722 		if ( !isset($_registered_pages[$hookname]) )
  1945 
       
  1946 		if ( ! isset( $_registered_pages[ $hookname ] ) ) {
  1723 			return false;
  1947 			return false;
  1724 	}
  1948 		}
  1725 
  1949 	}
  1726 	if ( empty( $parent) ) {
  1950 
  1727 		if ( isset( $_wp_menu_nopriv[$pagenow] ) )
  1951 	if ( empty( $parent ) ) {
       
  1952 		if ( isset( $_wp_menu_nopriv[ $pagenow ] ) ) {
  1728 			return false;
  1953 			return false;
  1729 		if ( isset( $_wp_submenu_nopriv[$pagenow][$pagenow] ) )
  1954 		}
       
  1955 		if ( isset( $_wp_submenu_nopriv[ $pagenow ][ $pagenow ] ) ) {
  1730 			return false;
  1956 			return false;
  1731 		if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) )
  1957 		}
       
  1958 		if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $pagenow ][ $plugin_page ] ) ) {
  1732 			return false;
  1959 			return false;
  1733 		if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[$plugin_page] ) )
  1960 		}
       
  1961 		if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
  1734 			return false;
  1962 			return false;
  1735 		foreach (array_keys( $_wp_submenu_nopriv ) as $key ) {
  1963 		}
  1736 			if ( isset( $_wp_submenu_nopriv[$key][$pagenow] ) )
  1964 		foreach ( array_keys( $_wp_submenu_nopriv ) as $key ) {
       
  1965 			if ( isset( $_wp_submenu_nopriv[ $key ][ $pagenow ] ) ) {
  1737 				return false;
  1966 				return false;
  1738 			if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$key][$plugin_page] ) )
  1967 			}
  1739 			return false;
  1968 			if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $key ][ $plugin_page ] ) ) {
       
  1969 				return false;
       
  1970 			}
  1740 		}
  1971 		}
  1741 		return true;
  1972 		return true;
  1742 	}
  1973 	}
  1743 
  1974 
  1744 	if ( isset( $plugin_page ) && ( $plugin_page == $parent ) && isset( $_wp_menu_nopriv[$plugin_page] ) )
  1975 	if ( isset( $plugin_page ) && ( $plugin_page == $parent ) && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
  1745 		return false;
  1976 		return false;
  1746 
  1977 	}
  1747 	if ( isset( $submenu[$parent] ) ) {
  1978 
  1748 		foreach ( $submenu[$parent] as $submenu_array ) {
  1979 	if ( isset( $submenu[ $parent ] ) ) {
       
  1980 		foreach ( $submenu[ $parent ] as $submenu_array ) {
  1749 			if ( isset( $plugin_page ) && ( $submenu_array[2] == $plugin_page ) ) {
  1981 			if ( isset( $plugin_page ) && ( $submenu_array[2] == $plugin_page ) ) {
  1750 				if ( current_user_can( $submenu_array[1] ))
  1982 				if ( current_user_can( $submenu_array[1] ) ) {
  1751 					return true;
  1983 					return true;
  1752 				else
  1984 				} else {
  1753 					return false;
  1985 					return false;
       
  1986 				}
  1754 			} elseif ( $submenu_array[2] == $pagenow ) {
  1987 			} elseif ( $submenu_array[2] == $pagenow ) {
  1755 				if ( current_user_can( $submenu_array[1] ))
  1988 				if ( current_user_can( $submenu_array[1] ) ) {
  1756 					return true;
  1989 					return true;
  1757 				else
  1990 				} else {
  1758 					return false;
  1991 					return false;
       
  1992 				}
  1759 			}
  1993 			}
  1760 		}
  1994 		}
  1761 	}
  1995 	}
  1762 
  1996 
  1763 	foreach ( $menu as $menu_array ) {
  1997 	foreach ( $menu as $menu_array ) {
  1764 		if ( $menu_array[2] == $parent) {
  1998 		if ( $menu_array[2] == $parent ) {
  1765 			if ( current_user_can( $menu_array[1] ))
  1999 			if ( current_user_can( $menu_array[1] ) ) {
  1766 				return true;
  2000 				return true;
  1767 			else
  2001 			} else {
  1768 				return false;
  2002 				return false;
       
  2003 			}
  1769 		}
  2004 		}
  1770 	}
  2005 	}
  1771 
  2006 
  1772 	return true;
  2007 	return true;
  1773 }
  2008 }
  1787  * @return array
  2022  * @return array
  1788  */
  2023  */
  1789 function option_update_filter( $options ) {
  2024 function option_update_filter( $options ) {
  1790 	global $new_whitelist_options;
  2025 	global $new_whitelist_options;
  1791 
  2026 
  1792 	if ( is_array( $new_whitelist_options ) )
  2027 	if ( is_array( $new_whitelist_options ) ) {
  1793 		$options = add_option_whitelist( $new_whitelist_options, $options );
  2028 		$options = add_option_whitelist( $new_whitelist_options, $options );
       
  2029 	}
  1794 
  2030 
  1795 	return $options;
  2031 	return $options;
  1796 }
  2032 }
  1797 
  2033 
  1798 /**
  2034 /**
  1805  * @param array        $new_options
  2041  * @param array        $new_options
  1806  * @param string|array $options
  2042  * @param string|array $options
  1807  * @return array
  2043  * @return array
  1808  */
  2044  */
  1809 function add_option_whitelist( $new_options, $options = '' ) {
  2045 function add_option_whitelist( $new_options, $options = '' ) {
  1810 	if ( $options == '' )
  2046 	if ( $options == '' ) {
  1811 		global $whitelist_options;
  2047 		global $whitelist_options;
  1812 	else
  2048 	} else {
  1813 		$whitelist_options = $options;
  2049 		$whitelist_options = $options;
       
  2050 	}
  1814 
  2051 
  1815 	foreach ( $new_options as $page => $keys ) {
  2052 	foreach ( $new_options as $page => $keys ) {
  1816 		foreach ( $keys as $key ) {
  2053 		foreach ( $keys as $key ) {
  1817 			if ( !isset($whitelist_options[ $page ]) || !is_array($whitelist_options[ $page ]) ) {
  2054 			if ( ! isset( $whitelist_options[ $page ] ) || ! is_array( $whitelist_options[ $page ] ) ) {
  1818 				$whitelist_options[ $page ] = array();
  2055 				$whitelist_options[ $page ]   = array();
  1819 				$whitelist_options[ $page ][] = $key;
  2056 				$whitelist_options[ $page ][] = $key;
  1820 			} else {
  2057 			} else {
  1821 				$pos = array_search( $key, $whitelist_options[ $page ] );
  2058 				$pos = array_search( $key, $whitelist_options[ $page ] );
  1822 				if ( $pos === false )
  2059 				if ( $pos === false ) {
  1823 					$whitelist_options[ $page ][] = $key;
  2060 					$whitelist_options[ $page ][] = $key;
       
  2061 				}
  1824 			}
  2062 			}
  1825 		}
  2063 		}
  1826 	}
  2064 	}
  1827 
  2065 
  1828 	return $whitelist_options;
  2066 	return $whitelist_options;
  1838  * @param array        $del_options
  2076  * @param array        $del_options
  1839  * @param string|array $options
  2077  * @param string|array $options
  1840  * @return array
  2078  * @return array
  1841  */
  2079  */
  1842 function remove_option_whitelist( $del_options, $options = '' ) {
  2080 function remove_option_whitelist( $del_options, $options = '' ) {
  1843 	if ( $options == '' )
  2081 	if ( $options == '' ) {
  1844 		global $whitelist_options;
  2082 		global $whitelist_options;
  1845 	else
  2083 	} else {
  1846 		$whitelist_options = $options;
  2084 		$whitelist_options = $options;
       
  2085 	}
  1847 
  2086 
  1848 	foreach ( $del_options as $page => $keys ) {
  2087 	foreach ( $del_options as $page => $keys ) {
  1849 		foreach ( $keys as $key ) {
  2088 		foreach ( $keys as $key ) {
  1850 			if ( isset($whitelist_options[ $page ]) && is_array($whitelist_options[ $page ]) ) {
  2089 			if ( isset( $whitelist_options[ $page ] ) && is_array( $whitelist_options[ $page ] ) ) {
  1851 				$pos = array_search( $key, $whitelist_options[ $page ] );
  2090 				$pos = array_search( $key, $whitelist_options[ $page ] );
  1852 				if ( $pos !== false )
  2091 				if ( $pos !== false ) {
  1853 					unset( $whitelist_options[ $page ][ $pos ] );
  2092 					unset( $whitelist_options[ $page ][ $pos ] );
       
  2093 				}
  1854 			}
  2094 			}
  1855 		}
  2095 		}
  1856 	}
  2096 	}
  1857 
  2097 
  1858 	return $whitelist_options;
  2098 	return $whitelist_options;
  1863  *
  2103  *
  1864  * @since 2.7.0
  2104  * @since 2.7.0
  1865  *
  2105  *
  1866  * @param string $option_group A settings group name. This should match the group name used in register_setting().
  2106  * @param string $option_group A settings group name. This should match the group name used in register_setting().
  1867  */
  2107  */
  1868 function settings_fields($option_group) {
  2108 function settings_fields( $option_group ) {
  1869 	echo "<input type='hidden' name='option_page' value='" . esc_attr($option_group) . "' />";
  2109 	echo "<input type='hidden' name='option_page' value='" . esc_attr( $option_group ) . "' />";
  1870 	echo '<input type="hidden" name="action" value="update" />';
  2110 	echo '<input type="hidden" name="action" value="update" />';
  1871 	wp_nonce_field("$option_group-options");
  2111 	wp_nonce_field( "$option_group-options" );
  1872 }
  2112 }
  1873 
  2113 
  1874 /**
  2114 /**
  1875  * Clears the Plugins cache used by get_plugins() and by default, the Plugin Update cache.
  2115  * Clears the Plugins cache used by get_plugins() and by default, the Plugin Update cache.
  1876  *
  2116  *
  1877  * @since 3.7.0
  2117  * @since 3.7.0
  1878  *
  2118  *
  1879  * @param bool $clear_update_cache Whether to clear the Plugin updates cache
  2119  * @param bool $clear_update_cache Whether to clear the Plugin updates cache
  1880  */
  2120  */
  1881 function wp_clean_plugins_cache( $clear_update_cache = true ) {
  2121 function wp_clean_plugins_cache( $clear_update_cache = true ) {
  1882 	if ( $clear_update_cache )
  2122 	if ( $clear_update_cache ) {
  1883 		delete_site_transient( 'update_plugins' );
  2123 		delete_site_transient( 'update_plugins' );
       
  2124 	}
  1884 	wp_cache_delete( 'plugins', 'plugins' );
  2125 	wp_cache_delete( 'plugins', 'plugins' );
  1885 }
  2126 }
  1886 
  2127 
  1887 /**
  2128 /**
  1888  * Load a given plugin attempt to generate errors.
  2129  * Load a given plugin attempt to generate errors.
  1889  *
  2130  *
  1890  * @since 3.0.0
  2131  * @since 3.0.0
  1891  * @since 4.4.0 Function was moved into the `wp-admin/includes/plugin.php` file.
  2132  * @since 4.4.0 Function was moved into the `wp-admin/includes/plugin.php` file.
  1892  *
  2133  *
  1893  * @param string $plugin Plugin file to load.
  2134  * @param string $plugin Path to the plugin file relative to the plugins directory.
  1894  */
  2135  */
  1895 function plugin_sandbox_scrape( $plugin ) {
  2136 function plugin_sandbox_scrape( $plugin ) {
       
  2137 	if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
       
  2138 		define( 'WP_SANDBOX_SCRAPING', true );
       
  2139 	}
  1896 	wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
  2140 	wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
  1897 	include( WP_PLUGIN_DIR . '/' . $plugin );
  2141 	include( WP_PLUGIN_DIR . '/' . $plugin );
  1898 }
  2142 }
  1899 
  2143 
  1900 /**
  2144 /**
  1946 		require_once( ABSPATH . 'wp-admin/includes/misc.php' );
  2190 		require_once( ABSPATH . 'wp-admin/includes/misc.php' );
  1947 	}
  2191 	}
  1948 
  2192 
  1949 	WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
  2193 	WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
  1950 }
  2194 }
       
  2195 
       
  2196 /**
       
  2197  * Determines whether a plugin is technically active but was paused while
       
  2198  * loading.
       
  2199  *
       
  2200  * For more information on this and similar theme functions, check out
       
  2201  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
       
  2202  * Conditional Tags} article in the Theme Developer Handbook.
       
  2203  *
       
  2204  * @since 5.2.0
       
  2205  *
       
  2206  * @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.
       
  2208  */
       
  2209 function is_plugin_paused( $plugin ) {
       
  2210 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
       
  2211 		return false;
       
  2212 	}
       
  2213 
       
  2214 	if ( ! is_plugin_active( $plugin ) ) {
       
  2215 		return false;
       
  2216 	}
       
  2217 
       
  2218 	list( $plugin ) = explode( '/', $plugin );
       
  2219 
       
  2220 	return array_key_exists( $plugin, $GLOBALS['_paused_plugins'] );
       
  2221 }
       
  2222 
       
  2223 /**
       
  2224  * Gets the error that was recorded for a paused plugin.
       
  2225  *
       
  2226  * @since 5.2.0
       
  2227  *
       
  2228  * @param string $plugin Path to the plugin file relative to the plugins
       
  2229  *                       directory.
       
  2230  * @return array|false Array of error information as it was returned by
       
  2231  *                     `error_get_last()`, or false if none was recorded.
       
  2232  */
       
  2233 function wp_get_plugin_error( $plugin ) {
       
  2234 	if ( ! isset( $GLOBALS['_paused_plugins'] ) ) {
       
  2235 		return false;
       
  2236 	}
       
  2237 
       
  2238 	list( $plugin ) = explode( '/', $plugin );
       
  2239 
       
  2240 	if ( ! array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ) ) {
       
  2241 		return false;
       
  2242 	}
       
  2243 
       
  2244 	return $GLOBALS['_paused_plugins'][ $plugin ];
       
  2245 }
       
  2246 
       
  2247 /**
       
  2248  * Tries to resume a single plugin.
       
  2249  *
       
  2250  * If a redirect was provided, we first ensure the plugin does not throw fatal
       
  2251  * errors anymore.
       
  2252  *
       
  2253  * The way it works is by setting the redirection to the error before trying to
       
  2254  * 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.
       
  2256  *
       
  2257  * @since 5.2.0
       
  2258  *
       
  2259  * @param string $plugin       Single plugin to resume.
       
  2260  * @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,
       
  2262  *                       `WP_Error` on failure.
       
  2263  */
       
  2264 function resume_plugin( $plugin, $redirect = '' ) {
       
  2265 	/*
       
  2266 	 * We'll override this later if the plugin could be resumed without
       
  2267 	 * creating a fatal error.
       
  2268 	 */
       
  2269 	if ( ! empty( $redirect ) ) {
       
  2270 		wp_redirect(
       
  2271 			add_query_arg(
       
  2272 				'_error_nonce',
       
  2273 				wp_create_nonce( 'plugin-resume-error_' . $plugin ),
       
  2274 				$redirect
       
  2275 			)
       
  2276 		);
       
  2277 
       
  2278 		// Load the plugin to test whether it throws a fatal error.
       
  2279 		ob_start();
       
  2280 		plugin_sandbox_scrape( $plugin );
       
  2281 		ob_clean();
       
  2282 	}
       
  2283 
       
  2284 	list( $extension ) = explode( '/', $plugin );
       
  2285 
       
  2286 	$result = wp_paused_plugins()->delete( $extension );
       
  2287 
       
  2288 	if ( ! $result ) {
       
  2289 		return new WP_Error(
       
  2290 			'could_not_resume_plugin',
       
  2291 			__( 'Could not resume the plugin.' )
       
  2292 		);
       
  2293 	}
       
  2294 
       
  2295 	return true;
       
  2296 }
       
  2297 
       
  2298 /**
       
  2299  * Renders an admin notice in case some plugins have been paused due to errors.
       
  2300  *
       
  2301  * @since 5.2.0
       
  2302  */
       
  2303 function paused_plugins_notice() {
       
  2304 	if ( 'plugins.php' === $GLOBALS['pagenow'] ) {
       
  2305 		return;
       
  2306 	}
       
  2307 
       
  2308 	if ( ! current_user_can( 'resume_plugins' ) ) {
       
  2309 		return;
       
  2310 	}
       
  2311 
       
  2312 	if ( ! isset( $GLOBALS['_paused_plugins'] ) || empty( $GLOBALS['_paused_plugins'] ) ) {
       
  2313 		return;
       
  2314 	}
       
  2315 
       
  2316 	printf(
       
  2317 		'<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>',
       
  2318 		__( 'One or more plugins failed to load properly.' ),
       
  2319 		__( 'You can find more details and make changes on the Plugins screen.' ),
       
  2320 		esc_url( admin_url( 'plugins.php?plugin_status=paused' ) ),
       
  2321 		__( 'Go to the Plugins screen' )
       
  2322 	);
       
  2323 }