gocodebox/lifterlms

View on GitHub
includes/admin/reporting/tables/llms.table.students.php

Summary

Maintainability
F
3 days
Test Coverage
C
70%
<?php
/**
 * Students Reporting Table
 *
 * @package LifterLMS/Admin/Reporting/Tables/Classes
 *
 * @since 3.2.0
 * @version 6.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * LLMS_Table_Students class
 *
 * @since 3.2.0
 * @since 3.28.0 Unknown.
 * @since 3.31.0 Allow filtering the table by Course or Membership
 * @since 3.36.0 Add "Last Seen" column.
 * @since 3.36.1 Fixed "Last Seen" column displaying wrong date when the student last login date was saved as timestamp.
 * @since 3.37.2 The post filter on the students table now limits post results based on instructor access.
 */
class LLMS_Table_Students extends LLMS_Admin_Table {

    /**
     * Unique ID for the Table
     *
     * @var string
     */
    protected $id = 'students';

    /**
     * Value of the field being filtered by
     *
     * Only applicable if $filterby is set.
     *
     * @since 3.31.0
     * @var string
     */
    protected $filter = '';

    /**
     * Field results are filtered by
     *
     * @since 3.31.0
     * @var string
     */
    protected $filterby = 'course_membership';

    /**
     * Is the Table Exportable?
     *
     * @var boolean
     */
    protected $is_exportable = true;

    /**
     * Determine if the table is filterable
     *
     * @since 3.31.0
     * @var boolean
     */
    protected $is_filterable = true;

    /**
     * If true, tfoot will add ajax pagination links
     *
     * @var boolean
     */
    protected $is_paginated = true;

    /**
     * Determine of the table is searchable
     *
     * @var boolean
     */
    protected $is_searchable = true;

    /**
     * Results sort order
     *
     * 'ASC' or 'DESC'
     * Only applicable of $orderby is not set.
     *
     * @var string
     */
    protected $order = 'ASC';

    /**
     * Field results are sorted by
     *
     * @var string
     */
    protected $orderby = 'name';

    /**
     * Number of records to display per page
     *
     * @var int
     */
    protected $per_page = 25;

    /**
     * Retrieve data for the columns
     *
     * @since 3.2.0
     * @since 3.15.0 Unknown.
     * @since 3.36.0 Added "Last Seen" column.
     * @since 3.36.1 Fixed "Last Seen" column displaying wrong date when the student last login date was saved as timestamp.
     * @since 4.7.0 Speed up the query used to retrieve the last seen column by avoiding the found rows calculation.
     * @since 6.0.0 Don't access `LLMS_Events_Query` properties directly
     *              Use `LLMS_Student::get_awards_count()` for retrieving the number of earned achievements and certificates.
     *
     * @param string       $key     The column id / key.
     * @param LLMS_Student $student Instance of the LLMS_Student.
     * @return mixed
     */
    public function get_data( $key, $student ) {

        switch ( $key ) {

            case 'achievements':
                $url   = LLMS_Admin_Reporting::get_current_tab_url(
                    array(
                        'stab'       => 'achievements',
                        'student_id' => $student->get_id(),
                    )
                );
                $value = '<a href="' . esc_url( $url ) . '">' . $student->get_awards_count( 'achievement' ) . '</a>';
                break;

            case 'certificates':
                $url   = LLMS_Admin_Reporting::get_current_tab_url(
                    array(
                        'stab'       => 'certificates',
                        'student_id' => $student->get_id(),
                    )
                );
                $value = '<a href="' . esc_url( $url ) . '">' . $student->get_awards_count( 'certificate' ) . '</a>';
                break;

            case 'completions':
                $courses = $student->get_completed_courses();
                $value   = count( $courses['results'] );
                break;

            case 'enrollments':
                $url         = LLMS_Admin_Reporting::get_current_tab_url(
                    array(
                        'stab'       => 'courses',
                        'student_id' => $student->get_id(),
                    )
                );
                $enrollments = $student->get_courses(
                    array(
                        'limit' => 1,
                    )
                );
                $value       = '<a href="' . esc_url( $url ) . '">' . $enrollments['found'] . '</a>';
                break;

            case 'id':
                $id = $student->get_id();
                if ( current_user_can( 'list_users' ) ) {
                    $value = '<a href="' . esc_url( get_edit_user_link( $id ) ) . '">' . $id . '</a>';
                } else {
                    $value = $id;
                }
                break;

            case 'last_seen':
                $query = new LLMS_Events_Query(
                    array(
                        'actor'         => $student->get_id(),
                        'per_page'      => 1,
                        'sort'          => array(
                            'date' => 'DESC',
                        ),
                        'no_found_rows' => true,
                    )
                );

                if ( $query->has_results() ) {
                    $events = $query->get_events();
                    $last   = array_shift( $events );
                    $value  = $last->get( 'date' );
                } else {
                    $value = $student->get( 'last_login' );
                }

                $value = $value ? date_i18n( get_option( 'date_format' ), is_numeric( $value ) ? $value : strtotime( $value ) ) : '&ndash;';

                break;

            case 'memberships':
                $url   = LLMS_Admin_Reporting::get_current_tab_url(
                    array(
                        'stab'       => 'memberships',
                        'student_id' => $student->get_id(),
                    )
                );
                $value = '<a href="' . esc_url( $url ) . '">' . count( $student->get_membership_levels() ) . '</a>';
                break;

            case 'name':
                $first = $student->get( 'first_name' );
                $last  = $student->get( 'last_name' );

                if ( ! $first || ! $last ) {
                    $value = $student->get( 'display_name' );
                } else {
                    $value = $last . ', ' . $first;
                }

                $url   = LLMS_Admin_Reporting::get_current_tab_url(
                    array(
                        'student_id' => $student->get_id(),
                    )
                );
                $value = '<a href="' . esc_url( $url ) . '">' . $value . '</a>';

                break;

            case 'overall_grade':
                $value = $student->get_overall_grade( true );
                if ( is_numeric( $value ) ) {
                    $value .= '%';
                }
                break;

            case 'overall_progress':
                $value = $this->get_progress_bar_html( $student->get_overall_progress( true ) );
                break;

            case 'registered':
                $value = $student->get_registration_date();
                break;

            default:
                $value = $key;

        }

        return $this->filter_get_data( $value, $key, $student );

    }

