diff --git a/README.md b/README.md index 1e71a5ab..918c3bce 100644 --- a/README.md +++ b/README.md @@ -1685,6 +1685,8 @@ Errors if the option already exists. Should this option be automatically loaded. --- options: + - 'on' + - 'off' - 'yes' - 'no' --- @@ -1990,6 +1992,8 @@ wp option update [] [--autoload=] [--format=] Requires WP 4.2. Should this option be automatically loaded. --- options: + - 'on' + - 'off' - 'yes' - 'no' --- @@ -2053,6 +2057,8 @@ wp option set-autoload Should this option be automatically loaded. --- options: + - 'on' + - 'off' - 'yes' - 'no' --- @@ -6294,6 +6300,196 @@ wp user set-role [] +### wp user signup + +Manages signups on a multisite installation. + +~~~ +wp user signup +~~~ + +**EXAMPLES** + + # List signups. + $ wp user signup list + +-----------+------------+---------------------+---------------------+--------+------------------+ + | signup_id | user_login | user_email | registered | active | activation_key | + +-----------+------------+---------------------+---------------------+--------+------------------+ + | 1 | bobuser | bobuser@example.com | 2024-03-13 05:46:53 | 1 | 7320b2f009266618 | + | 2 | johndoe | johndoe@example.com | 2024-03-13 06:24:44 | 0 | 9068d859186cd0b5 | + +-----------+------------+---------------------+---------------------+--------+------------------+ + + # Activate signup. + $ wp user signup activate 2 + Signup 2 activated. Password: bZFSGsfzb9xs + Success: Activated 1 of 1 signups. + + # Delete signup. + $ wp user signup delete 3 + Signup 3 deleted. + Success: Deleted 1 of 1 signups. + + + + + +### wp user signup activate + +Activates one or more signups. + +~~~ +wp user signup activate ... +~~~ + +**OPTIONS** + + ... + The signup ID, user login, user email, or activation key of the signup(s) to activate. + +**EXAMPLES** + + # Activate signup. + $ wp user signup activate 2 + Signup 2 activated. Password: bZFSGsfzb9xs + Success: Activated 1 of 1 signups. + + + +### wp user signup delete + +Deletes one or more signups. + +~~~ +wp user signup delete [...] [--all] +~~~ + +**OPTIONS** + + [...] + The signup ID, user login, user email, or activation key of the signup(s) to delete. + + [--all] + If set, all signups will be deleted. + +**EXAMPLES** + + # Delete signup. + $ wp user signup delete 3 + Signup 3 deleted. + Success: Deleted 1 of 1 signups. + + + +### wp user signup get + +Gets details about a signup. + +~~~ +wp user signup get [--field=] [--fields=] [--format=] +~~~ + +**OPTIONS** + + + The signup ID, user login, user email, or activation key. + + [--field=] + Instead of returning the whole signup, returns the value of a single field. + + [--fields=] + Limit the output to specific fields. Defaults to all fields. + + [--format=] + Render output in a particular format. + --- + default: table + options: + - table + - csv + - json + - yaml + --- + +**EXAMPLES** + + # Get signup. + $ wp user signup get 1 --field=user_login + bobuser + + # Get signup and export to JSON file. + $ wp user signup get bobuser --format=json > bobuser.json + + + +### wp user signup list + +Lists signups. + +~~~ +wp user signup list [--=] [--field=] [--fields=] [--format=] [--per_page=] +~~~ + + [--=] + Filter the list by a specific field. + + [--field=] + Prints the value of a single field for each signup. + + [--fields=] + Limit the output to specific object fields. + + [--format=] + Render output in a particular format. + --- + default: table + options: + - table + - csv + - ids + - json + - count + - yaml + --- + + [--per_page=] + Limits the signups to the given number. Defaults to none. + +**AVAILABLE FIELDS** + +These fields will be displayed by default for each signup: + +* signup_id +* user_login +* user_email +* registered +* active +* activation_key + +These fields are optionally available: + +* domain +* path +* title +* activated +* meta + +**EXAMPLES** + + # List signup IDs. + $ wp user signup list --field=signup_id + 1 + + # List all signups. + $ wp user signup list + +-----------+------------+---------------------+---------------------+--------+------------------+ + | signup_id | user_login | user_email | registered | active | activation_key | + +-----------+------------+---------------------+---------------------+--------+------------------+ + | 1 | bobuser | bobuser@example.com | 2024-03-13 05:46:53 | 1 | 7320b2f009266618 | + | 2 | johndoe | johndoe@example.com | 2024-03-13 06:24:44 | 0 | 9068d859186cd0b5 | + +-----------+------------+---------------------+---------------------+--------+------------------+ + + + ### wp user spam Marks one or more users as spam on multisite. diff --git a/composer.json b/composer.json index 163b0bd4..4aea0938 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "wp-cli/wp-cli": "^2.10" + "wp-cli/wp-cli": "^2.11" }, "require-dev": { "wp-cli/cache-command": "^1 || ^2", @@ -198,6 +198,11 @@ "user session destroy", "user session list", "user set-role", + "user signup", + "user signup activate", + "user signup delete", + "user signup get", + "user signup list", "user spam", "user term", "user term add", diff --git a/entity-command.php b/entity-command.php index a9e5381d..2146f886 100644 --- a/entity-command.php +++ b/entity-command.php @@ -100,3 +100,15 @@ if ( class_exists( 'WP_CLI\Dispatcher\CommandNamespace' ) ) { WP_CLI::add_command( 'network', 'Network_Namespace' ); } + +WP_CLI::add_command( + 'user signup', + 'Signup_Command', + array( + 'before_invoke' => function () { + if ( ! is_multisite() ) { + WP_CLI::error( 'This is not a multisite installation.' ); + } + }, + ) +); diff --git a/features/signup.feature b/features/signup.feature new file mode 100644 index 00000000..d7c72976 --- /dev/null +++ b/features/signup.feature @@ -0,0 +1,198 @@ +Feature: Manage signups in a multisite installation + + Scenario: Not applicable in single installation site + Given a WP install + + When I try `wp user signup list` + Then STDERR should be: + """ + Error: This is not a multisite installation. + """ + + Scenario: List signups + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + And I run `wp eval 'wpmu_signup_user( "johnuser", "johnuser@example.com" );'` + + When I run `wp user signup list --fields=signup_id,user_login,user_email,active --format=csv` + Then STDOUT should be: + """ + signup_id,user_login,user_email,active + 1,bobuser,bobuser@example.com,0 + 2,johnuser,johnuser@example.com,0 + """ + + When I run `wp user signup list --format=count --active=1` + Then STDOUT should be: + """ + 0 + """ + + When I run `wp user signup activate bobuser` + Then STDOUT should contain: + """ + Success: Activated 1 of 1 signups. + """ + + When I run `wp user signup list --fields=signup_id,user_login,user_email,active --format=csv --active=1` + Then STDOUT should be: + """ + signup_id,user_login,user_email,active + 1,bobuser,bobuser@example.com,1 + """ + + When I run `wp user signup list --fields=signup_id,user_login,user_email,active --format=csv --per_page=1` + Then STDOUT should be: + """ + signup_id,user_login,user_email,active + 1,bobuser,bobuser@example.com,1 + """ + + Scenario: Get signup + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + + When I run `wp user signup get 1 --field=user_login` + Then STDOUT should be: + """ + bobuser + """ + + When I run `wp user signup get bobuser --fields=signup_id,user_login,user_email,active --format=csv` + Then STDOUT should be: + """ + signup_id,user_login,user_email,active + 1,bobuser,bobuser@example.com,0 + """ + + Scenario: Activate signup + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + + When I run `wp user signup get bobuser --field=active` + Then STDOUT should be: + """ + 0 + """ + + When I run `wp user signup activate bobuser` + Then STDOUT should contain: + """ + Success: Activated 1 of 1 signups. + """ + + When I try the previous command again + Then STDERR should contain: + """ + Warning: Failed activating signup 1. + """ + + When I run `wp user signup get bobuser --field=active` + Then STDOUT should be: + """ + 1 + """ + + When I run `wp user get bobuser --field=user_email` + Then STDOUT should be: + """ + bobuser@example.com + """ + + Scenario: Activate multiple signups + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + And I run `wp eval 'wpmu_signup_user( "johnuser", "johnuser@example.com" );'` + + When I run `wp user signup list --active=0 --format=count` + Then STDOUT should be: + """ + 2 + """ + + When I run `wp user signup activate bobuser johnuser` + Then STDOUT should contain: + """ + Success: Activated 2 of 2 signups. + """ + + When I run `wp user signup list --active=1 --format=count` + Then STDOUT should be: + """ + 2 + """ + + Scenario: Activate blog signup entry + Given a WP multisite install + And I run `wp eval 'wpmu_signup_blog( "example.com", "/bobsite/", "My Awesome Title", "bobuser", "bobuser@example.com" );'` + + When I run `wp user signup get bobuser --fields=user_login,domain,path,active --format=csv` + Then STDOUT should be: + """ + user_login,domain,path,active + bobuser,example.com,/bobsite/,0 + """ + + When I run `wp user signup activate bobuser` + Then STDOUT should contain: + """ + Success: Activated 1 of 1 signups. + """ + + When I run `wp site list --fields=domain,path` + Then STDOUT should be a table containing rows: + | domain | path | + | example.com | / | + | example.com | /bobsite/ | + + Scenario: Delete signups + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + And I run `wp eval 'wpmu_signup_user( "johnuser", "johnuser@example.com" );'` + + When I run `wp user signup get bobuser --field=user_email` + Then STDOUT should be: + """ + bobuser@example.com + """ + + When I run `wp user signup get johnuser --field=user_email` + Then STDOUT should be: + """ + johnuser@example.com + """ + + When I run `wp user signup delete bobuser@example.com johnuser@example.com` + Then STDOUT should contain: + """ + Success: Deleted 2 of 2 signups. + """ + + When I try `wp user signup get bobuser` + Then STDERR should be: + """ + Error: Invalid signup ID, email, login, or activation key: 'bobuser' + """ + + Scenario: Delete all signups + Given a WP multisite install + And I run `wp eval 'wpmu_signup_user( "bobuser", "bobuser@example.com" );'` + And I run `wp eval 'wpmu_signup_user( "johnuser", "johnuser@example.com" );'` + + When I try `wp user signup delete` + Then STDERR should be: + """ + Error: You need to specify either one or more signups or provide the --all flag. + """ + + When I run `wp user signup delete --all` + Then STDOUT should contain: + """ + Success: Deleted all signups. + """ + + When I run `wp user signup list --format=count` + Then STDOUT should be: + """ + 0 + """ diff --git a/phpcs.xml.dist b/phpcs.xml.dist index b0a3612e..1e08f6e1 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -53,7 +53,7 @@ - */src/WP_CLI/Fetchers/(Comment|Post|Site|User)\.php$ + */src/WP_CLI/Fetchers/(Comment|Post|Signup|Site|User)\.php$ */src/WP_CLI/CommandWith(DBObject|Meta|Terms)\.php$ @@ -65,6 +65,7 @@ */src/Network_Namespace\.php$ */src/Option_Command\.php$ */src/Post(_Meta|_Term|_Type)?_Command\.php$ + */src/Signup_Command\.php$ */src/Site(_Meta|_Option)?_Command\.php$ */src/Term(_Meta)?_Command\.php$ */src/User(_Application_Password|_Meta|_Session|_Term)?_Command\.php$ diff --git a/src/Signup_Command.php b/src/Signup_Command.php new file mode 100644 index 00000000..21a647ac --- /dev/null +++ b/src/Signup_Command.php @@ -0,0 +1,334 @@ +fetcher = new SignupFetcher(); + } + + /** + * Lists signups. + * + * [--=] + * : Filter the list by a specific field. + * + * [--field=] + * : Prints the value of a single field for each signup. + * + * [--fields=] + * : Limit the output to specific object fields. + * + * [--format=] + * : Render output in a particular format. + * --- + * default: table + * options: + * - table + * - csv + * - ids + * - json + * - count + * - yaml + * --- + * + * [--per_page=] + * : Limits the signups to the given number. Defaults to none. + * + * ## AVAILABLE FIELDS + * + * These fields will be displayed by default for each signup: + * + * * signup_id + * * user_login + * * user_email + * * registered + * * active + * * activation_key + * + * These fields are optionally available: + * + * * domain + * * path + * * title + * * activated + * * meta + * + * ## EXAMPLES + * + * # List signup IDs. + * $ wp user signup list --field=signup_id + * 1 + * + * # List all signups. + * $ wp user signup list + * +-----------+------------+---------------------+---------------------+--------+------------------+ + * | signup_id | user_login | user_email | registered | active | activation_key | + * +-----------+------------+---------------------+---------------------+--------+------------------+ + * | 1 | bobuser | bobuser@example.com | 2024-03-13 05:46:53 | 1 | 7320b2f009266618 | + * | 2 | johndoe | johndoe@example.com | 2024-03-13 06:24:44 | 0 | 9068d859186cd0b5 | + * +-----------+------------+---------------------+---------------------+--------+------------------+ + * + * @subcommand list + * + * @package wp-cli + */ + public function list_( $args, $assoc_args ) { + global $wpdb; + + if ( isset( $assoc_args['fields'] ) ) { + $assoc_args['fields'] = explode( ',', $assoc_args['fields'] ); + } else { + $assoc_args['fields'] = $this->obj_fields; + } + + $signups = array(); + + $per_page = (int) Utils\get_flag_value( $assoc_args, 'per_page' ); + + $limit = $per_page ? $wpdb->prepare( 'LIMIT %d', $per_page ) : ''; + + $query = "SELECT * FROM $wpdb->signups {$limit}"; + + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Prepared properly above. + $results = $wpdb->get_results( $query, ARRAY_A ); + + if ( $results ) { + foreach ( $results as $item ) { + // Support features like --active=0. + foreach ( array_keys( $item ) as $field ) { + if ( isset( $assoc_args[ $field ] ) && $assoc_args[ $field ] !== $item[ $field ] ) { + continue 2; + } + } + + $signups[] = $item; + } + } + + $format = Utils\get_flag_value( $assoc_args, 'format', 'table' ); + + $formatter = $this->get_formatter( $assoc_args ); + + if ( 'ids' === $format ) { + WP_CLI::line( implode( ' ', wp_list_pluck( $signups, 'signup_id' ) ) ); + } else { + $formatter->display_items( $signups ); + } + } + + /** + * Gets details about a signup. + * + * ## OPTIONS + * + * + * : The signup ID, user login, user email, or activation key. + * + * [--field=] + * : Instead of returning the whole signup, returns the value of a single field. + * + * [--fields=] + * : Limit the output to specific fields. Defaults to all fields. + * + * [--format=] + * : Render output in a particular format. + * --- + * default: table + * options: + * - table + * - csv + * - json + * - yaml + * --- + * + * ## EXAMPLES + * + * # Get signup. + * $ wp user signup get 1 --field=user_login + * bobuser + * + * # Get signup and export to JSON file. + * $ wp user signup get bobuser --format=json > bobuser.json + * + * @package wp-cli + */ + public function get( $args, $assoc_args ) { + $signup = $this->fetcher->get_check( $args[0] ); + + if ( empty( $assoc_args['fields'] ) ) { + $assoc_args['fields'] = array_keys( (array) $signup ); + } + + $formatter = $this->get_formatter( $assoc_args ); + + $formatter->display_items( array( $signup ) ); + } + + /** + * Activates one or more signups. + * + * ## OPTIONS + * + * ... + * : The signup ID, user login, user email, or activation key of the signup(s) to activate. + * + * ## EXAMPLES + * + * # Activate signup. + * $ wp user signup activate 2 + * Signup 2 activated. Password: bZFSGsfzb9xs + * Success: Activated 1 of 1 signups. + * + * @package wp-cli + */ + public function activate( $args, $assoc_args ) { + $signups = $this->fetcher->get_many( $args ); + + $successes = 0; + $errors = 0; + + foreach ( $signups as $signup ) { + $result = wpmu_activate_signup( $signup->activation_key ); + + if ( is_wp_error( $result ) ) { + WP_CLI::warning( "Failed activating signup {$signup->signup_id}." ); + ++$errors; + } else { + WP_CLI::log( "Signup {$signup->signup_id} activated. Password: {$result['password']}" ); + ++$successes; + } + } + + Utils\report_batch_operation_results( 'signup', 'activate', count( $args ), $successes, $errors ); + } + + /** + * Deletes one or more signups. + * + * ## OPTIONS + * + * [...] + * : The signup ID, user login, user email, or activation key of the signup(s) to delete. + * + * [--all] + * : If set, all signups will be deleted. + * + * ## EXAMPLES + * + * # Delete signup. + * $ wp user signup delete 3 + * Signup 3 deleted. + * Success: Deleted 1 of 1 signups. + * + * @package wp-cli + */ + public function delete( $args, $assoc_args ) { + $count = count( $args ); + + $all = Utils\get_flag_value( $assoc_args, 'all', false ); + + if ( ( 0 < $count && true === $all ) || ( 0 === $count && true !== $all ) ) { + WP_CLI::error( 'You need to specify either one or more signups or provide the --all flag.' ); + } + + if ( true === $all ) { + if ( ! $this->delete_all_signups() ) { + WP_CLI::error( 'Error deleting signups.' ); + } + + WP_CLI::success( 'Deleted all signups.' ); + WP_CLI::halt( 0 ); + } + + $signups = $this->fetcher->get_many( $args ); + + $successes = 0; + $errors = 0; + + foreach ( $signups as $signup ) { + if ( $this->delete_signup( $signup ) ) { + WP_CLI::log( "Signup {$signup->signup_id} deleted." ); + ++$successes; + } else { + WP_CLI::warning( "Failed deleting signup {$signup->signup_id}." ); + ++$errors; + } + } + + Utils\report_batch_operation_results( 'signup', 'delete', $count, $successes, $errors ); + } + + /** + * Deletes signup. + * + * @param stdClasss $signup + * @return bool True if success; otherwise false. + */ + private function delete_signup( $signup ) { + global $wpdb; + + $signup_id = $signup->signup_id; + + $result = $wpdb->delete( $wpdb->signups, array( 'signup_id' => $signup_id ), array( '%d' ) ); + + return $result ? true : false; + } + + /** + * Deletes all signup. + * + * @return bool True if success; otherwise false. + */ + private function delete_all_signups() { + global $wpdb; + + $results = $wpdb->query( 'DELETE FROM ' . $wpdb->signups ); + + return $results ? true : false; + } +}