diff --git a/inc/packages/admin/namespace.php b/inc/packages/admin/namespace.php index 6b62a69a..7199664b 100644 --- a/inc/packages/admin/namespace.php +++ b/inc/packages/admin/namespace.php @@ -35,6 +35,7 @@ function bootstrap() { add_action( 'load-plugin-install.php', __NAMESPACE__ . '\\load_plugin_install' ); add_action( 'install_plugins_pre_plugin-information', __NAMESPACE__ . '\\maybe_hijack_plugin_info', 0 ); add_filter( 'plugins_api_result', __NAMESPACE__ . '\\alter_slugs', 10, 3 ); + add_filter( 'plugins_api_result', __NAMESPACE__ . '\\sort_sections_in_api', 15, 1 ); add_filter( 'plugin_install_action_links', __NAMESPACE__ . '\\maybe_hijack_plugin_install_button', 10, 2 ); add_filter( 'plugin_install_description', __NAMESPACE__ . '\\maybe_add_data_to_description', 10, 2 ); add_action( 'wp_ajax_check_plugin_dependencies', __NAMESPACE__ . '\\set_slug_to_hashed' ); @@ -427,6 +428,34 @@ function alter_slugs( $res, $action, $args ) { return $res; } +/** + * Sort plugin modal tabs. + * + * Based on standard tab listing order. + * + * @param object|WP_Error $res Response object or WP_Error. + * @return object|WP_Error + */ +function sort_sections_in_api( $res ) { + $ordered_sections = [ + 'description', + 'installation', + 'faq', + 'screenshots', + 'changelog', + 'upgrade_notice', + 'security', + 'other_notes', + 'reviews', + ]; + if ( property_exists( $res, 'sections' ) && is_array( $res->sections ) ) { + $properly_ordered = array_merge( array_fill_keys( $ordered_sections, '' ), $res->sections ); + $res->sections = array_filter( $properly_ordered ); + } + + return $res; +} + /** * Override the install button, for bridged plugins. * diff --git a/inc/packages/namespace.php b/inc/packages/namespace.php index 469320f6..3b95fd5c 100644 --- a/inc/packages/namespace.php +++ b/inc/packages/namespace.php @@ -13,6 +13,7 @@ use FAIR\Packages\DID\PLC; use FAIR\Packages\DID\Web; use FAIR\Updater; +use function FAIR\Packages\Admin\sort_sections_in_api; use WP_Error; use WP_Upgrader; @@ -170,6 +171,14 @@ function fetch_metadata_doc( string $url ) { } elseif ( $code !== 200 ) { return new WP_Error( 'fair.packages.metadata.failure', __( 'HTTP error code received', 'fair' ) ); } + + // Reorder sections before caching. + $body = json_decode( $response['body'] ); + $body->sections = (array) $body->sections; + $body = sort_sections_in_api( $body ); + $body->sections = (object) $body->sections; + $response['body'] = json_encode( $body ); + set_transient( $cache_key, $response, CACHE_LIFETIME ); } diff --git a/tests/phpunit/tests/Packages/Admin/SortSectionsInApiTest.php b/tests/phpunit/tests/Packages/Admin/SortSectionsInApiTest.php new file mode 100644 index 00000000..0f78f21c --- /dev/null +++ b/tests/phpunit/tests/Packages/Admin/SortSectionsInApiTest.php @@ -0,0 +1,123 @@ +sections = $sections; + $actual = sort_sections_in_api( $res ); + $this->assertIsObject( $actual, 'The response is not an object.' ); + // $this->assertObjectHasProperty( 'sections', $actual, 'The response object has no sections.' ); + $this->assertSame( + $expected_order, + $actual->sections, + 'The sections were not in the expected order.' + ); + } + + /** + * Data provider. + */ + public static function data_plugin_detail_sections(): array { + return [ + 'expected sections' => [ + 'sections' => [ + 'faq' => 'faq', + 'screenshots' => 'screenshots', + 'changelog' => 'changelog', + 'description' => 'description', + 'security' => 'security', + 'reviews' => 'reviews', + 'other_notes' => 'other_notes', + 'installation' => 'installation', + 'upgrade_notice' => 'upgrade_notice', + ], + 'expected_order' => [ + 'description' => 'description', + 'installation' => 'installation', + 'faq' => 'faq', + 'screenshots' => 'screenshots', + 'changelog' => 'changelog', + 'upgrade_notice' => 'upgrade_notice', + 'security' => 'security', + 'other_notes' => 'other_notes', + 'reviews' => 'reviews', + ], + ], + 'unknown sections' => [ + 'sections' => [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + ], + 'expected_order' => [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + ], + ], + 'expected and unknown sections' => [ + 'sections' => [ + 'faq' => 'faq', + 'foo' => 'foo', + 'screenshots' => 'screenshots', + 'changelog' => 'changelog', + 'bar' => 'bar', + 'reviews' => 'reviews', + 'installation' => 'installation', + 'security' => 'security', + ], + 'expected_order' => [ + 'installation' => 'installation', + 'faq' => 'faq', + 'screenshots' => 'screenshots', + 'changelog' => 'changelog', + 'security' => 'security', + 'reviews' => 'reviews', + 'foo' => 'foo', + 'bar' => 'bar', + ], + ], + 'some empty sections' => [ + 'sections' => [ + 'faq' => '', + 'foo' => '', + 'screenshots' => 'screenshots', + 'changelog' => '', + 'bar' => '', + 'reviews' => 'reviews', + 'installation' => '', + 'security' => '', + ], + 'expected_order' => [ + 'screenshots' => 'screenshots', + 'reviews' => 'reviews', + ], + ], + 'no sections' => [ + 'sections' => [], + 'expected_order' => [], + ], + ]; + } +}