From 3ced29511365ab01d6401391fa1997b26b7c7e40 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 18 Mar 2024 14:39:48 +0530 Subject: [PATCH 01/11] Initial changes --- .../workflows/deploy-standalone-plugins.yml | 38 +- .../workflows/php-test-standalone-plugins.yml | 2 - admin/js/perflab-module-migration-notice.js | 56 -- admin/load.php | 602 +----------------- admin/plugins.php | 19 +- bin/plugin/cli.js | 44 -- bin/plugin/commands/build-plugins.js | 219 ------- bin/plugin/commands/common.js | 167 ----- bin/plugin/commands/enabled-modules.js | 121 ---- bin/plugin/commands/get-plugin-dir.js | 73 --- bin/plugin/commands/get-plugin-version.js | 15 +- bin/plugin/commands/readme.js | 65 +- bin/plugin/commands/test-plugins.js | 57 +- bin/plugin/commands/translations.js | 144 ----- default-enabled-modules.php | 8 - load.php | 418 +----------- module-i18n.php | 15 - package.json | 3 - phpcs.xml.dist | 6 - phpstan.neon.dist | 2 - readme.txt | 16 +- tests/admin/load-tests.php | 180 +----- tests/load-tests.php | 142 +---- .../check-error/demo-module-4/can-load.php | 10 - .../check-error/demo-module-4/load.php | 10 - .../images/demo-module-3/can-load.php | 8 - .../images/demo-module-3/load.php | 10 - .../js-and-css/demo-module-1/can-load.php | 8 - .../js-and-css/demo-module-1/load.php | 10 - .../something/demo-module-2/activate.php | 11 - .../something/demo-module-2/deactivate.php | 11 - .../something/demo-module-2/load.php | 10 - 32 files changed, 71 insertions(+), 2429 deletions(-) delete mode 100644 admin/js/perflab-module-migration-notice.js delete mode 100644 bin/plugin/commands/build-plugins.js delete mode 100644 bin/plugin/commands/common.js delete mode 100644 bin/plugin/commands/enabled-modules.js delete mode 100644 bin/plugin/commands/get-plugin-dir.js delete mode 100644 bin/plugin/commands/translations.js delete mode 100644 default-enabled-modules.php delete mode 100644 module-i18n.php delete mode 100644 tests/testdata/demo-modules/check-error/demo-module-4/can-load.php delete mode 100644 tests/testdata/demo-modules/check-error/demo-module-4/load.php delete mode 100644 tests/testdata/demo-modules/images/demo-module-3/can-load.php delete mode 100644 tests/testdata/demo-modules/images/demo-module-3/load.php delete mode 100644 tests/testdata/demo-modules/js-and-css/demo-module-1/can-load.php delete mode 100644 tests/testdata/demo-modules/js-and-css/demo-module-1/load.php delete mode 100644 tests/testdata/demo-modules/something/demo-module-2/activate.php delete mode 100644 tests/testdata/demo-modules/something/demo-module-2/deactivate.php delete mode 100644 tests/testdata/demo-modules/something/demo-module-2/load.php diff --git a/.github/workflows/deploy-standalone-plugins.yml b/.github/workflows/deploy-standalone-plugins.yml index 24bfc828e1..939816dcb2 100644 --- a/.github/workflows/deploy-standalone-plugins.yml +++ b/.github/workflows/deploy-standalone-plugins.yml @@ -1,6 +1,18 @@ name: Deploy standalone plugins to WordPress.org on: + # TODO The pull_request will be removed once the workflow is tested. + pull_request: + branches: + - trunk + - 'release/**' + - 'feature/**' + paths: + - '.github/workflows/deploy-standalone-plugins.yml' + types: + - opened + - reopened + - synchronize release: types: [published] workflow_dispatch: @@ -28,11 +40,6 @@ jobs: cache: npm - name: Install npm dependencies run: npm ci - - name: Get directory - id: get-plugin-directory - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - echo "directory=$(node ./bin/plugin/cli.js get-plugin-dir --slug=${{ inputs.slug }})" >> $GITHUB_OUTPUT - name: Get plugin version id: get-version if: ${{ github.event_name == 'workflow_dispatch' }} @@ -45,9 +52,9 @@ jobs: result=$(echo "${{ steps.get-version.outputs.version }}" | awk '/^(\*|[0-9]+(\.[0-9]+){0,2}(-[a-zA-Z0-9.]+)?)$/ {print "Matched"}') if [[ -n "$result" ]]; then # Set the manual input values in JSON format for use in the matrix. - echo "matrix={\"include\":[{\"slug\":\"${{ inputs.slug }}\",\"version\":\"${{ steps.get-version.outputs.version }}\",\"directory\":\"${{ steps.get-plugin-directory.outputs.directory }}\",\"dry-run\":\"${{ inputs.dry-run }}\"}]}" >> $GITHUB_OUTPUT + echo "matrix={\"include\":[{\"slug\":\"${{ inputs.slug }}\",\"version\":\"${{ steps.get-version.outputs.version }}\",\"dry-run\":\"true\"}]}" >> $GITHUB_OUTPUT else - echo "The ${{ inputs.slug }} module slug is missing in the file plugins.json." + echo "The ${{ inputs.slug }} plugin slug is missing in the file plugins.json." exit 1 fi else @@ -57,7 +64,7 @@ jobs: # The "dry-run" parameter is included here to set the deployment mode. # When running the manual (workflow_dispatch) workflow, this value will be set from manual input type. - echo "matrix=$(jq -c '{include: ([.modules | to_entries[] | {name:.key, slug: .value.slug, version: .value.version, directory: "build", "dry-run": false}] + ([.plugins[] | {name:. , slug:. , directory: "plugins", "dry-run": false}]))}' plugins.json)" >> $GITHUB_OUTPUT + echo "matrix=$(jq -c '{include: ([.plugins[] | {name:. , slug:. , "dry-run": true}])}' plugins.json)" >> $GITHUB_OUTPUT fi deploy: name: Deploy Plugin @@ -75,9 +82,6 @@ jobs: cache: npm - name: Install npm dependencies run: npm ci - - name: Building standalone plugins - if: ${{ matrix.directory != 'plugins' }} - run: npm run build-plugins - name: Set version id: set_version run: | @@ -91,9 +95,13 @@ jobs: with: dry-run: ${{ matrix.dry-run }} env: - SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} - SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + # TODO Once the workflow is tested, we will remove the comment and use the secret SVN access. + #SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + #SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + # TODO Once the workflow is tested, we will remove this test credential. + SVN_PASSWORD: SVN_PASSWORD + SVN_USERNAME: SVN_USERNAME SLUG: ${{ matrix.slug }} VERSION: ${{ steps.set_version.outputs.version }} - BUILD_DIR: ./${{ matrix.directory }}/${{ matrix.slug }} - ASSETS_DIR: ./${{ matrix.directory }}/${{ matrix.slug }}/.wordpress-org + BUILD_DIR: ./plugins/${{ matrix.slug }} + ASSETS_DIR: ./plugins/${{ matrix.slug }}/.wordpress-org diff --git a/.github/workflows/php-test-standalone-plugins.yml b/.github/workflows/php-test-standalone-plugins.yml index 8c67cb78e6..186a35ca7a 100644 --- a/.github/workflows/php-test-standalone-plugins.yml +++ b/.github/workflows/php-test-standalone-plugins.yml @@ -76,8 +76,6 @@ jobs: node --version composer --version php -v - - name: Building standalone plugins - run: npm run build-plugins - name: Running single site standalone plugin integration tests run: npm run test-plugins - name: Running multisite standalone plugin integration tests diff --git a/admin/js/perflab-module-migration-notice.js b/admin/js/perflab-module-migration-notice.js deleted file mode 100644 index bdebc98f26..0000000000 --- a/admin/js/perflab-module-migration-notice.js +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint camelcase: "off", no-alert: "off" */ -/* global perflab_module_migration_notice:false */ - -( function ( document ) { - document.addEventListener( 'DOMContentLoaded', function () { - document.addEventListener( 'click', function ( event ) { - if ( - event.target.classList.contains( - 'perflab-install-active-plugin' - ) - ) { - const target = event.target; - target.parentElement - .querySelector( 'span' ) - .classList.remove( 'hidden' ); - - const data = new FormData(); - data.append( - 'action', - 'perflab_install_activate_standalone_plugins' - ); - data.append( 'nonce', perflab_module_migration_notice.nonce ); - - fetch( perflab_module_migration_notice.ajaxurl, { - method: 'POST', - credentials: 'same-origin', - body: data, - } ) - .then( function ( response ) { - if ( ! response.ok ) { - throw new Error( - wp.i18n.__( - 'Network response was not ok.', - 'performance-lab' - ) - ); - } - return response.json(); - } ) - .then( function ( result ) { - target.parentElement - .querySelector( 'span' ) - .classList.add( 'hidden' ); - if ( ! result.success ) { - alert( result.data.errorMessage ); - } - window.location.reload(); - } ) - .catch( function ( error ) { - alert( error.errorMessage ); - window.location.reload(); - } ); - } - } ); - } ); -} )( document ); diff --git a/admin/load.php b/admin/load.php index d29937498c..c34c13aee4 100644 --- a/admin/load.php +++ b/admin/load.php @@ -1,6 +1,6 @@ __( 'Other', 'performance-lab' ) ); - foreach ( $sections as $section_slug => $section_data ) { - add_settings_section( - $section_slug, - $section_data['name'], - null, - PERFLAB_MODULES_SCREEN - ); - } - - // Register fields for all modules. - if ( ! is_array( $modules ) ) { - $modules = perflab_get_modules(); - } - $settings = perflab_get_module_settings(); - foreach ( $modules as $module_slug => $module_data ) { - $module_settings = isset( $settings[ $module_slug ] ) ? $settings[ $module_slug ] : array(); - $module_section = isset( $sections[ $module_data['focus'] ] ) ? $module_data['focus'] : 'other'; - - // Mark this module's section as added. - $sections[ $module_section ]['added'] = true; - - add_settings_field( - $module_slug, - $module_data['name'], - static function () use ( $module_slug, $module_data, $module_settings ) { - perflab_render_modules_page_field( $module_slug, $module_data, $module_settings ); - }, - PERFLAB_MODULES_SCREEN, - $module_section - ); - } - - // Remove all sections for which there are no modules. - foreach ( $sections as $section_slug => $section_data ) { - if ( empty( $section_data['added'] ) ) { - unset( $wp_settings_sections[ PERFLAB_MODULES_SCREEN ][ $section_slug ] ); - } - } -} - -/** - * Renders the modules page. + * Renders the plugin page. * * @since 1.0.0 */ @@ -117,272 +47,10 @@ function perflab_render_modules_page() { ?>
- -

- -

- -
- - - -
-
- - - - -

" class="description"> - -

