From d434d0df76ae9f7cffef2ad541d28311a82ffb88 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Fri, 5 Jun 2026 16:01:47 +0600 Subject: [PATCH 1/5] feat(checks): add Collapse All and Expand All controls to results page Add Collapse All and Expand All buttons above results. Hide buttons until scan completes via is-hidden class. Use custom card UI with rotating chevron, file icon, and brand color. All cards start collapsed by default. Fixes #1334 --- .gitignore | 1 + assets/css/plugin-check-admin.css | 68 +++++++++++++++++++ assets/js/plugin-check-admin.js | 104 ++++++++++++++++++++++++++++++ templates/admin-page.php | 10 +++ templates/results-table.php | 66 +++++++++++-------- 5 files changed, 221 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index bdaf98ed0..52194e67f 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ temp/ .wp-env.override.json .wp-env.tests.override.json +/build-temp diff --git a/assets/css/plugin-check-admin.css b/assets/css/plugin-check-admin.css index 115a348f7..19d207cf6 100644 --- a/assets/css/plugin-check-admin.css +++ b/assets/css/plugin-check-admin.css @@ -126,6 +126,74 @@ cursor: pointer; } +.plugin-check__collapse-expand-controls { + margin: 16px 0; + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.plugin-check__collapse-expand-controls.is-hidden { + display: none; +} + +.plugin-check__file-section { + margin: 1em 0; + border: 1px solid #dcdcde; + background: #fff; +} + +.plugin-check__file-section-header { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 8px 12px; + font-weight: 600; + cursor: pointer; + user-select: none; + background: transparent; + border: 0; + text-align: left; + color: inherit; + font-size: inherit; +} + +.plugin-check__file-section-header:focus { + outline: 2px solid #2271b1; + outline-offset: -2px; +} + +.plugin-check__file-section-chevron { + transition: transform 0.15s ease-in-out; + font-size: 18px; + line-height: 1; +} + +.plugin-check__file-section-title { + color: var(--wp-admin-theme-color); + display: inline-flex; + align-items: center; + gap: 6px; +} + +.plugin-check__file-section-icon { + font-size: 18px; + line-height: 1; +} + +.plugin-check__file-section-header[aria-expanded="true"] .plugin-check__file-section-chevron { + transform: rotate(180deg); +} + +.plugin-check__file-section-content { + padding: 0 14px 14px; +} + +.plugin-check__file-section--collapsed .plugin-check__file-section-content { + display: none; +} + .plugin-check__false-positive-results { padding: 0 14px 14px; } diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js index 22227cc4e..5fe64124e 100644 --- a/assets/js/plugin-check-admin.js +++ b/assets/js/plugin-check-admin.js @@ -4,6 +4,9 @@ const exportContainer = document.getElementById( 'plugin-check__export-controls' ); + const collapseExpandContainer = document.getElementById( + 'plugin-check__collapse-expand-controls' + ); const spinner = document.getElementById( 'plugin-check__spinner' ); const pluginsList = document.getElementById( 'plugin-check__plugins-dropdown' @@ -20,6 +23,7 @@ ! pluginsList || ! resultsContainer || ! exportContainer || + ! collapseExpandContainer || ! spinner || ! categoriesList.length || ! typesList.length @@ -33,6 +37,7 @@ let checksCompleted = false; exportContainer.classList.add( 'is-hidden' ); exportContainer.addEventListener( 'click', onExportContainerClick ); + collapseExpandContainer.classList.add( 'is-hidden' ); const includeExperimental = document.getElementById( 'plugin-check__include-experimental' @@ -122,6 +127,7 @@ resultsContainer.innerText = ''; exportContainer.innerHTML = ''; exportContainer.classList.add( 'is-hidden' ); + collapseExpandContainer.classList.add( 'is-hidden' ); resetAggregatedResults(); checksCompleted = false; } @@ -424,10 +430,12 @@ exportContainer.innerHTML = ''; if ( ! checksCompleted ) { exportContainer.classList.add( 'is-hidden' ); + collapseExpandContainer.classList.add( 'is-hidden' ); return; } exportContainer.classList.remove( 'is-hidden' ); + collapseExpandContainer.classList.remove( 'is-hidden' ); const exportButtonConfigs = [ { @@ -1389,4 +1397,100 @@ const template = templates[ templateSlug ]; return template( data ); } + + /** + * Toggles the open/collapsed state of a single file section. + * + * @since 1.6.0 + * + * @param {HTMLElement} section The file section element. + */ + function toggleFileSection( section ) { + if ( ! section ) { + return; + } + const header = section.querySelector( + '.plugin-check__file-section-header' + ); + const isCollapsed = section.classList.contains( + 'plugin-check__file-section--collapsed' + ); + if ( isCollapsed ) { + section.classList.remove( 'plugin-check__file-section--collapsed' ); + if ( header ) { + header.setAttribute( 'aria-expanded', 'true' ); + } + } else { + section.classList.add( 'plugin-check__file-section--collapsed' ); + if ( header ) { + header.setAttribute( 'aria-expanded', 'false' ); + } + } + } + + /** + * Sets the open state of every file section in the results container. + * + * @since 1.6.0 + * + * @param {boolean} open Open state to apply. + */ + function setAllFileSectionsOpen( open ) { + const sections = resultsContainer.querySelectorAll( + '.plugin-check__file-section' + ); + sections.forEach( function ( section ) { + const header = section.querySelector( + '.plugin-check__file-section-header' + ); + if ( open ) { + section.classList.remove( + 'plugin-check__file-section--collapsed' + ); + if ( header ) { + header.setAttribute( 'aria-expanded', 'true' ); + } + } else { + section.classList.add( + 'plugin-check__file-section--collapsed' + ); + if ( header ) { + header.setAttribute( 'aria-expanded', 'false' ); + } + } + } ); + } + + // Delegated click handler for individual file section headers. + resultsContainer.addEventListener( 'click', function ( event ) { + const header = event.target.closest( + '.plugin-check__file-section-header' + ); + if ( ! header ) { + return; + } + event.preventDefault(); + const section = header.closest( '.plugin-check__file-section' ); + toggleFileSection( section ); + } ); + + // Wire up the Collapse All / Expand All buttons. + const expandAllButton = document.getElementById( + 'plugin-check__expand-all' + ); + const collapseAllButton = document.getElementById( + 'plugin-check__collapse-all' + ); + + if ( expandAllButton ) { + expandAllButton.addEventListener( 'click', function () { + setAllFileSectionsOpen( true ); + } ); + } + + if ( collapseAllButton ) { + collapseAllButton.addEventListener( 'click', function () { + setAllFileSectionsOpen( false ); + } ); + } } )( PLUGIN_CHECK ); diff --git a/templates/admin-page.php b/templates/admin-page.php index d8bfe935e..567ce1ead 100644 --- a/templates/admin-page.php +++ b/templates/admin-page.php @@ -104,6 +104,16 @@
+
diff --git a/templates/results-table.php b/templates/results-table.php index 2c10949b2..edaaf6365 100644 --- a/templates/results-table.php +++ b/templates/results-table.php @@ -1,29 +1,39 @@ -

