Skip to content

Commit b67c76e

Browse files
committed
Plugins: Load wp-admin/includes/plugin.php earlier.
Partially reverts [59479] and [59461], which previously tried to move some functions from `wp-admin/includes/plugin.php` to `wp-includes/functions.php` so they are available early, so that `get_plugin_data()` can be used. However, other functions from that file are often used by plugins without necessarily checking whether they are available, easily causing fatal errors. Requiring this file directly is a safer approach to avoid such errors. Props peterwilsoncc, dd32, swissspidy, johnbillion. Fixes #62244. git-svn-id: https://develop.svn.wordpress.org/trunk@59488 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 7d3ce7a commit b67c76e

File tree

3 files changed

+309
-306
lines changed

3 files changed

+309
-306
lines changed

src/wp-admin/includes/plugin.php

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,221 @@
66
* @subpackage Administration
77
*/
88

9+
/**
10+
* Parses the plugin contents to retrieve plugin's metadata.
11+
*
12+
* All plugin headers must be on their own line. Plugin description must not have
13+
* any newlines, otherwise only parts of the description will be displayed.
14+
* The below is formatted for printing.
15+
*
16+
* /*
17+
* Plugin Name: Name of the plugin.
18+
* Plugin URI: The home page of the plugin.
19+
* Description: Plugin description.
20+
* Author: Plugin author's name.
21+
* Author URI: Link to the author's website.
22+
* Version: Plugin version.
23+
* Text Domain: Optional. Unique identifier, should be same as the one used in
24+
* load_plugin_textdomain().
25+
* Domain Path: Optional. Only useful if the translations are located in a
26+
* folder above the plugin's base path. For example, if .mo files are
27+
* located in the locale folder then Domain Path will be "/locale/" and
28+
* must have the first slash. Defaults to the base folder the plugin is
29+
* located in.
30+
* Network: Optional. Specify "Network: true" to require that a plugin is activated
31+
* across all sites in an installation. This will prevent a plugin from being
32+
* activated on a single site when Multisite is enabled.
33+
* Requires at least: Optional. Specify the minimum required WordPress version.
34+
* Requires PHP: Optional. Specify the minimum required PHP version.
35+
* * / # Remove the space to close comment.
36+
*
37+
* The first 8 KB of the file will be pulled in and if the plugin data is not
38+
* within that first 8 KB, then the plugin author should correct their plugin
39+
* and move the plugin data headers to the top.
40+
*
41+
* The plugin file is assumed to have permissions to allow for scripts to read
42+
* the file. This is not checked however and the file is only opened for
43+
* reading.
44+
*
45+
* @since 1.5.0
46+
* @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers.
47+
* @since 5.8.0 Added support for `Update URI` header.
48+
* @since 6.5.0 Added support for `Requires Plugins` header.
49+
*
50+
* @param string $plugin_file Absolute path to the main plugin file.
51+
* @param bool $markup Optional. If the returned data should have HTML markup applied.
52+
* Default true.
53+
* @param bool $translate Optional. If the returned data should be translated. Default true.
54+
* @return array {
55+
* Plugin data. Values will be empty if not supplied by the plugin.
56+
*
57+
* @type string $Name Name of the plugin. Should be unique.
58+
* @type string $PluginURI Plugin URI.
59+
* @type string $Version Plugin version.
60+
* @type string $Description Plugin description.
61+
* @type string $Author Plugin author's name.
62+
* @type string $AuthorURI Plugin author's website address (if set).
63+
* @type string $TextDomain Plugin textdomain.
64+
* @type string $DomainPath Plugin's relative directory path to .mo files.
65+
* @type bool $Network Whether the plugin can only be activated network-wide.
66+
* @type string $RequiresWP Minimum required version of WordPress.
67+
* @type string $RequiresPHP Minimum required version of PHP.
68+
* @type string $UpdateURI ID of the plugin for update purposes, should be a URI.
69+
* @type string $RequiresPlugins Comma separated list of dot org plugin slugs.
70+
* @type string $Title Title of the plugin and link to the plugin's site (if set).
71+
* @type string $AuthorName Plugin author's name.
72+
* }
73+
*/
74+
function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
75+
76+
$default_headers = array(
77+
'Name' => 'Plugin Name',
78+
'PluginURI' => 'Plugin URI',
79+
'Version' => 'Version',
80+
'Description' => 'Description',
81+
'Author' => 'Author',
82+
'AuthorURI' => 'Author URI',
83+
'TextDomain' => 'Text Domain',
84+
'DomainPath' => 'Domain Path',
85+
'Network' => 'Network',
86+
'RequiresWP' => 'Requires at least',
87+
'RequiresPHP' => 'Requires PHP',
88+
'UpdateURI' => 'Update URI',
89+
'RequiresPlugins' => 'Requires Plugins',
90+
// Site Wide Only is deprecated in favor of Network.
91+
'_sitewide' => 'Site Wide Only',
92+
);
93+
94+
$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
95+
96+
// Site Wide Only is the old header for Network.
97+
if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) {
98+
/* translators: 1: Site Wide Only: true, 2: Network: true */
99+
_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), '<code>Site Wide Only: true</code>', '<code>Network: true</code>' ) );
100+
$plugin_data['Network'] = $plugin_data['_sitewide'];
101+
}
102+
$plugin_data['Network'] = ( 'true' === strtolower( $plugin_data['Network'] ) );
103+
unset( $plugin_data['_sitewide'] );
104+
105+
// If no text domain is defined fall back to the plugin slug.
106+
if ( ! $plugin_data['TextDomain'] ) {
107+
$plugin_slug = dirname( plugin_basename( $plugin_file ) );
108+
if ( '.' !== $plugin_slug && ! str_contains( $plugin_slug, '/' ) ) {
109+
$plugin_data['TextDomain'] = $plugin_slug;
110+
}
111+
}
112+
113+
if ( $markup || $translate ) {
114+
$plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate );
115+
} else {
116+
$plugin_data['Title'] = $plugin_data['Name'];
117+
$plugin_data['AuthorName'] = $plugin_data['Author'];
118+
}
119+
120+
return $plugin_data;
121+
}
122+
123+
/**
124+
* Sanitizes plugin data, optionally adds markup, optionally translates.
125+
*
126+
* @since 2.7.0
127+
*
128+
* @see get_plugin_data()
129+
*
130+
* @access private
131+
*
132+
* @param string $plugin_file Path to the main plugin file.
133+
* @param array $plugin_data An array of plugin data. See get_plugin_data().
134+
* @param bool $markup Optional. If the returned data should have HTML markup applied.
135+
* Default true.
136+
* @param bool $translate Optional. If the returned data should be translated. Default true.
137+
* @return array Plugin data. Values will be empty if not supplied by the plugin.
138+
* See get_plugin_data() for the list of possible values.
139+
*/
140+
function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) {
141+
142+
// Sanitize the plugin filename to a WP_PLUGIN_DIR relative path.
143+
$plugin_file = plugin_basename( $plugin_file );
144+
145+
// Translate fields.
146+
if ( $translate ) {
147+
$textdomain = $plugin_data['TextDomain'];
148+
if ( $textdomain ) {
149+
if ( ! is_textdomain_loaded( $textdomain ) ) {
150+
if ( $plugin_data['DomainPath'] ) {
151+
load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] );
152+
} else {
153+
load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) );
154+
}
155+
}
156+
} elseif ( 'hello.php' === basename( $plugin_file ) ) {
157+
$textdomain = 'default';
158+
}
159+
if ( $textdomain ) {
160+
foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field ) {
161+
if ( ! empty( $plugin_data[ $field ] ) ) {
162+
// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
163+
$plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain );
164+
}
165+
}
166+
}
167+
}
168+
169+
// Sanitize fields.
170+
$allowed_tags_in_links = array(
171+
'abbr' => array( 'title' => true ),
172+
'acronym' => array( 'title' => true ),
173+
'code' => true,
174+
'em' => true,
175+
'strong' => true,
176+
);
177+
178+
$allowed_tags = $allowed_tags_in_links;
179+
$allowed_tags['a'] = array(
180+
'href' => true,
181+
'title' => true,
182+
);
183+
184+
/*
185+
* Name is marked up inside <a> tags. Don't allow these.
186+
* Author is too, but some plugins have used <a> here (omitting Author URI).
187+
*/
188+
$plugin_data['Name'] = wp_kses( $plugin_data['Name'], $allowed_tags_in_links );
189+
$plugin_data['Author'] = wp_kses( $plugin_data['Author'], $allowed_tags );
190+
191+
$plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags );
192+
$plugin_data['Version'] = wp_kses( $plugin_data['Version'], $allowed_tags );
193+
194+
$plugin_data['PluginURI'] = esc_url( $plugin_data['PluginURI'] );
195+
$plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] );
196+
197+
$plugin_data['Title'] = $plugin_data['Name'];
198+
$plugin_data['AuthorName'] = $plugin_data['Author'];
199+
200+
// Apply markup.
201+
if ( $markup ) {
202+
if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) {
203+
$plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>';
204+
}
205+
206+
if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] ) {
207+
$plugin_data['Author'] = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
208+
}
209+
210+
$plugin_data['Description'] = wptexturize( $plugin_data['Description'] );
211+
212+
if ( $plugin_data['Author'] ) {
213+
$plugin_data['Description'] .= sprintf(
214+
/* translators: %s: Plugin author. */
215+
' <cite>' . __( 'By %s.' ) . '</cite>',
216+
$plugin_data['Author']
217+
);
218+
}
219+
}
220+
221+
return $plugin_data;
222+
}
223+
9224
/**
10225
* Gets a list of a plugin's files.
11226
*
@@ -304,6 +519,97 @@ function _get_dropins() {
304519
return $dropins;
305520
}
306521

522+
/**
523+
* Determines whether a plugin is active.
524+
*
525+
* Only plugins installed in the plugins/ folder can be active.
526+
*
527+
* Plugins in the mu-plugins/ folder can't be "activated," so this function will
528+
* return false for those plugins.
529+
*
530+
* For more information on this and similar theme functions, check out
531+
* the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
532+
* Conditional Tags} article in the Theme Developer Handbook.
533+
*
534+
* @since 2.5.0
535+
*
536+
* @param string $plugin Path to the plugin file relative to the plugins directory.
537+
* @return bool True, if in the active plugins list. False, not in the list.
538+
*/
539+
function is_plugin_active( $plugin ) {
540+
return in_array( $plugin, (array) get_option( 'active_plugins', array() ), true ) || is_plugin_active_for_network( $plugin );
541+
}
542+
543+
/**
544+
* Determines whether the plugin is inactive.
545+
*
546+
* Reverse of is_plugin_active(). Used as a callback.
547+
*
548+
* For more information on this and similar theme functions, check out
549+
* the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
550+
* Conditional Tags} article in the Theme Developer Handbook.
551+
*
552+
* @since 3.1.0
553+
*
554+
* @see is_plugin_active()
555+
*
556+
* @param string $plugin Path to the plugin file relative to the plugins directory.
557+
* @return bool True if inactive. False if active.
558+
*/
559+
function is_plugin_inactive( $plugin ) {
560+
return ! is_plugin_active( $plugin );
561+
}
562+
563+
/**
564+
* Determines whether the plugin is active for the entire network.
565+
*
566+
* Only plugins installed in the plugins/ folder can be active.
567+
*
568+
* Plugins in the mu-plugins/ folder can't be "activated," so this function will
569+
* return false for those plugins.
570+
*
571+
* For more information on this and similar theme functions, check out
572+
* the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
573+
* Conditional Tags} article in the Theme Developer Handbook.
574+
*
575+
* @since 3.0.0
576+
*
577+
* @param string $plugin Path to the plugin file relative to the plugins directory.
578+
* @return bool True if active for the network, otherwise false.
579+
*/
580+
function is_plugin_active_for_network( $plugin ) {
581+
if ( ! is_multisite() ) {
582+
return false;
583+
}
584+
585+
$plugins = get_site_option( 'active_sitewide_plugins' );
586+
if ( isset( $plugins[ $plugin ] ) ) {
587+
return true;
588+
}
589+
590+
return false;
591+
}
592+
593+
/**
594+
* Checks for "Network: true" in the plugin header to see if this should
595+
* be activated only as a network wide plugin. The plugin would also work
596+
* when Multisite is not enabled.
597+
*
598+
* Checks for "Site Wide Only: true" for backward compatibility.
599+
*
600+
* @since 3.0.0
601+
*
602+
* @param string $plugin Path to the plugin file relative to the plugins directory.
603+
* @return bool True if plugin is network only, false otherwise.
604+
*/
605+
function is_network_only_plugin( $plugin ) {
606+
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
607+
if ( $plugin_data ) {
608+
return $plugin_data['Network'];
609+
}
610+
return false;
611+
}
612+
307613
/**
308614
* Attempts activation of plugin in a "sandbox" and redirects on success.
309615
*

0 commit comments

Comments
 (0)