Skip to content

Plugin Directory: Detect wp_add_dashboard_widget() during import#625

Closed
dd32 wants to merge 8 commits intoWordPress:trunkfrom
dd32:add/claude/plugin-section-dashboard-widgets
Closed

Plugin Directory: Detect wp_add_dashboard_widget() during import#625
dd32 wants to merge 8 commits intoWordPress:trunkfrom
dd32:add/claude/plugin-section-dashboard-widgets

Conversation

@dd32
Copy link
Copy Markdown
Member

@dd32 dd32 commented May 8, 2026

Summary

  • Adds find_dashboard_widgets_in_file() to the plugin importer, scanning PHP files for wp_add_dashboard_widget() calls.
  • Assigns the dashboard-widgets plugin_section term to plugins where any call is detected, and stores each detected widget label as dashboard_widget_name post meta.
  • The label is the first quoted string literal inside the second argument, so wrappers like __(), _x(), and esc_html__() are handled.

Context: https://wordpress.slack.com/archives/C0ATCQF2PMM/p1778193159754749

Notes

  • The dashboard-widgets term is assumed to exist on production (manual creation, same approach as featured, popular, block). The local environment seeds it via environments/plugin-directory/bin/after-start.sh.
  • A backfill helper, bin/process-dashboard-widgets.php, is included for convenience. It pulls candidate slugs from a Veloria CSV export and re-imports them. This script likely does not need to be merged and can be dropped from the PR if preferred.
  • Unit tests for the new helper are intentionally not included here. They will be proposed as a follow-up branch/PR alongside coverage for the existing find_blocks_in_file().

Test plan

  • Run bin/import-plugin.php elementor (or another known dashboard-widget plugin) in a dev environment and confirm the dashboard-widgets plugin_section term is set and dashboard_widget_name meta is populated.
  • Visit /plugins/browse/dashboard-widgets/ and confirm the plugin lists.
  • Confirm a re-import of a plugin with no wp_add_dashboard_widget() calls does not gain the section.

🤖 Generated with Claude Code

dd32 and others added 4 commits May 8, 2026 14:53
…r the dashboard-widgets section.

Scans imported plugin source for wp_add_dashboard_widget() calls, assigns the dashboard-widgets plugin_section term, and stores each detected widget label as dashboard_widget_name post meta. The label is extracted as the first string literal inside the second argument, so wrappers like __(), _x(), and esc_html__() are handled.

Includes a CLI backfill script that walks candidate slugs from a CSV and re-imports them, plus the section term seeding for the local dev environment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t string array.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 05:02
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props dd32.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Add explicit visibility to find_dashboard_widgets_in_file() and clean up the backfill script for the new-file PHPCS scan: split the multi-assignment, rename a local that overrode a WordPress global, and switch echo to fwrite(STDOUT, ...).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds dashboard-widget detection to the Plugin Directory importer so plugins that register dashboard widgets can be automatically categorized and have widget labels recorded during import.

Changes:

  • Collects wp_add_dashboard_widget() labels from imported plugin PHP files and passes them through the importer parse pipeline.
  • Assigns/removes the dashboard-widgets plugin_section term and stores detected labels in dashboard_widget_name post meta during import.
  • Seeds and internationalizes the “Dashboard Widgets” section name in local environment + translation strings, and adds a convenience backfill script.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 5 comments.

File Description
wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php Adds PHP scanning + import-time taxonomy/meta updates for dashboard widgets.
wordpress.org/public_html/wp-content/plugins/plugin-directory/class-i18n.php Adds translatable string for the new section name.
wordpress.org/public_html/wp-content/plugins/plugin-directory/bin/process-dashboard-widgets.php Adds a CLI helper script to backfill by re-importing candidate plugins.
environments/plugin-directory/bin/after-start.sh Seeds the dashboard-widgets section in local environments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +498 to +501
delete_post_meta( $plugin->ID, 'dashboard_widget_name' );
foreach ( $dashboard_widgets as $widget_name ) {
add_post_meta( $plugin->ID, 'dashboard_widget_name', $widget_name, false );
}
Comment on lines +1047 to +1053
// Find dashboard widget registrations (wp_add_dashboard_widget calls).
$dashboard_widgets = array();
foreach ( Filesystem::list_files( $base_dir, true, '!\.php$!i' ) as $filename ) {
foreach ( self::find_dashboard_widgets_in_file( $filename ) as $widget ) {
$dashboard_widgets[] = $widget;
}
}
Comment on lines +1276 to +1281
// Match wp_add_dashboard_widget( <first arg>, <second arg up to next top-level comma or close-paren> ).
if ( preg_match_all(
'#wp_add_dashboard_widget\s*\(\s*[^,]{1,200},\s*([^;]{1,500}?)(?:,|\))#ms',
$contents,
$matches,
PREG_SET_ORDER
Comment on lines +1284 to +1286
// Pull the first quoted string out of the second argument.
if ( preg_match( '#[\'"]([^\'"]+)[\'"]#', $match[1], $title_match ) ) {
$widgets[] = $title_match[1];
dd32 and others added 2 commits May 8, 2026 15:10
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…scanner.

Skip files under any /vendor/ path during the dashboard widget scan, and move the array_unique() into the file-scanner return so the helper hands back a clean list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 05:12
@dd32
Copy link
Copy Markdown
Member Author

dd32 commented May 8, 2026

Thanks for the review. Triage:

  1. Auto-creation of misnamed term — leaving as-is. The pre-existing-term assumption matches how adopt-me, featured, popular, block, etc. are handled, and is the explicitly chosen approach for this PR; the term will be created on production before deploy and the local environment seeds it via environments/plugin-directory/bin/after-start.sh.
  2. Duplicate labels — fixed in 3211e9a: array_unique() inside find_dashboard_widgets_in_file() so the helper hands back a clean list.
  3. Recursive scan cost — accepted scope. This mirrors the fallback in find_blocks_in_file() (which also walks every PHP file when no block.json is present), so it is consistent with existing import behavior. Vendor directories are now skipped (3211e9a) which removes the worst-offender case.
  4. token_get_all for accurate parsing — deferred. A separate PR will introduce a shared token_get_all-based extractor and migrate both block detection and dashboard widget detection to it at the same time, so both benefit equally.
  5. Escaped quotes in labels — addressed by the same follow-up. Token-based extraction returns the unescaped value natively, which is the right place to handle this.

The backfill is a one-shot operation that does not need to live in the repo. It can run as an ad-hoc script outside the tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

$dashboard_widgets[] = $widget;
}
}

Comment on lines +496 to +497
wp_add_object_terms( $plugin->ID, 'dashboard-widgets', 'plugin_section' );

Comment on lines +1288 to +1290
// Pull the first quoted string out of the second argument.
if ( preg_match( '#[\'"]([^\'"]+)[\'"]#', $match[1], $title_match ) ) {
$widgets[] = $title_match[1];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants