diff --git a/modules/custom-status/custom-status.php b/modules/custom-status/custom-status.php
index 0086c70a1..f0776d937 100644
--- a/modules/custom-status/custom-status.php
+++ b/modules/custom-status/custom-status.php
@@ -98,7 +98,8 @@ function init() {
// These seven-ish methods are hacks for fixing bugs in WordPress core
add_action( 'admin_init', array( $this, 'check_timestamp_on_publish' ) );
add_filter( 'wp_insert_post_data', array( $this, 'fix_custom_status_timestamp' ), 10, 2 );
- add_action( 'wp_insert_post', array( $this, 'fix_post_name' ), 10, 2 );
+ add_filter( 'wp_insert_post_data', array( $this, 'maybe_keep_post_name_empty' ), 10, 2 );
+ add_filter( 'pre_wp_unique_post_slug', array( $this, 'fix_unique_post_slug' ), 10, 6 );
add_filter( 'preview_post_link', array( $this, 'fix_preview_link_part_one' ) );
add_filter( 'post_link', array( $this, 'fix_preview_link_part_two' ), 10, 3 );
add_filter( 'page_link', array( $this, 'fix_preview_link_part_two' ), 10, 3 );
@@ -1367,41 +1368,54 @@ function fix_custom_status_timestamp( $data, $postarr ) {
}
/**
- * Another hack! hack! hack! until core better supports custom statuses`
+ * A new hack! hack! hack! until core better supports custom statuses`
*
- * @since 0.7.4
+ * @since 0.9.3
*
- * Keep the post_name value empty for posts with custom statuses
- * Unless they've set it customly
+ * If the slug should stay empty, mark it
* @see https://github.com/Automattic/Edit-Flow/issues/123
* @see http://core.trac.wordpress.org/browser/tags/3.4.2/wp-includes/post.php#L2530
* @see http://core.trac.wordpress.org/browser/tags/3.4.2/wp-includes/post.php#L2646
*/
- public function fix_post_name( $post_id, $post ) {
- global $pagenow;
+ public function maybe_keep_post_name_empty( $data, $postarr ) {
+ $status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' );
- /*
- * Filters the $post object that will be modified
- *
- * @param $post WP_Post Post object being processed.
- */
- $post = apply_filters( 'ef_fix_post_name_post', $post );
+ if ( ! in_array( $data['post_status'], $status_slugs )
+ || ! in_array( $data['post_type'], $this->get_post_types_for_module( $this->module ) ) ) {
+ $data['post_name'] = !isset( $postarr['post_name'] ) ? $data['post_name'] : $postarr['post_name'];
- // Only modify if we're using a pre-publish status on a supported custom post type
+ return $data;
+ }
+
+ if ( ! empty( $postarr['post_name'] ) ) {
+ $data['post_name'] = $postarr['post_name'];
+ return $data;
+ }
+
+ $data['post_name'] = '';
+
+ return $data;
+ }
+
+ public function fix_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type, $post_parent ) {
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' );
- if ( 'post.php' != $pagenow
- || ! in_array( $post->post_status, $status_slugs )
- || ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) )
- return;
- // The slug has been set by the meta box
- if ( ! empty( $_POST['post_name'] ) )
- return;
+ if ( ! in_array( $post_status, $status_slugs )
+ || ! in_array( $post_type, $this->get_post_types_for_module( $this->module ) ) ) {
+ return null;
+ }
- global $wpdb;
+ $post = get_post( $post_ID );
+
+ if ( empty( $post ) ) {
+ return null;
+ }
- $wpdb->update( $wpdb->posts, array( 'post_name' => '' ), array( 'ID' => $post_id ) );
- clean_post_cache( $post_id );
+ if ( $post->post_name ) {
+ return $slug;
+ }
+
+ return '';
}
@@ -1509,49 +1523,33 @@ public function fix_preview_link_part_three( $preview_link, $query_args ) {
* @return string $link Direct link to complete the action
*/
public function fix_get_sample_permalink( $permalink, $post_id, $title, $name, $post ) {
- //Should we be doing anything at all?
- if( !in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) )
- return $permalink;
- //Is this published?
- if( in_array( $post->post_status, $this->published_statuses ) )
- return $permalink;
+ $status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' );
- //Are we overriding the permalink? Don't do anything
- if( isset( $_POST['action'] ) && $_POST['action'] == 'sample-permalink' )
+ if ( !in_array( $post->post_status, $status_slugs )
+ || ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) {
return $permalink;
+ }
- list( $permalink, $post_name ) = $permalink;
-
- $post_name = $post->post_name ? $post->post_name : sanitize_title( $post->post_title );
- $post->post_name = $post_name;
-
- $ptype = get_post_type_object( $post->post_type );
-
- if ( $ptype->hierarchical ) {
- $post->filter = 'sample';
-
- $uri = get_page_uri( $post->ID ) . $post_name;
+ remove_filter( 'get_sample_permalink', array( $this, 'fix_get_sample_permalink' ), 10, 5 );
- if ( $uri ) {
- $uri = untrailingslashit($uri);
- $uri = strrev( stristr( strrev( $uri ), '/' ) );
- $uri = untrailingslashit($uri);
- }
+ $new_name = ! is_null( $name ) ? $name : $post->post_name;
+ $new_title = ! is_null( $title ) ? $title : $post->post_title;
- /** This filter is documented in wp-admin/edit-tag-form.php */
- $uri = apply_filters( 'editable_slug', $uri, $post );
-
- if ( !empty($uri) ) {
- $uri .= '/';
- }
+ /**
+ * Replicate what get_sample_permalink is already doing, but for Edit Flow customs statuses
+ *
+ * @see: https://github.com/WordPress/WordPress/blob/037a73675722c40557f9507717c9548fdd031d0d/wp-admin/includes/post.php#L1345
+ */
+ $post = get_post( $post_id );
+ $status_before = $post->post_status;
+ $post->post_status = 'draft';
- $permalink = str_replace('%pagename%', "{$uri}%pagename%", $permalink);
- }
+ $permalink = get_sample_permalink( $post, $title, sanitize_title( $new_name ? $new_name : $new_title, $post->ID ) );
- unset($post->post_name);
+ add_filter( 'get_sample_permalink', array( $this, 'fix_get_sample_permalink' ), 10, 5 );
- return array( $permalink, $post_name );
+ return $permalink;
}
/**
@@ -1566,75 +1564,33 @@ public function fix_get_sample_permalink( $permalink, $post_id, $title, $name, $
*
* @since 0.8.2
*
- * @param string $return Sample permalink HTML markup
- * @param int $post_id Post ID
- * @param string $new_title New sample permalink title
- * @param string $new_slug New sample permalink kslug
- * @param WP_Post $post Post object
+ * @param string $return Sample permalink HTML markup.
+ * @param int $post_id Post ID.
+ * @param string $new_title New sample permalink title.
+ * @param string $new_slug New sample permalink slug.
+ * @param WP_Post $post Post object.
*/
- function fix_get_sample_permalink_html( $return, $post_id, $new_title, $new_slug, $post ) {
+ function fix_get_sample_permalink_html( $permalink, $post_id, $new_title, $new_slug, $post ) {
$status_slugs = wp_list_pluck( $this->get_custom_statuses(), 'slug' );
- list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
-
- $view_link = false;
- $preview_target = '';
-
- if ( current_user_can( 'read_post', $post_id ) ) {
- if ( in_array( $post->post_status, $status_slugs ) ) {
- $view_link = $this->get_preview_link( $post );
- $preview_target = " target='wp-preview-{$post->ID}'";
- } else {
- if ( 'publish' === $post->post_status || 'attachment' === $post->post_type ) {
- $view_link = get_permalink( $post );
- } else {
- // Allow non-published (private, future) to be viewed at a pretty permalink.
- $view_link = str_replace( array( '%pagename%', '%postname%' ), $post->post_name, $permalink );
- }
- }
+ if ( !in_array( $post->post_status, $status_slugs )
+ || ! in_array( $post->post_type, $this->get_post_types_for_module( $this->module ) ) ) {
+ return $permalink;
}
- // Permalinks without a post/page name placeholder don't have anything to edit
- if ( false === strpos( $permalink, '%postname%' ) && false === strpos( $permalink, '%pagename%' ) ) {
- $return = '' . __( 'Permalink:' ) . "\n";
+ remove_filter( 'get_sample_permalink_html', array( $this, 'fix_get_sample_permalink_html' ), 10, 5);
- if ( false !== $view_link ) {
- $display_link = urldecode( $view_link );
- $return .= '' . $display_link . "\n";
- } else {
- $return .= '' . $permalink . "\n";
- }
-
- // Encourage a pretty permalink setting
- if ( '' == get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) && !( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') ) ) {
- $return .= '' . __('Change Permalinks') . "\n";
- }
- } else {
- if ( function_exists( 'mb_strlen' ) ) {
- if ( mb_strlen( $post_name ) > 34 ) {
- $post_name_abridged = mb_substr( $post_name, 0, 16 ) . '…' . mb_substr( $post_name, -16 );
- } else {
- $post_name_abridged = $post_name;
- }
- } else {
- if ( strlen( $post_name ) > 34 ) {
- $post_name_abridged = substr( $post_name, 0, 16 ) . '…' . substr( $post_name, -16 );
- } else {
- $post_name_abridged = $post_name;
- }
- }
-
- $post_name_html = '' . $post_name_abridged . '';
- $display_link = str_replace( array( '%pagename%', '%postname%' ), $post_name_html, urldecode( $permalink ) );
+ /**
+ * Replicate what get_sample_permalink is already doing, but for Edit Flow custom statuses
+ *
+ * @see: https://github.com/WordPress/WordPress/blob/037a73675722c40557f9507717c9548fdd031d0d/wp-admin/includes/post.php#L1345
+ */
+ $post->post_status = 'draft';
+ $sample_permalink_html = get_sample_permalink_html( $post, $new_title, $new_slug );
- $return = '' . __( 'Permalink:' ) . "\n";
- $return .= '' . $display_link . "\n";
- $return .= ''; // Fix bi-directional text display defect in RTL languages.
- $return .= '\n";
- $return .= '' . $post_name . "\n";
- }
+ add_filter( 'get_sample_permalink_html', array( $this, 'fix_get_sample_permalink_html' ), 10, 5);
- return $return;
+ return $sample_permalink_html;
}
diff --git a/tests/test-edit-flow-custom-status.php b/tests/test-edit-flow-custom-status.php
index 291ce2580..62038d28e 100644
--- a/tests/test-edit-flow-custom-status.php
+++ b/tests/test-edit-flow-custom-status.php
@@ -372,4 +372,414 @@ public function test_ensure_post_state_is_skipped_when_filtered() {
$post_states = apply_filters( 'display_post_states', array(), get_post( $post ) );
$this->assertFalse( array_key_exists( 'pitch', $post_states ) );
}
+
+ /**
+ * When a post with a custom status is inserted, post_name should remain empty
+ */
+ public function test_post_with_custom_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'pitch',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a custom status that replaces a core status is inserted, post_name should remain empty
+ */
+ public function test_post_with_custom_status_replacing_core_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'draft',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a "scheduled" status is inserted, post_name should be set
+ */
+ public function test_post_with_scheduled_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'future',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertNotEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a "publish" status is inserted, post_name should be set
+ */
+ public function test_post_with_publish_status_post_name_is_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'publish',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertNotEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a page with a custom status is inserted, post_name should remain empty
+ */
+ public function test_page_with_custom_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'page',
+ 'post_title' => 'Page',
+ 'post_status' => 'pitch',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a page with a custom status that replaces a core status is inserted, post_name should remain empty
+ */
+ public function test_page_with_custom_status_replacing_core_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'page',
+ 'post_title' => 'Page',
+ 'post_status' => 'draft',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a page with a "scheduled" status is inserted, post_name should be set
+ */
+ public function test_page_with_scheduled_status_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'page',
+ 'post_title' => 'Page',
+ 'post_status' => 'future',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertNotEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a "publish" status is inserted, post_name should be set
+ */
+ public function test_page_with_publish_status_post_name_is_set() {
+ $post = array (
+ 'post_type' => 'page',
+ 'post_title' => 'Page',
+ 'post_status' => 'publish',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertNotEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a custom status is updated, post_name should remain empty
+ */
+ public function test_post_with_custom_status_updated_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'pitch',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_insert_post( array_merge( $post, [ 'post_title' => 'New Post' ] ) );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a custom status replacing a core status is updated, post_name should remain empty
+ */
+ public function test_post_with_custom_status_replacing_core_status_updated_post_name_not_set() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'draft',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_insert_post( array_merge( $post, [ 'post_title' => 'New Post' ] ) );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEmpty( $post_inserted->post_name );
+ }
+
+ /**
+ * When a post with a "publish" status is updated, post_name should not change
+ */
+ public function test_post_with_publish_status_updated_post_name_does_not_change() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'publish',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_insert_post( array_merge( $post_inserted->to_array(), [ 'post_title' => 'New Post' ] ) );
+
+ $post_updated = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertEquals( $post_inserted->post_name, $post_updated->post_name );
+ }
+
+ /**
+ * When a post with a "publish" status is updated and post name is explicitly set, post_name should change
+ */
+ public function test_post_with_publish_status_updated_post_name_set_post_name_should_change() {
+ $post = array (
+ 'post_type' => 'post',
+ 'post_title' => 'Post',
+ 'post_status' => 'publish',
+ 'post_author' => self::$admin_user_id
+ );
+
+ $post_id = wp_insert_post( $post );
+
+ $post_inserted = get_post( $post_id );
+
+ wp_insert_post( array_merge( $post_inserted->to_array(), [ 'post_name' => 'a-new-slug' ] ) );
+
+ $post_updated = get_post( $post_id );
+
+ wp_delete_post( $post_id, true );
+
+ $this->assertNotEquals( $post_inserted->post_name, $post_updated->post_name );
+ }
+
+ /**
+ * When a request with the REST API is made to create a post with a custom status,
+ * the post name should not be set
+ */
+ public function test_post_with_custom_status_post_name_not_set_rest_api() {
+ wp_set_current_user( self::$admin_user_id );
+
+ $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
+ $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $params = array(
+ 'title' => 'Post title',
+ 'content' => 'Post content',
+ 'status' => 'pitch',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $request->set_body_params( $params );
+ $response = rest_get_server()->dispatch( $request );
+
+ $data = $response->get_data();
+ $post = get_post( $data['id'] );
+
+ $this->assertEmpty( $post->post_name );
+ }
+
+ /**
+ * When a request with the REST API is made to create a post with a custom status that replaces a core status,
+ * the post name should not be set
+ */
+ public function test_post_with_custom_status_replacing_core_status_post_name_not_set_rest_api() {
+ wp_set_current_user( self::$admin_user_id );
+
+ $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
+ $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $params = array(
+ 'title' => 'Post title',
+ 'content' => 'Post content',
+ 'status' => 'draft',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $request->set_body_params( $params );
+ $response = rest_get_server()->dispatch( $request );
+
+ $data = $response->get_data();
+ $post = get_post( $data['id'] );
+
+ $this->assertEmpty( $post->post_name );
+ }
+
+ /**
+ * When a request with the REST API is made to update a post with a custom status,
+ * the post name should not be set
+ */
+ public function test_post_with_custom_status_updated_post_name_not_set_rest_api() {
+ wp_set_current_user( self::$admin_user_id );
+
+ $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
+ $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $params = array(
+ 'title' => 'Post title',
+ 'content' => 'Post content',
+ 'status' => 'pitch',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $request->set_body_params( $params );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+
+ $update_request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $data['id'] ) );
+ $update_request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $update_params = array(
+ 'title' => 'Post title new',
+ 'content' => 'Post content new',
+ 'status' => 'pitch',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $update_request->set_body_params( $update_params );
+ $update_response = rest_get_server()->dispatch( $update_request );
+
+ $updated_data = $data = $update_response->get_data();
+ $updated_post = get_post( $updated_data['id'] );
+
+ $this->assertEmpty( $updated_post->post_name );
+ }
+
+ /**
+ * When a request with the REST API is made to create a post with a "publish" status,
+ * the post name should be set
+ */
+ public function test_post_with_publish_status_post_name_set_rest_api() {
+ wp_set_current_user( self::$admin_user_id );
+
+ $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
+ $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $params = array(
+ 'title' => 'Post title',
+ 'content' => 'Post content',
+ 'status' => 'publish',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $request->set_body_params( $params );
+ $response = rest_get_server()->dispatch( $request );
+
+ $data = $response->get_data();
+ $post = get_post( $data['id'] );
+
+ $this->assertNotEmpty( $post->post_name );
+ }
+
+ /**
+ * When a request with the REST API is made to create a post with a custom status, and the the post_name is set,
+ * if the post is updated the post_name should remain the same
+ */
+ public function test_post_with_custom_status_set_post_name_stays_set_rest_api() {
+ wp_set_current_user( self::$admin_user_id );
+
+ $custom_post_name = 'a-post-name';
+
+ $p = self::factory()->post->create( array(
+ 'post_status' => 'pitch',
+ 'post_author' => self::$admin_user_id
+ ) );
+
+ $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $p ) );
+ $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $params = array(
+ 'title' => 'Post title new',
+ 'content' => 'Post content new',
+ 'slug' => $custom_post_name,
+ 'status' => 'pitch',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $request->set_body_params( $params );
+ rest_get_server()->dispatch( $request );
+
+ $update_request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $p ) );
+ $update_request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
+ $update_params = array(
+ 'title' => 'Post title new',
+ 'content' => 'Post content new',
+ 'status' => 'pitch',
+ 'author' => self::$admin_user_id,
+ 'type' => 'post'
+ );
+ $update_request->set_body_params( $update_params );
+ $update_response = rest_get_server()->dispatch( $update_request );
+
+ $update_data = $update_response->get_data();
+ $update_post = get_post( $update_data['id'] );
+
+ $this->assertEquals( $custom_post_name, $update_post->post_name );
+ }
}