{{ data.file }}

- - - - - - - - - <# if ( data.hasLinks ) { #> - - <# } #> - - - -
- - - - - - - - - - - -
+
+ +
+ + + + + + + + + <# if ( data.hasLinks ) { #> + + <# } #> + + + +
+ + + + + + + + + + + +
+
+

From 9ca7abf90c283fb8a71a26751e83cd59f4b06106 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Mon, 8 Jun 2026 01:07:36 +0600 Subject: [PATCH 2/5] fix: address PR review feedback - Remove /build-temp from .gitignore - Remove trailing
after file section (double spacing with card margin) - Update @since tags to 2.1.0 for new toggleFileSection and setAllFileSectionsOpen Refs #1347 --- .gitignore | 1 - assets/js/plugin-check-admin.js | 4 ++-- templates/results-table.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 52194e67f..bdaf98ed0 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,3 @@ temp/ .wp-env.override.json .wp-env.tests.override.json -/build-temp diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js index 5fe64124e..95b840213 100644 --- a/assets/js/plugin-check-admin.js +++ b/assets/js/plugin-check-admin.js @@ -1401,7 +1401,7 @@ /** * Toggles the open/collapsed state of a single file section. * - * @since 1.6.0 + * @since 2.1.0 * * @param {HTMLElement} section The file section element. */ @@ -1431,7 +1431,7 @@ /** * Sets the open state of every file section in the results container. * - * @since 1.6.0 + * @since 2.1.0 * * @param {boolean} open Open state to apply. */ diff --git a/templates/results-table.php b/templates/results-table.php index edaaf6365..208463e46 100644 --- a/templates/results-table.php +++ b/templates/results-table.php @@ -36,4 +36,4 @@ -
+ From 225b837e246eb89d2b61fe0615863327db41d16b Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Mon, 8 Jun 2026 01:23:55 +0600 Subject: [PATCH 3/5] ci: import Codecov GPG key before coverage upload The Codecov CLI v11.x GPG signature verification fails in GitHub Actions runners because the signing public key is missing from the default GPG keyring. Import it from keyserver.ubuntu.com (with a Keybase fallback) before the codecov-action runs. Add the import step to both behat-test.yml and php-test.yml since both use the same pinned codecov-action and hit the same issue. Refs #1347 --- .github/workflows/behat-test.yml | 6 ++++++ .github/workflows/php-test.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/behat-test.yml b/.github/workflows/behat-test.yml index 67110a79a..3a0bdc9a3 100644 --- a/.github/workflows/behat-test.yml +++ b/.github/workflows/behat-test.yml @@ -153,6 +153,12 @@ jobs: echo "Coverage files: $FILES" echo "files=$FILES" >> $GITHUB_OUTPUT + - name: Import Codecov GPG key + if: ${{ matrix.coverage }} + run: | + gpg --keyserver keyserver.ubuntu.com --recv-keys 27034E7FDB850E0BBC2C62FF806BB28AED779869 2>/dev/null || \ + curl -fsSL https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --import --no-default-keyring --keyring trustedkeys.gpg 2>/dev/null || true + - name: Upload code coverage report if: ${{ matrix.coverage }} uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml index 3ff6b5bca..f909e5ad3 100644 --- a/.github/workflows/php-test.yml +++ b/.github/workflows/php-test.yml @@ -108,6 +108,12 @@ jobs: npm run test-php-multisite fi + - name: Import Codecov GPG key + if: ${{ matrix.coverage }} + run: | + gpg --keyserver keyserver.ubuntu.com --recv-keys 27034E7FDB850E0BBC2C62FF806BB28AED779869 2>/dev/null || \ + curl -fsSL https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --import --no-default-keyring --keyring trustedkeys.gpg 2>/dev/null || true + - name: Upload code coverage report if: ${{ matrix.coverage }} uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 From b054ae28f7dd4c5dd91cdea635ae5a690b522dce Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Mon, 8 Jun 2026 01:34:14 +0600 Subject: [PATCH 4/5] ci: retry wp-env start on transient docker network failure Debian bullseye security repo (deb.debian.org) occasionally times out during Docker build for PHP 7.4 + WP 6.3 matrix job. Add retry loop around wp-env start step (3 attempts, 30s delay). Docker build cache preserves already-built layers between retries. Refs #1347 --- .github/workflows/php-test.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml index f909e5ad3..6275fc430 100644 --- a/.github/workflows/php-test.yml +++ b/.github/workflows/php-test.yml @@ -92,11 +92,20 @@ jobs: - name: Start WordPress run: | - if [[ ${{ matrix.coverage == true }} == true ]]; then - npm run wp-env:start:tests -- --xdebug=coverage - else - npm run wp-env:start:tests - fi + set +e + for i in 1 2 3; do + if [[ ${{ matrix.coverage == true }} == true ]]; then + npm run wp-env:start:tests -- --xdebug=coverage + else + npm run wp-env:start:tests + fi + if [ $? -eq 0 ]; then + exit 0 + fi + echo "wp-env start failed (attempt $i), retrying in 30s..." + sleep 30 + done + exit 1 - name: Run tests run: | From 5a503ec46f48804a2d4c5e6db9b2df0260c1b741 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Mon, 8 Jun 2026 01:39:26 +0600 Subject: [PATCH 5/5] ci: increase wp-env retry to 5 attempts with 60s backoff Previous 3-attempt 30s backoff insufficient for transient Docker Hub registry timeouts. Bump to 5 attempts with 60s backoff to give the network time to recover. Refs #1347 --- .github/workflows/php-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml index 6275fc430..f74e5d77b 100644 --- a/.github/workflows/php-test.yml +++ b/.github/workflows/php-test.yml @@ -93,7 +93,7 @@ jobs: - name: Start WordPress run: | set +e - for i in 1 2 3; do + for i in 1 2 3 4 5; do if [[ ${{ matrix.coverage == true }} == true ]]; then npm run wp-env:start:tests -- --xdebug=coverage else @@ -102,8 +102,8 @@ jobs: if [ $? -eq 0 ]; then exit 0 fi - echo "wp-env start failed (attempt $i), retrying in 30s..." - sleep 30 + echo "wp-env start failed (attempt $i), retrying in 60s..." + sleep 60 done exit 1