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

Data Module: Refactor the Post Type Capabilities fetching to use the data module #5875

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions core-data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,19 @@ export function receivePostTypes( postTypes ) {
postTypes: castArray( postTypes ),
};
}

/**
* Returns an action object used in signalling that the user capabilties fora post type have been received.
*
* @param {string} postTypeSlug Post Type
* @param {Object} capabilities Capabilities received.
*
* @return {Object} Action object.
*/
export function receiveUserPostTypeCapabilities( postTypeSlug, capabilities ) {
return {
type: 'RECEIVE_USER_POST_TYPE_CAPABILITIES',
postTypeSlug,
capabilities,
};
}
21 changes: 21 additions & 0 deletions core-data/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,29 @@ export function postTypes( state = {}, action ) {
return state;
}

/**
* Reducer managing user post types capabilties state. Keyed by post type.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function userPostTypeCapabilities( state = {}, action ) {
switch ( action.type ) {
case 'RECEIVE_USER_POST_TYPE_CAPABILITIES':
return {
...state,
[ action.postTypeSlug ]: action.capabilities,
};
}

return state;
}

export default combineReducers( {
terms,
media,
postTypes,
userPostTypeCapabilities,
} );
15 changes: 14 additions & 1 deletion core-data/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
receiveTerms,
receiveMedia,
receivePostTypes,
receiveUserPostTypeCapabilities,
} from './actions';

/**
Expand Down Expand Up @@ -38,9 +39,21 @@ export async function* getMedia( state, id ) {
* Requests a post type element from the REST API.
*
* @param {Object} state State tree
* @param {number} slug Post Type slug
* @param {string} slug Post Type slug
*/
export async function* getPostType( state, slug ) {
const postType = await apiRequest( { path: `/wp/v2/types/${ slug }?context=edit` } );
yield receivePostTypes( postType );
}

/**
* Request user capabilities for a given post type from the REST API.
* @param {Object} state State tree
* @param {string} postTypeSlug Post Type slug
*/
export async function* getUserPostTypeCapabilities( state, postTypeSlug ) {
const userWithCapabilities = await apiRequest( { path: `/wp/v2/users/me?post_type=${ postTypeSlug }&context=edit` } );
yield receiveUserPostTypeCapabilities( postTypeSlug, userWithCapabilities.post_type_capabilities );
}

export const getUserPostTypeCapability = getUserPostTypeCapabilities;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now this is hacky as it creates several requests if we pass different capabilities. A solution for these is being discussed in #5826

19 changes: 18 additions & 1 deletion core-data/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,27 @@ export function getMedia( state, id ) {
* Returns the Post Type object by slug.
*
* @param {Object} state Data state.
* @param {number} slug Post Type slug.
* @param {string} slug Post Type slug.
*
* @return {Object?} Post Type object.
*/
export function getPostType( state, slug ) {
return state.postTypes[ slug ];
}

/**
* Returns whether a user has a capability per post type.
*
* @param {Object} state Data state.
* @param {string} postTypeSlug Post Type slug.
* @param {string} capability Capability.
*
* @return {boolean} Whether the user has the give cabability.
*/
export function getUserPostTypeCapability( state, postTypeSlug, capability ) {
const capabilities = state.userPostTypeCapabilities[ postTypeSlug ];

// If the capabilities are not loaded return undefined
// If the capabilities are loaded but the capability not set return false
return capabilities ? capabilities[ capability ] || false : undefined;
}
30 changes: 29 additions & 1 deletion core-data/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze';
/**
* Internal dependencies
*/
import { terms, media, postTypes } from '../reducer';
import { terms, media, postTypes, userPostTypeCapabilities } from '../reducer';

describe( 'terms()', () => {
it( 'returns an empty object by default', () => {
Expand Down Expand Up @@ -109,3 +109,31 @@ describe( 'postTypes', () => {
} );
} );
} );

describe( 'userPostTypeCapabilities', () => {
it( 'returns an empty object by default', () => {
const state = userPostTypeCapabilities( undefined, {} );

expect( state ).toEqual( {} );
} );

it( 'returns with received capabilities by slug', () => {
const originalState = deepFreeze( {
page: {
editPost: false,
},
} );
const state = userPostTypeCapabilities( originalState, {
type: 'RECEIVE_USER_POST_TYPE_CAPABILITIES',
postTypeSlug: 'post',
capabilities: {
publishPost: true,
},
} );

expect( state ).toEqual( {
page: { editPost: false },
post: { publishPost: true },
} );
} );
} );
34 changes: 32 additions & 2 deletions core-data/test/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@ import apiRequest from '@wordpress/api-request';
/**
* Internal dependencies
*/
import { getCategories, getMedia, getPostType } from '../resolvers';
import { setRequested, receiveTerms, receiveMedia, receivePostTypes } from '../actions';
import {
getCategories,
getMedia,
getPostType,
getUserPostTypeCapabilities,
} from '../resolvers';
import {
setRequested,
receiveTerms,
receiveMedia,
receivePostTypes,
receiveUserPostTypeCapabilities,
} from '../actions';

jest.mock( '@wordpress/api-request' );

Expand Down Expand Up @@ -66,3 +77,22 @@ describe( 'getPostType', () => {
expect( received ).toEqual( receivePostTypes( POST_TYPE ) );
} );
} );

