PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 All 142 releases
learnpress / inc / TemplateHooks / Admin / AdminListStudentsEnrolled.php

AdminListStudentsEnrolled.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.10, at inc/TemplateHooks/Admin/AdminListStudentsEnrolled.php

716 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace LearnPress\TemplateHooks\Admin;
4
5 use Exception;
6 use LearnPress\Databases\UserItemsDB;
7 use LearnPress\Filters\UserItemsFilter;
8 use LearnPress\Helpers\Singleton;
9 use LearnPress\Helpers\Template;
10 use LearnPress\Models\CourseModel;
11 use LearnPress\Models\UserItems\UserCourseModel;
12 use LearnPress\Models\UserItems\UserItemModel;
13 use LearnPress\Models\UserModel;
14 use LearnPress\TemplateHooks\Instructor\SingleInstructorTemplate;
15 use LearnPress\TemplateHooks\Table\TableListTemplate;
16 use LearnPress\TemplateHooks\TemplateAJAX;
17 use LearnPress\TemplateHooks\UserItem\UserCourseTemplate;
18 use LP_Debug;
19 use LP_Helper;
20 use LP_Page_Controller;
21 use LP_Request;
22 use stdClass;
23 use Throwable;
24
25 /**
26 * Template Admin List Students Enrolled.
27 *
28 * Displays enrolled students for Admin (all courses) and Instructor (own courses only).
29 * Provides WP Admin submenu + Frontend profile tab.
30 *
31 * @since 4.3.3
32 * @version 1.0.1
33 */
34 class AdminListStudentsEnrolled {
35 use Singleton;
36
37 const PER_PAGE = 10;
38
39 public function init(): void {
40 // 2. Whitelist AJAX callback.
41 add_filter( 'lp/rest/ajax/allow_callback', array( $this, 'allow_callback' ) );
42 // 4. Render a modal toolbar template from PHP (used by JS modal).
43 add_action( 'admin_footer', array( $this, 'print_modal_toolbar_template' ) );
44 add_action( 'wp_footer', array( $this, 'print_modal_toolbar_template' ) );
45 }
46
47 /**
48 * Admin page output callback.
49 */
50 public function admin_page_output() {
51 ob_start();
52 $this->enrolled_students_layout();
53 $content = ob_get_clean();
54
55 echo AdminTemplate::html_on_wp_admin_screen(
56 array(
57 'content' => $content,
58 'title' => __( 'Students', 'learnpress' ),
59 'id' => 'lp-enrolled-students',
60 )
61 );
62 }
63
64 /**
65 * Allow callback for AJAX.
66 *
67 * @param array $callbacks
68 *
69 * @return array
70 */
71 public function allow_callback( array $callbacks ): array {
72 $callbacks[] = get_class( $this ) . ':render_enrolled_students';
73
74 return $callbacks;
75 }
76
77 /**
78 * Render initial layout with TemplateAJAX::load_content_via_ajax().
79 */
80 public function enrolled_students_layout() {
81 try {
82 $page_current = '';
83 if ( function_exists( 'get_current_screen' ) ) {
84 $wp_screen = get_current_screen();
85 if ( $wp_screen ) {
86 // Page on the Admin screen.
87 if ( $wp_screen->id === 'learnpress_page_learn-press-students-enrolled' ) {
88 $page_current = 'learnpress_page_learn-press-students-enrolled';
89 }
90 }
91 }
92
93 $args = array(
94 'id_url' => 'lp-enrolled-students',
95 'course_id' => LP_Request::get_param( 'course_id', 0 ),
96 'course_name' => LP_Request::get_param( 'course_name' ),
97 'paged' => 1,
98 'search' => lp_request::get_param( 'search' ),
99 'start_date' => LP_Request::get_param( 'start_date' ),
100 'end_date' => LP_Request::get_param( 'end_date' ),
101 'enableUpdateParamsUrl' => false,
102 );
103
104 /** @use self::render_enrolled_students $call_back */
105 $call_back = array(
106 'class' => self::class,
107 'method' => 'render_enrolled_students',
108 );
109
110 $section = [
111 'wrap' => sprintf(
112 '<div class="lp-students-enrolled-layout %s">',
113 $page_current
114 ),
115 'toolbar' => $this->html_toolbar(),
116 'table' => TemplateAJAX::load_content_via_ajax( $args, $call_back ),
117 'wrap-end' => '</div>',
118 ];
119
120 echo Template::combine_components( $section );
121 } catch ( Throwable $e ) {
122 Template::print_message( $e->getMessage(), 'error' );
123 }
124 }
125
126 /**
127 * Static render method called by AJAX — returns stdClass{ content }.
128 *
129 * Permission check: Admin=full, Instructor=own courses only.
130 *
131 * @param array $data
132 *
133 * @return stdClass
134 */
135 public static function render_enrolled_students( array $data ): stdClass {
136 $content = new stdClass();
137 $content->content = '';
138
139 try {
140 // Check permission
141 if ( ! current_user_can( UserModel::ROLE_ADMINISTRATOR )
142 && ! current_user_can( UserModel::ROLE_INSTRUCTOR ) ) {
143 throw new Exception( esc_html__( 'You do not have permission to view enrolled students.', 'learnpress' ) );
144 }
145
146 $course_id = abs( LP_Helper::sanitize_params_submitted( $data['course_id'] ?? 0, 'int' ) );
147 $paged = max( 1, abs( LP_Helper::sanitize_params_submitted( $data['paged'] ?? 1, 'int' ) ) );
148 $course_name = LP_Helper::sanitize_params_submitted( $data['course_name'] ?? '' );
149 $search = LP_Helper::sanitize_params_submitted( $data['search'] ?? '' );
150 $start_date = lp_helper::sanitize_params_submitted( $data['start_date'] ?? '' );
151 $end_date = lp_helper::sanitize_params_submitted( $data['end_date'] ?? '' );
152 $per_page = self::PER_PAGE;
153
154 // Normalize date range if request is reversed.
155 if ( $start_date && $end_date && strtotime( $start_date ) > strtotime( $end_date ) ) {
156 $tmp = $start_date;
157 $start_date = $end_date;
158 $end_date = $tmp;
159 }
160
161 $lp_db_user_items = UserItemsDB::getInstance();
162 $filter = new UserItemsFilter();
163 $filter->item_type = LP_COURSE_CPT;
164 $filter->statues = array( 'enrolled', 'finished' );
165 $filter->limit = $per_page;
166 $filter->page = $paged;
167 $filter->order_by = 'ui.start_time';
168 $filter->order = 'DESC';
169 $filter->field_count = 'ui.user_item_id';
170 $filter->only_fields = array(
171 'ui.user_item_id',
172 'ui.user_id',
173 'ui.item_id',
174 'ui.start_time',
175 'ui.status',
176 'ui.graduation',
177 'u.display_name',
178 'u.user_email',
179 'p.post_title AS course_title',
180 );
181 $filter->join[] = "JOIN {$lp_db_user_items->wpdb->posts} p ON ui.item_id = p.ID";
182 $filter->join[] = "JOIN {$lp_db_user_items->wpdb->users} u ON ui.user_id = u.ID";
183
184 $instructor_id = get_current_user_id();
185 if ( $instructor_id > 0 && current_user_can( UserModel::ROLE_INSTRUCTOR ) ) {
186 $filter->where[] = $lp_db_user_items->wpdb->prepare( 'AND p.post_author = %d', $instructor_id );
187 }
188
189 if ( $course_id > 0 ) {
190 $filter->item_id = $course_id;
191 } elseif ( ! empty( $course_name ) ) {
192 $course_name_like = '%' . $lp_db_user_items->wpdb->esc_like( $course_name ) . '%';
193 $filter->where[] = $lp_db_user_items->wpdb->prepare(
194 'AND p.post_title LIKE %s',
195 $course_name_like
196 );
197 }
198
199 if ( ! empty( $search ) ) {
200 $search_like = '%' . $lp_db_user_items->wpdb->esc_like( $search ) . '%';
201 $filter->where[] = $lp_db_user_items->wpdb->prepare(
202 'AND ( u.display_name LIKE %s OR u.user_email LIKE %s )',
203 $search_like,
204 $search_like
205 );
206 }
207
208 if ( ! empty( $start_date ) ) {
209 $filter->where[] = $lp_db_user_items->wpdb->prepare(
210 'AND ui.start_time >= %s',
211 $start_date . ' 00:00:00'
212 );
213 }
214
215 if ( ! empty( $end_date ) ) {
216 $filter->where[] = $lp_db_user_items->wpdb->prepare(
217 'AND ui.start_time <= %s',
218 $end_date . ' 23:59:59'
219 );
220 }
221
222 $filter = apply_filters(
223 'learn-press/filter-enrolled-students',
224 $filter
225 );
226 $total_rows = 0;
227 $rows = $lp_db_user_items->get_user_items( $filter, $total_rows );
228 if ( ! is_array( $rows ) ) {
229 $rows = array();
230 }
231
232 // Build HTML.
233 $html = self::instance()->html_table(
234 $rows,
235 array(
236 'total' => $total_rows,
237 'paged' => $paged,
238 'per_page' => $per_page,
239 'students-of-course' => $course_id,
240 )
241 );
242
243 $content->content = $html;
244 } catch ( Throwable $e ) {
245 $content->content = Template::print_message( $e->getMessage(), 'error', false );
246 LP_Debug::error_log( $e );
247 }
248
249 return $content;
250 }
251
252 /**
253 * HTML builder: toolbar (course filter, search).
254 *
255 * @return string
256 */
257 public function html_toolbar(): string {
258 $courses = array();
259
260 $data_get = LP_Helper::sanitize_params_submitted( $_GET );
261 $selected_course = abs( LP_Helper::sanitize_params_submitted( $data_get['course_id'] ?? 0, 'int' ) );
262 $search_course = LP_Helper::sanitize_params_submitted( $data_get['course_name'] ?? '' );
263 $search_student = LP_Helper::sanitize_params_submitted( $data_get['search'] ?? '' );
264 $search_start = lp_helper::sanitize_params_submitted( $data_get['start_date'] ?? '' );
265 $search_end = lp_helper::sanitize_params_submitted( $data_get['end_date'] ?? '' );
266
267 $html_fields = sprintf(
268 '<div class="filter-field">
269 <label for="lp-enrolled-filter-course-name">%s</label>
270 <input id="lp-enrolled-filter-course-name"
271 class="lp-enrolled-filter-course-name"
272 type="text"
273 name="course_name"
274 list="lp-enrolled-course-list"
275 value="%s"
276 placeholder="%s">
277 </div>
278 <div class="filter-field">
279 <label for="lp-enrolled-search-input">%s</label>
280 <input id="lp-enrolled-search-input"
281 class="lp-enrolled-search-input"
282 type="text"
283 name="search" value="%s" placeholder="%s">
284 </div>
285 <div class="filter-field">
286 <label for="lp-enrolled-filter-start-date">%s</label>
287 <input id="lp-enrolled-filter-start-date"
288 class="lp-enrolled-filter-start-date"
289 type="date" name="start_date"
290 value="%s"
291 placeholder="mm/dd/yyyy">
292 </div>
293 <div class="filter-field">
294 <label for="lp-enrolled-filter-end-date">%s</label>
295 <input id="lp-enrolled-filter-end-date" class="lp-enrolled-filter-end-date" type="date" name="end_date" value="%s" placeholder="mm/dd/yyyy">
296 </div>
297 <datalist id="lp-enrolled-course-list">%s</datalist>',
298 esc_html__( 'Course Filter', 'learnpress' ),
299 esc_attr( $search_course ),
300 esc_attr__( 'Search course...', 'learnpress' ),
301 esc_html__( 'Student', 'learnpress' ),
302 esc_attr( $search_student ),
303 esc_attr__( 'Enter student name or email', 'learnpress' ),
304 esc_html__( 'Start Date', 'learnpress' ),
305 esc_attr( $search_start ),
306 esc_html__( 'End date', 'learnpress' ),
307 esc_attr( $search_end ),
308 ''
309 );
310
311 $html_btn_actions = sprintf(
312 '<button type="button" class="lp-button lp-enrolled-btn-search">%s</button>
313 <button type="button" class="lp-button lp-enrolled-btn-clear">%s</button>',
314 esc_html__( 'Search', 'learnpress' ),
315 esc_html__( 'Clear Filter', 'learnpress' )
316 );
317
318 $html_toolbar = AdminTemplate::html_form_filter(
319 array(
320 'form_classes' => 'lp-enrolled-students-form',
321 'fields' => $html_fields,
322 'btn_actions' => $html_btn_actions,
323 )
324 );
325
326 return apply_filters(
327 'learn-press/admin/enrolled-students/toolbar/section',
328 $html_toolbar,
329 $courses,
330 $selected_course
331 );
332 }
333
334 /**
335 * Render toolbar used inside View Students modal.
336 *
337 * @return string
338 */
339 public function html_toolbar_modal(): string {
340 $html_fields = sprintf(
341 '<div class="filter-field">
342 <label for="lp-modal-enrolled-search-input">%s</label>
343 <input id="lp-modal-enrolled-search-input"
344 class="lp-enrolled-search-input"
345 type="text" name="search" placeholder="%s">
346 </div>
347 <div class="filter-field">
348 <label for="lp-modal-enrolled-filter-start-date">%s</label>
349 <input id="lp-modal-enrolled-filter-start-date"
350 class="lp-enrolled-filter-start-date"
351 type="date" name="start_date" placeholder="mm/dd/yyyy">
352 </div>
353 <div class="filter-field">
354 <label for="lp-modal-enrolled-filter-end-date">%s</label>
355 <input id="lp-modal-enrolled-filter-end-date"
356 class="lp-enrolled-filter-end-date"
357 type="date" name="end_date" placeholder="mm/dd/yyyy">
358 </div>',
359 esc_html__( 'Student', 'learnpress' ),
360 esc_attr__( 'Enter student name or email', 'learnpress' ),
361 esc_html__( 'Start Date', 'learnpress' ),
362 esc_html__( 'End Date', 'learnpress' )
363 );
364
365 $html_btn_actions = sprintf(
366 '<button type="button" class="lp-button lp-enrolled-btn-search-modal">%s</button>
367 <button type="button" class="lp-button lp-enrolled-btn-clear-modal">%s</button>',
368 esc_html__( 'Search', 'learnpress' ),
369 esc_html__( 'Clear Filter', 'learnpress' )
370 );
371
372 return AdminTemplate::html_form_filter(
373 array(
374 'id' => 'lp-modal-enrolled-form',
375 'form_classes' => 'lp-enrolled-students-form lp-enrolled-students-form--modal lp-enrolled-students-table-toolbar--modal',
376 'fields' => $html_fields,
377 'btn_actions' => $html_btn_actions,
378 )
379 );
380 }
381
382 /**
383 * Render lp-target layout used in View Students modal.
384 *
385 * @return string
386 */
387 public function html_modal_students_target(): string {
388 $args = array(
389 'id_url' => 'lp-modal-enrolled-students',
390 'course_id' => 0,
391 'paged' => 1,
392 'enableScrollToView' => false,
393 'enableUpdateParamsUrl' => false,
394 'html_no_load_ajax_first' => sprintf(
395 '<div class="learn-press-message" style="%s">%s</div>',
396 'width: 95%;',
397 esc_html__( 'Loading', 'learnpress' )
398 ),
399 );
400
401 $call_back = array(
402 'class' => self::class,
403 'method' => 'render_enrolled_students',
404 );
405
406 $section = array(
407 'wrap' => '<div id="lp-modal-enrolled-wrap">',
408 'target' => TemplateAJAX::load_content_via_ajax( $args, $call_back ),
409 'wrap-end' => '</div>',
410 );
411
412 return Template::combine_components( $section );
413 }
414
415 /**
416 * Print HTML template for modal toolbar.
417 *
418 * @return void
419 */
420 public function print_modal_toolbar_template() {
421
422 $should_print = false;
423
424 if ( is_admin() ) {
425 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
426 if ( $screen && isset( $screen->id ) && $screen->id === 'edit-' . LP_COURSE_CPT ) {
427 $should_print = true;
428 }
429 } elseif ( class_exists( 'LP_Page_Controller' ) && defined( 'LP_PAGE_PROFILE' ) ) {
430 $should_print = LP_Page_Controller::page_current() === LP_PAGE_PROFILE;
431 }
432
433 if ( ! $should_print ) {
434 return;
435 }
436
437 echo '<script type="text/html" id="lp-tmpl-enrolled-students-toolbar-modal">';
438 echo $this->html_toolbar_modal();
439 echo '</script>';
440
441 echo '<script type="text/html" id="lp-tmpl-enrolled-students-target-modal">';
442 echo $this->html_modal_students_target();
443 echo '</script>';
444 }
445
446 /**
447 * HTML builder: table wrapper.
448 *
449 * @param array $rows DB result rows.
450 * @param array $meta [ total, paged, per_page ].
451 *
452 * @return string
453 */
454 private function html_table( array $rows, array $meta ): string {
455 if ( empty( $rows ) ) {
456 $section_empty = array(
457 'wrap' => '<div class="lp-enrolled-students-table-wrap">',
458 'empty' => sprintf(
459 '<div class="lp-enrolled-empty"><p>%s</p></div>',
460 esc_html__( 'No students found.', 'learnpress' )
461 ),
462 'wrap-end' => '</div>',
463 );
464 $section_empty = apply_filters(
465 'learn-press/admin/enrolled-students/table/empty/section',
466 $section_empty,
467 $rows,
468 $meta
469 );
470
471 return Template::combine_components( $section_empty );
472 }
473
474 $rows_html = array();
475
476 $results_map = array();
477 foreach ( $rows as $item ) {
478 $userCourseModel = new UserCourseModel( $item );
479 $rows_html[] = $this->html_student_row( $userCourseModel, $meta );
480 }
481 $rows_html = apply_filters(
482 'learn-press/admin/enrolled-students/table/rows-html',
483 $rows_html,
484 $rows,
485 $results_map,
486 $meta
487 );
488
489 $table_args = array(
490 'class_table' => 'lp-enrolled-students-table',
491 'header' => array(
492 'student' => array(
493 'class' => 'lp-col-student',
494 'title' => esc_html__( 'Student', 'learnpress' ),
495 ),
496 'course' => array(
497 'class' => 'lp-col-course',
498 'title' => esc_html__( 'Course', 'learnpress' ),
499 ),
500 'date' => array(
501 'class' => 'lp-col-date',
502 'title' => esc_html__( 'Enrolled Date', 'learnpress' ),
503 ),
504 'progress' => array(
505 'class' => 'lp-col-progress',
506 'title' => esc_html__( 'Progress', 'learnpress' ),
507 ),
508 'status' => array(
509 'class' => 'lp-col-status',
510 'title' => esc_html__( 'Status', 'learnpress' ),
511 ),
512 ),
513 'body' => array(
514 'rows_html' => Template::combine_components( $rows_html ),
515 ),
516 );
517 $section_footer = array(
518 'wrap' => '<div class="lp-enrolled-students-table-footer">',
519 'page_result' => sprintf(
520 '<span class="lp-enrolled-students-table-footer__count">%s</span>',
521 TableListTemplate::instance()->html_page_result(
522 array(
523 'paged' => $meta['paged'],
524 'per_page' => $meta['per_page'],
525 'total_rows' => $meta['total'],
526 'item_name' => _n( 'student', 'students', $meta['total'], 'learnpress' ),
527 )
528 )
529 ),
530 'pagination' => $this->html_pagination( $meta['total'], $meta['paged'], $meta['per_page'] ),
531 'wrap-end' => '</div>',
532 );
533 $section_footer = apply_filters(
534 'learn-press/admin/enrolled-students/table/footer/section',
535 $section_footer,
536 $rows,
537 $results_map,
538 $meta
539 );
540 $table_args['footer'] = Template::combine_components( $section_footer );
541 $table_args = apply_filters(
542 'learn-press/admin/enrolled-students/table/args',
543 $table_args,
544 $rows,
545 $results_map,
546 $meta
547 );
548
549 if ( ! empty( $meta['students-of-course'] ) ) {
550 unset( $table_args['header']['course'] ); // Remove Course column if filtering by course, as it's redundant.
551 }
552
553 $section = array(
554 'wrap' => '<div class="lp-enrolled-students-table-wrap">',
555 'table' => TableListTemplate::instance()->html_table( $table_args ),
556 'wrap-end' => '</div>',
557 );
558 $section = apply_filters(
559 'learn-press/admin/enrolled-students/table/section',
560 $section,
561 $rows,
562 $results_map,
563 $meta
564 );
565
566 return Template::combine_components( $section );
567 }
568
569 /**
570 * HTML builder: single student row.
571 *
572 * @param UserCourseModel $userCourseModel
573 *
574 * @return string
575 */
576 private function html_student_row( UserCourseModel $userCourseModel, array $data = [] ): string {
577 // Avatar initials.
578 $userModel = $userCourseModel->get_user_model();
579 if ( ! $userModel instanceof UserModel ) {
580 return '';
581 }
582
583 $courseModel = $userCourseModel->get_course_model();
584 if ( ! $courseModel instanceof CourseModel ) {
585 return '';
586 }
587
588 // Progress.
589 $course_result = $userCourseModel->calculate_course_results();
590 $progress = (float) ( $course_result['result'] ?? 0 );
591
592 // Status badge.
593 $graduation = $userCourseModel->get_graduation();
594 $status_raw = $graduation !== UserItemModel::GRADUATION_IN_PROGRESS
595 ? $userCourseModel->get_graduation()
596 : $userCourseModel->get_status();
597 $status_label = $graduation !== UserItemModel::GRADUATION_IN_PROGRESS
598 ? $userCourseModel->get_graduation_label()
599 : $userCourseModel->get_status_label();
600 $badge_class = 'lp-badge--' . sanitize_html_class( $status_raw );
601
602 // Date.
603 $date = UserCourseTemplate::instance()->html_start_date_time( $userCourseModel, false );
604
605 $user_display_name = $userModel->get_display_name();
606 if ( empty( $user_display_name ) ) {
607 $user_display_name = $userModel->get_username();
608 }
609
610 $section = array(
611 'row' => '<tr>',
612 'student-cell-open' => '<td><div class="lp-cell-student">',
613 'avatar' => SingleInstructorTemplate::instance()->html_avatar( $userModel ),
614 'meta-open' => '<div class="lp-meta">',
615 'name' => sprintf(
616 '<span class="lp-name">%s</span>',
617 esc_html( $user_display_name )
618 ),
619 'email' => sprintf(
620 '<span class="lp-email">%s</span>',
621 esc_html( $userModel->get_email() )
622 ),
623 'meta-close' => '</div>',
624 'student-cell-close' => '</div></td>',
625 'course-cell' => sprintf(
626 '<td class="lp-cell-course"><a href="%s">%s</a></td>',
627 esc_url_raw( $courseModel->get_permalink() ),
628 $courseModel->get_title()
629 ),
630 'date-cell' => sprintf(
631 '<td class="lp-cell-date">%s</td>',
632 wp_kses_post( $date )
633 ),
634 'progress-cell-open' => '<td class="lp-cell-progress">',
635 'progress-bar' => sprintf(
636 '<div class="lp-progress-bar"><span class="" style="width: %d%%;"></span></div>',
637 $progress
638 ),
639 'progress-text' => sprintf(
640 '<span class="lp-progress-text">%d%%</span>',
641 $progress
642 ),
643 'progress-cell-close' => '</td>',
644 'status-cell-open' => '<td class="lp-cell-status">',
645 'status-badge' => sprintf(
646 '<span class="lp-badge %s">%s</span>',
647 esc_attr( $badge_class ),
648 esc_html( $status_label )
649 ),
650 'status-cell-close' => '</td>',
651 'row-end' => '</tr>',
652 );
653
654 if ( ! empty( $data['students-of-course'] ) ) {
655 unset( $section['course-cell'] ); // Remove Course cell if filtering by course, as it's redundant.
656 }
657
658 $section = apply_filters(
659 'learn-press/admin/enrolled-students/row/section',
660 $section,
661 $userCourseModel
662 );
663
664 return Template::combine_components( $section );
665 }
666
667 /**
668 * HTML builder: pagination.
669 *
670 * Uses .page-numbers class for loadAJAX.js compatibility.
671 *
672 * @param int $total
673 * @param int $paged
674 * @param int $per_page
675 *
676 * @return string
677 */
678 private function html_pagination( int $total, int $paged, int $per_page ): string {
679 $total_pages = max( 1, ceil( $total / $per_page ) );
680
681 $pagination_items = Template::instance()->html_pagination(
682 array(
683 'total_pages' => $total_pages,
684 'paged' => $paged,
685 'wrapper' => array(
686 '<nav class="learn-press-pagination navigation pagination lp-pagination">' => '</nav>',
687 ),
688 )
689 );
690 $pagination_items = apply_filters(
691 'learn-press/admin/enrolled-students/pagination/items',
692 $pagination_items,
693 $total,
694 $paged,
695 $per_page,
696 $total_pages
697 );
698
699 $section = array(
700 'wrap' => '<div class="lp-enrolled-students-table-footer__pagination">',
701 'pagination' => $pagination_items,
702 'wrap-end' => '</div>',
703 );
704 $section = apply_filters(
705 'learn-press/admin/enrolled-students/pagination/section',
706 $section,
707 $total,
708 $paged,
709 $per_page,
710 $total_pages
711 );
712
713 return Template::combine_components( $section );
714 }
715 }
716