From b36ee81ea6941030f11dc7a31233b8b6a3cfafd5 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 10 Oct 2018 18:10:15 +0200 Subject: [PATCH] MDL-63523 enrol: Return extra fields for new dashboard WebService core_enrol_get_users_courses should return extra information for the app so we can display all the new dashboard information without having to do extra WS requests. --- enrol/externallib.php | 61 ++++++++++++++++++++++-- enrol/tests/externallib_test.php | 82 +++++++++++++++++++++++++++----- enrol/upgrade.txt | 10 ++++ 3 files changed, 135 insertions(+), 18 deletions(-) diff --git a/enrol/externallib.php b/enrol/externallib.php index 55b074c4164..6b06aa44eed 100644 --- a/enrol/externallib.php +++ b/enrol/externallib.php @@ -295,15 +295,18 @@ class core_enrol_external extends external_api { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/course/lib.php'); + require_once($CFG->libdir . '/completionlib.php'); // Do basic automatic PARAM checks on incoming data, using params description // If any problems are found then exceptions are thrown with helpful error messages $params = self::validate_parameters(self::get_users_courses_parameters(), array('userid'=>$userid)); - $courses = enrol_get_users_courses($params['userid'], true, 'id, shortname, fullname, idnumber, visible, - summary, summaryformat, format, showgrades, lang, enablecompletion, category, startdate, enddate'); + $courses = enrol_get_users_courses($params['userid'], true, '*'); $result = array(); + // Get user data including last access to courses. + $user = get_complete_user_data('id', $userid); + foreach ($courses as $course) { $context = context_course::instance($course->id, IGNORE_MISSING); try { @@ -313,7 +316,8 @@ class core_enrol_external extends external_api { continue; } - if ($userid != $USER->id and !course_can_view_participants($context)) { + $sameuser = $USER->id == $userid; + if (!$sameuser and !course_can_view_participants($context)) { // we need capability to view participants continue; } @@ -322,20 +326,58 @@ class core_enrol_external extends external_api { $enrolledsql = "SELECT COUNT('x') FROM ($enrolledsqlselect) enrolleduserids"; $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams); + $displayname = external_format_string(get_course_display_name_for_list($course), $context->id); list($course->summary, $course->summaryformat) = external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null); $course->fullname = external_format_string($course->fullname, $context->id); $course->shortname = external_format_string($course->shortname, $context->id); $progress = null; - if ($course->enablecompletion) { - $progress = \core_completion\progress::get_course_progress_percentage($course); + $completed = null; + + // Return only private information if the user should be able to see it. + if ($sameuser || completion_can_view_data($userid, $course)) { + if ($course->enablecompletion) { + $completion = new completion_info($course); + $completed = $completion->is_course_complete($userid); + $progress = \core_completion\progress::get_course_progress_percentage($course, $userid); + } + } + + $lastaccess = null; + // Check if last access is a hidden field. + $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); + $canviewlastaccess = $sameuser || !isset($hiddenfields['lastaccess']); + if (!$canviewlastaccess) { + $canviewlastaccess = has_capability('moodle/course:viewhiddenuserfields', $context); + } + + if ($canviewlastaccess && isset($user->lastcourseaccess[$course->id])) { + $lastaccess = $user->lastcourseaccess[$course->id]; + } + + // Retrieve course overview used files. + $courselist = new core_course_list_element($course); + $overviewfiles = array(); + foreach ($courselist->get_course_overviewfiles() as $file) { + $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(), + $file->get_filearea(), null, $file->get_filepath(), + $file->get_filename())->out(false); + $overviewfiles[] = array( + 'filename' => $file->get_filename(), + 'fileurl' => $fileurl, + 'filesize' => $file->get_filesize(), + 'filepath' => $file->get_filepath(), + 'mimetype' => $file->get_mimetype(), + 'timemodified' => $file->get_timemodified(), + ); } $result[] = array( 'id' => $course->id, 'shortname' => $course->shortname, 'fullname' => $course->fullname, + 'displayname' => $displayname, 'idnumber' => $course->idnumber, 'visible' => $course->visible, 'enrolledusercount' => $enrolledusercount, @@ -347,8 +389,12 @@ class core_enrol_external extends external_api { 'enablecompletion' => $course->enablecompletion, 'category' => $course->category, 'progress' => $progress, + 'completed' => $completed, 'startdate' => $course->startdate, 'enddate' => $course->enddate, + 'marker' => $course->marker, + 'lastaccess' => $lastaccess, + 'overviewfiles' => $overviewfiles, ); } @@ -367,6 +413,7 @@ class core_enrol_external extends external_api { 'id' => new external_value(PARAM_INT, 'id of course'), 'shortname' => new external_value(PARAM_RAW, 'short name of course'), 'fullname' => new external_value(PARAM_RAW, 'long name of course'), + 'displayname' => new external_value(PARAM_TEXT, 'course display name for lists.', VALUE_OPTIONAL), 'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course'), 'idnumber' => new external_value(PARAM_RAW, 'id number of course'), 'visible' => new external_value(PARAM_INT, '1 means visible, 0 means hidden course'), @@ -379,8 +426,12 @@ class core_enrol_external extends external_api { VALUE_OPTIONAL), 'category' => new external_value(PARAM_INT, 'course category id', VALUE_OPTIONAL), 'progress' => new external_value(PARAM_FLOAT, 'Progress percentage', VALUE_OPTIONAL), + 'completed' => new external_value(PARAM_BOOL, 'Whether the course is completed.', VALUE_OPTIONAL), 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL), 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL), + 'marker' => new external_value(PARAM_INT, 'Course section marker.', VALUE_OPTIONAL), + 'lastaccess' => new external_value(PARAM_INT, 'Last access to the course (timestamp).', VALUE_OPTIONAL), + 'overviewfiles' => new external_files('Overview files attached to this course.', VALUE_OPTIONAL), ) ) ); diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php index 22c3c5c5e16..90029e272c3 100644 --- a/enrol/tests/externallib_test.php +++ b/enrol/tests/externallib_test.php @@ -359,9 +359,10 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { * Test get_users_courses */ public function test_get_users_courses() { - global $USER; + global $CFG, $DB; $this->resetAfterTest(true); + $CFG->enablecompletion = 1; $timenow = time(); $coursedata1 = array( @@ -373,7 +374,8 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { 'enablecompletion' => true, 'showgrades' => true, 'startdate' => $timenow, - 'enddate' => $timenow + WEEKSECS + 'enddate' => $timenow + WEEKSECS, + 'marker' => 1 ); $coursedata2 = array( @@ -383,21 +385,32 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { $course1 = self::getDataGenerator()->create_course($coursedata1); $course2 = self::getDataGenerator()->create_course($coursedata2); $courses = array($course1, $course2); + $contexts = array ($course1->id => context_course::instance($course1->id), + $course2->id => context_course::instance($course2->id)); - // Enrol $USER in the courses. - // We use the manual plugin. - $roleid = null; - $contexts = array(); - foreach ($courses as $course) { - $contexts[$course->id] = context_course::instance($course->id); - $roleid = $this->assignUserCapability('moodle/course:viewparticipants', - $contexts[$course->id]->id, $roleid); + $student = $this->getDataGenerator()->create_user(); + $otherstudent = $this->getDataGenerator()->create_user(); + $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentroleid); + $this->getDataGenerator()->enrol_user($otherstudent->id, $course1->id, $studentroleid); + $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentroleid); - $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid, 'manual'); - } + // Force last access. + $timenow = time(); + $lastaccess = array( + 'userid' => $student->id, + 'courseid' => $course1->id, + 'timeaccess' => $timenow + ); + $DB->insert_record('user_lastaccess', $lastaccess); + // Force completion. + $ccompletion = new completion_completion(array('course' => $course1->id, 'userid' => $student->id)); + $ccompletion->mark_complete(); + + $this->setUser($student); // Call the external function. - $enrolledincourses = core_enrol_external::get_users_courses($USER->id); + $enrolledincourses = core_enrol_external::get_users_courses($student->id); // We need to execute the return values cleaning process to simulate the web service server. $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); @@ -418,11 +431,54 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { foreach ($coursedata1 as $fieldname => $value) { $this->assertEquals($courseenrol[$fieldname], $course1->$fieldname); } + // Text extra fields. + $this->assertEquals($course1->fullname, $courseenrol['displayname']); + $this->assertEquals([], $courseenrol['overviewfiles']); + $this->assertEquals($timenow, $courseenrol['lastaccess']); + $this->assertEquals(100.0, $courseenrol['progress']); + $this->assertEquals(true, $courseenrol['completed']); } else { // Check language pack. Should be empty since an incorrect one was used when creating the course. $this->assertEmpty($courseenrol['lang']); + $this->assertEquals($course2->fullname, $courseenrol['displayname']); + $this->assertEquals([], $courseenrol['overviewfiles']); + $this->assertEquals(0, $courseenrol['lastaccess']); + $this->assertEquals(0, $courseenrol['progress']); + $this->assertEquals(false, $courseenrol['completed']); } } + + // Now check that admin users can see all the info. + $this->setAdminUser(); + + $enrolledincourses = core_enrol_external::get_users_courses($student->id); + $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); + $this->assertEquals(2, count($enrolledincourses)); + foreach ($enrolledincourses as $courseenrol) { + if ($courseenrol['id'] == $course1->id) { + $this->assertEquals($timenow, $courseenrol['lastaccess']); + $this->assertEquals(100.0, $courseenrol['progress']); + } else { + $this->assertEquals(0, $courseenrol['progress']); + } + } + + // Check other users can't see private info. + $this->setUser($otherstudent); + + $enrolledincourses = core_enrol_external::get_users_courses($student->id); + $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); + $this->assertEquals(1, count($enrolledincourses)); + + $this->assertEquals($timenow, $enrolledincourses[0]['lastaccess']); // I can see this, not hidden. + $this->assertEquals(null, $enrolledincourses[0]['progress']); // I can't see this, private. + + // Change some global profile visibility fields. + $CFG->hiddenuserfields = 'lastaccess'; + $enrolledincourses = core_enrol_external::get_users_courses($student->id); + $enrolledincourses = external_api::clean_returnvalue(core_enrol_external::get_users_courses_returns(), $enrolledincourses); + + $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting. } /** diff --git a/enrol/upgrade.txt b/enrol/upgrade.txt index 09ff3baf9bd..69d47feaeb3 100644 --- a/enrol/upgrade.txt +++ b/enrol/upgrade.txt @@ -1,6 +1,16 @@ This files describes API changes in /enrol/* - plugins, information provided here is intended especially for developers. +=== 3.6 === + +* External function core_enrol_external::get_users_courses now return more information to avoid multiple queries to build the + user dashboard: + - displayname: Course display name for lists. + - marker: Course section active marker. + - completed: Whether the given user completed the course or not. + - lastaccess: Last time the user accessed the course. + - overviewfiles: Course overview files. + === 3.5 === * Default sorting in enrol_get_my_courses(), enrol_get_all_users_courses() and enrol_get_users_courses() now respects