true, 'strategy' => 'defer', ] ); foreach ( (array) glob( __DIR__ . '/blocks/*/block.json' ) as $metadata ) { $requires = self::requires_shortcode( basename( dirname( $metadata ) ) ); if ( $requires && ! shortcode_exists( $requires ) ) { continue; } register_block_type( dirname( $metadata ) ); } register_block_style( 'core/post-terms', [ 'name' => 'academy-with-images', 'label' => __( 'With category images', 'academy' ), 'inline_style' => '.is-style-academy-with-images a{display:inline-flex;align-items:center;gap:.35em}.is-style-academy-with-images .academy-post-terms__category-thumb{width:1.4em;height:1.4em;border-radius:50%;object-fit:cover}', ] ); } /** * Blocks that wrap a feature from Academy Pro, by the shortcode that feature * adds. They are only offered when that feature is switched on. * * @param string $block Block folder name. * @return string Shortcode tag, or '' when the block always applies. */ public static function requires_shortcode( $block ) { $map = [ 'certificate-verification' => 'academy_pro_certificate_verification', 'social-login' => 'academy_social_login', ]; return isset( $map[ $block ] ) ? $map[ $block ] : ''; } /** * Block patterns for course grids and course pages. * * @return void */ public function register_patterns() { register_block_pattern_category( 'academy', [ 'label' => __( 'Academy LMS', 'academy' ) ] ); $patterns = [ 'course-grid-list' => [ 'title' => __( 'Course list', 'academy' ), 'description' => __( 'Courses in a single column with the image beside the details.', 'academy' ), 'blockTypes' => [ 'core/query' ], ], 'course-grid-minimal' => [ 'title' => __( 'Course grid: minimal', 'academy' ), 'description' => __( 'Borderless course cards with image, title, instructor and price.', 'academy' ), 'blockTypes' => [ 'core/query' ], ], 'featured-courses' => [ 'title' => __( 'Featured courses', 'academy' ), 'description' => __( 'A heading and three featured courses.', 'academy' ), ], 'course-catalog' => [ 'title' => __( 'Course catalog with filters', 'academy' ), 'description' => __( 'Course filters beside a sortable course grid, for a courses page.', 'academy' ), ], 'course-page' => [ 'title' => __( 'Course page: two columns', 'academy' ), 'description' => __( 'Course details and sections beside a sticky enroll box.', 'academy' ), 'templateTypes' => [ 'single-academy_courses' ], ], ]; foreach ( $patterns as $slug => $pattern ) { $file = __DIR__ . '/blocks/patterns/' . $slug . '.php'; if ( file_exists( $file ) ) { register_block_pattern( 'academy/' . $slug, array_merge( $pattern, [ 'categories' => [ 'academy' ], 'filePath' => $file, ] ) ); } } } /** * Hide aBlocks' Academy blocks from the inserter now that Academy LMS has * its own. Content that already uses them keeps working. * * @return void */ public function hide_replaced_blocks() { $replaced = [ 'ablocks/academy-courses', 'ablocks/academy-enroll-form', 'ablocks/academy-student-registration-form', 'ablocks/academy-course-search', 'ablocks/academy-instructor-registration-form', 'ablocks/academy-pdf', 'ablocks/academy-password-reset-form', 'ablocks/academy-login-form', 'ablocks/academy-course-media', 'ablocks/academy-course-curriculums', 'ablocks/academy-course-reviews', 'ablocks/academy-review-form', 'ablocks/academy-review-list', 'ablocks/academy-addition-info', 'ablocks/academy-course-description', 'ablocks/academy-course-instructor', 'ablocks/academy-enroll-content', ]; wp_add_inline_script( 'wp-blocks', sprintf( 'wp.hooks.addFilter("blocks.registerBlockType","academy/hide-replaced-blocks",function(settings,name){return %s.indexOf(name)>-1?Object.assign({},settings,{supports:Object.assign({},settings.supports,{inserter:false})}):settings;});', wp_json_encode( $replaced ) ), 'after' ); } /** * Show the login and password reset forms (not the "already logged in" * message) in the editor's block previews. * * @param bool $logged_in Whether the visitor counts as logged in. * @return bool */ public function show_forms_in_preview( $logged_in ) { $uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; if ( defined( 'REST_REQUEST' ) && REST_REQUEST && false !== strpos( $uri, 'block-renderer' ) ) { return false; } return $logged_in; } /** * Add category images to Post Terms blocks that use the "With category * images" style, in any context (course grids included). * * @param string $block_content Rendered block. * @param array $block Parsed block. * @param \WP_Block $instance Block instance. * @return string */ public function post_terms_category_images( $block_content, $block, $instance ) { $class_name = isset( $block['attrs']['className'] ) ? (string) $block['attrs']['className'] : ''; if ( false === strpos( $class_name, 'is-style-academy-with-images' ) || 'academy_courses_category' !== ( $block['attrs']['term'] ?? '' ) ) { return $block_content; } // Single course pages already get images from Frontend\Template. if ( false !== strpos( $block_content, 'academy-post-terms__category-thumb' ) ) { return $block_content; } $post_id = isset( $instance->context['postId'] ) ? (int) $instance->context['postId'] : (int) get_the_ID(); $terms = get_the_terms( $post_id, 'academy_courses_category' ); if ( empty( $terms ) || is_wp_error( $terms ) ) { return $block_content; } foreach ( $terms as $term ) { $link = get_term_link( $term ); if ( is_wp_error( $link ) ) { continue; } $image = sprintf( '', esc_url( Helper::get_the_course_category_image_url( $term->term_id ) ) ); $block_content = (string) preg_replace( '/(]*>)/', '$1' . $image, $block_content, 1 ); } return $block_content; } /** * Blocks that print Academy's own course page markup and need its styles * and scripts. */ const ASSET_BLOCKS = [ 'academy/course-media', 'academy/course-description', 'academy/course-additional-info', 'academy/course-curriculum', 'academy/course-instructor-profiles', 'academy/course-rating-summary', 'academy/course-reviews', 'academy/course-review-form', 'academy/course-attachments', 'academy/course-enroll-card', 'academy/course-enroll-button', 'academy/course-wishlist', 'academy/login-form', 'academy/student-registration-form', 'academy/instructor-registration-form', 'academy/password-reset-form', 'academy/enroll-form', 'academy/student-dashboard', 'academy/enrolled-courses', 'academy/pdf', ]; /** * Source files of the editor script, for translations generated from them. */ const EDITOR_SOURCES = [ 'dev_academy/blocks/index.js', 'dev_academy/blocks/course-price.js', 'dev_academy/blocks/course-rating.js', 'dev_academy/blocks/course-instructors.js', 'dev_academy/blocks/course-meta.js', 'dev_academy/blocks/course-enroll-button.js', 'dev_academy/blocks/card-extras.js', 'dev_academy/blocks/course-grid.js', 'dev_academy/blocks/course-sections.js', 'dev_academy/blocks/shared/use-course.js', 'dev_academy/blocks/shared/icons.js', ]; /** * Load Academy's styles in the page head when the post content uses a block * that needs them, instead of late in the footer. * * @return void */ public function early_course_assets() { if ( ! is_singular() ) { return; } $post = get_queried_object(); if ( ! $post instanceof \WP_Post || false === strpos( $post->post_content, '