From 6db207fd34a0b80c9b3f93f1d4daccd3793eff38 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Fri, 11 Dec 2020 13:43:52 +0200 Subject: [PATCH 01/15] Introduce restricted content block --- assets/blocks/restricted-content/block.json | 14 ++++ assets/blocks/restricted-content/edit.js | 59 +++++++++++++++++ assets/blocks/restricted-content/index.js | 26 ++++++++ assets/blocks/restricted-content/save.js | 9 +++ assets/blocks/restricted-content/settings.js | 63 ++++++++++++++++++ assets/blocks/restricted-content/style.scss | 21 ++++++ assets/blocks/sensei-single-course-blocks.js | 3 + assets/blocks/single-course.scss | 1 + .../class-sensei-block-contact-teacher.php | 2 +- .../blocks/class-sensei-block-take-course.php | 2 +- .../blocks/class-sensei-course-blocks.php | 39 ++++++++++- .../class-sensei-course-outline-block.php | 25 +------ .../class-sensei-course-progress-block.php | 2 +- .../class-sensei-restricted-content-block.php | 65 +++++++++++++++++++ .../outline-block-post-content.html | 0 .../test-class-sensei-block-take-course.php | 5 ++ ...est-class-sensei-contact-teacher-block.php | 5 ++ ...test-class-sensei-course-outline-block.php | 8 ++- 18 files changed, 321 insertions(+), 28 deletions(-) create mode 100644 assets/blocks/restricted-content/block.json create mode 100644 assets/blocks/restricted-content/edit.js create mode 100644 assets/blocks/restricted-content/index.js create mode 100644 assets/blocks/restricted-content/save.js create mode 100644 assets/blocks/restricted-content/settings.js create mode 100644 assets/blocks/restricted-content/style.scss create mode 100644 includes/blocks/class-sensei-restricted-content-block.php rename tests/unit-tests/{ => blocks}/sample-data/outline-block-post-content.html (100%) rename tests/unit-tests/{ => blocks}/test-class-sensei-contact-teacher-block.php (92%) rename tests/unit-tests/{ => blocks}/test-class-sensei-course-outline-block.php (97%) diff --git a/assets/blocks/restricted-content/block.json b/assets/blocks/restricted-content/block.json new file mode 100644 index 0000000000..9dacabc445 --- /dev/null +++ b/assets/blocks/restricted-content/block.json @@ -0,0 +1,14 @@ +{ + "name": "sensei-lms/restricted-content", + "category": "sensei-lms", + "supports": { + "html": false, + "align": [ "wide", "full" ] + }, + "attributes": { + "optionSelected": { + "type": "string", + "default": "enrolled" + } + } +} \ No newline at end of file diff --git a/assets/blocks/restricted-content/edit.js b/assets/blocks/restricted-content/edit.js new file mode 100644 index 0000000000..d36dd8e33c --- /dev/null +++ b/assets/blocks/restricted-content/edit.js @@ -0,0 +1,59 @@ +import { InnerBlocks } from '@wordpress/block-editor'; +import { compose } from '@wordpress/compose'; +import { withSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { RestrictedContentSettings } from './settings'; + +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, + attributes: { optionSelected }, + setAttributes, +} ) => { + return ( + <> +
+ +
+ + setAttributes( { + optionSelected: option, + } ) + } + /> + + ); +}; + +export default compose( [ + withSelect( ( select, { clientId } ) => { + const { getBlock } = select( 'core/block-editor' ); + + const block = getBlock( clientId ); + + return { + hasInnerBlocks: !! ( block && block.innerBlocks.length ), + }; + } ), +] )( EditRestrictedContent ); diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js new file mode 100644 index 0000000000..fce80a5fa5 --- /dev/null +++ b/assets/blocks/restricted-content/index.js @@ -0,0 +1,26 @@ +import { __ } from '@wordpress/i18n'; +import { Icon } from '@wordpress/components'; +import edit from './edit'; +import save from './save'; +import metadata from './block'; + +export default { + title: __( 'Restricted Content', 'sensei-lms' ), + description: __( + 'Content inside this container block will be restricted to specific cases only.', + '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: () => , + edit, + save, + ...metadata, +}; diff --git a/assets/blocks/restricted-content/save.js b/assets/blocks/restricted-content/save.js new file mode 100644 index 0000000000..86811c2f37 --- /dev/null +++ b/assets/blocks/restricted-content/save.js @@ -0,0 +1,9 @@ +import { InnerBlocks } from '@wordpress/block-editor'; + +export default function SaveRestrictedContent( { className } ) { + return ( +
+ +
+ ); +} diff --git a/assets/blocks/restricted-content/settings.js b/assets/blocks/restricted-content/settings.js new file mode 100644 index 0000000000..000cda56ea --- /dev/null +++ b/assets/blocks/restricted-content/settings.js @@ -0,0 +1,63 @@ +import { BlockControls } from '@wordpress/block-editor'; +import { + Button, + Dropdown, + NavigableMenu, + Toolbar, +} from '@wordpress/components'; +import { RestrictOptions, RestrictOptionLabels } from './edit'; + +/** + * The restricted content block settings. + * + * @param {Object} props Component properties. + * @param {number} props.selectedRestriction The restriction that is currently selected. + * @param {Function} props.onRestrictionChange Callback which is called when a new option is selected. + */ +export function RestrictedContentSettings( { + selectedRestriction, + onRestrictionChange, +} ) { + return ( + + + ( + + ) } + renderContent={ ( { onClose } ) => { + return ( + + { Object.values( RestrictOptions ).map( + ( option ) => ( + + ) + ) } + + ); + } } + /> + + + ); +} diff --git a/assets/blocks/restricted-content/style.scss b/assets/blocks/restricted-content/style.scss new file mode 100644 index 0000000000..2e9e622332 --- /dev/null +++ b/assets/blocks/restricted-content/style.scss @@ -0,0 +1,21 @@ +.wp-block-sensei-lms-restricted-toggle { + min-width: 165px; + + .wp-block-sensei-lms-restricted-toggle-button { + justify-content: center; + width: 100%; + } +} + +.wp-block-sensei-lms-restricted-content { + .components-popover__content { + border: 1px solid #1e1e1e; + box-shadow: none; + min-width: 165px; + } + + .wp-block-sensei-lms-restricted-content-button { + width: 100%; + justify-content: center; + } +} diff --git a/assets/blocks/sensei-single-course-blocks.js b/assets/blocks/sensei-single-course-blocks.js index 0e68c08e3d..29a2c0188d 100644 --- a/assets/blocks/sensei-single-course-blocks.js +++ b/assets/blocks/sensei-single-course-blocks.js @@ -2,12 +2,14 @@ import registerSenseiBlocks from './register-sensei-blocks'; import TakeCourseButtonBlock from './take-course'; import ContactTeacherButton from './contact-teacher'; import CourseProgressBlock from './course-progress'; +import RestrictedContent from './restricted-content'; import { CourseOutlineBlock, CourseOutlineLessonBlock, CourseOutlineModuleBlock, } from './course-outline'; + registerSenseiBlocks( [ CourseOutlineBlock, CourseOutlineModuleBlock, @@ -15,4 +17,5 @@ registerSenseiBlocks( [ TakeCourseButtonBlock, ContactTeacherButton, CourseProgressBlock, + RestrictedContent, ] ); diff --git a/assets/blocks/single-course.scss b/assets/blocks/single-course.scss index 64f524424a..191a91e9e6 100644 --- a/assets/blocks/single-course.scss +++ b/assets/blocks/single-course.scss @@ -3,3 +3,4 @@ @import 'course-outline/style'; @import 'course-progress/style'; @import 'button/button'; +@import 'restricted-content/style'; diff --git a/includes/blocks/class-sensei-block-contact-teacher.php b/includes/blocks/class-sensei-block-contact-teacher.php index 4bc52414dd..fa83a3ce97 100644 --- a/includes/blocks/class-sensei-block-contact-teacher.php +++ b/includes/blocks/class-sensei-block-contact-teacher.php @@ -18,7 +18,7 @@ class Sensei_Block_Contact_Teacher { * Sensei_Block_Contact_Teacher constructor. */ public function __construct() { - add_action( 'init', [ $this, 'register_block' ] ); + $this->register_block(); } /** diff --git a/includes/blocks/class-sensei-block-take-course.php b/includes/blocks/class-sensei-block-take-course.php index 73e5d50f16..302115b764 100644 --- a/includes/blocks/class-sensei-block-take-course.php +++ b/includes/blocks/class-sensei-block-take-course.php @@ -19,7 +19,7 @@ class Sensei_Block_Take_Course { * Sensei_Block_Take_Course constructor. */ public function __construct() { - add_action( 'init', [ $this, 'register_block' ] ); + $this->register_block(); } diff --git a/includes/blocks/class-sensei-course-blocks.php b/includes/blocks/class-sensei-course-blocks.php index ec44677299..48a5043045 100644 --- a/includes/blocks/class-sensei-course-blocks.php +++ b/includes/blocks/class-sensei-course-blocks.php @@ -42,18 +42,53 @@ class Sensei_Course_Blocks { public $take_course; /** - * Sensei_Blocks constructor. + * Sensei_Course_Blocks constructor. */ public function __construct() { add_action( 'enqueue_block_assets', [ $this, 'enqueue_block_assets' ] ); add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] ); add_filter( 'sensei_use_sensei_template', [ 'Sensei_Course_Blocks', 'skip_single_course_template' ] ); + add_action( 'template_redirect', [ $this, 'maybe_initialize_blocks' ] ); + add_action( 'current_screen', [ $this, 'maybe_initialize_blocks' ] ); + } + + /** + * Check if course blocks should be initialized and do initialization. + * + * @access private + */ + public function maybe_initialize_blocks() { + if ( is_admin() ) { + $screen = get_current_screen(); - // Init blocks. + if ( ! $screen->is_block_editor || 'course' !== $screen->post_type ) { + return; + } + } elseif ( 'course' !== get_post_type() ) { + return; + } + + $this->initialize_blocks(); + } + + /** + * Initialize blocks that are used in course pages. + */ + public function initialize_blocks() { $this->outline = new Sensei_Course_Outline_Block(); $this->progress = new Sensei_Course_Progress_Block(); $this->contact_teacher = new Sensei_Block_Contact_Teacher(); $this->take_course = new Sensei_Block_Take_Course(); + new Sensei_Restricted_Content_Block(); + + $post_type_object = get_post_type_object( 'course' ); + + $post_type_object->template = [ + [ 'sensei-lms/button-take-course' ], + [ 'sensei-lms/button-contact-teacher' ], + [ 'sensei-lms/course-progress' ], + [ 'sensei-lms/course-outline' ], + ]; } /** diff --git a/includes/blocks/class-sensei-course-outline-block.php b/includes/blocks/class-sensei-course-outline-block.php index 70586558fa..ea6e8576ea 100644 --- a/includes/blocks/class-sensei-course-outline-block.php +++ b/includes/blocks/class-sensei-course-outline-block.php @@ -57,18 +57,15 @@ class Sensei_Course_Outline_Block { * Sensei_Course_Outline_Block constructor. */ public function __construct() { - - add_action( 'init', [ $this, 'register_course_template' ], 101 ); - add_action( 'init', [ $this, 'register_blocks' ] ); - add_action( 'init', [ $this, 'init' ] ); - $this->course = new Sensei_Course_Outline_Course_Block(); $this->lesson = new Sensei_Course_Outline_Lesson_Block(); $this->module = new Sensei_Course_Outline_Module_Block(); + + $this->register_blocks(); } /** - * Initialize block instance. + * Resets block content. * * @access private */ @@ -76,22 +73,6 @@ public function init() { $this->block_content = null; } - /** - * Register course template. - * - * @access private - */ - public function register_course_template() { - $post_type_object = get_post_type_object( 'course' ); - - $post_type_object->template = [ - [ 'sensei-lms/button-take-course' ], - [ 'sensei-lms/button-contact-teacher' ], - [ 'sensei-lms/course-progress' ], - [ 'sensei-lms/course-outline' ], - ]; - } - /** * Register course outline block. * diff --git a/includes/blocks/class-sensei-course-progress-block.php b/includes/blocks/class-sensei-course-progress-block.php index 865cd9893b..0be4ab69d3 100644 --- a/includes/blocks/class-sensei-course-progress-block.php +++ b/includes/blocks/class-sensei-course-progress-block.php @@ -18,7 +18,7 @@ class Sensei_Course_Progress_Block { * Sensei_Course_Progress_Block constructor. */ public function __construct() { - add_action( 'init', [ $this, 'register_block' ] ); + $this->register_block(); } /** diff --git a/includes/blocks/class-sensei-restricted-content-block.php b/includes/blocks/class-sensei-restricted-content-block.php new file mode 100644 index 0000000000..ad5e691e3b --- /dev/null +++ b/includes/blocks/class-sensei-restricted-content-block.php @@ -0,0 +1,65 @@ + [ $this, 'render' ], + ], + Sensei()->assets->src_path( 'blocks/restricted-content/unenrolled' ) + ); + + Sensei_Blocks::register_sensei_block( + 'sensei-lms/enrolled-content', + [ + 'render_callback' => [ $this, 'render' ], + ], + Sensei()->assets->src_path( 'blocks/restricted-content/enrolled' ) + ); + } + + /** + * Renders restricted content blocks in the frontend. + * + * @param array $attributes The block attributes. + * @param string $content The inner block content. + * @param WP_Block $block The block object. + * + * @return string The HTML of the block. + */ + public function render( $attributes, $content, $block ) : string { + $course_id = null; + + if ( 'course' === get_post_type() ) { + $course_id = get_the_ID(); + } elseif ( 'lesson' === get_post_type() ) { + $course_id = Sensei()->lesson->get_course_id( get_the_ID() ); + } + + $should_hide = ( 'sensei-lms/enrolled-content' === $block->name && ! Sensei()->course::is_user_enrolled( $course_id ) ) || + ( 'sensei-lms/unenrolled-content' === $block->name && Sensei()->course::is_user_enrolled( $course_id ) ); + + if ( $should_hide ) { + return ''; + } + + return $content; + } +} diff --git a/tests/unit-tests/sample-data/outline-block-post-content.html b/tests/unit-tests/blocks/sample-data/outline-block-post-content.html similarity index 100% rename from tests/unit-tests/sample-data/outline-block-post-content.html rename to tests/unit-tests/blocks/sample-data/outline-block-post-content.html diff --git a/tests/unit-tests/blocks/test-class-sensei-block-take-course.php b/tests/unit-tests/blocks/test-class-sensei-block-take-course.php index fa841f36ea..d6c8adbcab 100644 --- a/tests/unit-tests/blocks/test-class-sensei-block-take-course.php +++ b/tests/unit-tests/blocks/test-class-sensei-block-take-course.php @@ -38,6 +38,11 @@ public function setUp() { } + public function tearDown() { + parent::tearDown(); + WP_Block_Type_Registry::get_instance()->unregister( 'sensei-lms/button-take-course' ); + } + /** * The take course block is registered and renders content. */ diff --git a/tests/unit-tests/test-class-sensei-contact-teacher-block.php b/tests/unit-tests/blocks/test-class-sensei-contact-teacher-block.php similarity index 92% rename from tests/unit-tests/test-class-sensei-contact-teacher-block.php rename to tests/unit-tests/blocks/test-class-sensei-contact-teacher-block.php index 9ea6179165..00586164d9 100644 --- a/tests/unit-tests/test-class-sensei-contact-teacher-block.php +++ b/tests/unit-tests/blocks/test-class-sensei-contact-teacher-block.php @@ -30,6 +30,11 @@ public function setUp() { $this->login_as_student(); } + public function tearDown() { + parent::tearDown(); + WP_Block_Type_Registry::get_instance()->unregister( 'sensei-lms/button-contact-teacher' ); + } + /** * Test the saved block content is used for the button, with link added to open the form. */ diff --git a/tests/unit-tests/test-class-sensei-course-outline-block.php b/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php similarity index 97% rename from tests/unit-tests/test-class-sensei-course-outline-block.php rename to tests/unit-tests/blocks/test-class-sensei-course-outline-block.php index 5297570449..64b9908f1c 100644 --- a/tests/unit-tests/test-class-sensei-course-outline-block.php +++ b/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php @@ -7,6 +7,13 @@ */ class Sensei_Course_Outline_Block_Test extends WP_UnitTestCase { + /** + * Initialize the blocks once. + */ + public static function setUpBeforeClass() { + Sensei()->blocks->course->initialize_blocks(); + } + /** * Set up the test. */ @@ -172,7 +179,6 @@ public function testBlockAttributesMatched() { unregister_block_type( 'sensei-lms/course-outline-module' ); $outline_block = new Sensei_Course_Outline_Block(); - $outline_block->register_blocks(); $this->mockPostCourseStructure( [ From 309536720da4ba0cacfdbffea4b96f1db1329ad8 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 11 Jan 2021 15:57:20 +0200 Subject: [PATCH 02/15] Add restrict content block transforms --- assets/blocks/restricted-content/index.js | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js index fce80a5fa5..56924be693 100644 --- a/assets/blocks/restricted-content/index.js +++ b/assets/blocks/restricted-content/index.js @@ -3,6 +3,7 @@ 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 Content', 'sensei-lms' ), @@ -23,4 +24,52 @@ export default { 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 + ); + }, + }, + ], + }, }; From ca76cbd1e0e0e69200c3f34a35b0d1bcd738877f Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 11 Jan 2021 16:59:56 +0200 Subject: [PATCH 03/15] Add an option which unwraps the inner blocks --- assets/blocks/restricted-content/edit.js | 3 + assets/blocks/restricted-content/settings.js | 155 ++++++++++++++----- 2 files changed, 118 insertions(+), 40 deletions(-) diff --git a/assets/blocks/restricted-content/edit.js b/assets/blocks/restricted-content/edit.js index d36dd8e33c..59a2c044eb 100644 --- a/assets/blocks/restricted-content/edit.js +++ b/assets/blocks/restricted-content/edit.js @@ -22,6 +22,7 @@ export const RestrictOptionLabels = { const EditRestrictedContent = ( { className, hasInnerBlocks, + clientId, attributes: { optionSelected }, setAttributes, } ) => { @@ -41,6 +42,8 @@ const EditRestrictedContent = ( { optionSelected: option, } ) } + clientId={ clientId } + hasInnerBlocks={ hasInnerBlocks } /> ); diff --git a/assets/blocks/restricted-content/settings.js b/assets/blocks/restricted-content/settings.js index 000cda56ea..bc85e3fed6 100644 --- a/assets/blocks/restricted-content/settings.js +++ b/assets/blocks/restricted-content/settings.js @@ -1,63 +1,138 @@ -import { BlockControls } from '@wordpress/block-editor'; +import { + BlockControls, + BlockSettingsMenuControls, +} from '@wordpress/block-editor'; import { Button, Dropdown, NavigableMenu, Toolbar, + MenuItem, } from '@wordpress/components'; import { RestrictOptions, RestrictOptionLabels } from './edit'; +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; + +/** + * Check if the current block is the only one that is selected. + * + * @param {string} clientId The block client id. + */ +const useIsSingleRestrictSelected = function ( 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 = function ( 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 {number} props.selectedRestriction The restriction that is currently selected. + * @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 ); + return ( - - - ( - + ) } + renderContent={ ( { onClose } ) => { + return ( + + { Object.values( RestrictOptions ).map( + ( option ) => ( + + ) + ) } + + ); + } } + /> + + + { isSingleRestrictSelected && hasInnerBlocks && ( + + { ( { onClose } ) => ( + { + onRestrictRemoval(); + onClose(); + } } > - { RestrictOptionLabels[ selectedRestriction ] } - + { __( 'Remove restriction', 'sensei-lms' ) } + ) } - renderContent={ ( { onClose } ) => { - return ( - - { Object.values( RestrictOptions ).map( - ( option ) => ( - - ) - ) } - - ); - } } - /> - - + + ) } + ); } From 2e56da260d21189ebfea2c94b9dab46d8b5cc771 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 11 Jan 2021 17:23:36 +0200 Subject: [PATCH 04/15] Handle hiding content in the frontend --- assets/blocks/restricted-content/index.js | 2 +- .../class-sensei-restricted-content-block.php | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js index 56924be693..f28629d38e 100644 --- a/assets/blocks/restricted-content/index.js +++ b/assets/blocks/restricted-content/index.js @@ -8,7 +8,7 @@ import { createBlock } from '@wordpress/blocks'; export default { title: __( 'Restricted Content', 'sensei-lms' ), description: __( - 'Content inside this container block will be restricted to specific cases only.', + 'Content inside this container block will be restricted to specific users, according to the block settings.', 'sensei-lms' ), keywords: [ diff --git a/includes/blocks/class-sensei-restricted-content-block.php b/includes/blocks/class-sensei-restricted-content-block.php index ad5e691e3b..f4ed86878b 100644 --- a/includes/blocks/class-sensei-restricted-content-block.php +++ b/includes/blocks/class-sensei-restricted-content-block.php @@ -19,19 +19,11 @@ class Sensei_Restricted_Content_Block { */ public function __construct() { Sensei_Blocks::register_sensei_block( - 'sensei-lms/unenrolled-content', + 'sensei-lms/restricted-content', [ 'render_callback' => [ $this, 'render' ], ], - Sensei()->assets->src_path( 'blocks/restricted-content/unenrolled' ) - ); - - Sensei_Blocks::register_sensei_block( - 'sensei-lms/enrolled-content', - [ - 'render_callback' => [ $this, 'render' ], - ], - Sensei()->assets->src_path( 'blocks/restricted-content/enrolled' ) + Sensei()->assets->src_path( 'blocks/restricted-content' ) ); } @@ -53,8 +45,21 @@ public function render( $attributes, $content, $block ) : string { $course_id = Sensei()->lesson->get_course_id( get_the_ID() ); } - $should_hide = ( 'sensei-lms/enrolled-content' === $block->name && ! Sensei()->course::is_user_enrolled( $course_id ) ) || - ( 'sensei-lms/unenrolled-content' === $block->name && Sensei()->course::is_user_enrolled( $course_id ) ); + $should_hide = false; + + switch ( $attributes['optionSelected'] ) { + case 'enrolled': + $should_hide = ! Sensei()->course::is_user_enrolled( $course_id ); + break; + case 'unenrolled': + $should_hide = Sensei()->course::is_user_enrolled( $course_id ); + break; + case 'course-completed': + $should_hide = ! Sensei_Utils::user_completed_course( $course_id ); + break; + default: + break; + } if ( $should_hide ) { return ''; From 0db18330df26fc77523f20783b6b1b20d6006a93 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 11 Jan 2021 18:05:17 +0200 Subject: [PATCH 05/15] Remove extra argument and dont add menu controls when they don't exist --- assets/blocks/restricted-content/settings.js | 30 ++++++++++--------- .../class-sensei-restricted-content-block.php | 7 ++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/assets/blocks/restricted-content/settings.js b/assets/blocks/restricted-content/settings.js index bc85e3fed6..7043c44138 100644 --- a/assets/blocks/restricted-content/settings.js +++ b/assets/blocks/restricted-content/settings.js @@ -119,20 +119,22 @@ export function RestrictedContentSettings( { /> - { isSingleRestrictSelected && hasInnerBlocks && ( - - { ( { onClose } ) => ( - { - onRestrictRemoval(); - onClose(); - } } - > - { __( 'Remove restriction', 'sensei-lms' ) } - - ) } - - ) } + { isSingleRestrictSelected && + hasInnerBlocks && + BlockSettingsMenuControls && ( + + { ( { onClose } ) => ( + { + onRestrictRemoval(); + onClose(); + } } + > + { __( 'Remove restriction', 'sensei-lms' ) } + + ) } + + ) } ); } diff --git a/includes/blocks/class-sensei-restricted-content-block.php b/includes/blocks/class-sensei-restricted-content-block.php index f4ed86878b..79aa43d383 100644 --- a/includes/blocks/class-sensei-restricted-content-block.php +++ b/includes/blocks/class-sensei-restricted-content-block.php @@ -30,13 +30,12 @@ public function __construct() { /** * Renders restricted content blocks in the frontend. * - * @param array $attributes The block attributes. - * @param string $content The inner block content. - * @param WP_Block $block The block object. + * @param array $attributes The block attributes. + * @param string $content The inner block content. * * @return string The HTML of the block. */ - public function render( $attributes, $content, $block ) : string { + public function render( $attributes, $content ) : string { $course_id = null; if ( 'course' === get_post_type() ) { From 40a6ba17347973666b5a1965d5ce8c8203c0ddbf Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 18 Jan 2021 15:25:18 +0200 Subject: [PATCH 06/15] Rename method --- includes/blocks/class-sensei-course-outline-block.php | 2 +- .../blocks/test-class-sensei-course-outline-block.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/blocks/class-sensei-course-outline-block.php b/includes/blocks/class-sensei-course-outline-block.php index ea6e8576ea..4964a93d29 100644 --- a/includes/blocks/class-sensei-course-outline-block.php +++ b/includes/blocks/class-sensei-course-outline-block.php @@ -69,7 +69,7 @@ public function __construct() { * * @access private */ - public function init() { + public function clear_block_content() { $this->block_content = null; } diff --git a/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php b/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php index 64b9908f1c..818e25c072 100644 --- a/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php +++ b/tests/unit-tests/blocks/test-class-sensei-course-outline-block.php @@ -21,7 +21,7 @@ public function setUp() { parent::setUp(); $this->factory = new Sensei_Factory(); - Sensei()->blocks->course->outline->init(); + Sensei()->blocks->course->outline->clear_block_content(); } /** From e086e000a337900bed6f63a08d635b4551c0997d Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 18 Jan 2021 15:30:09 +0200 Subject: [PATCH 07/15] Use arrow function --- assets/blocks/restricted-content/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/blocks/restricted-content/settings.js b/assets/blocks/restricted-content/settings.js index 7043c44138..8d1648b4ef 100644 --- a/assets/blocks/restricted-content/settings.js +++ b/assets/blocks/restricted-content/settings.js @@ -18,7 +18,7 @@ import { useSelect, useDispatch } from '@wordpress/data'; * * @param {string} clientId The block client id. */ -const useIsSingleRestrictSelected = function ( clientId ) { +const useIsSingleRestrictSelected = ( clientId ) => { return useSelect( ( select ) => { const selectedClientIds = select( @@ -39,7 +39,7 @@ const useIsSingleRestrictSelected = function ( clientId ) { * * @param {string} clientId The block client id. */ -const useOnRestrictionRemoval = function ( clientId ) { +const useOnRestrictionRemoval = ( clientId ) => { const block = useSelect( ( select ) => select( 'core/block-editor' ).getBlock( clientId ), [ clientId ] From abeee6cafec9a826a9fcb46696465df343f66123 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Mon, 18 Jan 2021 19:20:19 +0200 Subject: [PATCH 08/15] Rename attribute to restrictionType --- assets/blocks/restricted-content/block.json | 2 +- assets/blocks/restricted-content/edit.js | 6 +++--- includes/blocks/class-sensei-restricted-content-block.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/blocks/restricted-content/block.json b/assets/blocks/restricted-content/block.json index 9dacabc445..15a7e39e97 100644 --- a/assets/blocks/restricted-content/block.json +++ b/assets/blocks/restricted-content/block.json @@ -6,7 +6,7 @@ "align": [ "wide", "full" ] }, "attributes": { - "optionSelected": { + "restrictionType": { "type": "string", "default": "enrolled" } diff --git a/assets/blocks/restricted-content/edit.js b/assets/blocks/restricted-content/edit.js index 59a2c044eb..f8a17fd7ef 100644 --- a/assets/blocks/restricted-content/edit.js +++ b/assets/blocks/restricted-content/edit.js @@ -23,7 +23,7 @@ const EditRestrictedContent = ( { className, hasInnerBlocks, clientId, - attributes: { optionSelected }, + attributes: { restrictionType }, setAttributes, } ) => { return ( @@ -36,10 +36,10 @@ const EditRestrictedContent = ( { /> setAttributes( { - optionSelected: option, + restrictionType: option, } ) } clientId={ clientId } diff --git a/includes/blocks/class-sensei-restricted-content-block.php b/includes/blocks/class-sensei-restricted-content-block.php index 79aa43d383..a3df9221e8 100644 --- a/includes/blocks/class-sensei-restricted-content-block.php +++ b/includes/blocks/class-sensei-restricted-content-block.php @@ -46,7 +46,7 @@ public function render( $attributes, $content ) : string { $should_hide = false; - switch ( $attributes['optionSelected'] ) { + switch ( $attributes['restrictionType'] ) { case 'enrolled': $should_hide = ! Sensei()->course::is_user_enrolled( $course_id ); break; From 68da872c35547f233991d62198c7a9089ff64477 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Tue, 19 Jan 2021 16:00:05 +0200 Subject: [PATCH 09/15] Add group block classes to restrict content block --- assets/blocks/restricted-content/edit.js | 17 ++++++++++------- assets/blocks/restricted-content/save.js | 9 ++++++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/assets/blocks/restricted-content/edit.js b/assets/blocks/restricted-content/edit.js index f8a17fd7ef..a805c14826 100644 --- a/assets/blocks/restricted-content/edit.js +++ b/assets/blocks/restricted-content/edit.js @@ -3,6 +3,7 @@ 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', @@ -28,13 +29,15 @@ const EditRestrictedContent = ( { } ) => { return ( <> -
- -
+
+
+ +
+
diff --git a/assets/blocks/restricted-content/save.js b/assets/blocks/restricted-content/save.js index 86811c2f37..d58844a991 100644 --- a/assets/blocks/restricted-content/save.js +++ b/assets/blocks/restricted-content/save.js @@ -1,9 +1,12 @@ import { InnerBlocks } from '@wordpress/block-editor'; +import classnames from 'classnames'; export default function SaveRestrictedContent( { className } ) { return ( -
- -
+
+
+ +
+
); } From 79bf06c7dcb0e3b07fb4b9d73de232884a28933a Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Tue, 19 Jan 2021 17:09:09 +0200 Subject: [PATCH 10/15] Update restricted content block descriptions --- assets/blocks/restricted-content/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js index f28629d38e..7ccda7cf22 100644 --- a/assets/blocks/restricted-content/index.js +++ b/assets/blocks/restricted-content/index.js @@ -8,7 +8,7 @@ import { createBlock } from '@wordpress/blocks'; export default { title: __( 'Restricted Content', 'sensei-lms' ), description: __( - 'Content inside this container block will be restricted to specific users, according to the block settings.', + 'Content inside this container block can be restricted to a specified group of users. Possible options are enrolled users, not enrolled users and users which completed the course.', 'sensei-lms' ), keywords: [ From c4b9691672b85cef23024bf1f2c953a984cc841a Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Tue, 19 Jan 2021 17:26:55 +0200 Subject: [PATCH 11/15] Rename restrict content block --- assets/blocks/restricted-content/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js index 7ccda7cf22..bdb8d81ddb 100644 --- a/assets/blocks/restricted-content/index.js +++ b/assets/blocks/restricted-content/index.js @@ -6,7 +6,7 @@ import metadata from './block'; import { createBlock } from '@wordpress/blocks'; export default { - title: __( 'Restricted Content', 'sensei-lms' ), + 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 which completed the course.', 'sensei-lms' From 8a9122528c1a21cdc073c3332d95e23e78c712e3 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Wed, 20 Jan 2021 10:15:20 +0200 Subject: [PATCH 12/15] Correct block description --- assets/blocks/restricted-content/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/blocks/restricted-content/index.js b/assets/blocks/restricted-content/index.js index bdb8d81ddb..41144bac7f 100644 --- a/assets/blocks/restricted-content/index.js +++ b/assets/blocks/restricted-content/index.js @@ -8,7 +8,7 @@ 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 which completed the course.', + '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: [ From acf63705f9a36316ef5ec9439b21e555022237f5 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Wed, 20 Jan 2021 10:44:50 +0200 Subject: [PATCH 13/15] Remove empty line --- assets/blocks/sensei-single-course-blocks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/blocks/sensei-single-course-blocks.js b/assets/blocks/sensei-single-course-blocks.js index 29a2c0188d..8e79d94608 100644 --- a/assets/blocks/sensei-single-course-blocks.js +++ b/assets/blocks/sensei-single-course-blocks.js @@ -9,7 +9,6 @@ import { CourseOutlineModuleBlock, } from './course-outline'; - registerSenseiBlocks( [ CourseOutlineBlock, CourseOutlineModuleBlock, From 5e7af978acf8608467e55d5636210713c42f50ee Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Wed, 20 Jan 2021 11:45:39 +0200 Subject: [PATCH 14/15] Switch to using ToolbarDropdown component --- assets/blocks/restricted-content/settings.js | 66 +++++--------------- assets/blocks/restricted-content/style.scss | 21 ------- assets/blocks/single-course.scss | 1 - 3 files changed, 16 insertions(+), 72 deletions(-) delete mode 100644 assets/blocks/restricted-content/style.scss diff --git a/assets/blocks/restricted-content/settings.js b/assets/blocks/restricted-content/settings.js index 8d1648b4ef..52f3f10a50 100644 --- a/assets/blocks/restricted-content/settings.js +++ b/assets/blocks/restricted-content/settings.js @@ -2,16 +2,11 @@ import { BlockControls, BlockSettingsMenuControls, } from '@wordpress/block-editor'; -import { - Button, - Dropdown, - NavigableMenu, - Toolbar, - MenuItem, -} from '@wordpress/components'; +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. @@ -71,53 +66,24 @@ export function RestrictedContentSettings( { const isSingleRestrictSelected = useIsSingleRestrictSelected( clientId ); const onRestrictRemoval = useOnRestrictionRemoval( clientId ); + const toolbarOptions = Object.keys( RestrictOptions ).map( + ( optionKey ) => ( { + value: RestrictOptions[ optionKey ], + label: RestrictOptionLabels[ RestrictOptions[ optionKey ] ], + } ) + ); + return ( <> - - ( - - ) } - renderContent={ ( { onClose } ) => { - return ( - - { Object.values( RestrictOptions ).map( - ( option ) => ( - - ) - ) } - - ); - } } + + - + { isSingleRestrictSelected && hasInnerBlocks && diff --git a/assets/blocks/restricted-content/style.scss b/assets/blocks/restricted-content/style.scss deleted file mode 100644 index 2e9e622332..0000000000 --- a/assets/blocks/restricted-content/style.scss +++ /dev/null @@ -1,21 +0,0 @@ -.wp-block-sensei-lms-restricted-toggle { - min-width: 165px; - - .wp-block-sensei-lms-restricted-toggle-button { - justify-content: center; - width: 100%; - } -} - -.wp-block-sensei-lms-restricted-content { - .components-popover__content { - border: 1px solid #1e1e1e; - box-shadow: none; - min-width: 165px; - } - - .wp-block-sensei-lms-restricted-content-button { - width: 100%; - justify-content: center; - } -} diff --git a/assets/blocks/single-course.scss b/assets/blocks/single-course.scss index 191a91e9e6..64f524424a 100644 --- a/assets/blocks/single-course.scss +++ b/assets/blocks/single-course.scss @@ -3,4 +3,3 @@ @import 'course-outline/style'; @import 'course-progress/style'; @import 'button/button'; -@import 'restricted-content/style'; From 45e13c3ec2ec22384baf79f84e5979049374e373 Mon Sep 17 00:00:00 2001 From: Giannis Karagiannis Date: Wed, 20 Jan 2021 14:58:41 +0200 Subject: [PATCH 15/15] Add restricted content block to tracks --- includes/class-sensei-course.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/includes/class-sensei-course.php b/includes/class-sensei-course.php index 46334e46ea..a2567606e9 100755 --- a/includes/class-sensei-course.php +++ b/includes/class-sensei-course.php @@ -3592,15 +3592,16 @@ public function log_course_update() { $product_count = empty( $product_ids ) ? 0 : count( array_filter( $product_ids, 'is_numeric' ) ); $event_properties = [ - 'course_id' => $course_id, - 'has_outline_block' => has_block( 'sensei-lms/course-outline', $content ) ? 1 : 0, - 'has_progress_block' => has_block( 'sensei-lms/course-progress', $content ) ? 1 : 0, - 'has_take_course_block' => has_block( 'sensei-lms/button-take-course', $content ) ? 1 : 0, - 'has_contact_teacher_block' => has_block( 'sensei-lms/button-contact-teacher', $content ) ? 1 : 0, - 'module_count' => count( wp_get_post_terms( $course_id, 'module' ) ), - 'lesson_count' => $this->course_lesson_count( $course_id ), - 'product_count' => $product_count, - 'sample_course' => 'getting-started-with-sensei-lms' === $post->post_name ? 1 : 0, + 'course_id' => $course_id, + 'has_outline_block' => has_block( 'sensei-lms/course-outline', $content ) ? 1 : 0, + 'has_progress_block' => has_block( 'sensei-lms/course-progress', $content ) ? 1 : 0, + 'has_take_course_block' => has_block( 'sensei-lms/button-take-course', $content ) ? 1 : 0, + 'has_contact_teacher_block' => has_block( 'sensei-lms/button-contact-teacher', $content ) ? 1 : 0, + 'has_restricted_content_block' => has_block( 'sensei-lms/restricted-content', $content ) ? 1 : 0, + 'module_count' => count( wp_get_post_terms( $course_id, 'module' ) ), + 'lesson_count' => $this->course_lesson_count( $course_id ), + 'product_count' => $product_count, + 'sample_course' => 'getting-started-with-sensei-lms' === $post->post_name ? 1 : 0, ]; sensei_log_event( 'course_update', $event_properties );