Skip to content

Commit

Permalink
Try: Improve permalink editing (#12009)
Browse files Browse the repository at this point in the history
Allows immediate permalink editing after initializing the editor.

* Filter REST API to return permalink_template

* Add new getEditedPostSlug selector

* Update auto-generated docs

* Make permalink editing more timely/accurate in title editor

* Make permalink editing more timely/accurate in sidebar panel

* Update e2e tests to reflect that panel should always show

* Add unit tests for getEditedPostSlug selector
  • Loading branch information
earnjam authored Mar 18, 2020
1 parent 548e600 commit 8ee1f0c
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 50 deletions.
14 changes: 14 additions & 0 deletions docs/designers-developers/developers/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,20 @@ _Returns_

- `?string`: Preview Link.

<a name="getEditedPostSlug" href="#getEditedPostSlug">#</a> **getEditedPostSlug**

Returns the slug for the post being edited, preferring a manually edited
value if one exists, then a sanitized version of the current post title, and
finally the post ID.

_Parameters_

- _state_ `Object`: Editor state.

_Returns_

- `string`: The current slug to be displayed in the editor

<a name="getEditedPostVisibility" href="#getEditedPostVisibility">#</a> **getEditedPostVisibility**

Returns the current visibility of the post being edited, preferring the
Expand Down
73 changes: 73 additions & 0 deletions lib/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,76 @@ function wp_api_nav_menus_taxonomy_args( $args, $taxonomy ) {
return $args;
}
add_filter( 'register_taxonomy_args', 'wp_api_nav_menus_taxonomy_args', 10, 2 );

/**
* Shim for get_sample_permalink() to add support for auto-draft status.
*
* This function filters the return from get_sample_permalink() and essentially
* re-runs the same logic minus the filters, but pretends a status of auto-save
* is actually publish in order to return the future permalink format.
*
* This is a temporary fix until we can patch get_sample_permalink()
*
* @see https://core.trac.wordpress.org/ticket/46266
*
* @param array $permalink Array containing the sample permalink with placeholder for the post name, and the post name.
* @param int $id ID of the post.
* @param string $title Title of the post.
* @param string $name Slug of the post.
* @param object $post WP_Post object.
*
* @return array Array containing the sample permalink with placeholder for the post name, and the post name.
*/
function gutenberg_auto_draft_get_sample_permalink( $permalink, $id, $title, $name, $post ) {
if ( 'auto-draft' !== $post->post_status ) {
return $permalink;
}
$ptype = get_post_type_object( $post->post_type );

$original_status = $post->post_status;
$original_date = $post->post_date;
$original_name = $post->post_name;

// Hack: get_permalink() would return ugly permalink for drafts, so we will fake that our post is published.
$post->post_status = 'publish';
$post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID );

// If the user wants to set a new name -- override the current one.
// Note: if empty name is supplied -- use the title instead, see #6072.
if ( ! is_null( $name ) ) {
$post->post_name = sanitize_title( $name ? $name : $title, $post->ID );
}

$post->post_name = wp_unique_post_slug( $post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent );

$post->filter = 'sample';

$permalink = get_permalink( $post, true );

// Replace custom post_type Token with generic pagename token for ease of use.
$permalink = str_replace( "%$post->post_type%", '%pagename%', $permalink );

// Handle page hierarchy.
if ( $ptype->hierarchical ) {
$uri = get_page_uri( $post );
if ( $uri ) {
$uri = untrailingslashit( $uri );
$uri = strrev( stristr( strrev( $uri ), '/' ) );
$uri = untrailingslashit( $uri );
}

if ( ! empty( $uri ) ) {
$uri .= '/';
}
$permalink = str_replace( '%pagename%', "{$uri}%pagename%", $permalink );
}

$permalink = array( $permalink, $post->post_name );
$post->post_status = $original_status;
$post->post_date = $original_date;
$post->post_name = $original_name;
unset( $post->filter );

return $permalink;
}
add_filter( 'get_sample_permalink', 'gutenberg_auto_draft_get_sample_permalink', 10, 5 );
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
createNewPost,
deactivatePlugin,
findSidebarPanelWithTitle,
openDocumentSettingsSidebar,
publishPost,
} from '@wordpress/e2e-test-utils';

