From b4355e0f45de7fb5e080e85e2a58e5b91ef828ea Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 11:36:47 +0000 Subject: [PATCH 1/2] Add failing tests for silently ignored parameters Three documented parameters accept a value and drop it: - `user create --user_nicename` and `--rich_editing`. User_Command::create() builds an explicit stdClass for wp_insert_user() and never sets either property, so both are read off $assoc_args nowhere. - `post create` / `post update --post_modified` and `--post_modified_gmt`. wp_insert_post() computes both itself and never reads them from $postarr; on update they are unconditionally current_time( 'mysql' ). All three report success. These are the same silent no-op as https://github.com/wp-cli/wp-cli/issues/5286, reached from the other side: there the parameter name is wrong, here the name is right and the value is discarded anyway. These scenarios fail on main. The fix follows in the next commit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- features/post.feature | 48 +++++++++++++++++++++++++++++++++++++++++++ features/user.feature | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/features/post.feature b/features/post.feature index 02a845e01..0462afb32 100644 --- a/features/post.feature +++ b/features/post.feature @@ -591,3 +591,51 @@ Feature: Manage WordPress posts """ {"block_version":1} """ + + Scenario: Set a post's modification date on update + Given a WP install + + When I run `wp post create --post_title='A post' --post_status=publish --porcelain` + Then STDOUT should be a number + And save STDOUT as {POST_ID} + + When I run `wp post update {POST_ID} --post_modified='2020-01-01 12:00:00'` + Then STDOUT should be: + """ + Success: Updated post {POST_ID}. + """ + + When I run `wp post get {POST_ID} --field=post_modified` + Then STDOUT should be: + """ + 2020-01-01 12:00:00 + """ + + Scenario: Set a post's modification date on create + Given a WP install + + When I run `wp post create --post_title='Another post' --post_date='2019-05-05 10:00:00' --post_modified='2020-01-01 12:00:00' --porcelain` + Then STDOUT should be a number + And save STDOUT as {POST_ID} + + When I run `wp post get {POST_ID} --field=post_modified` + Then STDOUT should be: + """ + 2020-01-01 12:00:00 + """ + + Scenario: A post's modification date defaults to the current time + Given a WP install + + When I run `wp post create --post_title='Undated post' --post_status=publish --porcelain` + Then STDOUT should be a number + And save STDOUT as {POST_ID} + + When I run `wp post update {POST_ID} --post_title='Retitled'` + Then STDOUT should be: + """ + Success: Updated post {POST_ID}. + """ + + When I run `wp post get {POST_ID} --field=post_modified` + Then STDOUT should not be empty diff --git a/features/user.feature b/features/user.feature index 76ede2740..dfcaba6e3 100644 --- a/features/user.feature +++ b/features/user.feature @@ -811,3 +811,41 @@ Feature: Manage WordPress users """ newtestuser """ + + Scenario: Create a user with a nicename and rich editing preference + Given a WP install + + When I run `wp user create bob bob@example.com --user_nicename=bobby --rich_editing=false --porcelain` + Then STDOUT should be a number + And save STDOUT as {USER_ID} + + When I run `wp user get {USER_ID} --field=user_nicename` + Then STDOUT should be: + """ + bobby + """ + + When I run `wp user meta get {USER_ID} rich_editing` + Then STDOUT should be: + """ + false + """ + + Scenario: Creating a user without a nicename falls back to the login + Given a WP install + + When I run `wp user create carol carol@example.com --porcelain` + Then STDOUT should be a number + And save STDOUT as {USER_ID} + + When I run `wp user get {USER_ID} --field=user_nicename` + Then STDOUT should be: + """ + carol + """ + + When I run `wp user meta get {USER_ID} rich_editing` + Then STDOUT should be: + """ + true + """ From aeaa92ba467817b364191456073c26f4cf135a20 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 11:45:23 +0000 Subject: [PATCH 2/2] Apply the parameters that were being silently discarded Makes the tests from the previous commit pass. user create: set user_nicename and rich_editing on the object handed to wp_insert_user(). Both use the same `false` default as the neighbouring properties, which core reads as "not supplied" - user_nicename then falls back to the login and rich_editing to 'true'. post create / post update: wp_insert_post() derives post_modified and post_modified_gmt itself and never reads them back from $postarr, so apply the requested value through the wp_insert_post_data filter, registered around the insert and removed straight after. When only one of the pair is given the other is derived from it, so the two never disagree. That last change makes a previously harmless bug visible: `post create --from-post` copies the source post's fields, and post_modified was not among the ones it unset. Core ignored it before, so the duplicate got the current time; now it would inherit the original's. Unset it alongside post_date, and cover it with a scenario. The alternative to all of this is to remove the parameters from the docblocks instead. That is a smaller change but loses functionality the documentation has been promising, so it seemed the wrong way round - happy to invert it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- features/post-create-duplicate.feature | 20 ++++++++++ src/Post_Command.php | 55 ++++++++++++++++++++++++++ src/User_Command.php | 4 ++ 3 files changed, 79 insertions(+) diff --git a/features/post-create-duplicate.feature b/features/post-create-duplicate.feature index bfc9f3bc2..dcca8d81e 100644 --- a/features/post-create-duplicate.feature +++ b/features/post-create-duplicate.feature @@ -79,3 +79,23 @@ Feature: Create Duplicate WordPress post from existing posts. Then STDOUT should be a table containing rows: | Field | Value | | post_type | page | + + Scenario: Duplicating a post does not inherit its modification date + Given a WP install + + When I run `wp post create --post_title='Source' --post_status=publish --porcelain` + Then STDOUT should be a number + And save STDOUT as {SOURCE_ID} + + When I run `wp post update {SOURCE_ID} --post_modified='2015-03-03 09:00:00'` + Then STDOUT should not be empty + + When I run `wp post create --from-post={SOURCE_ID} --post_title='Duplicate' --porcelain` + Then STDOUT should be a number + And save STDOUT as {DUPLICATE_ID} + + When I run `wp post get {DUPLICATE_ID} --field=post_modified` + Then STDOUT should not contain: + """ + 2015-03-03 + """ diff --git a/src/Post_Command.php b/src/Post_Command.php index 5e10c546b..68fde8467 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -194,6 +194,8 @@ public function create( $args, $assoc_args ) { $post_id = $post_arr['ID']; unset( $post_arr['post_date'] ); unset( $post_arr['post_date_gmt'] ); + unset( $post_arr['post_modified'] ); + unset( $post_arr['post_modified_gmt'] ); unset( $post_arr['guid'] ); unset( $post_arr['ID'] ); @@ -242,8 +244,14 @@ function ( $params ) { } } + $modified_callback = self::add_post_modified_filter( $params ); + $result = wp_insert_post( $params, true ); + if ( $modified_callback ) { + remove_filter( 'wp_insert_post_data', $modified_callback ); + } + if ( $filter_callback ) { remove_filter( 'user_has_cap', $filter_callback ); } @@ -253,6 +261,47 @@ function ( $params ) { ); } + /** + * Applies an explicitly requested modification date. + * + * wp_insert_post() derives post_modified and post_modified_gmt itself and + * never reads them back from $postarr — on update they are unconditionally + * the current time — so the documented parameters have to be applied to the + * post data on its way to the database. + * + * @param array $params Parameters passed to wp_insert_post() or wp_update_post(). + * @return callable|null The registered callback, for the caller to remove, or null when + * no modification date was requested. + */ + private static function add_post_modified_filter( $params ) { + $modified = []; + + foreach ( [ 'post_modified', 'post_modified_gmt' ] as $key ) { + if ( ! empty( $params[ $key ] ) ) { + $modified[ $key ] = $params[ $key ]; + } + } + + if ( empty( $modified ) ) { + return null; + } + + // Keep the pair consistent when only one of the two was given. + if ( ! isset( $modified['post_modified_gmt'] ) ) { + $modified['post_modified_gmt'] = get_gmt_from_date( $modified['post_modified'] ); + } elseif ( ! isset( $modified['post_modified'] ) ) { + $modified['post_modified'] = get_date_from_gmt( $modified['post_modified_gmt'] ); + } + + $callback = static function ( $data ) use ( $modified ) { + return array_merge( $data, $modified ); + }; + + add_filter( 'wp_insert_post_data', $callback ); + + return $callback; + } + /** * Updates one or more existing posts. * @@ -422,8 +471,14 @@ function ( $params ) { } } + $modified_callback = self::add_post_modified_filter( $params ); + $result = wp_update_post( $params, true ); + if ( $modified_callback ) { + remove_filter( 'wp_insert_post_data', $modified_callback ); + } + if ( $filter_callback ) { remove_filter( 'user_has_cap', $filter_callback ); } diff --git a/src/User_Command.php b/src/User_Command.php index 59d6f3d34..98bcecc19 100644 --- a/src/User_Command.php +++ b/src/User_Command.php @@ -437,6 +437,10 @@ public function create( $args, $assoc_args ) { $user->user_url = Utils\get_flag_value( $assoc_args, 'user_url', false ); + $user->user_nicename = Utils\get_flag_value( $assoc_args, 'user_nicename', false ); + + $user->rich_editing = Utils\get_flag_value( $assoc_args, 'rich_editing', false ); + if ( isset( $assoc_args['user_pass'] ) ) { $user->user_pass = $assoc_args['user_pass']; } else {