| 1 |
<?php |
| 2 |
/** |
| 3 |
* Template hooks Tab Course in Course Builder. |
| 4 |
* |
| 5 |
* @since 4.3.6 |
| 6 |
* @version 1.0.0 |
| 7 |
*/ |
| 8 |
|
| 9 |
namespace LearnPress\TemplateHooks\CourseBuilder\Course; |
| 10 |
|
| 11 |
use LearnPress\CourseBuilder\CourseBuilder; |
| 12 |
use LearnPress\Databases\DataBase; |
| 13 |
use LearnPress\Helpers\Singleton; |
| 14 |
use LearnPress\Helpers\Template; |
| 15 |
use LearnPress\Models\CourseModel; |
| 16 |
use LearnPress\Models\Courses; |
| 17 |
use LearnPress\Models\PostModel; |
| 18 |
use LearnPress\Models\UserModel; |
| 19 |
use LearnPress\Services\OpenAiService; |
| 20 |
use LearnPress\TemplateHooks\Admin\AI\AdminCreateCourseAITemplate; |
| 21 |
use LearnPress\TemplateHooks\Course\SingleCourseOfflineTemplate; |
| 22 |
use LearnPress\TemplateHooks\Course\SingleCourseTemplate; |
| 23 |
use LearnPress\TemplateHooks\CourseBuilder\CourseBuilderTemplate; |
| 24 |
use LP_Course_Filter; |
| 25 |
use LP_WP_Filesystem; |
| 26 |
use Throwable; |
| 27 |
|
| 28 |
class BuilderListCoursesTemplate { |
| 29 |
use Singleton; |
| 30 |
|
| 31 |
public function init() {} |
| 32 |
|
| 33 |
/** |
| 34 |
* HTML list courses on Course Builder screen |
| 35 |
* |
| 36 |
* @param array $data |
| 37 |
* |
| 38 |
* @since 4.3.6 |
| 39 |
* @version 1.0.1 |
| 40 |
* @return string |
| 41 |
*/ |
| 42 |
public function layout( array $data = [] ): string { |
| 43 |
$section = [ |
| 44 |
'header' => $this->html_header( $data ), |
| 45 |
'filter_bar' => $this->html_filter_bar(), |
| 46 |
'courses' => $this->tab_list_courses(), |
| 47 |
'ai_templates' => AdminCreateCourseAITemplate::instance()->render_for_frontend(), |
| 48 |
]; |
| 49 |
|
| 50 |
return Template::combine_components( $section ); |
| 51 |
} |
| 52 |
|
| 53 |
/** |
| 54 |
* HTML header |
| 55 |
* |
| 56 |
* @param array $data |
| 57 |
* |
| 58 |
* @return string |
| 59 |
*/ |
| 60 |
public function html_header( array $data = [] ): string { |
| 61 |
$btn_add_new = sprintf( |
| 62 |
'<a href="%s" class="cb-btn-add-new lp-button">%s</a>', |
| 63 |
esc_url( CourseBuilder::get_link_course_builder( 'courses/create' ) ), |
| 64 |
__( 'Add New Course', 'learnpress' ) |
| 65 |
); |
| 66 |
|
| 67 |
$enable_open_ai = OpenAiService::instance()->is_enable() |
| 68 |
&& ! empty( OpenAiService::instance()->get_secret_key() ); |
| 69 |
$ai_btn_class = $enable_open_ai ? 'lp-btn-generate-course-with-ai' : 'lp-btn-warning-enable-ai'; |
| 70 |
$btn_generate_ai = sprintf( |
| 71 |
'<button type="button" class="cb-btn-add-new %s"> |
| 72 |
<i class="lp-ico-ai"></i> %s |
| 73 |
</button>', |
| 74 |
esc_attr( $ai_btn_class ), |
| 75 |
esc_html__( 'Generate with AI', 'learnpress' ) |
| 76 |
); |
| 77 |
|
| 78 |
$header = [ |
| 79 |
'wrapper' => '<div class="cb-tab-header">', |
| 80 |
'title' => sprintf( '<h2 class="lp-cb-tab__title">%s</h2>', __( 'Courses', 'learnpress' ) ), |
| 81 |
'actions' => sprintf( |
| 82 |
'<div class="cb-tab-header-actions" style="display:flex;align-items:center;gap:8px;">%s%s</div>', |
| 83 |
$btn_add_new, |
| 84 |
$btn_generate_ai |
| 85 |
), |
| 86 |
'wrapper_end' => '</div>', |
| 87 |
]; |
| 88 |
|
| 89 |
return Template::combine_components( $header ); |
| 90 |
} |
| 91 |
|
| 92 |
/** |
| 93 |
* Render filter bar with search, status, items per page dropdowns |
| 94 |
* |
| 95 |
* @return string |
| 96 |
* @since 4.3.0 |
| 97 |
*/ |
| 98 |
public function html_filter_bar(): string { |
| 99 |
$args = lp_archive_skeleton_get_args(); |
| 100 |
$link_tab = CourseBuilder::get_tab_link( 'courses' ); |
| 101 |
|
| 102 |
// Current filter values |
| 103 |
$current_search = $args['c_search'] ?? ''; |
| 104 |
$current_status = $args['c_status'] ?? ''; |
| 105 |
$current_limit = $args['per_page'] ?? 20; |
| 106 |
|
| 107 |
// Status options |
| 108 |
$statuses = [ |
| 109 |
'' => __( 'All Status', 'learnpress' ), |
| 110 |
'publish' => __( 'Published', 'learnpress' ), |
| 111 |
'future' => __( 'Scheduled', 'learnpress' ), |
| 112 |
'draft' => __( 'Draft', 'learnpress' ), |
| 113 |
'pending' => __( 'Pending Review', 'learnpress' ), |
| 114 |
'private' => __( 'Private', 'learnpress' ), |
| 115 |
'trash' => __( 'Trash', 'learnpress' ), |
| 116 |
]; |
| 117 |
|
| 118 |
// Items per page options |
| 119 |
$per_page_options = [ 10, 20, 50 ]; |
| 120 |
|
| 121 |
// Build status dropdown HTML |
| 122 |
$status_options = ''; |
| 123 |
foreach ( $statuses as $value => $label ) { |
| 124 |
$selected = ( $current_status === $value ) ? 'selected' : ''; |
| 125 |
$status_options .= sprintf( '<option value="%s" %s>%s</option>', esc_attr( $value ), $selected, esc_html( $label ) ); |
| 126 |
} |
| 127 |
|
| 128 |
// Build per page dropdown HTML |
| 129 |
$per_page_html = ''; |
| 130 |
foreach ( $per_page_options as $option ) { |
| 131 |
$selected = ( (int) $current_limit === $option ) ? 'selected' : ''; |
| 132 |
$per_page_html .= sprintf( '<option value="%d" %s>%d</option>', $option, $selected, $option ); |
| 133 |
} |
| 134 |
|
| 135 |
// Row 2: Filter toolbar |
| 136 |
$filter = [ |
| 137 |
'wrapper' => sprintf( '<form class="cb-tab-filter-bar" method="get" action="%s">', esc_url( $link_tab ) ), |
| 138 |
'fields' => '<div class="cb-filter-fields">', |
| 139 |
'search' => sprintf( |
| 140 |
'<div class="cb-filter-group"> |
| 141 |
<label>%s</label> |
| 142 |
<div class="cb-filter-search"> |
| 143 |
<input type="search" name="c_search" placeholder="%s" value="%s"> |
| 144 |
</div> |
| 145 |
</div>', |
| 146 |
esc_html__( 'Search', 'learnpress' ), |
| 147 |
esc_attr__( 'Search by title', 'learnpress' ), |
| 148 |
esc_attr( $current_search ) |
| 149 |
), |
| 150 |
'status' => sprintf( |
| 151 |
'<div class="cb-filter-group"> |
| 152 |
<label>%s</label> |
| 153 |
<select name="c_status" class="cb-filter-select">%s</select> |
| 154 |
</div>', |
| 155 |
esc_html__( 'Status', 'learnpress' ), |
| 156 |
$status_options |
| 157 |
), |
| 158 |
'per_page' => sprintf( |
| 159 |
'<div class="cb-filter-group"> |
| 160 |
<label>%s</label> |
| 161 |
<select name="per_page" class="cb-filter-select">%s</select> |
| 162 |
</div>', |
| 163 |
esc_html__( 'Items per page', 'learnpress' ), |
| 164 |
$per_page_html |
| 165 |
), |
| 166 |
'actions' => '<div class="cb-filter-actions">', |
| 167 |
'filter_btn' => sprintf( '<button type="submit" class="cb-filter-btn">%s</button>', __( 'Filter', 'learnpress' ) ), |
| 168 |
'reset_btn' => sprintf( '<a href="%s" class="cb-filter-reset">%s</a>', esc_url( $link_tab ), __( 'Reset', 'learnpress' ) ), |
| 169 |
'actions_end' => '</div>', |
| 170 |
'fields_end' => '</div>', |
| 171 |
'wrapper_end' => '</form>', |
| 172 |
]; |
| 173 |
|
| 174 |
return Template::combine_components( $filter ); |
| 175 |
} |
| 176 |
|
| 177 |
public function tab_list_courses(): string { |
| 178 |
$content = ''; |
| 179 |
|
| 180 |
try { |
| 181 |
$user = UserModel::find( get_current_user_id(), true ); |
| 182 |
// Query courses of user |
| 183 |
$filter = new LP_Course_Filter(); |
| 184 |
$param = lp_archive_skeleton_get_args(); |
| 185 |
$param['id_url'] = 'tab-list-courses'; |
| 186 |
|
| 187 |
// Handle status filter - if empty, show all; otherwise filter by selected status |
| 188 |
$status_param = $param['c_status'] ?? ''; |
| 189 |
if ( empty( $status_param ) ) { |
| 190 |
$param['c_status'] = 'publish,future,private,draft,pending,trash'; |
| 191 |
} |
| 192 |
|
| 193 |
Courses::handle_params_for_query_courses( $filter, $param ); |
| 194 |
// Admin can view all courses in Course Builder. |
| 195 |
if ( ! current_user_can( ADMIN_ROLE ) ) { |
| 196 |
$filter->post_author = $user->get_id(); |
| 197 |
} |
| 198 |
|
| 199 |
// Handle per_page parameter |
| 200 |
$per_page = isset( $param['per_page'] ) ? absint( $param['per_page'] ) : 20; |
| 201 |
$filter->limit = $per_page > 0 ? $per_page : 20; |
| 202 |
|
| 203 |
$filter->page = $GLOBALS['wp_query']->get( 'paged', 1 ) ? $GLOBALS['wp_query']->get( 'paged', 1 ) : 1; |
| 204 |
$filter = apply_filters( 'lp/course-builder/courses/query/filter', $filter, [] ); |
| 205 |
|
| 206 |
$total_courses = 0; |
| 207 |
$courses = Courses::get_courses( $filter, $total_courses ); |
| 208 |
if ( ! empty( $courses ) ) { |
| 209 |
$html_courses = $this->list_courses( $courses ); |
| 210 |
} else { |
| 211 |
$html_courses = Template::print_message( |
| 212 |
sprintf( __( 'No courses found', 'learnpress' ) ), |
| 213 |
'info', |
| 214 |
false |
| 215 |
); |
| 216 |
} |
| 217 |
|
| 218 |
$data_pagination = [ |
| 219 |
'paged' => $filter->page, |
| 220 |
'total_pages' => DataBase::get_total_pages( $filter->limit, $total_courses ), |
| 221 |
]; |
| 222 |
|
| 223 |
$sections = apply_filters( |
| 224 |
'learn-press/course-builder/courses/sections', |
| 225 |
[ |
| 226 |
'wrapper' => '<div class="courses-builder__course-tab learn-press-courses">', |
| 227 |
'courses' => $html_courses, |
| 228 |
'pagination' => Template::instance()->html_pagination( $data_pagination ), |
| 229 |
'wrapper_end' => '</div>', |
| 230 |
], |
| 231 |
$courses, |
| 232 |
$user |
| 233 |
); |
| 234 |
|
| 235 |
$content = Template::combine_components( $sections ); |
| 236 |
} catch ( Throwable $e ) { |
| 237 |
error_log( __METHOD__ . ': ' . $e->getMessage() ); |
| 238 |
} |
| 239 |
|
| 240 |
return $content; |
| 241 |
} |
| 242 |
|
| 243 |
/** |
| 244 |
* Display list courses. |
| 245 |
* |
| 246 |
* @param $instructor |
| 247 |
* @param $courses |
| 248 |
* |
| 249 |
* @return string |
| 250 |
*/ |
| 251 |
public function list_courses( $courses ): string { |
| 252 |
$content = ''; |
| 253 |
|
| 254 |
try { |
| 255 |
$html_list_course = ''; |
| 256 |
foreach ( $courses as $course_obj ) { |
| 257 |
// Read fresh model to avoid stale post_status in long-lived cache, |
| 258 |
// especially around future -> publish transitions. |
| 259 |
$course = CourseModel::find( $course_obj->ID, false ); |
| 260 |
$html_list_course .= self::render_course( $course ); |
| 261 |
} |
| 262 |
|
| 263 |
$sections = [ |
| 264 |
'wrapper' => '<ul class="cb-list-course">', |
| 265 |
'list_course' => $html_list_course, |
| 266 |
'wrapper_end' => '</ul>', |
| 267 |
]; |
| 268 |
|
| 269 |
$content = Template::combine_components( $sections ); |
| 270 |
} catch ( Throwable $e ) { |
| 271 |
error_log( __METHOD__ . ': ' . $e->getMessage() ); |
| 272 |
} |
| 273 |
|
| 274 |
return $content; |
| 275 |
} |
| 276 |
|
| 277 |
/** |
| 278 |
* Render course in course builder |
| 279 |
* |
| 280 |
* @param CourseModel $courseModel |
| 281 |
* @param array $settings |
| 282 |
* |
| 283 |
* @return string |
| 284 |
* @since 4.3.6 |
| 285 |
* @version 1.0.0 |
| 286 |
*/ |
| 287 |
public static function render_course( CourseModel $courseModel, array $settings = [] ): string { |
| 288 |
$singleCourseTemplate = SingleCourseTemplate::instance(); |
| 289 |
|
| 290 |
try { |
| 291 |
$course_id = $courseModel->get_id(); |
| 292 |
$course_status = $courseModel->get_status(); |
| 293 |
$edit_link = CourseBuilder::get_link_course_builder( CourseBuilderTemplate::MENU_COURSES . "/{$course_id}" ); |
| 294 |
|
| 295 |
// Offline badge overlay |
| 296 |
$offline_badge = ''; |
| 297 |
if ( $courseModel->is_offline() ) { |
| 298 |
$offline_badge = '<span class="cb-item-status-badge offline">Offline</span>'; |
| 299 |
} |
| 300 |
|
| 301 |
$html_img = apply_filters( |
| 302 |
'learn-press/course-builder/list-courses/item/section-top', |
| 303 |
[ |
| 304 |
'wrapper' => '<div class="course-thumbnail">', |
| 305 |
'link' => sprintf( '<a href="%s">', $edit_link ), |
| 306 |
'img' => $singleCourseTemplate->html_image( $courseModel ), |
| 307 |
'badge' => $offline_badge, |
| 308 |
'link_end' => '</a>', |
| 309 |
'wrapper_end' => '</div>', |
| 310 |
], |
| 311 |
$courseModel, |
| 312 |
$settings |
| 313 |
); |
| 314 |
|
| 315 |
// Icon mapping for meta items (using LearnPress frontend icons) |
| 316 |
$meta_icons = [ |
| 317 |
'lesson' => '<i class="lp-icon-file-o"></i>', |
| 318 |
'student' => '<i class="lp-icon-user-graduate"></i>', |
| 319 |
'duration' => '<i class="lp-icon-clock-o"></i>', |
| 320 |
'level' => '<i class="lp-icon-signal"></i>', |
| 321 |
'quiz' => '<i class="lp-icon-puzzle-piece"></i>', |
| 322 |
'address' => '<i class="lp-icon-map-marker"></i>', |
| 323 |
]; |
| 324 |
|
| 325 |
$meta_data = apply_filters( |
| 326 |
'learn-press/course-builder/list-courses/item/meta-data', |
| 327 |
[ |
| 328 |
'duration' => $singleCourseTemplate->html_duration( $courseModel ), |
| 329 |
'level' => $singleCourseTemplate->html_level( $courseModel ), |
| 330 |
'lesson' => $singleCourseTemplate->html_count_item( $courseModel, LP_LESSON_CPT ), |
| 331 |
'quiz' => $singleCourseTemplate->html_count_item( $courseModel, LP_QUIZ_CPT ), |
| 332 |
'student' => $singleCourseTemplate->html_count_student( $courseModel ), |
| 333 |
], |
| 334 |
$courseModel, |
| 335 |
$settings |
| 336 |
); |
| 337 |
|
| 338 |
if ( $courseModel->is_offline() ) { |
| 339 |
$singleCourseOfflineTemplate = SingleCourseOfflineTemplate::instance(); |
| 340 |
unset( $meta_data['quiz'] ); |
| 341 |
unset( $meta_data['student'] ); |
| 342 |
if ( ! empty( $meta_data['lesson'] ) ) { |
| 343 |
$meta_data['lesson'] = $singleCourseOfflineTemplate->html_lesson_info( $courseModel, true ); |
| 344 |
} |
| 345 |
|
| 346 |
$html_address = $singleCourseOfflineTemplate->html_address( $courseModel ); |
| 347 |
if ( ! empty( $html_address ) ) { |
| 348 |
$meta_data['address'] = $singleCourseOfflineTemplate->html_address( $courseModel ); |
| 349 |
} |
| 350 |
} |
| 351 |
|
| 352 |
$html_meta_data = ''; |
| 353 |
if ( ! empty( $meta_data ) ) { |
| 354 |
foreach ( $meta_data as $k => $v ) { |
| 355 |
$icon = $meta_icons[ $k ] ?? ''; |
| 356 |
$html_meta_data .= sprintf( '<div class="meta-item meta-item-%s">%s%s</div>', $k, $icon, $v ); |
| 357 |
} |
| 358 |
|
| 359 |
$html_meta_data = sprintf( '<div class="course-wrap-meta">%s</div>', $html_meta_data ); |
| 360 |
} |
| 361 |
|
| 362 |
$status_label = 'future' === $course_status ? __( 'Scheduled', 'learnpress' ) : $course_status; |
| 363 |
$html_status = sprintf( |
| 364 |
'<div class="course-status %1$s"> |
| 365 |
<span>%2$s</span> |
| 366 |
</div>', |
| 367 |
esc_attr( $course_status ), |
| 368 |
esc_html( $status_label ) |
| 369 |
); |
| 370 |
|
| 371 |
// Price |
| 372 |
$html_price = sprintf( '<div class="course-item-price-wrap">%s</div>', $singleCourseTemplate->html_price( $courseModel ) ); |
| 373 |
|
| 374 |
// Categories |
| 375 |
$html_categories = ''; |
| 376 |
$categories = $courseModel->get_categories(); |
| 377 |
if ( ! empty( $categories ) ) { |
| 378 |
$cat_names = array(); |
| 379 |
foreach ( $categories as $cat ) { |
| 380 |
$cat_name = is_object( $cat ) ? $cat->name : ( is_array( $cat ) ? ( $cat['name'] ?? '' ) : '' ); |
| 381 |
if ( ! empty( $cat_name ) ) { |
| 382 |
$cat_names[] = sprintf( '<span class="course-category-name">%s</span>', esc_html( $cat_name ) ); |
| 383 |
} |
| 384 |
} |
| 385 |
if ( ! empty( $cat_names ) ) { |
| 386 |
$html_categories = sprintf( |
| 387 |
' <span class="course-categories-label">%s</span> %s', |
| 388 |
__( 'in', 'learnpress' ), |
| 389 |
implode( ', ', $cat_names ) |
| 390 |
); |
| 391 |
} |
| 392 |
} |
| 393 |
|
| 394 |
// Last Updated |
| 395 |
$post_obj = get_post( $courseModel->get_id() ); |
| 396 |
$html_last_updated = ''; |
| 397 |
if ( $post_obj && ! empty( $post_obj->post_modified ) ) { |
| 398 |
$modified_time = strtotime( $post_obj->post_modified ); |
| 399 |
$html_last_updated = sprintf( |
| 400 |
'<div class="course-last-updated">%s %s</div>', |
| 401 |
__( 'Last Updated on', 'learnpress' ), |
| 402 |
date_i18n( 'Y/m/d \a\t g:i a', $modified_time ) |
| 403 |
); |
| 404 |
} |
| 405 |
|
| 406 |
$section_bottom_end = apply_filters( |
| 407 |
'learn-press/course-builder/list-courses/item/section/bottom/end', |
| 408 |
[ |
| 409 |
'wrapper' => '<div class="course-bottom">', |
| 410 |
'price' => $html_price, |
| 411 |
'status' => $html_status, |
| 412 |
'last_updated' => $html_last_updated, |
| 413 |
'wrapper_end' => '</div>', |
| 414 |
], |
| 415 |
$courseModel, |
| 416 |
$settings |
| 417 |
); |
| 418 |
|
| 419 |
// Instructor with categories |
| 420 |
$instructor = $courseModel->get_author_model(); |
| 421 |
$instructor_name = $instructor ? $instructor->get_display_name() : ''; |
| 422 |
$html_instructor_category = ''; |
| 423 |
if ( ! empty( $instructor_name ) ) { |
| 424 |
$html_instructor_category = sprintf( |
| 425 |
'<div class="course-instructor-category"><div><span class="course-by-label">%s</span> <span class="instructor-display-name">%s</span>%s</div></div>', |
| 426 |
__( 'by', 'learnpress' ), |
| 427 |
esc_html( $instructor_name ), |
| 428 |
$html_categories |
| 429 |
); |
| 430 |
} |
| 431 |
|
| 432 |
$html_content = apply_filters( |
| 433 |
'learn-press/course-builder/list-courses/item/section/bottom', |
| 434 |
[ |
| 435 |
'wrapper' => '<div class="course-content">', |
| 436 |
'title' => sprintf( |
| 437 |
'<h3 class="wap-course-title"><a href="%s">%s</a></h3>', |
| 438 |
$edit_link, |
| 439 |
$singleCourseTemplate->html_title( $courseModel ) |
| 440 |
), |
| 441 |
'instructor' => $html_instructor_category, |
| 442 |
'meta' => $html_meta_data, |
| 443 |
'info' => Template::combine_components( $section_bottom_end ), |
| 444 |
'wrapper_end' => '</div>', |
| 445 |
], |
| 446 |
$courseModel, |
| 447 |
$settings |
| 448 |
); |
| 449 |
|
| 450 |
$more_actions_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-more.svg' ); |
| 451 |
|
| 452 |
// Set action by status |
| 453 |
$action_by_status = []; |
| 454 |
if ( $course_status === PostModel::STATUS_TRASH ) { |
| 455 |
$action_by_status['restore'] = __( 'Restore', 'learnpress' ); |
| 456 |
$action_by_status['delete'] = __( 'Delete permanently', 'learnpress' ); |
| 457 |
} else { |
| 458 |
$action_by_status['duplicate'] = __( 'Duplicate', 'learnpress' ); |
| 459 |
$action_by_status[ PostModel::STATUS_PUBLISH ] = __( 'Publish', 'learnpress' ); |
| 460 |
$action_by_status[ PostModel::STATUS_PENDING ] = __( 'Pending Review', 'learnpress' ); |
| 461 |
$action_by_status[ PostModel::STATUS_DRAFT ] = __( 'Draft', 'learnpress' ); |
| 462 |
$action_by_status[ PostModel::STATUS_TRASH ] = __( 'Trash', 'learnpress' ); |
| 463 |
} |
| 464 |
// Unset current status on action |
| 465 |
unset( $action_by_status[ $course_status ] ); |
| 466 |
|
| 467 |
$html_action_by_status = ''; |
| 468 |
|
| 469 |
$data_send_action = [ |
| 470 |
'id_url' => 'cb-quick-edit-action', |
| 471 |
'action' => 'cb_quick_edit_save_course', |
| 472 |
'course_id' => $courseModel->get_id(), |
| 473 |
]; |
| 474 |
|
| 475 |
foreach ( $action_by_status as $action_status => $action_label ) { |
| 476 |
$html_action_by_status .= sprintf( |
| 477 |
'<span class="lp-cb-item-action %s" data-send="%s">%s</span>', |
| 478 |
$action_status, |
| 479 |
Template::convert_data_to_json( $data_send_action + [ 'action_type' => $action_status ] ), |
| 480 |
$action_label |
| 481 |
); |
| 482 |
} |
| 483 |
|
| 484 |
$html_action = apply_filters( |
| 485 |
'learn-press/course-builder/list-courses/item/action', |
| 486 |
[ |
| 487 |
'wrapper' => '<div class="lp-cb-item-action-wrap">', |
| 488 |
'edit' => $course_status !== PostModel::STATUS_TRASH ? sprintf( |
| 489 |
'<div class="course-action-editor"><a class="btn-edit-course course-edit-permalink" href="%s">%s</a></div>', |
| 490 |
$edit_link, |
| 491 |
__( 'Edit', 'learnpress' ) |
| 492 |
) : '', |
| 493 |
'action_expanded_button' => sprintf( |
| 494 |
'<div class="lp-cb-item-action-expand-toggle lp-button">%s</div>', |
| 495 |
$more_actions_icon |
| 496 |
), |
| 497 |
'action_wrap' => '<div class="lp-cb-item-action-expand lp-hidden">', |
| 498 |
'action' => $html_action_by_status, |
| 499 |
'action_wrap_end' => '</div>', |
| 500 |
'wrapper_end' => '</div>', |
| 501 |
], |
| 502 |
$courseModel, |
| 503 |
$settings |
| 504 |
); |
| 505 |
|
| 506 |
$section = apply_filters( |
| 507 |
'learn-press/course-builder/list-courses/item-li', |
| 508 |
[ |
| 509 |
'wrapper_li' => '<li class="course">', |
| 510 |
'wrapper_div' => sprintf( '<div class="course-item" data-course-id="%s">', $courseModel->get_id() ), |
| 511 |
'media' => Template::combine_components( $html_img ), |
| 512 |
'course_info' => Template::combine_components( $html_content ), |
| 513 |
'course_action' => Template::combine_components( $html_action ), |
| 514 |
'wrapper_div_end' => '</div>', |
| 515 |
'wrapper_li_end' => '</li>', |
| 516 |
], |
| 517 |
$courseModel, |
| 518 |
$settings |
| 519 |
); |
| 520 |
|
| 521 |
$html_item = Template::combine_components( $section ); |
| 522 |
} catch ( Throwable $e ) { |
| 523 |
$html_item = $e->getMessage(); |
| 524 |
} |
| 525 |
|
| 526 |
return $html_item; |
| 527 |
} |
| 528 |
} |
| 529 |
|