From ed1d72ea70411274c428a3842491bf36d763fa8d Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Thu, 11 Nov 2010 03:37:56 +0000 Subject: [PATCH] navigation MDL-25142 Fixed up differences in navigation between guest and normal user. As part of this patch I also introduced a new method in accesslib can_access_course that can be used to check a users access to a course. I also fixed a minor issue with guest access flag caching in require_login --- enrol/guest/lib.php | 2 +- lib/accesslib.php | 87 +++++++++++++++++++++++++++++++++++++++++++ lib/enrollib.php | 4 ++ lib/moodlelib.php | 2 + lib/navigationlib.php | 11 +++--- 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/enrol/guest/lib.php b/enrol/guest/lib.php index f31a46863a7..570539c6bc0 100644 --- a/enrol/guest/lib.php +++ b/enrol/guest/lib.php @@ -78,7 +78,7 @@ class enrol_guest_plugin extends enrol_plugin { // Temporarily assign them some guest role for this context $context = get_context_instance(CONTEXT_COURSE, $instance->courseid); $USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access); - return ENROL_REQUIRE_LOGIN_CACHE_PERIOD; + return ENROL_REQUIRE_LOGIN_CACHE_PERIOD + time(); } return false; diff --git a/lib/accesslib.php b/lib/accesslib.php index d6d6a08e0d1..b393851aa7a 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -2978,6 +2978,93 @@ function is_enrolled($context, $user = null, $withcapability = '', $onlyactive = return true; } +/** + * Returns true if the user is able to access the course. + * + * This function is in no way, shape, or form a substitute for require_login. + * It should only be used in circumstances where it is not possible to call require_login + * such as the navigation. + * + * This function checks many of the methods of access to a course such as the view + * capability, enrollments, and guest access. It also makes use of the cache + * generated by require_login for guest access. + * + * The flags within the $USER object that are used here should NEVER be used outside + * of this function can_access_course and require_login. Doing so WILL break future + * versions. + * + * @global moodle_database $DB + * @param stdClass $context + * @param stdClass|null $user + * @param string $withcapability Check for this capability as well. + * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions + * @param boolean $trustcache If set to false guest access will always be checked + * against the enrolment plugins from the course, rather + * than the cache generated by require_login. + * @return boolean Returns true if the user is able to access the course + */ +function can_access_course($context, $user = null, $withcapability = '', $onlyactive = false, $trustcache = true) { + global $DB, $USER; + + $coursecontext = get_course_context($context); + $courseid = $coursecontext->instanceid; + + // First check the obvious, is the user viewing or is the user enrolled. + if (is_viewing($coursecontext, $user, $withcapability) || is_enrolled($coursecontext, $user, $withcapability, $onlyactive)) { + // How easy was that! + return true; + } + + $access = false; + if (!isset($USER->enrol)) { + // Cache hasn't been generated yet so we can't trust it + $trustcache = false; + /** + * These flags within the $USER object should NEVER be used outside of this + * function can_access_course and the function require_login. + * Doing so WILL break future versions!!!! + */ + $USER->enrol = array(); + $USER->enrol['enrolled'] = array(); + $USER->enrol['tempguest'] = array(); + } + + // If we don't trust the cache we need to check with the courses enrolment + // plugin instances to see if the user can access the course as a guest. + if (!$trustcache) { + // Ok, off to the database we go! + $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC'); + $enrols = enrol_get_plugins(true); + foreach($instances as $instance) { + if (!isset($enrols[$instance->enrol])) { + continue; + } + $until = $enrols[$instance->enrol]->try_guestaccess($instance); + if ($until !== false) { + // Never use me anywhere but here and require_login + $USER->enrol['tempguest'][$courseid] = $until; + $access = true; + break; + } + } + } + + // If we don't already have access (from above) check the cache and see whether + // there is record of it in there. + if (!$access && isset($USER->enrol['tempguest'][$courseid])) { + // Never use me anywhere but here and require_login + if ($USER->enrol['tempguest'][$courseid] == 0) { + $access = true; + } else if ($USER->enrol['tempguest'][$courseid] > time()) { + $access = true; + } else { + //expired + unset($USER->enrol['tempguest'][$courseid]); + } + } + return $access; +} + /** * Returns array with sql code and parameters returning all ids * of users enrolled into course. diff --git a/lib/enrollib.php b/lib/enrollib.php index 8e7741c915b..5c338b5008f 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -958,6 +958,8 @@ abstract class enrol_plugin { * Attempt to automatically enrol current user in course without any interaction, * calling code has to make sure the plugin and instance are active. * + * This should return either a timestamp in the future or false. + * * @param stdClass $instance course enrol instance * @param stdClass $user record * @return bool|int false means not enrolled, integer means timeend @@ -972,6 +974,8 @@ abstract class enrol_plugin { * Attempt to automatically gain temporary guest access to course, * calling code has to make sure the plugin and instance are active. * + * This should return either a timestamp in the future or false. + * * @param stdClass $instance course enrol instance * @param stdClass $user record * @return bool|int false means no guest access, integer means timeend diff --git a/lib/moodlelib.php b/lib/moodlelib.php index ff9f6661fdd..a915dd83ef1 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -2540,6 +2540,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $ if (!isset($enrols[$instance->enrol])) { continue; } + // Get a duration for the guestaccess, a timestamp in the future or false. $until = $enrols[$instance->enrol]->try_autoenrol($instance); if ($until !== false) { $USER->enrol['enrolled'][$course->id] = $until; @@ -2554,6 +2555,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $ if (!isset($enrols[$instance->enrol])) { continue; } + // Get a duration for the guestaccess, a timestamp in the future or false. $until = $enrols[$instance->enrol]->try_guestaccess($instance); if ($until !== false) { $USER->enrol['tempguest'][$course->id] = $until; diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 2733be21cf6..318d1de478f 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -990,7 +990,8 @@ class global_navigation extends navigation_node { // course node and not populate it. $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); // Not enrolled, can't view, and hasn't switched roles - if ((!is_enrolled($coursecontext) && !has_capability('moodle/course:view', $coursecontext) && !is_role_switched($course->id))) { + + if (!can_access_course($coursecontext)) { $coursenode->make_active(); $canviewcourseprofile = false; break; @@ -1014,7 +1015,7 @@ class global_navigation extends navigation_node { // If the user is not enrolled then we only want to show the // course node and not populate it. $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - if ($course->id !== SITEID && !is_enrolled($coursecontext) && !has_capability('moodle/course:view', $coursecontext)) { + if (can_access_course($coursecontext)) { if ($coursenode) { $coursenode->make_active(); } @@ -1069,7 +1070,7 @@ class global_navigation extends navigation_node { // If the user is not enrolled then we only want to show the // course node and not populate it. $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - if (!is_enrolled($coursecontext) && !has_capability('moodle/course:view', $coursecontext)) { + if (can_access_course($coursecontext)) { $coursenode->make_active(); $canviewcourseprofile = false; break; @@ -1763,7 +1764,7 @@ class global_navigation extends navigation_node { $usercoursenode->add(get_string('notes', 'notes'), $url, self::TYPE_SETTING); } - if (has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $usercourse->id))) { + if (can_access_course(get_context_instance(CONTEXT_COURSE, $usercourse->id), $user->id)) { $usercoursenode->add(get_string('entercourse'), new moodle_url('/course/view.php', array('id'=>$usercourse->id)), self::TYPE_SETTING, null, null, new pix_icon('i/course', '')); } @@ -3288,7 +3289,7 @@ class settings_navigation extends navigation_node { return false; } } else { - if ((!has_capability('moodle/user:viewdetails', $coursecontext) && !has_capability('moodle/user:viewdetails', $usercontext)) || !is_enrolled($coursecontext, $user->id)) { + if ((!has_capability('moodle/user:viewdetails', $coursecontext) && !has_capability('moodle/user:viewdetails', $usercontext)) || !can_access_course($coursecontext, $user->id)) { return false; } if (groups_get_course_groupmode($course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $coursecontext)) {