-
- array( - 'name' => __( 'Images', 'performance-lab' ), - ), - 'js-and-css' => array( - 'name' => __( 'JS & CSS', 'performance-lab' ), - ), - 'database' => array( - 'name' => __( 'Database', 'performance-lab' ), - ), - 'measurement' => array( - 'name' => __( 'Measurement', 'performance-lab' ), - ), - 'object-cache' => array( - 'name' => __( 'Object Cache', 'performance-lab' ), - ), - ); -} - -/** - * Gets all available modules. - * - * This function iterates through the modules directory and therefore should only be called on the modules page. - * It searches all modules, similar to how plugins are searched in the WordPress core function `get_plugins()`. - * - * @since 1.0.0 - * - * @param string $modules_root Modules root directory to look for modules in. Default is the `/modules` directory - * in the plugin's root. - * @return array Associative array of parsed module data, keyed by module slug. Fields for every module include - * 'name', 'description', 'focus', and 'experimental'. - */ -function perflab_get_modules( $modules_root = null ) { - if ( null === $modules_root ) { - $modules_root = dirname( __DIR__ ) . '/modules'; - } - - $modules = array(); - $module_files = array(); - // PHPCS ignore reason: A modules directory is always present. - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged - $modules_dir = @opendir( $modules_root ); - - // Modules are organized as {focus}/{module-slug} in the modules folder. - if ( $modules_dir ) { - // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition - while ( ( $focus = readdir( $modules_dir ) ) !== false ) { - if ( '.' === substr( $focus, 0, 1 ) ) { - continue; - } - - // Each focus area must be a directory. - if ( ! is_dir( $modules_root . '/' . $focus ) ) { - continue; - } - - // PHPCS ignore reason: Only the focus area directory is allowed. - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged - $focus_dir = @opendir( $modules_root . '/' . $focus ); - if ( $focus_dir ) { - // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition - while ( ( $file = readdir( $focus_dir ) ) !== false ) { - // Unlike plugins, modules must be in a directory. - if ( ! is_dir( $modules_root . '/' . $focus . '/' . $file ) ) { - continue; - } - - // PHPCS ignore reason: Only the module directory is allowed. - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged - $module_dir = @opendir( $modules_root . '/' . $focus . '/' . $file ); - if ( $module_dir ) { - // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition - while ( ( $subfile = readdir( $module_dir ) ) !== false ) { - if ( '.' === substr( $subfile, 0, 1 ) ) { - continue; - } - - // Unlike plugins, module main files must be called `load.php`. - if ( 'load.php' !== $subfile ) { - continue; - } - - $module_files[] = "$focus/$file/$subfile"; - } - - closedir( $module_dir ); - } - } - - closedir( $focus_dir ); - } - } - - closedir( $modules_dir ); - } - - foreach ( $module_files as $module_file ) { - if ( ! is_readable( "$modules_root/$module_file" ) ) { - continue; - } - $module_dir = dirname( $module_file ); - $module_data = perflab_get_module_data( "$modules_root/$module_file" ); - if ( ! $module_data ) { - continue; - } - - $modules[ $module_dir ] = $module_data; - } - - uasort( - $modules, - static function ( $a, $b ) { - return strnatcasecmp( $a['name'], $b['name'] ); - } - ); - - return $modules; -} - -/** - * Parses the module main file to get the module's metadata. - * - * This is similar to how plugin data is parsed in the WordPress core function `get_plugin_data()`. - * The user-facing strings will be translated. - * - * @since 1.0.0 - * - * @param string $module_file Absolute path to the main module file. - * @return array|bool Associative array of parsed module data, or false on failure. Fields for every module include - * 'name', 'description', 'focus', and 'experimental'. - */ -function perflab_get_module_data( $module_file ) { - // Extract the module dir in the form {focus}/{module-slug}. - preg_match( '/.*\/(.*\/.*)\/load\.php$/i', $module_file, $matches ); - $module_dir = $matches[1]; - - $default_headers = array( - 'name' => 'Module Name', - 'description' => 'Description', - 'experimental' => 'Experimental', - ); - - $module_data = get_file_data( $module_file, $default_headers, 'perflab_module' ); - - // Module name and description are the minimum requirements. - if ( ! $module_data['name'] || ! $module_data['description'] ) { - return false; - } - - // Experimental should be a boolean. - if ( 'yes' === strtolower( trim( $module_data['experimental'] ) ) ) { - $module_data['experimental'] = true; - } else { - $module_data['experimental'] = false; - } - - // Extract the module focus from the module directory. - if ( strpos( $module_dir, '/' ) ) { - list( $focus, $slug ) = explode( '/', $module_dir ); - $module_data['focus'] = $focus; - $module_data['slug'] = $slug; - } - - // Translate fields using low-level function since they come from PHP comments, including the necessary context for - // `_x()`. This must match how these are translated in the generated `/module-i18n.php` file. - $translatable_fields = array( - 'name' => 'module name', - 'description' => 'module description', - ); - foreach ( $translatable_fields as $field => $context ) { - // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralText - $module_data[ $field ] = translate_with_gettext_context( $module_data[ $field ], $context, 'performance-lab' ); - } - - return $module_data; -} - /** * Initializes admin pointer. * @@ -406,39 +74,6 @@ function perflab_admin_pointer( $hook_suffix ) { $current_user = get_current_user_id(); $dismissed = explode( ',', (string) get_user_meta( $current_user, 'dismissed_wp_pointers', true ) ); - /* - * If there are any active modules with inactive standalone plugins, - * show an admin pointer to prompt the user to migrate. - */ - $active_modules_with_inactive_plugins = perflab_get_active_module_data_with_inactive_standalone_plugins(); - if ( - ! empty( $active_modules_with_inactive_plugins ) - && ( current_user_can( 'install_plugins' ) || current_user_can( 'activate_plugins' ) ) - && ! in_array( 'perflab-module-migration-pointer', $dismissed, true ) - ) { - // Enqueue the pointer logic and return early. - wp_enqueue_style( 'wp-pointer' ); - wp_enqueue_script( 'wp-pointer' ); - add_action( - 'admin_print_footer_scripts', - static function () { - $content = sprintf( - /* translators: %s: settings page link */ - esc_html__( 'Your site is using modules which will be removed in the future in favor of their equivalent standalone plugins. Open %s to learn more about next steps to keep the functionality available.', 'performance-lab' ), - '' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '' - ); - perflab_render_pointer( - 'perflab-module-migration-pointer', - array( - 'heading' => __( 'Performance Lab: Action required', 'performance-lab' ), - 'content' => $content, - ) - ); - } - ); - return; - } - if ( in_array( 'perflab-admin-pointer', $dismissed, true ) ) { return; } @@ -469,7 +104,7 @@ function perflab_render_pointer( $pointer_id = 'perflab-admin-pointer', $args = if ( ! isset( $args['content'] ) ) { $args['content'] = sprintf( /* translators: %s: settings page link */ - esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features included in the plugin.', 'performance-lab' ), + esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features.', 'performance-lab' ), '' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '' ); } @@ -586,31 +221,6 @@ function perflab_enqueue_modules_page_scripts() { 'strategy' => 'defer', ) ); - - // Bail early if module is not active. - $get_active_modules_with_standalone_plugins = perflab_get_active_modules_with_standalone_plugins(); - if ( empty( $get_active_modules_with_standalone_plugins ) ) { - return; - } - - wp_enqueue_script( - 'perflab-module-migration-notice', - plugin_dir_url( __FILE__ ) . 'js/perflab-module-migration-notice.js', - array( 'wp-i18n' ), - '1.0.0', - array( - 'strategy' => 'defer', - ) - ); - - wp_localize_script( - 'perflab-module-migration-notice', - 'perflab_module_migration_notice', - array( - 'ajaxurl' => admin_url( 'admin-ajax.php' ), - 'nonce' => wp_create_nonce( 'perflab-install-activate-plugins' ), - ) - ); } /** @@ -700,89 +310,6 @@ function perflab_deactivate_plugin() { } add_action( 'admin_action_perflab_deactivate_plugin', 'perflab_deactivate_plugin' ); -// WordPress AJAX action to handle the button click event. -add_action( 'wp_ajax_perflab_install_activate_standalone_plugins', 'perflab_install_activate_standalone_plugins_callback' ); - -/** - * Handles the standalone plugin install and activation via AJAX. - * - * @since 2.8.0 - */ -function perflab_install_activate_standalone_plugins_callback() { - if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'perflab-install-activate-plugins' ) ) { - $status['errorMessage'] = __( 'Invalid nonce: Please refresh and try again.', 'performance-lab' ); - wp_send_json_error( $status ); - } - - if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) { - $status['errorMessage'] = __( 'Sorry, you are not allowed to manage plugins for this site. Please contact the administrator.', 'performance-lab' ); - wp_send_json_error( $status ); - } - - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php'; - - $plugins_to_activate = perflab_get_active_modules_with_standalone_plugins(); - $modules = perflab_get_module_settings(); - $plugins = get_plugins(); - $status = array(); - - foreach ( $plugins_to_activate as $module_slug ) { - - // Skip checking for already activated plugin. - if ( perflab_is_standalone_plugin_loaded( $module_slug ) ) { - continue; - } - - $plugin_slug = basename( $module_slug ); - $plugin_basename = $plugin_slug . '/load.php'; - $api = perflab_query_plugin_info( $plugin_slug ); - - // Return early if plugin API return an error. - if ( ! $api ) { - $status['errorMessage'] = html_entity_decode( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration.', 'performance-lab' ), ENT_QUOTES ); - wp_send_json_error( $status ); - } - - if ( ! $plugin_slug ) { - $status['errorMessage'] = __( 'Invalid plugin.', 'performance-lab' ); - wp_send_json_error( $status ); - } - - // Install the plugin if it is not installed yet. - if ( ! isset( $plugins[ $plugin_basename ] ) ) { - // Replace new Plugin_Installer_Skin with new Quiet_Upgrader_Skin when output needs to be suppressed. - $skin = new WP_Ajax_Upgrader_Skin( array( 'api' => $api ) ); - $upgrader = new Plugin_Upgrader( $skin ); - $result = $upgrader->install( $api['download_link'] ); - - if ( is_wp_error( $result ) ) { - $status['errorMessage'] = $result->get_error_message(); - wp_send_json_error( $status ); - } elseif ( is_wp_error( $skin->result ) ) { - $status['errorMessage'] = $skin->result->get_error_message(); - wp_send_json_error( $status ); - } elseif ( $skin->get_errors()->has_errors() ) { - $status['errorMessage'] = $skin->get_error_messages(); - wp_send_json_error( $status ); - } - } - - $result = activate_plugin( $plugin_basename ); - if ( is_wp_error( $result ) ) { - $status['errorMessage'] = $result->get_error_message(); - wp_send_json_error( $status ); - } - - // Deactivate legacy modules. - unset( $modules[ $module_slug ] ); - - update_option( PERFLAB_MODULES_SETTING, $modules ); - } - wp_send_json_success( $status ); -} - /** * Callback function hooked to admin_notices to render admin notices on the plugin's screen. * @@ -802,113 +329,4 @@ function perflab_plugin_admin_notices() { '; - $message .= sprintf( - /* translators: Module name */ - esc_html__( 'Your site is using the "%s" module which will be removed in the future in favor of its equivalent standalone plugin.', 'performance-lab' ), - esc_attr( $available_module_names[0] ) - ); - $message .= ' '; - $message .= esc_html( $additional_message ); - $message .= ' '; - $message .= esc_html__( 'This will not impact any of the underlying functionality.', 'performance-lab' ); - $message .= '

'; - } else { - $message = '

'; - $message .= esc_html__( 'Your site is using modules which will be removed in the future in favor of their equivalent standalone plugins.', 'performance-lab' ); - $message .= ' '; - $message .= esc_html( $additional_message ); - $message .= ' '; - $message .= esc_html__( 'This will not impact any of the underlying functionality.', 'performance-lab' ); - $message .= '

'; - $message .= '' . esc_html__( 'Available standalone plugins:', 'performance-lab' ) . ''; - $message .= '
    '; - foreach ( $available_module_names as $module_name ) { - $message .= sprintf( '
  1. %s
  2. ', esc_html( $module_name ) ); - } - $message .= '
'; - } - ?> -
- - -

- - -

- -
- - - { - const pluginsFile = path.join( '.', 'plugins.json' ); - fs.readFile( pluginsFile, 'utf8', ( err, jsonString ) => { - if ( err ) { - log( formats.error( `Error reading file from disk: "${ err }"` ) ); - } - - try { - const pluginsConfig = JSON.parse( jsonString ); - const plugins = pluginsConfig.modules; - if ( ! plugins ) { - log( - formats.error( - 'The given module configuration is invalid, the modules are missing, or they are misspelled.' - ) - ); - return; - } - for ( const moduleDir in plugins ) { - const pluginVersion = plugins[ moduleDir ]?.version; - const pluginSlug = plugins[ moduleDir ]?.slug; - - if ( ! pluginVersion || ! pluginSlug ) { - log( - formats.error( - 'The given module configuration is invalid, the module JSON object is missing the "version" and/or "slug" properties, or they are misspelled.' - ) - ); - return; - } - - try { - // Copy module files from the folder. - const modulePath = path.join( '.', 'modules/' + moduleDir ); - const buildModulePath = path.join( - '.', - 'build/' + pluginSlug - ); - try { - // Clean up build module files directory. - fs.rmSync( buildModulePath, { - force: true, - recursive: true, - } ); - - fs.copySync( modulePath, buildModulePath, { - overwrite: true, - } ); - } catch ( copyError ) { - log( - formats.error( - `Error copying files for plugin "${ pluginSlug }": "${ copyError }"` - ) - ); - continue; - } - - // Update file content. - updatePluginHeader( { - modulePath: moduleDir, - slug: pluginSlug, - version: pluginVersion, - pluginPath: buildModulePath, - } ); - - // Update text domain. - updateModuleDetails( { - pluginPath: buildModulePath, - regex: "[']performance-lab[']", - result: `'${ pluginSlug }'`, - } ); - - // Update `@package`. - updateModuleDetails( { - pluginPath: buildModulePath, - regex: '@package\\s{1,}performance-lab', - result: '@package ' + pluginSlug, - } ); - - // Update version constant. - updateModuleDetails( { - pluginPath: buildModulePath, - regex: "[']Performance Lab ['] . PERFLAB_VERSION", - result: `'${ pluginVersion }'`, - } ); - } catch ( error ) { - log( formats.error( `${ error }` ) ); - } - } - } catch ( jsonError ) { - log( - formats.error( `Error parsing JSON string: "${ jsonError }"` ) - ); - } - } ); -}; - -/** - * Updates the `load.php` file header information. - * - * @param {Object} settings Plugin settings. - */ -async function updatePluginHeader( settings ) { - const { modulePath, version, slug, pluginPath } = settings; - // Specific module `load.php` file content. - const buildLoadFile = path.join( pluginPath, 'load.php' ); - let buildLoadFileContent = ''; - try { - buildLoadFileContent = fs.readFileSync( buildLoadFile, 'utf-8' ); - } catch ( err ) { - log( - formats.error( - `Error reading the file "${ buildLoadFile }": "${ err }"` - ) - ); - } - - if ( buildLoadFileContent === '' ) { - log( formats.error( `Error reading the file "${ buildLoadFile }"` ) ); - return false; - } - const moduleHeader = await getModuleHeader( buildLoadFileContent ); - - // Get module header data. - const { name, description } = await getModuleDataFromHeader( moduleHeader ); - - const pluginHeader = `/** - * Plugin Name: ${ name } - * Plugin URI: https://github.com/WordPress/performance/tree/trunk/modules/${ modulePath } - * Description: ${ description } - * Requires at least: 6.3 - * Requires PHP: 7.0 - * Version: ${ version } - * Author: WordPress Performance Team - * Author URI: https://make.wordpress.org/performance/ - * License: GPLv2 or later - * License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * Text Domain: ${ slug } - * - * @package ${ slug } - `; - try { - // Replace the module file header. - fs.writeFileSync( - buildLoadFile, - buildLoadFileContent.replace( moduleHeader, pluginHeader ) - ); - } catch ( error ) { - log( - formats.error( `Error replacing module file header: "${ error }"` ) - ); - } -} - -/** - * Updates the text domain and package details in the build folder files content. - * - * @param {Object} settings Plugin settings. - */ -async function updateModuleDetails( settings ) { - const patterns = [ path.resolve( settings.pluginPath, './**/*.php' ) ]; - - const files = await glob( patterns, { - ignore: [ __filename ], - } ); - - const regexp = new RegExp( settings.regex, 'gm' ); - - files.forEach( ( file ) => { - let content = ''; - try { - content = fs.readFileSync( file, 'utf-8' ); - } catch ( err ) { - log( - formats.error( - `Error reading the file "${ file }": "${ err }"` - ) - ); - } - - if ( content === '' ) { - log( formats.error( `Error reading the file "${ file }"` ) ); - return false; - } - - if ( regexp.test( content ) ) { - try { - fs.writeFileSync( - file, - content.replace( regexp, `${ settings.result }` ) - ); - } catch ( error ) { - log( - formats.error( - `Error replacing content for regex "${ settings.regex }": "${ error }"` - ) - ); - } - } - } ); -} diff --git a/bin/plugin/commands/common.js b/bin/plugin/commands/common.js deleted file mode 100644 index 61916dba93..0000000000 --- a/bin/plugin/commands/common.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * External dependencies - */ -const path = require( 'path' ); -const glob = require( 'fast-glob' ); -const fs = require( 'fs' ); -const { log, formats } = require( '../lib/logger' ); - -/** - * @typedef WPModuleData - * - * @property {string} slug Module slug. - * @property {string} focus Module focus. - * @property {string} name Module name. - * @property {string} description Module description. - * @property {boolean} experimental Whether the module is experimental. - */ - -/** - * Definition of the groups areas as an object with a number to specify the order priority of each, - * with increments of 10 between each module, in order to allow space for any change down the road. - */ -const FOCUS_AREAS = { - images: 10, - 'js-and-css': 20, - database: 30, - measurement: 40, - 'object-cache': 50, -}; - -/** - * Returns a promise resolving to the list of data for all modules. - * - * @param {string} modulesDir Modules directory. - * @return {Promise<[]WPModuleData>} Promise resolving to module data list. - */ -exports.getModuleData = async ( modulesDir ) => { - const moduleFilePattern = path.join( modulesDir, '*/*/load.php' ); - const moduleFiles = await glob( path.resolve( '.', moduleFilePattern ) ); - - return moduleFiles - .map( ( moduleFile ) => { - let moduleFileContent = ''; - try { - moduleFileContent = fs.readFileSync( moduleFile, 'utf-8' ); - } catch ( error ) { - log( - formats.error( - `Error reading the file "${ moduleFile }": "${ error }"` - ) - ); - } - const moduleHeader = exports.getModuleHeader( moduleFileContent ); - - // Populate slug and focus based on file path. - const moduleDir = path.dirname( moduleFile ); - const moduleData = { - slug: path.basename( moduleDir ), - focus: path.basename( path.dirname( moduleDir ) ), - ...exports.getModuleDataFromHeader( moduleHeader ), - }; - return moduleData; - } ) - .filter( ( moduleData ) => { - const requiredProperties = []; - if ( ! moduleData.name ) { - requiredProperties.push( 'name' ); - } - - if ( ! moduleData.description ) { - requiredProperties.push( 'description' ); - } - - if ( typeof moduleData.experimental === 'undefined' ) { - requiredProperties.push( 'experimental' ); - } - - if ( requiredProperties.length >= 1 ) { - const properties = requiredProperties - .map( ( property ) => `'${ property }'` ) - .join( ', ' ); - log( - formats.warning( - `This module was not included because it misses required properties: ${ properties }.\nDetails: ${ JSON.stringify( - moduleData, - null, - 4 - ) }` - ) - ); - } - - return ( - moduleData.name && - moduleData.description && - typeof moduleData.experimental !== 'undefined' - ); - } ) - .sort( ( firstModule, secondModule ) => { - // Not the same focus group. - if ( firstModule.focus !== secondModule.focus ) { - return ( - FOCUS_AREAS[ firstModule.focus ] - - FOCUS_AREAS[ secondModule.focus ] - ); - } - - if ( firstModule.experimental !== secondModule.experimental ) { - return firstModule.experimental ? 1 : -1; - } - - // Lastly order alphabetically. - return firstModule.slug.localeCompare( secondModule.slug ); - } ); -}; - -/** - * Returns the list of module data for module. - * - * @param {string} moduleHeader Modules file header contetnt. - * @return {WPModuleData} Module data. - */ -exports.getModuleDataFromHeader = ( moduleHeader ) => { - const moduleData = {}; - // Map of module header => object property. - const headers = { - 'Module Name': 'name', - Description: 'description', - Experimental: 'experimental', - }; - - const regex = new RegExp( - `^(?:[ \t]* { - const regex = /\/\\*\\*[\s\S]+?(?=\*\/)/im; - const moduleHeader = moduleFileContent.match( regex )?.[ 0 ]; - return moduleHeader; -}; diff --git a/bin/plugin/commands/enabled-modules.js b/bin/plugin/commands/enabled-modules.js deleted file mode 100644 index bd85c7b38b..0000000000 --- a/bin/plugin/commands/enabled-modules.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * External dependencies - */ -const path = require( 'path' ); -const fs = require( 'fs' ); -const { EOL } = require( 'os' ); - -/** - * Internal dependencies - */ -const { log, formats } = require( '../lib/logger' ); -const { getModuleData } = require( './common' ); - -const TAB = '\t'; -const NEWLINE = EOL; -const FILE_HEADER = `', - description: 'Modules directory', - }, - { - argname: '-d, --output ', - description: 'Output file', - }, -]; - -/** - * Command that generates a PHP file with non-experimental module slugs. - * - * @param {WPEnabledModulesCommandOptions} opt - */ -exports.handler = async ( opt ) => { - await createEnabledModules( { - directory: opt.directory || 'modules', - output: opt.output || 'default-enabled-modules.php', - } ); -}; - -/** - * Gathers the non-experimental modules as the default enabled modules. - * - * @param {WPEnabledModulesSettings} settings Default enabled modules settings. - * - * @return {[]string} List of default enabled module paths relative to modules directory. - */ -async function getDefaultEnabledModules( settings ) { - const modulesData = await getModuleData( settings.directory ); - return modulesData - .filter( ( moduleData ) => ! moduleData.experimental ) - .map( ( moduleData ) => `${ moduleData.focus }/${ moduleData.slug }` ); -} - -/** - * Creates PHP file with the given default enabled modules. - * - * @param {[]string} enabledModules List of default enabled module paths relative to modules directory. - * @param {WPEnabledModulesSettings} settings Default enabled modules settings. - */ -function createEnabledModulesPHPFile( enabledModules, settings ) { - const output = enabledModules.map( ( enabledModule ) => { - // Escape single quotes. - return `${ TAB }'${ enabledModule.replace( /'/g, "\\'" ) }',`; - } ); - - const fileOutput = `${ FILE_HEADER }${ output.join( - NEWLINE - ) }${ FILE_FOOTER }`; - fs.writeFileSync( path.join( '.', settings.output ), fileOutput ); -} - -/** - * Gathers non-experimental modules and generates a PHP file with them. - * - * @param {WPEnabledModulesSettings} settings Default enabled modules settings. - */ -async function createEnabledModules( settings ) { - log( - formats.title( - `\n💃Gathering non-experimental modules for "${ settings.directory }" in "${ settings.output }"\n\n` - ) - ); - - try { - const enabledModules = await getDefaultEnabledModules( settings ); - createEnabledModulesPHPFile( enabledModules, settings ); - } catch ( error ) { - if ( error instanceof Error ) { - log( formats.error( error.stack ) ); - return; - } - } - - log( - formats.success( - `\n💃Non-experimental modules successfully set in "${ settings.output }"\n\n` - ) - ); -} diff --git a/bin/plugin/commands/get-plugin-dir.js b/bin/plugin/commands/get-plugin-dir.js deleted file mode 100644 index 5f05019a5b..0000000000 --- a/bin/plugin/commands/get-plugin-dir.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * External dependencies - */ -const path = require( 'path' ); - -/** - * Internal dependencies - */ -const { log } = require( '../lib/logger' ); - -exports.options = [ - { - argname: '-s, --slug ', - description: 'Slug to search out whether it is a plugin or module.', - }, -]; - -/** - * Command to get directory for plugin/module based on the slug. - * - * @param {Object} opt Command options. - * @param {string} opt.slug Plugin/module slug. - */ -exports.handler = async ( opt ) => { - doRunGetPluginDir( { - pluginsJsonFile: 'plugins.json', // Path to plugins.json file. - slug: opt.slug, - } ); -}; - -/** - * Prints directory root for plugin or module based on the slug. - * - * @param {Object} settings Plugin settings. - * @param {string} settings.pluginsJsonFile Path to plugins JSON file. - * @param {string} settings.slug Slug for the plugin or module. - */ -function doRunGetPluginDir( settings ) { - if ( settings.slug === undefined ) { - throw Error( 'A slug must be provided via the --slug (-s) argument.' ); - } - - // Resolve the absolute path to the plugins.json file. - const pluginsFile = path.join( - __dirname, - '../../../' + settings.pluginsJsonFile - ); - - try { - // Read the plugins.json file synchronously. - const { modules, plugins } = require( pluginsFile ); - - for ( const module of Object.values( modules ) ) { - if ( settings.slug === module.slug ) { - log( 'build' ); - return; - } - } - - for ( const plugin of Object.values( plugins ) ) { - if ( settings.slug === plugin ) { - log( 'plugins' ); - return; - } - } - } catch ( error ) { - throw Error( `Error reading file at "${ pluginsFile }": ${ error }` ); - } - - throw Error( - `The "${ settings.slug }" module/plugin slug is missing in the file "${ pluginsFile }".` - ); -} diff --git a/bin/plugin/commands/get-plugin-version.js b/bin/plugin/commands/get-plugin-version.js index e9a5649284..41d1d067a1 100644 --- a/bin/plugin/commands/get-plugin-version.js +++ b/bin/plugin/commands/get-plugin-version.js @@ -20,7 +20,7 @@ exports.options = [ * Command to get the plugin version based on the slug. * * @param {Object} opt Command options. - * @param {string} opt.slug Plugin/module slug. + * @param {string} opt.slug Plugin slug. */ exports.handler = async ( opt ) => { doRunGetPluginVersion( { @@ -34,7 +34,7 @@ exports.handler = async ( opt ) => { * * @param {Object} settings Plugin settings. * @param {string} settings.pluginsJsonFile Path to plugins JSON file. - * @param {string} settings.slug Slug for the plugin or module. + * @param {string} settings.slug Slug for the plugin. */ function doRunGetPluginVersion( settings ) { if ( settings.slug === undefined ) { @@ -49,14 +49,7 @@ function doRunGetPluginVersion( settings ) { try { // Read the plugins.json file synchronously. - const { modules, plugins } = require( pluginsFile ); - - for ( const module of Object.values( modules ) ) { - if ( settings.slug === module.slug ) { - log( module.version ); - return; - } - } + const { plugins } = require( pluginsFile ); for ( const plugin of Object.values( plugins ) ) { if ( settings.slug === plugin ) { @@ -91,6 +84,6 @@ function doRunGetPluginVersion( settings ) { } throw Error( - `The "${ settings.slug }" module/plugin slug is missing in the file "${ pluginsFile }".` + `The "${ settings.slug }" plugin slug is missing in the file "${ pluginsFile }".` ); } diff --git a/bin/plugin/commands/readme.js b/bin/plugin/commands/readme.js index bc48ce6d20..12f44b0df9 100644 --- a/bin/plugin/commands/readme.js +++ b/bin/plugin/commands/readme.js @@ -9,13 +9,11 @@ const fs = require( 'fs' ); */ const { log, formats } = require( '../lib/logger' ); const config = require( '../config' ); -const { getModuleData } = require( './common' ); const { getChangelog } = require( './changelog' ); /** * @typedef WPReadmeCommandOptions * - * @property {string=} directory Optional directory, default is the root `/modules` directory. * @property {string=} milestone Optional milestone title, to update the changelog in the readme. * @property {string=} token Optional personal GitHub access token, only relevant for changelog updates. */ @@ -25,16 +23,11 @@ const { getChangelog } = require( './changelog' ); * * @property {string} owner GitHub repository owner. * @property {string} repo GitHub repository name. - * @property {string} directory Modules directory. * @property {string=} milestone Optional milestone title, to update the changelog in the readme. * @property {string=} token Optional personal GitHub access token, only relevant for changelog updates. */ exports.options = [ - { - argname: '-d, --directory ', - description: 'Modules directory', - }, { argname: '-m, --milestone ', description: 'Milestone title, to update the changelog', @@ -54,46 +47,11 @@ exports.handler = async ( opt ) => { await updateReadme( { owner: config.githubRepositoryOwner, repo: config.githubRepositoryName, - directory: opt.directory || 'modules', milestone: opt.milestone, token: opt.token, } ); }; -/** - * Returns a promise resolving to the module description list string for the `readme.txt` file. - * - * @param {WPReadmeSettings} settings Readme settings. - * - * @return {Promise} Promise resolving to module description list in markdown, with trailing newline. - */ -async function getModuleDescriptionList( settings ) { - const modulesData = await getModuleData( settings.directory ); - - return modulesData - .map( - ( moduleData ) => - `* **${ moduleData.name }:** ${ moduleData.description }` - ) - .join( '\n' ) - .concat( '\n' ); -} - -/** - * Updates the `readme.txt` file with the given module description list. - * - * @param {string} moduleList Module description list in markdown, with trailing newline. - */ -function updateReadmeModuleDescriptionList( moduleList ) { - const readmeFile = path.join( '.', 'readme.txt' ); - const fileContent = fs.readFileSync( readmeFile, 'utf8' ); - const newContent = fileContent.replace( - /(the following performance modules:\s+)((\*.*\n)+)/, - ( match, prefix ) => `${ prefix }${ moduleList }` - ); - fs.writeFileSync( readmeFile, newContent ); -} - /** * Updates the `readme.txt` file with the given changelog. * @@ -132,28 +90,10 @@ async function updateReadme( settings ) { if ( settings.milestone ) { log( formats.title( - `\n💃Updating readme.txt for "${ settings.directory }" and changelog for milestone "${ settings.milestone }"\n\n` - ) - ); - } else { - log( - formats.title( - `\n💃Updating readme.txt for "${ settings.directory }"\n\n` + `\n💃Updating readme.txt changelog for milestone "${ settings.milestone }"\n\n` ) ); - } - try { - const moduleList = await getModuleDescriptionList( settings ); - updateReadmeModuleDescriptionList( moduleList ); - } catch ( error ) { - if ( error instanceof Error ) { - log( formats.error( error.stack ) ); - return; - } - } - - if ( settings.milestone ) { try { const changelog = await getChangelog( { owner: settings.owner, @@ -168,7 +108,6 @@ async function updateReadme( settings ) { return; } } + log( formats.success( `\n💃readme.txt successfully updated\n\n` ) ); } - - log( formats.success( `\n💃readme.txt successfully updated\n\n` ) ); } diff --git a/bin/plugin/commands/test-plugins.js b/bin/plugin/commands/test-plugins.js index 5200bd57af..bb71e185cc 100644 --- a/bin/plugin/commands/test-plugins.js +++ b/bin/plugin/commands/test-plugins.js @@ -414,55 +414,32 @@ function doRunStandalonePluginTests( settings ) { try { // Read the plugins.json file synchronously. - const { modules, plugins } = require( pluginsFile ); + const { plugins } = require( pluginsFile ); // Create an array of plugins from entries in plugins JSON file. - builtPlugins = Object.keys( modules ) - .filter( ( item ) => { - if ( - ! fs.pathExistsSync( - `${ settings.builtPluginsDir }${ modules[ item ].slug }` - ) - ) { + builtPlugins = Object.values( plugins ) + .filter( ( plugin ) => { + try { + fs.copySync( + `${ settings.pluginsDir }${ plugin }/`, + `${ settings.builtPluginsDir }${ plugin }/`, + { + overwrite: true, + } + ); + log( formats.success( `Copied plugin "${ plugin }".\n` ) ); + return true; + } catch ( e ) { + // Handle the error appropriately log( formats.error( - `Built plugin path "${ settings.builtPluginsDir }${ modules[ item ].slug }" not found, skipping and removing from plugin list` + `Error copying plugin "${ plugin }": ${ e.message }` ) ); return false; } - return true; } ) - .map( ( item ) => modules[ item ].slug ); - - // Create an array of plugins from entries in plugins JSON file. - builtPlugins = builtPlugins.concat( - Object.values( plugins ) - .filter( ( plugin ) => { - try { - fs.copySync( - `${ settings.pluginsDir }${ plugin }/`, - `${ settings.builtPluginsDir }${ plugin }/`, - { - overwrite: true, - } - ); - log( - formats.success( `Copied plugin "${ plugin }".\n` ) - ); - return true; - } catch ( e ) { - // Handle the error appropriately - log( - formats.error( - `Error copying plugin "${ plugin }": ${ e.message }` - ) - ); - return false; - } - } ) - .map( ( plugin ) => plugin ) - ); + .map( ( plugin ) => plugin ); } catch ( error ) { throw Error( `Error reading file at "${ pluginsFile }": ${ error }` ); } diff --git a/bin/plugin/commands/translations.js b/bin/plugin/commands/translations.js deleted file mode 100644 index cca33749c5..0000000000 --- a/bin/plugin/commands/translations.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * External dependencies - */ -const path = require( 'path' ); -const fs = require( 'fs' ); -const { EOL } = require( 'os' ); - -/** - * Internal dependencies - */ -const { log, formats } = require( '../lib/logger' ); -const config = require( '../config' ); -const { getModuleData } = require( './common' ); - -const TAB = '\t'; -const NEWLINE = EOL; -const FILE_HEADER = `', - description: 'Modules directory', - }, - { - argname: '-d, --output ', - description: 'Output file', - }, -]; - -/** - * Command that generates a PHP file from module header translation strings. - * - * @param {WPTranslationsCommandOptions} opt - */ -exports.handler = async ( opt ) => { - await createTranslations( { - textDomain: config.textDomain, - directory: opt.directory || 'modules', - output: opt.output || 'module-i18n.php', - } ); -}; - -/** - * Parses module header translation strings. - * - * @param {WPTranslationsSettings} settings Translations settings. - * - * @return {[]WPTranslationEntry} List of translation entries. - */ -async function getTranslations( settings ) { - const modulesData = await getModuleData( settings.directory ); - const moduleTranslations = modulesData.map( ( moduleData ) => { - return [ - { - text: moduleData.name, - context: 'module name', - }, - { - text: moduleData.description, - context: 'module description', - }, - ]; - } ); - - return moduleTranslations.flat(); -} - -/** - * Parses module header translation strings. - * - * @param {[]WPTranslationEntry} translations List of translation entries. - * @param {WPTranslationsSettings} settings Translations settings. - */ -function createTranslationsPHPFile( translations, settings ) { - const output = translations.map( ( translation ) => { - // Escape single quotes. - return `${ TAB }_x( '${ translation.text.replace( /'/g, "\\'" ) }', '${ - translation.context - }', '${ settings.textDomain }' ),`; - } ); - - const fileOutput = `${ FILE_HEADER }${ output.join( - NEWLINE - ) }${ FILE_FOOTER }`; - fs.writeFileSync( path.join( '.', settings.output ), fileOutput ); -} - -/** - * Parses module header translation strings and generates a PHP file with them. - * - * @param {WPTranslationsSettings} settings Translations settings. - */ -async function createTranslations( settings ) { - log( - formats.title( - `\n💃Preparing module translations for "${ settings.directory }" in "${ settings.output }"\n\n` - ) - ); - - try { - const translations = await getTranslations( settings ); - createTranslationsPHPFile( translations, settings ); - } catch ( error ) { - if ( error instanceof Error ) { - log( formats.error( error.stack ) ); - return; - } - } - - log( - formats.success( - `\n💃Module translations successfully set in "${ settings.output }"\n\n` - ) - ); -} diff --git a/default-enabled-modules.php b/default-enabled-modules.php deleted file mode 100644 index 74c9838232..0000000000 --- a/default-enabled-modules.php +++ /dev/null @@ -1,8 +0,0 @@ - 'object', - 'sanitize_callback' => 'perflab_sanitize_modules_setting', - 'default' => perflab_get_modules_setting_default(), - ) - ); -} -add_action( 'init', 'perflab_register_modules_setting' ); - -/** - * Gets the default value for the performance modules setting. - * - * @since 1.0.0 - * - * @return array Associative array of module settings keyed by module slug. - */ -function perflab_get_modules_setting_default() { - // Since the default relies on some minimal logic that includes requiring an additional file, - // the result is "cached" in a static variable. - static $default_option = null; - - if ( null === $default_option ) { - // To set the default value for which modules are enabled, rely on this generated file. - $default_enabled_modules = require PERFLAB_PLUGIN_DIR_PATH . 'default-enabled-modules.php'; - $default_option = array_reduce( - $default_enabled_modules, - static function ( $module_settings, $module_dir ) { - $module_settings[ $module_dir ] = array( 'enabled' => true ); - return $module_settings; - }, - array() - ); - } - - return $default_option; -} - -/** - * Sanitizes the performance modules setting. - * - * @since 1.0.0 - * - * @param mixed $value Modules setting value. - * @return array Sanitized modules setting value. - */ -function perflab_sanitize_modules_setting( $value ) { - if ( ! is_array( $value ) ) { - return array(); - } - - // Ensure that every element is an array with an 'enabled' key. - return array_filter( - array_map( - static function ( $module_settings ) { - if ( ! is_array( $module_settings ) ) { - return array(); - } - return array_merge( - array( 'enabled' => false ), - $module_settings - ); - }, - $value - ) - ); -} - -/** - * Gets the performance module settings. - * - * @since 1.0.0 - * - * @return array Associative array of module settings keyed by module slug. - */ -function perflab_get_module_settings() { - // Even though a default value is registered for this setting, the default must be explicitly - // passed here, to support scenarios where this function is called before the 'init' action, - // for example when loading the active modules. - return (array) get_option( PERFLAB_MODULES_SETTING, perflab_get_modules_setting_default() ); -} - -/** - * Gets the active performance modules. - * - * @since 1.0.0 - * - * @return array List of active module slugs. - */ -function perflab_get_active_modules() { - $modules = array_keys( - array_filter( - perflab_get_module_settings(), - static function ( $module_settings ) { - return isset( $module_settings['enabled'] ) && $module_settings['enabled']; - } - ) - ); - - /** - * Filters active modules to allow programmatically control which modules are active. - * - * @since 1.0.0 - * - * @param array $modules An array of the currently active modules. - */ - $modules = apply_filters( 'perflab_active_modules', $modules ); - - return $modules; -} - -/** - * Gets the active and valid performance modules. - * - * @since 1.3.0 - * @since 2.2.0 Adds an additional check for standalone plugins. - * - * @param string $module Slug of the module. - * @return bool True if the module is active and valid, otherwise false. - */ -function perflab_is_valid_module( $module ) { - - if ( empty( $module ) ) { - return false; - } - - // Do not load the module if it can be loaded by a separate plugin. - if ( perflab_is_standalone_plugin_loaded( $module ) ) { - return false; - } - - // Do not load module if no longer exists. - $module_file = PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/load.php'; - if ( ! file_exists( $module_file ) ) { - return false; - } - - // Do not load module if it cannot be loaded, e.g. if it was already merged and is available in WordPress core. - $can_load_module = perflab_can_load_module( $module ); - return $can_load_module && ! is_wp_error( $can_load_module ); -} - /** * Gets the content attribute for the generator tag for the Performance Lab plugin. * @@ -197,21 +47,19 @@ function perflab_is_valid_module( $module ) { * * @since 1.1.0 * @since 2.9.0 The generator tag now includes the active standalone plugin slugs. + * @since n.e.x.t Remove modules from the generator tag. */ function perflab_get_generator_content() { - $active_and_valid_modules = array_filter( perflab_get_active_modules(), 'perflab_is_valid_module' ); - $active_plugins = array(); - foreach ( perflab_get_standalone_plugin_version_constants( 'plugins' ) as $plugin_slug => $constant_name ) { + foreach ( perflab_get_standalone_plugin_version_constants() as $plugin_slug => $constant_name ) { if ( defined( $constant_name ) && ! str_starts_with( constant( $constant_name ), 'Performance Lab ' ) ) { $active_plugins[] = $plugin_slug; } } return sprintf( - 'Performance Lab %1$s; modules: %2$s; plugins: %3$s', + 'Performance Lab %1$s; plugins: %2$s', PERFLAB_VERSION, - implode( ', ', $active_and_valid_modules ), implode( ', ', $active_plugins ) ); } @@ -231,90 +79,14 @@ function perflab_render_generator() { add_action( 'wp_head', 'perflab_render_generator' ); /** - * Checks whether the given module can be loaded in the current environment. - * - * @since 1.3.0 - * @since 2.8.0 The function may now alternatively return a WP_Error. - * - * @param string $module Slug of the module. - * @return bool|WP_Error True if the module can be loaded, or false or a WP_Error with more concrete information otherwise. - */ -function perflab_can_load_module( $module ) { - $module_load_file = PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/can-load.php'; - - // If the `can-load.php` file does not exist, assume the module can be loaded. - if ( ! file_exists( $module_load_file ) ) { - return true; - } - - // Require the file to get the closure for whether the module can load. - $module = require $module_load_file; - - // If the `can-load.php` file is invalid and does not return a closure, assume the module can be loaded. - if ( ! is_callable( $module ) ) { - return true; - } - - // Call the closure to determine whether the module can be loaded. - $result = $module(); - - if ( is_wp_error( $result ) ) { - return $result; - } - - return (bool) $result; -} - -/** - * Checks whether the given module has already been loaded by a separate plugin. - * - * @since 2.2.0 - * - * @param string $module Slug of the module. - * @return bool Whether the module has already been loaded by a separate plugin. - */ -function perflab_is_standalone_plugin_loaded( $module ) { - $standalone_plugins_constants = perflab_get_standalone_plugin_version_constants( 'modules' ); - if ( - isset( $standalone_plugins_constants[ $module ] ) && - defined( $standalone_plugins_constants[ $module ] ) && - ! str_starts_with( constant( $standalone_plugins_constants[ $module ] ), 'Performance Lab ' ) - ) { - return true; - } - return false; -} - -/** - * Gets the standalone plugin constants used for each module with a standalone plugin. - * - * @since 2.2.0 - * @deprecated 2.9.0 - * - * @return array Map of module path to version constant used. - */ -function perflab_get_standalone_plugins_constants() { - _deprecated_function( __FUNCTION__, 'Performance Lab 2.9.0', "perflab_get_standalone_plugin_version_constants( 'modules' )" ); - return perflab_get_standalone_plugin_version_constants( 'modules' ); -} - -/** - * Gets the standalone plugin constants used for each available standalone plugin, or module with a standalone plugin. + * Gets the standalone plugin constants used for each available standalone plugin. * * @since 2.9.0 + * @since n.e.x.t Remove $source parameter. * - * @param string $source Optional. Either 'plugins' or 'modules'. Default 'plugins'. * @return array Map of plugin slug / module path and the version constant used. */ -function perflab_get_standalone_plugin_version_constants( $source = 'plugins' ) { - if ( 'modules' === $source ) { - /* - * This list includes all modules which are also available as standalone plugins, - * as `$module_dir => $version_constant` pairs. - */ - return array(); - } - +function perflab_get_standalone_plugin_version_constants() { /* * This list includes all standalone plugins that are part of the Performance Lab project, * as `$plugin_slug => $version_constant` pairs. @@ -328,22 +100,6 @@ function perflab_get_standalone_plugin_version_constants( $source = 'plugins' ) ); } -/** - * Loads the active and valid performance modules. - * - * @since 1.0.0 - * @since 1.3.0 Renamed to perflab_load_active_and_valid_modules(). - */ -function perflab_load_active_and_valid_modules() { - $active_and_valid_modules = array_filter( perflab_get_active_modules(), 'perflab_is_valid_module' ); - - foreach ( $active_and_valid_modules as $module ) { - - require_once PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module . '/load.php'; - } -} -add_action( 'plugins_loaded', 'perflab_load_active_and_valid_modules' ); - /** * Places the Performance Lab's object cache drop-in in the drop-ins folder. * @@ -512,165 +268,3 @@ function perflab_maybe_remove_object_cache_dropin() { require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/server-timing.php'; require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/plugins.php'; } - -/** - * Trigger actions when a module gets activated or deactivated. - * - * @since 1.8.0 - * - * @param mixed $old_value Old value of the option. - * @param mixed $value New value of the option. - */ -function perflab_run_module_activation_deactivation( $old_value, $value ) { - $old_value = (array) $old_value; - $value = (array) $value; - - // Get the list of modules that were activated, and load the activate.php files if they exist. - if ( ! empty( $value ) ) { - $reset_migration_pointer_dismissals = false; - foreach ( $value as $module => $module_settings ) { - if ( ! empty( $module_settings['enabled'] ) && ( empty( $old_value[ $module ] ) || empty( $old_value[ $module ]['enabled'] ) ) ) { - perflab_activate_module( PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module ); - $reset_migration_pointer_dismissals = true; - } - } - if ( $reset_migration_pointer_dismissals ) { - // Retrieve a list of active modules with associated standalone plugins. - $active_modules_with_plugins = perflab_get_active_modules_with_standalone_plugins(); - - /* - * Check if there are any active modules with compatible standalone plugins. - * If no such modules are found bail early. - */ - if ( empty( $active_modules_with_plugins ) ) { - return; - } - - $current_user = wp_get_current_user(); - - /* - * Disable WordPress pointers for specific users based on conditions. - * - * Checks if there is a large user count on the site. If true, - * disables pointers for the current user only. Otherwise, disables - * pointers for users with the same role as the current user. - */ - if ( wp_is_large_user_count() ) { - perflab_undismiss_module_migration_pointer( $current_user ); - } else { - $current_user_roles = $current_user->roles; - $current_user_role = array_shift( $current_user_roles ); - - $args = array( - 'role' => $current_user_role, - 'meta_query' => array( - array( - 'key' => 'dismissed_wp_pointers', - 'value' => 'perflab-module-migration-pointer', - 'compare' => 'LIKE', - ), - ), - ); - - $users = get_users( $args ); - - foreach ( $users as $user ) { - perflab_undismiss_module_migration_pointer( $user ); - } - } - } - } - - // Get the list of modules that were deactivated, and load the deactivate.php files if they exist. - if ( ! empty( $old_value ) ) { - foreach ( $old_value as $module => $module_settings ) { - if ( ! empty( $module_settings['enabled'] ) && ( empty( $value[ $module ] ) || empty( $value[ $module ]['enabled'] ) ) ) { - perflab_deactivate_module( PERFLAB_PLUGIN_DIR_PATH . 'modules/' . $module ); - } - } - } - - return $value; -} - -/** - * Reverts the module migration pointer dismissal for the given user. - * - * @since 2.8.0 - * - * @param WP_User $user The WP_User object. - */ -function perflab_undismiss_module_migration_pointer( $user ) { - $dismissed = array_filter( explode( ',', (string) get_user_meta( $user->ID, 'dismissed_wp_pointers', true ) ) ); - - $pointer_index = array_search( 'perflab-module-migration-pointer', $dismissed, true ); - if ( false === $pointer_index ) { - return; - } - - unset( $dismissed[ $pointer_index ] ); - $dismissed = implode( ',', $dismissed ); - - update_user_meta( $user->ID, 'dismissed_wp_pointers', $dismissed ); -} - -/** - * Activate a module. - * - * Runs the activate.php file if it exists. - * - * @since 1.8.0 - * - * @param string $module_dir_path The module's directory path. - */ -function perflab_activate_module( $module_dir_path ) { - $module_activation_file = $module_dir_path . '/activate.php'; - if ( ! file_exists( $module_activation_file ) ) { - return; - } - $module = require $module_activation_file; - if ( ! is_callable( $module ) ) { - return; - } - $module(); -} - -/** - * Deactivate a module. - * - * Runs the deactivate.php file if it exists. - * - * @since 1.8.0 - * - * @param string $module_dir_path The module's directory path. - */ -function perflab_deactivate_module( $module_dir_path ) { - $module_deactivation_file = $module_dir_path . '/deactivate.php'; - if ( ! file_exists( $module_deactivation_file ) ) { - return; - } - $module = require $module_deactivation_file; - if ( ! is_callable( $module ) ) { - return; - } - $module(); -} - -// Run the module activation & deactivation actions when the option is updated. -add_action( 'update_option_' . PERFLAB_MODULES_SETTING, 'perflab_run_module_activation_deactivation', 10, 2 ); - -// Run the module activation & deactivation actions when the option is added. -add_action( - 'add_option_' . PERFLAB_MODULES_SETTING, - /** - * Fires after the option has been added. - * - * @param string $option Name of the option to add. - * @param mixed $value Value of the option. - */ - static function ( $option, $value ) { - perflab_run_module_activation_deactivation( perflab_get_modules_setting_default(), $value ); - }, - 10, - 2 -); diff --git a/module-i18n.php b/module-i18n.php deleted file mode 100644 index 5270c77bf1..0000000000 --- a/module-i18n.php +++ /dev/null @@ -1,15 +0,0 @@ - - - - default-enabled-modules.php - module-i18n.php - - includes/server-timing/object-cache.copy.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 55eff44c9d..13084b48f0 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,9 +4,7 @@ parameters: level: 0 paths: - admin/ - - default-enabled-modules.php - load.php - - module-i18n.php - includes/ - plugins/ - tests/ diff --git a/readme.txt b/readme.txt index 936feaa574..c197259e9a 100644 --- a/readme.txt +++ b/readme.txt @@ -9,19 +9,11 @@ License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, javascript, site health, measurement, object caching -Performance plugin from the WordPress Performance Team, which is a collection of standalone performance modules. +Performance plugin from the WordPress Performance Team, which is a collection of standalone performance plugins. == Description == -The Performance Lab plugin is a collection of modules focused on enhancing performance of your site, most of which should eventually be merged into WordPress core. The plugin allows to individually enable and test the modules to get their benefits before they become available in WordPress core, and to provide feedback to further improve the solutions. - -Currently the plugin includes the following performance modules: - -* **Dominant Color Images:** Adds support to store the dominant color of newly uploaded images and create a placeholder background of that color. -* **WebP Support Health Check:** Adds a WebP support check in Site Health status. -* **WebP Uploads:** Creates WebP versions for new JPEG image uploads if supported by the server. -* **Enqueued Assets Health Check:** Adds a CSS and JS resource check in Site Health status. -* **Autoloaded Options Health Check:** Adds a check for autoloaded options in Site Health status. +The Performance Lab plugin is a collection of plugins focused on enhancing performance of your site, most of which should eventually be merged into WordPress core. The plugin allows to individually enable and test the plugins to get their benefits before they become available in WordPress core, and to provide feedback to further improve the solutions. == Installation == @@ -40,13 +32,13 @@ Currently the plugin includes the following performance modules: = After activation = 1. Visit the new **Settings > Performance** menu. -2. Enable the individual modules you would like to use. +2. Enable the individual plugins you would like to use. == Frequently Asked Questions == = What is the purpose of this plugin? = -The primary purpose of the Performance Lab plugin is to allow testing of various performance modules for which the goal is to eventually land in WordPress core. It is essentially a collection of "feature plugins", which makes it different from other performance plugins that offer performance features which are not targeted at WordPress core and potentially rely on functionality that would not be feasible to use in WordPress core. The list of available modules will regularly change: Existing modules may be removed after they have been released in WordPress core, while new modules may be added in any release. +The primary purpose of the Performance Lab plugin is to allow testing of various performance plugins for which the goal is to eventually land in WordPress core. It is essentially a collection of "feature plugins", which makes it different from other performance plugins that offer performance features which are not targeted at WordPress core and potentially rely on functionality that would not be feasible to use in WordPress core. The list of available plugins will regularly change: Existing plugins may be removed after they have been released in WordPress core, while new modules may be added in any release. = Can I use this plugin on my production site? = diff --git a/tests/admin/load-tests.php b/tests/admin/load-tests.php index 83e4555d8e..b788321eff 100644 --- a/tests/admin/load-tests.php +++ b/tests/admin/load-tests.php @@ -10,55 +10,6 @@ */ class Admin_Load_Tests extends WP_UnitTestCase { - private static $demo_modules = array( - 'js-and-css/demo-module-1' => array( - 'name' => 'Demo Module 1', - 'description' => 'This is the description for demo module 1.', - 'experimental' => false, - 'focus' => 'js-and-css', - 'slug' => 'demo-module-1', - ), - 'something/demo-module-2' => array( - 'name' => 'Demo Module 2', - 'description' => 'This is the description for demo module 2.', - 'experimental' => true, - 'focus' => 'something', - 'slug' => 'demo-module-2', - ), - 'images/demo-module-3' => array( - 'name' => 'Demo Module 3', - 'description' => 'This is the description for demo module 3.', - 'experimental' => false, - 'focus' => 'images', - 'slug' => 'demo-module-3', - ), - 'check-error/demo-module-4' => array( - 'name' => 'Demo Module 4', - 'description' => 'This is the description for demo module 4.', - 'experimental' => false, - 'focus' => 'check-error', - 'slug' => 'demo-module-4', - ), - ); - - private static $demo_focus_areas = array( - 'images' => array( - 'name' => 'Images', - ), - 'js-and-css' => array( - 'name' => 'js-and-css', - ), - 'database' => array( - 'name' => 'Database', - ), - 'measurement' => array( - 'name' => 'Measurement', - ), - 'object-cache' => array( - 'name' => 'Object Cache', - ), - ); - public function test_perflab_add_modules_page() { global $_wp_submenu_nopriv; @@ -89,84 +40,6 @@ public function test_perflab_add_modules_page() { // Reset relevant globals and filters. $_wp_submenu_nopriv = array(); remove_all_filters( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ) ); - - // Does not register the page if the perflab_active_modules filter is used. - add_filter( 'perflab_active_modules', '__return_array' ); - $hook_suffix = perflab_add_modules_page(); - $this->assertFalse( $hook_suffix ); - $this->assertFalse( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_MODULES_SCREEN ] ) ); - // Ensure plugin action link is not added. - $this->assertFalse( (bool) has_action( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' ) ); - } - - public function test_perflab_load_modules_page() { - global $wp_settings_sections, $wp_settings_fields; - - // Reset relevant globals. - $wp_settings_sections = array(); - $wp_settings_fields = array(); - - // Pass no modules, resulting in nothing being registered. - perflab_load_modules_page( array(), self::$demo_focus_areas ); - $this->assertFalse( ! empty( $wp_settings_sections[ PERFLAB_MODULES_SCREEN ] ) ); - $this->assertFalse( ! empty( $wp_settings_fields[ PERFLAB_MODULES_SCREEN ] ) ); - - // Reset relevant globals. - $wp_settings_sections = array(); - $wp_settings_fields = array(); - - // Pass demo modules, resulting in relevant sections and fields for those modules being registered. - perflab_load_modules_page( self::$demo_modules, self::$demo_focus_areas ); - $this->assertTrue( ! empty( $wp_settings_sections[ PERFLAB_MODULES_SCREEN ] ) ); - $this->assertSame( - array( - 'images' => array( - 'id' => 'images', - 'title' => 'Images', - 'callback' => null, - 'before_section' => '', - 'after_section' => '', - 'section_class' => '', - ), - 'js-and-css' => array( - 'id' => 'js-and-css', - 'title' => 'js-and-css', - 'callback' => null, - 'before_section' => '', - 'after_section' => '', - 'section_class' => '', - ), - 'other' => array( - 'id' => 'other', - 'title' => 'Other', - 'callback' => null, - 'before_section' => '', - 'after_section' => '', - 'section_class' => '', - ), - ), - $wp_settings_sections[ PERFLAB_MODULES_SCREEN ] - ); - $this->assertEqualSets( - array( - 'images', - 'js-and-css', - 'other', - ), - array_keys( $wp_settings_fields[ PERFLAB_MODULES_SCREEN ] ) - ); - $this->assertEqualSets( - array( 'images/demo-module-3' ), - array_keys( $wp_settings_fields[ PERFLAB_MODULES_SCREEN ]['images'] ) - ); - $this->assertEqualSets( - array( 'js-and-css/demo-module-1' ), - array_keys( $wp_settings_fields[ PERFLAB_MODULES_SCREEN ]['js-and-css'] ) - ); - $this->assertEqualSets( - array( 'something/demo-module-2', 'check-error/demo-module-4' ), - array_keys( $wp_settings_fields[ PERFLAB_MODULES_SCREEN ]['other'] ) - ); } public function test_perflab_render_modules_page() { @@ -174,58 +47,7 @@ public function test_perflab_render_modules_page() { perflab_render_modules_page(); $output = ob_get_clean(); $this->assertStringContainsString( '
', $output ); - $this->assertStringContainsString( "", $output ); - } - - public function test_perflab_render_modules_page_field() { - $module_slug = 'js-and-css/demo-module-1'; - $module_data = self::$demo_modules[ $module_slug ]; - $module_settings = array( 'enabled' => false ); - - // Assert correct 'id' and 'name' attributes, label, and unchecked checkbox. - ob_start(); - perflab_render_modules_page_field( $module_slug, $module_data, $module_settings ); - $output = ob_get_clean(); - $this->assertStringContainsString( ' id="module_' . $module_slug . '_enabled"', $output ); - $this->assertStringContainsString( ' name="' . PERFLAB_MODULES_SETTING . '[' . $module_slug . '][enabled]"', $output ); - $this->assertStringContainsString( 'Enable ' . $module_data['name'], $output ); - $this->assertStringNotContainsString( ' checked', $output ); - - // Assert correct 'id' and 'name' attributes, experimental label, and checked checkbox. - $module_data['experimental'] = true; - $module_settings['enabled'] = true; - ob_start(); - perflab_render_modules_page_field( $module_slug, $module_data, $module_settings ); - $output = ob_get_clean(); - $this->assertStringContainsString( ' id="module_' . $module_slug . '_enabled"', $output ); - $this->assertStringContainsString( ' name="' . PERFLAB_MODULES_SETTING . '[' . $module_slug . '][enabled]"', $output ); - $this->assertStringContainsString( 'Enable ' . $module_data['name'] . ' (experimental)', $output ); - $this->assertStringContainsString( " checked='checked'", $output ); - } - - public function test_perflab_get_focus_areas() { - $expected_focus_areas = array( - 'images', - 'js-and-css', - 'database', - 'measurement', - 'object-cache', - ); - $this->assertSame( $expected_focus_areas, array_keys( perflab_get_focus_areas() ) ); - } - - public function test_perflab_get_modules() { - // Use test data directory with demo modules that match the modules declared on top of this file. - $modules = perflab_get_modules( TESTS_PLUGIN_DIR . '/tests/testdata/demo-modules' ); - $this->assertSame( self::$demo_modules, $modules ); - } - - public function test_perflab_get_module_data() { - // Use test data directory with demo modules that match the modules declared on top of this file. - foreach ( self::$demo_modules as $module_slug => $expected_module_data ) { - $module_data = perflab_get_module_data( TESTS_PLUGIN_DIR . '/tests/testdata/demo-modules/' . $module_slug . '/load.php' ); - $this->assertSame( $expected_module_data, $module_data ); - } + $this->assertStringNotContainsString( "", $output ); } public function test_perflab_plugin_action_links_add_settings() { diff --git a/tests/load-tests.php b/tests/load-tests.php index f387e93f8b..e31e16c1a4 100644 --- a/tests/load-tests.php +++ b/tests/load-tests.php @@ -7,140 +7,14 @@ class Load_Tests extends WP_UnitTestCase { - public function test_perflab_register_modules_setting() { - global $new_allowed_options, $wp_registered_settings; - - // Reset relevant globals. - $wp_registered_settings = array(); - // `$new_allowed_options` was only introduced in WordPress 5.5. - if ( isset( $new_allowed_options ) ) { - $new_allowed_options = array(); - } - - perflab_register_modules_setting(); - - // Assert that the setting is correctly registered. - $settings = get_registered_settings(); - $this->assertTrue( isset( $settings[ PERFLAB_MODULES_SETTING ] ) ); - // `$new_allowed_options` was only introduced in WordPress 5.5. - if ( isset( $new_allowed_options ) ) { - $this->assertTrue( isset( $new_allowed_options[ PERFLAB_MODULES_SCREEN ] ) ); - } - - // Assert that registered default works correctly. - $this->assertSame( perflab_get_modules_setting_default(), get_option( PERFLAB_MODULES_SETTING ) ); - - // Assert that most basic sanitization works correctly (an array is required). - update_option( PERFLAB_MODULES_SETTING, 'invalid' ); - $this->assertSame( array(), get_option( PERFLAB_MODULES_SETTING ) ); - } - - public function test_perflab_sanitize_modules_setting() { - // Assert that any non-array value gets sanitized to an empty array. - $sanitized = perflab_sanitize_modules_setting( 'invalid' ); - $this->assertSame( array(), $sanitized ); - - // Assert that any non-array value within the array gets stripped. - $sanitized = perflab_sanitize_modules_setting( - array( - 'valid-module' => array( 'enabled' => true ), - 'invalid-module' => 'invalid', - ) - ); - $this->assertSame( array( 'valid-module' => array( 'enabled' => true ) ), $sanitized ); - - // Assert that every array value within the array has an 'enabled' key. - $sanitized = perflab_sanitize_modules_setting( - array( 'my-module' => array() ) - ); - $this->assertSame( array( 'my-module' => array( 'enabled' => false ) ), $sanitized ); - } - - public function test_perflab_get_modules_setting_default() { - $default_enabled_modules = require PERFLAB_PLUGIN_DIR_PATH . 'default-enabled-modules.php'; - $expected = array(); - foreach ( $default_enabled_modules as $default_enabled_module ) { - $expected[ $default_enabled_module ] = array( 'enabled' => true ); - } - - $this->assertSame( $expected, perflab_get_modules_setting_default() ); - } - - public function test_perflab_get_module_settings() { - // Assert that by default the settings are using the same value as the registered default. - $settings = perflab_get_module_settings(); - $this->assertEqualSetsWithIndex( perflab_get_modules_setting_default(), $settings ); - - // More specifically though, assert that the default is also passed through to the - // get_option() call, to support scenarios where the function is called before 'init'. - // Unhook the registered default logic to verify the default comes from the passed value. - remove_all_filters( 'default_option_' . PERFLAB_MODULES_SETTING ); - $has_passed_default = false; - add_filter( - 'default_option_' . PERFLAB_MODULES_SETTING, - static function ( $current_default, $option, $passed_default ) use ( &$has_passed_default ) { - // This callback just records whether there is a default value being passed. - $has_passed_default = $passed_default; - return $current_default; - }, - 10, - 3 - ); - $settings = perflab_get_module_settings(); - $this->assertTrue( $has_passed_default ); - $this->assertEqualSetsWithIndex( perflab_get_modules_setting_default(), $settings ); - - // Assert that option updates are reflected in the settings correctly. - $new_value = array( 'my-module' => array( 'enabled' => true ) ); - update_option( PERFLAB_MODULES_SETTING, $new_value ); - $settings = perflab_get_module_settings(); - $this->assertEqualSetsWithIndex( $new_value, $settings ); - } - - public function test_perflab_get_active_modules() { - // Assert that by default there are no active modules. - $active_modules = perflab_get_active_modules(); - $expected_active_modules = array_keys( - array_filter( - perflab_get_modules_setting_default(), - static function ( $module_settings ) { - return $module_settings['enabled']; - } - ) - ); - $this->assertEqualSetsWithIndex( $expected_active_modules, $active_modules ); - - // Assert that option updates affect the active modules correctly. - $new_value = array( - 'inactive-module' => array( 'enabled' => false ), - 'active-module' => array( 'enabled' => true ), - ); - update_option( PERFLAB_MODULES_SETTING, $new_value ); - $active_modules = perflab_get_active_modules(); - $this->assertEqualSetsWithIndex( array( 'active-module' ), $active_modules ); - } - public function test_perflab_get_generator_content() { - // Assert that it returns the current version and active modules. - // For this test, set the active modules to all defaults but the last one. - $active_modules = require PERFLAB_PLUGIN_DIR_PATH . 'default-enabled-modules.php'; - array_pop( $active_modules ); - add_filter( - 'perflab_active_modules', - static function () use ( $active_modules ) { - return $active_modules; - } - ); - $active_modules = array_filter( perflab_get_active_modules(), 'perflab_is_valid_module' ); - $expected = 'Performance Lab ' . PERFLAB_VERSION . '; modules: ' . implode( ', ', $active_modules ) . '; plugins: '; - $content = perflab_get_generator_content(); + $expected = 'Performance Lab ' . PERFLAB_VERSION . '; plugins: '; + $content = perflab_get_generator_content(); $this->assertSame( $expected, $content ); } public function test_perflab_render_generator() { - // Assert generator tag is rendered. Content does not matter, so just use no modules active. - add_filter( 'perflab_active_modules', '__return_empty_array' ); - $expected = '' . "\n"; + $expected = '' . "\n"; $output = get_echo( 'perflab_render_generator' ); $this->assertSame( $expected, $output ); @@ -151,16 +25,6 @@ public function test_perflab_render_generator() { $this->assertStringContainsString( $expected, $output ); } - public function test_perflab_activate_module() { - perflab_activate_module( __DIR__ . '/testdata/demo-modules/something/demo-module-2' ); - $this->assertSame( 'activated', get_option( 'test_demo_module_activation_status' ) ); - } - - public function test_perflab_deactivate_module() { - perflab_deactivate_module( __DIR__ . '/testdata/demo-modules/something/demo-module-2' ); - $this->assertSame( 'deactivated', get_option( 'test_demo_module_activation_status' ) ); - } - public function test_perflab_maybe_set_object_cache_dropin_no_conflict() { global $wp_filesystem; diff --git a/tests/testdata/demo-modules/check-error/demo-module-4/can-load.php b/tests/testdata/demo-modules/check-error/demo-module-4/can-load.php deleted file mode 100644 index 6a59ab2995..0000000000 --- a/tests/testdata/demo-modules/check-error/demo-module-4/can-load.php +++ /dev/null @@ -1,10 +0,0 @@ - Date: Mon, 18 Mar 2024 14:59:35 +0530 Subject: [PATCH 02/11] Minor updates --- .github/workflows/deploy-standalone-plugins.yml | 6 ++---- plugins.json | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy-standalone-plugins.yml b/.github/workflows/deploy-standalone-plugins.yml index 939816dcb2..d79d8ce6dc 100644 --- a/.github/workflows/deploy-standalone-plugins.yml +++ b/.github/workflows/deploy-standalone-plugins.yml @@ -58,13 +58,11 @@ jobs: exit 1 fi else - # Load the JSON file and parse from "{name: {slug, version}, ...}" to "include: [{ name, slug, version }, ...]" - # for use in the matrix. + # Load the JSON file and parse plugin information to prepare it for inclusion in the matrix. # For plugins, the "version" parameter is not included here; it will dynamically get it in its own job. - # The "dry-run" parameter is included here to set the deployment mode. # When running the manual (workflow_dispatch) workflow, this value will be set from manual input type. - echo "matrix=$(jq -c '{include: ([.plugins[] | {name:. , slug:. , "dry-run": true}])}' plugins.json)" >> $GITHUB_OUTPUT + echo "matrix=$(jq -c '{include: ([.plugins[] | {slug:. , "dry-run": true}])}' plugins.json)" >> $GITHUB_OUTPUT fi deploy: name: Deploy Plugin diff --git a/plugins.json b/plugins.json index 79c5f11bd6..e89419fc5f 100644 --- a/plugins.json +++ b/plugins.json @@ -1,5 +1,4 @@ { - "modules": {}, "plugins": [ "auto-sizes", "dominant-color-images", From 66f0492278e40c1c5a31790b9d20515977e6d3b3 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 19 Mar 2024 10:21:53 +0530 Subject: [PATCH 03/11] Address review feedback --- admin/load.php | 36 ++++++++++++++++++++++++------------ load.php | 4 ++-- readme.txt | 6 +++--- tests/admin/load-tests.php | 10 +++++----- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/admin/load.php b/admin/load.php index c34c13aee4..d99530fbb4 100644 --- a/admin/load.php +++ b/admin/load.php @@ -13,37 +13,49 @@ * Adds the modules page to the Settings menu. * * @since 1.0.0 + * @since n.e.x.t Renamed to perflab_add_features_page(). */ -function perflab_add_modules_page() { +function perflab_add_features_page() { $hook_suffix = add_options_page( - __( 'Performance Plugins', 'performance-lab' ), + __( 'Performance Features', 'performance-lab' ), __( 'Performance', 'performance-lab' ), 'manage_options', PERFLAB_MODULES_SCREEN, - 'perflab_render_modules_page' + 'perflab_render_settings_page' ); // Add the following hooks only if the screen was successfully added. if ( false !== $hook_suffix ) { - // Handle script enqueuing for settings page. - add_action( 'admin_enqueue_scripts', 'perflab_enqueue_modules_page_scripts' ); - - // Handle admin notices for settings page. - add_action( 'admin_notices', 'perflab_plugin_admin_notices' ); - + add_action( "load-{$hook_suffix}", 'perflab_load_settings_page', 10, 0 ); add_filter( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' ); } return $hook_suffix; } -add_action( 'admin_menu', 'perflab_add_modules_page' ); +add_action( 'admin_menu', 'perflab_add_features_page' ); + +/** + * Initializes settings sections and fields for the modules page. + * + * @since 1.0.0 + * @since n.e.x.t Renamed to perflab_load_settings_page(), and the + * $module and $hook_suffix parameters were removed. + */ +function perflab_load_settings_page() { + // Handle script enqueuing for settings page. + add_action( 'admin_enqueue_scripts', 'perflab_enqueue_modules_page_scripts' ); + + // Handle admin notices for settings page. + add_action( 'admin_notices', 'perflab_plugin_admin_notices' ); +} /** * Renders the plugin page. * * @since 1.0.0 + * @since n.e.x.t Renamed to perflab_render_settings_page(). */ -function perflab_render_modules_page() { +function perflab_render_settings_page() { ?>
@@ -152,7 +164,7 @@ function perflab_render_pointer( $pointer_id = 'perflab-admin-pointer', $args = * * @since 1.0.0 * - * @see perflab_add_modules_page() + * @see perflab_add_features_page() * * @param array $links List of plugin action links HTML. * @return array Modified list of plugin action links HTML. diff --git a/load.php b/load.php index b60cbbe5a3..5fcbd05d7a 100644 --- a/load.php +++ b/load.php @@ -47,7 +47,7 @@ * * @since 1.1.0 * @since 2.9.0 The generator tag now includes the active standalone plugin slugs. - * @since n.e.x.t Remove modules from the generator tag. + * @since n.e.x.t The generator tag no longer includes module slugs. */ function perflab_get_generator_content() { $active_plugins = array(); @@ -82,7 +82,7 @@ function perflab_render_generator() { * Gets the standalone plugin constants used for each available standalone plugin. * * @since 2.9.0 - * @since n.e.x.t Remove $source parameter. + * @since n.e.x.t The $source parameter was removed. * * @return array Map of plugin slug / module path and the version constant used. */ diff --git a/readme.txt b/readme.txt index c197259e9a..061bd686bc 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ Performance plugin from the WordPress Performance Team, which is a collection of == Description == -The Performance Lab plugin is a collection of plugins focused on enhancing performance of your site, most of which should eventually be merged into WordPress core. The plugin allows to individually enable and test the plugins to get their benefits before they become available in WordPress core, and to provide feedback to further improve the solutions. +The Performance Lab plugin is a collection of features focused on enhancing performance of your site, most of which should eventually be merged into WordPress core. The plugin allows to individually enable and test the features to get their benefits before they become available in WordPress core, and to provide feedback to further improve the solutions. == Installation == @@ -32,13 +32,13 @@ The Performance Lab plugin is a collection of plugins focused on enhancing perfo = After activation = 1. Visit the new **Settings > Performance** menu. -2. Enable the individual plugins you would like to use. +2. Enable the individual features you would like to use. == Frequently Asked Questions == = What is the purpose of this plugin? = -The primary purpose of the Performance Lab plugin is to allow testing of various performance plugins for which the goal is to eventually land in WordPress core. It is essentially a collection of "feature plugins", which makes it different from other performance plugins that offer performance features which are not targeted at WordPress core and potentially rely on functionality that would not be feasible to use in WordPress core. The list of available plugins will regularly change: Existing plugins may be removed after they have been released in WordPress core, while new modules may be added in any release. +The primary purpose of the Performance Lab plugin is to allow testing of various performance features for which the goal is to eventually land in WordPress core. It is essentially a collection of "feature plugins", which makes it different from other performance plugins that offer performance features which are not targeted at WordPress core and potentially rely on functionality that would not be feasible to use in WordPress core. The list of available features will regularly change: Existing features may be removed after they have been released in WordPress core, while new features may be added in any release. = Can I use this plugin on my production site? = diff --git a/tests/admin/load-tests.php b/tests/admin/load-tests.php index b788321eff..c68b136763 100644 --- a/tests/admin/load-tests.php +++ b/tests/admin/load-tests.php @@ -10,7 +10,7 @@ */ class Admin_Load_Tests extends WP_UnitTestCase { - public function test_perflab_add_modules_page() { + public function test_perflab_add_features_page() { global $_wp_submenu_nopriv; // Reset relevant globals and filters. @@ -18,7 +18,7 @@ public function test_perflab_add_modules_page() { remove_all_filters( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ) ); // The default user does not have the 'manage_options' capability. - $hook_suffix = perflab_add_modules_page(); + $hook_suffix = perflab_add_features_page(); $this->assertFalse( $hook_suffix ); $this->assertTrue( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_MODULES_SCREEN ] ) ); // Ensure plugin action link is not added. @@ -31,7 +31,7 @@ public function test_perflab_add_modules_page() { // Rely on current user to be an administrator (with 'manage_options' capability). $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); wp_set_current_user( $user_id ); - $hook_suffix = perflab_add_modules_page(); + $hook_suffix = perflab_add_features_page(); $this->assertSame( get_plugin_page_hookname( PERFLAB_MODULES_SCREEN, 'options-general.php' ), $hook_suffix ); $this->assertFalse( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_MODULES_SCREEN ] ) ); // Ensure plugin action link is added. @@ -42,9 +42,9 @@ public function test_perflab_add_modules_page() { remove_all_filters( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ) ); } - public function test_perflab_render_modules_page() { + public function test_perflab_render_settings_page() { ob_start(); - perflab_render_modules_page(); + perflab_render_settings_page(); $output = ob_get_clean(); $this->assertStringContainsString( '
', $output ); $this->assertStringNotContainsString( "", $output ); From 079088b24819b5336a3bc080d97febf40f39a319 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 20 Mar 2024 10:20:21 +0530 Subject: [PATCH 04/11] Minor updates --- README.md | 2 +- admin/load.php | 6 +++--- composer.json | 2 +- load.php | 2 +- readme.txt | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c8d8407273..34fe58479b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Performance Lab ![Performance Lab plugin banner with icon](https://user-images.githubusercontent.com/3531426/159084476-af352db4-192e-4927-a383-7f76bb3641df.png) -Monorepo for the [WordPress Performance Team](https://make.wordpress.org/performance/), primarily for the Performance Lab plugin, which is a collection of standalone performance modules. +Monorepo for the [WordPress Performance Team](https://make.wordpress.org/performance/), primarily for the Performance Lab plugin, which is a collection of standalone performance features. Details about the Performance Lab plugin, including instructions for getting started and contributing, are available in the [Performance Team Handbook here](https://make.wordpress.org/performance/handbook/performance-lab/). diff --git a/admin/load.php b/admin/load.php index d99530fbb4..39363a0767 100644 --- a/admin/load.php +++ b/admin/load.php @@ -26,7 +26,7 @@ function perflab_add_features_page() { // Add the following hooks only if the screen was successfully added. if ( false !== $hook_suffix ) { - add_action( "load-{$hook_suffix}", 'perflab_load_settings_page', 10, 0 ); + add_action( "load-{$hook_suffix}", 'perflab_load_features_page', 10, 0 ); add_filter( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' ); } @@ -38,10 +38,10 @@ function perflab_add_features_page() { * Initializes settings sections and fields for the modules page. * * @since 1.0.0 - * @since n.e.x.t Renamed to perflab_load_settings_page(), and the + * @since n.e.x.t Renamed to perflab_load_features_page(), and the * $module and $hook_suffix parameters were removed. */ -function perflab_load_settings_page() { +function perflab_load_features_page() { // Handle script enqueuing for settings page. add_action( 'admin_enqueue_scripts', 'perflab_enqueue_modules_page_scripts' ); diff --git a/composer.json b/composer.json index 6b77657e7f..69ac8b2f88 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "wordpress/performance", "type": "wordpress-plugin", "license": "GPL-2.0-or-later", - "description": "Performance plugin from the WordPress Performance Team, which is a collection of standalone performance modules.", + "description": "Performance plugin from the WordPress Performance Team, which is a collection of standalone performance features.", "homepage": "https://wordpress.org/plugins/performance-lab/", "keywords": [ "performance", diff --git a/load.php b/load.php index a3d0c38ebd..8aa262d813 100644 --- a/load.php +++ b/load.php @@ -2,7 +2,7 @@ /** * Plugin Name: Performance Lab * Plugin URI: https://github.com/WordPress/performance - * Description: Performance plugin from the WordPress Performance Team, which is a collection of standalone performance modules. + * Description: Performance plugin from the WordPress Performance Team, which is a collection of standalone performance features. * Requires at least: 6.4 * Requires PHP: 7.0 * Version: 2.9.0 diff --git a/readme.txt b/readme.txt index ebad9bf7da..5298cf3279 100644 --- a/readme.txt +++ b/readme.txt @@ -9,7 +9,7 @@ License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, javascript, site health, measurement, object caching -Performance plugin from the WordPress Performance Team, which is a collection of standalone performance plugins. +Performance plugin from the WordPress Performance Team, which is a collection of standalone performance features. == Description == @@ -42,7 +42,7 @@ The primary purpose of the Performance Lab plugin is to allow testing of various = Can I use this plugin on my production site? = -Per the primary purpose of the plugin (see above), it can mostly be considered a beta testing plugin for the various performance modules it includes. However, unless a module is explicitly marked as "experimental", it has been tested and established to a degree where it should be okay to use in production. Still, as with every plugin, you are doing so at your own risk. +Per the primary purpose of the plugin (see above), it can mostly be considered a beta testing plugin for the various performance features it includes. However, it's essential to understand that utilizing it comes with inherent risks. Users are encouraged to proceed with caution and understand that they are doing so at their own risk. = Where can I submit my plugin feedback? = From 3d406e524a260437e474a876a99e9b789f685c73 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 20 Mar 2024 10:35:14 +0530 Subject: [PATCH 05/11] Remove PERFLAB_MODULES_SETTING --- load.php | 1 - uninstall.php | 1 - 2 files changed, 2 deletions(-) diff --git a/load.php b/load.php index 8aa262d813..4a8d0ead91 100644 --- a/load.php +++ b/load.php @@ -22,7 +22,6 @@ define( 'PERFLAB_VERSION', '2.9.0' ); define( 'PERFLAB_MAIN_FILE', __FILE__ ); define( 'PERFLAB_PLUGIN_DIR_PATH', plugin_dir_path( PERFLAB_MAIN_FILE ) ); -define( 'PERFLAB_MODULES_SETTING', 'perflab_modules_settings' ); define( 'PERFLAB_MODULES_SCREEN', 'perflab-modules' ); // If the constant isn't defined yet, it means the Performance Lab object cache file is not loaded. diff --git a/uninstall.php b/uninstall.php index bb349f9a87..d1f16c713a 100644 --- a/uninstall.php +++ b/uninstall.php @@ -37,6 +37,5 @@ * @since 1.4.0 */ function perflab_delete_plugin_option() { - delete_option( 'perflab_modules_settings' ); delete_option( 'perflab_generate_webp_and_jpeg' ); } From 988aabcf2751a124c04ebd66c1d1951015151cb5 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 20 Mar 2024 10:46:37 +0530 Subject: [PATCH 06/11] Add missing end line --- .github/workflows/deploy-standalone-plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-standalone-plugins.yml b/.github/workflows/deploy-standalone-plugins.yml index 15caa20693..4fd129fc96 100644 --- a/.github/workflows/deploy-standalone-plugins.yml +++ b/.github/workflows/deploy-standalone-plugins.yml @@ -198,4 +198,4 @@ jobs: with: files: | ./build/dist/${{ matrix.plugin }}.zip - ./build/dist/${{ matrix.plugin }}.zip.sha256 \ No newline at end of file + ./build/dist/${{ matrix.plugin }}.zip.sha256 From 59c96e58a7770fa623fbf0ebb95d1ca1bd48a5c3 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 21 Mar 2024 10:11:22 +0530 Subject: [PATCH 07/11] Update page slug and delete settings option --- admin/load.php | 6 +++--- load.php | 37 ++++++++++++++++++++++++++++++++++++- tests/admin/load-tests.php | 10 +++++----- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/admin/load.php b/admin/load.php index 39363a0767..3b89a6db8d 100644 --- a/admin/load.php +++ b/admin/load.php @@ -20,7 +20,7 @@ function perflab_add_features_page() { __( 'Performance Features', 'performance-lab' ), __( 'Performance', 'performance-lab' ), 'manage_options', - PERFLAB_MODULES_SCREEN, + PERFLAB_SCREEN, 'perflab_render_settings_page' ); @@ -117,7 +117,7 @@ function perflab_render_pointer( $pointer_id = 'perflab-admin-pointer', $args = $args['content'] = sprintf( /* translators: %s: settings page link */ esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features.', 'performance-lab' ), - '' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '' + '' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '' ); } @@ -173,7 +173,7 @@ function perflab_plugin_action_links_add_settings( $links ) { // Add link as the first plugin action link. $settings_link = sprintf( '%s', - esc_url( add_query_arg( 'page', PERFLAB_MODULES_SCREEN, admin_url( 'options-general.php' ) ) ), + esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ), esc_html__( 'Settings', 'performance-lab' ) ); array_unshift( $links, $settings_link ); diff --git a/load.php b/load.php index 4a8d0ead91..06965fe89d 100644 --- a/load.php +++ b/load.php @@ -22,7 +22,7 @@ define( 'PERFLAB_VERSION', '2.9.0' ); define( 'PERFLAB_MAIN_FILE', __FILE__ ); define( 'PERFLAB_PLUGIN_DIR_PATH', plugin_dir_path( PERFLAB_MAIN_FILE ) ); -define( 'PERFLAB_MODULES_SCREEN', 'perflab-modules' ); +define( 'PERFLAB_SCREEN', 'performance-lab' ); // If the constant isn't defined yet, it means the Performance Lab object cache file is not loaded. if ( ! defined( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION' ) ) { @@ -261,6 +261,41 @@ function perflab_maybe_remove_object_cache_dropin() { } register_deactivation_hook( __FILE__, 'perflab_maybe_remove_object_cache_dropin' ); +/** + * Redirects module pages to the peformance feature page. + * + * @since n.e.x.t + * + * @global $plugin_page + */ +function perflab_no_access_redirect_module_to_peformance_feature_page() { + global $plugin_page; + + if ( 'perflab-modules' !== $plugin_page ) { + return; + } + + if ( current_user_can( 'manage_options' ) ) { + wp_safe_redirect( + add_query_arg( 'page', PERFLAB_SCREEN ) + ); + exit; + } +} +add_action( 'admin_page_access_denied', 'perflab_no_access_redirect_module_to_peformance_feature_page' ); + +/** + * Cleanup function to delete 'perflab_modules_settings' option if present. + * + * @since n.e.x.t + */ +function perflab_cleanup_option() { + if ( current_user_can( 'manage_options' ) && get_option( 'perflab_modules_settings' ) ) { + delete_option( 'perflab_modules_settings' ); + } +} +add_action( 'admin_init', 'perflab_cleanup_option' ); + // Only load admin integration when in admin. if ( is_admin() ) { require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/load.php'; diff --git a/tests/admin/load-tests.php b/tests/admin/load-tests.php index c68b136763..b8fc585923 100644 --- a/tests/admin/load-tests.php +++ b/tests/admin/load-tests.php @@ -20,7 +20,7 @@ public function test_perflab_add_features_page() { // The default user does not have the 'manage_options' capability. $hook_suffix = perflab_add_features_page(); $this->assertFalse( $hook_suffix ); - $this->assertTrue( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_MODULES_SCREEN ] ) ); + $this->assertTrue( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_SCREEN ] ) ); // Ensure plugin action link is not added. $this->assertFalse( (bool) has_action( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' ) ); @@ -32,8 +32,8 @@ public function test_perflab_add_features_page() { $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); wp_set_current_user( $user_id ); $hook_suffix = perflab_add_features_page(); - $this->assertSame( get_plugin_page_hookname( PERFLAB_MODULES_SCREEN, 'options-general.php' ), $hook_suffix ); - $this->assertFalse( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_MODULES_SCREEN ] ) ); + $this->assertSame( get_plugin_page_hookname( PERFLAB_SCREEN, 'options-general.php' ), $hook_suffix ); + $this->assertFalse( isset( $_wp_submenu_nopriv['options-general.php'][ PERFLAB_SCREEN ] ) ); // Ensure plugin action link is added. $this->assertTrue( (bool) has_action( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' ) ); @@ -47,13 +47,13 @@ public function test_perflab_render_settings_page() { perflab_render_settings_page(); $output = ob_get_clean(); $this->assertStringContainsString( '
', $output ); - $this->assertStringNotContainsString( "", $output ); + $this->assertStringNotContainsString( "", $output ); } public function test_perflab_plugin_action_links_add_settings() { $original_links = array( 'wordpress.org' ); $expected_links = array( - 'Settings', + 'Settings', $original_links[0], ); From cd9f564cb2a32f4ac4400879cf1107d61d1f05bb Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 21 Mar 2024 10:19:46 +0530 Subject: [PATCH 08/11] Fix spell --- load.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/load.php b/load.php index 01e9b269ca..7c836d90c0 100644 --- a/load.php +++ b/load.php @@ -263,13 +263,13 @@ function perflab_maybe_remove_object_cache_dropin() { register_deactivation_hook( __FILE__, 'perflab_maybe_remove_object_cache_dropin' ); /** - * Redirects module pages to the peformance feature page. + * Redirects module pages to the performance feature page. * * @since n.e.x.t * * @global $plugin_page */ -function perflab_no_access_redirect_module_to_peformance_feature_page() { +function perflab_no_access_redirect_module_to_performance_feature_page() { global $plugin_page; if ( 'perflab-modules' !== $plugin_page ) { @@ -283,7 +283,7 @@ function perflab_no_access_redirect_module_to_peformance_feature_page() { exit; } } -add_action( 'admin_page_access_denied', 'perflab_no_access_redirect_module_to_peformance_feature_page' ); +add_action( 'admin_page_access_denied', 'perflab_no_access_redirect_module_to_performance_feature_page' ); /** * Cleanup function to delete 'perflab_modules_settings' option if present. From 959377cec9dddc5d8997b7bb5e9bd03e49868f90 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 21 Mar 2024 20:53:18 +0530 Subject: [PATCH 09/11] Remove get_option call --- load.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/load.php b/load.php index 7c836d90c0..5f299dd0df 100644 --- a/load.php +++ b/load.php @@ -291,7 +291,7 @@ function perflab_no_access_redirect_module_to_performance_feature_page() { * @since n.e.x.t */ function perflab_cleanup_option() { - if ( current_user_can( 'manage_options' ) && get_option( 'perflab_modules_settings' ) ) { + if ( current_user_can( 'manage_options' ) ) { delete_option( 'perflab_modules_settings' ); } } From fe8f9f6c40a446664a6330bcd69a87f8e7b579ba Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 21 Mar 2024 09:18:07 -0700 Subject: [PATCH 10/11] Enhance testability of redirect. Co-authored-by: Weston Ruter --- load.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/load.php b/load.php index 5f299dd0df..b63e78bcb3 100644 --- a/load.php +++ b/load.php @@ -276,10 +276,10 @@ function perflab_no_access_redirect_module_to_performance_feature_page() { return; } - if ( current_user_can( 'manage_options' ) ) { - wp_safe_redirect( - add_query_arg( 'page', PERFLAB_SCREEN ) - ); + if ( + current_user_can( 'manage_options' ) && + wp_safe_redirect( add_query_arg( 'page', PERFLAB_SCREEN ) ) + ) { exit; } } From c40dde2550c24af94ec6bdb9f1b528dfe9c6c1af Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 21 Mar 2024 09:20:37 -0700 Subject: [PATCH 11/11] Remove extra whitespace. --- load.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/load.php b/load.php index b63e78bcb3..45d7cab1ab 100644 --- a/load.php +++ b/load.php @@ -276,9 +276,9 @@ function perflab_no_access_redirect_module_to_performance_feature_page() { return; } - if ( - current_user_can( 'manage_options' ) && - wp_safe_redirect( add_query_arg( 'page', PERFLAB_SCREEN ) ) + if ( + current_user_can( 'manage_options' ) && + wp_safe_redirect( add_query_arg( 'page', PERFLAB_SCREEN ) ) ) { exit; }