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/features/post.feature b/features/post.feature index 7d7571e1b..ce9858e4e 100644 --- a/features/post.feature +++ b/features/post.feature @@ -688,3 +688,107 @@ Feature: Manage WordPress posts """ {DOOMED_ID} """ + + 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 + + # Given a known modification date, so that an update which failed to set one + # would leave this value behind and be caught. + When I run `wp post create --post_title='Undated post' --post_status=publish --post_modified='2019-02-03 04:05:06' --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: + """ + 2019-02-03 04:05:06 + """ + + 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 contain: + """ + 2019-02-03 04:05:06 + """ + And STDOUT should not be empty + + Scenario: Setting only the GMT modification date derives the local one + Given a WP install + + # A timezone with an offset, so the derived local value is distinguishable + # from the GMT one it was derived from. 1 January is outside DST in New York. + When I run `wp option update timezone_string 'America/New_York'` + Then STDOUT should not be empty + + When I run `wp post create --post_title='GMT only' --post_status=publish --post_modified_gmt='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_gmt` + Then STDOUT should be: + """ + 2020-01-01 12:00:00 + """ + + When I run `wp post get {POST_ID} --field=post_modified` + Then STDOUT should be: + """ + 2020-01-01 07:00:00 + """ + + # And the same on update, which takes the other of the two code paths. + When I run `wp post update {POST_ID} --post_modified_gmt='2020-06-01 12:00:00'` + Then STDOUT should be: + """ + Success: Updated post {POST_ID}. + """ + + When I run `wp post get {POST_ID} --field=post_modified_gmt` + Then STDOUT should be: + """ + 2020-06-01 12:00:00 + """ + + # June is inside DST, so the offset is four hours rather than five. + When I run `wp post get {POST_ID} --field=post_modified` + Then STDOUT should be: + """ + 2020-06-01 08:00:00 + """ diff --git a/features/user.feature b/features/user.feature index 76ede2740..f89518126 100644 --- a/features/user.feature +++ b/features/user.feature @@ -811,3 +811,86 @@ 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 + """ + + # Multisite creates the user through wpmu_create_user(), which takes only a + # login, a password and an email, and then applies the rest with + # wp_update_user(). That is a different path to wp_insert_user(), so both + # fields are worth asserting again here. + Scenario: Create a user with a nicename and rich editing preference on multisite + Given a WP multisite install + + # Multisite requires a login of at least four characters, and a login that + # differs from the nicename shows the nicename was applied rather than + # derived. + When I run `wp user create robert robert@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 on multisite + Given a WP multisite 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 + """ diff --git a/src/Post_Command.php b/src/Post_Command.php index ab3a26f79..ffecd3063 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -201,6 +201,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'] ); @@ -249,8 +251,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 ); } @@ -260,6 +268,51 @@ 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 ) { + $local = ! empty( $params['post_modified'] ) && is_scalar( $params['post_modified'] ) + ? (string) $params['post_modified'] + : null; + $gmt = ! empty( $params['post_modified_gmt'] ) && is_scalar( $params['post_modified_gmt'] ) + ? (string) $params['post_modified_gmt'] + : null; + + if ( null === $local && null === $gmt ) { + return null; + } + + // Keep the pair consistent when only one of the two was given. + if ( null === $gmt ) { + $gmt = get_gmt_from_date( $local ); + } elseif ( null === $local ) { + $local = get_date_from_gmt( $gmt ); + } + + $modified = [ + 'post_modified' => $local, + 'post_modified_gmt' => $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. * @@ -432,8 +485,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 a508059ad..02909d940 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 {