-
Notifications
You must be signed in to change notification settings - Fork 199
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3849 from Automattic/add/enrolled-content-block
Add restricted content blocks
- Loading branch information
Showing
17 changed files
with
415 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "sensei-lms/restricted-content", | ||
"category": "sensei-lms", | ||
"supports": { | ||
"html": false, | ||
"align": [ "wide", "full" ] | ||
}, | ||
"attributes": { | ||
"restrictionType": { | ||
"type": "string", | ||
"default": "enrolled" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { InnerBlocks } from '@wordpress/block-editor'; | ||
import { compose } from '@wordpress/compose'; | ||
import { withSelect } from '@wordpress/data'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { RestrictedContentSettings } from './settings'; | ||
import classnames from 'classnames'; | ||
|
||
export const RestrictOptions = { | ||
ENROLLED: 'enrolled', | ||
UNENROLLED: 'unenrolled', | ||
COURSE_COMPLETED: 'course-completed', | ||
}; | ||
|
||
export const RestrictOptionLabels = { | ||
[ RestrictOptions.ENROLLED ]: __( 'Enrolled Users', 'sensei-lms' ), | ||
[ RestrictOptions.UNENROLLED ]: __( 'Unenrolled Users', 'sensei-lms' ), | ||
[ RestrictOptions.COURSE_COMPLETED ]: __( | ||
'Course Completed', | ||
'sensei-lms' | ||
), | ||
}; | ||
|
||
const EditRestrictedContent = ( { | ||
className, | ||
hasInnerBlocks, | ||
clientId, | ||
attributes: { restrictionType }, | ||
setAttributes, | ||
} ) => { | ||
return ( | ||
<> | ||
<div className={ classnames( 'wp-block-group', className ) }> | ||
<div className="wp-block-group__inner-container"> | ||
<InnerBlocks | ||
renderAppender={ | ||
! hasInnerBlocks && InnerBlocks.ButtonBlockAppender | ||
} | ||
/> | ||
</div> | ||
</div> | ||
<RestrictedContentSettings | ||
selectedRestriction={ restrictionType } | ||
onRestrictionChange={ ( option ) => | ||
setAttributes( { | ||
restrictionType: option, | ||
} ) | ||
} | ||
clientId={ clientId } | ||
hasInnerBlocks={ hasInnerBlocks } | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default compose( [ | ||
withSelect( ( select, { clientId } ) => { | ||
const { getBlock } = select( 'core/block-editor' ); | ||
|
||
const block = getBlock( clientId ); | ||
|
||
return { | ||
hasInnerBlocks: !! ( block && block.innerBlocks.length ), | ||
}; | ||
} ), | ||
] )( EditRestrictedContent ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Icon } from '@wordpress/components'; | ||
import edit from './edit'; | ||
import save from './save'; | ||
import metadata from './block'; | ||
import { createBlock } from '@wordpress/blocks'; | ||
|
||
export default { | ||
title: __( 'Restricted Course Content', 'sensei-lms' ), | ||
description: __( | ||
'Content inside this container block can be restricted to a specified group of users. Possible options are enrolled users, not enrolled users and users who have completed the course', | ||
'sensei-lms' | ||
), | ||
keywords: [ | ||
__( 'Enrolled', 'sensei-lms' ), | ||
__( 'Content', 'sensei-lms' ), | ||
__( 'Locked', 'sensei-lms' ), | ||
__( 'Private', 'sensei-lms' ), | ||
__( 'Completed', 'sensei-lms' ), | ||
__( 'Unenrolled', 'sensei-lms' ), | ||
__( 'Restricted', 'sensei-lms' ), | ||
], | ||
icon: () => <Icon icon="lock" />, | ||
edit, | ||
save, | ||
...metadata, | ||
transforms: { | ||
from: [ | ||
{ | ||
type: 'block', | ||
isMultiBlock: true, | ||
blocks: [ '*' ], | ||
__experimentalConvert: ( blocks ) => { | ||
if ( | ||
blocks.length === 1 && | ||
blocks[ 0 ].name === 'sensei-lms/restricted-content' | ||
) { | ||
return; | ||
} | ||
|
||
// The conversion is done by creating a wrapper block and setting the selected blocks as inner blocks. | ||
const wrapperInnerBlocks = blocks.map( ( block ) => { | ||
return createBlock( | ||
block.name, | ||
block.attributes, | ||
block.innerBlocks | ||
); | ||
} ); | ||
|
||
const alignments = [ 'wide', 'full' ]; | ||
|
||
// Determine the widest setting of all the blocks to be grouped. | ||
const widestAlignment = blocks.reduce( | ||
( result, block ) => { | ||
const { align } = block.attributes; | ||
return alignments.indexOf( align ) > | ||
alignments.indexOf( result ) | ||
? align | ||
: result; | ||
}, | ||
undefined | ||
); | ||
|
||
return createBlock( | ||
'sensei-lms/restricted-content', | ||
{ | ||
align: widestAlignment, | ||
}, | ||
wrapperInnerBlocks | ||
); | ||
}, | ||
}, | ||
], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { InnerBlocks } from '@wordpress/block-editor'; | ||
import classnames from 'classnames'; | ||
|
||
export default function SaveRestrictedContent( { className } ) { | ||
return ( | ||
<div className={ classnames( 'wp-block-group', className ) }> | ||
<div className="wp-block-group__inner-container"> | ||
<InnerBlocks.Content /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { | ||
BlockControls, | ||
BlockSettingsMenuControls, | ||
} from '@wordpress/block-editor'; | ||
import { ToolbarGroup, MenuItem } from '@wordpress/components'; | ||
import { RestrictOptions, RestrictOptionLabels } from './edit'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import ToolbarDropdown from '../editor-components/toolbar-dropdown'; | ||
|
||
/** | ||
* Check if the current block is the only one that is selected. | ||
* | ||
* @param {string} clientId The block client id. | ||
*/ | ||
const useIsSingleRestrictSelected = ( clientId ) => { | ||
return useSelect( | ||
( select ) => { | ||
const selectedClientIds = select( | ||
'core/block-editor' | ||
).getSelectedBlockClientIds(); | ||
|
||
return ( | ||
selectedClientIds.length === 1 && | ||
selectedClientIds[ 0 ] === clientId | ||
); | ||
}, | ||
[ clientId ] | ||
); | ||
}; | ||
|
||
/** | ||
* A hook that returns a function which unwraps the inner blocks from the restricted content block. | ||
* | ||
* @param {string} clientId The block client id. | ||
*/ | ||
const useOnRestrictionRemoval = ( clientId ) => { | ||
const block = useSelect( | ||
( select ) => select( 'core/block-editor' ).getBlock( clientId ), | ||
[ clientId ] | ||
); | ||
const { replaceBlocks } = useDispatch( 'core/block-editor' ); | ||
|
||
return () => { | ||
if ( block.innerBlocks.length ) { | ||
replaceBlocks( clientId, block.innerBlocks ); | ||
} | ||
}; | ||
}; | ||
|
||
/** | ||
* The restricted content block settings. | ||
* | ||
* @param {Object} props Component properties. | ||
* @param {string} props.selectedRestriction The restriction that is currently selected. | ||
* @param {Function} props.onRestrictionChange Callback which is called when a new option is selected. | ||
* @param {string} props.clientId The block client id. | ||
* @param {boolean} props.hasInnerBlocks True if there are inner blocks. | ||
*/ | ||
export function RestrictedContentSettings( { | ||
selectedRestriction, | ||
onRestrictionChange, | ||
clientId, | ||
hasInnerBlocks, | ||
} ) { | ||
const isSingleRestrictSelected = useIsSingleRestrictSelected( clientId ); | ||
const onRestrictRemoval = useOnRestrictionRemoval( clientId ); | ||
|
||
const toolbarOptions = Object.keys( RestrictOptions ).map( | ||
( optionKey ) => ( { | ||
value: RestrictOptions[ optionKey ], | ||
label: RestrictOptionLabels[ RestrictOptions[ optionKey ] ], | ||
} ) | ||
); | ||
|
||
return ( | ||
<> | ||
<BlockControls> | ||
<ToolbarGroup> | ||
<ToolbarDropdown | ||
options={ toolbarOptions } | ||
optionsLabel={ __( 'Visibility', 'sensei-lms' ) } | ||
value={ selectedRestriction } | ||
onChange={ onRestrictionChange } | ||
/> | ||
</ToolbarGroup> | ||
</BlockControls> | ||
{ isSingleRestrictSelected && | ||
hasInnerBlocks && | ||
BlockSettingsMenuControls && ( | ||
<BlockSettingsMenuControls> | ||
{ ( { onClose } ) => ( | ||
<MenuItem | ||
onClick={ () => { | ||
onRestrictRemoval(); | ||
onClose(); | ||
} } | ||
> | ||
{ __( 'Remove restriction', 'sensei-lms' ) } | ||
</MenuItem> | ||
) } | ||
</BlockSettingsMenuControls> | ||
) } | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.