Skip to content

Commit

Permalink
Merge pull request #7700 from Automattic/add/filter-student-managemen…
Browse files Browse the repository at this point in the history
…t-teacher-ids

Allow additional users to manage students
  • Loading branch information
donnapep authored Nov 4, 2024
2 parents 1217402 + 3d5202c commit afc74b1
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 34 deletions.
4 changes: 4 additions & 0 deletions changelog/add-filter-student-management-teacher-ids
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Allow additional users to manage students
96 changes: 62 additions & 34 deletions includes/admin/class-sensei-learner-management.php
Original file line number Diff line number Diff line change
Expand Up @@ -482,14 +482,15 @@ public function edit_date_started() {
exit( '' );
}

// validate we can edit date.
$may_edit_date = false;
$post_type = get_post_type( $post_id );

if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === (int) $post->post_author ) {
$may_edit_date = true;
if ( 'lesson' === $post_type ) {
$can_edit_date = $this->can_user_manage_students( (int) Sensei()->lesson->get_course_id( $post_id ), intval( $post->post_author ) );
} else {
$can_edit_date = $this->can_user_manage_students( $post_id, intval( $post->post_author ) );
}

if ( ! $may_edit_date ) {
if ( ! $can_edit_date ) {
exit( '' );
}

Expand Down Expand Up @@ -557,28 +558,28 @@ public function handle_user_async_action( $action ) {

$post = get_post( intval( $action_data['post_id'] ) );

if ( empty( $post ) ) {
if ( empty( $post ) || ! is_a( $post, 'WP_Post' ) ) {
exit( '' );
}

// validate the user.
$may_remove_user = false;
$can_remove_user = false;
$post_id = $action_data['post_id'] ? intval( $action_data['post_id'] ) : 0;
$post_type = $action_data['post_type'] ? sanitize_text_field( $action_data['post_type'] ) : '';

// Only teachers and admins can remove users.
if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === intval( $post->post_author ) ) {
$may_remove_user = true;
if ( 'lesson' === $post_type ) {
$can_remove_user = $this->can_user_manage_students( (int) Sensei()->lesson->get_course_id( $post_id ), intval( $post->post_author ) );
} else {
$can_remove_user = $this->can_user_manage_students( $post_id, intval( $post->post_author ) );
}

if ( ! is_a( $post, 'WP_Post' ) || ! $may_remove_user ) {
if ( ! $can_remove_user ) {
exit( '' );
}

if ( $action_data['user_id'] && $action_data['post_id'] && $action_data['post_type'] ) {
$user_id = intval( $action_data['user_id'] );
$post_id = intval( $action_data['post_id'] );
$post_type = sanitize_text_field( $action_data['post_type'] );
if ( $action_data['user_id'] && $post_id && $post_type ) {
$user_id = intval( $action_data['user_id'] );
$user = get_userdata( $user_id );

$user = get_userdata( $user_id );
if ( false === $user ) {
exit( '' );
}
Expand Down Expand Up @@ -648,22 +649,22 @@ public function handle_learner_actions() {
exit;
}

$user_id = intval( $_GET['user_id'] );
$course_id = intval( $_GET['course_id'] );
$post = get_post( $course_id );

$may_manage_enrolment = false;

// Only teachers and admins can enrol and withdraw users.
if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === intval( $post->post_author ) ) {
$may_manage_enrolment = true;
if ( ! is_a( $post, 'WP_Post' ) ) {
wp_safe_redirect( esc_url_raw( $failed_redirect_url ) );
exit;
}

if ( ! is_a( $post, 'WP_Post' ) || ! $may_manage_enrolment ) {
$can_manage_enrolment = $this->can_user_manage_students( $course_id, intval( $post->post_author ) );

if ( ! $can_manage_enrolment ) {
wp_safe_redirect( esc_url_raw( $failed_redirect_url ) );
exit;
}

$user_id = intval( $_GET['user_id'] );
$course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id );
$result = false;

Expand Down Expand Up @@ -697,6 +698,39 @@ public function remove_user_from_post() {
$this->handle_user_async_action( 'remove' );
}

/**
* Check if a user can manage a student's course.
*
* @param int $course_id Course ID.
* @param int $post_author Author ID for the course (i.e. teacher ID).
*
* @return bool true if the current user can manage a student's course.
*/
private function can_user_manage_students( int $course_id, int $post_author ): bool {
$allowed_ids = [];
$can_manage_student = false;

/**
* Filter the user IDs that have permission to manage students in a given course.
*
* @since $$next-version$$
*
* @hook sensei_learners_allowed_user_ids
*
* @param {int[]} $user_ids User IDs that have permission to manage students in a given course.
* Defaults to post author.
* @param {int} $course_id Course ID.
* @return {int[]} Filtered user IDs that have permission to manage students in a given course.
*/
$allowed_ids = apply_filters( 'sensei_learners_allowed_user_ids', [ $post_author ], $course_id );

if ( current_user_can( 'manage_sensei' ) || in_array( get_current_user_id(), $allowed_ids, true ) ) {
$can_manage_student = true;
}

return $can_manage_student;
}

/**
* Searches for a Learner by name or username.
*/
Expand Down Expand Up @@ -773,18 +807,12 @@ public function add_new_learners() {

$post_type = $_POST['add_post_type'];
$user_ids = array_map( 'intval', $_POST['add_user_id'] );
$course_id = absint( $_POST['add_course_id'] );
$lesson_id = isset( $_POST['add_lesson_id'] ) ? $_POST['add_lesson_id'] : '';
$course_id = intval( $_POST['add_course_id'] );
$lesson_id = intval( $_POST['add_lesson_id'] );
$course = get_post( $course_id );
$results = [];

$course = get_post( $course_id );
if (
! $course
|| (
! current_user_can( 'manage_sensei' )
&& get_current_user_id() !== intval( $course->post_author )
)
) {
if ( ! $course || ! $this->can_user_manage_students( $course_id, intval( $course->post_author ) ) ) {
return $result;
}

Expand Down
100 changes: 100 additions & 0 deletions tests/unit-tests/admin/test-class-sensei-learner-management.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
/**
* Tests student management functionality (formerly known as Learner Management).
*
* @package sensei
*/

/**
* Tests for Sensei_Learner_Management class.
*/
class Sensei_Learner_Management_Test extends WP_UnitTestCase {
/**
* Sensei_Factory instance.
*
* @var Sensei_Factory
*/
protected $factory;

/**
* Sensei_Learner_Management instance.
*
* @var Sensei_Learner_Management
*/
protected $learner_management;

public function setUp(): void {
parent::setUp();

$this->factory = new Sensei_Factory();
$this->learner_management = new Sensei_Learner_Management( '' );
}

public function tearDown(): void {
parent::tearDown();

$this->factory->tearDown();
}

/**
* Tests that students cannot be added to a course if current user is not the teacher.
*
* @covers Sensei_Learner_Management::add_new_learners
*/
public function testAddNewLearners_ToCourseWhenCurrentUserIsNotTeacher_ReturnsFalse() {
/* Arrange. */
$teacher_id = $this->factory->user->create();
$student_id = $this->factory->user->create();
$current_user_id = $this->factory->user->create();

wp_set_current_user( $current_user_id );

$_POST['add_learner_submit'] = 'some_value';
$_POST['add_learner_nonce'] = wp_create_nonce( 'add_learner_to_sensei' );
$_POST['add_post_type'] = 'course';
$_POST['add_user_id'] = [ $student_id ];
$_POST['add_course_id'] = $this->factory->course->create( [ 'post_author' => $teacher_id ] );
$_POST['add_lesson_id'] = 0;

/* Act. */
$result = $this->learner_management->add_new_learners();

/* Assert. */
$this->assertFalse( $result );
}

/**
* Tests that students cannot be added to a lesson if current user is not the teacher.
*
* @covers Sensei_Learner_Management::add_new_learners
*/
public function testAddNewLearners_ToLessonWhenCurrentUserIsNotTeacher_ReturnsFalse() {
/* Arrange. */
$teacher_id = $this->factory->user->create();
$student_id = $this->factory->user->create();
$current_user_id = $this->factory->user->create();
$course_id = $this->factory->course->create( [ 'post_author' => $teacher_id ] );
$lesson_id = $this->factory->lesson->create(
[
'meta_input' => [
'_lesson_course' => $course_id,
],
]
);

wp_set_current_user( $current_user_id );

$_POST['add_learner_submit'] = 'some_value';
$_POST['add_learner_nonce'] = wp_create_nonce( 'add_learner_to_sensei' );
$_POST['add_post_type'] = 'lesson';
$_POST['add_user_id'] = [ $student_id ];
$_POST['add_course_id'] = $course_id;
$_POST['add_lesson_id'] = $lesson_id;

/* Act. */
$result = $this->learner_management->add_new_learners();

/* Assert. */
$this->assertFalse( $result );
}
}

0 comments on commit afc74b1

Please sign in to comment.