Source: includes/reports/overview/list-table/class-sensei-reports-overview-list-table-students.php

<?php
/**
 * File containing the Sensei_Reports_Overview_List_Table_Students class.
 *
 * @package sensei
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Students overview list table class.
 *
 * @since 4.3.0
 */
class Sensei_Reports_Overview_List_Table_Students extends Sensei_Reports_Overview_List_Table_Abstract {

	/**
	 * Sensei reports courses service.
	 *
	 * @var Sensei_Reports_Overview_Service_Students
	 */
	private $reports_overview_service_students;

	/**
	 * Constructor
	 *
	 * @param Sensei_Reports_Overview_Data_Provider_Interface $data_provider Report data provider.
	 * @param Sensei_Reports_Overview_Service_Students        $reports_overview_service_students reports students service.
	 */
	public function __construct( Sensei_Reports_Overview_Data_Provider_Interface $data_provider, Sensei_Reports_Overview_Service_Students $reports_overview_service_students ) {
		// Load Parent token into constructor.
		parent::__construct( 'users', $data_provider );

		$this->reports_overview_service_students = $reports_overview_service_students;
	}

	/**
	 * Define the columns that are going to be used in the table
	 *
	 * @return array The array of columns to use with the table
	 */
	public function get_columns() {

		if ( $this->columns ) {
			return $this->columns;
		}

		$total_completed_courses = 0;
		$total_courses_started   = 0;
		$total_average_grade     = 0;

		$user_ids = $this->get_all_item_ids();
		if ( $user_ids ) {
			// Get total value for Courses Completed column in users table.
			$total_completed_courses = Sensei_Utils::sensei_check_for_activity(
				[
					'user_id' => $user_ids,
					'type'    => 'sensei_course_status',
					'status'  => 'complete',
				]
			);

			// Get the number of the courses that users have started.
			$total_courses_started = Sensei_Utils::sensei_check_for_activity(
				[
					'user_id' => $user_ids,
					'type'    => 'sensei_course_status',
					'status'  => 'any',
				]
			);

			// Get total average students grade.
			$total_average_grade = $this->reports_overview_service_students->get_graded_lessons_average_grade( $user_ids );
		}

		$columns = array(
			// translators: Placeholder value is total count of students.
			'title'             => sprintf( __( 'Student (%d)', 'sensei-lms' ), count( $user_ids ) ),
			'email'             => __( 'Email', 'sensei-lms' ),
			'date_registered'   => __( 'Date Registered', 'sensei-lms' ),
			'last_activity'     => __( 'Last Activity', 'sensei-lms' ),
			// translators: Placeholder value is all active courses.
			'active_courses'    => sprintf( __( 'Active Courses (%d)', 'sensei-lms' ), $total_courses_started - $total_completed_courses ),
			// translators: Placeholder value is all completed courses.
			'completed_courses' => sprintf( __( 'Completed Courses (%d)', 'sensei-lms' ), $total_completed_courses ),
			// translators: Placeholder value is graded average value.
			'average_grade'     => sprintf( __( 'Average Grade (%d%%)', 'sensei-lms' ), $total_average_grade ),
		);

		// Backwards compatible filter name, moving forward should have single filter name.
		/**
		 * Filter the columns that are going to be used in the table.
		 *
		 * @hook sensei_analysis_overview_users_columns
		 *
		 * @param {array} $columns The array of columns to use with the table.
		 * @param {Sensei_Reports_Overview_List_Table_Students} $this The current instance of the class.
		 * @return {array} Filtered columns.
		 */
		$columns = apply_filters( 'sensei_analysis_overview_users_columns', $columns, $this );

		/**
		 * Filter the columns that are going to be used in the table.
		 *
		 * @hook sensei_analysis_overview_columns
		 *
		 * @param {array} $columns The array of columns to use with the table.
		 * @param {Sensei_Reports_Overview_List_Table_Students} $this The current instance of the class.
		 * @return {array} Filtered columns.
		 */
		$columns = apply_filters( 'sensei_analysis_overview_columns', $columns, $this );

		$this->columns = $columns;

		return $this->columns;
	}

	/**
	 * Define the columns that are going to be used in the table
	 *
	 * @return array The array of columns to use with the table
	 */
	public function get_sortable_columns() {
		$columns = [
			'title'           => array( 'display_name', false ),
			'email'           => array( 'user_email', false ),
			'date_registered' => array( 'user_registered', false ),
			'last_activity'   => array( 'last_activity_date', false ),
		];

		// Backwards compatible filter name, moving forward should have single filter name.
		/**
		 * Filter the sortable columns that are going to be used in the table.
		 *
		 * @hook sensei_analysis_overview_users_columns_sortable
		 *
		 * @param {array} $columns The array of columns to use with the table.
		 * @param {Sensei_Reports_Overview_List_Table_Students} $this The current instance of the class.
		 * @return {array} Filtered columns.
		 */
		$columns = apply_filters( 'sensei_analysis_overview_users_columns_sortable', $columns, $this );

		/**
		 * Filter the sortable columns that are going to be used in the table.
		 *
		 * @hook sensei_analysis_overview_columns_sortable
		 *
		 * @param {array} $columns The array of columns to use with the table.
		 * @param {Sensei_Reports_Overview_List_Table_Students} $this The current instance of the class.
		 * @return {array} Filtered columns.
		 */
		$columns = apply_filters( 'sensei_analysis_overview_columns_sortable', $columns, $this );

		return $columns;
	}

