$this->html_header( $data ),
'filter_bar' => $this->html_filter_bar(),
'courses' => $this->tab_list_courses(),
'ai_templates' => AdminCreateCourseAITemplate::instance()->render_for_frontend(),
];
return Template::combine_components( $section );
}
/**
* HTML header
*
* @param array $data
*
* @return string
*/
public function html_header( array $data = [] ): string {
$btn_add_new = sprintf(
'%s',
esc_url( CourseBuilder::get_link_course_builder( 'courses/create' ) ),
__( 'Add New Course', 'learnpress' )
);
$enable_open_ai = OpenAiService::instance()->is_enable()
&& ! empty( OpenAiService::instance()->get_secret_key() );
$ai_btn_class = $enable_open_ai ? 'lp-btn-generate-course-with-ai' : 'lp-btn-warning-enable-ai';
$btn_generate_ai = sprintf(
'',
esc_attr( $ai_btn_class ),
esc_html__( 'Generate with AI', 'learnpress' )
);
$header = [
'wrapper' => '
',
];
return Template::combine_components( $header );
}
/**
* Render filter bar with search, status, items per page dropdowns
*
* @return string
* @since 4.3.0
*/
public function html_filter_bar(): string {
$args = lp_archive_skeleton_get_args();
$link_tab = CourseBuilder::get_tab_link( 'courses' );
// Current filter values
$current_search = $args['c_search'] ?? '';
$current_status = $args['c_status'] ?? '';
$current_limit = $args['per_page'] ?? 20;
// Status options
$statuses = [
'' => __( 'All Status', 'learnpress' ),
'publish' => __( 'Published', 'learnpress' ),
'future' => __( 'Scheduled', 'learnpress' ),
'draft' => __( 'Draft', 'learnpress' ),
'pending' => __( 'Pending Review', 'learnpress' ),
'private' => __( 'Private', 'learnpress' ),
'trash' => __( 'Trash', 'learnpress' ),
];
// Items per page options
$per_page_options = [ 10, 20, 50 ];
// Build status dropdown HTML
$status_options = '';
foreach ( $statuses as $value => $label ) {
$selected = ( $current_status === $value ) ? 'selected' : '';
$status_options .= sprintf( '', esc_attr( $value ), $selected, esc_html( $label ) );
}
// Build per page dropdown HTML
$per_page_html = '';
foreach ( $per_page_options as $option ) {
$selected = ( (int) $current_limit === $option ) ? 'selected' : '';
$per_page_html .= sprintf( '', $option, $selected, $option );
}
// Row 2: Filter toolbar
$filter = [
'wrapper' => sprintf( '',
];
return Template::combine_components( $filter );
}
public function tab_list_courses(): string {
$content = '';
try {
$user = UserModel::find( get_current_user_id(), true );
// Query courses of user
$filter = new LP_Course_Filter();
$param = lp_archive_skeleton_get_args();
$param['id_url'] = 'tab-list-courses';
// Handle status filter - if empty, show all; otherwise filter by selected status
$status_param = $param['c_status'] ?? '';
if ( empty( $status_param ) ) {
$param['c_status'] = 'publish,future,private,draft,pending,trash';
}
Courses::handle_params_for_query_courses( $filter, $param );
// Admin can view all courses in Course Builder.
if ( ! current_user_can( ADMIN_ROLE ) ) {
$filter->post_author = $user->get_id();
}
// Handle per_page parameter
$per_page = isset( $param['per_page'] ) ? absint( $param['per_page'] ) : 20;
$filter->limit = $per_page > 0 ? $per_page : 20;
$filter->page = $GLOBALS['wp_query']->get( 'paged', 1 ) ? $GLOBALS['wp_query']->get( 'paged', 1 ) : 1;
$filter = apply_filters( 'lp/course-builder/courses/query/filter', $filter, [] );
$total_courses = 0;
$courses = Courses::get_courses( $filter, $total_courses );
if ( ! empty( $courses ) ) {
$html_courses = $this->list_courses( $courses );
} else {
$html_courses = Template::print_message(
sprintf( __( 'No courses found', 'learnpress' ) ),
'info',
false
);
}
$data_pagination = [
'paged' => $filter->page,
'total_pages' => DataBase::get_total_pages( $filter->limit, $total_courses ),
];
$sections = apply_filters(
'learn-press/course-builder/courses/sections',
[
'wrapper' => '',
'courses' => $html_courses,
'pagination' => Template::instance()->html_pagination( $data_pagination ),
'wrapper_end' => '
',
],
$courses,
$user
);
$content = Template::combine_components( $sections );
} catch ( Throwable $e ) {
error_log( __METHOD__ . ': ' . $e->getMessage() );
}
return $content;
}
/**
* Display list courses.
*
* @param $instructor
* @param $courses
*
* @return string
*/
public function list_courses( $courses ): string {
$content = '';
try {
$html_list_course = '';
foreach ( $courses as $course_obj ) {
// Read fresh model to avoid stale post_status in long-lived cache,
// especially around future -> publish transitions.
$course = CourseModel::find( $course_obj->ID, false );
$html_list_course .= self::render_course( $course );
}
$sections = [
'wrapper' => '',
'list_course' => $html_list_course,
'wrapper_end' => '
',
];
$content = Template::combine_components( $sections );
} catch ( Throwable $e ) {
error_log( __METHOD__ . ': ' . $e->getMessage() );
}
return $content;
}
/**
* Render course in course builder
*
* @param CourseModel $courseModel
* @param array $settings
*
* @return string
* @since 4.3.6
* @version 1.0.0
*/
public static function render_course( CourseModel $courseModel, array $settings = [] ): string {
$singleCourseTemplate = SingleCourseTemplate::instance();
try {
$course_id = $courseModel->get_id();
$course_status = $courseModel->get_status();
$edit_link = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_COURSES . "/{$course_id}" );
// Offline badge overlay
$offline_badge = '';
if ( $courseModel->is_offline() ) {
$offline_badge = 'Offline';
}
$html_img = apply_filters(
'learn-press/course-builder/list-courses/item/section-top',
[
'wrapper' => '',
],
$courseModel,
$settings
);
// Icon mapping for meta items (using LearnPress frontend icons)
$meta_icons = [
'lesson' => '',
'student' => '',
'duration' => '',
'level' => '',
'quiz' => '',
'address' => '',
];
$meta_data = apply_filters(
'learn-press/course-builder/list-courses/item/meta-data',
[
'duration' => $singleCourseTemplate->html_duration( $courseModel ),
'level' => $singleCourseTemplate->html_level( $courseModel ),
'lesson' => $singleCourseTemplate->html_count_item( $courseModel, LP_LESSON_CPT ),
'quiz' => $singleCourseTemplate->html_count_item( $courseModel, LP_QUIZ_CPT ),
'student' => $singleCourseTemplate->html_count_student( $courseModel ),
],
$courseModel,
$settings
);
if ( $courseModel->is_offline() ) {
$singleCourseOfflineTemplate = SingleCourseOfflineTemplate::instance();
unset( $meta_data['quiz'] );
unset( $meta_data['student'] );
if ( ! empty( $meta_data['lesson'] ) ) {
$meta_data['lesson'] = $singleCourseOfflineTemplate->html_lesson_info( $courseModel, true );
}
$html_address = $singleCourseOfflineTemplate->html_address( $courseModel );
if ( ! empty( $html_address ) ) {
$meta_data['address'] = $singleCourseOfflineTemplate->html_address( $courseModel );
}
}
$html_meta_data = '';
if ( ! empty( $meta_data ) ) {
foreach ( $meta_data as $k => $v ) {
$icon = $meta_icons[ $k ] ?? '';
$html_meta_data .= sprintf( '%s%s
', $k, $icon, $v );
}
$html_meta_data = sprintf( '%s
', $html_meta_data );
}
$status_label = 'future' === $course_status ? __( 'Scheduled', 'learnpress' ) : $course_status;
$html_status = sprintf(
'
%2$s
',
esc_attr( $course_status ),
esc_html( $status_label )
);
// Price
$html_price = sprintf( '%s
', $singleCourseTemplate->html_price( $courseModel ) );
// Categories
$html_categories = '';
$categories = $courseModel->get_categories();
if ( ! empty( $categories ) ) {
$cat_names = array();
foreach ( $categories as $cat ) {
$cat_name = is_object( $cat ) ? $cat->name : ( is_array( $cat ) ? ( $cat['name'] ?? '' ) : '' );
if ( ! empty( $cat_name ) ) {
$cat_names[] = sprintf( '%s', esc_html( $cat_name ) );
}
}
if ( ! empty( $cat_names ) ) {
$html_categories = sprintf(
' %s %s',
__( 'in', 'learnpress' ),
implode( ', ', $cat_names )
);
}
}
// Last Updated
$post_obj = get_post( $courseModel->get_id() );
$html_last_updated = '';
if ( $post_obj && ! empty( $post_obj->post_modified ) ) {
$modified_time = strtotime( $post_obj->post_modified );
$html_last_updated = sprintf(
'%s %s
',
__( 'Last Updated on', 'learnpress' ),
date_i18n( 'Y/m/d \a\t g:i a', $modified_time )
);
}
$section_bottom_end = apply_filters(
'learn-press/course-builder/list-courses/item/section/bottom/end',
[
'wrapper' => '',
'price' => $html_price,
'status' => $html_status,
'last_updated' => $html_last_updated,
'wrapper_end' => '
',
],
$courseModel,
$settings
);
// Instructor with categories
$instructor = $courseModel->get_author_model();
$instructor_name = $instructor ? $instructor->get_display_name() : '';
$html_instructor_category = '';
if ( ! empty( $instructor_name ) ) {
$html_instructor_category = sprintf(
'',
__( 'by', 'learnpress' ),
esc_html( $instructor_name ),
$html_categories
);
}
$html_content = apply_filters(
'learn-press/course-builder/list-courses/item/section/bottom',
[
'wrapper' => '',
'title' => sprintf(
'
',
$edit_link,
$singleCourseTemplate->html_title( $courseModel )
),
'instructor' => $html_instructor_category,
'meta' => $html_meta_data,
'info' => Template::combine_components( $section_bottom_end ),
'wrapper_end' => '
',
],
$courseModel,
$settings
);
$more_actions_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-more.svg' );
// Set action by status
$action_by_status = [];
if ( $course_status === PostModel::STATUS_TRASH ) {
$action_by_status['restore'] = __( 'Restore', 'learnpress' );
$action_by_status['delete'] = __( 'Delete permanently', 'learnpress' );
} else {
$action_by_status['duplicate'] = __( 'Duplicate', 'learnpress' );
$action_by_status[ PostModel::STATUS_PUBLISH ] = __( 'Publish', 'learnpress' );
$action_by_status[ PostModel::STATUS_PENDING ] = __( 'Pending Review', 'learnpress' );
$action_by_status[ PostModel::STATUS_DRAFT ] = __( 'Draft', 'learnpress' );
$action_by_status[ PostModel::STATUS_TRASH ] = __( 'Trash', 'learnpress' );
}
// Unset current status on action
unset( $action_by_status[ $course_status ] );
$html_action_by_status = '';
$data_send_action = [
'id_url' => 'cb-quick-edit-action',
'action' => 'cb_quick_edit_save_course',
'course_id' => $courseModel->get_id(),
];
foreach ( $action_by_status as $action_status => $action_label ) {
$html_action_by_status .= sprintf(
'%s',
$action_status,
Template::convert_data_to_json( $data_send_action + [ 'action_type' => $action_status ] ),
$action_label
);
}
$html_action = apply_filters(
'learn-press/course-builder/list-courses/item/action',
[
'wrapper' => '',
'edit' => $course_status !== PostModel::STATUS_TRASH ? sprintf(
'
',
$edit_link,
__( 'Edit', 'learnpress' )
) : '',
'action_expanded_button' => sprintf(
'
%s
',
$more_actions_icon
),
'action_wrap' => '
',
'action' => $html_action_by_status,
'action_wrap_end' => '
',
'wrapper_end' => '
',
],
$courseModel,
$settings
);
$section = apply_filters(
'learn-press/course-builder/list-courses/item-li',
[
'wrapper_li' => '',
'wrapper_div' => sprintf( '', $courseModel->get_id() ),
'media' => Template::combine_components( $html_img ),
'course_info' => Template::combine_components( $html_content ),
'course_action' => Template::combine_components( $html_action ),
'wrapper_div_end' => '
',
'wrapper_li_end' => '',
],
$courseModel,
$settings
);
$html_item = Template::combine_components( $section );
} catch ( Throwable $e ) {
$html_item = $e->getMessage();
}
return $html_item;
}
}