~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-admin/includes/plugin.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

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