Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch post-author component to use /wp/v2/users/?who=authors #6515

Merged
merged 7 commits into from
May 2, 2018
Merged
16 changes: 16 additions & 0 deletions core-data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ export function receiveTerms( taxonomy, terms ) {
};
}

/**
* Returns an action object used in signalling that authors have been received.
*
* @param {string} queryID Query ID.
* @param {Array|Object} users Users received.
*
* @return {Object} Action object.
*/
export function receiveUserQuery( queryID, users ) {
return {
type: 'RECEIVE_USER_QUERY',
users: castArray( users ),
queryID,
};
}

/**
* Returns an action object used in signalling that media have been received.
*
Expand Down
29 changes: 28 additions & 1 deletion core-data/reducer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { keyBy } from 'lodash';
import { keyBy, map } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -42,6 +42,32 @@ export function terms( state = {}, action ) {
return state;
}

/**
* Reducer managing authors state. Keyed by id.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function users( state = { byId: {}, queries: {} }, action ) {
switch ( action.type ) {
case 'RECEIVE_USER_QUERY':
return {
byId: {
...state.byId,
...keyBy( action.users, 'id' ),
},
queries: {
...state.queries,
[ action.queryID ]: map( action.users, ( user ) => user.id ),
},
};
}

return state;
}

/**
* Reducer managing media state. Keyed by id.
*
Expand Down Expand Up @@ -104,6 +130,7 @@ export function themeSupports( state = {}, action ) {

export default combineReducers( {
terms,
users,
media,
postTypes,
themeSupports,
Expand Down
9 changes: 9 additions & 0 deletions core-data/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import apiRequest from '@wordpress/api-request';
import {
setRequested,
receiveTerms,
receiveUserQuery,
receiveMedia,
receivePostTypes,
receiveThemeSupportsFromIndex,
Expand All @@ -24,6 +25,14 @@ export async function* getCategories() {
yield receiveTerms( 'categories', categories );
}

/**
* Requests authors from the REST API.
*/
export async function* getAuthors() {
const users = await apiRequest( { path: '/wp/v2/users/?who=authors' } );
yield receiveUserQuery( 'authors', users );
}

/**
* Requests a media element from the REST API.
*
Expand Down
30 changes: 30 additions & 0 deletions core-data/selectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { map } from 'lodash';

/**
* Returns all the available terms for the given taxonomy.
*
Expand Down Expand Up @@ -58,6 +63,31 @@ export function getMedia( state, id ) {
return state.media[ id ];
}

/**
* Returns all available authors.
*
* @param {Object} state Data state.
*
* @return {Array} Authors list.
*/
export function getAuthors( state ) {
return getUserQueryResults( state, 'authors' );
}

/**
* Returns all the users returned by a query ID.
*
* @param {Object} state Data state.
* @param {string} queryID Query ID.
*
* @return {Array} Users list.
*/
export function getUserQueryResults( state, queryID ) {
const queryResults = state.users.queries[ queryID ];

return map( queryResults, ( id ) => state.users.byId[ id ] );
}

/**
* Returns the Post Type object by slug.
*
Expand Down
7 changes: 3 additions & 4 deletions editor/components/post-author/check.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { filter, get } from 'lodash';
import { get } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -15,8 +15,7 @@ import { withSelect } from '@wordpress/data';
*/
import PostTypeSupportCheck from '../post-type-support-check';