describe( 'getUserPostTypeCapabilities', () => {
const USER = { post_type_capabilities: { publishPost: true } };

beforeAll( () => {
apiRequest.mockImplementation( ( options ) => {
if ( options.path === '/wp/v2/users/me?post_type=post&context=edit' ) {
return Promise.resolve( USER );
}
} );
} );

it( 'yields with received capabilties', async () => {
const fulfillment = getUserPostTypeCapabilities( {}, 'post' );
const received = ( await fulfillment.next() ).value;
expect( received ).toEqual( receiveUserPostTypeCapabilities( 'post', { publishPost: true } ) );
} );
} );

39 changes: 38 additions & 1 deletion core-data/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import deepFreeze from 'deep-freeze';
/**
* Internal dependencies
*/
import { getTerms, isRequestingTerms, getMedia, getPostType } from '../selectors';
import {
getTerms,
isRequestingTerms,
getMedia,
getPostType,
getUserPostTypeCapability,
} from '../selectors';

describe( 'getTerms()', () => {
it( 'returns value of terms by taxonomy', () => {
Expand Down Expand Up @@ -92,3 +98,34 @@ describe( 'getPostType', () => {
expect( getPostType( state, 'post' ) ).toEqual( { slug: 'post' } );
} );
} );

describe( 'getUserPostTypeCapability', () => {
it( 'should return undefined for unknown post type', () => {
const state = deepFreeze( {
userPostTypeCapabilities: {},
} );
expect( getUserPostTypeCapability( state, 'post', 'publishPost' ) ).toBe( undefined );
} );

it( 'should return false for unknown capability', () => {
const state = deepFreeze( {
userPostTypeCapabilities: {
post: {
hasPost: false,
},
},
} );
expect( getUserPostTypeCapability( state, 'post', 'publishPost' ) ).toBe( false );
} );

it( 'should return the capability value', () => {
const state = deepFreeze( {
userPostTypeCapabilities: {
post: {
publishPost: true,
},
},
} );
expect( getUserPostTypeCapability( state, 'post', 'publishPost' ) ).toBe( true );
} );
} );
29 changes: 12 additions & 17 deletions editor/components/post-author/check.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';
import { filter, get } from 'lodash';

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

/**
* Internal dependencies
*/
import PostTypeSupportCheck from '../post-type-support-check';
import { getCurrentPostType } from '../../store/selectors';

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

if ( ! userCanPublishPosts || authors.length < 2 ) {
if ( ! canPublishPosts || authors.length < 2 ) {
return null;
}

return <PostTypeSupportCheck supportKeys="author">{ children }</PostTypeSupportCheck>;
}

const applyConnect = connect(
( state ) => {
const applyWithSelect = withSelect(
( select ) => {
const { getEditedPostAttribute } = select( 'core/editor' );
const { getUserPostTypeCapability } = select( 'core' );
return {
postType: getCurrentPostType( state ),
canPublishPosts: getUserPostTypeCapability( getEditedPostAttribute( 'type' ), 'publish_posts' ),
};
},
);

const applyWithAPIData = 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`,
};
} );
const applyWithAPIData = withAPIData( () => ( {
users: '/wp/v2/users?context=edit&per_page=100',
} ) );

export default compose( [
applyConnect,
applyWithSelect,
applyWithAPIData,
withInstanceId,
] )( PostAuthorCheck );
20 changes: 5 additions & 15 deletions editor/components/post-author/test/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,25 @@ describe( 'PostAuthorCheck', () => {
],
};

const user = {
data: {
post_type_capabilities: {
publish_posts: true,
},
},
};

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 } } }
}>
<PostAuthorCheck users={ users } canPublishPosts={ 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 users={ {} } canPublishPosts>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 }>
<PostAuthorCheck users={ { data: users.data.slice( 0, 1 ) } } canPublishPosts>
authors
</PostAuthorCheck>
);
Expand All @@ -72,7 +62,7 @@ describe( 'PostAuthorCheck', () => {

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

it( 'should render control', () => {
const wrapper = shallow(
<PostAuthorCheck users={ users } user={ user }>
<PostAuthorCheck users={ users } canPublishPosts>
authors
</PostAuthorCheck>
);
Expand Down
Loading