	/**
	 * Generates the overall array for a single item in the display
	 *
	 * @param object $item The current item.
	 *
	 * @return array Report row data.
	 * @throws Exception If date-time conversion fails.
	 */
	protected function get_row_data( $item ) {
		// Get Started Courses.
		$course_args = array(
			'user_id' => $item->ID,
			'type'    => 'sensei_course_status',
			'status'  => 'any',
		);

		/**
		 * Filter user progress query arguments for courses: find started courses.
		 *
		 * @hook sensei_analysis_user_courses_started
		 *
		 * @param {array} $course_args Array of query arguments for started user courses.
		 * @param {WP_User} $item Current user object.
		 * @return {array} Filtered array of query arguments for started user courses.
		*/
		$course_args          = apply_filters( 'sensei_analysis_user_courses_started', $course_args, $item );
		$user_courses_started = Sensei_Utils::sensei_check_for_activity( $course_args );

		// Get Completed Courses.
		$course_args = array(
			'user_id' => $item->ID,
			'type'    => 'sensei_course_status',
			'status'  => 'complete',
		);

		/**
		 * Filter user progress query arguments for courses: find completed courses.
		 *
		 * @hook sensei_analysis_user_courses_ended
		 *
		 * @param {array} $course_args Array of query arguments for ended user courses.
		 * @param {WP_User} $item Current user object.
		 * @return {array} Filtered array of query arguments for ended user courses.
		 */
		$user_courses_ended = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_user_courses_ended', $course_args, $item ) );

		// Get Quiz Grades.
		$grade_args = array(
			'user_id'  => $item->ID,
			'type'     => 'sensei_lesson_status',
			'status'   => 'any',
			'meta_key' => 'grade', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Filtering graded only.
		);

		/**
		 * Filter user progress query arguments for lessons: find graded lessons.
		 *
		 * @hook sensei_analysis_user_lesson_grades
		 *
		 * @param {array} $grade_args Array of query arguments for graded user lessons.
		 * @param {WP_User} $item Current user object.
		 * @return {array} Filtered array of query arguments for graded user lessons.
		 */
		$grade_count        = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_user_lesson_grades', $grade_args, $item ), false );
		$grade_total        = Sensei_Grading::get_user_graded_lessons_sum( $item->ID );
		$user_average_grade = 0;

		if ( $grade_total > 0 && $grade_count > 0 ) {
			$user_average_grade = Sensei_Utils::quotient_as_absolute_rounded_number( $grade_total, $grade_count, 2 );
		}

		$user_email = $item->user_email;

		// Output the users data.
		if ( ! $this->csv_output ) {
			$user_average_grade .= '%';
		}

		$last_activity_date = __( 'N/A', 'sensei-lms' );

		if ( ! empty( $item->last_activity_date ) ) {
			$last_activity_date = $this->csv_output ? $item->last_activity_date : Sensei_Utils::format_last_activity_date( $item->last_activity_date );
		}

		/**
		 * Filter the row data for the Analysis Overview list table.
		 *
		 * @hook sensei_analysis_overview_column_data
		 *
		 * @param {array} $column_data Array of column data for the report table.
		 * @param {object|WP_Post|WP_User} $item Current row object.
		 * @param {Sensei_Reports_Overview_List_Table_Students} $this Current instance of the list table.
		 * @return {array} Filtered array of column data for the report table.
		 */
		$column_data = apply_filters(
			'sensei_analysis_overview_column_data',
			array(
				'title'             => $this->format_user_name( $item->ID, $this->csv_output ),
				'email'             => $user_email,
				'date_registered'   => $this->format_date_registered( $item->user_registered ),
				'last_activity'     => $last_activity_date,
				'active_courses'    => ( $user_courses_started - $user_courses_ended ),
				'completed_courses' => $user_courses_ended,
				'average_grade'     => $user_average_grade,
			),
			$item,
			$this
		);

		$escaped_column_data = array();

		foreach ( $column_data as $key => $data ) {
			$escaped_column_data[ $key ] = wp_kses_post( $data );
		}

		return $escaped_column_data;
	}

	/**
	 * The text for the search button.
	 *
	 * @return string
	 */
	public function search_button() {
		return __( 'Search Students', 'sensei-lms' );
	}

	/**
	 * Return additional filters for current report.
	 *
	 * @return array
	 */
	protected function get_additional_filters(): array {
		return [
			'last_activity_date_from' => $this->get_start_date_and_time(),
			'last_activity_date_to'   => $this->get_end_date_and_time(),
		];
	}

	/**
	 * Format the registration date.
	 *
	 * @param string $date Registration date.
	 *
	 * @return string Formatted registration date.
	 */
	private function format_date_registered( string $date ) {
		$timezone = new DateTimeZone( 'GMT' );
		$date     = new DateTime( $date, $timezone );

		return wp_date( get_option( 'date_format' ), $date->getTimestamp(), $timezone );
	}

	/**
	 * Format user name wrapping or not with a link.
	 *
	 * @param int  $user_id user's id.
	 * @param bool $use_raw_name Indicate if it should return the wrap the name with the student link.
	 *
	 * @return string Return the student full name (first_name+last_name) optionally wrapped by a link
	 */
	private function format_user_name( $user_id, $use_raw_name ) {

		$user_name = Sensei_Learner::get_full_name( $user_id );

		if ( $use_raw_name ) {
			return $user_name;
		}

		$url = add_query_arg(
			array(
				'page'    => $this->page_slug,
				'user_id' => $user_id,
			),
			admin_url( 'admin.php' )
		);

		return '<strong><a class="row-title" href="' . esc_url( $url ) . '">' . esc_html( $user_name ) . '</a></strong>';
	}
}