Plugins: Add support for `Update URI` header.
This allows third-party plugins to avoid accidentally being overwritten with an update of a plugin of a similar name from the WordPress.org Plugin Directory. Additionally, introduce the `update_plugins_{$hostname}` filter, which third-party plugins can use to offer updates for a given hostname. If set, the `Update URI` header field should be a URI and have a unique hostname. Some examples include: * `https://wordpress.org/plugins/example-plugin/` * `https://example.com/my-plugin/` * `my-custom-plugin-name` `Update URI: false` also works, and unless there is code handling the `false` hostname, the plugin will never get an update notification. If the header is present, the WordPress.org API will currently only return updates for the plugin if it matches the following format: * `https://wordpress.org/plugins/{$slug}/` * `w.org/plugin/{$slug}` If the header has any other value, the API will not return a result and will ignore the plugin for update purposes. Props dd32, DavidAnderson, meloniq, markjaquith, DrewAPicture, mweichert, design_dolphin, filosofo, sean212, nhuja, JeroenReumkens, infolu, dingdang, joyously, earnjam, williampatton, grapplerulrich, markparnell, apedog, afragen, miqrogroove, rmccue, crazycoders, jdgrimes, damonganto, joostdevalk, jorbin, georgestephanis, khromov, GeekStreetWP, jb510, Rarst, juliobox, Ipstenu, mikejolley, Otto42, gMagicScott, TJNowell, GaryJ, knutsp, mordauk, nvartolomei, aspexi, chriscct7, benoitchantre, ryno267, lev0, gregorlove, dougwollison, SergeyBiryukov. See #14179, #23318, #32101. Built from https://develop.svn.wordpress.org/trunk@50921 git-svn-id: http://core.svn.wordpress.org/trunk@50530 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
ff46fda58d
commit
9fbc705dc0
|
@ -47,15 +47,19 @@ class WP_Plugin_Install_List_Table extends WP_List_Table {
|
|||
$plugin_info = get_site_transient( 'update_plugins' );
|
||||
if ( isset( $plugin_info->no_update ) ) {
|
||||
foreach ( $plugin_info->no_update as $plugin ) {
|
||||
$plugin->upgrade = false;
|
||||
$plugins[ $plugin->slug ] = $plugin;
|
||||
if ( isset( $plugin->slug ) ) {
|
||||
$plugin->upgrade = false;
|
||||
$plugins[ $plugin->slug ] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $plugin_info->response ) ) {
|
||||
foreach ( $plugin_info->response as $plugin ) {
|
||||
$plugin->upgrade = true;
|
||||
$plugins[ $plugin->slug ] = $plugin;
|
||||
if ( isset( $plugin->slug ) ) {
|
||||
$plugin->upgrade = true;
|
||||
$plugins[ $plugin->slug ] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
*
|
||||
* @since 1.5.0
|
||||
* @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers.
|
||||
* @since 5.8.0 Added support for `Update URI` header.
|
||||
*
|
||||
* @param string $plugin_file Absolute path to the main plugin file.
|
||||
* @param bool $markup Optional. If the returned data should have HTML markup applied.
|
||||
|
@ -63,6 +64,7 @@
|
|||
* @type bool $Network Whether the plugin can only be activated network-wide.
|
||||
* @type string $RequiresWP Minimum required version of WordPress.
|
||||
* @type string $RequiresPHP Minimum required version of PHP.
|
||||
* @type string $UpdateURI ID of the plugin for update purposes, should be a URI.
|
||||
* }
|
||||
*/
|
||||
function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
|
||||
|
@ -79,6 +81,7 @@ function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
|
|||
'Network' => 'Network',
|
||||
'RequiresWP' => 'Requires at least',
|
||||
'RequiresPHP' => 'Requires PHP',
|
||||
'UpdateURI' => 'Update URI',
|
||||
// Site Wide Only is deprecated in favor of Network.
|
||||
'_sitewide' => 'Site Wide Only',
|
||||
);
|
||||
|
|
|
@ -435,7 +435,24 @@ function wp_plugin_update_row( $file, $plugin_data ) {
|
|||
);
|
||||
|
||||
$plugin_name = wp_kses( $plugin_data['Name'], $plugins_allowedtags );
|
||||
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $response->slug . '§ion=changelog&TB_iframe=true&width=600&height=800' );
|
||||
$plugin_slug = isset( $response->slug ) ? $response->slug : $response->id;
|
||||
|
||||
if ( isset( $response->slug ) ) {
|
||||
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '§ion=changelog' );
|
||||
} elseif ( isset( $response->url ) ) {
|
||||
$details_url = $response->url;
|
||||
} else {
|
||||
$details_url = $plugin_data['PluginURI'];
|
||||
}
|
||||
|
||||
$details_url = add_query_arg(
|
||||
array(
|
||||
'TB_iframe' => 'true',
|
||||
'width' => 600,
|
||||
'height' => 800,
|
||||
),
|
||||
$details_url
|
||||
);
|
||||
|
||||
/** @var WP_Plugins_List_Table $wp_list_table */
|
||||
$wp_list_table = _get_list_table(
|
||||
|
@ -461,8 +478,8 @@ function wp_plugin_update_row( $file, $plugin_data ) {
|
|||
'<td colspan="%s" class="plugin-update colspanchange">' .
|
||||
'<div class="update-message notice inline %s notice-alt"><p>',
|
||||
$active_class,
|
||||
esc_attr( $response->slug . '-update' ),
|
||||
esc_attr( $response->slug ),
|
||||
esc_attr( $plugin_slug . '-update' ),
|
||||
esc_attr( $plugin_slug ),
|
||||
esc_attr( $file ),
|
||||
esc_attr( $wp_list_table->get_column_count() ),
|
||||
$notice_type
|
||||
|
|
|
@ -296,8 +296,11 @@ function wp_update_plugins( $extra_stats = array() ) {
|
|||
$current = new stdClass;
|
||||
}
|
||||
|
||||
$new_option = new stdClass;
|
||||
$new_option->last_checked = time();
|
||||
$updates = new stdClass;
|
||||
$updates->last_checked = time();
|
||||
$updates->response = array();
|
||||
$updates->translations = array();
|
||||
$updates->no_update = array();
|
||||
|
||||
$doing_cron = wp_doing_cron();
|
||||
|
||||
|
@ -327,7 +330,7 @@ function wp_update_plugins( $extra_stats = array() ) {
|
|||
$plugin_changed = false;
|
||||
|
||||
foreach ( $plugins as $file => $p ) {
|
||||
$new_option->checked[ $file ] = $p['Version'];
|
||||
$updates->checked[ $file ] = $p['Version'];
|
||||
|
||||
if ( ! isset( $current->checked[ $file ] ) || (string) $current->checked[ $file ] !== (string) $p['Version'] ) {
|
||||
$plugin_changed = true;
|
||||
|
@ -418,38 +421,114 @@ function wp_update_plugins( $extra_stats = array() ) {
|
|||
|
||||
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
|
||||
|
||||
foreach ( $response['plugins'] as &$plugin ) {
|
||||
$plugin = (object) $plugin;
|
||||
if ( $response && is_array( $response ) ) {
|
||||
$updates->response = $response['plugins'];
|
||||
$updates->translations = $response['translations'];
|
||||
$updates->no_update = $response['no_update'];
|
||||
}
|
||||
|
||||
if ( isset( $plugin->compatibility ) ) {
|
||||
$plugin->compatibility = (object) $plugin->compatibility;
|
||||
// Support updates for any plugins using the `Update URI` header field.
|
||||
foreach ( $plugins as $plugin_file => $plugin_data ) {
|
||||
if ( ! $plugin_data['UpdateURI'] || isset( $updates->response[ $plugin_file ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $plugin->compatibility as &$data ) {
|
||||
$data = (object) $data;
|
||||
$hostname = wp_parse_url( esc_url_raw( $plugin_data['UpdateURI'] ), PHP_URL_HOST );
|
||||
|
||||
/**
|
||||
* Filters the update response for a given plugin hostname.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$hostname`, refers to the hostname
|
||||
* of the URI specified in the `Update URI` header field.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param array|false $update {
|
||||
* The plugin update data with the latest details. Default false.
|
||||
*
|
||||
* @type string $id Optional. ID of the plugin for update purposes, should be a URI
|
||||
* specified in the `Update URI` header field.
|
||||
* @type string $slug Slug of the plugin.
|
||||
* @type string $version The version of the plugin.
|
||||
* @type string $url The URL for details of the plugin.
|
||||
* @type string $package Optional. The update ZIP for the plugin.
|
||||
* @type string $tested Optional. The version of WordPress the plugin is tested against.
|
||||
* @type string $requires_php Optional. The version of PHP which the plugin requires.
|
||||
* @type bool $autoupdate Optional. Whether the plugin should automatically update.
|
||||
* @type array $icons Optional. Array of plugin icons.
|
||||
* @type array $banners Optional. Array of plugin banners.
|
||||
* @type array $banners_rtl Optional. Array of plugin RTL banners.
|
||||
* @type array $translations {
|
||||
* Optional. List of translation updates for the plugin.
|
||||
*
|
||||
* @type string $language The language the translation update is for.
|
||||
* @type string $version The version of the plugin this translation is for.
|
||||
* This is not the version of the language file.
|
||||
* @type string $updated The update timestamp of the translation file.
|
||||
* Should be a date in the `YYYY-MM-DD HH:MM:SS` format.
|
||||
* @type string $package The ZIP location containing the translation update.
|
||||
* @type string $autoupdate Whether the translation should be automatically installed.
|
||||
* }
|
||||
* }
|
||||
* @param array $plugin_data Plugin headers.
|
||||
* @param string $plugin_file Plugin filename.
|
||||
* @param array $locales Installed locales to look translations for.
|
||||
*/
|
||||
$update = apply_filters( "update_plugins_{$hostname}", false, $plugin_data, $plugin_file, $locales );
|
||||
|
||||
if ( ! $update ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$update = (object) $update;
|
||||
|
||||
// Is it valid? We require at least a version.
|
||||
if ( ! isset( $update->version ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// These should remain constant.
|
||||
$update->id = $plugin_data['UpdateURI'];
|
||||
$update->plugin = $plugin_file;
|
||||
|
||||
// WordPress needs the version field specified as 'new_version'.
|
||||
if ( ! isset( $update->new_version ) ) {
|
||||
$update->new_version = $update->version;
|
||||
}
|
||||
|
||||
// Handle any translation updates.
|
||||
if ( ! empty( $update->translations ) ) {
|
||||
foreach ( $update->translations as $translation ) {
|
||||
if ( isset( $translation['language'], $translation['package'] ) ) {
|
||||
$translation['type'] = 'plugin';
|
||||
$translation['slug'] = isset( $update->slug ) ? $update->slug : $update->id;
|
||||
|
||||
$updates->translations[] = $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset( $updates->no_update[ $plugin_file ], $updates->response[ $plugin_file ] );
|
||||
|
||||
if ( version_compare( $update->new_version, $plugin_data['Version'], '>' ) ) {
|
||||
$updates->response[ $plugin_file ] = $update;
|
||||
} else {
|
||||
$updates->no_update[ $plugin_file ] = $update;
|
||||
}
|
||||
}
|
||||
|
||||
unset( $plugin, $data );
|
||||
$sanitize_plugin_update_payload = function( &$item ) {
|
||||
$item = (object) $item;
|
||||
|
||||
foreach ( $response['no_update'] as &$plugin ) {
|
||||
$plugin = (object) $plugin;
|
||||
}
|
||||
unset( $item->translations, $item->compatibility );
|
||||
|
||||
unset( $plugin );
|
||||
return $item;
|
||||
};
|
||||
|
||||
if ( is_array( $response ) ) {
|
||||
$new_option->response = $response['plugins'];
|
||||
$new_option->translations = $response['translations'];
|
||||
// TODO: Perhaps better to store no_update in a separate transient with an expiry?
|
||||
$new_option->no_update = $response['no_update'];
|
||||
} else {
|
||||
$new_option->response = array();
|
||||
$new_option->translations = array();
|
||||
$new_option->no_update = array();
|
||||
}
|
||||
array_walk( $updates->response, $sanitize_plugin_update_payload );
|
||||
array_walk( $updates->no_update, $sanitize_plugin_update_payload );
|
||||
|
||||
set_site_transient( 'update_plugins', $new_option );
|
||||
set_site_transient( 'update_plugins', $updates );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '5.8-alpha-50920';
|
||||
$wp_version = '5.8-alpha-50921';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
|
Loading…
Reference in New Issue