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, '