PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.5
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.5
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 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / TemplateHooks / Admin / AdminListStudentsEnrolled.php

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

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