Render\CourseHeader::class, Render\CourseIntro::NAME => Render\CourseIntro::class, Render\CoursePoints::NAME => Render\CoursePoints::class, Render\CourseText::NAME => Render\CourseText::class, Render\CourseFiles::NAME => Render\CourseFiles::class, Render\Faq::NAME => Render\Faq::class, Render\CourseReviews::NAME => Render\CourseReviews::class, Render\ComingSoon::NAME => Render\ComingSoon::class, Render\MyPoints::NAME => Render\MyPoints::class, Render\Leaderboard::NAME => Render\Leaderboard::class, Render\CourseTabs::NAME => Render\CourseTabs::class, Render\CourseTab::NAME => Render\CourseTab::class, Render\CourseMeta::NAME => Render\CourseMeta::class, Render\CourseDetails::NAME => Render\CourseDetails::class, Render\CourseActions::NAME => Render\CourseActions::class, ); private static bool $registered = false; public function register(): void { if ( self::$registered ) { return; } self::$registered = true; add_action( 'init', array( self::class, 'register_assets' ), 6 ); add_action( 'init', array( self::class, 'register_blocks' ), 20 ); add_action( 'init', array( self::class, 'add_shortcodes' ) ); add_filter( 'rooster_lms_elementor_blocks', array( self::class, 'elementor_blocks' ) ); add_filter( 'rooster_lms_account_sections', array( self::class, 'account_sections' ) ); add_action( 'wp_head', array( CourseJsonLd::class, 'head' ), 30 ); } public static function register_assets(): void { $url = plugins_url( 'assets/ui/', ROOSTER_LMS_FILE ); wp_register_style( View::HANDLE, $url . 'course-page.css', array( Assets::UI_BUTTON, Assets::UI_BADGE ), Assets::ver( 'course-page.css' ) ); wp_register_script( View::HANDLE, $url . 'course-page.js', array(), Assets::ver( 'course-page.js' ), array( 'in_footer' => true, 'strategy' => 'defer', ) ); add_action( 'wp_print_footer_scripts', array( self::class, 'script_data' ), 1 ); } public static function script_data(): void { if ( ! wp_script_is( View::HANDLE, 'enqueued' ) || ! empty( wp_scripts()->get_data( View::HANDLE, 'before' ) ) ) { return; } wp_add_inline_script( View::HANDLE, 'window.roosterLmsCoursePage = ' . wp_json_encode( self::script_settings() ) . ';', 'before' ); } public static function script_settings(): array { return array( 'root' => esc_url_raw( rest_url( 'rooster-lms/v1/' ) ), 'nonce' => is_user_logged_in() ? wp_create_nonce( 'wp_rest' ) : '', 'l10n' => array( 'saved' => __( 'Saved to your courses.', 'rooster-lms' ), 'removed' => __( 'Removed from your saved courses.', 'rooster-lms' ), 'error' => __( 'That did not work. Try again.', 'rooster-lms' ), 'joined' => __( 'You are on the waitlist. We will e-mail you when the course opens.', 'rooster-lms' ), 'left' => __( 'You left the waitlist.', 'rooster-lms' ), 'confirm' => __( 'Check your e-mail: open the link we sent to confirm.', 'rooster-lms' ), 'badEmail' => __( 'Enter a valid e-mail address.', 'rooster-lms' ), 'tooMany' => __( 'Too many requests. Try again later.', 'rooster-lms' ), 'loginFirst' => __( 'Log in to save courses.', 'rooster-lms' ), 'copied' => __( 'The course address is copied.', 'rooster-lms' ), ), ); } public static function register_blocks(): void { foreach ( self::BLOCKS as $name => $class ) { $dir = BlockRegistry::build_dir() . $name; if ( ! is_file( $dir . '/block.json' ) || BlockRegistry::is_registered( $name ) ) { continue; } $type = register_block_type( $dir, array( 'render_callback' => array( $class, 'render' ) ) ); if ( false === $type ) { continue; } $handle = generate_block_asset_handle( 'rooster-lms/' . $name, 'editorScript' ); if ( wp_script_is( $handle, 'registered' ) ) { wp_set_script_translations( $handle, 'rooster-lms', ROOSTER_LMS_DIR . 'languages' ); } } } public static function add_shortcodes(): void { foreach ( self::BLOCKS as $class ) { if ( '' !== $class::SHORTCODE ) { add_shortcode( $class::SHORTCODE, array( $class, 'shortcode' ) ); } } } public static function shortcode( string $name, string $tag, array $spec, $atts ): string { $attributes = self::block_attributes( $tag, $spec, is_array( $atts ) ? $atts : array() ); if ( BlockRegistry::is_registered( $name ) ) { return render_block( array( 'blockName' => 'rooster-lms/' . $name, 'attrs' => $attributes, 'innerHTML' => '', 'innerContent' => array(), 'innerBlocks' => array(), ) ); } $class = self::BLOCKS[ $name ] ?? ''; return '' === $class ? '' : (string) $class::render( $attributes ); } public static function block_attributes( string $tag, array $spec, array $atts ): array { $atts = shortcode_atts( array_fill_keys( array_keys( $spec ), null ), $atts, $tag ); $out = array(); foreach ( $spec as $key => $target ) { if ( null === $atts[ $key ] || ! is_scalar( $atts[ $key ] ) ) { continue; } $value = (string) $atts[ $key ]; $out[ $target[0] ] = match ( $target[1] ) { 'int' => max( 0, (int) $value ), 'bool' => (bool) filter_var( $value, FILTER_VALIDATE_BOOLEAN ), 'key' => sanitize_key( $value ), default => sanitize_text_field( $value ), }; } return $out; } public static function elementor_blocks( $blocks ) { if ( is_array( $blocks ) ) { foreach ( self::BLOCKS as $name => $class ) { if ( '' !== $class::SHORTCODE ) { $blocks[] = $name; } } } return $blocks; } public static function account_sections( $sections ) { if ( ! is_array( $sections ) ) { return $sections; } $sections[] = new Section( 'points', __( 'My points', 'rooster-lms' ), 'progress', 'learner', 40, static fn( AccountViewer $v ): bool => (bool) Settings::get( 'points_enabled' ), static fn(): string => Embed::block( Render\MyPoints::NAME, array( 'showHeading' => false ) ) . Embed::block( Render\Leaderboard::NAME ), null, 'trending' ); $sections[] = new Section( 'saved', __( 'Saved courses', 'rooster-lms' ), 'main', 'learner', 22, static fn( AccountViewer $v ): bool => true, static fn(): string => Embed::block( 'course-grid', array( 'show' => 'saved', 'perPage' => 12, 'columns' => 3, 'showWishlist' => true, ) ), null, 'list' ); return $sections; } }