'*' . $term . '*', 'search_columns' => array( 'user_login', 'user_email', 'display_name' ), 'number' => self::SEARCH_LIMIT, 'orderby' => 'display_name', 'fields' => array( 'ID', 'display_name', 'user_login', 'user_email' ), ) ); $results = array(); foreach ( $query->get_results() as $user ) { $results[] = array( 'id' => (int) $user->ID, 'label' => self::user_label( $user ), ); } wp_send_json_success( $results ); } /** * "Display Name (login, email)" for a user in the picker. * * @param object $user User (ID, display_name, user_login, user_email). * @return string */ private static function user_label( $user ) { /* translators: 1: display name, 2: username, 3: email address. */ return sprintf( __( '%1$s (%2$s, %3$s)', 'accessnow-lms' ), $user->display_name, $user->user_login, $user->user_email ); } /** * Users named in the enrolment form: the picker's IDs, plus anything typed in the box * (user IDs, usernames or emails separated by commas or new lines, which also works without * JavaScript). * * @param array $ids Picked user IDs. * @param string $text Typed text. * @return array { users: int[], unknown: string[] } */ public static function resolve_users( array $ids, $text ) { $users = array(); $unknown = array(); foreach ( $ids as $id ) { if ( get_userdata( absint( $id ) ) ) { $users[] = absint( $id ); } } foreach ( preg_split( '/[,\n]+/', (string) $text ) as $token ) { $token = trim( $token ); if ( '' === $token ) { continue; } if ( ctype_digit( $token ) ) { $user = get_userdata( (int) $token ); } elseif ( is_email( $token ) ) { $user = get_user_by( 'email', $token ); } else { $user = get_user_by( 'login', $token ); } if ( $user ) { $users[] = (int) $user->ID; } else { $unknown[] = $token; } } return array( 'users' => array_values( array_unique( $users ) ), 'unknown' => $unknown, ); } /** * Enrol each named user in each chosen course and print the outcome. */ private static function handle_enroll_form() { $picked = isset( $_POST['enroll_user_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['enroll_user_ids'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller. $typed = isset( $_POST['enroll_users'] ) ? sanitize_textarea_field( wp_unslash( $_POST['enroll_users'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller. $courses = isset( $_POST['enroll_course_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['enroll_course_ids'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller. $notify = ! empty( $_POST['notify_student'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller. $until = isset( $_POST['enroll_access_until'] ) ? sanitize_text_field( wp_unslash( $_POST['enroll_access_until'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller. $expires = '' === $until ? null : Enrollment::expiry_from_date( $until ); if ( '' !== $until && null === $expires ) { echo '

' . esc_html__( 'The access end date is not a valid date.', 'accessnow-lms' ) . '

'; return; } $courses = array_values( array_unique( array_filter( $courses, function ( $id ) { return 'slms_course' === get_post_type( $id ); } ) ) ); $found = self::resolve_users( $picked, $typed ); if ( $found['unknown'] ) { printf( '

%s

', /* translators: %s: comma-separated list of what was typed. */ esc_html( sprintf( __( 'No user found for: %s', 'accessnow-lms' ), implode( ', ', $found['unknown'] ) ) ) ); } if ( ! $found['users'] || ! $courses ) { echo '

' . esc_html__( 'Choose at least one student and one course.', 'accessnow-lms' ) . '

'; return; } $new = 0; $already = 0; foreach ( $found['users'] as $user_id ) { foreach ( $courses as $course_id ) { if ( Enrollment::get_active_enrollment( $user_id, $course_id ) ) { ++$already; continue; } $enrollment_id = Enrollment::enroll( $user_id, $course_id, get_current_user_id() ); if ( $enrollment_id ) { ++$new; if ( $expires ) { Enrollment::set_expiry( $enrollment_id, $expires ); } // Self-enrolment sends its email on its own; for an administrator it is this checkbox. if ( $notify ) { Notifications::send_enrollment_email( $user_id, $course_id ); } } } } $message = sprintf( /* translators: %s: number of new enrolments. */ _n( '%s new enrollment.', '%s new enrollments.', $new, 'accessnow-lms' ), number_format_i18n( $new ) ); if ( $already ) { $message .= ' ' . sprintf( /* translators: %s: number of enrolments that already existed. */ _n( '%s was already enrolled.', '%s were already enrolled.', $already, 'accessnow-lms' ), number_format_i18n( $already ) ); } printf( '

%s

', esc_html( $message ) ); } public static function render_page() { if ( ! current_user_can( 'manage_slms_enrollments' ) ) { wp_die( esc_html__( 'You do not have permission to manage enrollments.', 'accessnow-lms' ) ); } // Handle add enrollment if ( isset( $_POST['accessnow_slms_enroll_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['accessnow_slms_enroll_nonce'] ) ), 'accessnow_slms_admin_enroll' ) ) { self::handle_enroll_form(); } // Handle bulk actions if ( isset( $_POST['slms_bulk_action'] ) && isset( $_POST['enrollment_ids'] ) ) { check_admin_referer( 'accessnow_slms_bulk_enrollments' ); $action = sanitize_text_field( wp_unslash( $_POST['slms_bulk_action'] ) ); $ids = array_map( 'absint', (array) wp_unslash( $_POST['enrollment_ids'] ) ); if ( 'drop' === $action ) { foreach ( $ids as $id ) { Enrollment::drop( $id, get_current_user_id() ); } echo '

' . esc_html__( 'Enrollments updated.', 'accessnow-lms' ) . '

'; } elseif ( 'set_access' === $action || 'clear_access' === $action ) { $until = isset( $_POST['slms_access_until'] ) ? sanitize_text_field( wp_unslash( $_POST['slms_access_until'] ) ) : ''; $expires = 'set_access' === $action ? Enrollment::expiry_from_date( $until ) : null; if ( 'set_access' === $action && null === $expires ) { echo '

' . esc_html__( 'Choose the last day of access first.', 'accessnow-lms' ) . '

'; } else { $changed = 0; foreach ( $ids as $id ) { $enrollment = Enrollment::get_enrollment_by_id( $id ); // Dropped enrolments give no access, so their end date means nothing. if ( $enrollment && 'dropped' !== $enrollment->status && Enrollment::set_expiry( $id, $expires ) ) { ++$changed; } } printf( '

%s

', /* translators: %s: number of enrolments. */ esc_html( sprintf( _n( 'Access end date updated for %s enrollment.', 'Access end date updated for %s enrollments.', $changed, 'accessnow-lms' ), number_format_i18n( $changed ) ) ) ); } } elseif ( 'reenroll' === $action ) { $reenrolled = 0; foreach ( $ids as $id ) { $enrollment = Enrollment::get_enrollment_by_id( $id ); if ( $enrollment && in_array( $enrollment->status, array( 'completed', 'dropped' ), true ) ) { $new_id = Enrollment::enroll( $enrollment->user_id, $enrollment->course_id, get_current_user_id() ); if ( $new_id ) { ++$reenrolled; } } } if ( $reenrolled ) { printf( '

%s

', /* translators: %s: number of students. */ esc_html( sprintf( _n( '%s student re-enrolled.', '%s students re-enrolled.', $reenrolled, 'accessnow-lms' ), number_format_i18n( $reenrolled ) ) ) ); } } } // List filters from the query string: read-only, so no nonce. // phpcs:disable WordPress.Security.NonceVerification.Recommended $filter_course = isset( $_GET['course_id'] ) ? absint( $_GET['course_id'] ) : 0; $filter_status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : ''; $paged = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( ! in_array( $filter_status, array( '', 'active', 'completed', 'dropped' ), true ) ) { $filter_status = ''; } $per_page = 20; $args = array( 'per_page' => $per_page, 'offset' => ( $paged - 1 ) * $per_page, ); if ( $filter_course ) { $args['course_id'] = $filter_course; } if ( $filter_status ) { $args['status'] = $filter_status; } $enrollments = Enrollment::get_all_enrollments( $args ); $total = Enrollment::count_enrollments( $args ); $total_pages = ceil( $total / $per_page ); $courses = get_posts( array( 'post_type' => 'slms_course', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC', ) ); ?>

    user_id ); $course_title = get_the_title( $e->course_id ); $progress = Progress::get_course_progress( $e->user_id, $e->course_id, $e->id ); $enrolled_by = $e->enrolled_by ? get_userdata( $e->enrolled_by ) : null; $enrolled_by_name = $e->enrolled_by ? ( $enrolled_by ? $enrolled_by->display_name : '#' . $e->enrolled_by ) : __( 'Self', 'accessnow-lms' ); $access_until = Enrollment::access_end_date( $e ); if ( 'dropped' === $e->status ) { $access_label = '—'; } elseif ( '' === $access_until ) { $access_label = esc_html__( 'No end date', 'accessnow-lms' ); } elseif ( Enrollment::is_expired( $e ) ) { /* translators: %s: date. */ $access_label = esc_html( sprintf( __( 'Ended %s', 'accessnow-lms' ), $access_until ) ); } else { $access_label = esc_html( $access_until ); } ?>
    display_name : '#' . $e->user_id ); ?> status ) ); ?> enrolled_at ); ?>
    1 ) : ?>
    add_query_arg( 'paged', '%#%' ), 'format' => '', 'total' => $total_pages, 'current' => $paged, ) ) ); ?>