includes/server/class-llms-rest-students-controller.php
<?php
/**
* REST Resource Controller for Students.
*
* @package LifterLMS_REST/Classes/Controllers
*
* @since 1.0.0-beta.1
* @version 1.0.0-beta.27
*/
defined( 'ABSPATH' ) || exit;
/**
* LLMS_REST_Students_Controller class.
*
* @since 1.0.0-beta.1
* @since 1.0.0-beta.7 Added `prepare_args_for_total_count_query()` method override.
* @since 1.0.0-beta.12 Added item schema filter.
* Added 'llms_rest_student_registered' action hook - fired after student's creation.
* @since 1.0.0-beta.14 Update `prepare_links()` to accept a second parameter, `WP_REST_Request`.
*/
class LLMS_REST_Students_Controller extends LLMS_REST_Users_Controller {
/**
* Resource ID or Name.
*
* @var string
*/
protected $resource_name = 'student';
/**
* Route base.
*
* @var string
*/
protected $rest_base = 'students';
/**
* Temporary array of prepared query args used to filter WP_User_Query
* when `enrolled_in` and `enrolled_not_in` args are present on the request.
*
* @var array
*/
private $prepared_query_args = array();
/**
* Determine if the current user can view the requested student.
*
* @since 1.0.0-beta.1
*
* @param int $item_id WP_User id.
* @return bool
*/
protected function check_read_item_permissions( $item_id ) {
if ( get_current_user_id() === $item_id ) {
return true;
}
return current_user_can( 'view_students', $item_id );
}
/**
* Determine if current user has permission to create a user.
*
* @since 1.0.0-beta.1
*
* @param WP_REST_Request $request Request object.
* @return true|WP_Error
*/
public function create_item_permissions_check( $request ) {
if ( ! current_user_can( 'create_students' ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to create new students.', 'lifterlms' ) );
}
return $this->check_roles_permissions( $request );
}
/**
* Determine if current user has permission to delete a user.
*
* @since 1.0.0-beta.1
*
* @param WP_REST_Request $request Request object.
* @return true|WP_Error
*/
public function delete_item_permissions_check( $request ) {
if ( ! current_user_can( 'delete_students', $request['id'] ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to delete this student.', 'lifterlms' ) );
}
return true;
}
/**
* Retrieves the query params for the objects collection.
*
* @since 1.0.0-beta.1
*
* @return array Collection parameters.
*/
public function get_collection_params() {
$params = parent::get_collection_params();
$params['enrolled_in'] = array(
'description' => __( 'Retrieve only students enrolled in the specified course(s) and/or membership(s). Accepts a single WP Post ID or a comma separated list of IDs.', 'lifterlms' ),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
);
$params['enrolled_not_in'] = array(
'description' => __( 'Retrieve only students not enrolled in the specified course(s) and/or membership(s). Accepts a single WP Post ID or a comma separated list of IDs.', 'lifterlms' ),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
);
return $params;
}
/**
* Get the item schema base.
*
* @since 1.0.0-beta.27
*
* @return array
*/
public function get_item_schema_base() {
$schema = parent::get_item_schema_base();
$schema['properties']['roles']['default'] = array( 'student' );
return $schema;
}
/**
* Determine if current user has permission to get a user.
*
* @since 1.0.0-beta.1
*
* @param WP_REST_Request $request Request object.
* @return true|WP_Error
*/
public function get_item_permissions_check( $request ) {
if ( ! $this->check_read_item_permissions( $request['id'] ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to view this student.', 'lifterlms' ) );
}
return true;
}
/**
* Determine if current user has permission to list users.
*
* @since 1.0.0-beta.1
*
* @param WP_REST_Request $request Request object.
* @return true|WP_Error
*/
public function get_items_permissions_check( $request ) {
if ( ! empty( $request['roles'] ) && ! current_user_can( 'view_others_students' ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to filter students by role.', 'lifterlms' ) );
}
if ( ! current_user_can( 'view_students' ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to list students.', 'lifterlms' ) );
}
return true;
}
/**
* Get object.
*
* @since 1.0.0-beta.1
* @since 1.0.0-beta.26 Don't autoload current user if a falsy user id is supplied.
*
* @param int $id Object ID.
* @return LLMS_Student|WP_Error
*/
protected function get_object( $id ) {
$student = llms_get_student( $id, false );
return $student ? $student : llms_rest_not_found_error();
}
/**
* Retrieve a query object based on arguments from a `get_items()` (collection) request.
*
* @since 1.0.0-beta.1
*
* @param array $prepared Array of collection arguments.
* @param WP_REST_Request $request Request object.
* @return WP_User_Query
*/
protected function get_objects_query( $prepared, $request ) {
$remove = false;
if ( ! empty( $prepared['enrolled_in'] ) || ! empty( $prepared['enrolled_not_in'] ) ) {
$this->prepared_query_args = $prepared;
add_action( 'pre_user_query', array( $this, 'get_objects_query_pre' ) );
$remove = true;
}
$query = parent::get_objects_query( $prepared, $request );
if ( $remove ) {
$this->prepared_query_args = array();
remove_action( 'pre_user_query', array( $this, 'get_objects_query_pre' ) );
}
return $query;
}
/**
* Format query arguments to retrieve a collection of objects
*
* @since 1.0.2
*
* @param WP_REST_Request $request Full details about the request.
* @return array|WP_Error
*/
protected function prepare_collection_query_args( $request ) {
$query_args = parent::prepare_collection_query_args( $request );
if ( is_wp_error( $query_args ) ) {
return $query_args;
}
if ( empty( $request['roles'] ) ) {
$query_args = array_merge(
$query_args,
array(
'roles' =>
$this->get_item_schema_base()['properties']['roles']['default'],
)
);
}
return $query_args;
}
/**
* Callback for WP_User_Query "pre_user_query" action.
*
* Adds select fields and a having clause to check against `enrolled_in` and `enrolled_not_in` collection query args.
*
* @since 1.0.0-beta.1
*
* @link https://developer.wordpress.org/reference/hooks/pre_user_query/
*
* @param WP_User_Query $query Query object.
* @return void
*/
public function get_objects_query_pre( $query ) {
$query->query_where .= ' Having 1 ';
if ( ! empty( $this->prepared_query_args['enrolled_in'] ) ) {
foreach ( $this->prepared_query_args['enrolled_in'] as $post_id ) {
$post_id = absint( $post_id );
$query->query_fields .= ", {$this->get_objects_query_status_subquery( $post_id )}";
$query->query_where .= " AND p_{$post_id}_stat = 'enrolled'";
}
}
if ( ! empty( $this->prepared_query_args['enrolled_not_in'] ) ) {
foreach ( $this->prepared_query_args['enrolled_not_in'] as $post_id ) {
$post_id = absint( $post_id );
$query->query_fields .= ", {$this->get_objects_query_status_subquery( $post_id )}";
$query->query_where .= " AND ( p_{$post_id}_stat IS NULL OR p_{$post_id}_stat != 'enrolled' )";
}
}
}
/**
* Generates a subquery to check a user's enrollment status for a given course or membership.
*
* @since 1.0.0-beta.1
*
* @param int $post_id Course or membership id.
* @return string
*/
private function get_objects_query_status_subquery( $post_id ) {
global $wpdb;
return "(
SELECT meta_value
FROM {$wpdb->prefix}lifterlms_user_postmeta
WHERE user_id = {$wpdb->users}.ID
AND post_id = {$post_id}
AND meta_key = '_status'
ORDER BY updated_date DESC
LIMIT 1
) AS p_{$post_id}_stat";
}
/**
* Prepare query args for total count query.
*
* @since 1.0.0-beta.7
*
* @param array $args Array of query args.
* @param WP_REST_Request $request Full details about the request.
* @return array
*/
protected function prepare_args_for_total_count_query( $args, $request ) {
// Run the query again on page one to get a proper total count.
$args['page'] = 1;
return $args;
}
/**
* Called right after a student is completely inserted (created/updated).
*
* @since 1.0.0-beta.12
*
* @param LLMS_Student $student Inserted or updated llms student.
* @param WP_REST_Request $request Request object.
* @param array $schema The item schema.
* @param bool $creating True when creating a post, false when updating.
*/
protected function object_completely_inserted( $student, $request, $schema, $creating ) {
parent::object_completely_inserted( $student, $request, $schema, $creating );
if ( $creating ) {
/**
* Fires after a LifterLMS student has been created via the REST API.
*
* @since 1.0.0-beta.12
*
* @param LLMS_Student $student Inserted or updated llms student.
* @param WP_REST_Request $request Request object.
* @param array $schema The item schema.
*/
do_action( 'llms_rest_student_registered', $this->get_object_id( $student ), $request, $schema );
}
}
/**
* Prepare links for the request.
*
* @since 1.0.0-beta.1
* @since 1.0.0-beta.14 Added `$request` parameter.
*
* @param obj $object Item object.
* @param WP_REST_Request $request Request object.
* @return array
*/
protected function prepare_links( $object, $request ) {
$links = parent::prepare_links( $object, $request );
$links['enrollments'] = array(
'href' => sprintf( '%s/enrollments', $links['self']['href'] ),
);
$links['progress'] = array(
'href' => sprintf( '%s/progress', $links['self']['href'] ),
);
return $links;
}
/**
* Determine if current user has permission to update a user.
*
* @since 1.0.0-beta.1
*
* @param WP_REST_Request $request Request object.
* @return true|WP_Error
*/
public function update_item_permissions_check( $request ) {
if ( get_current_user_id() === $request['id'] ) {
return true;
}
if ( ! current_user_can( 'edit_students', $request['id'] ) ) {
return llms_rest_authorization_required_error( __( 'You are not allowed to edit this student.', 'lifterlms' ) );
}
return $this->check_roles_permissions( $request );
}
}