$person['id'],
'name' => $person['name'],
'avatar' => get_avatar_url( $person['id'], [ 'size' => 96 ] ),
'url' => $public ? home_url( '/author/' . $person['nicename'] ) : '',
];
}
if ( $is_bundle ) {
$is_enrolled = $user_id && \AcademyProCourseBundle\Helper::is_user_enrolled_in_bundle( $course_id, $user_id );
$is_paid = 'paid' === get_post_meta( $course_id, 'academy_course_bundle_type', true );
$is_public = false;
$is_complete = false;
} else {
$is_enrolled = $user_id && Helper::is_enrolled( $course_id, $user_id );
$is_paid = (bool) Helper::is_course_purchasable( $course_id );
$is_public = 'public' === Helper::get_course_type( $course_id );
$is_complete = $is_enrolled && Helper::is_completed_course( $course_id, $user_id );
}
$data = [
'id' => $course_id,
'type' => $is_bundle ? 'bundle' : 'course',
'url' => get_permalink( $course_id ),
'price' => self::price_html( $course_id ),
'is_paid' => $is_paid,
'is_public' => $is_public,
'is_sticky' => ! $is_bundle && Helper::is_course_sticky( $course_id ),
'reviews_enabled' => (bool) Helper::get_settings( 'is_enabled_course_review', true ) && ! get_post_meta( $course_id, 'academy_is_disabled_course_review', true ),
'rating' => $shared['rating'],
'instructors' => $instructors,
'lessons' => $shared['lessons'],
'duration' => $shared['duration'],
'level' => $shared['level'],
'enrolled' => $shared['enrolled'],
'is_enrolled' => (bool) $is_enrolled,
'is_complete' => (bool) $is_complete,
'continue_url' => ( $is_enrolled || $is_public ) && ! $is_bundle ? Helper::get_start_course_permalink( $course_id ) : '',
];
/**
* Filters the course facts used by the Academy LMS blocks.
*
* @param array $data Course facts.
* @param int $course_id Course ID.
*/
self::$request_cache[ $course_id ] = (array) apply_filters( 'academy/blocks/course_data', $data, $course_id );
return self::$request_cache[ $course_id ];
}
/**
* Cached facts that do not depend on the visitor.
*
* @param int $course_id Course ID.
* @return array
*/
private static function shared( $course_id ) {
$cached = wp_cache_get( $course_id, self::CACHE_GROUP );
if ( false === $cached ) {
$cached = get_transient( self::transient_key( $course_id ) );
}
if ( is_array( $cached ) && isset( $cached['v'] ) && self::CACHE_VERSION === $cached['v'] ) {
wp_cache_set( $course_id, $cached, self::CACHE_GROUP );
return $cached;
}
$is_bundle = 'alms_course_bundle' === get_post_type( $course_id );
$rating = (object) Helper::get_course_rating( $course_id );
$ids = [];
if ( ! $is_bundle ) {
foreach ( (array) Helper::get_instructors_by_course_id( $course_id ) as $row ) {
if ( is_object( $row ) && isset( $row->ID ) ) {
$ids[] = (int) $row->ID;
}
}
}
if ( ! $ids ) {
$ids[] = (int) get_post_field( 'post_author', $course_id );
}
$people = [];
foreach ( array_unique( array_filter( $ids ) ) as $id ) {
$user = get_userdata( $id );
if ( $user ) {
$people[] = [
'id' => $id,
'name' => $user->display_name,
'nicename' => $user->user_nicename,
];
}
}
if ( $is_bundle ) {
$lessons = (int) \AcademyProCourseBundle\Helper::get_bundle_lessons( $course_id );
$duration = self::format_clock_duration( (string) \AcademyProCourseBundle\Helper::get_bundle_duration( $course_id ) );
// Bundles have no difficulty level — the card shows how many
// courses it bundles in that slot instead.
$courses_count = count( (array) \AcademyProCourseBundle\Helper::get_bundle_courses_id( $course_id ) );
/* translators: %s: number of courses. */
$level = $courses_count > 0 ? sprintf( _n( '%s course', '%s courses', $courses_count, 'academy' ), number_format_i18n( $courses_count ) ) : '';
$enrolled = (int) \AcademyProCourseBundle\Helper::get_bundle_enrolled( $course_id );
} else {
$lessons = self::lesson_count( $course_id );
$duration = (string) Helper::get_course_duration( $course_id );
$level = (string) Helper::get_course_difficulty_level( $course_id );
$enrolled = (int) Helper::count_course_enrolled( $course_id );
}
$cached = [
'v' => self::CACHE_VERSION,
'rating' => [
'average' => isset( $rating->rating_avg ) ? (float) $rating->rating_avg : 0.0,
'count' => isset( $rating->rating_count ) ? (int) $rating->rating_count : 0,
],
'instructors' => $people,
'lessons' => $lessons,
'duration' => $duration,
'level' => $level,
'enrolled' => $enrolled,
];
set_transient( self::transient_key( $course_id ), $cached, DAY_IN_SECONDS );
wp_cache_set( $course_id, $cached, self::CACHE_GROUP );
return $cached;
}
/**
* Transient name for a course's cached facts.
*
* @param int $course_id Course ID.
* @return string
*/
private static function transient_key( $course_id ) {
return 'academy_course_card_' . (int) $course_id;
}
/**
* Clear a course's cached facts.
*
* @param int $course_id Course ID.
* @return void
*/
public static function flush( $course_id ) {
$course_id = (int) $course_id;
if ( $course_id <= 0 ) {
return;
}
delete_transient( self::transient_key( $course_id ) );
wp_cache_delete( $course_id, self::CACHE_GROUP );
unset( self::$request_cache[ $course_id ] );
}
/**
* Clear cached facts when a course, its reviews, enrolments or instructors change.
*
* @return void
*/
public static function register_cache_hooks() {
add_action(
'save_post',
function ( $post_id, $post ) {
if ( in_array( $post->post_type, self::post_types(), true ) ) {
self::flush( $post_id );
} elseif ( 'academy_enrolled' === $post->post_type ) {
self::flush( $post->post_parent );
}
},
10,
2
);
add_action(
'deleted_post',
function ( $post_id, $post ) {
if ( $post instanceof \WP_Post && 'academy_enrolled' === $post->post_type ) {
self::flush( $post->post_parent );
}
},
10,
2
);
add_action(
'transition_post_status',
function ( $new_status, $old_status, $post ) {
if ( $new_status !== $old_status && 'academy_enrolled' === $post->post_type ) {
self::flush( $post->post_parent );
}
},
10,
3
);
// Fires whenever a review is added, approved, unapproved or deleted.
add_action(
'wp_update_comment_count',
function ( $post_id ) {
if ( self::is_course( $post_id ) ) {
self::flush( $post_id );
}
}
);
$instructor_change = function ( $meta_id, $user_id, $meta_key, $meta_value ) {
if ( 'academy_instructor_course_id' === $meta_key ) {
self::flush( (int) $meta_value );
}
};
add_action( 'added_user_meta', $instructor_change, 10, 4 );
add_action( 'deleted_user_meta', $instructor_change, 10, 4 );
add_action(
'profile_update',
function ( $user_id ) {
foreach ( (array) get_user_meta( $user_id, 'academy_instructor_course_id', false ) as $course_id ) {
self::flush( (int) $course_id );
}
}
);
}
/**
* Price shown on course cards: the store price, "Free", "Public" or "Paid".
*
* @param int $course_id Course ID.
* @return string Sanitized HTML.
*/
public static function price_html( $course_id ) {
$price = '';
if ( 'alms_course_bundle' === get_post_type( $course_id ) ) {
$is_paid = 'paid' === get_post_meta( $course_id, 'academy_course_bundle_type', true );
$course_type = $is_paid ? 'paid' : 'free';
$product_id = $is_paid ? \AcademyProCourseBundle\Helper::get_bundle_product_id( $course_id ) : 0;
} else {
$course_type = Helper::get_course_type( $course_id );
$is_paid = Helper::is_course_purchasable( $course_id );
$product_id = $is_paid ? Helper::get_course_product_id( $course_id ) : 0;
}
if ( $product_id && Helper::is_active_woocommerce() && function_exists( 'wc_get_product' ) ) {
$product = wc_get_product( $product_id );
if ( $product ) {
$price = $product->get_price_html();
}
}
if ( $is_paid && empty( $price ) && 'storeengine' === Helper::monetization_engine() ) {
$provider = 'alms_course_bundle' === get_post_type( $course_id ) ? 'storeengine/course-bundle' : 'storeengine/academylms';
$price = Helper::get_storeengine_price_html( $course_id, $provider );
}
if ( $is_paid && empty( $price ) && 'alms_course_bundle' !== get_post_type( $course_id ) ) {
$price = Helper::get_plain_course_price_html( $course_id );
}
if ( $is_paid && $price ) {
$label = $price;
} elseif ( $is_paid ) {
$label = esc_html__( 'Paid', 'academy' );
} elseif ( 'public' === $course_type ) {
$label = esc_html__( 'Public', 'academy' );
} else {
$label = esc_html__( 'Free', 'academy' );
}
return wp_kses_post( apply_filters( 'academy/templates/loop/price', $label, $course_id ) );
}
/**
* Number of lessons in the course curriculum.
*
* @param int $course_id Course ID.
* @return int
*/
private static function lesson_count( $course_id ) {
$count = Helper::get_total_number_of_course_lesson( $course_id );
if ( is_array( $count ) ) {
$count = isset( $count['total_lessons'] ) ? $count['total_lessons'] : reset( $count );
}
return (int) $count;
}
/**
* "02:30:00" to "2 hr 30 mins", matching Helper::get_course_duration().
*
* @param string $clock Duration as HH:MM:SS.
* @return string
*/
private static function format_clock_duration( $clock ) {
$parts = array_map( 'intval', explode( ':', $clock ) );
if ( 3 !== count( $parts ) || 0 === array_sum( $parts ) ) {
return '';
}
$out = [];
if ( $parts[0] > 0 ) {
$out[] = $parts[0] . ' hr';
}
if ( $parts[1] > 0 ) {
$out[] = $parts[1] . ' mins';
}
if ( $parts[2] > 0 ) {
$out[] = $parts[2] . ' sec';
}
return implode( ' ', $out );
}
/**
* Inline SVG icon for course facts.
*
* @param string $name lessons|duration|level|enrolled.
* @return string
*/
public static function icon( $name ) {
$paths = [
'lessons' => '',
'duration' => '',
'level' => '',
'enrolled' => '',
];
if ( ! isset( $paths[ $name ] ) ) {
return '';
}
return '';
}
}