From d07373f289d3637c96b6158d7b650f41841150f2 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 6 Jun 2017 12:05:09 +0800 Subject: [PATCH] MDL-58136 boost: Limit courses in flatnav Only show "in-progress" courses in the boost flat navigation (as per the myoverview block). --- .../classes/output/courses_view.php | 13 ++-- course/lib.php | 44 +++++++++++++ course/tests/courselib_test.php | 61 +++++++++++++++++++ lib/navigationlib.php | 11 +++- 4 files changed, 121 insertions(+), 8 deletions(-) diff --git a/blocks/myoverview/classes/output/courses_view.php b/blocks/myoverview/classes/output/courses_view.php index 31dc8c62813dd..798eb7b826c0e 100644 --- a/blocks/myoverview/classes/output/courses_view.php +++ b/blocks/myoverview/classes/output/courses_view.php @@ -63,8 +63,6 @@ public function __construct($courses, $coursesprogress) { * @return array */ public function export_for_template(renderer_base $output) { - $today = time(); - // Build courses view data structure. $coursesview = [ 'hascourses' => !empty($this->courses) @@ -73,8 +71,6 @@ public function export_for_template(renderer_base $output) { // How many courses we have per status? $coursesbystatus = ['past' => 0, 'inprogress' => 0, 'future' => 0]; foreach ($this->courses as $course) { - $startdate = $course->startdate; - $enddate = $course->enddate; $courseid = $course->id; $context = \context_course::instance($courseid); $exporter = new course_summary_exporter($course, [ @@ -84,14 +80,17 @@ public function export_for_template(renderer_base $output) { // Convert summary to plain text. $exportedcourse->summary = content_to_text($exportedcourse->summary, $exportedcourse->summaryformat); + $courseprogress = null; + + $classified = course_classify_for_timeline($course); + if (isset($this->coursesprogress[$courseid])) { - $coursecompleted = $this->coursesprogress[$courseid]['completed']; $courseprogress = $this->coursesprogress[$courseid]['progress']; $exportedcourse->hasprogress = !is_null($courseprogress); $exportedcourse->progress = $courseprogress; } - if ((isset($coursecompleted) && $coursecompleted) || (!empty($enddate) && $enddate < $today)) { + if ($classified == COURSE_TIMELINE_PAST) { // Courses that have already ended. $pastpages = floor($coursesbystatus['past'] / $this::COURSES_PER_PAGE); @@ -100,7 +99,7 @@ public function export_for_template(renderer_base $output) { $coursesview['past']['pages'][$pastpages]['page'] = $pastpages + 1; $coursesview['past']['haspages'] = true; $coursesbystatus['past']++; - } else if ($startdate > $today) { + } else if ($classified == COURSE_TIMELINE_FUTURE) { // Courses that have not started yet. $futurepages = floor($coursesbystatus['future'] / $this::COURSES_PER_PAGE); diff --git a/course/lib.php b/course/lib.php index e60bd2c48b4e2..2f8cbdd780705 100644 --- a/course/lib.php +++ b/course/lib.php @@ -55,6 +55,10 @@ define('MOD_CLASS_ACTIVITY', 0); define('MOD_CLASS_RESOURCE', 1); +define('COURSE_TIMELINE_PAST', 'past'); +define('COURSE_TIMELINE_INPROGRESS', 'inprogress'); +define('COURSE_TIMELINE_FUTURE', 'future'); + function make_log_url($module, $url) { switch ($module) { case 'course': @@ -4001,6 +4005,46 @@ function course_check_updates($course, $tocheck, $filter = array()) { return array($instances, $warnings); } +/** + * This function classifies a course as past, in progress or future. + * + * This function may incur a DB hit to calculate course completion. + * @param stdClass $course Course record + * @param stdClass $user User record (optional - defaults to $USER). + * @param completion_info $completioninfo Completion record for the user (optional - will be fetched if required). + * @return string (one of COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_PAST) + */ +function course_classify_for_timeline($course, $user = null, $completioninfo = null) { + global $USER; + + if ($user == null) { + $user = $USER; + } + + $today = time(); + // End date past. + if (!empty($course->enddate) && $course->enddate < $today) { + return COURSE_TIMELINE_PAST; + } + + if ($completioninfo == null) { + $completioninfo = new completion_info($course); + } + + // Course was completed. + if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) { + return COURSE_TIMELINE_PAST; + } + + // Start date not reached. + if (!empty($course->startdate) && $course->startdate > $today) { + return COURSE_TIMELINE_FUTURE; + } + + // Everything else is in progress. + return COURSE_TIMELINE_INPROGRESS; +} + /** * Check module updates since a given time. * This function checks for updates in the module config, file areas, completion, grades, comments and ratings. diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index 61169d5b0b5d1..3ae8f0d307a45 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -3684,4 +3684,65 @@ public function test_async_section_deletion_hook_not_implemented() { } $this->assertEquals(2, $count); } + + function test_classify_course_for_timeline() { + global $DB, $CFG; + + require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php'); + require_once($CFG->dirroot.'/completion/cron.php'); + + set_config('enablecompletion', COMPLETION_ENABLED); + + $this->resetAfterTest(true); + $this->setAdminUser(); + + // Create courses for testing. + $generator = $this->getDataGenerator(); + $future = time() + 3600; + $past = time() - 3600; + $futurecourse = $generator->create_course(['startdate' => $future]); + $pastcourse = $generator->create_course(['startdate' => $past - 60, 'enddate' => $past]); + $completedcourse = $generator->create_course(['enablecompletion' => COMPLETION_ENABLED]); + $inprogresscourse = $generator->create_course(); + + // Set completion rules. + $completion = new completion_info($completedcourse); + + $criteriadata = new stdClass(); + $criteriadata->id = $completedcourse->id; + $criteriadata->criteria_activity = array(); + + // Self completion. + $criteriadata->criteria_self = COMPLETION_CRITERIA_TYPE_SELF; + $class = 'completion_criteria_self'; + $criterion = new $class(); + $criterion->update_config($criteriadata); + + // Handle overall aggregation. + $aggdata = array( + 'course' => $completedcourse->id, + 'criteriatype' => null + ); + $aggregation = new completion_aggregation($aggdata); + $aggregation->setMethod(COMPLETION_AGGREGATION_ALL); + $aggregation->save(); + + $user = $this->getDataGenerator()->create_user(); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($user->id, $futurecourse->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user->id, $pastcourse->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user->id, $completedcourse->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($user->id, $inprogresscourse->id, $studentrole->id); + + $this->setUser($user); + core_completion_external::mark_course_self_completed($completedcourse->id); + $ccompletion = new completion_completion(array('course' => $completedcourse->id, 'userid' => $user->id)); + $ccompletion->mark_complete(); + + // Aggregate the completions. + $this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($pastcourse)); + $this->assertEquals(COURSE_TIMELINE_FUTURE, course_classify_for_timeline($futurecourse)); + $this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($completedcourse)); + $this->assertEquals(COURSE_TIMELINE_INPROGRESS, course_classify_for_timeline($inprogresscourse)); + } } diff --git a/lib/navigationlib.php b/lib/navigationlib.php index aa29bd931fadf..4707f9965bf78 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -2567,7 +2567,16 @@ public function add_course(stdClass $course, $forcegeneric = false, $coursetype } $coursenode = $parent->add($coursename, $url, self::TYPE_COURSE, $shortname, $course->id); - $coursenode->showinflatnavigation = $coursetype == self::COURSE_MY; + + // Do some calculation to see if the course is past, current or future. + if ($coursetype == self::COURSE_MY) { + $classify = course_classify_for_timeline($course); + + if ($classify == COURSE_TIMELINE_INPROGRESS) { + $coursenode->showinflatnavigation = true; + } + } + $coursenode->hidden = (!$course->visible); $coursenode->title(format_string($course->fullname, true, array('context' => $coursecontext, 'escape' => false))); if ($canexpandcourse) {