    /**
     * Retrieve data for a cell in an export file
     *
     * Should be overridden in extending classes.
     *
     * @since 3.15.0
     * @since 3.26.1 Unknown.
     *
     * @param string       $key     The column id / key.
     * @param LLMS_Student $student Instance of the LLMS_Student.
     * @return mixed
     */
    public function get_export_data( $key, $student ) {

        switch ( $key ) {

            case 'id':
                $value = $student->get_id();
                break;

            case 'courses_cancelled':
            case 'courses_enrolled':
            case 'courses_expired':
                $status  = explode( '_', $key );
                $status  = array_pop( $status );
                $courses = $student->get_courses(
                    array(
                        'status' => $status,
                    )
                );
                $titles  = array();
                foreach ( $courses['results'] as $id ) {
                    $titles[] = get_the_title( $id );
                }
                $value = implode( ', ', $titles );

                break;

            case 'email':
                $value = $student->get( 'user_email' );
                break;

            case 'memberships_cancelled':
            case 'memberships_enrolled':
            case 'memberships_expired':
                $status      = explode( '_', $key );
                $status      = array_pop( $status );
                $memberships = $student->get_memberships(
                    array(
                        'status' => $status,
                    )
                );
                $titles      = array();
                foreach ( $memberships['results'] as $id ) {
                    $titles[] = get_the_title( $id );
                }
                $value = implode( ', ', $titles );

                break;

            case 'name_first':
                $value = $student->get( 'first_name' );
                break;

            case 'name_last':
                $value = $student->get( 'last_name' );
                break;

            case 'overall_grade':
                $value = $student->get_overall_grade( false );
                if ( is_numeric( $value ) ) {
                    $value .= '%';
                }
                break;

            case 'overall_progress':
                $value = $student->get_overall_progress( false ) . '%';
                break;

            case 'billing_address_1':
            case 'billing_address_2':
            case 'billing_city':
            case 'billing_state':
            case 'billing_zip':
            case 'billing_country':
            case 'phone':
                $value = $student->get( $key );
                break;

            default:
                $value = $this->get_data( $key, $student );

        }

        return $this->filter_get_data( $value, $key, $student, 'export' );

    }

    /**
     * Get the Text to be used as the placeholder in a searchable tables search input
     *
     * @since 3.2.0
     * @since 3.15.0 Unknown.
     *
     * @return string
     */
    public function get_table_search_form_placeholder() {
        return apply_filters( 'llms_table_get_' . $this->id . '_search_placeholder', __( 'Search students by name or email...', 'lifterlms' ) );
    }