export function PostAuthorCheck( { user, users, children } ) {
const authors = filter( users.data, ( { capabilities } ) => get( capabilities, [ 'level_1' ], false ) );
export function PostAuthorCheck( { user, authors, children } ) {
const userCanPublishPosts = get( user.data, [ 'post_type_capabilities', 'publish_posts' ], false );

if ( ! userCanPublishPosts || authors.length < 2 ) {
Expand All @@ -30,13 +29,13 @@ export default compose( [
withSelect( ( select ) => {
return {
postType: select( 'core/editor' ).getCurrentPostType(),
authors: select( 'core' ).getAuthors(),
};
} ),
withAPIData( ( props ) => {
const { postType } = props;

return {
users: '/wp/v2/users?context=edit&per_page=100',
user: `/wp/v2/users/me?post_type=${ postType }&context=edit`,
};
} ),
Expand Down
28 changes: 3 additions & 25 deletions editor/components/post-author/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
/**
* External dependencies
*/
import { get, filter } from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { withAPIData, withInstanceId } from '@wordpress/components';
import { withInstanceId } from '@wordpress/components';
import { Component, compose } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';

Expand All @@ -29,21 +24,8 @@ export class PostAuthor extends Component {
onUpdateAuthor( Number( value ) );
}

getAuthors() {
// While User Levels are officially deprecated, the behavior of the
// existing users dropdown on `who=authors` tests `user_level != 0`
//
// See: https://github.com/WordPress/WordPress/blob/a193916/wp-includes/class-wp-user-query.php#L322-L327
// See: https://codex.wordpress.org/Roles_and_Capabilities#User_Levels
const { users } = this.props;
return filter( users.data, ( user ) => {
return get( user, [ 'capabilities', 'level_1' ], false );
} );
}

render() {
const { postAuthor, instanceId } = this.props;
const authors = this.getAuthors();
const { postAuthor, instanceId, authors } = this.props;
const selectId = 'post-author-selector-' + instanceId;

// Disable reason: A select with an onchange throws a warning
Expand Down Expand Up @@ -72,17 +54,13 @@ export default compose( [
withSelect( ( select ) => {
return {
postAuthor: select( 'core/editor' ).getEditedPostAttribute( 'author' ),
authors: select( 'core' ).getAuthors(),
};
} ),
withDispatch( ( dispatch ) => ( {
onUpdateAuthor( author ) {
dispatch( 'core/editor' ).editPost( { author } );
},
} ) ),
withAPIData( () => {
return {
users: '/wp/v2/users?context=edit&per_page=100',
};
} ),
withInstanceId,
] )( PostAuthor );
28 changes: 3 additions & 25 deletions editor/components/post-author/test/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,36 +43,14 @@ describe( 'PostAuthorCheck', () => {
},
};

it( 'should not render anything if the user doesn\'t have the right capabilities', () => {
let wrapper = shallow( <PostAuthorCheck users={ users } user={ {} }>authors</PostAuthorCheck> );
expect( wrapper.type() ).toBe( null );
wrapper = shallow(
<PostAuthorCheck users={ users } user={
{ data: { post_type_capabilities: { publish_posts: false } } }
}>
authors
</PostAuthorCheck>
);
expect( wrapper.type() ).toBe( null );
} );

it( 'should not render anything if users unknown', () => {
const wrapper = shallow( <PostAuthorCheck users={ {} } user={ user }>authors</PostAuthorCheck> );
const wrapper = shallow( <PostAuthorCheck authors={ [] } user={ user }>authors</PostAuthorCheck> );
expect( wrapper.type() ).toBe( null );
} );

it( 'should not render anything if single user', () => {
const wrapper = shallow(
<PostAuthorCheck users={ { data: users.data.slice( 0, 1 ) } } user={ user }>
authors
</PostAuthorCheck>
);
expect( wrapper.type() ).toBe( null );
} );

it( 'should not render anything if single filtered user', () => {
const wrapper = shallow(
<PostAuthorCheck users={ { data: users.data.slice( 0, 2 ) } } user={ user }>
<PostAuthorCheck authors={ users.data.slice( 0, 1 ) } user={ user }>
authors
</PostAuthorCheck>
);
Expand All @@ -81,7 +59,7 @@ describe( 'PostAuthorCheck', () => {

it( 'should render control', () => {
const wrapper = shallow(
<PostAuthorCheck users={ users } user={ user }>
<PostAuthorCheck authors={ users } user={ user }>
authors
</PostAuthorCheck>
);
Expand Down
62 changes: 21 additions & 41 deletions editor/components/post-author/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,29 @@ import { shallow } from 'enzyme';
import { PostAuthor } from '../';

describe( 'PostAuthor', () => {
const users = {
data: [
{
id: 1,
name: 'admin',
capabilities: {
level_1: true,
},
const authors = [
{
id: 1,
name: 'admin',
capabilities: {
level_1: true,
},
{
id: 2,
name: 'subscriber',
capabilities: {
level_0: true,
},
},
{
id: 2,
name: 'subscriber',
capabilities: {
level_0: true,
},
{
id: 3,
name: 'andrew',
capabilities: {
level_1: true,
},
},
{
id: 3,
name: 'andrew',
capabilities: {
level_1: true,
},
],
};
},
];

const user = {
data: {
Expand All @@ -43,30 +41,12 @@ describe( 'PostAuthor', () => {
},
};

describe( '#getAuthors()', () => {
it( 'returns empty array on unknown users', () => {
const wrapper = shallow( <PostAuthor users={ {} } user={ user } /> );

const authors = wrapper.instance().getAuthors();

expect( authors ).toEqual( [] );
} );

it( 'filters users to authors', () => {
const wrapper = shallow( <PostAuthor users={ users } user={ user } /> );

const authors = wrapper.instance().getAuthors();

expect( authors.map( ( author ) => author.id ).sort() ).toEqual( [ 1, 3 ] );
} );
} );

describe( '#render()', () => {
it( 'should update author', () => {
const onUpdateAuthor = jest.fn();
const wrapper = shallow(
<PostAuthor
users={ users }
authors={ authors }
user={ user }
onUpdateAuthor={ onUpdateAuthor } />
);
Expand Down
Loading