From 0fd308b6d155e9c62f547746387f72ca1f017545 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 21:42:39 +0000 Subject: [PATCH 1/2] Document the list filters these commands implement themselves `site list` and `user application-password list` both filter on a fixed set of fields defined in their own code, and neither documents them. `site list` carries a $site_cols array of twelve columns; the application password command filters on its APPLICATION_PASSWORD_FIELDS. Users are left to infer all of it from `--=` and the "Available Fields" section. Some of these are already relied on by this repo's own tests - site.feature uses --site_id and --blog_id, user-application-password.feature uses --name - so they are supported behaviour that simply is not written down. Document them, with two deliberate omissions: - `path` for `site list`, which is unreachable because the global --path wins. The existing --site-path parameter already covers it and says so. - Everything for `post list`, `comment list`, `user list`, `term list`, `post-type list` and `taxonomy list`. Those pass their arguments to WP_Query, WP_Comment_Query, get_users(), get_terms(), get_post_types() and get_taxonomies(), where the accepted set is open-ended and extended by plugins. There is no fixed list to write down for them. `signup list` is a third case, left for later: its columns are fixed but it also filters on arbitrary keys unserialized from the signup meta. Behat coverage added for both, exercising the newly documented filters including the dashed --app-id spelling that the command normalizes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- features/site.feature | 55 ++++++++++++++++++++++ features/user-application-password.feature | 49 +++++++++++++++++++ src/Site_Command.php | 34 +++++++++++++ src/User_Application_Password_Command.php | 23 +++++++++ 4 files changed, 161 insertions(+) diff --git a/features/site.feature b/features/site.feature index c944642a6..f77406621 100644 --- a/features/site.feature +++ b/features/site.feature @@ -936,3 +936,58 @@ Feature: Manage sites in a multisite installation """ 1 """ + + Scenario: Filter the site list by columns of the sites table + Given a WP multisite install + + When I run `wp site create --slug=first --porcelain` + Then STDOUT should be a number + + When I run `wp site list --format=count` + Then STDOUT should be: + """ + 2 + """ + + # site_id is the ID of the network the site belongs to, not the site's own ID. + When I run `wp site list --site_id=1 --format=count` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp site list --site_id=2 --format=count` + Then STDOUT should be: + """ + 0 + """ + + When I run `wp site list --blog_id=2 --field=blog_id` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp site list --public=1 --format=count` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp site list --archived=0 --format=count` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp site list --deleted=1 --format=count` + Then STDOUT should be: + """ + 0 + """ + + When I run `wp site list --spam=0 --format=count` + Then STDOUT should be: + """ + 2 + """ diff --git a/features/user-application-password.feature b/features/user-application-password.feature index 507b7ee28..3c02088d2 100644 --- a/features/user-application-password.feature +++ b/features/user-application-password.feature @@ -315,3 +315,52 @@ Feature: Manage user custom fields """ true """ + + Scenario: Filter application passwords by field + Given a WP install + + When I run `wp user application-password create 1 myapp --app-id=abc123 --porcelain` + Then STDOUT should not be empty + + When I run `wp user application-password create 1 otherapp --porcelain` + Then STDOUT should not be empty + + When I run `wp user application-password list 1 --format=count` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp user application-password list 1 --app_id=abc123 --field=name` + Then STDOUT should be: + """ + myapp + """ + + When I run `wp user application-password list 1 --app-id=abc123 --field=name` + Then STDOUT should be: + """ + myapp + """ + + When I run `wp user application-password list 1 --name=otherapp --field=name` + Then STDOUT should be: + """ + otherapp + """ + + When I run `wp user application-password list 1 --name=myapp --field=uuid` + Then STDOUT should not be empty + And save STDOUT as {UUID} + + When I run `wp user application-password list 1 --uuid={UUID} --field=name` + Then STDOUT should be: + """ + myapp + """ + + When I run `wp user application-password list 1 --app_id=nosuchapp --format=count` + Then STDOUT should be: + """ + 0 + """ diff --git a/src/Site_Command.php b/src/Site_Command.php index f7735435a..a109b76e6 100644 --- a/src/Site_Command.php +++ b/src/Site_Command.php @@ -1001,6 +1001,40 @@ private function get_network( $network_id ) { * [--site-path=] * : Filter by path. Avoids conflict with the global `--path` parameter. * + * [--blog_id=] + * : Filter by site ID. + * + * [--site_id=] + * : Filter by the ID of the network the site belongs to. `--network` is an + * alias for this, and takes precedence when both are given. + * + * [--domain=] + * : Filter by domain. + * + * [--registered=] + * : Filter by the date the site was registered. + * + * [--last_updated=] + * : Filter by the date the site was last updated. + * + * [--public=] + * : Filter by whether the site is public. Accepts 1 or 0. + * + * [--archived=] + * : Filter by whether the site is archived. Accepts 1 or 0. + * + * [--mature=] + * : Filter by whether the site is flagged as mature. Accepts 1 or 0. + * + * [--spam=] + * : Filter by whether the site is flagged as spam. Accepts 1 or 0. + * + * [--deleted=] + * : Filter by whether the site is flagged as deleted. Accepts 1 or 0. + * + * [--lang_id=] + * : Filter by language ID. + * * [--field=] * : Prints the value of a single field for each site. * diff --git a/src/User_Application_Password_Command.php b/src/User_Application_Password_Command.php index 0dffef714..4ad9ad54e 100644 --- a/src/User_Application_Password_Command.php +++ b/src/User_Application_Password_Command.php @@ -81,6 +81,29 @@ final class User_Application_Password_Command { * [--=] * : Filter the list by a specific field. * + * [--uuid=] + * : Filter by the universally unique ID of the application password. + * + * [--app_id=] + * : Filter by the application ID. `--app-id` is also accepted. + * + * [--name=] + * : Filter by the name of the application password. + * + * [--password=] + * : Filter by the hashed password. + * + * [--created=] + * : Filter by the Unix timestamp the application password was created at. + * + * [--last_used=] + * : Filter by the Unix timestamp the application password was last used at. + * `--last-used` is also accepted. + * + * [--last_ip=] + * : Filter by the IP address the application password was last used from. + * `--last-ip` is also accepted. + * * [--field=] * : Prints the value of a single field for each application password. * From 843b8845942b464abd65ad6ebfaab441bdaf2105 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 05:45:09 +0000 Subject: [PATCH 2/2] Address review feedback on the list filter documentation Three things came out of review, one of them a real bug. `--created` and `--last_used` on `user application-password list` never matched anything. Core returns those two fields as integers, an argument always arrives as a string, and the filter compared with ===. Documenting them without noticing that would have added two more parameters that accept a value and quietly discard it, which is the exact failure this whole line of work is about. Compare as strings instead, the way Signup_Command already does for the same reason, and cover both filters - plus the --last-used spelling - in the scenario. The site list assertions were vacuous. Every site in the fixture shared the same status, so --public=1, --archived=0 and --spam=0 all matched the full set and would have passed even if the filters were ignored entirely. Give the two extra sites contrasting statuses, so each assertion now fails if its filter stops working, and add a case for --network taking precedence over --site_id. The new application password scenario was missing @require-wp-5.6, which every other scenario in that file carries. Without it the scenario runs on WordPress versions that do not have the feature at all. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- features/site.feature | 74 ++++++++++++++++++---- features/user-application-password.feature | 41 ++++++++++++ src/User_Application_Password_Command.php | 12 +++- 3 files changed, 113 insertions(+), 14 deletions(-) diff --git a/features/site.feature b/features/site.feature index f77406621..804e6ccbc 100644 --- a/features/site.feature +++ b/features/site.feature @@ -942,30 +942,54 @@ Feature: Manage sites in a multisite installation When I run `wp site create --slug=first --porcelain` Then STDOUT should be a number + And save STDOUT as {FIRST_ID} + + When I run `wp site create --slug=second --porcelain` + Then STDOUT should be a number + And save STDOUT as {SECOND_ID} + + # Give the two new sites contrasting statuses, so each filter below has + # something to discriminate on and cannot pass by matching every site. + When I run `wp site archive {FIRST_ID}` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp site private {SECOND_ID}` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp site spam {SECOND_ID}` + Then STDOUT should contain: + """ + Success: + """ When I run `wp site list --format=count` Then STDOUT should be: """ - 2 + 3 """ - # site_id is the ID of the network the site belongs to, not the site's own ID. - When I run `wp site list --site_id=1 --format=count` + When I run `wp site list --archived=1 --field=blog_id` Then STDOUT should be: """ - 2 + {FIRST_ID} """ - When I run `wp site list --site_id=2 --format=count` + When I run `wp site list --archived=0 --format=count` Then STDOUT should be: """ - 0 + 2 """ - When I run `wp site list --blog_id=2 --field=blog_id` + When I run `wp site list --public=0 --field=blog_id` Then STDOUT should be: """ - 2 + {SECOND_ID} """ When I run `wp site list --public=1 --format=count` @@ -974,20 +998,46 @@ Feature: Manage sites in a multisite installation 2 """ - When I run `wp site list --archived=0 --format=count` + When I run `wp site list --spam=1 --field=blog_id` + Then STDOUT should be: + """ + {SECOND_ID} + """ + + When I run `wp site list --spam=0 --format=count` Then STDOUT should be: """ 2 """ - When I run `wp site list --deleted=1 --format=count` + When I run `wp site list --deleted=0 --format=count` + Then STDOUT should be: + """ + 3 + """ + + When I run `wp site list --blog_id={FIRST_ID} --field=blog_id` + Then STDOUT should be: + """ + {FIRST_ID} + """ + + # site_id is the ID of the network the site belongs to, not the site's own ID. + When I run `wp site list --site_id=1 --format=count` + Then STDOUT should be: + """ + 3 + """ + + When I run `wp site list --site_id=2 --format=count` Then STDOUT should be: """ 0 """ - When I run `wp site list --spam=0 --format=count` + # --network is an alias for site_id and takes precedence over it. + When I run `wp site list --site_id=2 --network=1 --format=count` Then STDOUT should be: """ - 2 + 3 """ diff --git a/features/user-application-password.feature b/features/user-application-password.feature index 3c02088d2..c2779900b 100644 --- a/features/user-application-password.feature +++ b/features/user-application-password.feature @@ -316,6 +316,7 @@ Feature: Manage user custom fields true """ + @require-wp-5.6 Scenario: Filter application passwords by field Given a WP install @@ -364,3 +365,43 @@ Feature: Manage user custom fields """ 0 """ + + # 'created' and 'last_used' are integers in core but strings on the command + # line, so these only match if the comparison normalizes them. + When I run `wp user application-password list 1 --name=myapp --field=created` + Then STDOUT should not be empty + And save STDOUT as {CREATED} + + When I run `wp user application-password list 1 --created={CREATED} --field=name` + Then STDOUT should contain: + """ + myapp + """ + + When I run `wp user application-password list 1 --created=1 --format=count` + Then STDOUT should be: + """ + 0 + """ + + When I run `wp user application-password record-usage 1 {UUID}` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp user application-password list 1 --name=myapp --field=last_used` + Then STDOUT should not be empty + And save STDOUT as {LAST_USED} + + When I run `wp user application-password list 1 --last_used={LAST_USED} --field=name` + Then STDOUT should be: + """ + myapp + """ + + When I run `wp user application-password list 1 --last-used={LAST_USED} --field=name` + Then STDOUT should be: + """ + myapp + """ diff --git a/src/User_Application_Password_Command.php b/src/User_Application_Password_Command.php index 4ad9ad54e..0281fee5d 100644 --- a/src/User_Application_Password_Command.php +++ b/src/User_Application_Password_Command.php @@ -209,10 +209,18 @@ static function ( $a, $b ) use ( $orderby, $order ) { $value = Utils\get_flag_value( $assoc_args, $field ); + // 'created' and 'last_used' come back from core as integers, while an + // argument always arrives as a string, so compare them as strings. + $filter_value = is_scalar( $value ) ? (string) $value : ''; + $application_passwords = array_filter( $application_passwords, - static function ( $application_password ) use ( $field, $value ) { - return $application_password[ $field ] === $value; + static function ( $application_password ) use ( $field, $filter_value ) { + $item_value = isset( $application_password[ $field ] ) && is_scalar( $application_password[ $field ] ) + ? (string) $application_password[ $field ] + : ''; + + return $item_value === $filter_value; } ); }