Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/accesslib.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00126 defined('MOODLE_INTERNAL') || die();
00127 
00129 define('CAP_INHERIT', 0);
00131 define('CAP_ALLOW', 1);
00133 define('CAP_PREVENT', -1);
00135 define('CAP_PROHIBIT', -1000);
00136 
00138 define('CONTEXT_SYSTEM', 10);
00140 define('CONTEXT_USER', 30);
00142 define('CONTEXT_COURSECAT', 40);
00144 define('CONTEXT_COURSE', 50);
00146 define('CONTEXT_MODULE', 70);
00152 define('CONTEXT_BLOCK', 80);
00153 
00155 define('RISK_MANAGETRUST', 0x0001);
00157 define('RISK_CONFIG',      0x0002);
00159 define('RISK_XSS',         0x0004);
00161 define('RISK_PERSONAL',    0x0008);
00163 define('RISK_SPAM',        0x0010);
00165 define('RISK_DATALOSS',    0x0020);
00166 
00168 define('ROLENAME_ORIGINAL', 0);
00170 define('ROLENAME_ALIAS', 1);
00172 define('ROLENAME_BOTH', 2);
00174 define('ROLENAME_ORIGINALANDSHORT', 3);
00176 define('ROLENAME_ALIAS_RAW', 4);
00178 define('ROLENAME_SHORT', 5);
00179 
00181 if (!defined('CONTEXT_CACHE_MAX_SIZE')) {
00182     define('CONTEXT_CACHE_MAX_SIZE', 2500);
00183 }
00184 
00197 global $ACCESSLIB_PRIVATE;
00198 $ACCESSLIB_PRIVATE = new stdClass();
00199 $ACCESSLIB_PRIVATE->dirtycontexts    = null;    // Dirty contexts cache, loaded from DB once per page
00200 $ACCESSLIB_PRIVATE->accessdatabyuser = array(); // Holds the cache of $accessdata structure for users (including $USER)
00201 $ACCESSLIB_PRIVATE->rolepermissions  = array(); // role permissions cache - helps a lot with mem usage
00202 $ACCESSLIB_PRIVATE->capabilities     = null;    // detailed information about the capabilities
00203 
00213 function accesslib_clear_all_caches_for_unit_testing() {
00214     global $UNITTEST, $USER;
00215     if (empty($UNITTEST->running)) {
00216         throw new coding_exception('You must not call clear_all_caches outside of unit tests.');
00217     }
00218 
00219     accesslib_clear_all_caches(true);
00220 
00221     unset($USER->access);
00222 }
00223 
00233 function accesslib_clear_all_caches($resetcontexts) {
00234     global $ACCESSLIB_PRIVATE;
00235 
00236     $ACCESSLIB_PRIVATE->dirtycontexts    = null;
00237     $ACCESSLIB_PRIVATE->accessdatabyuser = array();
00238     $ACCESSLIB_PRIVATE->rolepermissions  = array();
00239     $ACCESSLIB_PRIVATE->capabilities     = null;
00240 
00241     if ($resetcontexts) {
00242         context_helper::reset_caches();
00243     }
00244 }
00245 
00253 function get_role_access($roleid) {
00254     global $DB, $ACCESSLIB_PRIVATE;
00255 
00256     /* Get it in 1 DB query...
00257      * - relevant role caps at the root and down
00258      *   to the course level - but not below
00259      */
00260 
00261     //TODO: MUC - this could be cached in shared memory to speed up first page loading, web crawlers, etc.
00262 
00263     $accessdata = get_empty_accessdata();
00264 
00265     $accessdata['ra']['/'.SYSCONTEXTID] = array((int)$roleid => (int)$roleid);
00266 
00267     //
00268     // Overrides for the role IN ANY CONTEXTS
00269     // down to COURSE - not below -
00270     //
00271     $sql = "SELECT ctx.path,
00272                    rc.capability, rc.permission
00273               FROM {context} ctx
00274               JOIN {role_capabilities} rc ON rc.contextid = ctx.id
00275          LEFT JOIN {context} cctx
00276                    ON (cctx.contextlevel = ".CONTEXT_COURSE." AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").")
00277              WHERE rc.roleid = ? AND cctx.id IS NULL";
00278     $params = array($roleid);
00279 
00280     // we need extra caching in CLI scripts and cron
00281     $rs = $DB->get_recordset_sql($sql, $params);
00282     foreach ($rs as $rd) {
00283         $k = "{$rd->path}:{$roleid}";
00284         $accessdata['rdef'][$k][$rd->capability] = (int)$rd->permission;
00285     }
00286     $rs->close();
00287 
00288     // share the role definitions
00289     foreach ($accessdata['rdef'] as $k=>$unused) {
00290         if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) {
00291             $ACCESSLIB_PRIVATE->rolepermissions[$k] = $accessdata['rdef'][$k];
00292         }
00293         $accessdata['rdef_count']++;
00294         $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k];
00295     }
00296 
00297     return $accessdata;
00298 }
00299 
00306 function get_guest_role() {
00307     global $CFG, $DB;
00308 
00309     if (empty($CFG->guestroleid)) {
00310         if ($roles = $DB->get_records('role', array('archetype'=>'guest'))) {
00311             $guestrole = array_shift($roles);   // Pick the first one
00312             set_config('guestroleid', $guestrole->id);
00313             return $guestrole;
00314         } else {
00315             debugging('Can not find any guest role!');
00316             return false;
00317         }
00318     } else {
00319         if ($guestrole = $DB->get_record('role', array('id'=>$CFG->guestroleid))) {
00320             return $guestrole;
00321         } else {
00322             // somebody is messing with guest roles, remove incorrect setting and try to find a new one
00323             set_config('guestroleid', '');
00324             return get_guest_role();
00325         }
00326     }
00327 }
00328 
00348 function has_capability($capability, context $context, $user = null, $doanything = true) {
00349     global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE;
00350 
00351     if (during_initial_install()) {
00352         if ($SCRIPT === "/$CFG->admin/index.php" or $SCRIPT === "/$CFG->admin/cli/install.php" or $SCRIPT === "/$CFG->admin/cli/install_database.php") {
00353             // we are in an installer - roles can not work yet
00354             return true;
00355         } else {
00356             return false;
00357         }
00358     }
00359 
00360     if (strpos($capability, 'moodle/legacy:') === 0) {
00361         throw new coding_exception('Legacy capabilities can not be used any more!');
00362     }
00363 
00364     if (!is_bool($doanything)) {
00365         throw new coding_exception('Capability parameter "doanything" is wierd, only true or false is allowed. This has to be fixed in code.');
00366     }
00367 
00368     // capability must exist
00369     if (!$capinfo = get_capability_info($capability)) {
00370         debugging('Capability "'.$capability.'" was not found! This has to be fixed in code.');
00371         return false;
00372     }
00373 
00374     if (!isset($USER->id)) {
00375         // should never happen
00376         $USER->id = 0;
00377     }
00378 
00379     // make sure there is a real user specified
00380     if ($user === null) {
00381         $userid = $USER->id;
00382     } else {
00383         $userid = is_object($user) ? $user->id : $user;
00384     }
00385 
00386     // make sure forcelogin cuts off not-logged-in users if enabled
00387     if (!empty($CFG->forcelogin) and $userid == 0) {
00388         return false;
00389     }
00390 
00391     // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are.
00392     if (($capinfo->captype === 'write') or ($capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) {
00393         if (isguestuser($userid) or $userid == 0) {
00394             return false;
00395         }
00396     }
00397 
00398     // somehow make sure the user is not deleted and actually exists
00399     if ($userid != 0) {
00400         if ($userid == $USER->id and isset($USER->deleted)) {
00401             // this prevents one query per page, it is a bit of cheating,
00402             // but hopefully session is terminated properly once user is deleted
00403             if ($USER->deleted) {
00404                 return false;
00405             }
00406         } else {
00407             if (!context_user::instance($userid, IGNORE_MISSING)) {
00408                 // no user context == invalid userid
00409                 return false;
00410             }
00411         }
00412     }
00413 
00414     // context path/depth must be valid
00415     if (empty($context->path) or $context->depth == 0) {
00416         // this should not happen often, each upgrade tries to rebuild the context paths
00417         debugging('Context id '.$context->id.' does not have valid path, please use build_context_path()');
00418         if (is_siteadmin($userid)) {
00419             return true;
00420         } else {
00421             return false;
00422         }
00423     }
00424 
00425     // Find out if user is admin - it is not possible to override the doanything in any way
00426     // and it is not possible to switch to admin role either.
00427     if ($doanything) {
00428         if (is_siteadmin($userid)) {
00429             if ($userid != $USER->id) {
00430                 return true;
00431             }
00432             // make sure switchrole is not used in this context
00433             if (empty($USER->access['rsw'])) {
00434                 return true;
00435             }
00436             $parts = explode('/', trim($context->path, '/'));
00437             $path = '';
00438             $switched = false;
00439             foreach ($parts as $part) {
00440                 $path .= '/' . $part;
00441                 if (!empty($USER->access['rsw'][$path])) {
00442                     $switched = true;
00443                     break;
00444                 }
00445             }
00446             if (!$switched) {
00447                 return true;
00448             }
00449             //ok, admin switched role in this context, let's use normal access control rules
00450         }
00451     }
00452 
00453     // Careful check for staleness...
00454     $context->reload_if_dirty();
00455 
00456     if ($USER->id == $userid) {
00457         if (!isset($USER->access)) {
00458             load_all_capabilities();
00459         }
00460         $access =& $USER->access;
00461 
00462     } else {
00463         // make sure user accessdata is really loaded
00464         get_user_accessdata($userid, true);
00465         $access =& $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
00466     }
00467 
00468 
00469     // Load accessdata for below-the-course context if necessary,
00470     // all contexts at and above all courses are already loaded
00471     if ($context->contextlevel != CONTEXT_COURSE and $coursecontext = $context->get_course_context(false)) {
00472         load_course_context($userid, $coursecontext, $access);
00473     }
00474 
00475     return has_capability_in_accessdata($capability, $context, $access);
00476 }
00477 
00492 function has_any_capability(array $capabilities, context $context, $userid = null, $doanything = true) {
00493     foreach ($capabilities as $capability) {
00494         if (has_capability($capability, $context, $userid, $doanything)) {
00495             return true;
00496         }
00497     }
00498     return false;
00499 }
00500 
00515 function has_all_capabilities(array $capabilities, context $context, $userid = null, $doanything = true) {
00516     foreach ($capabilities as $capability) {
00517         if (!has_capability($capability, $context, $userid, $doanything)) {
00518             return false;
00519         }
00520     }
00521     return true;
00522 }
00523 
00533 function is_siteadmin($user_or_id = null) {
00534     global $CFG, $USER;
00535 
00536     if ($user_or_id === null) {
00537         $user_or_id = $USER;
00538     }
00539 
00540     if (empty($user_or_id)) {
00541         return false;
00542     }
00543     if (!empty($user_or_id->id)) {
00544         $userid = $user_or_id->id;
00545     } else {
00546         $userid = $user_or_id;
00547     }
00548 
00549     $siteadmins = explode(',', $CFG->siteadmins);
00550     return in_array($userid, $siteadmins);
00551 }
00552 
00560 function has_coursecontact_role($userid) {
00561     global $DB, $CFG;
00562 
00563     if (empty($CFG->coursecontact)) {
00564         return false;
00565     }
00566     $sql = "SELECT 1
00567               FROM {role_assignments}
00568              WHERE userid = :userid AND roleid IN ($CFG->coursecontact)";
00569     return $DB->record_exists_sql($sql, array('userid'=>$userid));
00570 }
00571 
00606 function has_capability_in_accessdata($capability, context $context, array &$accessdata) {
00607     global $CFG;
00608 
00609     // Build $paths as a list of current + all parent "paths" with order bottom-to-top
00610     $path = $context->path;
00611     $paths = array($path);
00612     while($path = rtrim($path, '0123456789')) {
00613         $path = rtrim($path, '/');
00614         if ($path === '') {
00615             break;
00616         }
00617         $paths[] = $path;
00618     }
00619 
00620     $roles = array();
00621     $switchedrole = false;
00622 
00623     // Find out if role switched
00624     if (!empty($accessdata['rsw'])) {
00625         // From the bottom up...
00626         foreach ($paths as $path) {
00627             if (isset($accessdata['rsw'][$path])) {
00628                 // Found a switchrole assignment - check for that role _plus_ the default user role
00629                 $roles = array($accessdata['rsw'][$path]=>null, $CFG->defaultuserroleid=>null);
00630                 $switchedrole = true;
00631                 break;
00632             }
00633         }
00634     }
00635 
00636     if (!$switchedrole) {
00637         // get all users roles in this context and above
00638         foreach ($paths as $path) {
00639             if (isset($accessdata['ra'][$path])) {
00640                 foreach ($accessdata['ra'][$path] as $roleid) {
00641                     $roles[$roleid] = null;
00642                 }
00643             }
00644         }
00645     }
00646 
00647     // Now find out what access is given to each role, going bottom-->up direction
00648     $allowed = false;
00649     foreach ($roles as $roleid => $ignored) {
00650         foreach ($paths as $path) {
00651             if (isset($accessdata['rdef']["{$path}:$roleid"][$capability])) {
00652                 $perm = (int)$accessdata['rdef']["{$path}:$roleid"][$capability];
00653                 if ($perm === CAP_PROHIBIT) {
00654                     // any CAP_PROHIBIT found means no permission for the user
00655                     return false;
00656                 }
00657                 if (is_null($roles[$roleid])) {
00658                     $roles[$roleid] = $perm;
00659                 }
00660             }
00661         }
00662         // CAP_ALLOW in any role means the user has a permission, we continue only to detect prohibits
00663         $allowed = ($allowed or $roles[$roleid] === CAP_ALLOW);
00664     }
00665 
00666     return $allowed;
00667 }
00668 
00688 function require_capability($capability, context $context, $userid = null, $doanything = true,
00689                             $errormessage = 'nopermissions', $stringfile = '') {
00690     if (!has_capability($capability, $context, $userid, $doanything)) {
00691         throw new required_capability_exception($context, $capability, $errormessage, $stringfile);
00692     }
00693 }
00694 
00710 function get_user_access_sitewide($userid) {
00711     global $CFG, $DB, $ACCESSLIB_PRIVATE;
00712 
00713     /* Get in a few cheap DB queries...
00714      * - role assignments
00715      * - relevant role caps
00716      *   - above and within this user's RAs
00717      *   - below this user's RAs - limited to course level
00718      */
00719 
00720     // raparents collects paths & roles we need to walk up the parenthood to build the minimal rdef
00721     $raparents = array();
00722     $accessdata = get_empty_accessdata();
00723 
00724     // start with the default role
00725     if (!empty($CFG->defaultuserroleid)) {
00726         $syscontext = context_system::instance();
00727         $accessdata['ra'][$syscontext->path][(int)$CFG->defaultuserroleid] = (int)$CFG->defaultuserroleid;
00728         $raparents[$CFG->defaultuserroleid][$syscontext->id] = $syscontext->id;
00729     }
00730 
00731     // load the "default frontpage role"
00732     if (!empty($CFG->defaultfrontpageroleid)) {
00733         $frontpagecontext = context_course::instance(get_site()->id);
00734         if ($frontpagecontext->path) {
00735             $accessdata['ra'][$frontpagecontext->path][(int)$CFG->defaultfrontpageroleid] = (int)$CFG->defaultfrontpageroleid;
00736             $raparents[$CFG->defaultfrontpageroleid][$frontpagecontext->id] = $frontpagecontext->id;
00737         }
00738     }
00739 
00740     // preload every assigned role at and above course context
00741     $sql = "SELECT ctx.path, ra.roleid, ra.contextid
00742               FROM {role_assignments} ra
00743               JOIN {context} ctx
00744                    ON ctx.id = ra.contextid
00745          LEFT JOIN {block_instances} bi
00746                    ON (ctx.contextlevel = ".CONTEXT_BLOCK." AND bi.id = ctx.instanceid)
00747          LEFT JOIN {context} bpctx
00748                    ON (bpctx.id = bi.parentcontextid)
00749              WHERE ra.userid = :userid
00750                    AND (ctx.contextlevel <= ".CONTEXT_COURSE." OR bpctx.contextlevel < ".CONTEXT_COURSE.")";
00751     $params = array('userid'=>$userid);
00752     $rs = $DB->get_recordset_sql($sql, $params);
00753     foreach ($rs as $ra) {
00754         // RAs leafs are arrays to support multi-role assignments...
00755         $accessdata['ra'][$ra->path][(int)$ra->roleid] = (int)$ra->roleid;
00756         $raparents[$ra->roleid][$ra->contextid] = $ra->contextid;
00757     }
00758     $rs->close();
00759 
00760     if (empty($raparents)) {
00761         return $accessdata;
00762     }
00763 
00764     // now get overrides of interesting roles in all interesting child contexts
00765     // hopefully we will not run out of SQL limits here,
00766     // users would have to have very many roles at/above course context...
00767     $sqls = array();
00768     $params = array();
00769 
00770     static $cp = 0;
00771     foreach ($raparents as $roleid=>$ras) {
00772         $cp++;
00773         list($sqlcids, $cids) = $DB->get_in_or_equal($ras, SQL_PARAMS_NAMED, 'c'.$cp.'_');
00774         $params = array_merge($params, $cids);
00775         $params['r'.$cp] = $roleid;
00776         $sqls[] = "(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
00777                      FROM {role_capabilities} rc
00778                      JOIN {context} ctx
00779                           ON (ctx.id = rc.contextid)
00780                      JOIN {context} pctx
00781                           ON (pctx.id $sqlcids
00782                               AND (ctx.id = pctx.id
00783                                    OR ctx.path LIKE ".$DB->sql_concat('pctx.path',"'/%'")."
00784                                    OR pctx.path LIKE ".$DB->sql_concat('ctx.path',"'/%'")."))
00785                 LEFT JOIN {block_instances} bi
00786                           ON (ctx.contextlevel = ".CONTEXT_BLOCK." AND bi.id = ctx.instanceid)
00787                 LEFT JOIN {context} bpctx
00788                           ON (bpctx.id = bi.parentcontextid)
00789                     WHERE rc.roleid = :r{$cp}
00790                           AND (ctx.contextlevel <= ".CONTEXT_COURSE." OR bpctx.contextlevel < ".CONTEXT_COURSE.")
00791                    )";
00792     }
00793 
00794     // fixed capability order is necessary for rdef dedupe
00795     $rs = $DB->get_recordset_sql(implode("\nUNION\n", $sqls). "ORDER BY capability", $params);
00796 
00797     foreach ($rs as $rd) {
00798         $k = $rd->path.':'.$rd->roleid;
00799         $accessdata['rdef'][$k][$rd->capability] = (int)$rd->permission;
00800     }
00801     $rs->close();
00802 
00803     // share the role definitions
00804     foreach ($accessdata['rdef'] as $k=>$unused) {
00805         if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) {
00806             $ACCESSLIB_PRIVATE->rolepermissions[$k] = $accessdata['rdef'][$k];
00807         }
00808         $accessdata['rdef_count']++;
00809         $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k];
00810     }
00811 
00812     return $accessdata;
00813 }
00814 
00826 function load_course_context($userid, context_course $coursecontext, &$accessdata) {
00827     global $DB, $CFG, $ACCESSLIB_PRIVATE;
00828 
00829     if (empty($coursecontext->path)) {
00830         // weird, this should not happen
00831         return;
00832     }
00833 
00834     if (isset($accessdata['loaded'][$coursecontext->instanceid])) {
00835         // already loaded, great!
00836         return;
00837     }
00838 
00839     $roles = array();
00840 
00841     if (empty($userid)) {
00842         if (!empty($CFG->notloggedinroleid)) {
00843             $roles[$CFG->notloggedinroleid] = $CFG->notloggedinroleid;
00844         }
00845 
00846     } else if (isguestuser($userid)) {
00847         if ($guestrole = get_guest_role()) {
00848             $roles[$guestrole->id] = $guestrole->id;
00849         }
00850 
00851     } else {
00852         // Interesting role assignments at, above and below the course context
00853         list($parentsaself, $params) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_');
00854         $params['userid'] = $userid;
00855         $params['children'] = $coursecontext->path."/%";
00856         $sql = "SELECT ra.*, ctx.path
00857                   FROM {role_assignments} ra
00858                   JOIN {context} ctx ON ra.contextid = ctx.id
00859                  WHERE ra.userid = :userid AND (ctx.id $parentsaself OR ctx.path LIKE :children)";
00860         $rs = $DB->get_recordset_sql($sql, $params);
00861 
00862         // add missing role definitions
00863         foreach ($rs as $ra) {
00864             $accessdata['ra'][$ra->path][(int)$ra->roleid] = (int)$ra->roleid;
00865             $roles[$ra->roleid] = $ra->roleid;
00866         }
00867         $rs->close();
00868 
00869         // add the "default frontpage role" when on the frontpage
00870         if (!empty($CFG->defaultfrontpageroleid)) {
00871             $frontpagecontext = context_course::instance(get_site()->id);
00872             if ($frontpagecontext->id == $coursecontext->id) {
00873                 $roles[$CFG->defaultfrontpageroleid] = $CFG->defaultfrontpageroleid;
00874             }
00875         }
00876 
00877         // do not forget the default role
00878         if (!empty($CFG->defaultuserroleid)) {
00879             $roles[$CFG->defaultuserroleid] = $CFG->defaultuserroleid;
00880         }
00881     }
00882 
00883     if (!$roles) {
00884         // weird, default roles must be missing...
00885         $accessdata['loaded'][$coursecontext->instanceid] = 1;
00886         return;
00887     }
00888 
00889     // now get overrides of interesting roles in all interesting contexts (this course + children + parents)
00890     $params = array('c'=>$coursecontext->id);
00891     list($parentsaself, $rparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_');
00892     $params = array_merge($params, $rparams);
00893     list($roleids, $rparams) = $DB->get_in_or_equal($roles, SQL_PARAMS_NAMED, 'r_');
00894     $params = array_merge($params, $rparams);
00895 
00896     $sql = "SELECT ctx.path, rc.roleid, rc.capability, rc.permission
00897                  FROM {role_capabilities} rc
00898                  JOIN {context} ctx
00899                       ON (ctx.id = rc.contextid)
00900                  JOIN {context} cctx
00901                       ON (cctx.id = :c
00902                           AND (ctx.id $parentsaself OR ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'")."))
00903                 WHERE rc.roleid $roleids
00904              ORDER BY rc.capability"; // fixed capability order is necessary for rdef dedupe
00905     $rs = $DB->get_recordset_sql($sql, $params);
00906 
00907     $newrdefs = array();
00908     foreach ($rs as $rd) {
00909         $k = $rd->path.':'.$rd->roleid;
00910         if (isset($accessdata['rdef'][$k])) {
00911             continue;
00912         }
00913         $newrdefs[$k][$rd->capability] = (int)$rd->permission;
00914     }
00915     $rs->close();
00916 
00917     // share new role definitions
00918     foreach ($newrdefs as $k=>$unused) {
00919         if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) {
00920             $ACCESSLIB_PRIVATE->rolepermissions[$k] = $newrdefs[$k];
00921         }
00922         $accessdata['rdef_count']++;
00923         $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k];
00924     }
00925 
00926     $accessdata['loaded'][$coursecontext->instanceid] = 1;
00927 
00928     // we want to deduplicate the USER->access from time to time, this looks like a good place,
00929     // because we have to do it before the end of session
00930     dedupe_user_access();
00931 }
00932 
00946 function load_role_access_by_context($roleid, context $context, &$accessdata) {
00947     global $DB, $ACCESSLIB_PRIVATE;
00948 
00949     /* Get the relevant rolecaps into rdef
00950      * - relevant role caps
00951      *   - at ctx and above
00952      *   - below this ctx
00953      */
00954 
00955     if (empty($context->path)) {
00956         // weird, this should not happen
00957         return;
00958     }
00959 
00960     list($parentsaself, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_');
00961     $params['roleid'] = $roleid;
00962     $params['childpath'] = $context->path.'/%';
00963 
00964     $sql = "SELECT ctx.path, rc.capability, rc.permission
00965               FROM {role_capabilities} rc
00966               JOIN {context} ctx ON (rc.contextid = ctx.id)
00967              WHERE rc.roleid = :roleid AND (ctx.id $parentsaself OR ctx.path LIKE :childpath)
00968           ORDER BY rc.capability"; // fixed capability order is necessary for rdef dedupe
00969     $rs = $DB->get_recordset_sql($sql, $params);
00970 
00971     $newrdefs = array();
00972     foreach ($rs as $rd) {
00973         $k = $rd->path.':'.$roleid;
00974         if (isset($accessdata['rdef'][$k])) {
00975             continue;
00976         }
00977         $newrdefs[$k][$rd->capability] = (int)$rd->permission;
00978     }
00979     $rs->close();
00980 
00981     // share new role definitions
00982     foreach ($newrdefs as $k=>$unused) {
00983         if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) {
00984             $ACCESSLIB_PRIVATE->rolepermissions[$k] = $newrdefs[$k];
00985         }
00986         $accessdata['rdef_count']++;
00987         $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k];
00988     }
00989 }
00990 
00997 function get_empty_accessdata() {
00998     $accessdata               = array(); // named list
00999     $accessdata['ra']         = array();
01000     $accessdata['rdef']       = array();
01001     $accessdata['rdef_count'] = 0;       // this bloody hack is necessary because count($array) is slooooowwww in PHP
01002     $accessdata['rdef_lcc']   = 0;       // rdef_count during the last compression
01003     $accessdata['loaded']     = array(); // loaded course contexts
01004     $accessdata['time']       = time();
01005     $accessdata['rsw']        = array();
01006 
01007     return $accessdata;
01008 }
01009 
01018 function get_user_accessdata($userid, $preloadonly=false) {
01019     global $CFG, $ACCESSLIB_PRIVATE, $USER;
01020 
01021     if (!empty($USER->acces['rdef']) and empty($ACCESSLIB_PRIVATE->rolepermissions)) {
01022         // share rdef from USER session with rolepermissions cache in order to conserve memory
01023         foreach($USER->acces['rdef'] as $k=>$v) {
01024             $ACCESSLIB_PRIVATE->rolepermissions[$k] =& $USER->acces['rdef'][$k];
01025         }
01026         $ACCESSLIB_PRIVATE->accessdatabyuser[$USER->id] = $USER->acces;
01027     }
01028 
01029     if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
01030         if (empty($userid)) {
01031             if (!empty($CFG->notloggedinroleid)) {
01032                 $accessdata = get_role_access($CFG->notloggedinroleid);
01033             } else {
01034                 // weird
01035                 return get_empty_accessdata();
01036             }
01037 
01038         } else if (isguestuser($userid)) {
01039             if ($guestrole = get_guest_role()) {
01040                 $accessdata = get_role_access($guestrole->id);
01041             } else {
01042                 //weird
01043                 return get_empty_accessdata();
01044             }
01045 
01046         } else {
01047             $accessdata = get_user_access_sitewide($userid); // includes default role and frontpage role
01048         }
01049 
01050         $ACCESSLIB_PRIVATE->accessdatabyuser[$userid] = $accessdata;
01051     }
01052 
01053     if ($preloadonly) {
01054         return;
01055     } else {
01056         return $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
01057     }
01058 }
01059 
01067 function dedupe_user_access() {
01068     global $USER;
01069 
01070     if (CLI_SCRIPT) {
01071         // no session in CLI --> no compression necessary
01072         return;
01073     }
01074 
01075     if (empty($USER->access['rdef_count'])) {
01076         // weird, this should not happen
01077         return;
01078     }
01079 
01080     // the rdef is growing only, we never remove stuff from it, the rdef_lcc helps us to detect new stuff in rdef
01081     if ($USER->access['rdef_count'] - $USER->access['rdef_lcc'] > 10) {
01082         // do not compress after each change, wait till there is more stuff to be done
01083         return;
01084     }
01085 
01086     $hashmap = array();
01087     foreach ($USER->access['rdef'] as $k=>$def) {
01088         $hash = sha1(serialize($def));
01089         if (isset($hashmap[$hash])) {
01090             $USER->access['rdef'][$k] =& $hashmap[$hash];
01091         } else {
01092             $hashmap[$hash] =& $USER->access['rdef'][$k];
01093         }
01094     }
01095 
01096     $USER->access['rdef_lcc'] = $USER->access['rdef_count'];
01097 }
01098 
01109 function load_all_capabilities() {
01110     global $USER;
01111 
01112     // roles not installed yet - we are in the middle of installation
01113     if (during_initial_install()) {
01114         return;
01115     }
01116 
01117     if (!isset($USER->id)) {
01118         // this should not happen
01119         $USER->id = 0;
01120     }
01121 
01122     unset($USER->access);
01123     $USER->access = get_user_accessdata($USER->id);
01124 
01125     // deduplicate the overrides to minimize session size
01126     dedupe_user_access();
01127 
01128     // Clear to force a refresh
01129     unset($USER->mycourses);
01130 
01131     // init/reset internal enrol caches - active course enrolments and temp access
01132     $USER->enrol = array('enrolled'=>array(), 'tempguest'=>array());
01133 }
01134 
01148 function reload_all_capabilities() {
01149     global $USER, $DB, $ACCESSLIB_PRIVATE;
01150 
01151     // copy switchroles
01152     $sw = array();
01153     if (!empty($USER->access['rsw'])) {
01154         $sw = $USER->access['rsw'];
01155     }
01156 
01157     accesslib_clear_all_caches(true);
01158     unset($USER->access);
01159     $ACCESSLIB_PRIVATE->dirtycontexts = array(); // prevent dirty flags refetching on this page
01160 
01161     load_all_capabilities();
01162 
01163     foreach ($sw as $path => $roleid) {
01164         if ($record = $DB->get_record('context', array('path'=>$path))) {
01165             $context = context::instance_by_id($record->id);
01166             role_switch($roleid, $context);
01167         }
01168     }
01169 }
01170 
01181 function load_temp_course_role(context_course $coursecontext, $roleid) {
01182     global $USER, $SITE;
01183 
01184     if (empty($roleid)) {
01185         debugging('invalid role specified in load_temp_course_role()');
01186         return;
01187     }
01188 
01189     if ($coursecontext->instanceid == $SITE->id) {
01190         debugging('Can not use temp roles on the frontpage');
01191         return;
01192     }
01193 
01194     if (!isset($USER->access)) {
01195         load_all_capabilities();
01196     }
01197 
01198     $coursecontext->reload_if_dirty();
01199 
01200     if (isset($USER->access['ra'][$coursecontext->path][$roleid])) {
01201         return;
01202     }
01203 
01204     // load course stuff first
01205     load_course_context($USER->id, $coursecontext, $USER->access);
01206 
01207     $USER->access['ra'][$coursecontext->path][(int)$roleid] = (int)$roleid;
01208 
01209     load_role_access_by_context($roleid, $coursecontext, $USER->access);
01210 }
01211 
01219 function remove_temp_course_roles(context_course $coursecontext) {
01220     global $DB, $USER, $SITE;
01221 
01222     if ($coursecontext->instanceid == $SITE->id) {
01223         debugging('Can not use temp roles on the frontpage');
01224         return;
01225     }
01226 
01227     if (empty($USER->access['ra'][$coursecontext->path])) {
01228         //no roles here, weird
01229         return;
01230     }
01231 
01232     $sql = "SELECT DISTINCT ra.roleid AS id
01233               FROM {role_assignments} ra
01234              WHERE ra.contextid = :contextid AND ra.userid = :userid";
01235     $ras = $DB->get_records_sql($sql, array('contextid'=>$coursecontext->id, 'userid'=>$USER->id));
01236 
01237     $USER->access['ra'][$coursecontext->path] = array();
01238     foreach($ras as $r) {
01239         $USER->access['ra'][$coursecontext->path][(int)$r->id] = (int)$r->id;
01240     }
01241 }
01242 
01248 function get_role_archetypes() {
01249     return array(
01250         'manager'        => 'manager',
01251         'coursecreator'  => 'coursecreator',
01252         'editingteacher' => 'editingteacher',
01253         'teacher'        => 'teacher',
01254         'student'        => 'student',
01255         'guest'          => 'guest',
01256         'user'           => 'user',
01257         'frontpage'      => 'frontpage'
01258     );
01259 }
01260 
01275 function assign_legacy_capabilities($capability, $legacyperms) {
01276 
01277     $archetypes = get_role_archetypes();
01278 
01279     foreach ($legacyperms as $type => $perm) {
01280 
01281         $systemcontext = context_system::instance();
01282         if ($type === 'admin') {
01283             debugging('Legacy type admin in access.php was renamed to manager, please update the code.');
01284             $type = 'manager';
01285         }
01286 
01287         if (!array_key_exists($type, $archetypes)) {
01288             print_error('invalidlegacy', '', '', $type);
01289         }
01290 
01291         if ($roles = get_archetype_roles($type)) {
01292             foreach ($roles as $role) {
01293                 // Assign a site level capability.
01294                 if (!assign_capability($capability, $perm, $role->id, $systemcontext->id)) {
01295                     return false;
01296                 }
01297             }
01298         }
01299     }
01300     return true;
01301 }
01302 
01310 function is_safe_capability($capability) {
01311     return !((RISK_DATALOSS | RISK_MANAGETRUST | RISK_CONFIG | RISK_XSS | RISK_PERSONAL) & $capability->riskbitmask);
01312 }
01313 
01322 function get_local_override($roleid, $contextid, $capability) {
01323     global $DB;
01324     return $DB->get_record('role_capabilities', array('roleid'=>$roleid, 'capability'=>$capability, 'contextid'=>$contextid));
01325 }
01326 
01333 function get_context_info_array($contextid) {
01334     global $DB;
01335 
01336     $context = context::instance_by_id($contextid, MUST_EXIST);
01337     $course  = null;
01338     $cm      = null;
01339 
01340     if ($context->contextlevel == CONTEXT_COURSE) {
01341         $course = $DB->get_record('course', array('id'=>$context->instanceid), '*', MUST_EXIST);
01342 
01343     } else if ($context->contextlevel == CONTEXT_MODULE) {
01344         $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST);
01345         $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
01346 
01347     } else if ($context->contextlevel == CONTEXT_BLOCK) {
01348         $parent = $context->get_parent_context();
01349 
01350         if ($parent->contextlevel == CONTEXT_COURSE) {
01351             $course = $DB->get_record('course', array('id'=>$parent->instanceid), '*', MUST_EXIST);
01352         } else if ($parent->contextlevel == CONTEXT_MODULE) {
01353             $cm = get_coursemodule_from_id('', $parent->instanceid, 0, false, MUST_EXIST);
01354             $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
01355         }
01356     }
01357 
01358     return array($context, $course, $cm);
01359 }
01360 
01370 function create_role($name, $shortname, $description, $archetype = '') {
01371     global $DB;
01372 
01373     if (strpos($archetype, 'moodle/legacy:') !== false) {
01374         throw new coding_exception('Use new role archetype parameter in create_role() instead of old legacy capabilities.');
01375     }
01376 
01377     // verify role archetype actually exists
01378     $archetypes = get_role_archetypes();
01379     if (empty($archetypes[$archetype])) {
01380         $archetype = '';
01381     }
01382 
01383     // Insert the role record.
01384     $role = new stdClass();
01385     $role->name        = $name;
01386     $role->shortname   = $shortname;
01387     $role->description = $description;
01388     $role->archetype   = $archetype;
01389 
01390     //find free sortorder number
01391     $role->sortorder = $DB->get_field('role', 'MAX(sortorder) + 1', array());
01392     if (empty($role->sortorder)) {
01393         $role->sortorder = 1;
01394     }
01395     $id = $DB->insert_record('role', $role);
01396 
01397     return $id;
01398 }
01399 
01406 function delete_role($roleid) {
01407     global $DB;
01408 
01409     // first unssign all users
01410     role_unassign_all(array('roleid'=>$roleid));
01411 
01412     // cleanup all references to this role, ignore errors
01413     $DB->delete_records('role_capabilities',   array('roleid'=>$roleid));
01414     $DB->delete_records('role_allow_assign',   array('roleid'=>$roleid));
01415     $DB->delete_records('role_allow_assign',   array('allowassign'=>$roleid));
01416     $DB->delete_records('role_allow_override', array('roleid'=>$roleid));
01417     $DB->delete_records('role_allow_override', array('allowoverride'=>$roleid));
01418     $DB->delete_records('role_names',          array('roleid'=>$roleid));
01419     $DB->delete_records('role_context_levels', array('roleid'=>$roleid));
01420 
01421     // finally delete the role itself
01422     // get this before the name is gone for logging
01423     $rolename = $DB->get_field('role', 'name', array('id'=>$roleid));
01424 
01425     $DB->delete_records('role', array('id'=>$roleid));
01426 
01427     add_to_log(SITEID, 'role', 'delete', 'admin/roles/action=delete&roleid='.$roleid, $rolename, '');
01428 
01429     return true;
01430 }
01431 
01444 function assign_capability($capability, $permission, $roleid, $contextid, $overwrite = false) {
01445     global $USER, $DB;
01446 
01447     if ($contextid instanceof context) {
01448         $context = $contextid;
01449     } else {
01450         $context = context::instance_by_id($contextid);
01451     }
01452 
01453     if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set
01454         unassign_capability($capability, $roleid, $context->id);
01455         return true;
01456     }
01457 
01458     $existing = $DB->get_record('role_capabilities', array('contextid'=>$context->id, 'roleid'=>$roleid, 'capability'=>$capability));
01459 
01460     if ($existing and !$overwrite) {   // We want to keep whatever is there already
01461         return true;
01462     }
01463 
01464     $cap = new stdClass();
01465     $cap->contextid    = $context->id;
01466     $cap->roleid       = $roleid;
01467     $cap->capability   = $capability;
01468     $cap->permission   = $permission;
01469     $cap->timemodified = time();
01470     $cap->modifierid   = empty($USER->id) ? 0 : $USER->id;
01471 
01472     if ($existing) {
01473         $cap->id = $existing->id;
01474         $DB->update_record('role_capabilities', $cap);
01475     } else {
01476         if ($DB->record_exists('context', array('id'=>$context->id))) {
01477             $DB->insert_record('role_capabilities', $cap);
01478         }
01479     }
01480     return true;
01481 }
01482 
01493 function unassign_capability($capability, $roleid, $contextid = null) {
01494     global $DB;
01495 
01496     if (!empty($contextid)) {
01497         if ($contextid instanceof context) {
01498             $context = $contextid;
01499         } else {
01500             $context = context::instance_by_id($contextid);
01501         }
01502         // delete from context rel, if this is the last override in this context
01503         $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid, 'contextid'=>$context->id));
01504     } else {
01505         $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid));
01506     }
01507     return true;
01508 }
01509 
01523 function get_roles_with_capability($capability, $permission = null, $context = null) {
01524     global $DB;
01525 
01526     if ($context) {
01527         $contexts = $context->get_parent_context_ids(true);
01528         list($insql, $params) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED, 'ctx');
01529         $contextsql = "AND rc.contextid $insql";
01530     } else {
01531         $params = array();
01532         $contextsql = '';
01533     }
01534 
01535     if ($permission) {
01536         $permissionsql = " AND rc.permission = :permission";
01537         $params['permission'] = $permission;
01538     } else {
01539         $permissionsql = '';
01540     }
01541 
01542     $sql = "SELECT r.*
01543               FROM {role} r
01544              WHERE r.id IN (SELECT rc.roleid
01545                               FROM {role_capabilities} rc
01546                              WHERE rc.capability = :capname
01547                                    $contextsql
01548                                    $permissionsql)";
01549     $params['capname'] = $capability;
01550 
01551 
01552     return $DB->get_records_sql($sql, $params);
01553 }
01554 
01566 function role_assign($roleid, $userid, $contextid, $component = '', $itemid = 0, $timemodified = '') {
01567     global $USER, $DB;
01568 
01569     // first of all detect if somebody is using old style parameters
01570     if ($contextid === 0 or is_numeric($component)) {
01571         throw new coding_exception('Invalid call to role_assign(), code needs to be updated to use new order of parameters');
01572     }
01573 
01574     // now validate all parameters
01575     if (empty($roleid)) {
01576         throw new coding_exception('Invalid call to role_assign(), roleid can not be empty');
01577     }
01578 
01579     if (empty($userid)) {
01580         throw new coding_exception('Invalid call to role_assign(), userid can not be empty');
01581     }
01582 
01583     if ($itemid) {
01584         if (strpos($component, '_') === false) {
01585             throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as"enrol_" when itemid specified', 'component:'.$component);
01586         }
01587     } else {
01588         $itemid = 0;
01589         if ($component !== '' and strpos($component, '_') === false) {
01590             throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component);
01591         }
01592     }
01593 
01594     if (!$DB->record_exists('user', array('id'=>$userid, 'deleted'=>0))) {
01595         throw new coding_exception('User ID does not exist or is deleted!', 'userid:'.$userid);
01596     }
01597 
01598     if ($contextid instanceof context) {
01599         $context = $contextid;
01600     } else {
01601         $context = context::instance_by_id($contextid, MUST_EXIST);
01602     }
01603 
01604     if (!$timemodified) {
01605         $timemodified = time();
01606     }
01607 
01608     // Check for existing entry
01609     // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
01610     $component = ($component === '') ? $DB->sql_empty() : $component;
01611     $ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id');
01612 
01613     if ($ras) {
01614         // role already assigned - this should not happen
01615         if (count($ras) > 1) {
01616             // very weird - remove all duplicates!
01617             $ra = array_shift($ras);
01618             foreach ($ras as $r) {
01619                 $DB->delete_records('role_assignments', array('id'=>$r->id));
01620             }
01621         } else {
01622             $ra = reset($ras);
01623         }
01624 
01625         // actually there is no need to update, reset anything or trigger any event, so just return
01626         return $ra->id;
01627     }
01628 
01629     // Create a new entry
01630     $ra = new stdClass();
01631     $ra->roleid       = $roleid;
01632     $ra->contextid    = $context->id;
01633     $ra->userid       = $userid;
01634     $ra->component    = $component;
01635     $ra->itemid       = $itemid;
01636     $ra->timemodified = $timemodified;
01637     $ra->modifierid   = empty($USER->id) ? 0 : $USER->id;
01638 
01639     $ra->id = $DB->insert_record('role_assignments', $ra);
01640 
01641     // mark context as dirty - again expensive, but needed
01642     $context->mark_dirty();
01643 
01644     if (!empty($USER->id) && $USER->id == $userid) {
01645         // If the user is the current user, then do full reload of capabilities too.
01646         reload_all_capabilities();
01647     }
01648 
01649     events_trigger('role_assigned', $ra);
01650 
01651     return $ra->id;
01652 }
01653 
01664 function role_unassign($roleid, $userid, $contextid, $component = '', $itemid = 0) {
01665     // first make sure the params make sense
01666     if ($roleid == 0 or $userid == 0 or $contextid == 0) {
01667         throw new coding_exception('Invalid call to role_unassign(), please use role_unassign_all() when removing multiple role assignments');
01668     }
01669 
01670     if ($itemid) {
01671         if (strpos($component, '_') === false) {
01672             throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as "enrol_" when itemid specified', 'component:'.$component);
01673         }
01674     } else {
01675         $itemid = 0;
01676         if ($component !== '' and strpos($component, '_') === false) {
01677             throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component);
01678         }
01679     }
01680 
01681     role_unassign_all(array('roleid'=>$roleid, 'userid'=>$userid, 'contextid'=>$contextid, 'component'=>$component, 'itemid'=>$itemid), false, false);
01682 }
01683 
01693 function role_unassign_all(array $params, $subcontexts = false, $includemanual = false) {
01694     global $USER, $CFG, $DB;
01695 
01696     if (!$params) {
01697         throw new coding_exception('Missing parameters in role_unsassign_all() call');
01698     }
01699 
01700     $allowed = array('roleid', 'userid', 'contextid', 'component', 'itemid');
01701     foreach ($params as $key=>$value) {
01702         if (!in_array($key, $allowed)) {
01703             throw new coding_exception('Unknown role_unsassign_all() parameter key', 'key:'.$key);
01704         }
01705     }
01706 
01707     if (isset($params['component']) and $params['component'] !== '' and strpos($params['component'], '_') === false) {
01708         throw new coding_exception('Invalid component paramter in role_unsassign_all() call', 'component:'.$params['component']);
01709     }
01710 
01711     if ($includemanual) {
01712         if (!isset($params['component']) or $params['component'] === '') {
01713             throw new coding_exception('include manual parameter requires component parameter in role_unsassign_all() call');
01714         }
01715     }
01716 
01717     if ($subcontexts) {
01718         if (empty($params['contextid'])) {
01719             throw new coding_exception('subcontexts paramtere requires component parameter in role_unsassign_all() call');
01720         }
01721     }
01722 
01723     // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
01724     if (isset($params['component'])) {
01725         $params['component'] = ($params['component'] === '') ? $DB->sql_empty() : $params['component'];
01726     }
01727     $ras = $DB->get_records('role_assignments', $params);
01728     foreach($ras as $ra) {
01729         $DB->delete_records('role_assignments', array('id'=>$ra->id));
01730         if ($context = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
01731             // this is a bit expensive but necessary
01732             $context->mark_dirty();
01734             if (!empty($USER->id) && $USER->id == $ra->userid) {
01735                 reload_all_capabilities();
01736             }
01737         }
01738         events_trigger('role_unassigned', $ra);
01739     }
01740     unset($ras);
01741 
01742     // process subcontexts
01743     if ($subcontexts and $context = context::instance_by_id($params['contextid'], IGNORE_MISSING)) {
01744         if ($params['contextid'] instanceof context) {
01745             $context = $params['contextid'];
01746         } else {
01747             $context = context::instance_by_id($params['contextid'], IGNORE_MISSING);
01748         }
01749 
01750         if ($context) {
01751             $contexts = $context->get_child_contexts();
01752             $mparams = $params;
01753             foreach($contexts as $context) {
01754                 $mparams['contextid'] = $context->id;
01755                 $ras = $DB->get_records('role_assignments', $mparams);
01756                 foreach($ras as $ra) {
01757                     $DB->delete_records('role_assignments', array('id'=>$ra->id));
01758                     // this is a bit expensive but necessary
01759                     $context->mark_dirty();
01761                     if (!empty($USER->id) && $USER->id == $ra->userid) {
01762                         reload_all_capabilities();
01763                     }
01764                     events_trigger('role_unassigned', $ra);
01765                 }
01766             }
01767         }
01768     }
01769 
01770     // do this once more for all manual role assignments
01771     if ($includemanual) {
01772         $params['component'] = '';
01773         role_unassign_all($params, $subcontexts, false);
01774     }
01775 }
01776 
01782 function isloggedin() {
01783     global $USER;
01784 
01785     return (!empty($USER->id));
01786 }
01787 
01794 function isguestuser($user = null) {
01795     global $USER, $DB, $CFG;
01796 
01797     // make sure we have the user id cached in config table, because we are going to use it a lot
01798     if (empty($CFG->siteguest)) {
01799         if (!$guestid = $DB->get_field('user', 'id', array('username'=>'guest', 'mnethostid'=>$CFG->mnet_localhost_id))) {
01800             // guest does not exist yet, weird
01801             return false;
01802         }
01803         set_config('siteguest', $guestid);
01804     }
01805     if ($user === null) {
01806         $user = $USER;
01807     }
01808 
01809     if ($user === null) {
01810         // happens when setting the $USER
01811         return false;
01812 
01813     } else if (is_numeric($user)) {
01814         return ($CFG->siteguest == $user);
01815 
01816     } else if (is_object($user)) {
01817         if (empty($user->id)) {
01818             return false; // not logged in means is not be guest
01819         } else {
01820             return ($CFG->siteguest == $user->id);
01821         }
01822 
01823     } else {
01824         throw new coding_exception('Invalid user parameter supplied for isguestuser() function!');
01825     }
01826 }
01827 
01835 function is_guest(context $context, $user = null) {
01836     global $USER;
01837 
01838     // first find the course context
01839     $coursecontext = $context->get_course_context();
01840 
01841     // make sure there is a real user specified
01842     if ($user === null) {
01843         $userid = isset($USER->id) ? $USER->id : 0;
01844     } else {
01845         $userid = is_object($user) ? $user->id : $user;
01846     }
01847 
01848     if (isguestuser($userid)) {
01849         // can not inspect or be enrolled
01850         return true;
01851     }
01852 
01853     if (has_capability('moodle/course:view', $coursecontext, $user)) {
01854         // viewing users appear out of nowhere, they are neither guests nor participants
01855         return false;
01856     }
01857 
01858     // consider only real active enrolments here
01859     if (is_enrolled($coursecontext, $user, '', true)) {
01860         return false;
01861     }
01862 
01863     return true;
01864 }
01865 
01875 function is_viewing(context $context, $user = null, $withcapability = '') {
01876     // first find the course context
01877     $coursecontext = $context->get_course_context();
01878 
01879     if (isguestuser($user)) {
01880         // can not inspect
01881         return false;
01882     }
01883 
01884     if (!has_capability('moodle/course:view', $coursecontext, $user)) {
01885         // admins are allowed to inspect courses
01886         return false;
01887     }
01888 
01889     if ($withcapability and !has_capability($withcapability, $context, $user)) {
01890         // site admins always have the capability, but the enrolment above blocks
01891         return false;
01892     }
01893 
01894     return true;
01895 }
01896 
01909 function is_enrolled(context $context, $user = null, $withcapability = '', $onlyactive = false) {
01910     global $USER, $DB;
01911 
01912     // first find the course context
01913     $coursecontext = $context->get_course_context();
01914 
01915     // make sure there is a real user specified
01916     if ($user === null) {
01917         $userid = isset($USER->id) ? $USER->id : 0;
01918     } else {
01919         $userid = is_object($user) ? $user->id : $user;
01920     }
01921 
01922     if (empty($userid)) {
01923         // not-logged-in!
01924         return false;
01925     } else if (isguestuser($userid)) {
01926         // guest account can not be enrolled anywhere
01927         return false;
01928     }
01929 
01930     if ($coursecontext->instanceid == SITEID) {
01931         // everybody participates on frontpage
01932     } else {
01933         // try cached info first - the enrolled flag is set only when active enrolment present
01934         if ($USER->id == $userid) {
01935             $coursecontext->reload_if_dirty();
01936             if (isset($USER->enrol['enrolled'][$coursecontext->instanceid])) {
01937                 if ($USER->enrol['enrolled'][$coursecontext->instanceid] > time()) {
01938                     return true;
01939                 }
01940             }
01941         }
01942 
01943         if ($onlyactive) {
01944             // look for active enrolments only
01945             $until = enrol_get_enrolment_end($coursecontext->instanceid, $userid);
01946 
01947             if ($until === false) {
01948                 return false;
01949             }
01950 
01951             if ($USER->id == $userid) {
01952                 if ($until == 0) {
01953                     $until = ENROL_MAX_TIMESTAMP;
01954                 }
01955                 $USER->enrol['enrolled'][$coursecontext->instanceid] = $until;
01956                 if (isset($USER->enrol['tempguest'][$coursecontext->instanceid])) {
01957                     unset($USER->enrol['tempguest'][$coursecontext->instanceid]);
01958                     remove_temp_course_roles($coursecontext);
01959                 }
01960             }
01961 
01962         } else {
01963             // any enrolment is good for us here, even outdated, disabled or inactive
01964             $sql = "SELECT 'x'
01965                       FROM {user_enrolments} ue
01966                       JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
01967                       JOIN {user} u ON u.id = ue.userid
01968                      WHERE ue.userid = :userid AND u.deleted = 0";
01969             $params = array('userid'=>$userid, 'courseid'=>$coursecontext->instanceid);
01970             if (!$DB->record_exists_sql($sql, $params)) {
01971                 return false;
01972             }
01973         }
01974     }
01975 
01976     if ($withcapability and !has_capability($withcapability, $context, $userid)) {
01977         return false;
01978     }
01979 
01980     return true;
01981 }
01982 
02004 function can_access_course(stdClass $course, $user = null, $withcapability = '', $onlyactive = false) {
02005     global $DB, $USER;
02006 
02007     // this function originally accepted $coursecontext parameter
02008     if ($course instanceof context) {
02009         if ($course instanceof context_course) {
02010             debugging('deprecated context parameter, please use $course record');
02011             $coursecontext = $course;
02012             $course = $DB->get_record('course', array('id'=>$coursecontext->instanceid));
02013         } else {
02014             debugging('Invalid context parameter, please use $course record');
02015             return false;
02016         }
02017     } else {
02018         $coursecontext = context_course::instance($course->id);
02019     }
02020 
02021     if (!isset($USER->id)) {
02022         // should never happen
02023         $USER->id = 0;
02024     }
02025 
02026     // make sure there is a user specified
02027     if ($user === null) {
02028         $userid = $USER->id;
02029     } else {
02030         $userid = is_object($user) ? $user->id : $user;
02031     }
02032     unset($user);
02033 
02034     if ($withcapability and !has_capability($withcapability, $coursecontext, $userid)) {
02035         return false;
02036     }
02037 
02038     if ($userid == $USER->id) {
02039         if (!empty($USER->access['rsw'][$coursecontext->path])) {
02040             // the fact that somebody switched role means they can access the course no matter to what role they switched
02041             return true;
02042         }
02043     }
02044 
02045     if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext, $userid)) {
02046         return false;
02047     }
02048 
02049     if (is_viewing($coursecontext, $userid)) {
02050         return true;
02051     }
02052 
02053     if ($userid != $USER->id) {
02054         // for performance reasons we do not verify temporary guest access for other users, sorry...
02055         return is_enrolled($coursecontext, $userid, '', $onlyactive);
02056     }
02057 
02058     // === from here we deal only with $USER ===
02059 
02060     $coursecontext->reload_if_dirty();
02061 
02062     if (isset($USER->enrol['enrolled'][$course->id])) {
02063         if ($USER->enrol['enrolled'][$course->id] > time()) {
02064             return true;
02065         }
02066     }
02067     if (isset($USER->enrol['tempguest'][$course->id])) {
02068         if ($USER->enrol['tempguest'][$course->id] > time()) {
02069             return true;
02070         }
02071     }
02072 
02073     if (is_enrolled($coursecontext, $USER, '', $onlyactive)) {
02074         return true;
02075     }
02076 
02077     // if not enrolled try to gain temporary guest access
02078     $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
02079     $enrols = enrol_get_plugins(true);
02080     foreach($instances as $instance) {
02081         if (!isset($enrols[$instance->enrol])) {
02082             continue;
02083         }
02084         // Get a duration for the guest access, a timestamp in the future, 0 (always) or false.
02085         $until = $enrols[$instance->enrol]->try_guestaccess($instance);
02086         if ($until !== false and $until > time()) {
02087             $USER->enrol['tempguest'][$course->id] = $until;
02088             return true;
02089         }
02090     }
02091     if (isset($USER->enrol['tempguest'][$course->id])) {
02092         unset($USER->enrol['tempguest'][$course->id]);
02093         remove_temp_course_roles($coursecontext);
02094     }
02095 
02096     return false;
02097 }
02098 
02111 function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false) {
02112     global $DB, $CFG;
02113 
02114     // use unique prefix just in case somebody makes some SQL magic with the result
02115     static $i = 0;
02116     $i++;
02117     $prefix = 'eu'.$i.'_';
02118 
02119     // first find the course context
02120     $coursecontext = $context->get_course_context();
02121 
02122     $isfrontpage = ($coursecontext->instanceid == SITEID);
02123 
02124     $joins  = array();
02125     $wheres = array();
02126     $params = array();
02127 
02128     list($contextids, $contextpaths) = get_context_info_list($context);
02129 
02130     // get all relevant capability info for all roles
02131     if ($withcapability) {
02132         list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx');
02133         $cparams['cap'] = $withcapability;
02134 
02135         $defs = array();
02136         $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
02137                   FROM {role_capabilities} rc
02138                   JOIN {context} ctx on rc.contextid = ctx.id
02139                  WHERE rc.contextid $incontexts AND rc.capability = :cap";
02140         $rcs = $DB->get_records_sql($sql, $cparams);
02141         foreach ($rcs as $rc) {
02142             $defs[$rc->path][$rc->roleid] = $rc->permission;
02143         }
02144 
02145         $access = array();
02146         if (!empty($defs)) {
02147             foreach ($contextpaths as $path) {
02148                 if (empty($defs[$path])) {
02149                     continue;
02150                 }
02151                 foreach($defs[$path] as $roleid => $perm) {
02152                     if ($perm == CAP_PROHIBIT) {
02153                         $access[$roleid] = CAP_PROHIBIT;
02154                         continue;
02155                     }
02156                     if (!isset($access[$roleid])) {
02157                         $access[$roleid] = (int)$perm;
02158                     }
02159                 }
02160             }
02161         }
02162 
02163         unset($defs);
02164 
02165         // make lists of roles that are needed and prohibited
02166         $needed     = array(); // one of these is enough
02167         $prohibited = array(); // must not have any of these
02168         foreach ($access as $roleid => $perm) {
02169             if ($perm == CAP_PROHIBIT) {
02170                 unset($needed[$roleid]);
02171                 $prohibited[$roleid] = true;
02172             } else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
02173                 $needed[$roleid] = true;
02174             }
02175         }
02176 
02177         $defaultuserroleid      = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
02178         $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
02179 
02180         $nobody = false;
02181 
02182         if ($isfrontpage) {
02183             if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) {
02184                 $nobody = true;
02185             } else if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) {
02186                 // everybody not having prohibit has the capability
02187                 $needed = array();
02188             } else if (empty($needed)) {
02189                 $nobody = true;
02190             }
02191         } else {
02192             if (!empty($prohibited[$defaultuserroleid])) {
02193                 $nobody = true;
02194             } else if (!empty($needed[$defaultuserroleid])) {
02195                 // everybody not having prohibit has the capability
02196                 $needed = array();
02197             } else if (empty($needed)) {
02198                 $nobody = true;
02199             }
02200         }
02201 
02202         if ($nobody) {
02203             // nobody can match so return some SQL that does not return any results
02204             $wheres[] = "1 = 2";
02205 
02206         } else {
02207 
02208             if ($needed) {
02209                 $ctxids = implode(',', $contextids);
02210                 $roleids = implode(',', array_keys($needed));
02211                 $joins[] = "JOIN {role_assignments} {$prefix}ra3 ON ({$prefix}ra3.userid = {$prefix}u.id AND {$prefix}ra3.roleid IN ($roleids) AND {$prefix}ra3.contextid IN ($ctxids))";
02212             }
02213 
02214             if ($prohibited) {
02215                 $ctxids = implode(',', $contextids);
02216                 $roleids = implode(',', array_keys($prohibited));
02217                 $joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4 ON ({$prefix}ra4.userid = {$prefix}u.id AND {$prefix}ra4.roleid IN ($roleids) AND {$prefix}ra4.contextid IN ($ctxids))";
02218                 $wheres[] = "{$prefix}ra4.id IS NULL";
02219             }
02220 
02221             if ($groupid) {
02222                 $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)";
02223                 $params["{$prefix}gmid"] = $groupid;
02224             }
02225         }
02226 
02227     } else {
02228         if ($groupid) {
02229             $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)";
02230             $params["{$prefix}gmid"] = $groupid;
02231         }
02232     }
02233 
02234     $wheres[] = "{$prefix}u.deleted = 0 AND {$prefix}u.id <> :{$prefix}guestid";
02235     $params["{$prefix}guestid"] = $CFG->siteguest;
02236 
02237     if ($isfrontpage) {
02238         // all users are "enrolled" on the frontpage
02239     } else {
02240         $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id";
02241         $joins[] = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
02242         $params[$prefix.'courseid'] = $coursecontext->instanceid;
02243 
02244         if ($onlyactive) {
02245             $wheres[] = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
02246             $wheres[] = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
02247             $now = round(time(), -2); // rounding helps caching in DB
02248             $params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED,
02249                                                  $prefix.'active'=>ENROL_USER_ACTIVE,
02250                                                  $prefix.'now1'=>$now, $prefix.'now2'=>$now));
02251         }
02252     }
02253 
02254     $joins = implode("\n", $joins);
02255     $wheres = "WHERE ".implode(" AND ", $wheres);
02256 
02257     $sql = "SELECT DISTINCT {$prefix}u.id
02258               FROM {user} {$prefix}u
02259             $joins
02260            $wheres";
02261 
02262     return array($sql, $params);
02263 }
02264 
02277 function get_enrolled_users(context $context, $withcapability = '', $groupid = 0, $userfields = 'u.*', $orderby = '', $limitfrom = 0, $limitnum = 0) {
02278     global $DB;
02279 
02280     list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid);
02281     $sql = "SELECT $userfields
02282               FROM {user} u
02283               JOIN ($esql) je ON je.id = u.id
02284              WHERE u.deleted = 0";
02285 
02286     if ($orderby) {
02287         $sql = "$sql ORDER BY $orderby";
02288     } else {
02289         $sql = "$sql ORDER BY u.lastname ASC, u.firstname ASC";
02290     }
02291 
02292     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
02293 }
02294 
02303 function count_enrolled_users(context $context, $withcapability = '', $groupid = 0) {
02304     global $DB;
02305 
02306     list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid);
02307     $sql = "SELECT count(u.id)
02308               FROM {user} u
02309               JOIN ($esql) je ON je.id = u.id
02310              WHERE u.deleted = 0";
02311 
02312     return $DB->count_records_sql($sql, $params);
02313 }
02314 
02324 function load_capability_def($component) {
02325     $defpath = get_component_directory($component).'/db/access.php';
02326 
02327     $capabilities = array();
02328     if (file_exists($defpath)) {
02329         require($defpath);
02330         if (!empty(${$component.'_capabilities'})) {
02331             // BC capability array name
02332             // since 2.0 we prefer $capabilities instead - it is easier to use and matches db/* files
02333             debugging('componentname_capabilities array is deprecated, please use $capabilities array only in access.php files');
02334             $capabilities = ${$component.'_capabilities'};
02335         }
02336     }
02337 
02338     return $capabilities;
02339 }
02340 
02347 function get_cached_capabilities($component = 'moodle') {
02348     global $DB;
02349     return $DB->get_records('capabilities', array('component'=>$component));
02350 }
02351 
02358 function get_default_capabilities($archetype) {
02359     global $DB;
02360 
02361     if (!$archetype) {
02362         return array();
02363     }
02364 
02365     $alldefs = array();
02366     $defaults = array();
02367     $components = array();
02368     $allcaps = $DB->get_records('capabilities');
02369 
02370     foreach ($allcaps as $cap) {
02371         if (!in_array($cap->component, $components)) {
02372             $components[] = $cap->component;
02373             $alldefs = array_merge($alldefs, load_capability_def($cap->component));
02374         }
02375     }
02376     foreach($alldefs as $name=>$def) {
02377         // Use array 'archetypes if available. Only if not specified, use 'legacy'.
02378         if (isset($def['archetypes'])) {
02379             if (isset($def['archetypes'][$archetype])) {
02380                 $defaults[$name] = $def['archetypes'][$archetype];
02381             }
02382         // 'legacy' is for backward compatibility with 1.9 access.php
02383         } else {
02384             if (isset($def['legacy'][$archetype])) {
02385                 $defaults[$name] = $def['legacy'][$archetype];
02386             }
02387         }
02388     }
02389 
02390     return $defaults;
02391 }
02392 
02400 function reset_role_capabilities($roleid) {
02401     global $DB;
02402 
02403     $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
02404     $defaultcaps = get_default_capabilities($role->archetype);
02405 
02406     $systemcontext = context_system::instance();
02407 
02408     $DB->delete_records('role_capabilities', array('roleid'=>$roleid));
02409 
02410     foreach($defaultcaps as $cap=>$permission) {
02411         assign_capability($cap, $permission, $roleid, $systemcontext->id);
02412     }
02413 }
02414 
02427 function update_capabilities($component = 'moodle') {
02428     global $DB, $OUTPUT;
02429 
02430     $storedcaps = array();
02431 
02432     $filecaps = load_capability_def($component);
02433     foreach($filecaps as $capname=>$unused) {
02434         if (!preg_match('|^[a-z]+/[a-z_0-9]+:[a-z_0-9]+$|', $capname)) {
02435             debugging("Coding problem: Invalid capability name '$capname', use 'clonepermissionsfrom' field for migration.");
02436         }
02437     }
02438 
02439     $cachedcaps = get_cached_capabilities($component);
02440     if ($cachedcaps) {
02441         foreach ($cachedcaps as $cachedcap) {
02442             array_push($storedcaps, $cachedcap->name);
02443             // update risk bitmasks and context levels in existing capabilities if needed
02444             if (array_key_exists($cachedcap->name, $filecaps)) {
02445                 if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) {
02446                     $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified
02447                 }
02448                 if ($cachedcap->captype != $filecaps[$cachedcap->name]['captype']) {
02449                     $updatecap = new stdClass();
02450                     $updatecap->id = $cachedcap->id;
02451                     $updatecap->captype = $filecaps[$cachedcap->name]['captype'];
02452                     $DB->update_record('capabilities', $updatecap);
02453                 }
02454                 if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) {
02455                     $updatecap = new stdClass();
02456                     $updatecap->id = $cachedcap->id;
02457                     $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask'];
02458                     $DB->update_record('capabilities', $updatecap);
02459                 }
02460 
02461                 if (!array_key_exists('contextlevel', $filecaps[$cachedcap->name])) {
02462                     $filecaps[$cachedcap->name]['contextlevel'] = 0; // no context level defined
02463                 }
02464                 if ($cachedcap->contextlevel != $filecaps[$cachedcap->name]['contextlevel']) {
02465                     $updatecap = new stdClass();
02466                     $updatecap->id = $cachedcap->id;
02467                     $updatecap->contextlevel = $filecaps[$cachedcap->name]['contextlevel'];
02468                     $DB->update_record('capabilities', $updatecap);
02469                 }
02470             }
02471         }
02472     }
02473 
02474     // Are there new capabilities in the file definition?
02475     $newcaps = array();
02476 
02477     foreach ($filecaps as $filecap => $def) {
02478         if (!$storedcaps ||
02479                 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
02480             if (!array_key_exists('riskbitmask', $def)) {
02481                 $def['riskbitmask'] = 0; // no risk if not specified
02482             }
02483             $newcaps[$filecap] = $def;
02484         }
02485     }
02486     // Add new capabilities to the stored definition.
02487     foreach ($newcaps as $capname => $capdef) {
02488         $capability = new stdClass();
02489         $capability->name         = $capname;
02490         $capability->captype      = $capdef['captype'];
02491         $capability->contextlevel = $capdef['contextlevel'];
02492         $capability->component    = $component;
02493         $capability->riskbitmask  = $capdef['riskbitmask'];
02494 
02495         $DB->insert_record('capabilities', $capability, false);
02496 
02497         if (isset($capdef['clonepermissionsfrom']) && in_array($capdef['clonepermissionsfrom'], $storedcaps)){
02498             if ($rolecapabilities = $DB->get_records('role_capabilities', array('capability'=>$capdef['clonepermissionsfrom']))){
02499                 foreach ($rolecapabilities as $rolecapability){
02500                     //assign_capability will update rather than insert if capability exists
02501                     if (!assign_capability($capname, $rolecapability->permission,
02502                                             $rolecapability->roleid, $rolecapability->contextid, true)){
02503                          echo $OUTPUT->notification('Could not clone capabilities for '.$capname);
02504                     }
02505                 }
02506             }
02507         // we ignore archetype key if we have cloned permissions
02508         } else if (isset($capdef['archetypes']) && is_array($capdef['archetypes'])) {
02509             assign_legacy_capabilities($capname, $capdef['archetypes']);
02510         // 'legacy' is for backward compatibility with 1.9 access.php
02511         } else if (isset($capdef['legacy']) && is_array($capdef['legacy'])) {
02512             assign_legacy_capabilities($capname, $capdef['legacy']);
02513         }
02514     }
02515     // Are there any capabilities that have been removed from the file
02516     // definition that we need to delete from the stored capabilities and
02517     // role assignments?
02518     capabilities_cleanup($component, $filecaps);
02519 
02520     // reset static caches
02521     accesslib_clear_all_caches(false);
02522 
02523     return true;
02524 }
02525 
02535 function capabilities_cleanup($component, $newcapdef = null) {
02536     global $DB;
02537 
02538     $removedcount = 0;
02539 
02540     if ($cachedcaps = get_cached_capabilities($component)) {
02541         foreach ($cachedcaps as $cachedcap) {
02542             if (empty($newcapdef) ||
02543                         array_key_exists($cachedcap->name, $newcapdef) === false) {
02544 
02545                 // Remove from capabilities cache.
02546                 $DB->delete_records('capabilities', array('name'=>$cachedcap->name));
02547                 $removedcount++;
02548                 // Delete from roles.
02549                 if ($roles = get_roles_with_capability($cachedcap->name)) {
02550                     foreach($roles as $role) {
02551                         if (!unassign_capability($cachedcap->name, $role->id)) {
02552                             print_error('cannotunassigncap', 'error', '', (object)array('cap'=>$cachedcap->name, 'role'=>$role->name));
02553                         }
02554                     }
02555                 }
02556             } // End if.
02557         }
02558     }
02559     return $removedcount;
02560 }
02561 
02569 function get_all_risks() {
02570     return array(
02571         'riskmanagetrust' => RISK_MANAGETRUST,
02572         'riskconfig'      => RISK_CONFIG,
02573         'riskxss'         => RISK_XSS,
02574         'riskpersonal'    => RISK_PERSONAL,
02575         'riskspam'        => RISK_SPAM,
02576         'riskdataloss'    => RISK_DATALOSS,
02577     );
02578 }
02579 
02586 function get_capability_docs_link($capability) {
02587     $url = get_docs_url('Capabilities/' . $capability->name);
02588     return '<a onclick="this.target=\'docspopup\'" href="' . $url . '">' . get_capability_string($capability->name) . '</a>';
02589 }
02590 
02601 function role_context_capabilities($roleid, context $context, $cap = '') {
02602     global $DB;
02603 
02604     $contexts = $context->get_parent_context_ids(true);
02605     $contexts = '('.implode(',', $contexts).')';
02606 
02607     $params = array($roleid);
02608 
02609     if ($cap) {
02610         $search = " AND rc.capability = ? ";
02611         $params[] = $cap;
02612     } else {
02613         $search = '';
02614     }
02615 
02616     $sql = "SELECT rc.*
02617               FROM {role_capabilities} rc, {context} c
02618              WHERE rc.contextid in $contexts
02619                    AND rc.roleid = ?
02620                    AND rc.contextid = c.id $search
02621           ORDER BY c.contextlevel DESC, rc.capability DESC";
02622 
02623     $capabilities = array();
02624 
02625     if ($records = $DB->get_records_sql($sql, $params)) {
02626         // We are traversing via reverse order.
02627         foreach ($records as $record) {
02628             // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
02629             if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
02630                 $capabilities[$record->capability] = $record->permission;
02631             }
02632         }
02633     }
02634     return $capabilities;
02635 }
02636 
02645 function get_context_info_list(context $context) {
02646     $contextids = explode('/', ltrim($context->path, '/'));
02647     $contextpaths = array();
02648     $contextids2 = $contextids;
02649     while ($contextids2) {
02650         $contextpaths[] = '/' . implode('/', $contextids2);
02651         array_pop($contextids2);
02652     }
02653     return array($contextids, $contextpaths);
02654 }
02655 
02665 function is_inside_frontpage(context $context) {
02666     $frontpagecontext = context_course::instance(SITEID);
02667     return strpos($context->path . '/', $frontpagecontext->path . '/') === 0;
02668 }
02669 
02676 function get_capability_info($capabilityname) {
02677     global $ACCESSLIB_PRIVATE, $DB; // one request per page only
02678 
02679     //TODO: MUC - this could be cached in shared memory, it would eliminate 1 query per page
02680 
02681     if (empty($ACCESSLIB_PRIVATE->capabilities)) {
02682         $ACCESSLIB_PRIVATE->capabilities = array();
02683         $caps = $DB->get_records('capabilities', array(), 'id, name, captype, riskbitmask');
02684         foreach ($caps as $cap) {
02685             $capname = $cap->name;
02686             unset($cap->id);
02687             unset($cap->name);
02688             $cap->riskbitmask = (int)$cap->riskbitmask;
02689             $ACCESSLIB_PRIVATE->capabilities[$capname] = $cap;
02690         }
02691     }
02692 
02693     return isset($ACCESSLIB_PRIVATE->capabilities[$capabilityname]) ? $ACCESSLIB_PRIVATE->capabilities[$capabilityname] : null;
02694 }
02695 
02703 function get_capability_string($capabilityname) {
02704 
02705     // Typical capability name is 'plugintype/pluginname:capabilityname'
02706     list($type, $name, $capname) = preg_split('|[/:]|', $capabilityname);
02707 
02708     if ($type === 'moodle') {
02709         $component = 'core_role';
02710     } else if ($type === 'quizreport') {
02711         //ugly hack!!
02712         $component = 'quiz_'.$name;
02713     } else {
02714         $component = $type.'_'.$name;
02715     }
02716 
02717     $stringname = $name.':'.$capname;
02718 
02719     if ($component === 'core_role' or get_string_manager()->string_exists($stringname, $component)) {
02720         return get_string($stringname, $component);
02721     }
02722 
02723     $dir = get_component_directory($component);
02724     if (!file_exists($dir)) {
02725         // plugin broken or does not exist, do not bother with printing of debug message
02726         return $capabilityname.' ???';
02727     }
02728 
02729     // something is wrong in plugin, better print debug
02730     return get_string($stringname, $component);
02731 }
02732 
02740 function get_component_string($component, $contextlevel) {
02741 
02742     if ($component === 'moodle' or $component === 'core') {
02743         switch ($contextlevel) {
02744             // TODO: this should probably use context level names instead
02745             case CONTEXT_SYSTEM:    return get_string('coresystem');
02746             case CONTEXT_USER:      return get_string('users');
02747             case CONTEXT_COURSECAT: return get_string('categories');
02748             case CONTEXT_COURSE:    return get_string('course');
02749             case CONTEXT_MODULE:    return get_string('activities');
02750             case CONTEXT_BLOCK:     return get_string('block');
02751             default:                print_error('unknowncontext');
02752         }
02753     }
02754 
02755     list($type, $name) = normalize_component($component);
02756     $dir = get_plugin_directory($type, $name);
02757     if (!file_exists($dir)) {
02758         // plugin not installed, bad luck, there is no way to find the name
02759         return $component.' ???';
02760     }
02761 
02762     switch ($type) {
02763         // TODO: this is really hacky, anyway it should be probably moved to lib/pluginlib.php
02764         case 'quiz':         return get_string($name.':componentname', $component);// insane hack!!!
02765         case 'repository':   return get_string('repository', 'repository').': '.get_string('pluginname', $component);
02766         case 'gradeimport':  return get_string('gradeimport', 'grades').': '.get_string('pluginname', $component);
02767         case 'gradeexport':  return get_string('gradeexport', 'grades').': '.get_string('pluginname', $component);
02768         case 'gradereport':  return get_string('gradereport', 'grades').': '.get_string('pluginname', $component);
02769         case 'webservice':   return get_string('webservice', 'webservice').': '.get_string('pluginname', $component);
02770         case 'block':        return get_string('block').': '.get_string('pluginname', basename($component));
02771         case 'mod':
02772             if (get_string_manager()->string_exists('pluginname', $component)) {
02773                 return get_string('activity').': '.get_string('pluginname', $component);
02774             } else {
02775                 return get_string('activity').': '.get_string('modulename', $component);
02776             }
02777         default: return get_string('pluginname', $component);
02778     }
02779 }
02780 
02789 function get_profile_roles(context $context) {
02790     global $CFG, $DB;
02791 
02792     if (empty($CFG->profileroles)) {
02793         return array();
02794     }
02795 
02796     list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a');
02797     list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p');
02798     $params = array_merge($params, $cparams);
02799 
02800     $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder
02801               FROM {role_assignments} ra, {role} r
02802              WHERE r.id = ra.roleid
02803                    AND ra.contextid $contextlist
02804                    AND r.id $rallowed
02805           ORDER BY r.sortorder ASC";
02806 
02807     return $DB->get_records_sql($sql, $params);
02808 }
02809 
02816 function get_roles_used_in_context(context $context) {
02817     global $DB;
02818 
02819     list($contextlist, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(true));
02820 
02821     $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder
02822               FROM {role_assignments} ra, {role} r
02823              WHERE r.id = ra.roleid
02824                    AND ra.contextid $contextlist
02825           ORDER BY r.sortorder ASC";
02826 
02827     return $DB->get_records_sql($sql, $params);
02828 }
02829 
02839 function get_user_roles_in_course($userid, $courseid) {
02840     global $CFG, $DB;
02841 
02842     if (empty($CFG->profileroles)) {
02843         return '';
02844     }
02845 
02846     if ($courseid == SITEID) {
02847         $context = context_system::instance();
02848     } else {
02849         $context = context_course::instance($courseid);
02850     }
02851 
02852     if (empty($CFG->profileroles)) {
02853         return array();
02854     }
02855 
02856     list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a');
02857     list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p');
02858     $params = array_merge($params, $cparams);
02859 
02860     $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder
02861               FROM {role_assignments} ra, {role} r
02862              WHERE r.id = ra.roleid
02863                    AND ra.contextid $contextlist
02864                    AND r.id $rallowed
02865                    AND ra.userid = :userid
02866           ORDER BY r.sortorder ASC";
02867     $params['userid'] = $userid;
02868 
02869     $rolestring = '';
02870 
02871     if ($roles = $DB->get_records_sql($sql, $params)) {
02872         foreach ($roles as $userrole) {
02873             $rolenames[$userrole->id] = $userrole->name;
02874         }
02875 
02876         $rolenames = role_fix_names($rolenames, $context);   // Substitute aliases
02877 
02878         foreach ($rolenames as $roleid => $rolename) {
02879             $rolenames[$roleid] = '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$context->id.'&amp;roleid='.$roleid.'">'.$rolename.'</a>';
02880         }
02881         $rolestring = implode(',', $rolenames);
02882     }
02883 
02884     return $rolestring;
02885 }
02886 
02894 function user_can_assign(context $context, $targetroleid) {
02895     global $DB;
02896 
02897     // first check if user has override capability
02898     // if not return false;
02899     if (!has_capability('moodle/role:assign', $context)) {
02900         return false;
02901     }
02902     // pull out all active roles of this user from this context(or above)
02903     if ($userroles = get_user_roles($context)) {
02904         foreach ($userroles as $userrole) {
02905             // if any in the role_allow_override table, then it's ok
02906             if ($DB->get_record('role_allow_assign', array('roleid'=>$userrole->roleid, 'allowassign'=>$targetroleid))) {
02907                 return true;
02908             }
02909         }
02910     }
02911 
02912     return false;
02913 }
02914 
02920 function get_all_roles() {
02921     global $DB;
02922     return $DB->get_records('role', null, 'sortorder ASC');
02923 }
02924 
02931 function get_archetype_roles($archetype) {
02932     global $DB;
02933     return $DB->get_records('role', array('archetype'=>$archetype), 'sortorder ASC');
02934 }
02935 
02948 function get_user_roles(context $context, $userid = 0, $checkparentcontexts = true, $order = 'c.contextlevel DESC, r.sortorder ASC') {
02949     global $USER, $DB;
02950 
02951     if (empty($userid)) {
02952         if (empty($USER->id)) {
02953             return array();
02954         }
02955         $userid = $USER->id;
02956     }
02957 
02958     if ($checkparentcontexts) {
02959         $contextids = $context->get_parent_context_ids();
02960     } else {
02961         $contextids = array();
02962     }
02963     $contextids[] = $context->id;
02964 
02965     list($contextids, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_QM);
02966 
02967     array_unshift($params, $userid);
02968 
02969     $sql = "SELECT ra.*, r.name, r.shortname
02970               FROM {role_assignments} ra, {role} r, {context} c
02971              WHERE ra.userid = ?
02972                    AND ra.roleid = r.id
02973                    AND ra.contextid = c.id
02974                    AND ra.contextid $contextids
02975           ORDER BY $order";
02976 
02977     return $DB->get_records_sql($sql ,$params);
02978 }
02979 
02987 function allow_override($sroleid, $troleid) {
02988     global $DB;
02989 
02990     $record = new stdClass();
02991     $record->roleid        = $sroleid;
02992     $record->allowoverride = $troleid;
02993     $DB->insert_record('role_allow_override', $record);
02994 }
02995 
03003 function allow_assign($fromroleid, $targetroleid) {
03004     global $DB;
03005 
03006     $record = new stdClass();
03007     $record->roleid      = $fromroleid;
03008     $record->allowassign = $targetroleid;
03009     $DB->insert_record('role_allow_assign', $record);
03010 }
03011 
03019 function allow_switch($fromroleid, $targetroleid) {
03020     global $DB;
03021 
03022     $record = new stdClass();
03023     $record->roleid      = $fromroleid;
03024     $record->allowswitch = $targetroleid;
03025     $DB->insert_record('role_allow_switch', $record);
03026 }
03027 
03040 function get_assignable_roles(context $context, $rolenamedisplay = ROLENAME_ALIAS, $withusercounts = false, $user = null) {
03041     global $USER, $DB;
03042 
03043     // make sure there is a real user specified
03044     if ($user === null) {
03045         $userid = isset($USER->id) ? $USER->id : 0;
03046     } else {
03047         $userid = is_object($user) ? $user->id : $user;
03048     }
03049 
03050     if (!has_capability('moodle/role:assign', $context, $userid)) {
03051         if ($withusercounts) {
03052             return array(array(), array(), array());
03053         } else {
03054             return array();
03055         }
03056     }
03057 
03058     $parents = $context->get_parent_context_ids(true);
03059     $contexts = implode(',' , $parents);
03060 
03061     $params = array();
03062     $extrafields = '';
03063     if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT or $rolenamedisplay == ROLENAME_SHORT) {
03064         $extrafields .= ', r.shortname';
03065     }
03066 
03067     if ($withusercounts) {
03068         $extrafields = ', (SELECT count(u.id)
03069                              FROM {role_assignments} cra JOIN {user} u ON cra.userid = u.id
03070                             WHERE cra.roleid = r.id AND cra.contextid = :conid AND u.deleted = 0
03071                           ) AS usercount';
03072         $params['conid'] = $context->id;
03073     }
03074 
03075     if (is_siteadmin($userid)) {
03076         // show all roles allowed in this context to admins
03077         $assignrestriction = "";
03078     } else {
03079         $assignrestriction = "JOIN (SELECT DISTINCT raa.allowassign AS id
03080                                       FROM {role_allow_assign} raa
03081                                       JOIN {role_assignments} ra ON ra.roleid = raa.roleid
03082                                      WHERE ra.userid = :userid AND ra.contextid IN ($contexts)
03083                                    ) ar ON ar.id = r.id";
03084         $params['userid'] = $userid;
03085     }
03086     $params['contextlevel'] = $context->contextlevel;
03087     $sql = "SELECT r.id, r.name $extrafields
03088               FROM {role} r
03089               $assignrestriction
03090               JOIN {role_context_levels} rcl ON r.id = rcl.roleid
03091              WHERE rcl.contextlevel = :contextlevel
03092           ORDER BY r.sortorder ASC";
03093     $roles = $DB->get_records_sql($sql, $params);
03094 
03095     $rolenames = array();
03096     foreach ($roles as $role) {
03097         if ($rolenamedisplay == ROLENAME_SHORT) {
03098             $rolenames[$role->id] = $role->shortname;
03099             continue;
03100         }
03101         $rolenames[$role->id] = $role->name;
03102         if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) {
03103             $rolenames[$role->id] .= ' (' . $role->shortname . ')';
03104         }
03105     }
03106     if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT and $rolenamedisplay != ROLENAME_SHORT) {
03107         $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay);
03108     }
03109 
03110     if (!$withusercounts) {
03111         return $rolenames;
03112     }
03113 
03114     $rolecounts = array();
03115     $nameswithcounts = array();
03116     foreach ($roles as $role) {
03117         $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->usercount . ')';
03118         $rolecounts[$role->id] = $roles[$role->id]->usercount;
03119     }
03120     return array($rolenames, $rolecounts, $nameswithcounts);
03121 }
03122 
03133 function get_switchable_roles(context $context) {
03134     global $USER, $DB;
03135 
03136     $params = array();
03137     $extrajoins = '';
03138     $extrawhere = '';
03139     if (!is_siteadmin()) {
03140         // Admins are allowed to switch to any role with.
03141         // Others are subject to the additional constraint that the switch-to role must be allowed by
03142         // 'role_allow_switch' for some role they have assigned in this context or any parent.
03143         $parents = $context->get_parent_context_ids(true);
03144         $contexts = implode(',' , $parents);
03145 
03146         $extrajoins = "JOIN {role_allow_switch} ras ON ras.allowswitch = rc.roleid
03147         JOIN {role_assignments} ra ON ra.roleid = ras.roleid";
03148         $extrawhere = "WHERE ra.userid = :userid AND ra.contextid IN ($contexts)";
03149         $params['userid'] = $USER->id;
03150     }
03151 
03152     $query = "
03153         SELECT r.id, r.name
03154           FROM (SELECT DISTINCT rc.roleid
03155                   FROM {role_capabilities} rc
03156                   $extrajoins
03157                   $extrawhere) idlist
03158           JOIN {role} r ON r.id = idlist.roleid
03159       ORDER BY r.sortorder";
03160 
03161     $rolenames = $DB->get_records_sql_menu($query, $params);
03162     return role_fix_names($rolenames, $context, ROLENAME_ALIAS);
03163 }
03164 
03176 function get_overridable_roles(context $context, $rolenamedisplay = ROLENAME_ALIAS, $withcounts = false) {
03177     global $USER, $DB;
03178 
03179     if (!has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context)) {
03180         if ($withcounts) {
03181             return array(array(), array(), array());
03182         } else {
03183             return array();
03184         }
03185     }
03186 
03187     $parents = $context->get_parent_context_ids(true);
03188     $contexts = implode(',' , $parents);
03189 
03190     $params = array();
03191     $extrafields = '';
03192     if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) {
03193         $extrafields .= ', ro.shortname';
03194     }
03195 
03196     $params['userid'] = $USER->id;
03197     if ($withcounts) {
03198         $extrafields = ', (SELECT COUNT(rc.id) FROM {role_capabilities} rc
03199                 WHERE rc.roleid = ro.id AND rc.contextid = :conid) AS overridecount';
03200         $params['conid'] = $context->id;
03201     }
03202 
03203     if (is_siteadmin()) {
03204         // show all roles to admins
03205         $roles = $DB->get_records_sql("
03206             SELECT ro.id, ro.name$extrafields
03207               FROM {role} ro
03208           ORDER BY ro.sortorder ASC", $params);
03209 
03210     } else {
03211         $roles = $DB->get_records_sql("
03212             SELECT ro.id, ro.name$extrafields
03213               FROM {role} ro
03214               JOIN (SELECT DISTINCT r.id
03215                       FROM {role} r
03216                       JOIN {role_allow_override} rao ON r.id = rao.allowoverride
03217                       JOIN {role_assignments} ra ON rao.roleid = ra.roleid
03218                      WHERE ra.userid = :userid AND ra.contextid IN ($contexts)
03219                    ) inline_view ON ro.id = inline_view.id
03220           ORDER BY ro.sortorder ASC", $params);
03221     }
03222 
03223     $rolenames = array();
03224     foreach ($roles as $role) {
03225         $rolenames[$role->id] = $role->name;
03226         if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) {
03227             $rolenames[$role->id] .= ' (' . $role->shortname . ')';
03228         }
03229     }
03230     if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT) {
03231         $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay);
03232     }
03233 
03234     if (!$withcounts) {
03235         return $rolenames;
03236 }
03237 
03238     $rolecounts = array();
03239     $nameswithcounts = array();
03240     foreach ($roles as $role) {
03241         $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->overridecount . ')';
03242         $rolecounts[$role->id] = $roles[$role->id]->overridecount;
03243     }
03244     return array($rolenames, $rolecounts, $nameswithcounts);
03245 }
03246 
03253 function get_default_enrol_roles(context $context, $addroleid = null) {
03254     global $DB;
03255 
03256     $params = array('contextlevel'=>CONTEXT_COURSE);
03257     if ($addroleid) {
03258         $addrole = "OR r.id = :addroleid";
03259         $params['addroleid'] = $addroleid;
03260     } else {
03261         $addrole = "";
03262     }
03263     $sql = "SELECT r.id, r.name
03264               FROM {role} r
03265          LEFT JOIN {role_context_levels} rcl ON (rcl.roleid = r.id AND rcl.contextlevel = :contextlevel)
03266              WHERE rcl.id IS NOT NULL $addrole
03267           ORDER BY sortorder DESC";
03268 
03269     $roles = $DB->get_records_sql_menu($sql, $params);
03270     $roles = role_fix_names($roles, $context, ROLENAME_BOTH);
03271 
03272     return $roles;
03273 }
03274 
03280 function get_role_contextlevels($roleid) {
03281     global $DB;
03282     return $DB->get_records_menu('role_context_levels', array('roleid' => $roleid),
03283             'contextlevel', 'id,contextlevel');
03284 }
03285 
03294 function get_roles_for_contextlevels($contextlevel) {
03295     global $DB;
03296     return $DB->get_records_menu('role_context_levels', array('contextlevel' => $contextlevel),
03297             '', 'id,roleid');
03298 }
03299 
03307 function get_default_contextlevels($rolearchetype) {
03308     static $defaults = array(
03309         'manager'        => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE),
03310         'coursecreator'  => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT),
03311         'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE),
03312         'teacher'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
03313         'student'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
03314         'guest'          => array(),
03315         'user'           => array(),
03316         'frontpage'      => array());
03317 
03318     if (isset($defaults[$rolearchetype])) {
03319         return $defaults[$rolearchetype];
03320     } else {
03321         return array();
03322     }
03323 }
03324 
03334 function set_role_contextlevels($roleid, array $contextlevels) {
03335     global $DB;
03336     $DB->delete_records('role_context_levels', array('roleid' => $roleid));
03337     $rcl = new stdClass();
03338     $rcl->roleid = $roleid;
03339     $contextlevels = array_unique($contextlevels);
03340     foreach ($contextlevels as $level) {
03341         $rcl->contextlevel = $level;
03342         $DB->insert_record('role_context_levels', $rcl, false, true);
03343     }
03344 }
03345 
03373 function get_users_by_capability(context $context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '',
03374                                  $groups = '', $exceptions = '', $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false) {
03375     global $CFG, $DB;
03376 
03377     $defaultuserroleid      = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
03378     $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
03379 
03380     $ctxids = trim($context->path, '/');
03381     $ctxids = str_replace('/', ',', $ctxids);
03382 
03383     // Context is the frontpage
03384     $iscoursepage = false; // coursepage other than fp
03385     $isfrontpage = false;
03386     if ($context->contextlevel == CONTEXT_COURSE) {
03387         if ($context->instanceid == SITEID) {
03388             $isfrontpage = true;
03389         } else {
03390             $iscoursepage = true;
03391         }
03392     }
03393     $isfrontpage = ($isfrontpage || is_inside_frontpage($context));
03394 
03395     $caps = (array)$capability;
03396 
03397     // construct list of context paths bottom-->top
03398     list($contextids, $paths) = get_context_info_list($context);
03399 
03400     // we need to find out all roles that have these capabilities either in definition or in overrides
03401     $defs = array();
03402     list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con');
03403     list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, 'cap');
03404     $params = array_merge($params, $params2);
03405     $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path
03406               FROM {role_capabilities} rc
03407               JOIN {context} ctx on rc.contextid = ctx.id
03408              WHERE rc.contextid $incontexts AND rc.capability $incaps";
03409 
03410     $rcs = $DB->get_records_sql($sql, $params);
03411     foreach ($rcs as $rc) {
03412         $defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission;
03413     }
03414 
03415     // go through the permissions bottom-->top direction to evaluate the current permission,
03416     // first one wins (prohibit is an exception that always wins)
03417     $access = array();
03418     foreach ($caps as $cap) {
03419         foreach ($paths as $path) {
03420             if (empty($defs[$cap][$path])) {
03421                 continue;
03422             }
03423             foreach($defs[$cap][$path] as $roleid => $perm) {
03424                 if ($perm == CAP_PROHIBIT) {
03425                     $access[$cap][$roleid] = CAP_PROHIBIT;
03426                     continue;
03427                 }
03428                 if (!isset($access[$cap][$roleid])) {
03429                     $access[$cap][$roleid] = (int)$perm;
03430                 }
03431             }
03432         }
03433     }
03434 
03435     // make lists of roles that are needed and prohibited in this context
03436     $needed = array(); // one of these is enough
03437     $prohibited = array(); // must not have any of these
03438     foreach ($caps as $cap) {
03439         if (empty($access[$cap])) {
03440             continue;
03441         }
03442         foreach ($access[$cap] as $roleid => $perm) {
03443             if ($perm == CAP_PROHIBIT) {
03444                 unset($needed[$cap][$roleid]);
03445                 $prohibited[$cap][$roleid] = true;
03446             } else if ($perm == CAP_ALLOW and empty($prohibited[$cap][$roleid])) {
03447                 $needed[$cap][$roleid] = true;
03448             }
03449         }
03450         if (empty($needed[$cap]) or !empty($prohibited[$cap][$defaultuserroleid])) {
03451             // easy, nobody has the permission
03452             unset($needed[$cap]);
03453             unset($prohibited[$cap]);
03454         } else if ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) {
03455             // everybody is disqualified on the frontapge
03456             unset($needed[$cap]);
03457             unset($prohibited[$cap]);
03458         }
03459         if (empty($prohibited[$cap])) {
03460             unset($prohibited[$cap]);
03461         }
03462     }
03463 
03464     if (empty($needed)) {
03465         // there can not be anybody if no roles match this request
03466         return array();
03467     }
03468 
03469     if (empty($prohibited)) {
03470         // we can compact the needed roles
03471         $n = array();
03472         foreach ($needed as $cap) {
03473             foreach ($cap as $roleid=>$unused) {
03474                 $n[$roleid] = true;
03475             }
03476         }
03477         $needed = array('any'=>$n);
03478         unset($n);
03479     }
03480 
03482     if (empty($fields)) {
03483         if ($iscoursepage) {
03484             $fields = 'u.*, ul.timeaccess AS lastaccess';
03485         } else {
03486             $fields = 'u.*';
03487         }
03488     } else {
03489         if (debugging('', DEBUG_DEVELOPER) && strpos($fields, 'u.*') === false && strpos($fields, 'u.id') === false) {
03490             debugging('u.id must be included in the list of fields passed to get_users_by_capability().', DEBUG_DEVELOPER);
03491         }
03492     }
03493 
03495     if (empty($sort)) { // default to course lastaccess or just lastaccess
03496         if ($iscoursepage) {
03497             $sort = 'ul.timeaccess';
03498         } else {
03499             $sort = 'u.lastaccess';
03500         }
03501     }
03502 
03503     // Prepare query clauses
03504     $wherecond = array();
03505     $params    = array();
03506     $joins     = array();
03507 
03508     // User lastaccess JOIN
03509     if ((strpos($sort, 'ul.timeaccess') === false) and (strpos($fields, 'ul.timeaccess') === false)) {
03510          // user_lastaccess is not required MDL-13810
03511     } else {
03512         if ($iscoursepage) {
03513             $joins[] = "LEFT OUTER JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})";
03514         } else {
03515             throw new coding_exception('Invalid sort in get_users_by_capability(), ul.timeaccess allowed only for course contexts.');
03516         }
03517     }
03518 
03520     $wherecond[] = "u.deleted = 0 AND u.id <> :guestid";
03521     $params['guestid'] = $CFG->siteguest;
03522 
03524     if ($groups) {
03525         $groups = (array)$groups;
03526         list($grouptest, $grpparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grp');
03527         $grouptest = "u.id IN (SELECT userid FROM {groups_members} gm WHERE gm.groupid $grouptest)";
03528         $params = array_merge($params, $grpparams);
03529 
03530         if ($useviewallgroups) {
03531             $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions);
03532             if (!empty($viewallgroupsusers)) {
03533                 $wherecond[] =  "($grouptest OR u.id IN (" . implode(',', array_keys($viewallgroupsusers)) . '))';
03534             } else {
03535                 $wherecond[] =  "($grouptest)";
03536             }
03537         } else {
03538             $wherecond[] =  "($grouptest)";
03539         }
03540     }
03541 
03543     if (!empty($exceptions)) {
03544         $exceptions = (array)$exceptions;
03545         list($exsql, $exparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'exc', false);
03546         $params = array_merge($params, $exparams);
03547         $wherecond[] = "u.id $exsql";
03548     }
03549 
03550     // now add the needed and prohibited roles conditions as joins
03551     if (!empty($needed['any'])) {
03552         // simple case - there are no prohibits involved
03553         if (!empty($needed['any'][$defaultuserroleid]) or ($isfrontpage and !empty($needed['any'][$defaultfrontpageroleid]))) {
03554             // everybody
03555         } else {
03556             $joins[] = "JOIN (SELECT DISTINCT userid
03557                                 FROM {role_assignments}
03558                                WHERE contextid IN ($ctxids)
03559                                      AND roleid IN (".implode(',', array_keys($needed['any'])) .")
03560                              ) ra ON ra.userid = u.id";
03561         }
03562     } else {
03563         $unions = array();
03564         $everybody = false;
03565         foreach ($needed as $cap=>$unused) {
03566             if (empty($prohibited[$cap])) {
03567                 if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) {
03568                     $everybody = true;
03569                     break;
03570                 } else {
03571                     $unions[] = "SELECT userid
03572                                    FROM {role_assignments}
03573                                   WHERE contextid IN ($ctxids)
03574                                         AND roleid IN (".implode(',', array_keys($needed[$cap])) .")";
03575                 }
03576             } else {
03577                 if (!empty($prohibited[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid]))) {
03578                     // nobody can have this cap because it is prevented in default roles
03579                     continue;
03580 
03581                 } else if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) {
03582                     // everybody except the prohibitted - hiding does not matter
03583                     $unions[] = "SELECT id AS userid
03584                                    FROM {user}
03585                                   WHERE id NOT IN (SELECT userid
03586                                                      FROM {role_assignments}
03587                                                     WHERE contextid IN ($ctxids)
03588                                                           AND roleid IN (".implode(',', array_keys($prohibited[$cap])) ."))";
03589 
03590                 } else {
03591                     $unions[] = "SELECT userid
03592                                    FROM {role_assignments}
03593                                   WHERE contextid IN ($ctxids)
03594                                         AND roleid IN (".implode(',', array_keys($needed[$cap])) .")
03595                                         AND roleid NOT IN (".implode(',', array_keys($prohibited[$cap])) .")";
03596                 }
03597             }
03598         }
03599         if (!$everybody) {
03600             if ($unions) {
03601                 $joins[] = "JOIN (SELECT DISTINCT userid FROM ( ".implode(' UNION ', $unions)." ) us) ra ON ra.userid = u.id";
03602             } else {
03603                 // only prohibits found - nobody can be matched
03604                 $wherecond[] = "1 = 2";
03605             }
03606         }
03607     }
03608 
03609     // Collect WHERE conditions and needed joins
03610     $where = implode(' AND ', $wherecond);
03611     if ($where !== '') {
03612         $where = 'WHERE ' . $where;
03613     }
03614     $joins = implode("\n", $joins);
03615 
03617     $sql = "SELECT $fields
03618               FROM {user} u
03619             $joins
03620             $where
03621           ORDER BY $sort";
03622 
03623     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
03624 }
03625 
03657 function sort_by_roleassignment_authority($users, context $context, $roles = array(), $sortpolicy = 'locality') {
03658     global $DB;
03659 
03660     $userswhere = ' ra.userid IN (' . implode(',',array_keys($users)) . ')';
03661     $contextwhere = 'AND ra.contextid IN ('.str_replace('/', ',',substr($context->path, 1)).')';
03662     if (empty($roles)) {
03663         $roleswhere = '';
03664     } else {
03665         $roleswhere = ' AND ra.roleid IN ('.implode(',',$roles).')';
03666     }
03667 
03668     $sql = "SELECT ra.userid
03669               FROM {role_assignments} ra
03670               JOIN {role} r
03671                    ON ra.roleid=r.id
03672               JOIN {context} ctx
03673                    ON ra.contextid=ctx.id
03674              WHERE $userswhere
03675                    $contextwhere
03676                    $roleswhere";
03677 
03678     // Default 'locality' policy -- read PHPDoc notes
03679     // about sort policies...
03680     $orderby = 'ORDER BY '
03681                     .'ctx.depth DESC, '  /* locality wins */
03682                     .'r.sortorder ASC, ' /* rolesorting 2nd criteria */
03683                     .'ra.id';            /* role assignment order tie-breaker */
03684     if ($sortpolicy === 'sortorder') {
03685         $orderby = 'ORDER BY '
03686                         .'r.sortorder ASC, ' /* rolesorting 2nd criteria */
03687                         .'ra.id';            /* role assignment order tie-breaker */
03688     }
03689 
03690     $sortedids = $DB->get_fieldset_sql($sql . $orderby);
03691     $sortedusers = array();
03692     $seen = array();
03693 
03694     foreach ($sortedids as $id) {
03695         // Avoid duplicates
03696         if (isset($seen[$id])) {
03697             continue;
03698         }
03699         $seen[$id] = true;
03700 
03701         // assign
03702         $sortedusers[$id] = $users[$id];
03703     }
03704     return $sortedusers;
03705 }
03706 
03723 function get_role_users($roleid, context $context, $parent = false, $fields = '',
03724         $sort = 'u.lastname, u.firstname', $gethidden_ignored = null, $group = '',
03725         $limitfrom = '', $limitnum = '', $extrawheretest = '', $whereparams = array()) {
03726     global $DB;
03727 
03728     if (empty($fields)) {
03729         $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '.
03730                   'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
03731                   'u.country, u.picture, u.idnumber, u.department, u.institution, '.
03732                   'u.lang, u.timezone, u.lastaccess, u.mnethostid, r.name AS rolename, r.sortorder';
03733     }
03734 
03735     $parentcontexts = '';
03736     if ($parent) {
03737         $parentcontexts = substr($context->path, 1); // kill leading slash
03738         $parentcontexts = str_replace('/', ',', $parentcontexts);
03739         if ($parentcontexts !== '') {
03740             $parentcontexts = ' OR ra.contextid IN ('.$parentcontexts.' )';
03741         }
03742     }
03743 
03744     if ($roleid) {
03745         list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM);
03746         $roleselect = "AND ra.roleid $rids";
03747     } else {
03748         $params = array();
03749         $roleselect = '';
03750     }
03751 
03752     if ($group) {
03753         $groupjoin   = "JOIN {groups_members} gm ON gm.userid = u.id";
03754         $groupselect = " AND gm.groupid = ? ";
03755         $params[] = $group;
03756     } else {
03757         $groupjoin   = '';
03758         $groupselect = '';
03759     }
03760 
03761     array_unshift($params, $context->id);
03762 
03763     if ($extrawheretest) {
03764         $extrawheretest = ' AND ' . $extrawheretest;
03765         $params = array_merge($params, $whereparams);
03766     }
03767 
03768     $sql = "SELECT DISTINCT $fields, ra.roleid
03769               FROM {role_assignments} ra
03770               JOIN {user} u ON u.id = ra.userid
03771               JOIN {role} r ON ra.roleid = r.id
03772         $groupjoin
03773              WHERE (ra.contextid = ? $parentcontexts)
03774                    $roleselect
03775                    $groupselect
03776                    $extrawheretest
03777           ORDER BY $sort";                  // join now so that we can just use fullname() later
03778 
03779     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
03780 }
03781 
03790 function count_role_users($roleid, context $context, $parent = false) {
03791     global $DB;
03792 
03793     if ($parent) {
03794         if ($contexts = $context->get_parent_context_ids()) {
03795             $parentcontexts = ' OR r.contextid IN ('.implode(',', $contexts).')';
03796         } else {
03797             $parentcontexts = '';
03798         }
03799     } else {
03800         $parentcontexts = '';
03801     }
03802 
03803     if ($roleid) {
03804         list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM);
03805         $roleselect = "AND r.roleid $rids";
03806     } else {
03807         $params = array();
03808         $roleselect = '';
03809     }
03810 
03811     array_unshift($params, $context->id);
03812 
03813     $sql = "SELECT COUNT(u.id)
03814               FROM {role_assignments} r
03815               JOIN {user} u ON u.id = r.userid
03816              WHERE (r.contextid = ? $parentcontexts)
03817                    $roleselect
03818                    AND u.deleted = 0";
03819 
03820     return $DB->count_records_sql($sql, $params);
03821 }
03822 
03836 function get_user_capability_course($capability, $userid = null, $doanything = true, $fieldsexceptid = '', $orderby = '') {
03837     global $DB;
03838 
03839     // Convert fields list and ordering
03840     $fieldlist = '';
03841     if ($fieldsexceptid) {
03842         $fields = explode(',', $fieldsexceptid);
03843         foreach($fields as $field) {
03844             $fieldlist .= ',c.'.$field;
03845         }
03846     }
03847     if ($orderby) {
03848         $fields = explode(',', $orderby);
03849         $orderby = '';
03850         foreach($fields as $field) {
03851             if ($orderby) {
03852                 $orderby .= ',';
03853             }
03854             $orderby .= 'c.'.$field;
03855         }
03856         $orderby = 'ORDER BY '.$orderby;
03857     }
03858 
03859     // Obtain a list of everything relevant about all courses including context.
03860     // Note the result can be used directly as a context (we are going to), the course
03861     // fields are just appended.
03862 
03863     $contextpreload = context_helper::get_preload_record_columns_sql('x');
03864 
03865     $courses = array();
03866     $rs = $DB->get_recordset_sql("SELECT c.id $fieldlist, $contextpreload
03867                                     FROM {course} c
03868                                     JOIN {context} x ON (c.id=x.instanceid AND x.contextlevel=".CONTEXT_COURSE.")
03869                                 $orderby");
03870     // Check capability for each course in turn
03871     foreach ($rs as $course) {
03872         context_helper::preload_from_record($course);
03873         $context = context_course::instance($course->id);
03874         if (has_capability($capability, $context, $userid, $doanything)) {
03875             // We've got the capability. Make the record look like a course record
03876             // and store it
03877             $courses[] = $course;
03878         }
03879     }
03880     $rs->close();
03881     return empty($courses) ? false : $courses;
03882 }
03883 
03891 function get_roles_on_exact_context(context $context) {
03892     global $DB;
03893 
03894     return $DB->get_records_sql("SELECT r.*
03895                                    FROM {role_assignments} ra, {role} r
03896                                   WHERE ra.roleid = r.id AND ra.contextid = ?",
03897                                 array($context->id));
03898 }
03899 
03917 function role_switch($roleid, context $context) {
03918     global $USER;
03919 
03920     //
03921     // Plan of action
03922     //
03923     // - Add the ghost RA to $USER->access
03924     //   as $USER->access['rsw'][$path] = $roleid
03925     //
03926     // - Make sure $USER->access['rdef'] has the roledefs
03927     //   it needs to honour the switcherole
03928     //
03929     // Roledefs will get loaded "deep" here - down to the last child
03930     // context. Note that
03931     //
03932     // - When visiting subcontexts, our selective accessdata loading
03933     //   will still work fine - though those ra/rdefs will be ignored
03934     //   appropriately while the switch is in place
03935     //
03936     // - If a switcherole happens at a category with tons of courses
03937     //   (that have many overrides for switched-to role), the session
03938     //   will get... quite large. Sometimes you just can't win.
03939     //
03940     // To un-switch just unset($USER->access['rsw'][$path])
03941     //
03942     // Note: it is not possible to switch to roles that do not have course:view
03943 
03944     if (!isset($USER->access)) {
03945         load_all_capabilities();
03946     }
03947 
03948 
03949     // Add the switch RA
03950     if ($roleid == 0) {
03951         unset($USER->access['rsw'][$context->path]);
03952         return true;
03953     }
03954 
03955     $USER->access['rsw'][$context->path] = $roleid;
03956 
03957     // Load roledefs
03958     load_role_access_by_context($roleid, $context, $USER->access);
03959 
03960     return true;
03961 }
03962 
03973 function is_role_switched($courseid) {
03974     global $USER;
03975     $context = context_course::instance($courseid, MUST_EXIST);
03976     return (!empty($USER->access['rsw'][$context->path]));
03977 }
03978 
03985 function get_roles_with_override_on_context(context $context) {
03986     global $DB;
03987 
03988     return $DB->get_records_sql("SELECT r.*
03989                                    FROM {role_capabilities} rc, {role} r
03990                                   WHERE rc.roleid = r.id AND rc.contextid = ?",
03991                                 array($context->id));
03992 }
03993 
04001 function get_capabilities_from_role_on_context($role, context $context) {
04002     global $DB;
04003 
04004     return $DB->get_records_sql("SELECT *
04005                                    FROM {role_capabilities}
04006                                   WHERE contextid = ? AND roleid = ?",
04007                                 array($context->id, $role->id));
04008 }
04009 
04017 function get_roles_with_assignment_on_context(context $context) {
04018     global $DB;
04019 
04020     return $DB->get_records_sql("SELECT r.*
04021                                    FROM {role_assignments} ra, {role} r
04022                                   WHERE ra.roleid = r.id AND ra.contextid = ?",
04023                                 array($context->id));
04024 }
04025 
04033 function get_users_from_role_on_context($role, context $context) {
04034     global $DB;
04035 
04036     return $DB->get_records_sql("SELECT *
04037                                    FROM {role_assignments}
04038                                   WHERE contextid = ? AND roleid = ?",
04039                                 array($context->id, $role->id));
04040 }
04041 
04051 function user_has_role_assignment($userid, $roleid, $contextid = 0) {
04052     global $DB;
04053 
04054     if ($contextid) {
04055         if (!$context = context::instance_by_id($contextid, IGNORE_MISSING)) {
04056             return false;
04057         }
04058         $parents = $context->get_parent_context_ids(true);
04059         list($contexts, $params) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED, 'r');
04060         $params['userid'] = $userid;
04061         $params['roleid'] = $roleid;
04062 
04063         $sql = "SELECT COUNT(ra.id)
04064                   FROM {role_assignments} ra
04065                  WHERE ra.userid = :userid AND ra.roleid = :roleid AND ra.contextid $contexts";
04066 
04067         $count = $DB->get_field_sql($sql, $params);
04068         return ($count > 0);
04069 
04070     } else {
04071         return $DB->record_exists('role_assignments', array('userid'=>$userid, 'roleid'=>$roleid));
04072     }
04073 }
04074 
04082 function role_get_name($role, context_course $coursecontext) {
04083     global $DB;
04084 
04085     if ($r = $DB->get_record('role_names', array('roleid'=>$role->id, 'contextid'=>$coursecontext->id))) {
04086         return strip_tags(format_string($r->name));
04087     } else {
04088         return strip_tags(format_string($role->name));
04089     }
04090 }
04091 
04100 function role_fix_names($roleoptions, context $context, $rolenamedisplay = ROLENAME_ALIAS) {
04101     global $DB;
04102 
04103     // Make sure we have a course context.
04104     $coursecontext = $context->get_course_context(false);
04105 
04106     // Make sure we are working with an array roleid => name. Normally we
04107     // want to use the unlocalised name if the localised one is not present.
04108     $newnames = array();
04109     foreach ($roleoptions as $rid => $roleorname) {
04110         if ($rolenamedisplay != ROLENAME_ALIAS_RAW) {
04111             if (is_object($roleorname)) {
04112                 $newnames[$rid] = $roleorname->name;
04113             } else {
04114                 $newnames[$rid] = $roleorname;
04115             }
04116         } else {
04117             $newnames[$rid] = '';
04118         }
04119     }
04120 
04121     // If necessary, get the localised names.
04122     if ($rolenamedisplay != ROLENAME_ORIGINAL && !empty($coursecontext->id)) {
04123         // The get the relevant renames, and use them.
04124         $aliasnames = $DB->get_records('role_names', array('contextid'=>$coursecontext->id));
04125         foreach ($aliasnames as $alias) {
04126             if (isset($newnames[$alias->roleid])) {
04127                 if ($rolenamedisplay == ROLENAME_ALIAS || $rolenamedisplay == ROLENAME_ALIAS_RAW) {
04128                     $newnames[$alias->roleid] = $alias->name;
04129                 } else if ($rolenamedisplay == ROLENAME_BOTH) {
04130                     $newnames[$alias->roleid] = $alias->name . ' (' . $roleoptions[$alias->roleid] . ')';
04131                 }
04132             }
04133         }
04134     }
04135 
04136     // Finally, apply format_string and put the result in the right place.
04137     foreach ($roleoptions as $rid => $roleorname) {
04138         if ($rolenamedisplay != ROLENAME_ALIAS_RAW) {
04139             $newnames[$rid] = strip_tags(format_string($newnames[$rid]));
04140         }
04141         if (is_object($roleorname)) {
04142             $roleoptions[$rid]->localname = $newnames[$rid];
04143         } else {
04144             $roleoptions[$rid] = $newnames[$rid];
04145         }
04146     }
04147     return $roleoptions;
04148 }
04149 
04163 function component_level_changed($cap, $comp, $contextlevel) {
04164 
04165     if (strstr($cap->component, '/') && strstr($comp, '/')) {
04166         $compsa = explode('/', $cap->component);
04167         $compsb = explode('/', $comp);
04168 
04169         // list of system reports
04170         if (($compsa[0] == 'report') && ($compsb[0] == 'report')) {
04171             return false;
04172         }
04173 
04174         // we are in gradebook, still
04175         if (($compsa[0] == 'gradeexport' || $compsa[0] == 'gradeimport' || $compsa[0] == 'gradereport') &&
04176             ($compsb[0] == 'gradeexport' || $compsb[0] == 'gradeimport' || $compsb[0] == 'gradereport')) {
04177             return false;
04178         }
04179 
04180         if (($compsa[0] == 'coursereport') && ($compsb[0] == 'coursereport')) {
04181             return false;
04182         }
04183     }
04184 
04185     return ($cap->component != $comp || $cap->contextlevel != $contextlevel);
04186 }
04187 
04195 function fix_role_sortorder($allroles) {
04196     global $DB;
04197 
04198     $rolesort = array();
04199     $i = 0;
04200     foreach ($allroles as $role) {
04201         $rolesort[$i] = $role->id;
04202         if ($role->sortorder != $i) {
04203             $r = new stdClass();
04204             $r->id = $role->id;
04205             $r->sortorder = $i;
04206             $DB->update_record('role', $r);
04207             $allroles[$role->id]->sortorder = $i;
04208         }
04209         $i++;
04210     }
04211     return $rolesort;
04212 }
04213 
04221 function switch_roles($first, $second) {
04222     global $DB;
04223     $temp = $DB->get_field('role', 'MAX(sortorder) + 1', array());
04224     $result = $DB->set_field('role', 'sortorder', $temp, array('sortorder' => $first->sortorder));
04225     $result = $result && $DB->set_field('role', 'sortorder', $first->sortorder, array('sortorder' => $second->sortorder));
04226     $result = $result && $DB->set_field('role', 'sortorder', $second->sortorder, array('sortorder' => $temp));
04227     return $result;
04228 }
04229 
04236 function role_cap_duplicate($sourcerole, $targetrole) {
04237     global $DB;
04238 
04239     $systemcontext = context_system::instance();
04240     $caps = $DB->get_records_sql("SELECT *
04241                                     FROM {role_capabilities}
04242                                    WHERE roleid = ? AND contextid = ?",
04243                                  array($sourcerole->id, $systemcontext->id));
04244     // adding capabilities
04245     foreach ($caps as $cap) {
04246         unset($cap->id);
04247         $cap->roleid = $targetrole;
04248         $DB->insert_record('role_capabilities', $cap);
04249     }
04250 }
04251 
04262 function get_roles_with_cap_in_context($context, $capability) {
04263     global $DB;
04264 
04265     $ctxids = trim($context->path, '/'); // kill leading slash
04266     $ctxids = str_replace('/', ',', $ctxids);
04267 
04268     $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.depth
04269               FROM {role_capabilities} rc
04270               JOIN {context} ctx ON ctx.id = rc.contextid
04271              WHERE rc.capability = :cap AND ctx.id IN ($ctxids)
04272           ORDER BY rc.roleid ASC, ctx.depth DESC";
04273     $params = array('cap'=>$capability);
04274 
04275     if (!$capdefs = $DB->get_records_sql($sql, $params)) {
04276         // no cap definitions --> no capability
04277         return array(array(), array());
04278     }
04279 
04280     $forbidden = array();
04281     $needed    = array();
04282     foreach($capdefs as $def) {
04283         if (isset($forbidden[$def->roleid])) {
04284             continue;
04285         }
04286         if ($def->permission == CAP_PROHIBIT) {
04287             $forbidden[$def->roleid] = $def->roleid;
04288             unset($needed[$def->roleid]);
04289             continue;
04290         }
04291         if (!isset($needed[$def->roleid])) {
04292             if ($def->permission == CAP_ALLOW) {
04293                 $needed[$def->roleid] = true;
04294             } else if ($def->permission == CAP_PREVENT) {
04295                 $needed[$def->roleid] = false;
04296             }
04297         }
04298     }
04299     unset($capdefs);
04300 
04301     // remove all those roles not allowing
04302     foreach($needed as $key=>$value) {
04303         if (!$value) {
04304             unset($needed[$key]);
04305         } else {
04306             $needed[$key] = $key;
04307         }
04308     }
04309 
04310     return array($needed, $forbidden);
04311 }
04312 
04321 function get_roles_with_caps_in_context($context, $capabilities) {
04322     $neededarr = array();
04323     $forbiddenarr = array();
04324     foreach($capabilities as $caprequired) {
04325         list($neededarr[], $forbiddenarr[]) = get_roles_with_cap_in_context($context, $caprequired);
04326     }
04327 
04328     $rolesthatcanrate = array();
04329     if (!empty($neededarr)) {
04330         foreach ($neededarr as $needed) {
04331             if (empty($rolesthatcanrate)) {
04332                 $rolesthatcanrate = $needed;
04333             } else {
04334                 //only want roles that have all caps
04335                 $rolesthatcanrate = array_intersect_key($rolesthatcanrate,$needed);
04336             }
04337         }
04338     }
04339     if (!empty($forbiddenarr) && !empty($rolesthatcanrate)) {
04340         foreach ($forbiddenarr as $forbidden) {
04341            //remove any roles that are forbidden any of the caps
04342            $rolesthatcanrate = array_diff($rolesthatcanrate, $forbidden);
04343         }
04344     }
04345     return $rolesthatcanrate;
04346 }
04347 
04356 function get_role_names_with_caps_in_context($context, $capabilities) {
04357     global $DB;
04358 
04359     $rolesthatcanrate = get_roles_with_caps_in_context($context, $capabilities);
04360 
04361     $allroles = array();
04362     $roles = $DB->get_records('role', null, 'sortorder DESC');
04363     foreach ($roles as $roleid=>$role) {
04364         $allroles[$roleid] = $role->name;
04365     }
04366 
04367     $rolenames = array();
04368     foreach ($rolesthatcanrate as $r) {
04369         $rolenames[$r] = $allroles[$r];
04370     }
04371     $rolenames = role_fix_names($rolenames, $context);
04372     return $rolenames;
04373 }
04374 
04384 function prohibit_is_removable($roleid, context $context, $capability) {
04385     global $DB;
04386 
04387     $ctxids = trim($context->path, '/'); // kill leading slash
04388     $ctxids = str_replace('/', ',', $ctxids);
04389 
04390     $params = array('roleid'=>$roleid, 'cap'=>$capability, 'prohibit'=>CAP_PROHIBIT);
04391 
04392     $sql = "SELECT ctx.id
04393               FROM {role_capabilities} rc
04394               JOIN {context} ctx ON ctx.id = rc.contextid
04395              WHERE rc.roleid = :roleid AND rc.permission = :prohibit AND rc.capability = :cap AND ctx.id IN ($ctxids)
04396           ORDER BY ctx.depth DESC";
04397 
04398     if (!$prohibits = $DB->get_records_sql($sql, $params)) {
04399         // no prohibits == nothing to remove
04400         return true;
04401     }
04402 
04403     if (count($prohibits) > 1) {
04404         // more prohibints can not be removed
04405         return false;
04406     }
04407 
04408     return !empty($prohibits[$context->id]);
04409 }
04410 
04420 function role_change_permission($roleid, $context, $capname, $permission) {
04421     global $DB;
04422 
04423     if ($permission == CAP_INHERIT) {
04424         unassign_capability($capname, $roleid, $context->id);
04425         $context->mark_dirty();
04426         return;
04427     }
04428 
04429     $ctxids = trim($context->path, '/'); // kill leading slash
04430     $ctxids = str_replace('/', ',', $ctxids);
04431 
04432     $params = array('roleid'=>$roleid, 'cap'=>$capname);
04433 
04434     $sql = "SELECT ctx.id, rc.permission, ctx.depth
04435               FROM {role_capabilities} rc
04436               JOIN {context} ctx ON ctx.id = rc.contextid
04437              WHERE rc.roleid = :roleid AND rc.capability = :cap AND ctx.id IN ($ctxids)
04438           ORDER BY ctx.depth DESC";
04439 
04440     if ($existing = $DB->get_records_sql($sql, $params)) {
04441         foreach($existing as $e) {
04442             if ($e->permission == CAP_PROHIBIT) {
04443                 // prohibit can not be overridden, no point in changing anything
04444                 return;
04445             }
04446         }
04447         $lowest = array_shift($existing);
04448         if ($lowest->permission == $permission) {
04449             // permission already set in this context or parent - nothing to do
04450             return;
04451         }
04452         if ($existing) {
04453             $parent = array_shift($existing);
04454             if ($parent->permission == $permission) {
04455                 // permission already set in parent context or parent - just unset in this context
04456                 // we do this because we want as few overrides as possible for performance reasons
04457                 unassign_capability($capname, $roleid, $context->id);
04458                 $context->mark_dirty();
04459                 return;
04460             }
04461         }
04462 
04463     } else {
04464         if ($permission == CAP_PREVENT) {
04465             // nothing means role does not have permission
04466             return;
04467         }
04468     }
04469 
04470     // assign the needed capability
04471     assign_capability($capname, $permission, $roleid, $context->id, true);
04472 
04473     // force cap reloading
04474     $context->mark_dirty();
04475 }
04476 
04477 
04490 abstract class context extends stdClass {
04491 
04492     /*
04493      * Google confirms that no other important framework is using "context" class,
04494      * we could use something else like mcontext or moodle_context, but we need to type
04495      * this very often which would be annoying and it would take too much space...
04496      *
04497      * This class is derived from stdClass for backwards compatibility with
04498      * odl $context record that was returned from DML $DB->get_record()
04499      */
04500 
04501     protected $_id;
04502     protected $_contextlevel;
04503     protected $_instanceid;
04504     protected $_path;
04505     protected $_depth;
04506 
04507     /* context caching info */
04508 
04509     private static $cache_contextsbyid = array();
04510     private static $cache_contexts     = array();
04511     protected static $cache_count      = 0; // why do we do count contexts? Because count($array) is horribly slow for large arrays
04512 
04513     protected static $cache_preloaded  = array();
04514     protected static $systemcontext    = null;
04515 
04520     protected static function reset_caches() {
04521         self::$cache_contextsbyid = array();
04522         self::$cache_contexts     = array();
04523         self::$cache_count        = 0;
04524         self::$cache_preloaded    = array();
04525 
04526         self::$systemcontext = null;
04527     }
04528 
04537     protected static function cache_add(context $context) {
04538         if (isset(self::$cache_contextsbyid[$context->id])) {
04539             // already cached, no need to do anything - this is relatively cheap, we do all this because count() is slow
04540             return;
04541         }
04542 
04543         if (self::$cache_count >= CONTEXT_CACHE_MAX_SIZE) {
04544             $i = 0;
04545             foreach(self::$cache_contextsbyid as $ctx) {
04546                 $i++;
04547                 if ($i <= 100) {
04548                     // we want to keep the first contexts to be loaded on this page, hopefully they will be needed again later
04549                     continue;
04550                 }
04551                 if ($i > (CONTEXT_CACHE_MAX_SIZE / 3)) {
04552                     // we remove oldest third of the contexts to make room for more contexts
04553                     break;
04554                 }
04555                 unset(self::$cache_contextsbyid[$ctx->id]);
04556                 unset(self::$cache_contexts[$ctx->contextlevel][$ctx->instanceid]);
04557                 self::$cache_count--;
04558             }
04559         }
04560 
04561         self::$cache_contexts[$context->contextlevel][$context->instanceid] = $context;
04562         self::$cache_contextsbyid[$context->id] = $context;
04563         self::$cache_count++;
04564     }
04565 
04573     protected static function cache_remove(context $context) {
04574         if (!isset(self::$cache_contextsbyid[$context->id])) {
04575             // not cached, no need to do anything - this is relatively cheap, we do all this because count() is slow
04576             return;
04577         }
04578         unset(self::$cache_contexts[$context->contextlevel][$context->instanceid]);
04579         unset(self::$cache_contextsbyid[$context->id]);
04580 
04581         self::$cache_count--;
04582 
04583         if (self::$cache_count < 0) {
04584             self::$cache_count = 0;
04585         }
04586     }
04587 
04596     protected static function cache_get($contextlevel, $instance) {
04597         if (isset(self::$cache_contexts[$contextlevel][$instance])) {
04598             return self::$cache_contexts[$contextlevel][$instance];
04599         }
04600         return false;
04601     }
04602 
04610     protected static function cache_get_by_id($id) {
04611         if (isset(self::$cache_contextsbyid[$id])) {
04612             return self::$cache_contextsbyid[$id];
04613         }
04614         return false;
04615     }
04616 
04624      protected static function preload_from_record(stdClass $rec) {
04625          if (empty($rec->ctxid) or empty($rec->ctxlevel) or empty($rec->ctxinstance) or empty($rec->ctxpath) or empty($rec->ctxdepth)) {
04626              // $rec does not have enough data, passed here repeatedly or context does not exist yet
04627              return;
04628          }
04629 
04630          // note: in PHP5 the objects are passed by reference, no need to return $rec
04631          $record = new stdClass();
04632          $record->id           = $rec->ctxid;       unset($rec->ctxid);
04633          $record->contextlevel = $rec->ctxlevel;    unset($rec->ctxlevel);
04634          $record->instanceid   = $rec->ctxinstance; unset($rec->ctxinstance);
04635          $record->path         = $rec->ctxpath;     unset($rec->ctxpath);
04636          $record->depth        = $rec->ctxdepth;    unset($rec->ctxdepth);
04637 
04638          return context::create_instance_from_record($record);
04639      }
04640 
04641 
04642     // ====== magic methods =======
04643 
04649     public function __set($name, $value) {
04650         debugging('Can not change context instance properties!');
04651     }
04652 
04658     public function __get($name) {
04659         switch ($name) {
04660             case 'id':           return $this->_id;
04661             case 'contextlevel': return $this->_contextlevel;
04662             case 'instanceid':   return $this->_instanceid;
04663             case 'path':         return $this->_path;
04664             case 'depth':        return $this->_depth;
04665 
04666             default:
04667                 debugging('Invalid context property accessed! '.$name);
04668                 return null;
04669         }
04670     }
04671 
04677     public function __isset($name) {
04678         switch ($name) {
04679             case 'id':           return isset($this->_id);
04680             case 'contextlevel': return isset($this->_contextlevel);
04681             case 'instanceid':   return isset($this->_instanceid);
04682             case 'path':         return isset($this->_path);
04683             case 'depth':        return isset($this->_depth);
04684 
04685             default: return false;
04686         }
04687 
04688     }
04689 
04694     public function __unset($name) {
04695         debugging('Can not unset context instance properties!');
04696     }
04697 
04698     // ====== general context methods ======
04699 
04706     protected function __construct(stdClass $record) {
04707         $this->_id           = $record->id;
04708         $this->_contextlevel = (int)$record->contextlevel;
04709         $this->_instanceid   = $record->instanceid;
04710         $this->_path         = $record->path;
04711         $this->_depth        = $record->depth;
04712     }
04713 
04720     protected static function create_instance_from_record(stdClass $record) {
04721         $classname = context_helper::get_class_for_level($record->contextlevel);
04722 
04723         if ($context = context::cache_get_by_id($record->id)) {
04724             return $context;
04725         }
04726 
04727         $context = new $classname($record);
04728         context::cache_add($context);
04729 
04730         return $context;
04731     }
04732 
04738     protected static function merge_context_temp_table() {
04739         global $DB;
04740 
04741         /* MDL-11347:
04742          *  - mysql does not allow to use FROM in UPDATE statements
04743          *  - using two tables after UPDATE works in mysql, but might give unexpected
04744          *    results in pg 8 (depends on configuration)
04745          *  - using table alias in UPDATE does not work in pg < 8.2
04746          *
04747          * Different code for each database - mostly for performance reasons
04748          */
04749 
04750         $dbfamily = $DB->get_dbfamily();
04751         if ($dbfamily == 'mysql') {
04752             $updatesql = "UPDATE {context} ct, {context_temp} temp
04753                              SET ct.path     = temp.path,
04754                                  ct.depth    = temp.depth
04755                            WHERE ct.id = temp.id";
04756         } else if ($dbfamily == 'oracle') {
04757             $updatesql = "UPDATE {context} ct
04758                              SET (ct.path, ct.depth) =
04759                                  (SELECT temp.path, temp.depth
04760                                     FROM {context_temp} temp
04761                                    WHERE temp.id=ct.id)
04762                            WHERE EXISTS (SELECT 'x'
04763                                            FROM {context_temp} temp
04764                                            WHERE temp.id = ct.id)";
04765         } else if ($dbfamily == 'postgres' or $dbfamily == 'mssql') {
04766             $updatesql = "UPDATE {context}
04767                              SET path     = temp.path,
04768                                  depth    = temp.depth
04769                             FROM {context_temp} temp
04770                            WHERE temp.id={context}.id";
04771         } else {
04772             // sqlite and others
04773             $updatesql = "UPDATE {context}
04774                              SET path     = (SELECT path FROM {context_temp} WHERE id = {context}.id),
04775                                  depth    = (SELECT depth FROM {context_temp} WHERE id = {context}.id)
04776                              WHERE id IN (SELECT id FROM {context_temp})";
04777         }
04778 
04779         $DB->execute($updatesql);
04780     }
04781 
04791     public static function instance_by_id($id, $strictness = MUST_EXIST) {
04792         global $DB;
04793 
04794         if (get_called_class() !== 'context' and get_called_class() !== 'context_helper') {
04795             // some devs might confuse context->id and instanceid, better prevent these mistakes completely
04796             throw new coding_exception('use only context::instance_by_id() for real context levels use ::instance() methods');
04797         }
04798 
04799         if ($id == SYSCONTEXTID) {
04800             return context_system::instance(0, $strictness);
04801         }
04802 
04803         if (is_array($id) or is_object($id) or empty($id)) {
04804             throw new coding_exception('Invalid context id specified context::instance_by_id()');
04805         }
04806 
04807         if ($context = context::cache_get_by_id($id)) {
04808             return $context;
04809         }
04810 
04811         if ($record = $DB->get_record('context', array('id'=>$id), '*', $strictness)) {
04812             return context::create_instance_from_record($record);
04813         }
04814 
04815         return false;
04816     }
04817 
04824     public function update_moved(context $newparent) {
04825         global $DB;
04826 
04827         $frompath = $this->_path;
04828         $newpath  = $newparent->path . '/' . $this->_id;
04829 
04830         $trans = $DB->start_delegated_transaction();
04831 
04832         $this->mark_dirty();
04833 
04834         $setdepth = '';
04835         if (($newparent->depth +1) != $this->_depth) {
04836             $diff = $newparent->depth - $this->_depth + 1;
04837             $setdepth = ", depth = depth + $diff";
04838         }
04839         $sql = "UPDATE {context}
04840                    SET path = ?
04841                        $setdepth
04842                  WHERE id = ?";
04843         $params = array($newpath, $this->_id);
04844         $DB->execute($sql, $params);
04845 
04846         $this->_path  = $newpath;
04847         $this->_depth = $newparent->depth + 1;
04848 
04849         $sql = "UPDATE {context}
04850                    SET path = ".$DB->sql_concat("?", $DB->sql_substr("path", strlen($frompath)+1))."
04851                        $setdepth
04852                  WHERE path LIKE ?";
04853         $params = array($newpath, "{$frompath}/%");
04854         $DB->execute($sql, $params);
04855 
04856         $this->mark_dirty();
04857 
04858         context::reset_caches();
04859 
04860         $trans->allow_commit();
04861     }
04862 
04869     public function reset_paths($rebuild = true) {
04870         global $DB;
04871 
04872         if ($this->_path) {
04873             $this->mark_dirty();
04874         }
04875         $DB->set_field_select('context', 'depth', 0, "path LIKE '%/$this->_id/%'");
04876         $DB->set_field_select('context', 'path', NULL, "path LIKE '%/$this->_id/%'");
04877         if ($this->_contextlevel != CONTEXT_SYSTEM) {
04878             $DB->set_field('context', 'depth', 0, array('id'=>$this->_id));
04879             $DB->set_field('context', 'path', NULL, array('id'=>$this->_id));
04880             $this->_depth = 0;
04881             $this->_path = null;
04882         }
04883 
04884         if ($rebuild) {
04885             context_helper::build_all_paths(false);
04886         }
04887 
04888         context::reset_caches();
04889     }
04890 
04894     public function delete_content() {
04895         global $CFG, $DB;
04896 
04897         blocks_delete_all_for_context($this->_id);
04898         filter_delete_all_for_context($this->_id);
04899 
04900         require_once($CFG->dirroot . '/comment/lib.php');
04901         comment::delete_comments(array('contextid'=>$this->_id));
04902 
04903         require_once($CFG->dirroot.'/rating/lib.php');
04904         $delopt = new stdclass();
04905         $delopt->contextid = $this->_id;
04906         $rm = new rating_manager();
04907         $rm->delete_ratings($delopt);
04908 
04909         // delete all files attached to this context
04910         $fs = get_file_storage();
04911         $fs->delete_area_files($this->_id);
04912 
04913         // delete all advanced grading data attached to this context
04914         require_once($CFG->dirroot.'/grade/grading/lib.php');
04915         grading_manager::delete_all_for_context($this->_id);
04916 
04917         // now delete stuff from role related tables, role_unassign_all
04918         // and unenrol should be called earlier to do proper cleanup
04919         $DB->delete_records('role_assignments', array('contextid'=>$this->_id));
04920         $DB->delete_records('role_capabilities', array('contextid'=>$this->_id));
04921         $DB->delete_records('role_names', array('contextid'=>$this->_id));
04922     }
04923 
04927     public function delete() {
04928         global $DB;
04929 
04930         // double check the context still exists
04931         if (!$DB->record_exists('context', array('id'=>$this->_id))) {
04932             context::cache_remove($this);
04933             return;
04934         }
04935 
04936         $this->delete_content();
04937         $DB->delete_records('context', array('id'=>$this->_id));
04938         // purge static context cache if entry present
04939         context::cache_remove($this);
04940 
04941         // do not mark dirty contexts if parents unknown
04942         if (!is_null($this->_path) and $this->_depth > 0) {
04943             $this->mark_dirty();
04944         }
04945     }
04946 
04947     // ====== context level related methods ======
04948 
04958     protected static function insert_context_record($contextlevel, $instanceid, $parentpath) {
04959         global $DB;
04960 
04961         $record = new stdClass();
04962         $record->contextlevel = $contextlevel;
04963         $record->instanceid   = $instanceid;
04964         $record->depth        = 0;
04965         $record->path         = null; //not known before insert
04966 
04967         $record->id = $DB->insert_record('context', $record);
04968 
04969         // now add path if known - it can be added later
04970         if (!is_null($parentpath)) {
04971             $record->path = $parentpath.'/'.$record->id;
04972             $record->depth = substr_count($record->path, '/');
04973             $DB->update_record('context', $record);
04974         }
04975 
04976         return $record;
04977     }
04978 
04988     public function get_context_name($withprefix = true, $short = false) {
04989         // must be implemented in all context levels
04990         throw new coding_exception('can not get name of abstract context');
04991     }
04992 
04998     public abstract function get_url();
04999 
05005     public abstract function get_capabilities();
05006 
05022     public function get_child_contexts() {
05023         global $DB;
05024 
05025         $sql = "SELECT ctx.*
05026                   FROM {context} ctx
05027                  WHERE ctx.path LIKE ?";
05028         $params = array($this->_path.'/%');
05029         $records = $DB->get_records_sql($sql, $params);
05030 
05031         $result = array();
05032         foreach ($records as $record) {
05033             $result[$record->id] = context::create_instance_from_record($record);
05034         }
05035 
05036         return $result;
05037     }
05038 
05046     public function get_parent_contexts($includeself = false) {
05047         if (!$contextids = $this->get_parent_context_ids($includeself)) {
05048             return array();
05049         }
05050 
05051         $result = array();
05052         foreach ($contextids as $contextid) {
05053             $parent = context::instance_by_id($contextid, MUST_EXIST);
05054             $result[$parent->id] = $parent;
05055         }
05056 
05057         return $result;
05058     }
05059 
05067     public function get_parent_context_ids($includeself = false) {
05068         if (empty($this->_path)) {
05069             return array();
05070         }
05071 
05072         $parentcontexts = trim($this->_path, '/'); // kill leading slash
05073         $parentcontexts = explode('/', $parentcontexts);
05074         if (!$includeself) {
05075             array_pop($parentcontexts); // and remove its own id
05076         }
05077 
05078         return array_reverse($parentcontexts);
05079     }
05080 
05086     public function get_parent_context() {
05087         if (empty($this->_path) or $this->_id == SYSCONTEXTID) {
05088             return false;
05089         }
05090 
05091         $parentcontexts = trim($this->_path, '/'); // kill leading slash
05092         $parentcontexts = explode('/', $parentcontexts);
05093         array_pop($parentcontexts); // self
05094         $contextid = array_pop($parentcontexts); // immediate parent
05095 
05096         return context::instance_by_id($contextid, MUST_EXIST);
05097     }
05098 
05105     public function get_course_context($strict = true) {
05106         if ($strict) {
05107             throw new coding_exception('Context does not belong to any course.');
05108         } else {
05109             return false;
05110         }
05111     }
05112 
05119     protected static function get_cleanup_sql() {
05120         throw new coding_exception('get_cleanup_sql() method must be implemented in all context levels');
05121     }
05122 
05130     protected static function build_paths($force) {
05131         throw new coding_exception('build_paths() method must be implemented in all context levels');
05132     }
05133 
05140     protected static function create_level_instances() {
05141         throw new coding_exception('create_level_instances() method must be implemented in all context levels');
05142     }
05143 
05148     public function reload_if_dirty() {
05149         global $ACCESSLIB_PRIVATE, $USER;
05150 
05151         // Load dirty contexts list if needed
05152         if (CLI_SCRIPT) {
05153             if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
05154                 // we do not load dirty flags in CLI and cron
05155                 $ACCESSLIB_PRIVATE->dirtycontexts = array();
05156             }
05157         } else {
05158             if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
05159                 if (!isset($USER->access['time'])) {
05160                     // nothing was loaded yet, we do not need to check dirty contexts now
05161                     return;
05162                 }
05163                 // no idea why -2 is there, server cluster time difference maybe... (skodak)
05164                 $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2);
05165             }
05166         }
05167 
05168         foreach ($ACCESSLIB_PRIVATE->dirtycontexts as $path=>$unused) {
05169             if ($path === $this->_path or strpos($this->_path, $path.'/') === 0) {
05170                 // reload all capabilities of USER and others - preserving loginas, roleswitches, etc
05171                 // and then cleanup any marks of dirtyness... at least from our short term memory! :-)
05172                 reload_all_capabilities();
05173                 break;
05174             }
05175         }
05176     }
05177 
05181     public function mark_dirty() {
05182         global $CFG, $USER, $ACCESSLIB_PRIVATE;
05183 
05184         if (during_initial_install()) {
05185             return;
05186         }
05187 
05188         // only if it is a non-empty string
05189         if (is_string($this->_path) && $this->_path !== '') {
05190             set_cache_flag('accesslib/dirtycontexts', $this->_path, 1, time()+$CFG->sessiontimeout);
05191             if (isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
05192                 $ACCESSLIB_PRIVATE->dirtycontexts[$this->_path] = 1;
05193             } else {
05194                 if (CLI_SCRIPT) {
05195                     $ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1);
05196                 } else {
05197                     if (isset($USER->access['time'])) {
05198                         $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2);
05199                     } else {
05200                         $ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1);
05201                     }
05202                     // flags not loaded yet, it will be done later in $context->reload_if_dirty()
05203                 }
05204             }
05205         }
05206     }
05207 }
05208 
05209 
05222 class context_helper extends context {
05223 
05224     private static $alllevels = array(
05225             CONTEXT_SYSTEM    => 'context_system',
05226             CONTEXT_USER      => 'context_user',
05227             CONTEXT_COURSECAT => 'context_coursecat',
05228             CONTEXT_COURSE    => 'context_course',
05229             CONTEXT_MODULE    => 'context_module',
05230             CONTEXT_BLOCK     => 'context_block',
05231     );
05232 
05236     protected function __construct() {
05237     }
05238 
05246     public static function get_class_for_level($contextlevel) {
05247         if (isset(self::$alllevels[$contextlevel])) {
05248             return self::$alllevels[$contextlevel];
05249         } else {
05250             throw new coding_exception('Invalid context level specified');
05251         }
05252     }
05253 
05260     public static function get_all_levels() {
05261         return self::$alllevels;
05262     }
05263 
05271     public static function cleanup_instances() {
05272         global $DB;
05273         $sqls = array();
05274         foreach (self::$alllevels as $level=>$classname) {
05275             $sqls[] = $classname::get_cleanup_sql();
05276         }
05277 
05278         $sql = implode(" UNION ", $sqls);
05279 
05280         // it is probably better to use transactions, it might be faster too
05281         $transaction = $DB->start_delegated_transaction();
05282 
05283         $rs = $DB->get_recordset_sql($sql);
05284         foreach ($rs as $record) {
05285             $context = context::create_instance_from_record($record);
05286             $context->delete();
05287         }
05288         $rs->close();
05289 
05290         $transaction->allow_commit();
05291     }
05292 
05301     public static function create_instances($contextlevel = null, $buildpaths = true) {
05302         foreach (self::$alllevels as $level=>$classname) {
05303             if ($contextlevel and $level > $contextlevel) {
05304                 // skip potential sub-contexts
05305                 continue;
05306             }
05307             $classname::create_level_instances();
05308             if ($buildpaths) {
05309                 $classname::build_paths(false);
05310             }
05311         }
05312     }
05313 
05321     public static function build_all_paths($force = false) {
05322         foreach (self::$alllevels as $classname) {
05323             $classname::build_paths($force);
05324         }
05325 
05326         // reset static course cache - it might have incorrect cached data
05327         accesslib_clear_all_caches(true);
05328     }
05329 
05334     public static function reset_caches() {
05335         context::reset_caches();
05336     }
05337 
05347     public static function get_preload_record_columns($tablealias) {
05348         return array("$tablealias.id"=>"ctxid", "$tablealias.path"=>"ctxpath", "$tablealias.depth"=>"ctxdepth", "$tablealias.contextlevel"=>"ctxlevel", "$tablealias.instanceid"=>"ctxinstance");
05349     }
05350 
05360     public static function get_preload_record_columns_sql($tablealias) {
05361         return "$tablealias.id AS ctxid, $tablealias.path AS ctxpath, $tablealias.depth AS ctxdepth, $tablealias.contextlevel AS ctxlevel, $tablealias.instanceid AS ctxinstance";
05362     }
05363 
05373      public static function preload_from_record(stdClass $rec) {
05374          context::preload_from_record($rec);
05375      }
05376 
05385     public static function preload_course($courseid) {
05386         // Users can call this multiple times without doing any harm
05387         if (isset(context::$cache_preloaded[$courseid])) {
05388             return;
05389         }
05390         $coursecontext = context_course::instance($courseid);
05391         $coursecontext->get_child_contexts();
05392 
05393         context::$cache_preloaded[$courseid] = true;
05394     }
05395 
05404     public static function delete_instance($contextlevel, $instanceid) {
05405         global $DB;
05406 
05407         // double check the context still exists
05408         if ($record = $DB->get_record('context', array('contextlevel'=>$contextlevel, 'instanceid'=>$instanceid))) {
05409             $context = context::create_instance_from_record($record);
05410             $context->delete();
05411         } else {
05412             // we should try to purge the cache anyway
05413         }
05414     }
05415 
05423     public static function get_level_name($contextlevel) {
05424         $classname = context_helper::get_class_for_level($contextlevel);
05425         return $classname::get_level_name();
05426     }
05427 
05431     public function get_url() {
05432     }
05433 
05437     public function get_capabilities() {
05438     }
05439 }
05440 
05441 
05447 class context_system extends context {
05453     protected function __construct(stdClass $record) {
05454         parent::__construct($record);
05455         if ($record->contextlevel != CONTEXT_SYSTEM) {
05456             throw new coding_exception('Invalid $record->contextlevel in context_system constructor.');
05457         }
05458     }
05459 
05466     public static function get_level_name() {
05467         return get_string('coresystem');
05468     }
05469 
05477     public function get_context_name($withprefix = true, $short = false) {
05478         return self::get_level_name();
05479     }
05480 
05486     public function get_url() {
05487         return new moodle_url('/');
05488     }
05489 
05495     public function get_capabilities() {
05496         global $DB;
05497 
05498         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
05499 
05500         $params = array();
05501         $sql = "SELECT *
05502                   FROM {capabilities}";
05503 
05504         return $DB->get_records_sql($sql.' '.$sort, $params);
05505     }
05506 
05511     protected static function create_level_instances() {
05512         // nothing to do here, the system context is created automatically in installer
05513         self::instance(0);
05514     }
05515 
05525     public static function instance($instanceid = 0, $strictness = MUST_EXIST, $cache = true) {
05526         global $DB;
05527 
05528         if ($instanceid != 0) {
05529             debugging('context_system::instance(): invalid $id parameter detected, should be 0');
05530         }
05531 
05532         if (defined('SYSCONTEXTID') and $cache) { // dangerous: define this in config.php to eliminate 1 query/page
05533             if (!isset(context::$systemcontext)) {
05534                 $record = new stdClass();
05535                 $record->id           = SYSCONTEXTID;
05536                 $record->contextlevel = CONTEXT_SYSTEM;
05537                 $record->instanceid   = 0;
05538                 $record->path         = '/'.SYSCONTEXTID;
05539                 $record->depth        = 1;
05540                 context::$systemcontext = new context_system($record);
05541             }
05542             return context::$systemcontext;
05543         }
05544 
05545 
05546         try {
05547             // we ignore the strictness completely because system context must except except during install
05548             $record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST);
05549         } catch (dml_exception $e) {
05550             //table or record does not exist
05551             if (!during_initial_install()) {
05552                 // do not mess with system context after install, it simply must exist
05553                 throw $e;
05554             }
05555             $record = null;
05556         }
05557 
05558         if (!$record) {
05559             $record = new stdClass();
05560             $record->contextlevel = CONTEXT_SYSTEM;
05561             $record->instanceid   = 0;
05562             $record->depth        = 1;
05563             $record->path         = null; //not known before insert
05564 
05565             try {
05566                 if ($DB->count_records('context')) {
05567                     // contexts already exist, this is very weird, system must be first!!!
05568                     return null;
05569                 }
05570                 if (defined('SYSCONTEXTID')) {
05571                     // this would happen only in unittest on sites that went through weird 1.7 upgrade
05572                     $record->id = SYSCONTEXTID;
05573                     $DB->import_record('context', $record);
05574                     $DB->get_manager()->reset_sequence('context');
05575                 } else {
05576                     $record->id = $DB->insert_record('context', $record);
05577                 }
05578             } catch (dml_exception $e) {
05579                 // can not create context - table does not exist yet, sorry
05580                 return null;
05581             }
05582         }
05583 
05584         if ($record->instanceid != 0) {
05585             // this is very weird, somebody must be messing with context table
05586             debugging('Invalid system context detected');
05587         }
05588 
05589         if ($record->depth != 1 or $record->path != '/'.$record->id) {
05590             // fix path if necessary, initial install or path reset
05591             $record->depth = 1;
05592             $record->path  = '/'.$record->id;
05593             $DB->update_record('context', $record);
05594         }
05595 
05596         if (!defined('SYSCONTEXTID')) {
05597             define('SYSCONTEXTID', $record->id);
05598         }
05599 
05600         context::$systemcontext = new context_system($record);
05601         return context::$systemcontext;
05602     }
05603 
05611     public function get_child_contexts() {
05612         global $DB;
05613 
05614         debugging('Fetching of system context child courses is strongly discouraged on production servers (it may eat all available memory)!');
05615 
05616         // Just get all the contexts except for CONTEXT_SYSTEM level
05617         // and hope we don't OOM in the process - don't cache
05618         $sql = "SELECT c.*
05619                   FROM {context} c
05620                  WHERE contextlevel > ".CONTEXT_SYSTEM;
05621         $records = $DB->get_records_sql($sql);
05622 
05623         $result = array();
05624         foreach ($records as $record) {
05625             $result[$record->id] = context::create_instance_from_record($record);
05626         }
05627 
05628         return $result;
05629     }
05630 
05637     protected static function get_cleanup_sql() {
05638         $sql = "
05639                   SELECT c.*
05640                     FROM {context} c
05641                    WHERE 1=2
05642                ";
05643 
05644         return $sql;
05645     }
05646 
05653     protected static function build_paths($force) {
05654         global $DB;
05655 
05656         /* note: ignore $force here, we always do full test of system context */
05657 
05658         // exactly one record must exist
05659         $record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST);
05660 
05661         if ($record->instanceid != 0) {
05662             debugging('Invalid system context detected');
05663         }
05664 
05665         if (defined('SYSCONTEXTID') and $record->id != SYSCONTEXTID) {
05666             debugging('Invalid SYSCONTEXTID detected');
05667         }
05668 
05669         if ($record->depth != 1 or $record->path != '/'.$record->id) {
05670             // fix path if necessary, initial install or path reset
05671             $record->depth    = 1;
05672             $record->path     = '/'.$record->id;
05673             $DB->update_record('context', $record);
05674         }
05675     }
05676 }
05677 
05678 
05684 class context_user extends context {
05691     protected function __construct(stdClass $record) {
05692         parent::__construct($record);
05693         if ($record->contextlevel != CONTEXT_USER) {
05694             throw new coding_exception('Invalid $record->contextlevel in context_user constructor.');
05695         }
05696     }
05697 
05704     public static function get_level_name() {
05705         return get_string('user');
05706     }
05707 
05715     public function get_context_name($withprefix = true, $short = false) {
05716         global $DB;
05717 
05718         $name = '';
05719         if ($user = $DB->get_record('user', array('id'=>$this->_instanceid, 'deleted'=>0))) {
05720             if ($withprefix){
05721                 $name = get_string('user').': ';
05722             }
05723             $name .= fullname($user);
05724         }
05725         return $name;
05726     }
05727 
05733     public function get_url() {
05734         global $COURSE;
05735 
05736         if ($COURSE->id == SITEID) {
05737             $url = new moodle_url('/user/profile.php', array('id'=>$this->_instanceid));
05738         } else {
05739             $url = new moodle_url('/user/view.php', array('id'=>$this->_instanceid, 'courseid'=>$COURSE->id));
05740         }
05741         return $url;
05742     }
05743 
05749     public function get_capabilities() {
05750         global $DB;
05751 
05752         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
05753 
05754         $extracaps = array('moodle/grade:viewall');
05755         list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
05756         $sql = "SELECT *
05757                   FROM {capabilities}
05758                  WHERE contextlevel = ".CONTEXT_USER."
05759                        OR name $extra";
05760 
05761         return $records = $DB->get_records_sql($sql.' '.$sort, $params);
05762     }
05763 
05772     public static function instance($instanceid, $strictness = MUST_EXIST) {
05773         global $DB;
05774 
05775         if ($context = context::cache_get(CONTEXT_USER, $instanceid)) {
05776             return $context;
05777         }
05778 
05779         if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_USER, 'instanceid'=>$instanceid))) {
05780             if ($user = $DB->get_record('user', array('id'=>$instanceid, 'deleted'=>0), 'id', $strictness)) {
05781                 $record = context::insert_context_record(CONTEXT_USER, $user->id, '/'.SYSCONTEXTID, 0);
05782             }
05783         }
05784 
05785         if ($record) {
05786             $context = new context_user($record);
05787             context::cache_add($context);
05788             return $context;
05789         }
05790 
05791         return false;
05792     }
05793 
05798     protected static function create_level_instances() {
05799         global $DB;
05800 
05801         $sql = "INSERT INTO {context} (contextlevel, instanceid)
05802                 SELECT ".CONTEXT_USER.", u.id
05803                   FROM {user} u
05804                  WHERE u.deleted = 0
05805                        AND NOT EXISTS (SELECT 'x'
05806                                          FROM {context} cx
05807                                         WHERE u.id = cx.instanceid AND cx.contextlevel=".CONTEXT_USER.")";
05808         $DB->execute($sql);
05809     }
05810 
05817     protected static function get_cleanup_sql() {
05818         $sql = "
05819                   SELECT c.*
05820                     FROM {context} c
05821          LEFT OUTER JOIN {user} u ON (c.instanceid = u.id AND u.deleted = 0)
05822                    WHERE u.id IS NULL AND c.contextlevel = ".CONTEXT_USER."
05823                ";
05824 
05825         return $sql;
05826     }
05827 
05834     protected static function build_paths($force) {
05835         global $DB;
05836 
05837         // first update normal users
05838         $sql = "UPDATE {context}
05839                    SET depth = 2,
05840                        path = ".$DB->sql_concat("'/".SYSCONTEXTID."/'", 'id')."
05841                  WHERE contextlevel=".CONTEXT_USER;
05842         $DB->execute($sql);
05843     }
05844 }
05845 
05846 
05852 class context_coursecat extends context {
05859     protected function __construct(stdClass $record) {
05860         parent::__construct($record);
05861         if ($record->contextlevel != CONTEXT_COURSECAT) {
05862             throw new coding_exception('Invalid $record->contextlevel in context_coursecat constructor.');
05863         }
05864     }
05865 
05872     public static function get_level_name() {
05873         return get_string('category');
05874     }
05875 
05883     public function get_context_name($withprefix = true, $short = false) {
05884         global $DB;
05885 
05886         $name = '';
05887         if ($category = $DB->get_record('course_categories', array('id'=>$this->_instanceid))) {
05888             if ($withprefix){
05889                 $name = get_string('category').': ';
05890             }
05891             $name .= format_string($category->name, true, array('context' => $this));
05892         }
05893         return $name;
05894     }
05895 
05901     public function get_url() {
05902         return new moodle_url('/course/category.php', array('id'=>$this->_instanceid));
05903     }
05904 
05910     public function get_capabilities() {
05911         global $DB;
05912 
05913         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
05914 
05915         $params = array();
05916         $sql = "SELECT *
05917                   FROM {capabilities}
05918                  WHERE contextlevel IN (".CONTEXT_COURSECAT.",".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")";
05919 
05920         return $DB->get_records_sql($sql.' '.$sort, $params);
05921     }
05922 
05931     public static function instance($instanceid, $strictness = MUST_EXIST) {
05932         global $DB;
05933 
05934         if ($context = context::cache_get(CONTEXT_COURSECAT, $instanceid)) {
05935             return $context;
05936         }
05937 
05938         if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSECAT, 'instanceid'=>$instanceid))) {
05939             if ($category = $DB->get_record('course_categories', array('id'=>$instanceid), 'id,parent', $strictness)) {
05940                 if ($category->parent) {
05941                     $parentcontext = context_coursecat::instance($category->parent);
05942                     $record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, $parentcontext->path);
05943                 } else {
05944                     $record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, '/'.SYSCONTEXTID, 0);
05945                 }
05946             }
05947         }
05948 
05949         if ($record) {
05950             $context = new context_coursecat($record);
05951             context::cache_add($context);
05952             return $context;
05953         }
05954 
05955         return false;
05956     }
05957 
05964     public function get_child_contexts() {
05965         global $DB;
05966 
05967         $sql = "SELECT ctx.*
05968                   FROM {context} ctx
05969                  WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)";
05970         $params = array($this->_path.'/%', $this->depth+1, CONTEXT_COURSECAT);
05971         $records = $DB->get_records_sql($sql, $params);
05972 
05973         $result = array();
05974         foreach ($records as $record) {
05975             $result[$record->id] = context::create_instance_from_record($record);
05976         }
05977 
05978         return $result;
05979     }
05980 
05985     protected static function create_level_instances() {
05986         global $DB;
05987 
05988         $sql = "INSERT INTO {context} (contextlevel, instanceid)
05989                 SELECT ".CONTEXT_COURSECAT.", cc.id
05990                   FROM {course_categories} cc
05991                  WHERE NOT EXISTS (SELECT 'x'
05992                                      FROM {context} cx
05993                                     WHERE cc.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSECAT.")";
05994         $DB->execute($sql);
05995     }
05996 
06003     protected static function get_cleanup_sql() {
06004         $sql = "
06005                   SELECT c.*
06006                     FROM {context} c
06007          LEFT OUTER JOIN {course_categories} cc ON c.instanceid = cc.id
06008                    WHERE cc.id IS NULL AND c.contextlevel = ".CONTEXT_COURSECAT."
06009                ";
06010 
06011         return $sql;
06012     }
06013 
06020     protected static function build_paths($force) {
06021         global $DB;
06022 
06023         if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSECAT." AND (depth = 0 OR path IS NULL)")) {
06024             if ($force) {
06025                 $ctxemptyclause = $emptyclause = '';
06026             } else {
06027                 $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
06028                 $emptyclause    = "AND ({context}.path IS NULL OR {context}.depth = 0)";
06029             }
06030 
06031             $base = '/'.SYSCONTEXTID;
06032 
06033             // Normal top level categories
06034             $sql = "UPDATE {context}
06035                        SET depth=2,
06036                            path=".$DB->sql_concat("'$base/'", 'id')."
06037                      WHERE contextlevel=".CONTEXT_COURSECAT."
06038                            AND EXISTS (SELECT 'x'
06039                                          FROM {course_categories} cc
06040                                         WHERE cc.id = {context}.instanceid AND cc.depth=1)
06041                            $emptyclause";
06042             $DB->execute($sql);
06043 
06044             // Deeper categories - one query per depthlevel
06045             $maxdepth = $DB->get_field_sql("SELECT MAX(depth) FROM {course_categories}");
06046             for ($n=2; $n<=$maxdepth; $n++) {
06047                 $sql = "INSERT INTO {context_temp} (id, path, depth)
06048                         SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1
06049                           FROM {context} ctx
06050                           JOIN {course_categories} cc ON (cc.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_COURSECAT." AND cc.depth = $n)
06051                           JOIN {context} pctx ON (pctx.instanceid = cc.parent AND pctx.contextlevel = ".CONTEXT_COURSECAT.")
06052                          WHERE pctx.path IS NOT NULL AND pctx.depth > 0
06053                                $ctxemptyclause";
06054                 $trans = $DB->start_delegated_transaction();
06055                 $DB->delete_records('context_temp');
06056                 $DB->execute($sql);
06057                 context::merge_context_temp_table();
06058                 $DB->delete_records('context_temp');
06059                 $trans->allow_commit();
06060 
06061             }
06062         }
06063     }
06064 }
06065 
06066 
06072 class context_course extends context {
06079     protected function __construct(stdClass $record) {
06080         parent::__construct($record);
06081         if ($record->contextlevel != CONTEXT_COURSE) {
06082             throw new coding_exception('Invalid $record->contextlevel in context_course constructor.');
06083         }
06084     }
06085 
06092     public static function get_level_name() {
06093         return get_string('course');
06094     }
06095 
06103     public function get_context_name($withprefix = true, $short = false) {
06104         global $DB;
06105 
06106         $name = '';
06107         if ($this->_instanceid == SITEID) {
06108             $name = get_string('frontpage', 'admin');
06109         } else {
06110             if ($course = $DB->get_record('course', array('id'=>$this->_instanceid))) {
06111                 if ($withprefix){
06112                     $name = get_string('course').': ';
06113                 }
06114                 if ($short){
06115                     $name .= format_string($course->shortname, true, array('context' => $this));
06116                 } else {
06117                     $name .= format_string($course->fullname);
06118                }
06119             }
06120         }
06121         return $name;
06122     }
06123 
06129     public function get_url() {
06130         if ($this->_instanceid != SITEID) {
06131             return new moodle_url('/course/view.php', array('id'=>$this->_instanceid));
06132         }
06133 
06134         return new moodle_url('/');
06135     }
06136 
06142     public function get_capabilities() {
06143         global $DB;
06144 
06145         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
06146 
06147         $params = array();
06148         $sql = "SELECT *
06149                   FROM {capabilities}
06150                  WHERE contextlevel IN (".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")";
06151 
06152         return $DB->get_records_sql($sql.' '.$sort, $params);
06153     }
06154 
06161     public function get_course_context($strict = true) {
06162         return $this;
06163     }
06164 
06173     public static function instance($instanceid, $strictness = MUST_EXIST) {
06174         global $DB;
06175 
06176         if ($context = context::cache_get(CONTEXT_COURSE, $instanceid)) {
06177             return $context;
06178         }
06179 
06180         if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$instanceid))) {
06181             if ($course = $DB->get_record('course', array('id'=>$instanceid), 'id,category', $strictness)) {
06182                 if ($course->category) {
06183                     $parentcontext = context_coursecat::instance($course->category);
06184                     $record = context::insert_context_record(CONTEXT_COURSE, $course->id, $parentcontext->path);
06185                 } else {
06186                     $record = context::insert_context_record(CONTEXT_COURSE, $course->id, '/'.SYSCONTEXTID, 0);
06187                 }
06188             }
06189         }
06190 
06191         if ($record) {
06192             $context = new context_course($record);
06193             context::cache_add($context);
06194             return $context;
06195         }
06196 
06197         return false;
06198     }
06199 
06204     protected static function create_level_instances() {
06205         global $DB;
06206 
06207         $sql = "INSERT INTO {context} (contextlevel, instanceid)
06208                 SELECT ".CONTEXT_COURSE.", c.id
06209                   FROM {course} c
06210                  WHERE NOT EXISTS (SELECT 'x'
06211                                      FROM {context} cx
06212                                     WHERE c.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSE.")";
06213         $DB->execute($sql);
06214     }
06215 
06222     protected static function get_cleanup_sql() {
06223         $sql = "
06224                   SELECT c.*
06225                     FROM {context} c
06226          LEFT OUTER JOIN {course} co ON c.instanceid = co.id
06227                    WHERE co.id IS NULL AND c.contextlevel = ".CONTEXT_COURSE."
06228                ";
06229 
06230         return $sql;
06231     }
06232 
06239     protected static function build_paths($force) {
06240         global $DB;
06241 
06242         if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSE." AND (depth = 0 OR path IS NULL)")) {
06243             if ($force) {
06244                 $ctxemptyclause = $emptyclause = '';
06245             } else {
06246                 $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
06247                 $emptyclause    = "AND ({context}.path IS NULL OR {context}.depth = 0)";
06248             }
06249 
06250             $base = '/'.SYSCONTEXTID;
06251 
06252             // Standard frontpage
06253             $sql = "UPDATE {context}
06254                        SET depth = 2,
06255                            path = ".$DB->sql_concat("'$base/'", 'id')."
06256                      WHERE contextlevel = ".CONTEXT_COURSE."
06257                            AND EXISTS (SELECT 'x'
06258                                          FROM {course} c
06259                                         WHERE c.id = {context}.instanceid AND c.category = 0)
06260                            $emptyclause";
06261             $DB->execute($sql);
06262 
06263             // standard courses
06264             $sql = "INSERT INTO {context_temp} (id, path, depth)
06265                     SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1
06266                       FROM {context} ctx
06267                       JOIN {course} c ON (c.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_COURSE." AND c.category <> 0)
06268                       JOIN {context} pctx ON (pctx.instanceid = c.category AND pctx.contextlevel = ".CONTEXT_COURSECAT.")
06269                      WHERE pctx.path IS NOT NULL AND pctx.depth > 0
06270                            $ctxemptyclause";
06271             $trans = $DB->start_delegated_transaction();
06272             $DB->delete_records('context_temp');
06273             $DB->execute($sql);
06274             context::merge_context_temp_table();
06275             $DB->delete_records('context_temp');
06276             $trans->allow_commit();
06277         }
06278     }
06279 }
06280 
06281 
06287 class context_module extends context {
06294     protected function __construct(stdClass $record) {
06295         parent::__construct($record);
06296         if ($record->contextlevel != CONTEXT_MODULE) {
06297             throw new coding_exception('Invalid $record->contextlevel in context_module constructor.');
06298         }
06299     }
06300 
06307     public static function get_level_name() {
06308         return get_string('activitymodule');
06309     }
06310 
06319     public function get_context_name($withprefix = true, $short = false) {
06320         global $DB;
06321 
06322         $name = '';
06323         if ($cm = $DB->get_record_sql("SELECT cm.*, md.name AS modname
06324                                          FROM {course_modules} cm
06325                                          JOIN {modules} md ON md.id = cm.module
06326                                         WHERE cm.id = ?", array($this->_instanceid))) {
06327             if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) {
06328                     if ($withprefix){
06329                         $name = get_string('modulename', $cm->modname).': ';
06330                     }
06331                     $name .= $mod->name;
06332                 }
06333             }
06334         return $name;
06335     }
06336 
06342     public function get_url() {
06343         global $DB;
06344 
06345         if ($modname = $DB->get_field_sql("SELECT md.name AS modname
06346                                              FROM {course_modules} cm
06347                                              JOIN {modules} md ON md.id = cm.module
06348                                             WHERE cm.id = ?", array($this->_instanceid))) {
06349             return new moodle_url('/mod/' . $modname . '/view.php', array('id'=>$this->_instanceid));
06350         }
06351 
06352         return new moodle_url('/');
06353     }
06354 
06360     public function get_capabilities() {
06361         global $DB, $CFG;
06362 
06363         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
06364 
06365         $cm = $DB->get_record('course_modules', array('id'=>$this->_instanceid));
06366         $module = $DB->get_record('modules', array('id'=>$cm->module));
06367 
06368         $subcaps = array();
06369         $subpluginsfile = "$CFG->dirroot/mod/$module->name/db/subplugins.php";
06370         if (file_exists($subpluginsfile)) {
06371             $subplugins = array();  // should be redefined in the file
06372             include($subpluginsfile);
06373             if (!empty($subplugins)) {
06374                 foreach (array_keys($subplugins) as $subplugintype) {
06375                     foreach (array_keys(get_plugin_list($subplugintype)) as $subpluginname) {
06376                         $subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname)));
06377                     }
06378                 }
06379             }
06380         }
06381 
06382         $modfile = "$CFG->dirroot/mod/$module->name/lib.php";
06383         $extracaps = array();
06384         if (file_exists($modfile)) {
06385             include_once($modfile);
06386             $modfunction = $module->name.'_get_extra_capabilities';
06387             if (function_exists($modfunction)) {
06388                 $extracaps = $modfunction();
06389             }
06390         }
06391 
06392         $extracaps = array_merge($subcaps, $extracaps);
06393         $extra = '';
06394         list($extra, $params) = $DB->get_in_or_equal(
06395             $extracaps, SQL_PARAMS_NAMED, 'cap0', true, '');
06396         if (!empty($extra)) {
06397             $extra = "OR name $extra";
06398         }
06399         $sql = "SELECT *
06400                   FROM {capabilities}
06401                  WHERE (contextlevel = ".CONTEXT_MODULE."
06402                        AND (component = :component OR component = 'moodle'))
06403                        $extra";
06404         $params['component'] = "mod_$module->name";
06405 
06406         return $DB->get_records_sql($sql.' '.$sort, $params);
06407     }
06408 
06415     public function get_course_context($strict = true) {
06416         return $this->get_parent_context();
06417     }
06418 
06427     public static function instance($instanceid, $strictness = MUST_EXIST) {
06428         global $DB;
06429 
06430         if ($context = context::cache_get(CONTEXT_MODULE, $instanceid)) {
06431             return $context;
06432         }
06433 
06434         if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$instanceid))) {
06435             if ($cm = $DB->get_record('course_modules', array('id'=>$instanceid), 'id,course', $strictness)) {
06436                 $parentcontext = context_course::instance($cm->course);
06437                 $record = context::insert_context_record(CONTEXT_MODULE, $cm->id, $parentcontext->path);
06438             }
06439         }
06440 
06441         if ($record) {
06442             $context = new context_module($record);
06443             context::cache_add($context);
06444             return $context;
06445         }
06446 
06447         return false;
06448     }
06449 
06454     protected static function create_level_instances() {
06455         global $DB;
06456 
06457         $sql = "INSERT INTO {context} (contextlevel, instanceid)
06458                 SELECT ".CONTEXT_MODULE.", cm.id
06459                   FROM {course_modules} cm
06460                  WHERE NOT EXISTS (SELECT 'x'
06461                                      FROM {context} cx
06462                                     WHERE cm.id = cx.instanceid AND cx.contextlevel=".CONTEXT_MODULE.")";
06463         $DB->execute($sql);
06464     }
06465 
06472     protected static function get_cleanup_sql() {
06473         $sql = "
06474                   SELECT c.*
06475                     FROM {context} c
06476          LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id
06477                    WHERE cm.id IS NULL AND c.contextlevel = ".CONTEXT_MODULE."
06478                ";
06479 
06480         return $sql;
06481     }
06482 
06489     protected static function build_paths($force) {
06490         global $DB;
06491 
06492         if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_MODULE." AND (depth = 0 OR path IS NULL)")) {
06493             if ($force) {
06494                 $ctxemptyclause = '';
06495             } else {
06496                 $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
06497             }
06498 
06499             $sql = "INSERT INTO {context_temp} (id, path, depth)
06500                     SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1
06501                       FROM {context} ctx
06502                       JOIN {course_modules} cm ON (cm.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_MODULE.")
06503                       JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = ".CONTEXT_COURSE.")
06504                      WHERE pctx.path IS NOT NULL AND pctx.depth > 0
06505                            $ctxemptyclause";
06506             $trans = $DB->start_delegated_transaction();
06507             $DB->delete_records('context_temp');
06508             $DB->execute($sql);
06509             context::merge_context_temp_table();
06510             $DB->delete_records('context_temp');
06511             $trans->allow_commit();
06512         }
06513     }
06514 }
06515 
06516 
06522 class context_block extends context {
06529     protected function __construct(stdClass $record) {
06530         parent::__construct($record);
06531         if ($record->contextlevel != CONTEXT_BLOCK) {
06532             throw new coding_exception('Invalid $record->contextlevel in context_block constructor');
06533         }
06534     }
06535 
06542     public static function get_level_name() {
06543         return get_string('block');
06544     }
06545 
06553     public function get_context_name($withprefix = true, $short = false) {
06554         global $DB, $CFG;
06555 
06556         $name = '';
06557         if ($blockinstance = $DB->get_record('block_instances', array('id'=>$this->_instanceid))) {
06558             global $CFG;
06559             require_once("$CFG->dirroot/blocks/moodleblock.class.php");
06560             require_once("$CFG->dirroot/blocks/$blockinstance->blockname/block_$blockinstance->blockname.php");
06561             $blockname = "block_$blockinstance->blockname";
06562             if ($blockobject = new $blockname()) {
06563                 if ($withprefix){
06564                     $name = get_string('block').': ';
06565                 }
06566                 $name .= $blockobject->title;
06567             }
06568         }
06569 
06570         return $name;
06571     }
06572 
06578     public function get_url() {
06579         $parentcontexts = $this->get_parent_context();
06580         return $parentcontexts->get_url();
06581     }
06582 
06588     public function get_capabilities() {
06589         global $DB;
06590 
06591         $sort = 'ORDER BY contextlevel,component,name';   // To group them sensibly for display
06592 
06593         $params = array();
06594         $bi = $DB->get_record('block_instances', array('id' => $this->_instanceid));
06595 
06596         $extra = '';
06597         $extracaps = block_method_result($bi->blockname, 'get_extra_capabilities');
06598         if ($extracaps) {
06599             list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap');
06600             $extra = "OR name $extra";
06601         }
06602 
06603         $sql = "SELECT *
06604                   FROM {capabilities}
06605                  WHERE (contextlevel = ".CONTEXT_BLOCK."
06606                        AND component = :component)
06607                        $extra";
06608         $params['component'] = 'block_' . $bi->blockname;
06609 
06610         return $DB->get_records_sql($sql.' '.$sort, $params);
06611     }
06612 
06619     public function get_course_context($strict = true) {
06620         $parentcontext = $this->get_parent_context();
06621         return $parentcontext->get_course_context($strict);
06622     }
06623 
06632     public static function instance($instanceid, $strictness = MUST_EXIST) {
06633         global $DB;
06634 
06635         if ($context = context::cache_get(CONTEXT_BLOCK, $instanceid)) {
06636             return $context;
06637         }
06638 
06639         if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$instanceid))) {
06640             if ($bi = $DB->get_record('block_instances', array('id'=>$instanceid), 'id,parentcontextid', $strictness)) {
06641                 $parentcontext = context::instance_by_id($bi->parentcontextid);
06642                 $record = context::insert_context_record(CONTEXT_BLOCK, $bi->id, $parentcontext->path);
06643             }
06644         }
06645 
06646         if ($record) {
06647             $context = new context_block($record);
06648             context::cache_add($context);
06649             return $context;
06650         }
06651 
06652         return false;
06653     }
06654 
06659     public function get_child_contexts() {
06660         return array();
06661     }
06662 
06667     protected static function create_level_instances() {
06668         global $DB;
06669 
06670         $sql = "INSERT INTO {context} (contextlevel, instanceid)
06671                 SELECT ".CONTEXT_BLOCK.", bi.id
06672                   FROM {block_instances} bi
06673                  WHERE NOT EXISTS (SELECT 'x'
06674                                      FROM {context} cx
06675                                     WHERE bi.id = cx.instanceid AND cx.contextlevel=".CONTEXT_BLOCK.")";
06676         $DB->execute($sql);
06677     }
06678 
06685     protected static function get_cleanup_sql() {
06686         $sql = "
06687                   SELECT c.*
06688                     FROM {context} c
06689          LEFT OUTER JOIN {block_instances} bi ON c.instanceid = bi.id
06690                    WHERE bi.id IS NULL AND c.contextlevel = ".CONTEXT_BLOCK."
06691                ";
06692 
06693         return $sql;
06694     }
06695 
06702     protected static function build_paths($force) {
06703         global $DB;
06704 
06705         if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_BLOCK." AND (depth = 0 OR path IS NULL)")) {
06706             if ($force) {
06707                 $ctxemptyclause = '';
06708             } else {
06709                 $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
06710             }
06711 
06712             // pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent
06713             $sql = "INSERT INTO {context_temp} (id, path, depth)
06714                     SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1
06715                       FROM {context} ctx
06716                       JOIN {block_instances} bi ON (bi.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_BLOCK.")
06717                       JOIN {context} pctx ON (pctx.id = bi.parentcontextid)
06718                      WHERE (pctx.path IS NOT NULL AND pctx.depth > 0)
06719                            $ctxemptyclause";
06720             $trans = $DB->start_delegated_transaction();
06721             $DB->delete_records('context_temp');
06722             $DB->execute($sql);
06723             context::merge_context_temp_table();
06724             $DB->delete_records('context_temp');
06725             $trans->allow_commit();
06726         }
06727     }
06728 }
06729 
06730 
06731 // ============== DEPRECATED FUNCTIONS ==========================================
06732 // Old context related functions were deprecated in 2.0, it is recommended
06733 // to use context classes in new code. Old function can be used when
06734 // creating patches that are supposed to be backported to older stable branches.
06735 // These deprecated functions will not be removed in near future,
06736 // before removing devs will be warned with a debugging message first,
06737 // then we will add error message and only after that we can remove the functions
06738 // completely.
06739 
06740 
06750 function load_temp_role($context, $roleid, array $accessdata) {
06751     debugging('load_temp_role() is deprecated, please use load_temp_course_role() instead, temp role not loaded.');
06752     return $accessdata;
06753 }
06754 
06763 function remove_temp_roles($context, array $accessdata) {
06764     debugging('remove_temp_role() is deprecated, please use remove_temp_course_roles() instead.');
06765     return $accessdata;
06766 }
06767 
06775 function get_system_context($cache = true) {
06776     return context_system::instance(0, IGNORE_MISSING, $cache);
06777 }
06778 
06791 function get_context_instance($contextlevel, $instance = 0, $strictness = IGNORE_MISSING) {
06792     $instances = (array)$instance;
06793     $contexts = array();
06794 
06795     $classname = context_helper::get_class_for_level($contextlevel);
06796 
06797     // we do not load multiple contexts any more, PAGE should be responsible for any preloading
06798     foreach ($instances as $inst) {
06799         $contexts[$inst] = $classname::instance($inst, $strictness);
06800     }
06801 
06802     if (is_array($instance)) {
06803         return $contexts;
06804     } else {
06805         return $contexts[$instance];
06806     }
06807 }
06808 
06818 function get_context_instance_by_id($id, $strictness = IGNORE_MISSING) {
06819     return context::instance_by_id($id, $strictness);
06820 }
06821 
06832 function get_parent_contexts(context $context, $includeself = false) {
06833     return $context->get_parent_context_ids($includeself);
06834 }
06835 
06844 function get_parent_contextid(context $context) {
06845     if ($parent = $context->get_parent_context()) {
06846         return $parent->id;
06847     } else {
06848         return false;
06849     }
06850 }
06851 
06869 function get_child_contexts(context $context) {
06870     return $context->get_child_contexts();
06871 }
06872 
06881 function create_contexts($contextlevel = null, $buildpaths = true) {
06882     context_helper::create_instances($contextlevel, $buildpaths);
06883 }
06884 
06891 function cleanup_contexts() {
06892     context_helper::cleanup_instances();
06893     return true;
06894 }
06895 
06903 function build_context_path($force = false) {
06904     context_helper::build_all_paths($force);
06905 }
06906 
06914 function rebuild_contexts(array $fixcontexts) {
06915     foreach ($fixcontexts as $fixcontext) {
06916         $fixcontext->reset_paths(false);
06917     }
06918     context_helper::build_all_paths(false);
06919 }
06920 
06930 function preload_course_contexts($courseid) {
06931     context_helper::preload_course($courseid);
06932 }
06933 
06944 function context_instance_preload_sql($joinon, $contextlevel, $tablealias) {
06945     $select = ", ".context_helper::get_preload_record_columns_sql($tablealias);
06946     $join = "LEFT JOIN {context} $tablealias ON ($tablealias.instanceid = $joinon AND $tablealias.contextlevel = $contextlevel)";
06947     return array($select, $join);
06948 }
06949 
06958 function context_instance_preload(stdClass $rec) {
06959     context_helper::preload_from_record($rec);
06960 }
06961 
06968 function mark_context_dirty($path) {
06969     global $CFG, $USER, $ACCESSLIB_PRIVATE;
06970 
06971     if (during_initial_install()) {
06972         return;
06973     }
06974 
06975     // only if it is a non-empty string
06976     if (is_string($path) && $path !== '') {
06977         set_cache_flag('accesslib/dirtycontexts', $path, 1, time()+$CFG->sessiontimeout);
06978         if (isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
06979             $ACCESSLIB_PRIVATE->dirtycontexts[$path] = 1;
06980         } else {
06981             if (CLI_SCRIPT) {
06982                 $ACCESSLIB_PRIVATE->dirtycontexts = array($path => 1);
06983             } else {
06984                 if (isset($USER->access['time'])) {
06985                     $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2);
06986                 } else {
06987                     $ACCESSLIB_PRIVATE->dirtycontexts = array($path => 1);
06988                 }
06989                 // flags not loaded yet, it will be done later in $context->reload_if_dirty()
06990             }
06991         }
06992     }
06993 }
06994 
07011 function context_moved(context $context, context $newparent) {
07012     $context->update_moved($newparent);
07013 }
07014 
07025 function delete_context($contextlevel, $instanceid, $deleterecord = true) {
07026     if ($deleterecord) {
07027         context_helper::delete_instance($contextlevel, $instanceid);
07028     } else {
07029         $classname = context_helper::get_class_for_level($contextlevel);
07030         if ($context = $classname::instance($instanceid, IGNORE_MISSING)) {
07031             $context->delete_content();
07032         }
07033     }
07034 
07035     return true;
07036 }
07037 
07044 function get_contextlevel_name($contextlevel) {
07045     return context_helper::get_level_name($contextlevel);
07046 }
07047 
07059 function print_context_name(context $context, $withprefix = true, $short = false) {
07060     return $context->get_context_name($withprefix, $short);
07061 }
07062 
07072 function get_context_url(context $context) {
07073     return $context->get_url();
07074 }
07075 
07084 function get_course_context(context $context) {
07085     return $context->get_course_context(true);
07086 }
07087 
07095 function get_courseid_from_context(context $context) {
07096     if ($coursecontext = $context->get_course_context(false)) {
07097         return $coursecontext->instanceid;
07098     } else {
07099         return false;
07100     }
07101 }
07102 
07117 function get_user_courses_bycap($userid, $cap, $accessdata_ignored, $doanything_ignored, $sort = 'c.sortorder ASC', $fields = null, $limit_ignored = 0) {
07118 
07119     $courses = enrol_get_users_courses($userid, true, $fields, $sort);
07120     foreach ($courses as $id=>$course) {
07121         $context = context_course::instance($id);
07122         if (!has_capability($cap, $context, $userid)) {
07123             unset($courses[$id]);
07124         }
07125     }
07126 
07127     return $courses;
07128 }
07129 
07146 function fetch_context_capabilities(context $context) {
07147     return $context->get_capabilities();
07148 }
07149 
07162 function get_sorted_contexts($select, $params = array()) {
07163 
07164     //TODO: we should probably rewrite all the code that is using this thing, the trouble is we MUST NOT modify the context instances...
07165 
07166     global $DB;
07167     if ($select) {
07168         $select = 'WHERE ' . $select;
07169     }
07170     return $DB->get_records_sql("
07171             SELECT ctx.*
07172               FROM {context} ctx
07173               LEFT JOIN {user} u ON ctx.contextlevel = " . CONTEXT_USER . " AND u.id = ctx.instanceid
07174               LEFT JOIN {course_categories} cat ON ctx.contextlevel = " . CONTEXT_COURSECAT . " AND cat.id = ctx.instanceid
07175               LEFT JOIN {course} c ON ctx.contextlevel = " . CONTEXT_COURSE . " AND c.id = ctx.instanceid
07176               LEFT JOIN {course_modules} cm ON ctx.contextlevel = " . CONTEXT_MODULE . " AND cm.id = ctx.instanceid
07177               LEFT JOIN {block_instances} bi ON ctx.contextlevel = " . CONTEXT_BLOCK . " AND bi.id = ctx.instanceid
07178            $select
07179           ORDER BY ctx.contextlevel, bi.defaultregion, COALESCE(cat.sortorder, c.sortorder, cm.section, bi.defaultweight), u.lastname, u.firstname, cm.id
07180             ", $params);
07181 }
07182 
07191 function get_role_context_caps($roleid, context $context) {
07192     global $DB;
07193 
07194     //this is really slow!!!! - do not use above course context level!
07195     $result = array();
07196     $result[$context->id] = array();
07197 
07198     // first emulate the parent context capabilities merging into context
07199     $searchcontexts = array_reverse($context->get_parent_context_ids(true));
07200     foreach ($searchcontexts as $cid) {
07201         if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) {
07202             foreach ($capabilities as $cap) {
07203                 if (!array_key_exists($cap->capability, $result[$context->id])) {
07204                     $result[$context->id][$cap->capability] = 0;
07205                 }
07206                 $result[$context->id][$cap->capability] += $cap->permission;
07207             }
07208         }
07209     }
07210 
07211     // now go through the contexts below given context
07212     $searchcontexts = array_keys($context->get_child_contexts());
07213     foreach ($searchcontexts as $cid) {
07214         if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) {
07215             foreach ($capabilities as $cap) {
07216                 if (!array_key_exists($cap->contextid, $result)) {
07217                     $result[$cap->contextid] = array();
07218                 }
07219                 $result[$cap->contextid][$cap->capability] = $cap->permission;
07220             }
07221         }
07222     }
07223 
07224     return $result;
07225 }
07226 
07236 function get_related_contexts_string(context $context) {
07237 
07238     if ($parents = $context->get_parent_context_ids()) {
07239         return (' IN ('.$context->id.','.implode(',', $parents).')');
07240     } else {
07241         return (' ='.$context->id);
07242     }
07243 }
 All Data Structures Namespaces Files Functions Variables Enumerations