Wordpress 4.6
[autoinstalls/wordpress.git] / wp-admin / includes / plugin.php
1 <?php
2 /**
3  * WordPress Plugin Administration API
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 /**
10  * Parses the plugin contents to retrieve plugin's metadata.
11  *
12  * The metadata of the plugin's data searches for the following in the plugin's
13  * header. All plugin data must be on its own line. For plugin description, it
14  * must not have any newlines or only parts of the description will be displayed
15  * and the same goes for the plugin data. The below is formatted for printing.
16  *
17  *     /*
18  *     Plugin Name: Name of Plugin
19  *     Plugin URI: Link to plugin information
20  *     Description: Plugin Description
21  *     Author: Plugin author's name
22  *     Author URI: Link to the author's web site
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
25  *              load_plugin_textdomain()
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
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
30  *              located in.
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
33  *              activated on a single site when Multisite is enabled.
34  *      * / # Remove the space to close comment
35  *
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
38  * the plugin contents when it has all of the required plugin data.
39  *
40  * The first 8kiB of the file will be pulled in and if the plugin data is not
41  * within that first 8kiB, then the plugin author should correct their plugin
42  * and move the plugin data headers to the top.
43  *
44  * The plugin file is assumed to have permissions to allow for scripts to read
45  * the file. This is not checked however and the file is only opened for
46  * reading.
47  *
48  * @since 1.5.0
49  *
50  * @param string $plugin_file Path to the plugin file
51  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
52  *                            Default true.
53  * @param bool   $translate   Optional. If the returned data should be translated. Default true.
54  * @return array {
55  *     Plugin data. Values will be empty if not supplied by the plugin.
56  *
57  *     @type string $Name        Name of the plugin. Should be unique.
58  *     @type string $Title       Title of the plugin and link to the plugin's site (if set).
59  *     @type string $Description Plugin description.
60  *     @type string $Author      Author's name.
61  *     @type string $AuthorURI   Author's website address (if set).
62  *     @type string $Version     Plugin version.
63  *     @type string $TextDomain  Plugin textdomain.
64  *     @type string $DomainPath  Plugins relative directory path to .mo files.
65  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
66  * }
67  */
68 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
69
70         $default_headers = array(
71                 'Name' => 'Plugin Name',
72                 'PluginURI' => 'Plugin URI',
73                 'Version' => 'Version',
74                 'Description' => 'Description',
75                 'Author' => 'Author',
76                 'AuthorURI' => 'Author URI',
77                 'TextDomain' => 'Text Domain',
78                 'DomainPath' => 'Domain Path',
79                 'Network' => 'Network',
80                 // Site Wide Only is deprecated in favor of Network.
81                 '_sitewide' => 'Site Wide Only',
82         );
83
84         $plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
85
86         // Site Wide Only is the old header for Network
87         if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) {
88                 /* translators: 1: Site Wide Only: true, 2: Network: true */
89                 _deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), '<code>Site Wide Only: true</code>', '<code>Network: true</code>' ) );
90                 $plugin_data['Network'] = $plugin_data['_sitewide'];
91         }
92         $plugin_data['Network'] = ( 'true' == strtolower( $plugin_data['Network'] ) );
93         unset( $plugin_data['_sitewide'] );
94
95         // If no text domain is defined fall back to the plugin slug.
96         if ( ! $plugin_data['TextDomain'] ) {
97                 $plugin_slug = dirname( plugin_basename( $plugin_file ) );
98                 if ( '.' !== $plugin_slug && false === strpos( $plugin_slug, '/' ) ) {
99                         $plugin_data['TextDomain'] = $plugin_slug;
100                 }
101         }
102
103         if ( $markup || $translate ) {
104                 $plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate );
105         } else {
106                 $plugin_data['Title']      = $plugin_data['Name'];
107                 $plugin_data['AuthorName'] = $plugin_data['Author'];
108         }
109
110         return $plugin_data;
111 }
112
113 /**
114  * Sanitizes plugin data, optionally adds markup, optionally translates.
115  *
116  * @since 2.7.0
117  * @access private
118  * @see get_plugin_data()
119  */
120 function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
121
122         // Sanitize the plugin filename to a WP_PLUGIN_DIR relative path
123         $plugin_file = plugin_basename( $plugin_file );
124
125         // Translate fields
126         if ( $translate ) {
127                 if ( $textdomain = $plugin_data['TextDomain'] ) {
128                         if ( ! is_textdomain_loaded( $textdomain ) ) {
129                                 if ( $plugin_data['DomainPath'] ) {
130                                         load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] );
131                                 } else {
132                                         load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) );
133                                 }
134                         }
135                 } elseif ( 'hello.php' == basename( $plugin_file ) ) {
136                         $textdomain = 'default';
137                 }
138                 if ( $textdomain ) {
139                         foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field )
140                                 $plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
141                 }
142         }
143
144         // Sanitize fields
145         $allowed_tags = $allowed_tags_in_links = array(
146                 'abbr'    => array( 'title' => true ),
147                 'acronym' => array( 'title' => true ),
148                 'code'    => true,
149                 'em'      => true,
150                 'strong'  => true,
151         );
152         $allowed_tags['a'] = array( 'href' => true, 'title' => true );
153
154         // 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).
156         $plugin_data['Name']        = wp_kses( $plugin_data['Name'],        $allowed_tags_in_links );
157         $plugin_data['Author']      = wp_kses( $plugin_data['Author'],      $allowed_tags );
158
159         $plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags );
160         $plugin_data['Version']     = wp_kses( $plugin_data['Version'],     $allowed_tags );
161
162         $plugin_data['PluginURI']   = esc_url( $plugin_data['PluginURI'] );
163         $plugin_data['AuthorURI']   = esc_url( $plugin_data['AuthorURI'] );
164
165         $plugin_data['Title']      = $plugin_data['Name'];
166         $plugin_data['AuthorName'] = $plugin_data['Author'];
167
168         // Apply markup
169         if ( $markup ) {
170                 if ( $plugin_data['PluginURI'] && $plugin_data['Name'] )
171                         $plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
172
173                 if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] )
174                         $plugin_data['Author'] = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
175
176                 $plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
177
178                 if ( $plugin_data['Author'] )
179                         $plugin_data['Description'] .= ' <cite>' . sprintf( __('By %s.'), $plugin_data['Author'] ) . '</cite>';
180         }
181
182         return $plugin_data;
183 }
184
185 /**
186  * Get a list of a plugin's files.
187  *
188  * @since 2.8.0
189  *
190  * @param string $plugin Plugin ID
191  * @return array List of files relative to the plugin root.
192  */
193 function get_plugin_files($plugin) {
194         $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
195         $dir = dirname($plugin_file);
196         $plugin_files = array($plugin);
197         if ( is_dir($dir) && $dir != WP_PLUGIN_DIR ) {
198                 $plugins_dir = @ opendir( $dir );
199                 if ( $plugins_dir ) {
200                         while (($file = readdir( $plugins_dir ) ) !== false ) {
201                                 if ( substr($file, 0, 1) == '.' )
202                                         continue;
203                                 if ( is_dir( $dir . '/' . $file ) ) {
204                                         $plugins_subdir = @ opendir( $dir . '/' . $file );
205                                         if ( $plugins_subdir ) {
206                                                 while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
207                                                         if ( substr($subfile, 0, 1) == '.' )
208                                                                 continue;
209                                                         $plugin_files[] = plugin_basename("$dir/$file/$subfile");
210                                                 }
211                                                 @closedir( $plugins_subdir );
212                                         }
213                                 } else {
214                                         if ( plugin_basename("$dir/$file") != $plugin )
215                                                 $plugin_files[] = plugin_basename("$dir/$file");
216                                 }
217                         }
218                         @closedir( $plugins_dir );
219                 }
220         }
221
222         return $plugin_files;
223 }
224
225 /**
226  * Check the plugins directory and retrieve all plugin files with plugin data.
227  *
228  * WordPress only supports plugin files in the base plugins directory
229  * (wp-content/plugins) and in one directory above the plugins directory
230  * (wp-content/plugins/my-plugin). The file it looks for has the plugin data
231  * and must be found in those two locations. It is recommended to keep your
232  * plugin files in their own directories.
233  *
234  * The file with the plugin data is the file that will be included and therefore
235  * needs to have the main execution for the plugin. This does not mean
236  * everything must be contained in the file and it is recommended that the file
237  * be split for maintainability. Keep everything in one file for extreme
238  * optimization purposes.
239  *
240  * @since 1.5.0
241  *
242  * @param string $plugin_folder Optional. Relative path to single plugin folder.
243  * @return array Key is the plugin file path and the value is an array of the plugin data.
244  */
245 function get_plugins($plugin_folder = '') {
246
247         if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') )
248                 $cache_plugins = array();
249
250         if ( isset($cache_plugins[ $plugin_folder ]) )
251                 return $cache_plugins[ $plugin_folder ];
252
253         $wp_plugins = array ();
254         $plugin_root = WP_PLUGIN_DIR;
255         if ( !empty($plugin_folder) )
256                 $plugin_root .= $plugin_folder;
257
258         // Files in wp-content/plugins directory
259         $plugins_dir = @ opendir( $plugin_root);
260         $plugin_files = array();
261         if ( $plugins_dir ) {
262                 while (($file = readdir( $plugins_dir ) ) !== false ) {
263                         if ( substr($file, 0, 1) == '.' )
264                                 continue;
265                         if ( is_dir( $plugin_root.'/'.$file ) ) {
266                                 $plugins_subdir = @ opendir( $plugin_root.'/'.$file );
267                                 if ( $plugins_subdir ) {
268                                         while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
269                                                 if ( substr($subfile, 0, 1) == '.' )
270                                                         continue;
271                                                 if ( substr($subfile, -4) == '.php' )
272                                                         $plugin_files[] = "$file/$subfile";
273                                         }
274                                         closedir( $plugins_subdir );
275                                 }
276                         } else {
277                                 if ( substr($file, -4) == '.php' )
278                                         $plugin_files[] = $file;
279                         }
280                 }
281                 closedir( $plugins_dir );
282         }
283
284         if ( empty($plugin_files) )
285                 return $wp_plugins;
286
287         foreach ( $plugin_files as $plugin_file ) {
288                 if ( !is_readable( "$plugin_root/$plugin_file" ) )
289                         continue;
290
291                 $plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
292
293                 if ( empty ( $plugin_data['Name'] ) )
294                         continue;
295
296                 $wp_plugins[plugin_basename( $plugin_file )] = $plugin_data;
297         }
298
299         uasort( $wp_plugins, '_sort_uname_callback' );
300
301         $cache_plugins[ $plugin_folder ] = $wp_plugins;
302         wp_cache_set('plugins', $cache_plugins, 'plugins');
303
304         return $wp_plugins;
305 }
306
307 /**
308  * Check the mu-plugins directory and retrieve all mu-plugin files with any plugin data.
309  *
310  * WordPress only includes mu-plugin files in the base mu-plugins directory (wp-content/mu-plugins).
311  *
312  * @since 3.0.0
313  * @return array Key is the mu-plugin file path and the value is an array of the mu-plugin data.
314  */
315 function get_mu_plugins() {
316         $wp_plugins = array();
317         // Files in wp-content/mu-plugins directory
318         $plugin_files = array();
319
320         if ( ! is_dir( WPMU_PLUGIN_DIR ) )
321                 return $wp_plugins;
322         if ( $plugins_dir = @ opendir( WPMU_PLUGIN_DIR ) ) {
323                 while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
324                         if ( substr( $file, -4 ) == '.php' )
325                                 $plugin_files[] = $file;
326                 }
327         } else {
328                 return $wp_plugins;
329         }
330
331         @closedir( $plugins_dir );
332
333         if ( empty($plugin_files) )
334                 return $wp_plugins;
335
336         foreach ( $plugin_files as $plugin_file ) {
337                 if ( !is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) )
338                         continue;
339
340                 $plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
341
342                 if ( empty ( $plugin_data['Name'] ) )
343                         $plugin_data['Name'] = $plugin_file;
344
345                 $wp_plugins[ $plugin_file ] = $plugin_data;
346         }
347
348         if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php') <= 30 ) // silence is golden
349                 unset( $wp_plugins['index.php'] );
350
351         uasort( $wp_plugins, '_sort_uname_callback' );
352
353         return $wp_plugins;
354 }
355
356 /**
357  * Callback to sort array by a 'Name' key.
358  *
359  * @since 3.1.0
360  * @access private
361  */
362 function _sort_uname_callback( $a, $b ) {
363         return strnatcasecmp( $a['Name'], $b['Name'] );
364 }
365
366 /**
367  * Check the wp-content directory and retrieve all drop-ins with any plugin data.
368  *
369  * @since 3.0.0
370  * @return array Key is the file path and the value is an array of the plugin data.
371  */
372 function get_dropins() {
373         $dropins = array();
374         $plugin_files = array();
375
376         $_dropins = _get_dropins();
377
378         // These exist in the wp-content directory
379         if ( $plugins_dir = @ opendir( WP_CONTENT_DIR ) ) {
380                 while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
381                         if ( isset( $_dropins[ $file ] ) )
382                                 $plugin_files[] = $file;
383                 }
384         } else {
385                 return $dropins;
386         }
387
388         @closedir( $plugins_dir );
389
390         if ( empty($plugin_files) )
391                 return $dropins;
392
393         foreach ( $plugin_files as $plugin_file ) {
394                 if ( !is_readable( WP_CONTENT_DIR . "/$plugin_file" ) )
395                         continue;
396                 $plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached.
397                 if ( empty( $plugin_data['Name'] ) )
398                         $plugin_data['Name'] = $plugin_file;
399                 $dropins[ $plugin_file ] = $plugin_data;
400         }
401
402         uksort( $dropins, 'strnatcasecmp' );
403
404         return $dropins;
405 }
406
407 /**
408  * Returns drop-ins that WordPress uses.
409  *
410  * Includes Multisite drop-ins only when is_multisite()
411  *
412  * @since 3.0.0
413  * @return array Key is file name. The value is an array, with the first value the
414  *      purpose of the drop-in and the second value the name of the constant that must be
415  *      true for the drop-in to be used, or true if no constant is required.
416  */
417 function _get_dropins() {
418         $dropins = array(
419                 'advanced-cache.php' => array( __( 'Advanced caching plugin.'       ), 'WP_CACHE' ), // WP_CACHE
420                 'db.php'             => array( __( 'Custom database class.'         ), true ), // auto on load
421                 'db-error.php'       => array( __( 'Custom database error message.' ), true ), // auto on error
422                 'install.php'        => array( __( 'Custom install script.'         ), true ), // auto on install
423                 'maintenance.php'    => array( __( 'Custom maintenance message.'    ), true ), // auto on maintenance
424                 'object-cache.php'   => array( __( 'External object cache.'         ), true ), // auto on load
425         );
426
427         if ( is_multisite() ) {
428                 $dropins['sunrise.php'       ] = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE
429                 $dropins['blog-deleted.php'  ] = array( __( 'Custom site deleted message.'   ), true ); // auto on deleted blog
430                 $dropins['blog-inactive.php' ] = array( __( 'Custom site inactive message.'  ), true ); // auto on inactive blog
431                 $dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // auto on archived or spammed blog
432         }
433
434         return $dropins;
435 }
436
437 /**
438  * Check whether a plugin is active.
439  *
440  * Only plugins installed in the plugins/ folder can be active.
441  *
442  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
443  * return false for those plugins.
444  *
445  * @since 2.5.0
446  *
447  * @param string $plugin Base plugin path from plugins directory.
448  * @return bool True, if in the active plugins list. False, not in the list.
449  */
450 function is_plugin_active( $plugin ) {
451         return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin );
452 }
453
454 /**
455  * Check whether the plugin is inactive.
456  *
457  * Reverse of is_plugin_active(). Used as a callback.
458  *
459  * @since 3.1.0
460  * @see is_plugin_active()
461  *
462  * @param string $plugin Base plugin path from plugins directory.
463  * @return bool True if inactive. False if active.
464  */
465 function is_plugin_inactive( $plugin ) {
466         return ! is_plugin_active( $plugin );
467 }
468
469 /**
470  * Check whether the plugin is active for the entire network.
471  *
472  * Only plugins installed in the plugins/ folder can be active.
473  *
474  * Plugins in the mu-plugins/ folder can't be "activated," so this function will
475  * return false for those plugins.
476  *
477  * @since 3.0.0
478  *
479  * @param string $plugin Base plugin path from plugins directory.
480  * @return bool True, if active for the network, otherwise false.
481  */
482 function is_plugin_active_for_network( $plugin ) {
483         if ( !is_multisite() )
484                 return false;
485
486         $plugins = get_site_option( 'active_sitewide_plugins');
487         if ( isset($plugins[$plugin]) )
488                 return true;
489
490         return false;
491 }
492
493 /**
494  * Checks for "Network: true" in the plugin header to see if this should
495  * be activated only as a network wide plugin. The plugin would also work
496  * when Multisite is not enabled.
497  *
498  * Checks for "Site Wide Only: true" for backward compatibility.
499  *
500  * @since 3.0.0
501  *
502  * @param string $plugin Plugin to check
503  * @return bool True if plugin is network only, false otherwise.
504  */
505 function is_network_only_plugin( $plugin ) {
506         $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
507         if ( $plugin_data )
508                 return $plugin_data['Network'];
509         return false;
510 }
511
512 /**
513  * Attempts activation of plugin in a "sandbox" and redirects on success.
514  *
515  * A plugin that is already activated will not attempt to be activated again.
516  *
517  * The way it works is by setting the redirection to the error before trying to
518  * include the plugin file. If the plugin fails, then the redirection will not
519  * be overwritten with the success message. Also, the options will not be
520  * updated and the activation hook will not be called on plugin error.
521  *
522  * It should be noted that in no way the below code will actually prevent errors
523  * within the file. The code should not be used elsewhere to replicate the
524  * "sandbox", which uses redirection to work.
525  * {@source 13 1}
526  *
527  * If any errors are found or text is outputted, then it will be captured to
528  * ensure that the success redirection will update the error redirection.
529  *
530  * @since 2.5.0
531  *
532  * @param string $plugin       Plugin path to main plugin file with plugin data.
533  * @param string $redirect     Optional. URL to redirect to.
534  * @param bool   $network_wide Optional. Whether to enable the plugin for all sites in the network
535  *                             or just the current site. Multisite only. Default false.
536  * @param bool   $silent       Optional. Whether to prevent calling activation hooks. Default false.
537  * @return WP_Error|null WP_Error on invalid file or null on success.
538  */
539 function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
540         $plugin = plugin_basename( trim( $plugin ) );
541
542         if ( is_multisite() && ( $network_wide || is_network_only_plugin($plugin) ) ) {
543                 $network_wide = true;
544                 $current = get_site_option( 'active_sitewide_plugins', array() );
545                 $_GET['networkwide'] = 1; // Back compat for plugins looking for this value.
546         } else {
547                 $current = get_option( 'active_plugins', array() );
548         }
549
550         $valid = validate_plugin($plugin);
551         if ( is_wp_error($valid) )
552                 return $valid;
553
554         if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current ) ) ) {
555                 if ( !empty($redirect) )
556                         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
557                 ob_start();
558                 wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
559                 $_wp_plugin_file = $plugin;
560                 include_once( WP_PLUGIN_DIR . '/' . $plugin );
561                 $plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
562
563                 if ( ! $silent ) {
564                         /**
565                          * Fires before a plugin is activated.
566                          *
567                          * If a plugin is silently activated (such as during an update),
568                          * this hook does not fire.
569                          *
570                          * @since 2.9.0
571                          *
572                          * @param string $plugin       Plugin path to main plugin file with plugin data.
573                          * @param bool   $network_wide Whether to enable the plugin for all sites in the network
574                          *                             or just the current site. Multisite only. Default is false.
575                          */
576                         do_action( 'activate_plugin', $plugin, $network_wide );
577
578                         /**
579                          * Fires as a specific plugin is being activated.
580                          *
581                          * This hook is the "activation" hook used internally by register_activation_hook().
582                          * The dynamic portion of the hook name, `$plugin`, refers to the plugin basename.
583                          *
584                          * If a plugin is silently activated (such as during an update), this hook does not fire.
585                          *
586                          * @since 2.0.0
587                          *
588                          * @param bool $network_wide Whether to enable the plugin for all sites in the network
589                          *                           or just the current site. Multisite only. Default is false.
590                          */
591                         do_action( 'activate_' . $plugin, $network_wide );
592                 }
593
594                 if ( $network_wide ) {
595                         $current = get_site_option( 'active_sitewide_plugins', array() );
596                         $current[$plugin] = time();
597                         update_site_option( 'active_sitewide_plugins', $current );
598                 } else {
599                         $current = get_option( 'active_plugins', array() );
600                         $current[] = $plugin;
601                         sort($current);
602                         update_option('active_plugins', $current);
603                 }
604
605                 if ( ! $silent ) {
606                         /**
607                          * Fires after a plugin has been activated.
608                          *
609                          * If a plugin is silently activated (such as during an update),
610                          * this hook does not fire.
611                          *
612                          * @since 2.9.0
613                          *
614                          * @param string $plugin       Plugin path to main plugin file with plugin data.
615                          * @param bool   $network_wide Whether to enable the plugin for all sites in the network
616                          *                             or just the current site. Multisite only. Default is false.
617                          */
618                         do_action( 'activated_plugin', $plugin, $network_wide );
619                 }
620
621                 if ( ob_get_length() > 0 ) {
622                         $output = ob_get_clean();
623                         return new WP_Error('unexpected_output', __('The plugin generated unexpected output.'), $output);
624                 }
625                 ob_end_clean();
626         }
627
628         return null;
629 }
630
631 /**
632  * Deactivate a single plugin or multiple plugins.
633  *
634  * The deactivation hook is disabled by the plugin upgrader by using the $silent
635  * parameter.
636  *
637  * @since 2.5.0
638  *
639  * @param string|array $plugins Single plugin or list of plugins to deactivate.
640  * @param bool $silent Prevent calling deactivation hooks. Default is false.
641  * @param mixed $network_wide Whether to deactivate the plugin for all sites in the network.
642  *      A value of null (the default) will deactivate plugins for both the site and the network.
643  */
644 function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) {
645         if ( is_multisite() )
646                 $network_current = get_site_option( 'active_sitewide_plugins', array() );
647         $current = get_option( 'active_plugins', array() );
648         $do_blog = $do_network = false;
649
650         foreach ( (array) $plugins as $plugin ) {
651                 $plugin = plugin_basename( trim( $plugin ) );
652                 if ( ! is_plugin_active($plugin) )
653                         continue;
654
655                 $network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin );
656
657                 if ( ! $silent ) {
658                         /**
659                          * Fires before a plugin is deactivated.
660                          *
661                          * If a plugin is silently deactivated (such as during an update),
662                          * this hook does not fire.
663                          *
664                          * @since 2.9.0
665                          *
666                          * @param string $plugin               Plugin path to main plugin file with plugin data.
667                          * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
668                          *                                     or just the current site. Multisite only. Default is false.
669                          */
670                         do_action( 'deactivate_plugin', $plugin, $network_deactivating );
671                 }
672
673                 if ( false !== $network_wide ) {
674                         if ( is_plugin_active_for_network( $plugin ) ) {
675                                 $do_network = true;
676                                 unset( $network_current[ $plugin ] );
677                         } elseif ( $network_wide ) {
678                                 continue;
679                         }
680                 }
681
682                 if ( true !== $network_wide ) {
683                         $key = array_search( $plugin, $current );
684                         if ( false !== $key ) {
685                                 $do_blog = true;
686                                 unset( $current[ $key ] );
687                         }
688                 }
689
690                 if ( ! $silent ) {
691                         /**
692                          * Fires as a specific plugin is being deactivated.
693                          *
694                          * This hook is the "deactivation" hook used internally by register_deactivation_hook().
695                          * The dynamic portion of the hook name, `$plugin`, refers to the plugin basename.
696                          *
697                          * If a plugin is silently deactivated (such as during an update), this hook does not fire.
698                          *
699                          * @since 2.0.0
700                          *
701                          * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network
702                          *                                   or just the current site. Multisite only. Default is false.
703                          */
704                         do_action( 'deactivate_' . $plugin, $network_deactivating );
705
706                         /**
707                          * Fires after a plugin is deactivated.
708                          *
709                          * If a plugin is silently deactivated (such as during an update),
710                          * this hook does not fire.
711                          *
712                          * @since 2.9.0
713                          *
714                          * @param string $plugin               Plugin basename.
715                          * @param bool   $network_deactivating Whether the plugin is deactivated for all sites in the network
716                          *                                     or just the current site. Multisite only. Default false.
717                          */
718                         do_action( 'deactivated_plugin', $plugin, $network_deactivating );
719                 }
720         }
721
722         if ( $do_blog )
723                 update_option('active_plugins', $current);
724         if ( $do_network )
725                 update_site_option( 'active_sitewide_plugins', $network_current );
726 }
727
728 /**
729  * Activate multiple plugins.
730  *
731  * When WP_Error is returned, it does not mean that one of the plugins had
732  * errors. It means that one or more of the plugins file path was invalid.
733  *
734  * The execution will be halted as soon as one of the plugins has an error.
735  *
736  * @since 2.6.0
737  *
738  * @param string|array $plugins Single plugin or list of plugins to activate.
739  * @param string $redirect Redirect to page after successful activation.
740  * @param bool $network_wide Whether to enable the plugin for all sites in the network.
741  * @param bool $silent Prevent calling activation hooks. Default is false.
742  * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation.
743  */
744 function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) {
745         if ( !is_array($plugins) )
746                 $plugins = array($plugins);
747
748         $errors = array();
749         foreach ( $plugins as $plugin ) {
750                 if ( !empty($redirect) )
751                         $redirect = add_query_arg('plugin', $plugin, $redirect);
752                 $result = activate_plugin($plugin, $redirect, $network_wide, $silent);
753                 if ( is_wp_error($result) )
754                         $errors[$plugin] = $result;
755         }
756
757         if ( !empty($errors) )
758                 return new WP_Error('plugins_invalid', __('One of the plugins is invalid.'), $errors);
759
760         return true;
761 }
762
763 /**
764  * Remove directory and files of a plugin for a list of plugins.
765  *
766  * @since 2.6.0
767  *
768  * @global WP_Filesystem_Base $wp_filesystem
769  *
770  * @param array  $plugins    List of plugins to delete.
771  * @param string $deprecated Deprecated.
772  * @return bool|null|WP_Error True on success, false is $plugins is empty, WP_Error on failure.
773  *                            Null if filesystem credentials are required to proceed.
774  */
775 function delete_plugins( $plugins, $deprecated = '' ) {
776         global $wp_filesystem;
777
778         if ( empty($plugins) )
779                 return false;
780
781         $checked = array();
782         foreach ( $plugins as $plugin )
783                 $checked[] = 'checked[]=' . $plugin;
784
785         $url = wp_nonce_url('plugins.php?action=delete-selected&verify-delete=1&' . implode('&', $checked), 'bulk-plugins');
786
787         ob_start();
788         $credentials = request_filesystem_credentials( $url );
789         $data = ob_get_clean();
790
791         if ( false === $credentials ) {
792                 if ( ! empty($data) ){
793                         include_once( ABSPATH . 'wp-admin/admin-header.php');
794                         echo $data;
795                         include( ABSPATH . 'wp-admin/admin-footer.php');
796                         exit;
797                 }
798                 return;
799         }
800
801         if ( ! WP_Filesystem( $credentials ) ) {
802                 ob_start();
803                 request_filesystem_credentials( $url, '', true ); // Failed to connect, Error and request again.
804                 $data = ob_get_clean();
805
806                 if ( ! empty($data) ){
807                         include_once( ABSPATH . 'wp-admin/admin-header.php');
808                         echo $data;
809                         include( ABSPATH . 'wp-admin/admin-footer.php');
810                         exit;
811                 }
812                 return;
813         }
814
815         if ( ! is_object($wp_filesystem) )
816                 return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
817
818         if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
819                 return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors);
820
821         // Get the base plugin folder.
822         $plugins_dir = $wp_filesystem->wp_plugins_dir();
823         if ( empty( $plugins_dir ) ) {
824                 return new WP_Error( 'fs_no_plugins_dir', __( 'Unable to locate WordPress plugin directory.' ) );
825         }
826
827         $plugins_dir = trailingslashit( $plugins_dir );
828
829         $plugin_translations = wp_get_installed_translations( 'plugins' );
830
831         $errors = array();
832
833         foreach ( $plugins as $plugin_file ) {
834                 // Run Uninstall hook.
835                 if ( is_uninstallable_plugin( $plugin_file ) ) {
836                         uninstall_plugin($plugin_file);
837                 }
838
839                 /**
840                  * Fires immediately before a plugin deletion attempt.
841                  *
842                  * @since 4.4.0
843                  *
844                  * @param string $plugin_file Plugin file name.
845                  */
846                 do_action( 'delete_plugin', $plugin_file );
847
848                 $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );
849
850                 // If plugin is in its own directory, recursively delete the directory.
851                 if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir ) { //base check on if plugin includes directory separator AND that it's not the root plugin folder
852                         $deleted = $wp_filesystem->delete( $this_plugin_dir, true );
853                 } else {
854                         $deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file );
855                 }
856
857                 /**
858                  * Fires immediately after a plugin deletion attempt.
859                  *
860                  * @since 4.4.0
861                  *
862                  * @param string $plugin_file Plugin file name.
863                  * @param bool   $deleted     Whether the plugin deletion was successful.
864                  */
865                 do_action( 'deleted_plugin', $plugin_file, $deleted );
866
867                 if ( ! $deleted ) {
868                         $errors[] = $plugin_file;
869                         continue;
870                 }
871
872                 // Remove language files, silently.
873                 $plugin_slug = dirname( $plugin_file );
874                 if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) {
875                         $translations = $plugin_translations[ $plugin_slug ];
876
877                         foreach ( $translations as $translation => $data ) {
878                                 $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' );
879                                 $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' );
880                         }
881                 }
882         }
883
884         // Remove deleted plugins from the plugin updates list.
885         if ( $current = get_site_transient('update_plugins') ) {
886                 // Don't remove the plugins that weren't deleted.
887                 $deleted = array_diff( $plugins, $errors );
888
889                 foreach ( $deleted as $plugin_file ) {
890                         unset( $current->response[ $plugin_file ] );
891                 }
892
893                 set_site_transient( 'update_plugins', $current );
894         }
895
896         if ( ! empty($errors) )
897                 return new WP_Error('could_not_remove_plugin', sprintf(__('Could not fully remove the plugin(s) %s.'), implode(', ', $errors)) );
898
899         return true;
900 }
901
902 /**
903  * Validate active plugins
904  *
905  * Validate all active plugins, deactivates invalid and
906  * returns an array of deactivated ones.
907  *
908  * @since 2.5.0
909  * @return array invalid plugins, plugin as key, error as value
910  */
911 function validate_active_plugins() {
912         $plugins = get_option( 'active_plugins', array() );
913         // Validate vartype: array.
914         if ( ! is_array( $plugins ) ) {
915                 update_option( 'active_plugins', array() );
916                 $plugins = array();
917         }
918
919         if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) {
920                 $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
921                 $plugins = array_merge( $plugins, array_keys( $network_plugins ) );
922         }
923
924         if ( empty( $plugins ) )
925                 return array();
926
927         $invalid = array();
928
929         // Invalid plugins get deactivated.
930         foreach ( $plugins as $plugin ) {
931                 $result = validate_plugin( $plugin );
932                 if ( is_wp_error( $result ) ) {
933                         $invalid[$plugin] = $result;
934                         deactivate_plugins( $plugin, true );
935                 }
936         }
937         return $invalid;
938 }
939
940 /**
941  * Validate the plugin path.
942  *
943  * Checks that the file exists and is a valid file. See validate_file().
944  *
945  * @since 2.5.0
946  *
947  * @param string $plugin Plugin Path.
948  * @return WP_Error|int 0 on success, WP_Error on failure.
949  */
950 function validate_plugin($plugin) {
951         if ( validate_file($plugin) )
952                 return new WP_Error('plugin_invalid', __('Invalid plugin path.'));
953         if ( ! file_exists(WP_PLUGIN_DIR . '/' . $plugin) )
954                 return new WP_Error('plugin_not_found', __('Plugin file does not exist.'));
955
956         $installed_plugins = get_plugins();
957         if ( ! isset($installed_plugins[$plugin]) )
958                 return new WP_Error('no_plugin_header', __('The plugin does not have a valid header.'));
959         return 0;
960 }
961
962 /**
963  * Whether the plugin can be uninstalled.
964  *
965  * @since 2.7.0
966  *
967  * @param string $plugin Plugin path to check.
968  * @return bool Whether plugin can be uninstalled.
969  */
970 function is_uninstallable_plugin($plugin) {
971         $file = plugin_basename($plugin);
972
973         $uninstallable_plugins = (array) get_option('uninstall_plugins');
974         if ( isset( $uninstallable_plugins[$file] ) || file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) )
975                 return true;
976
977         return false;
978 }
979
980 /**
981  * Uninstall a single plugin.
982  *
983  * Calls the uninstall hook, if it is available.
984  *
985  * @since 2.7.0
986  *
987  * @param string $plugin Relative plugin path from Plugin Directory.
988  * @return true True if a plugin's uninstall.php file has been found and included.
989  */
990 function uninstall_plugin($plugin) {
991         $file = plugin_basename($plugin);
992
993         $uninstallable_plugins = (array) get_option('uninstall_plugins');
994
995         /**
996          * Fires in uninstall_plugin() immediately before the plugin is uninstalled.
997          *
998          * @since 4.5.0
999          *
1000          * @param string $plugin                Relative plugin path from plugin directory.
1001          * @param array  $uninstallable_plugins Uninstallable plugins.
1002          */
1003         do_action( 'pre_uninstall_plugin', $plugin, $uninstallable_plugins );
1004
1005         if ( file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) ) {
1006                 if ( isset( $uninstallable_plugins[$file] ) ) {
1007                         unset($uninstallable_plugins[$file]);
1008                         update_option('uninstall_plugins', $uninstallable_plugins);
1009                 }
1010                 unset($uninstallable_plugins);
1011
1012                 define('WP_UNINSTALL_PLUGIN', $file);
1013                 wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
1014                 include( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' );
1015
1016                 return true;
1017         }
1018
1019         if ( isset( $uninstallable_plugins[$file] ) ) {
1020                 $callable = $uninstallable_plugins[$file];
1021                 unset($uninstallable_plugins[$file]);
1022                 update_option('uninstall_plugins', $uninstallable_plugins);
1023                 unset($uninstallable_plugins);
1024
1025                 wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file );
1026                 include( WP_PLUGIN_DIR . '/' . $file );
1027
1028                 add_action( 'uninstall_' . $file, $callable );
1029
1030                 /**
1031                  * Fires in uninstall_plugin() once the plugin has been uninstalled.
1032                  *
1033                  * The action concatenates the 'uninstall_' prefix with the basename of the
1034                  * plugin passed to uninstall_plugin() to create a dynamically-named action.
1035                  *
1036                  * @since 2.7.0
1037                  */
1038                 do_action( 'uninstall_' . $file );
1039         }
1040 }
1041
1042 //
1043 // Menu
1044 //
1045
1046 /**
1047  * Add a top-level menu page.
1048  *
1049  * This function takes a capability which will be used to determine whether
1050  * or not a page is included in the menu.
1051  *
1052  * The function which is hooked in to handle the output of the page must check
1053  * that the user has the required capability as well.
1054  *
1055  * @global array $menu
1056  * @global array $admin_page_hooks
1057  * @global array $_registered_pages
1058  * @global array $_parent_pages
1059  *
1060  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1061  * @param string   $menu_title The text to be used for the menu.
1062  * @param string   $capability The capability required for this menu to be displayed to the user.
1063  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1064  * @param callable $function   The function to be called to output the content for this page.
1065  * @param string   $icon_url   The URL to the icon to be used for this menu.
1066  *                             * Pass a base64-encoded SVG using a data URI, which will be colored to match
1067  *                               the color scheme. This should begin with 'data:image/svg+xml;base64,'.
1068  *                             * Pass the name of a Dashicons helper class to use a font icon,
1069  *                               e.g. 'dashicons-chart-pie'.
1070  *                             * Pass 'none' to leave div.wp-menu-image empty so an icon can be added via CSS.
1071  * @param int      $position   The position in the menu order this one should appear.
1072  * @return string The resulting page's hook_suffix.
1073  */
1074 function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) {
1075         global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages;
1076
1077         $menu_slug = plugin_basename( $menu_slug );
1078
1079         $admin_page_hooks[$menu_slug] = sanitize_title( $menu_title );
1080
1081         $hookname = get_plugin_page_hookname( $menu_slug, '' );
1082
1083         if ( !empty( $function ) && !empty( $hookname ) && current_user_can( $capability ) )
1084                 add_action( $hookname, $function );
1085
1086         if ( empty($icon_url) ) {
1087                 $icon_url = 'dashicons-admin-generic';
1088                 $icon_class = 'menu-icon-generic ';
1089         } else {
1090                 $icon_url = set_url_scheme( $icon_url );
1091                 $icon_class = '';
1092         }
1093
1094         $new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url );
1095
1096         if ( null === $position ) {
1097                 $menu[] = $new_menu;
1098         } elseif ( isset( $menu[ "$position" ] ) ) {
1099                 $position = $position + substr( base_convert( md5( $menu_slug . $menu_title ), 16, 10 ) , -5 ) * 0.00001;
1100                 $menu[ "$position" ] = $new_menu;
1101         } else {
1102                 $menu[ $position ] = $new_menu;
1103         }
1104
1105         $_registered_pages[$hookname] = true;
1106
1107         // No parent as top level
1108         $_parent_pages[$menu_slug] = false;
1109
1110         return $hookname;
1111 }
1112
1113 /**
1114  * Add a submenu page.
1115  *
1116  * This function takes a capability which will be used to determine whether
1117  * or not a page is included in the menu.
1118  *
1119  * The function which is hooked in to handle the output of the page must check
1120  * that the user has the required capability as well.
1121  *
1122  * @global array $submenu
1123  * @global array $menu
1124  * @global array $_wp_real_parent_file
1125  * @global bool  $_wp_submenu_nopriv
1126  * @global array $_registered_pages
1127  * @global array $_parent_pages
1128  *
1129  * @param string   $parent_slug The slug name for the parent menu (or the file name of a standard WordPress admin page).
1130  * @param string   $page_title  The text to be displayed in the title tags of the page when the menu is selected.
1131  * @param string   $menu_title  The text to be used for the menu.
1132  * @param string   $capability  The capability required for this menu to be displayed to the user.
1133  * @param string   $menu_slug   The slug name to refer to this menu by (should be unique for this menu).
1134  * @param callable $function    The function to be called to output the content for this page.
1135  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1136  */
1137 function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1138         global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv,
1139                 $_registered_pages, $_parent_pages;
1140
1141         $menu_slug = plugin_basename( $menu_slug );
1142         $parent_slug = plugin_basename( $parent_slug);
1143
1144         if ( isset( $_wp_real_parent_file[$parent_slug] ) )
1145                 $parent_slug = $_wp_real_parent_file[$parent_slug];
1146
1147         if ( !current_user_can( $capability ) ) {
1148                 $_wp_submenu_nopriv[$parent_slug][$menu_slug] = true;
1149                 return false;
1150         }
1151
1152         /*
1153          * If the parent doesn't already have a submenu, add a link to the parent
1154          * as the first item in the submenu. If the submenu file is the same as the
1155          * parent file someone is trying to link back to the parent manually. In
1156          * this case, don't automatically add a link back to avoid duplication.
1157          */
1158         if (!isset( $submenu[$parent_slug] ) && $menu_slug != $parent_slug ) {
1159                 foreach ( (array)$menu as $parent_menu ) {
1160                         if ( $parent_menu[2] == $parent_slug && current_user_can( $parent_menu[1] ) )
1161                                 $submenu[$parent_slug][] = array_slice( $parent_menu, 0, 4 );
1162                 }
1163         }
1164
1165         $submenu[$parent_slug][] = array ( $menu_title, $capability, $menu_slug, $page_title );
1166
1167         $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug);
1168         if (!empty ( $function ) && !empty ( $hookname ))
1169                 add_action( $hookname, $function );
1170
1171         $_registered_pages[$hookname] = true;
1172
1173         /*
1174          * Backward-compatibility for plugins using add_management page.
1175          * See wp-admin/admin.php for redirect from edit.php to tools.php
1176          */
1177         if ( 'tools.php' == $parent_slug )
1178                 $_registered_pages[get_plugin_page_hookname( $menu_slug, 'edit.php')] = true;
1179
1180         // No parent as top level.
1181         $_parent_pages[$menu_slug] = $parent_slug;
1182
1183         return $hookname;
1184 }
1185
1186 /**
1187  * Add submenu page to the Tools main menu.
1188  *
1189  * This function takes a capability which will be used to determine whether
1190  * or not a page is included in the menu.
1191  *
1192  * The function which is hooked in to handle the output of the page must check
1193  * that the user has the required capability as well.
1194  *
1195  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1196  * @param string   $menu_title The text to be used for the menu.
1197  * @param string   $capability The capability required for this menu to be displayed to the user.
1198  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1199  * @param callable $function   The function to be called to output the content for this page.
1200  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1201  */
1202 function add_management_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1203         return add_submenu_page( 'tools.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1204 }
1205
1206 /**
1207  * Add submenu page to the Settings main menu.
1208  *
1209  * This function takes a capability which will be used to determine whether
1210  * or not a page is included in the menu.
1211  *
1212  * The function which is hooked in to handle the output of the page must check
1213  * that the user has the required capability as well.
1214  *
1215  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1216  * @param string   $menu_title The text to be used for the menu.
1217  * @param string   $capability The capability required for this menu to be displayed to the user.
1218  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1219  * @param callable $function   The function to be called to output the content for this page.
1220  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1221  */
1222 function add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1223         return add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1224 }
1225
1226 /**
1227  * Add submenu page to the Appearance main menu.
1228  *
1229  * This function takes a capability which will be used to determine whether
1230  * or not a page is included in the menu.
1231  *
1232  * The function which is hooked in to handle the output of the page must check
1233  * that the user has the required capability as well.
1234  *
1235  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1236  * @param string   $menu_title The text to be used for the menu.
1237  * @param string   $capability The capability required for this menu to be displayed to the user.
1238  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1239  * @param callable $function   The function to be called to output the content for this page.
1240  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1241  */
1242 function add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1243         return add_submenu_page( 'themes.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1244 }
1245
1246 /**
1247  * Add submenu page to the Plugins main menu.
1248  *
1249  * This function takes a capability which will be used to determine whether
1250  * or not a page is included in the menu.
1251  *
1252  * The function which is hooked in to handle the output of the page must check
1253  * that the user has the required capability as well.
1254  *
1255  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1256  * @param string   $menu_title The text to be used for the menu.
1257  * @param string   $capability The capability required for this menu to be displayed to the user.
1258  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1259  * @param callable $function   The function to be called to output the content for this page.
1260  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1261  */
1262 function add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1263         return add_submenu_page( 'plugins.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1264 }
1265
1266 /**
1267  * Add submenu page to the Users/Profile main menu.
1268  *
1269  * This function takes a capability which will be used to determine whether
1270  * or not a page is included in the menu.
1271  *
1272  * The function which is hooked in to handle the output of the page must check
1273  * that the user has the required capability as well.
1274  *
1275  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1276  * @param string   $menu_title The text to be used for the menu.
1277  * @param string   $capability The capability required for this menu to be displayed to the user.
1278  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1279  * @param callable $function   The function to be called to output the content for this page.
1280  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1281  */
1282 function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1283         if ( current_user_can('edit_users') )
1284                 $parent = 'users.php';
1285         else
1286                 $parent = 'profile.php';
1287         return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function );
1288 }
1289 /**
1290  * Add submenu page to the Dashboard main menu.
1291  *
1292  * This function takes a capability which will be used to determine whether
1293  * or not a page is included in the menu.
1294  *
1295  * The function which is hooked in to handle the output of the page must check
1296  * that the user has the required capability as well.
1297  *
1298  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1299  * @param string   $menu_title The text to be used for the menu.
1300  * @param string   $capability The capability required for this menu to be displayed to the user.
1301  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1302  * @param callable $function   The function to be called to output the content for this page.
1303  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1304  */
1305 function add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1306         return add_submenu_page( 'index.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1307 }
1308
1309 /**
1310  * Add submenu page to the Posts main menu.
1311  *
1312  * This function takes a capability which will be used to determine whether
1313  * or not a page is included in the menu.
1314  *
1315  * The function which is hooked in to handle the output of the page must check
1316  * that the user has the required capability as well.
1317  *
1318  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1319  * @param string   $menu_title The text to be used for the menu.
1320  * @param string   $capability The capability required for this menu to be displayed to the user.
1321  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1322  * @param callable $function   The function to be called to output the content for this page.
1323  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1324  */
1325 function add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1326         return add_submenu_page( 'edit.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1327 }
1328
1329 /**
1330  * Add submenu page to the Media main menu.
1331  *
1332  * This function takes a capability which will be used to determine whether
1333  * or not a page is included in the menu.
1334  *
1335  * The function which is hooked in to handle the output of the page must check
1336  * that the user has the required capability as well.
1337  *
1338  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1339  * @param string   $menu_title The text to be used for the menu.
1340  * @param string   $capability The capability required for this menu to be displayed to the user.
1341  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1342  * @param callable $function   The function to be called to output the content for this page.
1343  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1344  */
1345 function add_media_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1346         return add_submenu_page( 'upload.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1347 }
1348
1349 /**
1350  * Add submenu page to the Links main menu.
1351  *
1352  * This function takes a capability which will be used to determine whether
1353  * or not a page is included in the menu.
1354  *
1355  * The function which is hooked in to handle the output of the page must check
1356  * that the user has the required capability as well.
1357  *
1358  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1359  * @param string   $menu_title The text to be used for the menu.
1360  * @param string   $capability The capability required for this menu to be displayed to the user.
1361  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1362  * @param callable $function   The function to be called to output the content for this page.
1363  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1364  */
1365 function add_links_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1366         return add_submenu_page( 'link-manager.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1367 }
1368
1369 /**
1370  * Add submenu page to the Pages main menu.
1371  *
1372  * This function takes a capability which will be used to determine whether
1373  * or not a page is included in the menu.
1374  *
1375  * The function which is hooked in to handle the output of the page must check
1376  * that the user has the required capability as well.
1377  *
1378  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1379  * @param string   $menu_title The text to be used for the menu.
1380  * @param string   $capability The capability required for this menu to be displayed to the user.
1381  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1382  * @param callable $function   The function to be called to output the content for this page.
1383  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1384  */
1385 function add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1386         return add_submenu_page( 'edit.php?post_type=page', $page_title, $menu_title, $capability, $menu_slug, $function );
1387 }
1388
1389 /**
1390  * Add submenu page to the Comments main menu.
1391  *
1392  * This function takes a capability which will be used to determine whether
1393  * or not a page is included in the menu.
1394  *
1395  * The function which is hooked in to handle the output of the page must check
1396  * that the user has the required capability as well.
1397  *
1398  * @param string   $page_title The text to be displayed in the title tags of the page when the menu is selected.
1399  * @param string   $menu_title The text to be used for the menu.
1400  * @param string   $capability The capability required for this menu to be displayed to the user.
1401  * @param string   $menu_slug  The slug name to refer to this menu by (should be unique for this menu).
1402  * @param callable $function   The function to be called to output the content for this page.
1403  * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required.
1404  */
1405 function add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) {
1406         return add_submenu_page( 'edit-comments.php', $page_title, $menu_title, $capability, $menu_slug, $function );
1407 }
1408
1409 /**
1410  * Remove a top-level admin menu.
1411  *
1412  * @since 3.1.0
1413  *
1414  * @global array $menu
1415  *
1416  * @param string $menu_slug The slug of the menu.
1417  * @return array|bool The removed menu on success, false if not found.
1418  */
1419 function remove_menu_page( $menu_slug ) {
1420         global $menu;
1421
1422         foreach ( $menu as $i => $item ) {
1423                 if ( $menu_slug == $item[2] ) {
1424                         unset( $menu[$i] );
1425                         return $item;
1426                 }
1427         }
1428
1429         return false;
1430 }
1431
1432 /**
1433  * Remove an admin submenu.
1434  *
1435  * @since 3.1.0
1436  *
1437  * @global array $submenu
1438  *
1439  * @param string $menu_slug    The slug for the parent menu.
1440  * @param string $submenu_slug The slug of the submenu.
1441  * @return array|bool The removed submenu on success, false if not found.
1442  */
1443 function remove_submenu_page( $menu_slug, $submenu_slug ) {
1444         global $submenu;
1445
1446         if ( !isset( $submenu[$menu_slug] ) )
1447                 return false;
1448
1449         foreach ( $submenu[$menu_slug] as $i => $item ) {
1450                 if ( $submenu_slug == $item[2] ) {
1451                         unset( $submenu[$menu_slug][$i] );
1452                         return $item;
1453                 }
1454         }
1455
1456         return false;
1457 }
1458
1459 /**
1460  * Get the url to access a particular menu page based on the slug it was registered with.
1461  *
1462  * If the slug hasn't been registered properly no url will be returned
1463  *
1464  * @since 3.0.0
1465  *
1466  * @global array $_parent_pages
1467  *
1468  * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu)
1469  * @param bool $echo Whether or not to echo the url - default is true
1470  * @return string the url
1471  */
1472 function menu_page_url($menu_slug, $echo = true) {
1473         global $_parent_pages;
1474
1475         if ( isset( $_parent_pages[$menu_slug] ) ) {
1476                 $parent_slug = $_parent_pages[$menu_slug];
1477                 if ( $parent_slug && ! isset( $_parent_pages[$parent_slug] ) ) {
1478                         $url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) );
1479                 } else {
1480                         $url = admin_url( 'admin.php?page=' . $menu_slug );
1481                 }
1482         } else {
1483                 $url = '';
1484         }
1485
1486         $url = esc_url($url);
1487
1488         if ( $echo )
1489                 echo $url;
1490
1491         return $url;
1492 }
1493
1494 //
1495 // Pluggable Menu Support -- Private
1496 //
1497 /**
1498  *
1499  * @global string $parent_file
1500  * @global array $menu
1501  * @global array $submenu
1502  * @global string $pagenow
1503  * @global string $typenow
1504  * @global string $plugin_page
1505  * @global array $_wp_real_parent_file
1506  * @global array $_wp_menu_nopriv
1507  * @global array $_wp_submenu_nopriv
1508  */
1509 function get_admin_page_parent( $parent = '' ) {
1510         global $parent_file, $menu, $submenu, $pagenow, $typenow,
1511                 $plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv;
1512
1513         if ( !empty ( $parent ) && 'admin.php' != $parent ) {
1514                 if ( isset( $_wp_real_parent_file[$parent] ) )
1515                         $parent = $_wp_real_parent_file[$parent];
1516                 return $parent;
1517         }
1518
1519         if ( $pagenow == 'admin.php' && isset( $plugin_page ) ) {
1520                 foreach ( (array)$menu as $parent_menu ) {
1521                         if ( $parent_menu[2] == $plugin_page ) {
1522                                 $parent_file = $plugin_page;
1523                                 if ( isset( $_wp_real_parent_file[$parent_file] ) )
1524                                         $parent_file = $_wp_real_parent_file[$parent_file];
1525                                 return $parent_file;
1526                         }
1527                 }
1528                 if ( isset( $_wp_menu_nopriv[$plugin_page] ) ) {
1529                         $parent_file = $plugin_page;
1530                         if ( isset( $_wp_real_parent_file[$parent_file] ) )
1531                                         $parent_file = $_wp_real_parent_file[$parent_file];
1532                         return $parent_file;
1533                 }
1534         }
1535
1536         if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) ) {
1537                 $parent_file = $pagenow;
1538                 if ( isset( $_wp_real_parent_file[$parent_file] ) )
1539                         $parent_file = $_wp_real_parent_file[$parent_file];
1540                 return $parent_file;
1541         }
1542
1543         foreach (array_keys( (array)$submenu ) as $parent) {
1544                 foreach ( $submenu[$parent] as $submenu_array ) {
1545                         if ( isset( $_wp_real_parent_file[$parent] ) )
1546                                 $parent = $_wp_real_parent_file[$parent];
1547                         if ( !empty($typenow) && ($submenu_array[2] == "$pagenow?post_type=$typenow") ) {
1548                                 $parent_file = $parent;
1549                                 return $parent;
1550                         } elseif ( $submenu_array[2] == $pagenow && empty($typenow) && ( empty($parent_file) || false === strpos($parent_file, '?') ) ) {
1551                                 $parent_file = $parent;
1552                                 return $parent;
1553                         } elseif ( isset( $plugin_page ) && ($plugin_page == $submenu_array[2] ) ) {
1554                                 $parent_file = $parent;
1555                                 return $parent;
1556                         }
1557                 }
1558         }
1559
1560         if ( empty($parent_file) )
1561                 $parent_file = '';
1562         return '';
1563 }
1564
1565 /**
1566  *
1567  * @global string $title
1568  * @global array $menu
1569  * @global array $submenu
1570  * @global string $pagenow
1571  * @global string $plugin_page
1572  * @global string $typenow
1573  */
1574 function get_admin_page_title() {
1575         global $title, $menu, $submenu, $pagenow, $plugin_page, $typenow;
1576
1577         if ( ! empty ( $title ) )
1578                 return $title;
1579
1580         $hook = get_plugin_page_hook( $plugin_page, $pagenow );
1581
1582         $parent = $parent1 = get_admin_page_parent();
1583
1584         if ( empty ( $parent) ) {
1585                 foreach ( (array)$menu as $menu_array ) {
1586                         if ( isset( $menu_array[3] ) ) {
1587                                 if ( $menu_array[2] == $pagenow ) {
1588                                         $title = $menu_array[3];
1589                                         return $menu_array[3];
1590                                 } elseif ( isset( $plugin_page ) && ($plugin_page == $menu_array[2] ) && ($hook == $menu_array[3] ) ) {
1591                                         $title = $menu_array[3];
1592                                         return $menu_array[3];
1593                                 }
1594                         } else {
1595                                 $title = $menu_array[0];
1596                                 return $title;
1597                         }
1598                 }
1599         } else {
1600                 foreach ( array_keys( $submenu ) as $parent ) {
1601                         foreach ( $submenu[$parent] as $submenu_array ) {
1602                                 if ( isset( $plugin_page ) &&
1603                                         ( $plugin_page == $submenu_array[2] ) &&
1604                                         (
1605                                                 ( $parent == $pagenow ) ||
1606                                                 ( $parent == $plugin_page ) ||
1607                                                 ( $plugin_page == $hook ) ||
1608                                                 ( $pagenow == 'admin.php' && $parent1 != $submenu_array[2] ) ||
1609                                                 ( !empty($typenow) && $parent == $pagenow . '?post_type=' . $typenow)
1610                                         )
1611                                         ) {
1612                                                 $title = $submenu_array[3];
1613                                                 return $submenu_array[3];
1614                                         }
1615
1616                                 if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) // not the current page
1617                                         continue;
1618
1619                                 if ( isset( $submenu_array[3] ) ) {
1620                                         $title = $submenu_array[3];
1621                                         return $submenu_array[3];
1622                                 } else {
1623                                         $title = $submenu_array[0];
1624                                         return $title;
1625                                 }
1626                         }
1627                 }
1628                 if ( empty ( $title ) ) {
1629                         foreach ( $menu as $menu_array ) {
1630                                 if ( isset( $plugin_page ) &&
1631                                         ( $plugin_page == $menu_array[2] ) &&
1632                                         ( $pagenow == 'admin.php' ) &&
1633                                         ( $parent1 == $menu_array[2] ) )
1634                                         {
1635                                                 $title = $menu_array[3];
1636                                                 return $menu_array[3];
1637                                         }
1638                         }
1639                 }
1640         }
1641
1642         return $title;
1643 }
1644
1645 /**
1646  * @since 2.3.0
1647  *
1648  * @param string $plugin_page
1649  * @param string $parent_page
1650  * @return string|null
1651  */
1652 function get_plugin_page_hook( $plugin_page, $parent_page ) {
1653         $hook = get_plugin_page_hookname( $plugin_page, $parent_page );
1654         if ( has_action($hook) )
1655                 return $hook;
1656         else
1657                 return null;
1658 }
1659
1660 /**
1661  *
1662  * @global array $admin_page_hooks
1663  * @param string $plugin_page
1664  * @param string $parent_page
1665  */
1666 function get_plugin_page_hookname( $plugin_page, $parent_page ) {
1667         global $admin_page_hooks;
1668
1669         $parent = get_admin_page_parent( $parent_page );
1670
1671         $page_type = 'admin';
1672         if ( empty ( $parent_page ) || 'admin.php' == $parent_page || isset( $admin_page_hooks[$plugin_page] ) ) {
1673                 if ( isset( $admin_page_hooks[$plugin_page] ) ) {
1674                         $page_type = 'toplevel';
1675                 } elseif ( isset( $admin_page_hooks[$parent] )) {
1676                         $page_type = $admin_page_hooks[$parent];
1677                 }
1678         } elseif ( isset( $admin_page_hooks[$parent] ) ) {
1679                 $page_type = $admin_page_hooks[$parent];
1680         }
1681
1682         $plugin_name = preg_replace( '!\.php!', '', $plugin_page );
1683
1684         return $page_type . '_page_' . $plugin_name;
1685 }
1686
1687 /**
1688  *
1689  * @global string $pagenow
1690  * @global array $menu
1691  * @global array $submenu
1692  * @global array $_wp_menu_nopriv
1693  * @global array $_wp_submenu_nopriv
1694  * @global string $plugin_page
1695  * @global array $_registered_pages
1696  */
1697 function user_can_access_admin_page() {
1698         global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv,
1699                 $plugin_page, $_registered_pages;
1700
1701         $parent = get_admin_page_parent();
1702
1703         if ( !isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$parent][$pagenow] ) )
1704                 return false;
1705
1706         if ( isset( $plugin_page ) ) {
1707                 if ( isset( $_wp_submenu_nopriv[$parent][$plugin_page] ) )
1708                         return false;
1709
1710                 $hookname = get_plugin_page_hookname($plugin_page, $parent);
1711
1712                 if ( !isset($_registered_pages[$hookname]) )
1713                         return false;
1714         }
1715
1716         if ( empty( $parent) ) {
1717                 if ( isset( $_wp_menu_nopriv[$pagenow] ) )
1718                         return false;
1719                 if ( isset( $_wp_submenu_nopriv[$pagenow][$pagenow] ) )
1720                         return false;
1721                 if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) )
1722                         return false;
1723                 if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[$plugin_page] ) )
1724                         return false;
1725                 foreach (array_keys( $_wp_submenu_nopriv ) as $key ) {
1726                         if ( isset( $_wp_submenu_nopriv[$key][$pagenow] ) )
1727                                 return false;
1728                         if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$key][$plugin_page] ) )
1729                         return false;
1730                 }
1731                 return true;
1732         }
1733
1734         if ( isset( $plugin_page ) && ( $plugin_page == $parent ) && isset( $_wp_menu_nopriv[$plugin_page] ) )
1735                 return false;
1736
1737         if ( isset( $submenu[$parent] ) ) {
1738                 foreach ( $submenu[$parent] as $submenu_array ) {
1739                         if ( isset( $plugin_page ) && ( $submenu_array[2] == $plugin_page ) ) {
1740                                 if ( current_user_can( $submenu_array[1] ))
1741                                         return true;
1742                                 else
1743                                         return false;
1744                         } elseif ( $submenu_array[2] == $pagenow ) {
1745                                 if ( current_user_can( $submenu_array[1] ))
1746                                         return true;
1747                                 else
1748                                         return false;
1749                         }
1750                 }
1751         }
1752
1753         foreach ( $menu as $menu_array ) {
1754                 if ( $menu_array[2] == $parent) {
1755                         if ( current_user_can( $menu_array[1] ))
1756                                 return true;
1757                         else
1758                                 return false;
1759                 }
1760         }
1761
1762         return true;
1763 }
1764
1765 /* Whitelist functions */
1766
1767 /**
1768  * Register a setting and its sanitization callback
1769  *
1770  * @since 2.7.0
1771  *
1772  * @global array $new_whitelist_options
1773  *
1774  * @param string $option_group A settings group name. Should correspond to a whitelisted option key name.
1775  *      Default whitelisted option key names include "general," "discussion," and "reading," among others.
1776  * @param string $option_name The name of an option to sanitize and save.
1777  * @param callable $sanitize_callback A callback function that sanitizes the option's value.
1778  */
1779 function register_setting( $option_group, $option_name, $sanitize_callback = '' ) {
1780         global $new_whitelist_options;
1781
1782         if ( 'misc' == $option_group ) {
1783                 _deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
1784                 $option_group = 'general';
1785         }
1786
1787         if ( 'privacy' == $option_group ) {
1788                 _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
1789                 $option_group = 'reading';
1790         }
1791
1792         $new_whitelist_options[ $option_group ][] = $option_name;
1793         if ( $sanitize_callback != '' )
1794                 add_filter( "sanitize_option_{$option_name}", $sanitize_callback );
1795 }
1796
1797 /**
1798  * Unregister a setting
1799  *
1800  * @since 2.7.0
1801  *
1802  * @global array $new_whitelist_options
1803  *
1804  * @param string   $option_group
1805  * @param string   $option_name
1806  * @param callable $sanitize_callback
1807  */
1808 function unregister_setting( $option_group, $option_name, $sanitize_callback = '' ) {
1809         global $new_whitelist_options;
1810
1811         if ( 'misc' == $option_group ) {
1812                 _deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
1813                 $option_group = 'general';
1814         }
1815
1816         if ( 'privacy' == $option_group ) {
1817                 _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
1818                 $option_group = 'reading';
1819         }
1820
1821         $pos = array_search( $option_name, (array) $new_whitelist_options[ $option_group ] );
1822         if ( $pos !== false )
1823                 unset( $new_whitelist_options[ $option_group ][ $pos ] );
1824         if ( $sanitize_callback != '' )
1825                 remove_filter( "sanitize_option_{$option_name}", $sanitize_callback );
1826 }
1827
1828 /**
1829  * Refreshes the value of the options whitelist available via the 'whitelist_options' hook.
1830  *
1831  * See the {@see 'whitelist_options'} filter.
1832  *
1833  * @since 2.7.0
1834  *
1835  * @global array $new_whitelist_options
1836  *
1837  * @param array $options
1838  * @return array
1839  */
1840 function option_update_filter( $options ) {
1841         global $new_whitelist_options;
1842
1843         if ( is_array( $new_whitelist_options ) )
1844                 $options = add_option_whitelist( $new_whitelist_options, $options );
1845
1846         return $options;
1847 }
1848
1849 /**
1850  * Adds an array of options to the options whitelist.
1851  *
1852  * @since 2.7.0
1853  *
1854  * @global array $whitelist_options
1855  *
1856  * @param array        $new_options
1857  * @param string|array $options
1858  * @return array
1859  */
1860 function add_option_whitelist( $new_options, $options = '' ) {
1861         if ( $options == '' )
1862                 global $whitelist_options;
1863         else
1864                 $whitelist_options = $options;
1865
1866         foreach ( $new_options as $page => $keys ) {
1867                 foreach ( $keys as $key ) {
1868                         if ( !isset($whitelist_options[ $page ]) || !is_array($whitelist_options[ $page ]) ) {
1869                                 $whitelist_options[ $page ] = array();
1870                                 $whitelist_options[ $page ][] = $key;
1871                         } else {
1872                                 $pos = array_search( $key, $whitelist_options[ $page ] );
1873                                 if ( $pos === false )
1874                                         $whitelist_options[ $page ][] = $key;
1875                         }
1876                 }
1877         }
1878
1879         return $whitelist_options;
1880 }
1881
1882 /**
1883  * Removes a list of options from the options whitelist.
1884  *
1885  * @since 2.7.0
1886  *
1887  * @global array $whitelist_options
1888  *
1889  * @param array        $del_options
1890  * @param string|array $options
1891  * @return array
1892  */
1893 function remove_option_whitelist( $del_options, $options = '' ) {
1894         if ( $options == '' )
1895                 global $whitelist_options;
1896         else
1897                 $whitelist_options = $options;
1898
1899         foreach ( $del_options as $page => $keys ) {
1900                 foreach ( $keys as $key ) {
1901                         if ( isset($whitelist_options[ $page ]) && is_array($whitelist_options[ $page ]) ) {
1902                                 $pos = array_search( $key, $whitelist_options[ $page ] );
1903                                 if ( $pos !== false )
1904                                         unset( $whitelist_options[ $page ][ $pos ] );
1905                         }
1906                 }
1907         }
1908
1909         return $whitelist_options;
1910 }
1911
1912 /**
1913  * Output nonce, action, and option_page fields for a settings page.
1914  *
1915  * @since 2.7.0
1916  *
1917  * @param string $option_group A settings group name. This should match the group name used in register_setting().
1918  */
1919 function settings_fields($option_group) {
1920         echo "<input type='hidden' name='option_page' value='" . esc_attr($option_group) . "' />";
1921         echo '<input type="hidden" name="action" value="update" />';
1922         wp_nonce_field("$option_group-options");
1923 }
1924
1925 /**
1926  * Clears the Plugins cache used by get_plugins() and by default, the Plugin Update cache.
1927  *
1928  * @since 3.7.0
1929  *
1930  * @param bool $clear_update_cache Whether to clear the Plugin updates cache
1931  */
1932 function wp_clean_plugins_cache( $clear_update_cache = true ) {
1933         if ( $clear_update_cache )
1934                 delete_site_transient( 'update_plugins' );
1935         wp_cache_delete( 'plugins', 'plugins' );
1936 }
1937
1938 /**
1939  * @param string $plugin
1940  */
1941 function plugin_sandbox_scrape( $plugin ) {
1942         wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
1943         include( WP_PLUGIN_DIR . '/' . $plugin );
1944 }