    /**
     * Get HTML for the filters displayed in the head of the table
     *
     * This overrides the LLMS_Admin_Table method.
     *
     * @since 3.31.0
     * @since 3.37.2 Unknown.
     *
     * @return string
     */
    public function get_table_filters_html() {
        $select_id       = sprintf( '%1$s-%2$s-filter', $this->id, 'course-membership' );
        $additional_data = array();
        // Limit Course/Membership results based on instructor access.
        if ( ! current_user_can( 'view_others_lifterlms_reports' ) && current_user_can( 'view_lifterlms_reports' ) ) {
            $instructor = llms_get_instructor();
            if ( $instructor ) {
                $additional_data[] = sprintf( 'data-instructor_id="%d"', $instructor->get( 'id' ) );
            }
        }
        $additional_data = implode( ' ', $additional_data );
        ob_start();
        ?>
        <div class="llms-table-filters">
            <div class="llms-table-filter-wrap">
                <label class="screen-reader-text" for="<?php echo $select_id; ?>">
                    <?php _e( 'Choose Course/Membership', 'lifterlms' ); ?>
                </label>
                <select data-post-type="llms_membership,course" class="llms-posts-select2 llms-table-filter" id="<?php echo $select_id; ?>" name="course_membership" style="min-width:200px;max-width:500px;"<?php echo $additional_data; ?>></select>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Retrieve an array of query arguments to pass to the LLMS_Student_Query
     *
     * @since 3.28.0
     * @since 3.31.0 Added logic to setup the query args in order to allow the filtering by Course or Membership.
     *
     * @return array
     */
    private function get_query_args() {

        $query_args = array(
            'page'     => $this->get_current_page(),
            'post_id'  => array(),
            'per_page' => $this->get_per_page(),
            'sort'     => $this->get_sort(),
        );

        if ( 'status' === $this->get_filterby() && 'any' !== $this->get_filter() ) {

            $query_args['statuses'] = array( $this->get_filter() );

        } elseif ( 'course_membership' === $this->get_filterby() && '' !== $this->get_filter() ) {

            $query_args['post_id']  = absint( $this->get_filter() );
            $query_args['statuses'] = 'enrolled';

        }

        if ( $this->get_search() ) {
            $query_args['search'] = $this->get_search();
        }

        return $query_args;

    }

    /**
     * Execute a query to retrieve results from the table
     *
     * @since 3.2.0
     * @since 3.28.0 Unknown.
     * @since 6.0.0 Don't access `LLMS_Student_Query` properties directly.
     *
     * @param array $args Array of query args.
     * @return void
     */
    public function get_results( $args = array() ) {

        // Current user can't access this report.
        if ( ! current_user_can( 'view_others_lifterlms_reports' ) && ! current_user_can( 'view_lifterlms_reports' ) ) {
            return;
        }

        $this->parse_args( $args );

        $query_args = $this->get_query_args();

        if ( current_user_can( 'view_others_lifterlms_reports' ) ) {

            $query = new LLMS_Student_Query( $query_args );

        } elseif ( current_user_can( 'view_lifterlms_reports' ) ) {

            $instructor = llms_get_instructor();
            if ( ! $instructor ) {
                return;
            }
            $query = $instructor->get_students( $query_args );

        }

        $this->max_pages    = $query->get_max_pages();
        $this->is_last_page = $query->is_last_page();

        $this->tbody_data = $query->get_students();

    }

    /**
     * Setup the array of sort arguments to pass to the LLMS_Student_Query for the table
     *
     * @since 3.28.0
     *
     * @return array
     */
    private function get_sort() {

        $sort = array();
        switch ( $this->get_orderby() ) {

            case 'id':
                $sort = array(
                    'id' => $this->get_order(),
                );
                break;

            case 'name':
                $sort = array(
                    'last_name'  => $this->get_order(),
                    'first_name' => 'ASC',
                    'id'         => 'ASC',
                );
                break;

            case 'overall_grade':
                $sort = array(
                    'overall_grade' => $this->get_order(),
                    'last_name'     => 'ASC',
                    'first_name'    => 'ASC',
                    'id'            => 'ASC',
                );
                break;

            case 'overall_progress':
                $sort = array(
                    'overall_progress' => $this->get_order(),
                    'last_name'        => 'ASC',
                    'first_name'       => 'ASC',
                    'id'               => 'ASC',
                );
                break;

            case 'registered':
                $sort = array(
                    'registered' => $this->get_order(),
                    'last_name'  => 'ASC',
                    'first_name' => 'ASC',
                    'id'         => 'ASC',
                );
                break;

        }

        return $sort;

    }

    /**
     * Parse arguments passed to get_results() method & setup table class variables.
     *
     * @since 3.28.0
     * @since 3.31.0 Added logic to parse 'filterby' and 'filter' args when this table is filterable.
     *
     * @param array $args Array of arguments.
     * @return void
     */
    protected function parse_args( $args = array() ) {

        if ( ! $args ) {
            $args = $this->get_args();
        }

        $args = $this->clean_args( $args );

        if ( isset( $args['page'] ) ) {
            $this->current_page = absint( $args['page'] );
        }

        $this->order   = isset( $args['order'] ) ? $args['order'] : $this->get_order();
        $this->orderby = isset( $args['orderby'] ) ? $args['orderby'] : $this->get_orderby();

        $this->per_page = isset( $args['per_page'] ) ? $args['per_page'] : $this->get_per_page();

        if ( $this->is_filterable ) {
            $this->filterby = isset( $args['filterby'] ) ? $args['filterby'] : $this->get_filterby();
            $this->filter   = isset( $args['filter'] ) ? $args['filter'] : $this->get_filter();
        }

        if ( isset( $args['search'] ) ) {
            $this->search = $args['search'];
        }

    }

    /**
     * Define the structure of arguments used to pass to the get_results method
     *
     * @since 2.3.0
     * @since 3.28.0 Unknown.
     *
     * @return array
     */
    public function set_args() {
        return array(
            'per_page' => apply_filters( 'llms_table_' . $this->id . '_per_page', $this->per_page ),
        );
    }

    /**
     * Define the structure of the table
     *
     * @since 3.2.0
     * @since 3.15.0 Unknown.
     * @since 3.36.0 Add "Last Seen" column.
     *
     * @return array
     */
    public function set_columns() {
        return array(
            'id'                    => array(
                'exportable' => true,
                'sortable'   => true,
                'title'      => __( 'ID', 'lifterlms' ),
            ),
            'email'                 => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Email', 'lifterlms' ),
            ),
            'name'                  => array(
                'sortable' => true,
                'title'    => __( 'Name', 'lifterlms' ),
            ),
            'name_last'             => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Last Name', 'lifterlms' ),
            ),
            'name_first'            => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'First Name', 'lifterlms' ),
            ),
            'registered'            => array(
                'exportable' => true,
                'sortable'   => true,
                'title'      => __( 'Registration Date', 'lifterlms' ),
            ),
            'last_seen'             => array(
                'exportable' => true,
                'sortable'   => false,
                'title'      => __( 'Last Seen', 'lifterlms' ),
            ),
            'overall_progress'      => array(
                'exportable' => true,
                'sortable'   => true,
                'title'      => __( 'Progress', 'lifterlms' ),
            ),
            'overall_grade'         => array(
                'exportable' => true,
                'sortable'   => true,
                'title'      => __( 'Grade', 'lifterlms' ),
            ),
            'enrollments'           => array(
                'sortable' => false,
                'title'    => __( 'Enrollments', 'lifterlms' ),
            ),
            'completions'           => array(
                'sortable' => false,
                'title'    => __( 'Completions', 'lifterlms' ),
            ),
            'certificates'          => array(
                'sortable' => false,
                'title'    => __( 'Certificates', 'lifterlms' ),
            ),
            'achievements'          => array(
                'sortable' => false,
                'title'    => __( 'Achievements', 'lifterlms' ),
            ),
            'memberships'           => array(
                'sortable' => false,
                'title'    => __( 'Memberships', 'lifterlms' ),
            ),
            'billing_address_1'     => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing Address 1', 'lifterlms' ),
            ),
            'billing_address_2'     => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing Address 2', 'lifterlms' ),
            ),
            'billing_city'          => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing City', 'lifterlms' ),
            ),
            'billing_state'         => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing State', 'lifterlms' ),
            ),
            'billing_zip'           => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing Zip', 'lifterlms' ),
            ),
            'billing_country'       => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Billing Country', 'lifterlms' ),
            ),
            'phone'                 => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Phone', 'lifterlms' ),
            ),
            'courses_enrolled'      => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Courses (Enrolled)', 'lifterlms' ),
            ),
            'courses_cancelled'     => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Courses (Cancelled)', 'lifterlms' ),
            ),
            'courses_expired'       => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Courses (Expired)', 'lifterlms' ),
            ),
            'memberships_enrolled'  => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Memberships (Enrolled)', 'lifterlms' ),
            ),
            'memberships_cancelled' => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Memberships (Cancelled)', 'lifterlms' ),
            ),
            'memberships_expired'   => array(
                'exportable'  => true,
                'export_only' => true,
                'title'       => __( 'Memberships (Expired)', 'lifterlms' ),
            ),
        );
    }

    /**
     * Set the table's title.
     *
     * @since 3.28.0
     *
     * @return string
     */
    protected function set_title() {
        return __( 'Students', 'lifterlms' );
    }

}