/** * Check whether a user has a particular capability in a given context. * * For example: * $context = get_context_instance(CONTEXT_MODULE, $cm->id); * has_capability('mod/forum:replypost',$context) * * By default checks the capabilities of the current user, but you can pass a * different userid. By default will return true for admin users, but you can override that with the fourth argument. * * Guest and not-logged-in users can never get any dangerous capability - that is any write capability * or capabilities with XSS, config or data loss risks. * * @param string $capability the name of the capability to check. For example mod/forum:view * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}. * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user. * @param boolean $doanything If false, ignores effect of admin role assignment * @return boolean true if the user has this capability. Otherwise false. */ function has_capability($capability, context $context, $user = null, $doanything = true) { global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE; if (during_initial_install()) { if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cli/install.php" or $SCRIPT === "/{$CFG->admin}/cli/install_database.php") { // we are in an installer - roles can not work yet return true; } else { return false; } } if (strpos($capability, 'moodle/legacy:') === 0) { throw new coding_exception('Legacy capabilities can not be used any more!'); } if (!is_bool($doanything)) { throw new coding_exception('Capability parameter "doanything" is wierd, only true or false is allowed. This has to be fixed in code.'); } // capability must exist if (!($capinfo = get_capability_info($capability))) { debugging('Capability "' . $capability . '" was not found! This has to be fixed in code.'); return false; } if (!isset($USER->id)) { // should never happen $USER->id = 0; } // make sure there is a real user specified if ($user === null) { $userid = $USER->id; } else { $userid = is_object($user) ? $user->id : $user; } // make sure forcelogin cuts off not-logged-in users if enabled if (!empty($CFG->forcelogin) and $userid == 0) { return false; } // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are. if ($capinfo->captype === 'write' or $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) { if (isguestuser($userid) or $userid == 0) { return false; } } // somehow make sure the user is not deleted and actually exists if ($userid != 0) { if ($userid == $USER->id and isset($USER->deleted)) { // this prevents one query per page, it is a bit of cheating, // but hopefully session is terminated properly once user is deleted if ($USER->deleted) { return false; } } else { if (!context_user::instance($userid, IGNORE_MISSING)) { // no user context == invalid userid return false; } } } // context path/depth must be valid if (empty($context->path) or $context->depth == 0) { // this should not happen often, each upgrade tries to rebuild the context paths debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()'); if (is_siteadmin($userid)) { return true; } else { return false; } } // Find out if user is admin - it is not possible to override the doanything in any way // and it is not possible to switch to admin role either. if ($doanything) { if (is_siteadmin($userid)) { if ($userid != $USER->id) { return true; } // make sure switchrole is not used in this context if (empty($USER->access['rsw'])) { return true; } $parts = explode('/', trim($context->path, '/')); $path = ''; $switched = false; foreach ($parts as $part) { $path .= '/' . $part; if (!empty($USER->access['rsw'][$path])) { $switched = true; break; } } if (!$switched) { return true; } //ok, admin switched role in this context, let's use normal access control rules } } // Careful check for staleness... $context->reload_if_dirty(); if ($USER->id == $userid) { if (!isset($USER->access)) { load_all_capabilities(); } $access =& $USER->access; } else { // make sure user accessdata is really loaded get_user_accessdata($userid, true); $access =& $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; } // Load accessdata for below-the-course context if necessary, // all contexts at and above all courses are already loaded if ($context->contextlevel != CONTEXT_COURSE and $coursecontext = $context->get_course_context(false)) { load_course_context($userid, $coursecontext, $access); } return has_capability_in_accessdata($capability, $context, $access); }