Expand All @@ -23,21 +22,8 @@ describe( 'Sidebar Permalink Panel', () => {
await deactivatePlugin( 'gutenberg-test-custom-post-types' );
} );

it( 'should not render permalink sidebar panel while the post is new', async () => {
it( 'should allow permalink sidebar panel to be removed', async () => {
await createNewPost();
await openDocumentSettingsSidebar();
expect(
await findSidebarPanelWithTitle( 'Permalink' )
).toBeUndefined();
} );

it( 'should render permalink sidebar panel after the post is published and allow its removal', async () => {
await createNewPost();
await page.keyboard.type( 'aaaaa' );
await publishPost();
// Start editing again.
await page.type( '.editor-post-title__input', ' (Updated)' );
expect( await findSidebarPanelWithTitle( 'Permalink' ) ).toBeDefined();
await page.evaluate( () => {
const { removeEditorPanel } = wp.data.dispatch( 'core/edit-post' );
removeEditorPanel( 'post-link' );
Expand Down
31 changes: 9 additions & 22 deletions packages/edit-post/src/components/sidebar/post-link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,18 @@ function PostLink( {
editPermalink,
forceEmptyField,
setState,
postTitle,
postSlug,
postID,
postTypeLabel,
} ) {
const { prefix, suffix } = permalinkParts;
let prefixElement, postNameElement, suffixElement;
const currentSlug =
safeDecodeURIComponent( postSlug ) ||
cleanForSlug( postTitle ) ||
postID;
if ( isEditable ) {
prefixElement = prefix && (
<span className="edit-post-post-link__link-prefix">{ prefix }</span>
);
postNameElement = currentSlug && (
postNameElement = postSlug && (
<span className="edit-post-post-link__link-post-name">
{ currentSlug }
{ postSlug }
</span>
);
suffixElement = suffix && (
Expand All @@ -62,7 +56,7 @@ function PostLink( {
<div className="editor-post-link">
<TextControl
label={ __( 'URL Slug' ) }
value={ forceEmptyField ? '' : currentSlug }
value={ forceEmptyField ? '' : postSlug }
onChange={ ( newValue ) => {
editPermalink( newValue );
// When we delete the field the permalink gets
Expand Down Expand Up @@ -127,45 +121,38 @@ function PostLink( {
export default compose( [
withSelect( ( select ) => {
const {
isEditedPostNew,
isPermalinkEditable,
getCurrentPost,
isCurrentPostPublished,
getPermalinkParts,
getEditedPostAttribute,
getEditedPostSlug,
} = select( 'core/editor' );
const { isEditorPanelEnabled, isEditorPanelOpened } = select(
'core/edit-post'
);
const { getPostType } = select( 'core' );

const { link, id } = getCurrentPost();
const { link } = getCurrentPost();

const postTypeName = getEditedPostAttribute( 'type' );
const postType = getPostType( postTypeName );

return {
isNew: isEditedPostNew(),
postLink: link,
isEditable: isPermalinkEditable(),
isPublished: isCurrentPostPublished(),
isOpened: isEditorPanelOpened( PANEL_NAME ),
permalinkParts: getPermalinkParts(),
isEnabled: isEditorPanelEnabled( PANEL_NAME ),
isViewable: get( postType, [ 'viewable' ], false ),
postTitle: getEditedPostAttribute( 'title' ),
postSlug: getEditedPostAttribute( 'slug' ),
postID: id,
postSlug: safeDecodeURIComponent( getEditedPostSlug() ),
postTypeLabel: get( postType, [ 'labels', 'view_item' ] ),
};
} ),
ifCondition(
( { isEnabled, isNew, postLink, isViewable, permalinkParts } ) => {
return (
isEnabled && ! isNew && postLink && isViewable && permalinkParts
);
}
),
ifCondition( ( { isEnabled, postLink, isViewable, permalinkParts } ) => {
return isEnabled && postLink && isViewable && permalinkParts;
} ),
withDispatch( ( dispatch ) => {
const { toggleEditorPanelOpened } = dispatch( 'core/edit-post' );
const { editPost } = dispatch( 'core/editor' );
Expand Down
20 changes: 7 additions & 13 deletions packages/editor/src/components/post-permalink/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { link as linkIcon } from '@wordpress/icons';
* Internal dependencies
*/
import PostPermalinkEditor from './editor.js';
import { cleanForSlug } from '../../utils/url';

class PostPermalink extends Component {
constructor() {
Expand Down Expand Up @@ -70,8 +69,6 @@ class PostPermalink extends Component {
permalinkParts,
postLink,
postSlug,
postID,
postTitle,
} = this.props;

if ( isNew || ! isViewable || ! permalinkParts || ! postLink ) {
Expand All @@ -84,11 +81,9 @@ class PostPermalink extends Component {
: __( 'Copy the permalink' );

const { prefix, suffix } = permalinkParts;
const slug =
safeDecodeURIComponent( postSlug ) ||
cleanForSlug( postTitle ) ||
postID;
const samplePermalink = isEditable ? prefix + slug + suffix : prefix;
const samplePermalink = isEditable
? prefix + postSlug + suffix
: prefix;

return (
<div className="editor-post-permalink">
Expand Down Expand Up @@ -123,7 +118,7 @@ class PostPermalink extends Component {

{ isEditingPermalink && (
<PostPermalinkEditor
slug={ slug }
slug={ postSlug }
onSave={ () =>
this.setState( { isEditingPermalink: false } )
}
Expand Down Expand Up @@ -155,10 +150,11 @@ export default compose( [
getPermalinkParts,
getEditedPostAttribute,
isCurrentPostPublished,
getEditedPostSlug,
} = select( 'core/editor' );
const { getPostType } = select( 'core' );

const { id, link } = getCurrentPost();
const { link } = getCurrentPost();

const postTypeName = getEditedPostAttribute( 'type' );
const postType = getPostType( postTypeName );
Expand All @@ -167,11 +163,9 @@ export default compose( [
isNew: isEditedPostNew(),
postLink: link,
permalinkParts: getPermalinkParts(),
postSlug: getEditedPostAttribute( 'slug' ),
postSlug: safeDecodeURIComponent( getEditedPostSlug() ),
isEditable: isPermalinkEditable(),
isPublished: isCurrentPostPublished(),
postTitle: getEditedPostAttribute( 'title' ),
postID: id,
isViewable: get( postType, [ 'viewable' ], false ),
};
} ),
Expand Down
18 changes: 18 additions & 0 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from './constants';
import { getPostRawValue } from './reducer';
import serializeBlocks from './utils/serialize-blocks';
import { cleanForSlug } from '../utils/url';

/**
* Shared reference to an empty object for cases where it is important to avoid
Expand Down Expand Up @@ -1149,6 +1150,23 @@ export function getPermalink( state ) {
return prefix;
}

/**
* Returns the slug for the post being edited, preferring a manually edited
* value if one exists, then a sanitized version of the current post title, and
* finally the post ID.
*
* @param {Object} state Editor state.
*
* @return {string} The current slug to be displayed in the editor
*/
export function getEditedPostSlug( state ) {
return (
getEditedPostAttribute( state, 'slug' ) ||
cleanForSlug( getEditedPostAttribute( state, 'title' ) ) ||
getCurrentPostId( state )
);
}

/**
* Returns the permalink for a post, split into it's three parts: the prefix,
* the postName, and the suffix.
Expand Down
Loading

0 comments on commit 8ee1f0c

Please sign in to comment.