From 17bbfe2622c9b57de9673e4255765b0b256b101d Mon Sep 17 00:00:00 2001
From: David Levine
Date: Sat, 24 Jun 2023 22:31:46 +0000
Subject: [PATCH 1/5] dev: scaffold Updater PHP
---
src/Admin/Updater.php | 411 ++++++++++++++++++++++++++++++++++++++++++
src/class-acf.php | 39 ++--
2 files changed, 431 insertions(+), 19 deletions(-)
create mode 100644 src/Admin/Updater.php
diff --git a/src/Admin/Updater.php b/src/Admin/Updater.php
new file mode 100644
index 0000000..05a9f1e
--- /dev/null
+++ b/src/Admin/Updater.php
@@ -0,0 +1,411 @@
+plugin_config = [
+ 'plugin_file' => WPGRAPHQL_ACF_PLUGIN_FILE,
+ 'slug' => self::PLUGIN_SLUG,
+ 'proper_folder_name' => self::PLUGIN_SLUG,
+ 'api_url' => 'https://api.wordpress.org/plugins/info/1.0/' . self::PLUGIN_SLUG . '.json',
+ 'repo_url' => 'https://wordpress.org/plugins/' . self::PLUGIN_SLUG,
+ ];
+ }
+
+ /**
+ * Sets up the hooks.
+ */
+ public function init() : void {
+ add_filter( 'auto_update_plugin', [ $this, 'disable_autoupdate' ], 10, 2 );
+ add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'api_check' ] );
+ add_filter( 'plugins_api_result', [ $this, 'api_result' ], 10, 3 );
+ add_filter( 'upgrader_source_selection', [ $this, 'upgrader_source_selection' ], 10, 2 );
+ }
+
+ /**
+ * Disable auto updates for major releases of this plugin.
+ *
+ * @param bool|null $update Whether to update.
+ * @param object $item The plugin object.
+ *
+ * @return bool|null
+ */
+ public function disable_autoupdate( $update, $item ) {
+ // Return early if this is not our plugin.
+ if ( $item->slug !== $this->plugin_config['slug'] ) {
+ return $update;
+ }
+
+ // Bail if there's no new version.
+ if ( empty( $item->new_version ) ) {
+ return $update;
+ }
+
+ // Get the update type.
+ $update_type = self::get_semver_update_type( $item->new_version, WPGRAPHQL_ACF_VERSION );
+
+ // Non-'major' updates are allowed.
+ if ( 'major' !== $update_type ) {
+ return $update;
+ }
+
+ // Major updates should never happen automatically.
+ return false;
+ }
+
+ /**
+ * Hooks into the plugin upgrader to get the correct plugin version we want to install.
+ *
+ * @param object $transient The plugin upgrader transient.
+ *
+ * @return object
+ */
+ public function api_check( $transient ) {
+ // Clear the transient.
+ delete_site_transient( self::VERSION_TRANSIENT );
+
+ // Get the latest version we allow to be installed from this version.
+ // In the new codebase, we'll just do this on semver-autoupdates.
+ $plugin_data = $this->get_plugin_data();
+ $version = $plugin_data['Version'];
+ $new_version = $this->get_latest_version();
+
+ // Check if this is a version update.
+ $is_update = version_compare( $new_version, $version, '>' );
+
+ if ( ! $is_update ) {
+ return $transient;
+ }
+
+ // Get the download URL for reuse.
+ $download_url = $this->get_download_url( $new_version );
+
+ // Populate the transient data.
+ if ( ! isset( $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ] ) ) {
+ $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ] = (object) $this->plugin_config;
+ }
+
+ $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->new_version = $new_version;
+ $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->package = $download_url;
+ $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->zip_url = $download_url;
+
+ return $transient;
+ }
+
+ /**
+ * Filters the Installation API response result
+ *
+ * @param object|\WP_Error $response The API response object.
+ * @param string $action The type of information being requested from the Plugin Installation API.
+ * @param object $args Plugin API arguments.
+ *
+ * @return object|\WP_Error
+ */
+ public function api_result( $response, $action, $args ) {
+ // Bail if this is not checking our plugin.
+ if ( ! isset( $args->slug ) || $args->slug !== $this->plugin_config['slug'] ) {
+ return $response;
+ }
+
+ // Get the latest version.
+ $new_version = $this->get_latest_version();
+
+ // Bail if the version is not newer.
+ if ( version_compare( $new_version, $args->version, '<=' ) ) {
+ return $response;
+ }
+
+ // If we're returning a different version than the latest from WP.org, override the response.
+ $response->version = $new_version;
+ $response->download_link = $this->get_download_url( $new_version );
+
+ // If this is a major update, add a warning.
+ $update_type = self::get_semver_update_type( $new_version, WPGRAPHQL_ACF_VERSION );
+ $warning = '';
+
+ if ( 'major' === $update_type ) {
+ $warning = sprintf(
+ /* translators: %s: version number. */
+ __( '⚠%s
', 'wp-graphql-acf' ),
+ self::get_breaking_change_message( $new_version )
+ );
+ }
+
+ // If there is a warning, append it to each section.
+ if ( '' !== $warning ) {
+ foreach ( $response->sections as $key => $section ) {
+ $response->sections[ $key ] = $warning . $section;
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Rename the downloaded zip
+ *
+ * @param string $source File source location.
+ * @param string $remote_source Remote file source location.
+ *
+ * @return string|\WP_Error
+ */
+ public function upgrader_source_selection( $source, $remote_source ) {
+ global $wp_filesystem;
+
+ if ( strstr( $source, '/wp-graphql-acf' ) ) {
+ $corrected_source = trailingslashit( $remote_source ) . trailingslashit( $this->plugin_config['proper_folder_name'] );
+
+ if ( $wp_filesystem->move( $source, $corrected_source, true ) ) {
+ return $corrected_source;
+ } else {
+ return new \WP_Error();
+ }
+ }
+
+ return $source;
+ }
+
+ /**
+ * Returns the notice to display inline on the plugins page.
+ *
+ * @param string $upgrade_type The type of upgrade.
+ * @param string $message The message to display.
+ */
+ public function get_inline_notice( string $upgrade_type, string $message, ) : string {
+ ob_start();
+ ?>
+
+
+
+
+ get_wporg_data();
+
+ /** @var string $latest_version */
+ $latest_version = $data['version'] ?? '';
+
+ /** @var array $versions */
+ $versions = $data['versions'] ?? [];
+
+
+ foreach ( $versions as $version => $download_url ) {
+ // Skip trunk.
+ if ( 'trunk' === $version ) {
+ continue;
+ }
+
+ // If the current version is < 1.0.0, but this version is >= 2, skip it.
+ // @phpstan-ignore-next-line
+ if ( version_compare( WPGRAPHQL_ACF_VERSION, '1.0.0', '<' ) && version_compare( $version, '2.0.0', '>=' ) ) {
+ continue;
+ }
+
+ // Return the first matching version.
+ $latest_version = $version;
+ }
+
+ if ( ! empty( $latest_version ) ) {
+ set_site_transient( self::VERSION_TRANSIENT, $latest_version, 6 * HOUR_IN_SECONDS );
+ }
+ }
+
+ return $latest_version;
+ }
+
+ /**
+ * Gets the data from the WordPress plugin directory.
+ *
+ * @return array
+ */
+ protected function get_wporg_data() : array {
+ // Return cached data if available.
+ if ( ! empty( $this->wporg_data ) ) {
+ return $this->wporg_data;
+ }
+
+ // Get data from transient.
+ $data = get_site_transient( self::WPORG_DATA_TRANSIENT );
+
+ if ( empty( $data ) || ! is_array( $data ) ) {
+ $data = wp_remote_get( $this->plugin_config['api_url'] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
+
+ $body = wp_remote_retrieve_body( $data );
+
+ // Bail because we couldn't parse the body.
+ if ( empty( $body ) ) {
+ return [];
+ }
+
+ $data = json_decode( $body, true );
+
+ // Bail because we couldn't decode the body.
+ if ( empty( $data ) || ! is_array( $data ) ) {
+ return [];
+ }
+
+ // Refresh every 6 hours.
+ set_site_transient( self::WPORG_DATA_TRANSIENT, $data, 6 * HOUR_IN_SECONDS );
+ }
+
+ // Stash for reuse.
+ $this->wporg_data = $data;
+
+ return $data;
+ }
+
+ /**
+ * Gets the download URL for a specific version.
+ *
+ * @param string $version Version number.
+ *
+ * @return string|false
+ */
+ protected function get_download_url( string $version ) {
+ $data = $this->get_wporg_data();
+
+ if ( empty( $data['versions'][ $version ] ) ) {
+ return false;
+ }
+
+ return $data['versions'][ $version ];
+ }
+
+ /**
+ * Returns the SemVer type of update based on the new version.
+ *
+ * @param string $new_version The SemVer-compliant version number (x.y.z)
+ * @param string $current_version The SemVer-compliant version number (x.y.z)
+ *
+ * @return string{major|minor|patch} The type of update (major, minor, patch).
+ */
+ protected static function get_semver_update_type( string $new_version, string $current_version ) : string {
+ $current = explode( '.', $current_version );
+ $new = explode( '.', $new_version );
+
+ // If the first digit is 0, we need to compare the next digit.
+ if ( '0' === $new[0] && '0' !== $current[0] ) {
+ return self::get_semver_update_type(
+ implode( '.', array_slice( $new, 1 ) ),
+ implode( '.', array_slice( $current, 1 ) )
+ );
+ }
+
+ // If the major version is different, this is a major update.
+ if ( $current[0] !== $new[0] ) {
+ return 'major';
+ }
+
+ // If the minor version is different, this is a minor update.
+ if ( $current[1] !== $new[1] ) {
+ return 'minor';
+ }
+
+ return 'patch';
+ }
+
+ /**
+ * Gets the message to display for a breaking change.
+ *
+ * @param string $version The version number.
+ */
+ protected static function get_breaking_change_message( string $version ) : string {
+ return sprintf(
+ /* translators: %s: version number. */
+ __( 'Version %s of WPGraphQL for ACF is a major update and may contain breaking changes. Please review the changelog and test before updating on a production site.', 'wp-graphql-acf' ),
+ $version
+ );
+ }
+
+}
diff --git a/src/class-acf.php b/src/class-acf.php
index e7f8e21..e53d31c 100644
--- a/src/class-acf.php
+++ b/src/class-acf.php
@@ -8,6 +8,7 @@
namespace WPGraphQL\ACF;
use GraphQL\Type\Definition\ResolveInfo;
+use WPGraphQL\ACF\Admin\Updater;
/**
* Final class ACF
@@ -17,7 +18,7 @@ final class ACF {
/**
* Stores the instance of the WPGraphQL\ACF class
*
- * @var ACF The one true WPGraphQL\Extensions\ACF
+ * @var \WPGraphQL\ACF\ACF The one true WPGraphQL\Extensions\ACF
* @access private
*/
private static $instance;
@@ -25,12 +26,11 @@ final class ACF {
/**
* Get the singleton.
*
- * @return ACF
+ * @return \WPGraphQL\ACF\ACF
*/
public static function instance() {
-
- if ( ! isset( self::$instance ) && ! ( self::$instance instanceof ACF ) ) {
- self::$instance = new ACF();
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
+ self::$instance = new self();
self::$instance->setup_constants();
self::$instance->includes();
self::$instance->actions();
@@ -41,7 +41,7 @@ public static function instance() {
/**
* Fire off init action
*
- * @param ACF $instance The instance of the WPGraphQL\ACF class
+ * @param \WPGraphQL\ACF\ACF $instance The instance of the WPGraphQL\ACF class
*/
do_action( 'graphql_acf_init', self::$instance );
@@ -63,7 +63,6 @@ public function __clone() {
// Cloning instances of the class is forbidden.
_doing_it_wrong( __FUNCTION__, esc_html__( 'The \WPGraphQL\ACF class should not be cloned.', 'wp-graphql-acf' ), '0.0.1' );
-
}
/**
@@ -76,7 +75,6 @@ public function __wakeup() {
// De-serializing instances of the class is forbidden.
_doing_it_wrong( __FUNCTION__, esc_html__( 'De-serializing instances of the \WPGraphQL\ACF class is not allowed', 'wp-graphql-acf' ), '0.0.1' );
-
}
/**
@@ -101,7 +99,6 @@ private function setup_constants() {
if ( ! defined( 'WPGRAPHQL_ACF_PLUGIN_FILE' ) ) {
define( 'WPGRAPHQL_ACF_PLUGIN_FILE', __FILE__ . '/..' );
}
-
}
/**
@@ -121,7 +118,6 @@ private function includes() {
* cycle
*/
private function actions() {
-
}
/**
@@ -133,28 +129,33 @@ private function filters() {
* This filters any field that returns the `ContentTemplate` type
* to pass the source node down to the template for added context
*/
- add_filter( 'graphql_resolve_field', function( $result, $source, $args, $context, ResolveInfo $info, $type_name, $field_key, $field, $field_resolver ) {
- if ( isset( $info->returnType ) && strtolower( 'ContentTemplate' ) === strtolower( $info->returnType ) ) {
- if ( is_array( $result ) && ! isset( $result['node'] ) && ! empty( $source ) ) {
- $result['node'] = $source;
+ add_filter(
+ 'graphql_resolve_field',
+ static function ( $result, $source, $args, $context, ResolveInfo $info, $type_name, $field_key, $field, $field_resolver ) {
+ if ( isset( $info->returnType ) && strtolower( 'ContentTemplate' ) === strtolower( $info->returnType ) ) {
+ if ( is_array( $result ) && ! isset( $result['node'] ) && ! empty( $source ) ) {
+ $result['node'] = $source;
+ }
}
- }
- return $result;
- }, 10, 9 );
-
+ return $result;
+ },
+ 10,
+ 9
+ );
}
/**
* Initialize
*/
private function init() {
-
$config = new Config();
add_action( 'graphql_register_types', [ $config, 'init' ], 10, 1 );
$acf_settings = new ACF_Settings();
$acf_settings->init();
+ $updater = new Updater();
+ $updater->init();
}
}
From dd3644d1c0324a472dce81f212a03fc7a876be27 Mon Sep 17 00:00:00 2001
From: David Levine
Date: Sun, 25 Jun 2023 09:54:03 +0000
Subject: [PATCH 2/5] fix: incorrect plugin constants
---
src/Admin/Updater.php | 3 ++-
src/class-acf.php | 8 +++++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/Admin/Updater.php b/src/Admin/Updater.php
index 05a9f1e..75cda79 100644
--- a/src/Admin/Updater.php
+++ b/src/Admin/Updater.php
@@ -72,7 +72,7 @@ public function init() : void {
*/
public function disable_autoupdate( $update, $item ) {
// Return early if this is not our plugin.
- if ( $item->slug !== $this->plugin_config['slug'] ) {
+ if ( $item->slug !== $this->plugin_config['slug'] && $item->plugin !== $this->plugin_config['slug'] .'/' .$this->plugin_config['slug'] .'.php') {
return $update;
}
@@ -129,6 +129,7 @@ public function api_check( $transient ) {
$transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->package = $download_url;
$transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->zip_url = $download_url;
+
return $transient;
}
diff --git a/src/class-acf.php b/src/class-acf.php
index e53d31c..1566641 100644
--- a/src/class-acf.php
+++ b/src/class-acf.php
@@ -84,20 +84,22 @@ public function __wakeup() {
* @return void
*/
private function setup_constants() {
+ $main_file_path = dirname( __DIR__ ) . '/wp-graphql-acf.php';
+
// Plugin Folder Path.
if ( ! defined( 'WPGRAPHQL_ACF_PLUGIN_DIR' ) ) {
- define( 'WPGRAPHQL_ACF_PLUGIN_DIR', plugin_dir_path( __FILE__ . '/..' ) );
+ define( 'WPGRAPHQL_ACF_PLUGIN_DIR', plugin_dir_path( $main_file_path ) );
}
// Plugin Folder URL.
if ( ! defined( 'WPGRAPHQL_ACF_PLUGIN_URL' ) ) {
- define( 'WPGRAPHQL_ACF_PLUGIN_URL', plugin_dir_url( __FILE__ . '/..' ) );
+ define( 'WPGRAPHQL_ACF_PLUGIN_URL', plugin_dir_url( $main_file_path ) );
}
// Plugin Root File.
if ( ! defined( 'WPGRAPHQL_ACF_PLUGIN_FILE' ) ) {
- define( 'WPGRAPHQL_ACF_PLUGIN_FILE', __FILE__ . '/..' );
+ define( 'WPGRAPHQL_ACF_PLUGIN_FILE', $main_file_path );
}
}
From 1b5086cd6f19f8f576ef129de73205307fd681a2 Mon Sep 17 00:00:00 2001
From: David Levine
Date: Sun, 25 Jun 2023 10:32:54 +0000
Subject: [PATCH 3/5] fix: internal logic
---
src/Admin/Updater.php | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/Admin/Updater.php b/src/Admin/Updater.php
index 75cda79..6ccd0a5 100644
--- a/src/Admin/Updater.php
+++ b/src/Admin/Updater.php
@@ -44,7 +44,7 @@ class Updater {
public function __construct() {
// Defining this in the constructor makes things easier to mock/test
$this->plugin_config = [
- 'plugin_file' => WPGRAPHQL_ACF_PLUGIN_FILE,
+ 'plugin_file' => self::PLUGIN_SLUG . '/' . self::PLUGIN_SLUG . '.php',
'slug' => self::PLUGIN_SLUG,
'proper_folder_name' => self::PLUGIN_SLUG,
'api_url' => 'https://api.wordpress.org/plugins/info/1.0/' . self::PLUGIN_SLUG . '.json',
@@ -72,7 +72,7 @@ public function init() : void {
*/
public function disable_autoupdate( $update, $item ) {
// Return early if this is not our plugin.
- if ( $item->slug !== $this->plugin_config['slug'] && $item->plugin !== $this->plugin_config['slug'] .'/' .$this->plugin_config['slug'] .'.php') {
+ if ( $item->slug !== $this->plugin_config['slug'] && $item->plugin !== $this->plugin_config['plugin_file'] ) {
return $update;
}
@@ -121,14 +121,13 @@ public function api_check( $transient ) {
$download_url = $this->get_download_url( $new_version );
// Populate the transient data.
- if ( ! isset( $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ] ) ) {
- $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ] = (object) $this->plugin_config;
+ if ( ! isset( $transient->response[ $this->plugin_config['plugin_file'] ] ) ) {
+ $transient->response[ $this->plugin_config['plugin_file'] ] = (object) $this->plugin_config;
}
- $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->new_version = $new_version;
- $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->package = $download_url;
- $transient->response[ WPGRAPHQL_ACF_PLUGIN_FILE ]->zip_url = $download_url;
-
+ $transient->response[ $this->plugin_config['plugin_file'] ]->new_version = $new_version;
+ $transient->response[ $this->plugin_config['plugin_file'] ]->package = $download_url;
+ $transient->response[ $this->plugin_config['plugin_file'] ]->zip_url = $download_url;
return $transient;
}
@@ -270,7 +269,6 @@ protected function get_plugin_data() : array {
*/
protected function get_latest_version() : string {
$latest_version = get_site_transient( self::VERSION_TRANSIENT );
-
if ( empty( $latest_version ) ) {
$data = $this->get_wporg_data();
@@ -280,6 +278,10 @@ protected function get_latest_version() : string {
/** @var array $versions */
$versions = $data['versions'] ?? [];
+ // Sort the versions by descending order.
+ uksort( $versions, function( $a, $b ) {
+ return version_compare( $b, $a );
+ });
foreach ( $versions as $version => $download_url ) {
// Skip trunk.
@@ -295,6 +297,7 @@ protected function get_latest_version() : string {
// Return the first matching version.
$latest_version = $version;
+ break;
}
if ( ! empty( $latest_version ) ) {
@@ -319,7 +322,7 @@ protected function get_wporg_data() : array {
// Get data from transient.
$data = get_site_transient( self::WPORG_DATA_TRANSIENT );
- if ( empty( $data ) || ! is_array( $data ) ) {
+ if ( empty( $data ) || ! is_array( $data ) || isset( $data['error'] ) ) {
$data = wp_remote_get( $this->plugin_config['api_url'] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
$body = wp_remote_retrieve_body( $data );
From c21819619e1893702b6a82678a84ede575b1ef2f Mon Sep 17 00:00:00 2001
From: David Levine
Date: Sun, 25 Jun 2023 15:35:47 +0000
Subject: [PATCH 4/5] dev: add inline update messages
---
src/Admin/Updater.php | 113 ++++++++++++++++++++++++++++++++----------
1 file changed, 87 insertions(+), 26 deletions(-)
diff --git a/src/Admin/Updater.php b/src/Admin/Updater.php
index 6ccd0a5..5aca0c8 100644
--- a/src/Admin/Updater.php
+++ b/src/Admin/Updater.php
@@ -34,7 +34,7 @@ class Updater {
/**
* The Plugin data from wordpress.org
*
- * @var array
+ * @var ?object
*/
protected $wporg_data;
@@ -60,6 +60,7 @@ public function init() : void {
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'api_check' ] );
add_filter( 'plugins_api_result', [ $this, 'api_result' ], 10, 3 );
add_filter( 'upgrader_source_selection', [ $this, 'upgrader_source_selection' ], 10, 2 );
+ add_action( 'in_plugin_update_message-' . $this->plugin_config['plugin_file'], [ $this, 'update_message' ] );
}
/**
@@ -129,6 +130,17 @@ public function api_check( $transient ) {
$transient->response[ $this->plugin_config['plugin_file'] ]->package = $download_url;
$transient->response[ $this->plugin_config['plugin_file'] ]->zip_url = $download_url;
+ $update_type = self::get_semver_update_type( $new_version, WPGRAPHQL_ACF_VERSION );
+ if ( 'major' === $update_type ) {
+ $message = sprintf(
+ /* translators: %s: breaking change message. */
+ __( '⚠ Warning: %s', 'wp-graphql-acf' ),
+ self::get_breaking_change_message( $new_version )
+ );
+ $transient->response[ $this->plugin_config['plugin_file'] ]->upgrade_notice = $message;
+ }
+
+
return $transient;
}
@@ -142,6 +154,18 @@ public function api_check( $transient ) {
* @return object|\WP_Error
*/
public function api_result( $response, $action, $args ) {
+ /**
+ * Filters the slug used to check the API response.
+ * This is useful for testing.
+ *
+ * @param ?string $custom_slug The custom slug to use. If null, the default slug is used.
+ * @param object $args The API request arguments.
+ */
+ $custom_slug = apply_filters( 'wpgraphql_acf_wporg_api_result_slug', null, $args );
+ if ( null !== $custom_slug ) {
+ $args->slug = $custom_slug;
+ }
+
// Bail if this is not checking our plugin.
if ( ! isset( $args->slug ) || $args->slug !== $this->plugin_config['slug'] ) {
return $response;
@@ -151,7 +175,7 @@ public function api_result( $response, $action, $args ) {
$new_version = $this->get_latest_version();
// Bail if the version is not newer.
- if ( version_compare( $new_version, $args->version, '<=' ) ) {
+ if ( version_compare( $new_version, WPGRAPHQL_ACF_VERSION, '<=' ) ) {
return $response;
}
@@ -164,11 +188,12 @@ public function api_result( $response, $action, $args ) {
$warning = '';
if ( 'major' === $update_type ) {
- $warning = sprintf(
+ $message = sprintf(
/* translators: %s: version number. */
- __( '⚠%s
', 'wp-graphql-acf' ),
+ __( '⚠ Warning%s
', 'wp-graphql-acf' ),
self::get_breaking_change_message( $new_version )
);
+ $warning = $this->get_inline_notice( 'major', $message );
}
// If there is a warning, append it to each section.
@@ -205,6 +230,35 @@ public function upgrader_source_selection( $source, $remote_source ) {
return $source;
}
+ /**
+ * Outputs a warning message about a major update.
+ *
+ * @param array $args The update message arguments.
+ */
+ public function update_message( $args ) : void {
+ if ( ! isset( $args['slug'] ) || $args['slug'] !== $this->plugin_config['slug'] ) {
+ return;
+ }
+
+ // Get the latest version.
+ $new_version = $args['new_version'];
+
+ // If this is a major update, add a warning.
+ $update_type = self::get_semver_update_type( $new_version, WPGRAPHQL_ACF_VERSION );
+
+ if ( 'major' !== $update_type ) {
+ return;
+ }
+
+ $message = sprintf(
+ /* translators: %s: version number. */
+ __( '⚠ Warning%s
', 'wp-graphql-acf' ),
+ self::get_breaking_change_message( $new_version )
+ );
+
+ echo '
' . wp_kses_post( $this->get_inline_notice( 'major', $message ) );
+ }
+
/**
* Returns the notice to display inline on the plugins page.
*
@@ -214,8 +268,8 @@ public function upgrader_source_selection( $source, $remote_source ) {
public function get_inline_notice( string $upgrade_type, string $message, ) : string {
ob_start();
?>
-
-
+
+
get_wporg_data();
/** @var string $latest_version */
- $latest_version = $data['version'] ?? '';
+ $latest_version = isset( $data->version ) ? $data->version : '';
/** @var array
$versions */
- $versions = $data['versions'] ?? [];
+ $versions = isset( $data->versions ) ? (array) $data->versions : [];
// Sort the versions by descending order.
- uksort( $versions, function( $a, $b ) {
- return version_compare( $b, $a );
- });
+ uksort(
+ $versions,
+ static function ( $a, $b ) {
+ return version_compare( $b, $a );
+ }
+ );
foreach ( $versions as $version => $download_url ) {
// Skip trunk.
@@ -311,9 +369,9 @@ protected function get_latest_version() : string {
/**
* Gets the data from the WordPress plugin directory.
*
- * @return array
+ * @return ?object
*/
- protected function get_wporg_data() : array {
+ protected function get_wporg_data() {
// Return cached data if available.
if ( ! empty( $this->wporg_data ) ) {
return $this->wporg_data;
@@ -322,21 +380,28 @@ protected function get_wporg_data() : array {
// Get data from transient.
$data = get_site_transient( self::WPORG_DATA_TRANSIENT );
- if ( empty( $data ) || ! is_array( $data ) || isset( $data['error'] ) ) {
- $data = wp_remote_get( $this->plugin_config['api_url'] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
+ if ( empty( $data ) || ! is_object( $data ) ) {
+ /**
+ * Filters the API URL to use for fetching the plugin data.
+ * Useful for testing.
+ *
+ * @param string $api_url The API URL to use.
+ */
+ $api_url = apply_filters( 'wpgraphql_acf_wporg_api_url', $this->plugin_config['api_url'] );
+
+ $req = wp_remote_get( $api_url ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
- $body = wp_remote_retrieve_body( $data );
+ $body = wp_remote_retrieve_body( $req );
- // Bail because we couldn't parse the body.
if ( empty( $body ) ) {
- return [];
+ return null;
}
- $data = json_decode( $body, true );
+ $data = json_decode( $body );
// Bail because we couldn't decode the body.
- if ( empty( $data ) || ! is_array( $data ) ) {
- return [];
+ if ( empty( $data ) || ! is_object( $data ) ) {
+ return null;
}
// Refresh every 6 hours.
@@ -359,11 +424,7 @@ protected function get_wporg_data() : array {
protected function get_download_url( string $version ) {
$data = $this->get_wporg_data();
- if ( empty( $data['versions'][ $version ] ) ) {
- return false;
- }
-
- return $data['versions'][ $version ];
+ return ! empty( $data->versions->$version ) ? $data->versions->$version : false;
}
/**
From 760c6c01f8eb037cf7f0b60c606af47d7ffb01e4 Mon Sep 17 00:00:00 2001
From: David Levine
Date: Mon, 26 Jun 2023 15:27:21 +0000
Subject: [PATCH 5/5] dev: check for test slug in section notice
---
src/Admin/Updater.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Admin/Updater.php b/src/Admin/Updater.php
index 5aca0c8..c179b53 100644
--- a/src/Admin/Updater.php
+++ b/src/Admin/Updater.php
@@ -167,7 +167,7 @@ public function api_result( $response, $action, $args ) {
}
// Bail if this is not checking our plugin.
- if ( ! isset( $args->slug ) || $args->slug !== $this->plugin_config['slug'] ) {
+ if ( ! isset( $args->slug ) || ( $args->slug !== $this->plugin_config['slug'] && $args->slug !== $custom_slug ) ) {
return $response;
}