Merged from 1.7.
[moodle.git] / lib / accesslib.php
CommitLineData
bbbf2d40 1<?php
cee0901c 2 /**
3 * Capability session information format
bbbf2d40 4 * 2 x 2 array
5 * [context][capability]
6 * where context is the context id of the table 'context'
7 * and capability is a string defining the capability
8 * e.g.
9 *
10 * [Capabilities] => [26][mod/forum:viewpost] = 1
11 * [26][mod/forum:startdiscussion] = -8990
12 * [26][mod/forum:editallpost] = -1
13 * [273][moodle:blahblah] = 1
14 * [273][moodle:blahblahblah] = 2
15 */
bbbf2d40 16
17// permission definitions
e49e61bf 18define('CAP_INHERIT', 0);
bbbf2d40 19define('CAP_ALLOW', 1);
20define('CAP_PREVENT', -1);
21define('CAP_PROHIBIT', -1000);
22
bbbf2d40 23// context definitions
24define('CONTEXT_SYSTEM', 10);
25define('CONTEXT_PERSONAL', 20);
4b10f08b 26define('CONTEXT_USER', 30);
bbbf2d40 27define('CONTEXT_COURSECAT', 40);
28define('CONTEXT_COURSE', 50);
29define('CONTEXT_GROUP', 60);
30define('CONTEXT_MODULE', 70);
31define('CONTEXT_BLOCK', 80);
32
21b6db6e 33// capability risks - see http://docs.moodle.org/en/Hardening_new_Roles_system
34define('RISK_MANAGETRUST', 0x0001);
a6b02b65 35define('RISK_CONFIG', 0x0002);
21b6db6e 36define('RISK_XSS', 0x0004);
37define('RISK_PERSONAL', 0x0008);
38define('RISK_SPAM', 0x0010);
39
f3f7610c 40require_once($CFG->dirroot.'/group/lib.php');
21b6db6e 41
340ea4e8 42$context_cache = array(); // Cache of all used context objects for performance (by level and instance)
43$context_cache_id = array(); // Index to above cache by id
bbbf2d40 44
7700027f 45
e7876c1e 46/**
47 * Loads the capabilities for the default guest role to the current user in a specific context
48 * @return object
49 */
8d2b18a8 50function load_guest_role($context=NULL, $mergewith=NULL) {
e7876c1e 51 global $USER;
52
53 static $guestrole;
54
55 if (!isloggedin()) {
56 return false;
57 }
58
21c9bace 59 if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) {
e7876c1e 60 return false;
61 }
62
63 if (empty($context)) {
64 $context = $sitecontext;
65 }
66
67 if (empty($guestrole)) {
8f8ed475 68 if (!$guestrole = get_guest_role()) {
e7876c1e 69 return false;
70 }
71 }
72
eef868d1 73 if ($capabilities = get_records_select('role_capabilities',
e7876c1e 74 "roleid = $guestrole->id AND contextid = $sitecontext->id")) {
75 foreach ($capabilities as $capability) {
8d2b18a8 76 if ($mergewith === NULL) {
77 $USER->capabilities[$context->id][$capability->capability] = $capability->permission;
78 } else {
79 $mergewith[$context->id][$capability->capability] = $capability->permission;
80 }
e7876c1e 81 }
82 }
83
8d2b18a8 84 if ($mergewith === NULL) {
85 has_capability('clearcache');
86 return true;
87 } else {
88 return $mergewith;
89 }
e7876c1e 90}
91
7700027f 92/**
93 * Load default not logged in role capabilities when user is not logged in
eef868d1 94 * @return bool
7700027f 95 */
20aeb4b8 96function load_notloggedin_role() {
97 global $CFG, $USER;
98
21c9bace 99 if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) {
7700027f 100 return false;
35a518c5 101 }
102
7700027f 103 if (empty($CFG->notloggedinroleid)) { // Let's set the default to the guest role
8f8ed475 104 if ($role = get_guest_role()) {
7700027f 105 set_config('notloggedinroleid', $role->id);
106 } else {
107 return false;
108 }
109 }
20aeb4b8 110
eef868d1 111 if ($capabilities = get_records_select('role_capabilities',
7700027f 112 "roleid = $CFG->notloggedinroleid AND contextid = $sitecontext->id")) {
113 foreach ($capabilities as $capability) {
eef868d1 114 $USER->capabilities[$sitecontext->id][$capability->capability] = $capability->permission;
7700027f 115 }
99ad7633 116 has_capability('clearcache');
20aeb4b8 117 }
118
119 return true;
120}
cee0901c 121
8f8ed475 122/**
123 * Load default not logged in role capabilities when user is not logged in
eef868d1 124 * @return bool
8f8ed475 125 */
8d2b18a8 126function load_defaultuser_role($return=false) {
8f8ed475 127 global $CFG, $USER;
128
21c9bace 129 if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) {
8f8ed475 130 return false;
131 }
132
133 if (empty($CFG->defaultuserroleid)) { // Let's set the default to the guest role
134 if ($role = get_guest_role()) {
135 set_config('defaultuserroleid', $role->id);
136 } else {
137 return false;
138 }
139 }
140
8d2b18a8 141 if ($return) {
142 $defcaps = array();
143 }
144
eef868d1 145 if ($capabilities = get_records_select('role_capabilities',
0163b1d0 146 "roleid = $CFG->defaultuserroleid AND contextid = $sitecontext->id AND permission <> 0")) {
40d86709 147 // Find out if this default role is a guest role, for the hack below
8d2b18a8 148
40d86709 149 $defaultisguestrole=false;
8f8ed475 150 foreach ($capabilities as $capability) {
40d86709 151 if($capability->capability=='moodle/legacy:guest') {
152 $defaultisguestrole=true;
153 }
154 }
155 foreach ($capabilities as $capability) {
156 // If the default role is a guest role, then don't copy legacy:guest,
157 // otherwise this user could get confused with a REAL guest. Also don't copy
158 // course:view, which is a hack that's necessary because guest roles are
159 // not really handled properly (see MDL-7513)
8d2b18a8 160 if($defaultisguestrole &&
40d86709 161 ($capability->capability=='moodle/legacy:guest' ||
162 $capability->capability=='moodle/course:view')) {
163 continue;
164 }
8d2b18a8 165 if ($return) {
166 $defcaps[$sitecontext->id][$capability->capability] = $capability->permission;
167 } else {
168 // Don't overwrite capabilities from real role...
169 if (!isset($USER->capabilities[$sitecontext->id][$capability->capability])) {
170 $USER->capabilities[$sitecontext->id][$capability->capability] = $capability->permission;
171 }
ca23ffdb 172 }
8f8ed475 173 }
8f8ed475 174 }
175
8d2b18a8 176 if ($return) {
177 return $defcaps;
178 } else {
179 has_capability('clearcache');
180 return true;
181 }
8f8ed475 182}
183
184
185/**
186 * Get the default guest role
187 * @return object role
188 */
189function get_guest_role() {
ebce32b5 190 global $CFG;
191
192 if (empty($CFG->guestroleid)) {
193 if ($roles = get_roles_with_capability('moodle/legacy:guest', CAP_ALLOW)) {
194 $guestrole = array_shift($roles); // Pick the first one
195 set_config('guestroleid', $guestrole->id);
196 return $guestrole;
197 } else {
198 debugging('Can not find any guest role!');
199 return false;
200 }
8f8ed475 201 } else {
ebce32b5 202 if ($guestrole = get_record('role','id', $CFG->guestroleid)) {
203 return $guestrole;
204 } else {
205 //somebody is messing with guest roles, remove incorrect setting and try to find a new one
206 set_config('guestroleid', '');
207 return get_guest_role();
208 }
8f8ed475 209 }
210}
211
212
bbbf2d40 213/**
214 * This functions get all the course categories in proper order
40a2a15f 215 * (!)note this only gets course category contexts, and not the site
216 * context
d963740d 217 * @param object $context
bbbf2d40 218 * @param int $type
219 * @return array of contextids
220 */
0468976c 221function get_parent_cats($context, $type) {
eef868d1 222
98882637 223 $parents = array();
eef868d1 224
c5ddc3fd 225 switch ($type) {
40a2a15f 226 // a category can be the parent of another category
227 // there is no limit of depth in this case
98882637 228 case CONTEXT_COURSECAT:
c5ddc3fd 229 if (!$cat = get_record('course_categories','id',$context->instanceid)) {
230 break;
231 }
232
233 while (!empty($cat->parent)) {
234 if (!$context = get_context_instance(CONTEXT_COURSECAT, $cat->parent)) {
235 break;
236 }
98882637 237 $parents[] = $context->id;
238 $cat = get_record('course_categories','id',$cat->parent);
239 }
98882637 240 break;
40a2a15f 241
242 // a course always fall into a category, unless it's a site course
8ba412da 243 // this happens when SITEID == $course->id
40a2a15f 244 // in this case the parent of the course is site context
98882637 245 case CONTEXT_COURSE:
c5ddc3fd 246 if (!$course = get_record('course', 'id', $context->instanceid)) {
247 break;
248 }
249 if (!$catinstance = get_context_instance(CONTEXT_COURSECAT, $course->category)) {
250 break;
251 }
252
98882637 253 $parents[] = $catinstance->id;
c5ddc3fd 254
255 if (!$cat = get_record('course_categories','id',$course->category)) {
256 break;
257 }
40a2a15f 258 // Yu: Separating site and site course context
259 if ($course->id == SITEID) {
260 break;
261 }
c5ddc3fd 262
263 while (!empty($cat->parent)) {
264 if (!$context = get_context_instance(CONTEXT_COURSECAT, $cat->parent)) {
265 break;
266 }
98882637 267 $parents[] = $context->id;
268 $cat = get_record('course_categories','id',$cat->parent);
269 }
270 break;
eef868d1 271
98882637 272 default:
273 break;
98882637 274 }
98882637 275 return array_reverse($parents);
bbbf2d40 276}
277
278
cee0901c 279
0468976c 280/**
281 * This function checks for a capability assertion being true. If it isn't
282 * then the page is terminated neatly with a standard error message
283 * @param string $capability - name of the capability
284 * @param object $context - a context object (record from context table)
285 * @param integer $userid - a userid number
d74067e8 286 * @param bool $doanything - if false, ignore do anything
0468976c 287 * @param string $errorstring - an errorstring
d74067e8 288 * @param string $stringfile - which stringfile to get it from
0468976c 289 */
eef868d1 290function require_capability($capability, $context=NULL, $userid=NULL, $doanything=true,
71483894 291 $errormessage='nopermissions', $stringfile='') {
a9e1c058 292
71483894 293 global $USER, $CFG;
a9e1c058 294
71483894 295/// If the current user is not logged in, then make sure they are (if needed)
a9e1c058 296
6605128e 297 if (empty($userid) and empty($USER->capabilities)) {
aad2ba95 298 if ($context && ($context->contextlevel == CONTEXT_COURSE)) {
a9e1c058 299 require_login($context->instanceid);
11ac79ff 300 } else if ($context && ($context->contextlevel == CONTEXT_MODULE)) {
301 if ($cm = get_record('course_modules','id',$context->instanceid)) {
71483894 302 if (!$course = get_record('course', 'id', $cm->course)) {
303 error('Incorrect course.');
304 }
305 require_course_login($course, true, $cm);
306
11ac79ff 307 } else {
308 require_login();
309 }
71483894 310 } else if ($context && ($context->contextlevel == CONTEXT_SYSTEM)) {
311 if (!empty($CFG->forcelogin)) {
312 require_login();
313 }
314
a9e1c058 315 } else {
316 require_login();
317 }
318 }
eef868d1 319
a9e1c058 320/// OK, if they still don't have the capability then print a nice error message
321
d74067e8 322 if (!has_capability($capability, $context, $userid, $doanything)) {
0468976c 323 $capabilityname = get_capability_string($capability);
324 print_error($errormessage, $stringfile, '', $capabilityname);
325 }
326}
327
328
bbbf2d40 329/**
330 * This function returns whether the current user has the capability of performing a function
331 * For example, we can do has_capability('mod/forum:replypost',$cm) in forum
332 * only one of the 4 (moduleinstance, courseid, site, userid) would be set at 1 time
333 * This is a recursive funciton.
bbbf2d40 334 * @uses $USER
caac8977 335 * @param string $capability - name of the capability (or debugcache or clearcache)
0468976c 336 * @param object $context - a context object (record from context table)
337 * @param integer $userid - a userid number
20aeb4b8 338 * @param bool $doanything - if false, ignore do anything
bbbf2d40 339 * @return bool
340 */
d74067e8 341function has_capability($capability, $context=NULL, $userid=NULL, $doanything=true) {
bbbf2d40 342
20aeb4b8 343 global $USER, $CONTEXT, $CFG;
bbbf2d40 344
922633bd 345 static $capcache = array(); // Cache of capabilities
346
caac8977 347
348/// Cache management
349
350 if ($capability == 'clearcache') {
351 $capcache = array(); // Clear ALL the capability cache
352 return false;
353 }
354
922633bd 355/// Some sanity checks
356 if (debugging()) {
357 if ($capability == 'debugcache') {
358 print_object($capcache);
359 return true;
360 }
361 if (!record_exists('capabilities', 'name', $capability)) {
362 debugging('Capability "'.$capability.'" was not found! This should be fixed in code.');
363 }
364 if ($doanything != true and $doanything != false) {
365 debugging('Capability parameter "doanything" is wierd ("'.$doanything.'"). This should be fixed in code.');
366 }
367 if (!is_object($context) && $context !== NULL) {
368 debugging('Incorrect context parameter "'.$context.'" for has_capability(), object expected! This should be fixed in code.');
369 }
a8a7300a 370 }
371
922633bd 372/// Make sure we know the current context
373 if (empty($context)) { // Use default CONTEXT if none specified
374 if (empty($CONTEXT)) {
375 return false;
376 } else {
377 $context = $CONTEXT;
378 }
379 } else { // A context was given to us
380 if (empty($CONTEXT)) {
381 $CONTEXT = $context; // Store FIRST used context in this global as future default
382 }
383 }
384
385/// Check and return cache in case we've processed this one before.
8d2b18a8 386 $requsteduser = empty($userid) ? $USER->id : $userid; // find out the requested user id, $USER->id might have been changed
387 $cachekey = $capability.'_'.$context->id.'_'.intval($requsteduser).'_'.intval($doanything);
caac8977 388
922633bd 389 if (isset($capcache[$cachekey])) {
390 return $capcache[$cachekey];
391 }
392
20aeb4b8 393
922633bd 394/// Load up the capabilities list or item as necessary
8d2b18a8 395 if ($userid) {
396 if (empty($USER->id) or ($userid != $USER->id) or empty($USER->capabilities)) {
397 // this is expensive
398 $capabilities = load_user_capability($capability, $context, $userid);
399
400 static $guestuserid = false; // cached guest user id
401 if (!$guestuserid) {
402 $guestuserid = get_field('user', 'id', 'username', 'guest');
403 }
404
405 if ($userid == $guestuserid) {
406 //this might be cached too, but should not needed
407 $capabilities = load_guest_role($context, $capabilities);
408
409 } else {
410 static $defcaps = false; //cached default user caps - this might help cron
411 if (empty($capcache) or !$defcaps) { //first run or capcache was reset
412 $defcaps = load_defaultuser_role(true);
413 }
414 foreach($defcaps as $contextid=>$caps) {//apply only extra caps defined for default user
415 foreach($caps as $cap=>$permission) {
416 if (!isset($capabilities[$contextid][$cap])) {
417 $capabilities[$contextid][$cap] = $permission;
418 }
419 }
420 }
421 }
422 } else { //$USER->id == $userid and capabilities already present
423 $capabilities = $USER->capabilities;
9425b25f 424 }
8d2b18a8 425
9425b25f 426 } else { // no userid
8d2b18a8 427 if (empty($USER->capabilities)) {
428 if (empty($USER->id)) {
429 //not logged in user first time here
430 load_notloggedin_role();
431 } else {
432 // 'Simulated' user first time here - load_all_capabilities() not called from login/index.php
433 load_all_capabilities(); // expensive - but we have to do it once anyway
434 }
435 }
436 $capabilities = $USER->capabilities;
922633bd 437 $userid = $USER->id;
98882637 438 }
9425b25f 439
caac8977 440/// We act a little differently when switchroles is active
441
442 $switchroleactive = false; // Assume it isn't active in this context
443
bbbf2d40 444
922633bd 445/// First deal with the "doanything" capability
5fe9a11d 446
20aeb4b8 447 if ($doanything) {
2d07587b 448
f4e2d38a 449 /// First make sure that we aren't in a "switched role"
450
f4e2d38a 451 if (!empty($USER->switchrole)) { // Switchrole is active somewhere!
452 if (!empty($USER->switchrole[$context->id])) { // Because of current context
453 $switchroleactive = true;
454 } else { // Check parent contexts
455 if ($parentcontextids = get_parent_contexts($context)) {
456 foreach ($parentcontextids as $parentcontextid) {
457 if (!empty($USER->switchrole[$parentcontextid])) { // Yep, switchroles active here
458 $switchroleactive = true;
459 break;
460 }
461 }
462 }
463 }
464 }
465
466 /// Check the site context for doanything (most common) first
467
468 if (empty($switchroleactive)) { // Ignore site setting if switchrole is active
21c9bace 469 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
2d07587b 470 if (isset($capabilities[$sitecontext->id]['moodle/site:doanything'])) {
922633bd 471 $result = (0 < $capabilities[$sitecontext->id]['moodle/site:doanything']);
472 $capcache[$cachekey] = $result;
473 return $result;
2d07587b 474 }
20aeb4b8 475 }
40a2a15f 476 /// If it's not set at site level, it is possible to be set on other levels
477 /// Though this usage is not common and can cause risks
aad2ba95 478 switch ($context->contextlevel) {
eef868d1 479
20aeb4b8 480 case CONTEXT_COURSECAT:
481 // Check parent cats.
482 $parentcats = get_parent_cats($context, CONTEXT_COURSECAT);
483 foreach ($parentcats as $parentcat) {
484 if (isset($capabilities[$parentcat]['moodle/site:doanything'])) {
922633bd 485 $result = (0 < $capabilities[$parentcat]['moodle/site:doanything']);
486 $capcache[$cachekey] = $result;
487 return $result;
20aeb4b8 488 }
cee0901c 489 }
20aeb4b8 490 break;
bbbf2d40 491
20aeb4b8 492 case CONTEXT_COURSE:
493 // Check parent cat.
494 $parentcats = get_parent_cats($context, CONTEXT_COURSE);
98882637 495
20aeb4b8 496 foreach ($parentcats as $parentcat) {
497 if (isset($capabilities[$parentcat]['do_anything'])) {
922633bd 498 $result = (0 < $capabilities[$parentcat]['do_anything']);
499 $capcache[$cachekey] = $result;
500 return $result;
20aeb4b8 501 }
9425b25f 502 }
20aeb4b8 503 break;
bbbf2d40 504
20aeb4b8 505 case CONTEXT_GROUP:
506 // Find course.
f3f7610c
ML
507 $courseid = groups_get_course($context->instanceid);
508 $courseinstance = get_context_instance(CONTEXT_COURSE, $courseid);
9425b25f 509
20aeb4b8 510 $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE);
511 foreach ($parentcats as $parentcat) {
b4a1805a 512 if (isset($capabilities[$parentcat]['do_anything'])) {
513 $result = (0 < $capabilities[$parentcat]['do_anything']);
922633bd 514 $capcache[$cachekey] = $result;
515 return $result;
20aeb4b8 516 }
9425b25f 517 }
9425b25f 518
20aeb4b8 519 $coursecontext = '';
520 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
922633bd 521 $result = (0 < $capabilities[$courseinstance->id]['do_anything']);
522 $capcache[$cachekey] = $result;
523 return $result;
20aeb4b8 524 }
9425b25f 525
20aeb4b8 526 break;
bbbf2d40 527
20aeb4b8 528 case CONTEXT_MODULE:
529 // Find course.
530 $cm = get_record('course_modules', 'id', $context->instanceid);
531 $courseinstance = get_context_instance(CONTEXT_COURSE, $cm->course);
9425b25f 532
20aeb4b8 533 if ($parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE)) {
534 foreach ($parentcats as $parentcat) {
535 if (isset($capabilities[$parentcat]['do_anything'])) {
922633bd 536 $result = (0 < $capabilities[$parentcat]['do_anything']);
537 $capcache[$cachekey] = $result;
538 return $result;
20aeb4b8 539 }
cee0901c 540 }
9425b25f 541 }
98882637 542
20aeb4b8 543 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
922633bd 544 $result = (0 < $capabilities[$courseinstance->id]['do_anything']);
545 $capcache[$cachekey] = $result;
546 return $result;
20aeb4b8 547 }
bbbf2d40 548
20aeb4b8 549 break;
bbbf2d40 550
20aeb4b8 551 case CONTEXT_BLOCK:
2d95f702 552 // not necessarily 1 to 1 to course.
20aeb4b8 553 $block = get_record('block_instance','id',$context->instanceid);
2d95f702 554 if ($block->pagetype == 'course-view') {
555 $courseinstance = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
556 $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE);
557
558 foreach ($parentcats as $parentcat) {
559 if (isset($capabilities[$parentcat]['do_anything'])) {
560 $result = (0 < $capabilities[$parentcat]['do_anything']);
561 $capcache[$cachekey] = $result;
562 return $result;
563 }
564 }
565
566 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
567 $result = (0 < $capabilities[$courseinstance->id]['do_anything']);
568 $capcache[$cachekey] = $result;
569 return $result;
570 }
571 } else { // if not course-view type of blocks, check site
572 if (isset($capabilities[$sitecontext->id]['do_anything'])) {
573 $result = (0 < $capabilities[$sitecontext->id]['do_anything']);
922633bd 574 $capcache[$cachekey] = $result;
575 return $result;
20aeb4b8 576 }
20aeb4b8 577 }
578 break;
bbbf2d40 579
20aeb4b8 580 default:
4b10f08b 581 // CONTEXT_SYSTEM: CONTEXT_PERSONAL: CONTEXT_USER:
40a2a15f 582 // Do nothing, because the parents are site context
583 // which has been checked already
20aeb4b8 584 break;
585 }
bbbf2d40 586
20aeb4b8 587 // Last: check self.
588 if (isset($capabilities[$context->id]['do_anything'])) {
922633bd 589 $result = (0 < $capabilities[$context->id]['do_anything']);
590 $capcache[$cachekey] = $result;
591 return $result;
20aeb4b8 592 }
98882637 593 }
caac8977 594 // do_anything has not been set, we now look for it the normal way.
595 $result = (0 < capability_search($capability, $context, $capabilities, $switchroleactive));
922633bd 596 $capcache[$cachekey] = $result;
597 return $result;
bbbf2d40 598
9425b25f 599}
bbbf2d40 600
601
602/**
603 * In a separate function so that we won't have to deal with do_anything.
40a2a15f 604 * again. Used by function has_capability().
bbbf2d40 605 * @param $capability - capability string
0468976c 606 * @param $context - the context object
40a2a15f 607 * @param $capabilities - either $USER->capability or loaded array (for other users)
bbbf2d40 608 * @return permission (int)
609 */
caac8977 610function capability_search($capability, $context, $capabilities, $switchroleactive=false) {
759ac72d 611
bbbf2d40 612 global $USER, $CFG;
0468976c 613
11ac79ff 614 if (!isset($context->id)) {
615 return 0;
616 }
40a2a15f 617 // if already set in the array explicitly, no need to look for it in parent
618 // context any longer
0468976c 619 if (isset($capabilities[$context->id][$capability])) {
620 return ($capabilities[$context->id][$capability]);
bbbf2d40 621 }
9425b25f 622
bbbf2d40 623 /* Then, we check the cache recursively */
9425b25f 624 $permission = 0;
625
aad2ba95 626 switch ($context->contextlevel) {
bbbf2d40 627
628 case CONTEXT_SYSTEM: // by now it's a definite an inherit
629 $permission = 0;
630 break;
631
632 case CONTEXT_PERSONAL:
21c9bace 633 $parentcontext = get_context_instance(CONTEXT_SYSTEM);
a2b6ee75 634 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 635 break;
9425b25f 636
4b10f08b 637 case CONTEXT_USER:
21c9bace 638 $parentcontext = get_context_instance(CONTEXT_SYSTEM);
a2b6ee75 639 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 640 break;
9425b25f 641
bbbf2d40 642 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
643 $coursecat = get_record('course_categories','id',$context->instanceid);
0468976c 644 if (!empty($coursecat->parent)) { // return parent value if it exists
645 $parentcontext = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
bbbf2d40 646 } else { // else return site value
21c9bace 647 $parentcontext = get_context_instance(CONTEXT_SYSTEM);
bbbf2d40 648 }
a2b6ee75 649 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 650 break;
651
652 case CONTEXT_COURSE: // 1 to 1 to course cat
caac8977 653 if (empty($switchroleactive)) {
654 // find the course cat, and return its value
655 $course = get_record('course','id',$context->instanceid);
656 if ($course->id == SITEID) { // In 1.8 we've separated site course and system
657 $parentcontext = get_context_instance(CONTEXT_SYSTEM);
658 } else {
659 $parentcontext = get_context_instance(CONTEXT_COURSECAT, $course->category);
660 }
a2b6ee75 661 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
40a2a15f 662 }
bbbf2d40 663 break;
664
665 case CONTEXT_GROUP: // 1 to 1 to course
f3f7610c
ML
666 $courseid = groups_get_course($context->instanceid);
667 $parentcontext = get_context_instance(CONTEXT_COURSE, $courseid);
a2b6ee75 668 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 669 break;
670
671 case CONTEXT_MODULE: // 1 to 1 to course
672 $cm = get_record('course_modules','id',$context->instanceid);
0468976c 673 $parentcontext = get_context_instance(CONTEXT_COURSE, $cm->course);
a2b6ee75 674 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 675 break;
676
2d95f702 677 case CONTEXT_BLOCK: // not necessarily 1 to 1 to course
bbbf2d40 678 $block = get_record('block_instance','id',$context->instanceid);
2d95f702 679 if ($block->pagetype == 'course-view') {
680 $parentcontext = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
681 } else {
682 $parentcontext = get_context_instance(CONTEXT_SYSTEM);
683 }
a2b6ee75 684 $permission = capability_search($capability, $parentcontext, $capabilities, $switchroleactive);
bbbf2d40 685 break;
686
687 default:
688 error ('This is an unknown context!');
689 return false;
690 }
9425b25f 691
98882637 692 return $permission;
bbbf2d40 693}
694
40a2a15f 695/**
696 * auxillary function for load_user_capabilities()
697 * checks if context c1 is a parent (or itself) of context c2
698 * @param int $c1 - context id of context 1
699 * @param int $c2 - context id of context 2
700 * @return bool
701 */
9fccc080 702function is_parent_context($c1, $c2) {
703 static $parentsarray;
704
705 // context can be itself and this is ok
706 if ($c1 == $c2) {
707 return true;
708 }
709 // hit in cache?
710 if (isset($parentsarray[$c1][$c2])) {
711 return $parentsarray[$c1][$c2];
712 }
713
714 if (!$co2 = get_record('context', 'id', $c2)) {
715 return false;
716 }
717
718 if (!$parents = get_parent_contexts($co2)) {
719 return false;
720 }
721
722 foreach ($parents as $parent) {
723 $parentsarray[$parent][$c2] = true;
724 }
725
726 if (in_array($c1, $parents)) {
727 return true;
728 } else { // else not a parent, set the cache anyway
729 $parentsarray[$c1][$c2] = false;
730 return false;
731 }
732}
733
734
735/*
736 * auxillary function for load_user_capabilities()
737 * handler in usort() to sort contexts according to level
40a2a15f 738 * @param object contexta
739 * @param object contextb
740 * @return int
9fccc080 741 */
742function roles_context_cmp($contexta, $contextb) {
743 if ($contexta->contextlevel == $contextb->contextlevel) {
744 return 0;
745 }
746 return ($contexta->contextlevel < $contextb->contextlevel) ? -1 : 1;
747}
748
bbbf2d40 749/**
bbbf2d40 750 * It will build an array of all the capabilities at each level
751 * i.e. site/metacourse/course_category/course/moduleinstance
752 * Note we should only load capabilities if they are explicitly assigned already,
753 * we should not load all module's capability!
21c9bace 754 *
bbbf2d40 755 * [Capabilities] => [26][forum_post] = 1
756 * [26][forum_start] = -8990
757 * [26][forum_edit] = -1
758 * [273][blah blah] = 1
759 * [273][blah blah blah] = 2
21c9bace 760 *
761 * @param $capability string - Only get a specific capability (string)
762 * @param $context object - Only get capabilities for a specific context object
763 * @param $userid integer - the id of the user whose capabilities we want to load
764 * @return array of permissions (or nothing if they get assigned to $USER)
bbbf2d40 765 */
21c9bace 766function load_user_capability($capability='', $context = NULL, $userid='') {
d140ad3f 767
98882637 768 global $USER, $CFG;
55526eee 769
40a2a15f 770 // this flag has not been set!
771 // (not clean install, or upgraded successfully to 1.7 and up)
2f1a4248 772 if (empty($CFG->rolesactive)) {
773 return false;
774 }
775
bbbf2d40 776 if (empty($userid)) {
dc411d1b 777 if (empty($USER->id)) { // We have no user to get capabilities for
64026e8c 778 debugging('User not logged in for load_user_capability!');
dc411d1b 779 return false;
780 }
64026e8c 781 unset($USER->capabilities); // We don't want possible older capabilites hanging around
782
783 check_enrolment_plugins($USER); // Call "enrol" system to ensure that we have the correct picture
8f8ed475 784
bbbf2d40 785 $userid = $USER->id;
dc411d1b 786 $otheruserid = false;
bbbf2d40 787 } else {
64026e8c 788 if (!$user = get_record('user', 'id', $userid)) {
789 debugging('Non-existent userid in load_user_capability!');
790 return false;
791 }
792
793 check_enrolment_plugins($user); // Ensure that we have the correct picture
794
9425b25f 795 $otheruserid = $userid;
bbbf2d40 796 }
9425b25f 797
5f70bcc3 798
799/// First we generate a list of all relevant contexts of the user
800
801 $usercontexts = array();
bbbf2d40 802
0468976c 803 if ($context) { // if context is specified
eef868d1 804 $usercontexts = get_parent_contexts($context);
9343a733 805 $usercontexts[] = $context->id; // Add the current context as well
98882637 806 } else { // else, we load everything
5f70bcc3 807 if ($userroles = get_records('role_assignments','userid',$userid)) {
808 foreach ($userroles as $userrole) {
0db6adc9 809 if (!in_array($userrole->contextid, $usercontexts)) {
810 $usercontexts[] = $userrole->contextid;
811 }
5f70bcc3 812 }
98882637 813 }
5f70bcc3 814 }
815
816/// Set up SQL fragments for searching contexts
817
818 if ($usercontexts) {
0468976c 819 $listofcontexts = '('.implode(',', $usercontexts).')';
5f70bcc3 820 $searchcontexts1 = "c1.id IN $listofcontexts AND";
5f70bcc3 821 } else {
c76e095f 822 $searchcontexts1 = '';
bbbf2d40 823 }
3ca2dea5 824
64026e8c 825 if ($capability) {
826 $capsearch = " AND rc.capability = '$capability' ";
827 } else {
eef868d1 828 $capsearch ="";
64026e8c 829 }
830
e38f38c3 831/// Set up SQL fragments for timestart, timeend etc
832 $now = time();
85f101fa 833 $timesql = "AND ((ra.timestart = 0 OR ra.timestart < $now) AND (ra.timeend = 0 OR ra.timeend > $now))";
e38f38c3 834
5f70bcc3 835/// Then we use 1 giant SQL to bring out all relevant capabilities.
836/// The first part gets the capabilities of orginal role.
837/// The second part gets the capabilities of overriden roles.
bbbf2d40 838
21c9bace 839 $siteinstance = get_context_instance(CONTEXT_SYSTEM);
9fccc080 840 $capabilities = array(); // Reinitialize.
841
842 // SQL for normal capabilities
843 $SQL1 = "SELECT rc.capability, c1.id as id1, c1.id as id2, (c1.contextlevel * 100) AS aggrlevel,
bbbf2d40 844 SUM(rc.permission) AS sum
845 FROM
eef868d1 846 {$CFG->prefix}role_assignments ra,
42ac3ecf 847 {$CFG->prefix}role_capabilities rc,
848 {$CFG->prefix}context c1
bbbf2d40 849 WHERE
d4649c76 850 ra.contextid=c1.id AND
851 ra.roleid=rc.roleid AND
bbbf2d40 852 ra.userid=$userid AND
5f70bcc3 853 $searchcontexts1
eef868d1 854 rc.contextid=$siteinstance->id
98882637 855 $capsearch
e38f38c3 856 $timesql
bbbf2d40 857 GROUP BY
55526eee 858 rc.capability, c1.id, c1.contextlevel * 100
bbbf2d40 859 HAVING
9fccc080 860 SUM(rc.permission) != 0
0db6adc9 861
862 UNION ALL
863
864 SELECT rc.capability, c1.id as id1, c2.id as id2, (c1.contextlevel * 100 + c2.contextlevel) AS aggrlevel,
865 SUM(rc.permission) AS sum
866 FROM
867 {$CFG->prefix}role_assignments ra LEFT JOIN
868 {$CFG->prefix}role_capabilities rc on ra.roleid = rc.roleid LEFT JOIN
869 {$CFG->prefix}context c1 on ra.contextid = c1.id LEFT JOIN
870 {$CFG->prefix}context c2 on rc.contextid = c2.id LEFT JOIN
871 {$CFG->prefix}context_rel cr on cr.c1 = c2.id
872 WHERE
873 ra.userid=$userid AND
874 $searchcontexts1
875 rc.contextid != $siteinstance->id
876 $capsearch
877 $timesql
878 AND cr.c2 = c1.id
879 GROUP BY
55526eee 880 rc.capability, c1.id, c2.id, c1.contextlevel * 100 + c2.contextlevel
0db6adc9 881 HAVING
882 SUM(rc.permission) != 0
9fccc080 883 ORDER BY
884 aggrlevel ASC";
0db6adc9 885
9fccc080 886 if (!$rs = get_recordset_sql($SQL1)) {
887 error("Query failed in load_user_capability.");
888 }
bbbf2d40 889
9fccc080 890 if ($rs && $rs->RecordCount() > 0) {
bcf88cbb 891 while ($caprec = rs_fetch_next_record($rs)) {
892 $array = (array)$caprec;
9fccc080 893 $temprecord = new object;
894
895 foreach ($array as $key=>$val) {
896 if ($key == 'aggrlevel') {
897 $temprecord->contextlevel = $val;
898 } else {
899 $temprecord->{$key} = $val;
900 }
901 }
9fccc080 902 $capabilities[] = $temprecord;
9fccc080 903 }
bcf88cbb 904 rs_close($rs);
b7e40271 905 }
bcf88cbb 906
9fccc080 907 // SQL for overrides
908 // this is take out because we have no way of making sure c1 is indeed related to c2 (parent)
909 // if we do not group by sum, it is possible to have multiple records of rc.capability, c1.id, c2.id, tuple having
910 // different values, we can maually sum it when we go through the list
0db6adc9 911
912 /*
913
9fccc080 914 $SQL2 = "SELECT rc.capability, c1.id as id1, c2.id as id2, (c1.contextlevel * 100 + c2.contextlevel) AS aggrlevel,
915 rc.permission AS sum
bbbf2d40 916 FROM
42ac3ecf 917 {$CFG->prefix}role_assignments ra,
918 {$CFG->prefix}role_capabilities rc,
919 {$CFG->prefix}context c1,
920 {$CFG->prefix}context c2
bbbf2d40 921 WHERE
d4649c76 922 ra.contextid=c1.id AND
eef868d1 923 ra.roleid=rc.roleid AND
924 ra.userid=$userid AND
925 rc.contextid=c2.id AND
5f70bcc3 926 $searchcontexts1
5f70bcc3 927 rc.contextid != $siteinstance->id
bbbf2d40 928 $capsearch
e38f38c3 929 $timesql
eef868d1 930
bbbf2d40 931 GROUP BY
21c9bace 932 rc.capability, (c1.contextlevel * 100 + c2.contextlevel), c1.id, c2.id, rc.permission
bbbf2d40 933 ORDER BY
75e84883 934 aggrlevel ASC
0db6adc9 935 ";*/
9fccc080 936
0db6adc9 937/*
9fccc080 938 if (!$rs = get_recordset_sql($SQL2)) {
75e84883 939 error("Query failed in load_user_capability.");
940 }
5cf38a57 941
bbbf2d40 942 if ($rs && $rs->RecordCount() > 0) {
bcf88cbb 943 while ($caprec = rs_fetch_next_record($rs)) {
944 $array = (array)$caprec;
75e84883 945 $temprecord = new object;
eef868d1 946
98882637 947 foreach ($array as $key=>$val) {
75e84883 948 if ($key == 'aggrlevel') {
aad2ba95 949 $temprecord->contextlevel = $val;
75e84883 950 } else {
951 $temprecord->{$key} = $val;
952 }
98882637 953 }
9fccc080 954 // for overrides, we have to make sure that context2 is a child of context1
955 // otherwise the combination makes no sense
0db6adc9 956 //if (is_parent_context($temprecord->id1, $temprecord->id2)) {
9fccc080 957 $capabilities[] = $temprecord;
0db6adc9 958 //} // only write if relevant
bbbf2d40 959 }
bcf88cbb 960 rs_close($rs);
bbbf2d40 961 }
0db6adc9 962
9fccc080 963 // this step sorts capabilities according to the contextlevel
964 // it is very important because the order matters when we
965 // go through each capabilities later. (i.e. higher level contextlevel
966 // will override lower contextlevel settings
967 usort($capabilities, 'roles_context_cmp');
0db6adc9 968*/
bbbf2d40 969 /* so up to this point we should have somethign like this
aad2ba95 970 * $capabilities[1] ->contextlevel = 1000
8ba412da 971 ->module = 0 // changed from SITEID in 1.8 (??)
bbbf2d40 972 ->capability = do_anything
973 ->id = 1 (id is the context id)
974 ->sum = 0
eef868d1 975
aad2ba95 976 * $capabilities[2] ->contextlevel = 1000
8ba412da 977 ->module = 0 // changed from SITEID in 1.8 (??)
bbbf2d40 978 ->capability = post_messages
979 ->id = 1
980 ->sum = -9000
981
aad2ba95 982 * $capabilittes[3] ->contextlevel = 3000
bbbf2d40 983 ->module = course
984 ->capability = view_course_activities
985 ->id = 25
986 ->sum = 1
987
aad2ba95 988 * $capabilittes[4] ->contextlevel = 3000
bbbf2d40 989 ->module = course
990 ->capability = view_course_activities
991 ->id = 26
992 ->sum = 0 (this is another course)
eef868d1 993
aad2ba95 994 * $capabilities[5] ->contextlevel = 3050
bbbf2d40 995 ->module = course
996 ->capability = view_course_activities
997 ->id = 25 (override in course 25)
998 ->sum = -1
999 * ....
1000 * now we proceed to write the session array, going from top to bottom
1001 * at anypoint, we need to go up and check parent to look for prohibit
1002 */
1003 // print_object($capabilities);
1004
1005 /* This is where we write to the actualy capabilities array
1006 * what we need to do from here on is
1007 * going down the array from lowest level to highest level
1008 * 1) recursively check for prohibit,
1009 * if any, we write prohibit
1010 * else, we write the value
1011 * 2) at an override level, we overwrite current level
1012 * if it's not set to prohibit already, and if different
1013 * ........ that should be it ........
1014 */
efb58884 1015
1016 // This is the flag used for detecting the current context level. Since we are going through
1017 // the array in ascending order of context level. For normal capabilities, there should only
1018 // be 1 value per (capability, contextlevel, context), because they are already summed. But,
1019 // for overrides, since we are processing them separate, we need to sum the relevcant entries.
1020 // We set this flag when we hit a new level.
1021 // If the flag is already set, we keep adding (summing), otherwise, we just override previous
1022 // settings (from lower level contexts)
1023 $capflags = array(); // (contextid, contextlevel, capability)
98882637 1024 $usercap = array(); // for other user's capabilities
bbbf2d40 1025 foreach ($capabilities as $capability) {
1026
9fccc080 1027 if (!$context = get_context_instance_by_id($capability->id2)) {
7bfa3101 1028 continue; // incorrect stale context
1029 }
0468976c 1030
41811960 1031 if (!empty($otheruserid)) { // we are pulling out other user's capabilities, do not write to session
eef868d1 1032
0468976c 1033 if (capability_prohibits($capability->capability, $context, $capability->sum, $usercap)) {
9fccc080 1034 $usercap[$capability->id2][$capability->capability] = CAP_PROHIBIT;
98882637 1035 continue;
1036 }
efb58884 1037 if (isset($usercap[$capability->id2][$capability->capability])) { // use isset because it can be sum 0
1038 if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) {
1039 $usercap[$capability->id2][$capability->capability] += $capability->sum;
1040 } else { // else we override, and update flag
1041 $usercap[$capability->id2][$capability->capability] = $capability->sum;
1042 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
1043 }
9fccc080 1044 } else {
1045 $usercap[$capability->id2][$capability->capability] = $capability->sum;
efb58884 1046 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
9fccc080 1047 }
eef868d1 1048
98882637 1049 } else {
1050
0468976c 1051 if (capability_prohibits($capability->capability, $context, $capability->sum)) { // if any parent or parent's parent is set to prohibit
9fccc080 1052 $USER->capabilities[$capability->id2][$capability->capability] = CAP_PROHIBIT;
98882637 1053 continue;
1054 }
eef868d1 1055
98882637 1056 // if no parental prohibit set
1057 // just write to session, i am not sure this is correct yet
1058 // since 3050 shows up after 3000, and 3070 shows up after 3050,
1059 // it should be ok just to overwrite like this, provided that there's no
1060 // parental prohibits
98882637 1061 // we need to write even if it's 0, because it could be an inherit override
efb58884 1062 if (isset($USER->capabilities[$capability->id2][$capability->capability])) {
1063 if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) {
1064 $USER->capabilities[$capability->id2][$capability->capability] += $capability->sum;
1065 } else { // else we override, and update flag
1066 $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum;
1067 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
1068 }
9fccc080 1069 } else {
1070 $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum;
efb58884 1071 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
9fccc080 1072 }
98882637 1073 }
bbbf2d40 1074 }
eef868d1 1075
bbbf2d40 1076 // now we don't care about the huge array anymore, we can dispose it.
1077 unset($capabilities);
efb58884 1078 unset($capflags);
eef868d1 1079
dbe7e582 1080 if (!empty($otheruserid)) {
eef868d1 1081 return $usercap; // return the array
bbbf2d40 1082 }
2f1a4248 1083}
1084
1085
1086/*
1087 * A convenience function to completely load all the capabilities
1088 * for the current user. This is what gets called from login, for example.
1089 */
1090function load_all_capabilities() {
1091 global $USER;
1092
1093 if (empty($USER->username)) {
1094 return;
1095 }
bbbf2d40 1096
8e82745a 1097 unset($USER->mycourses); // Reset a cache used by get_my_courses
1098
2f1a4248 1099 load_user_capability(); // Load basic capabilities assigned to this user
1100
1101 if ($USER->username == 'guest') {
1102 load_guest_role(); // All non-guest users get this by default
1103 } else {
1104 load_defaultuser_role(); // All non-guest users get this by default
1105 }
bbbf2d40 1106}
1107
2f1a4248 1108
64026e8c 1109/*
1110 * Check all the login enrolment information for the given user object
eef868d1 1111 * by querying the enrolment plugins
64026e8c 1112 */
1113function check_enrolment_plugins(&$user) {
1114 global $CFG;
1115
e4ec4e41 1116 static $inprogress; // To prevent this function being called more than once in an invocation
1117
218eb651 1118 if (!empty($inprogress[$user->id])) {
e4ec4e41 1119 return;
1120 }
1121
218eb651 1122 $inprogress[$user->id] = true; // Set the flag
e4ec4e41 1123
64026e8c 1124 require_once($CFG->dirroot .'/enrol/enrol.class.php');
eef868d1 1125
64026e8c 1126 if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
1127 $plugins = array($CFG->enrol);
1128 }
1129
1130 foreach ($plugins as $plugin) {
1131 $enrol = enrolment_factory::factory($plugin);
1132 if (method_exists($enrol, 'setup_enrolments')) { /// Plugin supports Roles (Moodle 1.7 and later)
1133 $enrol->setup_enrolments($user);
1134 } else { /// Run legacy enrolment methods
1135 if (method_exists($enrol, 'get_student_courses')) {
1136 $enrol->get_student_courses($user);
1137 }
1138 if (method_exists($enrol, 'get_teacher_courses')) {
1139 $enrol->get_teacher_courses($user);
1140 }
1141
1142 /// deal with $user->students and $user->teachers stuff
1143 unset($user->student);
1144 unset($user->teacher);
1145 }
1146 unset($enrol);
1147 }
e4ec4e41 1148
218eb651 1149 unset($inprogress[$user->id]); // Unset the flag
64026e8c 1150}
1151
bbbf2d40 1152
1153/**
1154 * This is a recursive function that checks whether the capability in this
1155 * context, or the parent capabilities are set to prohibit.
1156 *
1157 * At this point, we can probably just use the values already set in the
1158 * session variable, since we are going down the level. Any prohit set in
1159 * parents would already reflect in the session.
1160 *
1161 * @param $capability - capability name
1162 * @param $sum - sum of all capabilities values
0468976c 1163 * @param $context - the context object
bbbf2d40 1164 * @param $array - when loading another user caps, their caps are not stored in session but an array
1165 */
0468976c 1166function capability_prohibits($capability, $context, $sum='', $array='') {
bbbf2d40 1167 global $USER;
0468976c 1168
0db6adc9 1169 // caching, mainly to save unnecessary sqls
1170 static $prohibits; //[capability][contextid]
1171 if (isset($prohibits[$capability][$context->id])) {
1172 return $prohibits[$capability][$context->id];
1173 }
1174
2176adf1 1175 if (empty($context->id)) {
0db6adc9 1176 $prohibits[$capability][$context->id] = false;
2176adf1 1177 return false;
1178 }
1179
1180 if (empty($capability)) {
0db6adc9 1181 $prohibits[$capability][$context->id] = false;
2176adf1 1182 return false;
1183 }
1184
819e5a70 1185 if ($sum < (CAP_PROHIBIT/2)) {
bbbf2d40 1186 // If this capability is set to prohibit.
0db6adc9 1187 $prohibits[$capability][$context->id] = true;
bbbf2d40 1188 return true;
1189 }
eef868d1 1190
819e5a70 1191 if (!empty($array)) {
eef868d1 1192 if (isset($array[$context->id][$capability])
819e5a70 1193 && $array[$context->id][$capability] < (CAP_PROHIBIT/2)) {
0db6adc9 1194 $prohibits[$capability][$context->id] = true;
98882637 1195 return true;
eef868d1 1196 }
bbbf2d40 1197 } else {
98882637 1198 // Else if set in session.
eef868d1 1199 if (isset($USER->capabilities[$context->id][$capability])
819e5a70 1200 && $USER->capabilities[$context->id][$capability] < (CAP_PROHIBIT/2)) {
0db6adc9 1201 $prohibits[$capability][$context->id] = true;
98882637 1202 return true;
1203 }
bbbf2d40 1204 }
aad2ba95 1205 switch ($context->contextlevel) {
eef868d1 1206
bbbf2d40 1207 case CONTEXT_SYSTEM:
1208 // By now it's a definite an inherit.
1209 return 0;
1210 break;
1211
1212 case CONTEXT_PERSONAL:
2176adf1 1213 $parent = get_context_instance(CONTEXT_SYSTEM);
0db6adc9 1214 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1215 return $prohibits[$capability][$context->id];
bbbf2d40 1216 break;
1217
4b10f08b 1218 case CONTEXT_USER:
2176adf1 1219 $parent = get_context_instance(CONTEXT_SYSTEM);
0db6adc9 1220 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1221 return $prohibits[$capability][$context->id];
bbbf2d40 1222 break;
1223
1224 case CONTEXT_COURSECAT:
1225 // Coursecat -> coursecat or site.
2176adf1 1226 if (!$coursecat = get_record('course_categories','id',$context->instanceid)) {
0db6adc9 1227 $prohibits[$capability][$context->id] = false;
2176adf1 1228 return false;
40a2a15f 1229 }
41811960 1230 if (!empty($coursecat->parent)) {
bbbf2d40 1231 // return parent value if exist.
1232 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1233 } else {
1234 // Return site value.
2176adf1 1235 $parent = get_context_instance(CONTEXT_SYSTEM);
bbbf2d40 1236 }
0db6adc9 1237 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1238 return $prohibits[$capability][$context->id];
bbbf2d40 1239 break;
1240
1241 case CONTEXT_COURSE:
1242 // 1 to 1 to course cat.
1243 // Find the course cat, and return its value.
2176adf1 1244 if (!$course = get_record('course','id',$context->instanceid)) {
0db6adc9 1245 $prohibits[$capability][$context->id] = false;
2176adf1 1246 return false;
1247 }
40a2a15f 1248 // Yu: Separating site and site course context
1249 if ($course->id == SITEID) {
1250 $parent = get_context_instance(CONTEXT_SYSTEM);
1251 } else {
1252 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1253 }
0db6adc9 1254 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1255 return $prohibits[$capability][$context->id];
bbbf2d40 1256 break;
1257
1258 case CONTEXT_GROUP:
1259 // 1 to 1 to course.
f3f7610c 1260 if (!$courseid = groups_get_course($context->instanceid)) {
0db6adc9 1261 $prohibits[$capability][$context->id] = false;
2176adf1 1262 return false;
1263 }
f3f7610c 1264 $parent = get_context_instance(CONTEXT_COURSE, $courseid);
0db6adc9 1265 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1266 return $prohibits[$capability][$context->id];
bbbf2d40 1267 break;
1268
1269 case CONTEXT_MODULE:
1270 // 1 to 1 to course.
2176adf1 1271 if (!$cm = get_record('course_modules','id',$context->instanceid)) {
0db6adc9 1272 $prohibits[$capability][$context->id] = false;
2176adf1 1273 return false;
1274 }
bbbf2d40 1275 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
0db6adc9 1276 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1277 return $prohibits[$capability][$context->id];
bbbf2d40 1278 break;
1279
1280 case CONTEXT_BLOCK:
1281 // 1 to 1 to course.
2176adf1 1282 if (!$block = get_record('block_instance','id',$context->instanceid)) {
0db6adc9 1283 $prohibits[$capability][$context->id] = false;
2176adf1 1284 return false;
1285 }
bbbf2d40 1286 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
0db6adc9 1287 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1288 return $prohibits[$capability][$context->id];
bbbf2d40 1289 break;
1290
1291 default:
2176adf1 1292 print_error('unknowncontext');
1293 return false;
bbbf2d40 1294 }
1295}
1296
1297
1298/**
1299 * A print form function. This should either grab all the capabilities from
1300 * files or a central table for that particular module instance, then present
1301 * them in check boxes. Only relevant capabilities should print for known
1302 * context.
1303 * @param $mod - module id of the mod
1304 */
1305function print_capabilities($modid=0) {
1306 global $CFG;
eef868d1 1307
bbbf2d40 1308 $capabilities = array();
1309
1310 if ($modid) {
1311 // We are in a module specific context.
1312
1313 // Get the mod's name.
1314 // Call the function that grabs the file and parse.
1315 $cm = get_record('course_modules', 'id', $modid);
1316 $module = get_record('modules', 'id', $cm->module);
eef868d1 1317
bbbf2d40 1318 } else {
1319 // Print all capabilities.
1320 foreach ($capabilities as $capability) {
1321 // Prints the check box component.
1322 }
1323 }
1324}
1325
1326
1327/**
1afecc03 1328 * Installs the roles system.
1329 * This function runs on a fresh install as well as on an upgrade from the old
1330 * hard-coded student/teacher/admin etc. roles to the new roles system.
bbbf2d40 1331 */
1afecc03 1332function moodle_install_roles() {
bbbf2d40 1333
1afecc03 1334 global $CFG, $db;
eef868d1 1335
459c1ff1 1336/// Create a system wide context for assignemnt.
21c9bace 1337 $systemcontext = $context = get_context_instance(CONTEXT_SYSTEM);
bbbf2d40 1338
1afecc03 1339
459c1ff1 1340/// Create default/legacy roles and capabilities.
1341/// (1 legacy capability per legacy role at system level).
1342
69aaada0 1343 $adminrole = create_role(addslashes(get_string('administrator')), 'admin',
1344 addslashes(get_string('administratordescription')), 'moodle/legacy:admin');
1345 $coursecreatorrole = create_role(addslashes(get_string('coursecreators')), 'coursecreator',
1346 addslashes(get_string('coursecreatorsdescription')), 'moodle/legacy:coursecreator');
1347 $editteacherrole = create_role(addslashes(get_string('defaultcourseteacher')), 'editingteacher',
1348 addslashes(get_string('defaultcourseteacherdescription')), 'moodle/legacy:editingteacher');
1349 $noneditteacherrole = create_role(addslashes(get_string('noneditingteacher')), 'teacher',
1350 addslashes(get_string('noneditingteacherdescription')), 'moodle/legacy:teacher');
1351 $studentrole = create_role(addslashes(get_string('defaultcoursestudent')), 'student',
1352 addslashes(get_string('defaultcoursestudentdescription')), 'moodle/legacy:student');
1353 $guestrole = create_role(addslashes(get_string('guest')), 'guest',
1354 addslashes(get_string('guestdescription')), 'moodle/legacy:guest');
c785d40a 1355 $userrole = create_role(addslashes(get_string('authenticateduser')), 'user',
1356 addslashes(get_string('authenticateduserdescription')), 'moodle/legacy:user');
2851ba9b 1357
17e5635c 1358/// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles
459c1ff1 1359
98882637 1360 if (!assign_capability('moodle/site:doanything', CAP_ALLOW, $adminrole, $systemcontext->id)) {
bbbf2d40 1361 error('Could not assign moodle/site:doanything to the admin role');
1362 }
250934b8 1363 if (!update_capabilities()) {
1364 error('Had trouble upgrading the core capabilities for the Roles System');
1365 }
1afecc03 1366
459c1ff1 1367/// Look inside user_admin, user_creator, user_teachers, user_students and
1368/// assign above new roles. If a user has both teacher and student role,
1369/// only teacher role is assigned. The assignment should be system level.
1370
1afecc03 1371 $dbtables = $db->MetaTables('TABLES');
eef868d1 1372
72da5046 1373/// Set up the progress bar
1374
1375 $usertables = array('user_admins', 'user_coursecreators', 'user_teachers', 'user_students');
1376
1377 $totalcount = $progresscount = 0;
1378 foreach ($usertables as $usertable) {
1379 if (in_array($CFG->prefix.$usertable, $dbtables)) {
1380 $totalcount += count_records($usertable);
1381 }
1382 }
1383
aae37b63 1384 print_progress(0, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1385
459c1ff1 1386/// Upgrade the admins.
1387/// Sort using id ASC, first one is primary admin.
1388
1afecc03 1389 if (in_array($CFG->prefix.'user_admins', $dbtables)) {
f1dcf000 1390 if ($rs = get_recordset_sql('SELECT * from '.$CFG->prefix.'user_admins ORDER BY ID ASC')) {
0f5dafff 1391 while ($admin = rs_fetch_next_record($rs)) {
1afecc03 1392 role_assign($adminrole, $admin->userid, 0, $systemcontext->id);
72da5046 1393 $progresscount++;
aae37b63 1394 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1395 }
0f5dafff 1396 rs_close($rs);
1afecc03 1397 }
1398 } else {
1399 // This is a fresh install.
bbbf2d40 1400 }
1afecc03 1401
1402
459c1ff1 1403/// Upgrade course creators.
1afecc03 1404 if (in_array($CFG->prefix.'user_coursecreators', $dbtables)) {
f1dcf000 1405 if ($rs = get_recordset('user_coursecreators')) {
0f5dafff 1406 while ($coursecreator = rs_fetch_next_record($rs)) {
56b4d70d 1407 role_assign($coursecreatorrole, $coursecreator->userid, 0, $systemcontext->id);
72da5046 1408 $progresscount++;
aae37b63 1409 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1410 }
0f5dafff 1411 rs_close($rs);
1afecc03 1412 }
bbbf2d40 1413 }
1414
1afecc03 1415
459c1ff1 1416/// Upgrade editting teachers and non-editting teachers.
1afecc03 1417 if (in_array($CFG->prefix.'user_teachers', $dbtables)) {
f1dcf000 1418 if ($rs = get_recordset('user_teachers')) {
0f5dafff 1419 while ($teacher = rs_fetch_next_record($rs)) {
d5511451 1420
1421 // removed code here to ignore site level assignments
1422 // since the contexts are separated now
1423
17d6a25e 1424 // populate the user_lastaccess table
ece4945b 1425 $access = new object();
17d6a25e 1426 $access->timeaccess = $teacher->timeaccess;
1427 $access->userid = $teacher->userid;
1428 $access->courseid = $teacher->course;
1429 insert_record('user_lastaccess', $access);
f1dcf000 1430
17d6a25e 1431 // assign the default student role
1afecc03 1432 $coursecontext = get_context_instance(CONTEXT_COURSE, $teacher->course); // needs cache
1433 if ($teacher->editall) { // editting teacher
1434 role_assign($editteacherrole, $teacher->userid, 0, $coursecontext->id);
1435 } else {
1436 role_assign($noneditteacherrole, $teacher->userid, 0, $coursecontext->id);
1437 }
72da5046 1438 $progresscount++;
aae37b63 1439 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1440 }
0f5dafff 1441 rs_close($rs);
bbbf2d40 1442 }
1443 }
1afecc03 1444
1445
459c1ff1 1446/// Upgrade students.
1afecc03 1447 if (in_array($CFG->prefix.'user_students', $dbtables)) {
f1dcf000 1448 if ($rs = get_recordset('user_students')) {
0f5dafff 1449 while ($student = rs_fetch_next_record($rs)) {
f1dcf000 1450
17d6a25e 1451 // populate the user_lastaccess table
f1dcf000 1452 $access = new object;
17d6a25e 1453 $access->timeaccess = $student->timeaccess;
1454 $access->userid = $student->userid;
1455 $access->courseid = $student->course;
1456 insert_record('user_lastaccess', $access);
f1dcf000 1457
17d6a25e 1458 // assign the default student role
1afecc03 1459 $coursecontext = get_context_instance(CONTEXT_COURSE, $student->course);
1460 role_assign($studentrole, $student->userid, 0, $coursecontext->id);
72da5046 1461 $progresscount++;
aae37b63 1462 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1463 }
0f5dafff 1464 rs_close($rs);
1afecc03 1465 }
bbbf2d40 1466 }
1afecc03 1467
1468
459c1ff1 1469/// Upgrade guest (only 1 entry).
1afecc03 1470 if ($guestuser = get_record('user', 'username', 'guest')) {
1471 role_assign($guestrole, $guestuser->id, 0, $systemcontext->id);
1472 }
aae37b63 1473 print_progress($totalcount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1474
459c1ff1 1475
1476/// Insert the correct records for legacy roles
945f88ca 1477 allow_assign($adminrole, $adminrole);
1478 allow_assign($adminrole, $coursecreatorrole);
1479 allow_assign($adminrole, $noneditteacherrole);
eef868d1 1480 allow_assign($adminrole, $editteacherrole);
945f88ca 1481 allow_assign($adminrole, $studentrole);
1482 allow_assign($adminrole, $guestrole);
eef868d1 1483
945f88ca 1484 allow_assign($coursecreatorrole, $noneditteacherrole);
1485 allow_assign($coursecreatorrole, $editteacherrole);
eef868d1 1486 allow_assign($coursecreatorrole, $studentrole);
945f88ca 1487 allow_assign($coursecreatorrole, $guestrole);
eef868d1 1488
1489 allow_assign($editteacherrole, $noneditteacherrole);
1490 allow_assign($editteacherrole, $studentrole);
945f88ca 1491 allow_assign($editteacherrole, $guestrole);
eef868d1 1492
459c1ff1 1493/// Set up default permissions for overrides
945f88ca 1494 allow_override($adminrole, $adminrole);
1495 allow_override($adminrole, $coursecreatorrole);
1496 allow_override($adminrole, $noneditteacherrole);
eef868d1 1497 allow_override($adminrole, $editteacherrole);
945f88ca 1498 allow_override($adminrole, $studentrole);
eef868d1 1499 allow_override($adminrole, $guestrole);
c785d40a 1500 allow_override($adminrole, $userrole);
1afecc03 1501
746a04c5 1502
459c1ff1 1503/// Delete the old user tables when we are done
1504
83ea392e 1505 drop_table(new XMLDBTable('user_students'));
1506 drop_table(new XMLDBTable('user_teachers'));
1507 drop_table(new XMLDBTable('user_coursecreators'));
1508 drop_table(new XMLDBTable('user_admins'));
459c1ff1 1509
bbbf2d40 1510}
1511
bbbf2d40 1512/**
1513 * Assign the defaults found in this capabality definition to roles that have
1514 * the corresponding legacy capabilities assigned to them.
1515 * @param $legacyperms - an array in the format (example):
1516 * 'guest' => CAP_PREVENT,
1517 * 'student' => CAP_ALLOW,
1518 * 'teacher' => CAP_ALLOW,
1519 * 'editingteacher' => CAP_ALLOW,
1520 * 'coursecreator' => CAP_ALLOW,
1521 * 'admin' => CAP_ALLOW
1522 * @return boolean - success or failure.
1523 */
1524function assign_legacy_capabilities($capability, $legacyperms) {
eef868d1 1525
bbbf2d40 1526 foreach ($legacyperms as $type => $perm) {
eef868d1 1527
21c9bace 1528 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
eef868d1 1529
bbbf2d40 1530 // The legacy capabilities are:
1531 // 'moodle/legacy:guest'
1532 // 'moodle/legacy:student'
1533 // 'moodle/legacy:teacher'
1534 // 'moodle/legacy:editingteacher'
1535 // 'moodle/legacy:coursecreator'
1536 // 'moodle/legacy:admin'
eef868d1 1537
2e85fffe 1538 if ($roles = get_roles_with_capability('moodle/legacy:'.$type, CAP_ALLOW)) {
1539 foreach ($roles as $role) {
1540 // Assign a site level capability.
1541 if (!assign_capability($capability, $perm, $role->id, $systemcontext->id)) {
1542 return false;
1543 }
bbbf2d40 1544 }
1545 }
1546 }
1547 return true;
1548}
1549
1550
cee0901c 1551/**
1552 * Checks to see if a capability is a legacy capability.
1553 * @param $capabilityname
1554 * @return boolean
1555 */
bbbf2d40 1556function islegacy($capabilityname) {
98882637 1557 if (strstr($capabilityname, 'legacy') === false) {
eef868d1 1558 return false;
98882637 1559 } else {
eef868d1 1560 return true;
98882637 1561 }
bbbf2d40 1562}
1563
cee0901c 1564
1565
1566/**********************************
bbbf2d40 1567 * Context Manipulation functions *
1568 **********************************/
1569
bbbf2d40 1570/**
9991d157 1571 * Create a new context record for use by all roles-related stuff
bbbf2d40 1572 * @param $level
1573 * @param $instanceid
3ca2dea5 1574 *
1575 * @return object newly created context (or existing one with a debug warning)
bbbf2d40 1576 */
aad2ba95 1577function create_context($contextlevel, $instanceid) {
3ca2dea5 1578 if (!$context = get_record('context','contextlevel',$contextlevel,'instanceid',$instanceid)) {
1579 if (!validate_context($contextlevel, $instanceid)) {
1580 debugging('Error: Invalid context creation request for level "'.s($contextlevel).'", instance "'.s($instanceid).'".');
1581 return NULL;
1582 }
8ba412da 1583 if ($contextlevel == CONTEXT_SYSTEM) {
1584 return create_system_context();
1585
1586 }
3ca2dea5 1587 $context = new object();
aad2ba95 1588 $context->contextlevel = $contextlevel;
bbbf2d40 1589 $context->instanceid = $instanceid;
3ca2dea5 1590 if ($id = insert_record('context',$context)) {
0db6adc9 1591 // we need to populate context_rel for every new context inserted
1592 $c = get_record('context','id',$id);
1593 insert_context_rel ($c);
1594 return $c;
3ca2dea5 1595 } else {
1596 debugging('Error: could not insert new context level "'.s($contextlevel).'", instance "'.s($instanceid).'".');
1597 return NULL;
1598 }
1599 } else {
1600 debugging('Warning: Context id "'.s($context->id).'" not created, because it already exists.');
1601 return $context;
bbbf2d40 1602 }
1603}
1604
8ba412da 1605/*
1606 * This hacky function is needed because we can not change system context instanceid using normal upgrade routine.
1607 */
1608function create_system_context() {
1609 if ($context = get_record('context', 'contextlevel', CONTEXT_SYSTEM, 'instanceid', SITEID)) {
1610 // we are going to change instanceid of system context to 0 now
1611 $context->instanceid = 0;
1612 update_record('context', $context);
1613 //context rel not affected
1614 return $context;
1615
1616 } else {
1617 $context = new object();
1618 $context->contextlevel = CONTEXT_SYSTEM;
1619 $context->instanceid = 0;
1620 if ($context->id = insert_record('context',$context)) {
1621 // we need not to populate context_rel for system context
1622 return $context;
1623 } else {
1624 debugging('Can not create system context');
1625 return NULL;
1626 }
1627 }
1628}
9991d157 1629/**
1630 * Create a new context record for use by all roles-related stuff
1631 * @param $level
1632 * @param $instanceid
3ca2dea5 1633 *
1634 * @return true if properly deleted
9991d157 1635 */
1636function delete_context($contextlevel, $instanceid) {
0db6adc9 1637 if ($context = get_context_instance($contextlevel, $instanceid)) {
1638 delete_records('context_rel', 'c2', $context->id); // might not be a parent
9991d157 1639 return delete_records('context', 'id', $context->id) &&
1640 delete_records('role_assignments', 'contextid', $context->id) &&
0db6adc9 1641 delete_records('role_capabilities', 'contextid', $context->id) &&
1642 delete_records('context_rel', 'c1', $context->id);
9991d157 1643 }
1644 return true;
1645}
1646
3ca2dea5 1647/**
1648 * Validate that object with instanceid really exists in given context level.
1649 *
1650 * return if instanceid object exists
1651 */
1652function validate_context($contextlevel, $instanceid) {
1653 switch ($contextlevel) {
1654
1655 case CONTEXT_SYSTEM:
8ba412da 1656 return ($instanceid == 0);
3ca2dea5 1657
1658 case CONTEXT_PERSONAL:
1659 return (boolean)count_records('user', 'id', $instanceid);
1660
1661 case CONTEXT_USER:
1662 return (boolean)count_records('user', 'id', $instanceid);
1663
1664 case CONTEXT_COURSECAT:
1cd3eba9 1665 if ($instanceid == 0) {
1666 return true; // site course category
1667 }
3ca2dea5 1668 return (boolean)count_records('course_categories', 'id', $instanceid);
1669
1670 case CONTEXT_COURSE:
1671 return (boolean)count_records('course', 'id', $instanceid);
1672
1673 case CONTEXT_GROUP:
f3f7610c
ML
1674 //return (boolean)count_records('groups_groups', 'id', $instanceid); //TODO:DONOTCOMMIT:
1675 return groups_group_exists($instanceid);
3ca2dea5 1676
1677 case CONTEXT_MODULE:
1678 return (boolean)count_records('course_modules', 'id', $instanceid);
1679
1680 case CONTEXT_BLOCK:
1681 return (boolean)count_records('block_instance', 'id', $instanceid);
1682
1683 default:
1684 return false;
1685 }
1686}
bbbf2d40 1687
1688/**
1689 * Get the context instance as an object. This function will create the
1690 * context instance if it does not exist yet.
1691 * @param $level
1692 * @param $instance
1693 */
8ba412da 1694function get_context_instance($contextlevel=NULL, $instance=0) {
e5605780 1695
51195e6f 1696 global $context_cache, $context_cache_id, $CONTEXT;
a36a3a3f 1697 static $allowed_contexts = array(CONTEXT_SYSTEM, CONTEXT_PERSONAL, CONTEXT_USER, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_GROUP, CONTEXT_MODULE, CONTEXT_BLOCK);
d9a35e12 1698
8ba412da 1699 // Yu: Separating site and site course context - removed CONTEXT_COURSE override when SITEID
b7cec865 1700
340ea4e8 1701/// If no level is supplied then return the current global context if there is one
aad2ba95 1702 if (empty($contextlevel)) {
340ea4e8 1703 if (empty($CONTEXT)) {
a36a3a3f 1704 //fatal error, code must be fixed
1705 error("Error: get_context_instance() called without a context");
340ea4e8 1706 } else {
1707 return $CONTEXT;
1708 }
e5605780 1709 }
1710
8ba412da 1711/// Backwards compatibility with obsoleted (CONTEXT_SYSTEM, SITEID)
1712 if ($contextlevel == CONTEXT_SYSTEM) {
1713 $instance = 0;
1714 }
1715
a36a3a3f 1716/// check allowed context levels
1717 if (!in_array($contextlevel, $allowed_contexts)) {
7bfa3101 1718 // fatal error, code must be fixed - probably typo or switched parameters
a36a3a3f 1719 error('Error: get_context_instance() called with incorrect context level "'.s($contextlevel).'"');
1720 }
1721
340ea4e8 1722/// Check the cache
aad2ba95 1723 if (isset($context_cache[$contextlevel][$instance])) { // Already cached
1724 return $context_cache[$contextlevel][$instance];
e5605780 1725 }
1726
340ea4e8 1727/// Get it from the database, or create it
aad2ba95 1728 if (!$context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance)) {
1729 create_context($contextlevel, $instance);
1730 $context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance);
e5605780 1731 }
1732
ccfc5ecc 1733/// Only add to cache if context isn't empty.
1734 if (!empty($context)) {
aad2ba95 1735 $context_cache[$contextlevel][$instance] = $context; // Cache it for later
ccfc5ecc 1736 $context_cache_id[$context->id] = $context; // Cache it for later
1737 }
0468976c 1738
bbbf2d40 1739 return $context;
1740}
1741
cee0901c 1742
340ea4e8 1743/**
1744 * Get a context instance as an object, from a given id.
1745 * @param $id
1746 */
1747function get_context_instance_by_id($id) {
1748
d9a35e12 1749 global $context_cache, $context_cache_id;
1750
340ea4e8 1751 if (isset($context_cache_id[$id])) { // Already cached
75e84883 1752 return $context_cache_id[$id];
340ea4e8 1753 }
1754
1755 if ($context = get_record('context', 'id', $id)) { // Update the cache and return
aad2ba95 1756 $context_cache[$context->contextlevel][$context->instanceid] = $context;
340ea4e8 1757 $context_cache_id[$context->id] = $context;
1758 return $context;
1759 }
1760
1761 return false;
1762}
1763
bbbf2d40 1764
8737be58 1765/**
1766 * Get the local override (if any) for a given capability in a role in a context
1767 * @param $roleid
0468976c 1768 * @param $contextid
1769 * @param $capability
8737be58 1770 */
1771function get_local_override($roleid, $contextid, $capability) {
1772 return get_record('role_capabilities', 'roleid', $roleid, 'capability', $capability, 'contextid', $contextid);
1773}
1774
1775
bbbf2d40 1776
1777/************************************
1778 * DB TABLE RELATED FUNCTIONS *
1779 ************************************/
1780
cee0901c 1781/**
bbbf2d40 1782 * function that creates a role
1783 * @param name - role name
31f26796 1784 * @param shortname - role short name
bbbf2d40 1785 * @param description - role description
1786 * @param legacy - optional legacy capability
1787 * @return id or false
1788 */
8420bee9 1789function create_role($name, $shortname, $description, $legacy='') {
eef868d1 1790
98882637 1791 // check for duplicate role name
eef868d1 1792
98882637 1793 if ($role = get_record('role','name', $name)) {
eef868d1 1794 error('there is already a role with this name!');
98882637 1795 }
eef868d1 1796
31f26796 1797 if ($role = get_record('role','shortname', $shortname)) {
eef868d1 1798 error('there is already a role with this shortname!');
31f26796 1799 }
1800
b5959f30 1801 $role = new object();
98882637 1802 $role->name = $name;
31f26796 1803 $role->shortname = $shortname;
98882637 1804 $role->description = $description;
eef868d1 1805
8420bee9 1806 //find free sortorder number
1807 $role->sortorder = count_records('role');
1808 while (get_record('role','sortorder', $role->sortorder)) {
1809 $role->sortorder += 1;
b5959f30 1810 }
1811
21c9bace 1812 if (!$context = get_context_instance(CONTEXT_SYSTEM)) {
1813 return false;
1814 }
eef868d1 1815
98882637 1816 if ($id = insert_record('role', $role)) {
eef868d1 1817 if ($legacy) {
1818 assign_capability($legacy, CAP_ALLOW, $id, $context->id);
98882637 1819 }
eef868d1 1820
ec7a8b79 1821 /// By default, users with role:manage at site level
1822 /// should be able to assign users to this new role, and override this new role's capabilities
eef868d1 1823
ec7a8b79 1824 // find all admin roles
e46c0987 1825 if ($adminroles = get_roles_with_capability('moodle/role:manage', CAP_ALLOW, $context)) {
1826 // foreach admin role
1827 foreach ($adminroles as $arole) {
1828 // write allow_assign and allow_overrid
1829 allow_assign($arole->id, $id);
eef868d1 1830 allow_override($arole->id, $id);
e46c0987 1831 }
ec7a8b79 1832 }
eef868d1 1833
98882637 1834 return $id;
1835 } else {
eef868d1 1836 return false;
98882637 1837 }
eef868d1 1838
bbbf2d40 1839}
1840
8420bee9 1841/**
1842 * function that deletes a role and cleanups up after it
1843 * @param roleid - id of role to delete
1844 * @return success
1845 */
1846function delete_role($roleid) {
1847 $success = true;
1848
1849// first unssign all users
1850 if (!role_unassign($roleid)) {
1851 debugging("Error while unassigning all users from role with ID $roleid!");
1852 $success = false;
1853 }
1854
1855// cleanup all references to this role, ignore errors
1856 if ($success) {
1857 delete_records('role_capabilities', 'roleid', $roleid);
1858 delete_records('role_allow_assign', 'roleid', $roleid);
1859 delete_records('role_allow_assign', 'allowassign', $roleid);
1860 delete_records('role_allow_override', 'roleid', $roleid);
1861 delete_records('role_allow_override', 'allowoverride', $roleid);
1862 delete_records('role_names', 'roleid', $roleid);
1863 }
1864
1865// finally delete the role itself
1866 if ($success and !delete_records('role', 'id', $roleid)) {
ece4945b 1867 debugging("Could not delete role record with ID $roleid!");
8420bee9 1868 $success = false;
1869 }
1870
1871 return $success;
1872}
1873
bbbf2d40 1874/**
1875 * Function to write context specific overrides, or default capabilities.
1876 * @param module - string name
1877 * @param capability - string name
1878 * @param contextid - context id
1879 * @param roleid - role id
1880 * @param permission - int 1,-1 or -1000
96986241 1881 * should not be writing if permission is 0
bbbf2d40 1882 */
e7876c1e 1883function assign_capability($capability, $permission, $roleid, $contextid, $overwrite=false) {
eef868d1 1884
98882637 1885 global $USER;
eef868d1 1886
96986241 1887 if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set
eef868d1 1888 unassign_capability($capability, $roleid, $contextid);
96986241 1889 return true;
98882637 1890 }
eef868d1 1891
2e85fffe 1892 $existing = get_record('role_capabilities', 'contextid', $contextid, 'roleid', $roleid, 'capability', $capability);
e7876c1e 1893
1894 if ($existing and !$overwrite) { // We want to keep whatever is there already
1895 return true;
1896 }
1897
bbbf2d40 1898 $cap = new object;
1899 $cap->contextid = $contextid;
1900 $cap->roleid = $roleid;
1901 $cap->capability = $capability;
1902 $cap->permission = $permission;
1903 $cap->timemodified = time();
9db12da7 1904 $cap->modifierid = empty($USER->id) ? 0 : $USER->id;
e7876c1e 1905
1906 if ($existing) {
1907 $cap->id = $existing->id;
1908 return update_record('role_capabilities', $cap);
1909 } else {
1910 return insert_record('role_capabilities', $cap);
1911 }
bbbf2d40 1912}
1913
1914
1915/**
1916 * Unassign a capability from a role.
1917 * @param $roleid - the role id
1918 * @param $capability - the name of the capability
1919 * @return boolean - success or failure
1920 */
1921function unassign_capability($capability, $roleid, $contextid=NULL) {
eef868d1 1922
98882637 1923 if (isset($contextid)) {
1924 $status = delete_records('role_capabilities', 'capability', $capability,
1925 'roleid', $roleid, 'contextid', $contextid);
1926 } else {
1927 $status = delete_records('role_capabilities', 'capability', $capability,
1928 'roleid', $roleid);
1929 }
1930 return $status;
bbbf2d40 1931}
1932
1933
1934/**
759ac72d 1935 * Get the roles that have a given capability assigned to it. This function
1936 * does not resolve the actual permission of the capability. It just checks
1937 * for assignment only.
bbbf2d40 1938 * @param $capability - capability name (string)
1939 * @param $permission - optional, the permission defined for this capability
1940 * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT
1941 * @return array or role objects
1942 */
ec7a8b79 1943function get_roles_with_capability($capability, $permission=NULL, $context='') {
1944
bbbf2d40 1945 global $CFG;
eef868d1 1946
ec7a8b79 1947 if ($context) {
1948 if ($contexts = get_parent_contexts($context)) {
1949 $listofcontexts = '('.implode(',', $contexts).')';
1950 } else {
21c9bace 1951 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
eef868d1 1952 $listofcontexts = '('.$sitecontext->id.')'; // must be site
1953 }
42ac3ecf 1954 $contextstr = "AND (rc.contextid = '$context->id' OR rc.contextid IN $listofcontexts)";
ec7a8b79 1955 } else {
1956 $contextstr = '';
1957 }
eef868d1 1958
1959 $selectroles = "SELECT r.*
42ac3ecf 1960 FROM {$CFG->prefix}role r,
1961 {$CFG->prefix}role_capabilities rc
bbbf2d40 1962 WHERE rc.capability = '$capability'
ec7a8b79 1963 AND rc.roleid = r.id $contextstr";
bbbf2d40 1964
1965 if (isset($permission)) {
1966 $selectroles .= " AND rc.permission = '$permission'";
1967 }
1968 return get_records_sql($selectroles);
1969}
1970
1971
1972/**
a9e1c058 1973 * This function makes a role-assignment (a role for a user or group in a particular context)
bbbf2d40 1974 * @param $roleid - the role of the id
1975 * @param $userid - userid
1976 * @param $groupid - group id
1977 * @param $contextid - id of the context
1978 * @param $timestart - time this assignment becomes effective
1979 * @param $timeend - time this assignemnt ceases to be effective
1980 * @uses $USER
1981 * @return id - new id of the assigment
1982 */
f44152f4 1983function role_assign($roleid, $userid, $groupid, $contextid, $timestart=0, $timeend=0, $hidden=0, $enrol='manual') {
aa311411 1984 global $USER, $CFG;
bbbf2d40 1985
7eb0b60a 1986 debugging("Assign roleid $roleid userid $userid contextid $contextid", DEBUG_DEVELOPER);
bbbf2d40 1987
a9e1c058 1988/// Do some data validation
1989
bbbf2d40 1990 if (empty($roleid)) {
9d829c68 1991 debugging('Role ID not provided');
a9e1c058 1992 return false;
bbbf2d40 1993 }
1994
1995 if (empty($userid) && empty($groupid)) {
9d829c68 1996 debugging('Either userid or groupid must be provided');
a9e1c058 1997 return false;
bbbf2d40 1998 }
eef868d1 1999
7700027f 2000 if ($userid && !record_exists('user', 'id', $userid)) {
82396e5b 2001 debugging('User ID '.intval($userid).' does not exist!');
7700027f 2002 return false;
2003 }
bbbf2d40 2004
f3f7610c 2005 if ($groupid && !groups_group_exists($groupid)) {
82396e5b 2006 debugging('Group ID '.intval($groupid).' does not exist!');
dc411d1b 2007 return false;
2008 }
2009
7700027f 2010 if (!$context = get_context_instance_by_id($contextid)) {
82396e5b 2011 debugging('Context ID '.intval($contextid).' does not exist!');
a9e1c058 2012 return false;
bbbf2d40 2013 }
2014
a9e1c058 2015 if (($timestart and $timeend) and ($timestart > $timeend)) {
9d829c68 2016 debugging('The end time can not be earlier than the start time');
a9e1c058 2017 return false;
2018 }
2019
7700027f 2020
a9e1c058 2021/// Check for existing entry
2022 if ($userid) {
7700027f 2023 $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'userid', $userid);
a9e1c058 2024 } else {
7700027f 2025 $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'groupid', $groupid);
a9e1c058 2026 }
2027
9ebcb4d2 2028
a9e1c058 2029 $newra = new object;
bbbf2d40 2030
a9e1c058 2031 if (empty($ra)) { // Create a new entry
2032 $newra->roleid = $roleid;
7700027f 2033 $newra->contextid = $context->id;
a9e1c058 2034 $newra->userid = $userid;
a9e1c058 2035 $newra->hidden = $hidden;
f44152f4 2036 $newra->enrol = $enrol;
a9e1c058 2037 $newra->timestart = $timestart;
2038 $newra->timeend = $timeend;
2039 $newra->timemodified = time();
115faa2f 2040 $newra->modifierid = empty($USER->id) ? 0 : $USER->id;
a9e1c058 2041
9ebcb4d2 2042 $success = insert_record('role_assignments', $newra);
a9e1c058 2043
2044 } else { // We already have one, just update it
2045
2046 $newra->id = $ra->id;
2047 $newra->hidden = $hidden;
f44152f4 2048 $newra->enrol = $enrol;
a9e1c058 2049 $newra->timestart = $timestart;
2050 $newra->timeend = $timeend;
2051 $newra->timemodified = time();
115faa2f 2052 $newra->modifierid = empty($USER->id) ? 0 : $USER->id;
a9e1c058 2053
9ebcb4d2 2054 $success = update_record('role_assignments', $newra);
2055 }
2056
7700027f 2057 if ($success) { /// Role was assigned, so do some other things
2058
2059 /// If the user is the current user, then reload the capabilities too.
2060 if (!empty($USER->id) && $USER->id == $userid) {
2f1a4248 2061 load_all_capabilities();
7700027f 2062 }
9b5d7a46 2063
0f161e1f 2064 /// Ask all the modules if anything needs to be done for this user
2065 if ($mods = get_list_of_plugins('mod')) {
2066 foreach ($mods as $mod) {
2067 include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php');
2068 $functionname = $mod.'_role_assign';
2069 if (function_exists($functionname)) {
2070 $functionname($userid, $context);
2071 }
2072 }
2073 }
2074
2075 /// Make sure they have an entry in user_lastaccess for courses they can access
2076 // role_add_lastaccess_entries($userid, $context);
a9e1c058 2077 }
eef868d1 2078
4e5f3064 2079 /// now handle metacourse role assignments if in course context
aad2ba95 2080 if ($success and $context->contextlevel == CONTEXT_COURSE) {
4e5f3064 2081 if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) {
2082 foreach ($parents as $parent) {
1aad4310 2083 sync_metacourse($parent->parent_course);
4e5f3064 2084 }
2085 }
2086 }
6eb4f823 2087
2088 return $success;
bbbf2d40 2089}
2090
2091
2092/**
1dc1f037 2093 * Deletes one or more role assignments. You must specify at least one parameter.
bbbf2d40 2094 * @param $roleid
2095 * @param $userid
2096 * @param $groupid
2097 * @param $contextid
2098 * @return boolean - success or failure
2099 */
1dc1f037 2100function role_unassign($roleid=0, $userid=0, $groupid=0, $contextid=0) {
d74067e8 2101
2102 global $USER, $CFG;
eef868d1 2103
4e5f3064 2104 $success = true;
d74067e8 2105
1dc1f037 2106 $args = array('roleid', 'userid', 'groupid', 'contextid');
2107 $select = array();
2108 foreach ($args as $arg) {
2109 if ($$arg) {
2110 $select[] = $arg.' = '.$$arg;
2111 }
2112 }
d74067e8 2113
1dc1f037 2114 if ($select) {
4e5f3064 2115 if ($ras = get_records_select('role_assignments', implode(' AND ', $select))) {
2116 $mods = get_list_of_plugins('mod');
2117 foreach($ras as $ra) {
86e2c51d 2118 /// infinite loop protection when deleting recursively
2119 if (!$ra = get_record('role_assignments', 'id', $ra->id)) {
2120 continue;
2121 }
4e5f3064 2122 $success = delete_records('role_assignments', 'id', $ra->id) and $success;
86e2c51d 2123
4e5f3064 2124 /// If the user is the current user, then reload the capabilities too.
2125 if (!empty($USER->id) && $USER->id == $ra->userid) {
2f1a4248 2126 load_all_capabilities();
4e5f3064 2127 }
2128 $context = get_record('context', 'id', $ra->contextid);
0f161e1f 2129
2130 /// Ask all the modules if anything needs to be done for this user
4e5f3064 2131 foreach ($mods as $mod) {
2132 include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php');
2133 $functionname = $mod.'_role_unassign';
2134 if (function_exists($functionname)) {
2135 $functionname($ra->userid, $context); // watch out, $context might be NULL if something goes wrong
2136 }
2137 }
2138
2139 /// now handle metacourse role unassigment and removing from goups if in course context
aad2ba95 2140 if (!empty($context) and $context->contextlevel == CONTEXT_COURSE) {
4e5f3064 2141 //remove from groups when user has no role
2142 $roles = get_user_roles($context, $ra->userid, true);
2143 if (empty($roles)) {
2144 if ($groups = get_groups($context->instanceid, $ra->userid)) {
2145 foreach ($groups as $group) {
2146 delete_records('groups_members', 'groupid', $group->id, 'userid', $ra->userid);
2147 }
2148 }
2149 }
1aad4310 2150 //unassign roles in metacourses if needed
4e5f3064 2151 if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) {
2152 foreach ($parents as $parent) {
1aad4310 2153 sync_metacourse($parent->parent_course);
0f161e1f 2154 }
2155 }
0f161e1f 2156 }
2157 }
d74067e8 2158 }
1dc1f037 2159 }
4e5f3064 2160
2161 return $success;
bbbf2d40 2162}
2163
eef868d1 2164/*
2165 * A convenience function to take care of the common case where you
b963384f 2166 * just want to enrol someone using the default role into a course
2167 *
2168 * @param object $course
2169 * @param object $user
2170 * @param string $enrol - the plugin used to do this enrolment
2171 */
2172function enrol_into_course($course, $user, $enrol) {
2173
2174 if ($course->enrolperiod) {
2175 $timestart = time();
2176 $timeend = time() + $course->enrolperiod;
2177 } else {
2178 $timestart = $timeend = 0;
2179 }
2180
2181 if ($role = get_default_course_role($course)) {
c4381ef5 2182
2183 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2184
e2183037 2185 if (!role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, $enrol)) {
b963384f 2186 return false;
2187 }
eef868d1 2188
b963384f 2189 email_welcome_message_to_user($course, $user);
eef868d1 2190
b963384f 2191 add_to_log($course->id, 'course', 'enrol', 'view.php?id='.$course->id, $user->id);
2192
2193 return true;
2194 }
2195
2196 return false;
2197}
2198
0f161e1f 2199/**
2200 * Add last access times to user_lastaccess as required
2201 * @param $userid
2202 * @param $context
2203 * @return boolean - success or failure
2204 */
2205function role_add_lastaccess_entries($userid, $context) {
2206
2207 global $USER, $CFG;
2208
aad2ba95 2209 if (empty($context->contextlevel)) {
0f161e1f 2210 return false;
2211 }
2212
2213 $lastaccess = new object; // Reusable object below
2214 $lastaccess->userid = $userid;
2215 $lastaccess->timeaccess = 0;
2216
aad2ba95 2217 switch ($context->contextlevel) {
0f161e1f 2218
2219 case CONTEXT_SYSTEM: // For the whole site
2220 if ($courses = get_record('course')) {
2221 foreach ($courses as $course) {
2222 $lastaccess->courseid = $course->id;
2223 role_set_lastaccess($lastaccess);
2224 }
2225 }
2226 break;
2227
2228 case CONTEXT_CATEGORY: // For a whole category
2229 if ($courses = get_record('course', 'category', $context->instanceid)) {
2230 foreach ($courses as $course) {
2231 $lastaccess->courseid = $course->id;
2232 role_set_lastaccess($lastaccess);
2233 }
2234 }
2235 if ($categories = get_record('course_categories', 'parent', $context->instanceid)) {
2236 foreach ($categories as $category) {
2237 $subcontext = get_context_instance(CONTEXT_CATEGORY, $category->id);
2238 role_add_lastaccess_entries($userid, $subcontext);
2239 }
2240 }
2241 break;
eef868d1 2242
0f161e1f 2243
2244 case CONTEXT_COURSE: // For a whole course
2245 if ($course = get_record('course', 'id', $context->instanceid)) {
2246 $lastaccess->courseid = $course->id;
2247 role_set_lastaccess($lastaccess);
2248 }
2249 break;
2250 }
2251}
2252
2253/**
2254 * Delete last access times from user_lastaccess as required
2255 * @param $userid
2256 * @param $context
2257 * @return boolean - success or failure
2258 */
2259function role_remove_lastaccess_entries($userid, $context) {
2260
2261 global $USER, $CFG;
2262
2263}
2264
bbbf2d40 2265
2266/**
2267 * Loads the capability definitions for the component (from file). If no
2268 * capabilities are defined for the component, we simply return an empty array.
2269 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2270 * @return array of capabilities
2271 */
2272function load_capability_def($component) {
2273 global $CFG;
2274
2275 if ($component == 'moodle') {
2276 $defpath = $CFG->libdir.'/db/access.php';
2277 $varprefix = 'moodle';
2278 } else {
0c4d9f49 2279 $compparts = explode('/', $component);
eef868d1 2280
0c4d9f49 2281 if ($compparts[0] == 'block') {
2282 // Blocks are an exception. Blocks directory is 'blocks', and not
2283 // 'block'. So we need to jump through hoops.
2284 $defpath = $CFG->dirroot.'/'.$compparts[0].
2285 's/'.$compparts[1].'/db/access.php';
2286 $varprefix = $compparts[0].'_'.$compparts[1];
ae628043 2287 } else if ($compparts[0] == 'format') {
2288 // Similar to the above, course formats are 'format' while they
2289 // are stored in 'course/format'.
2290 $defpath = $CFG->dirroot.'/course/'.$component.'/db/access.php';
2291 $varprefix = $compparts[0].'_'.$compparts[1];
0c4d9f49 2292 } else {
2293 $defpath = $CFG->dirroot.'/'.$component.'/db/access.php';
2294 $varprefix = str_replace('/', '_', $component);
2295 }
bbbf2d40 2296 }
2297 $capabilities = array();
eef868d1 2298
bbbf2d40 2299 if (file_exists($defpath)) {
dc268b2f 2300 require($defpath);
bbbf2d40 2301 $capabilities = ${$varprefix.'_capabilities'};
2302 }
2303 return $capabilities;
2304}
2305
2306
2307/**
2308 * Gets the capabilities that have been cached in the database for this
2309 * component.
2310 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2311 * @return array of capabilities
2312 */
2313function get_cached_capabilities($component='moodle') {
2314 if ($component == 'moodle') {
2315 $storedcaps = get_records_select('capabilities',
2316 "name LIKE 'moodle/%:%'");
2317 } else {
2318 $storedcaps = get_records_select('capabilities',
2319 "name LIKE '$component:%'");
2320 }
2321 return $storedcaps;
2322}
2323
2324
2325/**
2326 * Updates the capabilities table with the component capability definitions.
2327 * If no parameters are given, the function updates the core moodle
2328 * capabilities.
2329 *
2330 * Note that the absence of the db/access.php capabilities definition file
2331 * will cause any stored capabilities for the component to be removed from
eef868d1 2332 * the database.
bbbf2d40 2333 *
2334 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2335 * @return boolean
2336 */
2337function update_capabilities($component='moodle') {
eef868d1 2338
bbbf2d40 2339 $storedcaps = array();
be4486da 2340
2341 $filecaps = load_capability_def($component);
bbbf2d40 2342 $cachedcaps = get_cached_capabilities($component);
2343 if ($cachedcaps) {
2344 foreach ($cachedcaps as $cachedcap) {
2345 array_push($storedcaps, $cachedcap->name);
17e5635c 2346 // update risk bitmasks in existing capabilities if needed
be4486da 2347 if (array_key_exists($cachedcap->name, $filecaps)) {
2348 if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) {
2b531945 2349 $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified
be4486da 2350 }
2351 if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) {
2352 $updatecap = new object;
2353 $updatecap->id = $cachedcap->id;
2354 $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask'];
2355 if (!update_record('capabilities', $updatecap)) {
2356 return false;
2357 }
2358 }
2359 }
bbbf2d40 2360 }
2361 }
be4486da 2362
bbbf2d40 2363 // Are there new capabilities in the file definition?
2364 $newcaps = array();
eef868d1 2365
bbbf2d40 2366 foreach ($filecaps as $filecap => $def) {
eef868d1 2367 if (!$storedcaps ||
bbbf2d40 2368 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
2b531945 2369 if (!array_key_exists('riskbitmask', $def)) {
2370 $def['riskbitmask'] = 0; // no risk if not specified
2371 }
bbbf2d40 2372 $newcaps[$filecap] = $def;
2373 }
2374 }
2375 // Add new capabilities to the stored definition.
2376 foreach ($newcaps as $capname => $capdef) {
2377 $capability = new object;
2378 $capability->name = $capname;
2379 $capability->captype = $capdef['captype'];
2380 $capability->contextlevel = $capdef['contextlevel'];
2381 $capability->component = $component;
be4486da 2382 $capability->riskbitmask = $capdef['riskbitmask'];
eef868d1 2383
bbbf2d40 2384 if (!insert_record('capabilities', $capability, false, 'id')) {
2385 return false;
2386 }
eef868d1 2387
bbbf2d40 2388 // Do we need to assign the new capabilities to roles that have the
2389 // legacy capabilities moodle/legacy:* as well?
2390 if (isset($capdef['legacy']) && is_array($capdef['legacy']) &&
2391 !assign_legacy_capabilities($capname, $capdef['legacy'])) {
2e85fffe 2392 notify('Could not assign legacy capabilities for '.$capname);
bbbf2d40 2393 }
2394 }
2395 // Are there any capabilities that have been removed from the file
2396 // definition that we need to delete from the stored capabilities and
2397 // role assignments?
2398 capabilities_cleanup($component, $filecaps);
eef868d1 2399
bbbf2d40 2400 return true;
2401}
2402
2403
2404/**
2405 * Deletes cached capabilities that are no longer needed by the component.
2406 * Also unassigns these capabilities from any roles that have them.
2407 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2408 * @param $newcapdef - array of the new capability definitions that will be
2409 * compared with the cached capabilities
2410 * @return int - number of deprecated capabilities that have been removed
2411 */
2412function capabilities_cleanup($component, $newcapdef=NULL) {
eef868d1 2413
bbbf2d40 2414 $removedcount = 0;
eef868d1 2415
bbbf2d40 2416 if ($cachedcaps = get_cached_capabilities($component)) {
2417 foreach ($cachedcaps as $cachedcap) {
2418 if (empty($newcapdef) ||
2419 array_key_exists($cachedcap->name, $newcapdef) === false) {
eef868d1 2420
bbbf2d40 2421 // Remove from capabilities cache.
2422 if (!delete_records('capabilities', 'name', $cachedcap->name)) {
2423 error('Could not delete deprecated capability '.$cachedcap->name);
2424 } else {
2425 $removedcount++;
2426 }
2427 // Delete from roles.
2428 if($roles = get_roles_with_capability($cachedcap->name)) {
2429 foreach($roles as $role) {
46943f7b 2430 if (!unassign_capability($cachedcap->name, $role->id)) {
bbbf2d40 2431 error('Could not unassign deprecated capability '.
2432 $cachedcap->name.' from role '.$role->name);
2433 }
2434 }
2435 }
2436 } // End if.
2437 }
2438 }
2439 return $removedcount;
2440}
2441
2442
2443
cee0901c 2444/****************
2445 * UI FUNCTIONS *
2446 ****************/
bbbf2d40 2447
2448
2449/**
2450 * prints human readable context identifier.
2451 */
0468976c 2452function print_context_name($context) {
340ea4e8 2453
ec0810ee 2454 $name = '';
aad2ba95 2455 switch ($context->contextlevel) {
ec0810ee 2456
bbbf2d40 2457 case CONTEXT_SYSTEM: // by now it's a definite an inherit
ec0810ee 2458 $name = get_string('site');
340ea4e8 2459 break;
bbbf2d40 2460
2461 case CONTEXT_PERSONAL:
ec0810ee 2462 $name = get_string('personal');
340ea4e8 2463 break;
2464
4b10f08b 2465 case CONTEXT_USER:
ec0810ee 2466 if ($user = get_record('user', 'id', $context->instanceid)) {
2467 $name = get_string('user').': '.fullname($user);
2468 }
340ea4e8 2469 break;
2470
bbbf2d40 2471 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
ec0810ee 2472 if ($category = get_record('course_categories', 'id', $context->instanceid)) {
2473 $name = get_string('category').': '.$category->name;
2474 }
340ea4e8 2475 break;
bbbf2d40 2476
2477 case CONTEXT_COURSE: // 1 to 1 to course cat
ec0810ee 2478 if ($course = get_record('course', 'id', $context->instanceid)) {
2479 $name = get_string('course').': '.$course->fullname;
2480 }
340ea4e8 2481 break;
bbbf2d40 2482
2483 case CONTEXT_GROUP: // 1 to 1 to course
f3f7610c
ML
2484 if ($name = groups_get_group_name($context->instanceid)) {
2485 $name = get_string('group').': '. $name;
ec0810ee 2486 }
340ea4e8 2487 break;
bbbf2d40 2488
2489 case CONTEXT_MODULE: // 1 to 1 to course
98882637 2490 if ($cm = get_record('course_modules','id',$context->instanceid)) {
2491 if ($module = get_record('modules','id',$cm->module)) {
2492 if ($mod = get_record($module->name, 'id', $cm->instance)) {
ec0810ee 2493 $name = get_string('activitymodule').': '.$mod->name;
98882637 2494 }
ec0810ee 2495 }
2496 }
340ea4e8 2497 break;
bbbf2d40 2498
2499 case CONTEXT_BLOCK: // 1 to 1 to course
98882637 2500 if ($blockinstance = get_record('block_instance','id',$context->instanceid)) {
2501 if ($block = get_record('block','id',$blockinstance->blockid)) {
91be52d7 2502 global $CFG;
2503 require_once("$CFG->dirroot/blocks/moodleblock.class.php");
2504 require_once("$CFG->dirroot/blocks/$block->name/block_$block->name.php");
2505 $blockname = "block_$block->name";
2506 if ($blockobject = new $blockname()) {
2507 $name = $blockobject->title.' ('.get_string('block').')';
2508 }
ec0810ee 2509 }
2510 }
340ea4e8 2511 break;
bbbf2d40 2512
2513 default:
2514 error ('This is an unknown context!');
340ea4e8 2515 return false;
2516
2517 }
340ea4e8 2518 return $name;
bbbf2d40 2519}
2520
2521
2522/**
eef868d1 2523 * Extracts the relevant capabilities given a contextid.
bbbf2d40 2524 * All case based, example an instance of forum context.
2525 * Will fetch all forum related capabilities, while course contexts
2526 * Will fetch all capabilities
0468976c 2527 * @param object context
bbbf2d40 2528 * @return array();
2529 *
2530 * capabilities
2531 * `name` varchar(150) NOT NULL,
2532 * `captype` varchar(50) NOT NULL,
2533 * `contextlevel` int(10) NOT NULL,
2534 * `component` varchar(100) NOT NULL,
2535 */
0468976c 2536function fetch_context_capabilities($context) {
eef868d1 2537
98882637 2538 global $CFG;
bbbf2d40 2539
2540 $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display
eef868d1 2541
aad2ba95 2542 switch ($context->contextlevel) {
bbbf2d40 2543
98882637 2544 case CONTEXT_SYSTEM: // all
2545 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2546 break;
2547
2548 case CONTEXT_PERSONAL:
0a8a95c9 2549 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_PERSONAL;
bbbf2d40 2550 break;
eef868d1 2551
4b10f08b 2552 case CONTEXT_USER:
8020a016 2553 $SQL = "SELECT *
2554 FROM {$CFG->prefix}capabilities
2555 WHERE contextlevel = ".CONTEXT_USER;
bbbf2d40 2556 break;
eef868d1 2557
bbbf2d40 2558 case CONTEXT_COURSECAT: // all
98882637 2559 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2560 break;
2561
2562 case CONTEXT_COURSE: // all
98882637 2563 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2564 break;
2565
2566 case CONTEXT_GROUP: // group caps
2567 break;
2568
2569 case CONTEXT_MODULE: // mod caps
98882637 2570 $cm = get_record('course_modules', 'id', $context->instanceid);
2571 $module = get_record('modules', 'id', $cm->module);
eef868d1 2572
98882637 2573 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE."
2574 and component = 'mod/$module->name'";
bbbf2d40 2575 break;
2576
2577 case CONTEXT_BLOCK: // block caps
98882637 2578 $cb = get_record('block_instance', 'id', $context->instanceid);
2579 $block = get_record('block', 'id', $cb->blockid);
eef868d1 2580
98882637 2581 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK."
2582 and component = 'block/$block->name'";
bbbf2d40 2583 break;
2584
2585 default:
2586 return false;
2587 }
2588
16e2e2f3 2589 if (!$records = get_records_sql($SQL.' '.$sort)) {
2590 $records = array();
2591 }
ba8d8027 2592
2593/// the rest of code is a bit hacky, think twice before modifying it :-(
69eb59f2 2594
2595 // special sorting of core system capabiltites and enrollments
e9c82dca 2596 if (in_array($context->contextlevel, array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE))) {
69eb59f2 2597 $first = array();
2598 foreach ($records as $key=>$record) {
2599 if (preg_match('|^moodle/|', $record->name) and $record->contextlevel == CONTEXT_SYSTEM) {
2600 $first[$key] = $record;
2601 unset($records[$key]);
2602 } else if (count($first)){
2603 break;
2604 }
2605 }
2606 if (count($first)) {
2607 $records = $first + $records; // merge the two arrays keeping the keys
2608 }
ba8d8027 2609 } else {
2610 $contextindependentcaps = fetch_context_independent_capabilities();
2611 $records = array_merge($contextindependentcaps, $records);
69eb59f2 2612 }
ba8d8027 2613
bbbf2d40 2614 return $records;
eef868d1 2615
bbbf2d40 2616}
2617
2618
759ac72d 2619/**
2620 * Gets the context-independent capabilities that should be overrridable in
2621 * any context.
2622 * @return array of capability records from the capabilities table.
2623 */
2624function fetch_context_independent_capabilities() {
eef868d1 2625
17e5635c 2626 //only CONTEXT_SYSTEM capabilities here or it will break the hack in fetch_context_capabilities()
759ac72d 2627 $contextindependentcaps = array(
2628 'moodle/site:accessallgroups'
2629 );
2630
2631 $records = array();
eef868d1 2632
759ac72d 2633 foreach ($contextindependentcaps as $capname) {
2634 $record = get_record('capabilities', 'name', $capname);
2635 array_push($records, $record);
2636 }
2637 return $records;
2638}
2639
2640
bbbf2d40 2641/**
2642 * This function pulls out all the resolved capabilities (overrides and
759ac72d 2643 * defaults) of a role used in capability overrides in contexts at a given
bbbf2d40 2644 * context.
0a8a95c9 2645 * @param obj $context
bbbf2d40 2646 * @param int $roleid
dc558690 2647 * @param bool self - if set to true, resolve till this level, else stop at immediate parent level
bbbf2d40 2648 * @return array
2649 */
1648afb2 2650function role_context_capabilities($roleid, $context, $cap='') {
dc558690 2651 global $CFG;
eef868d1 2652
8521d83a 2653 $contexts = get_parent_contexts($context);
2654 $contexts[] = $context->id;
98882637 2655 $contexts = '('.implode(',', $contexts).')';
eef868d1 2656
1648afb2 2657 if ($cap) {
e4697bf7 2658 $search = " AND rc.capability = '$cap' ";
1648afb2 2659 } else {
eef868d1 2660 $search = '';
1648afb2 2661 }
eef868d1 2662
2663 $SQL = "SELECT rc.*
2664 FROM {$CFG->prefix}role_capabilities rc,
dc558690 2665 {$CFG->prefix}context c
2666 WHERE rc.contextid in $contexts
2667 AND rc.roleid = $roleid
2668 AND rc.contextid = c.id $search
aad2ba95 2669 ORDER BY c.contextlevel DESC,
eef868d1 2670 rc.capability DESC";
759ac72d 2671
98882637 2672 $capabilities = array();
eef868d1 2673
4729012f 2674 if ($records = get_records_sql($SQL)) {
2675 // We are traversing via reverse order.
2676 foreach ($records as $record) {
2677 // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
2678 if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
2679 $capabilities[$record->capability] = $record->permission;
eef868d1 2680 }
4729012f 2681 }
98882637 2682 }
2683 return $capabilities;
bbbf2d40 2684}
2685
bbbf2d40 2686/**
eef868d1 2687 * Recursive function which, given a context, find all parent context ids,
bbbf2d40 2688 * and return the array in reverse order, i.e. parent first, then grand
2689 * parent, etc.
2690 * @param object $context
2691 * @return array()
2692 */
bbbf2d40 2693function get_parent_contexts($context) {
759ac72d 2694
1cd03601 2695 static $pcontexts; // cache
2696 if (isset($pcontexts[$context->id])) {
2697 return ($pcontexts[$context->id]);
2698 }
2699
aad2ba95 2700 switch ($context->contextlevel) {
bbbf2d40 2701
2702 case CONTEXT_SYSTEM: // no parent
957861f7 2703 return array();
bbbf2d40 2704 break;
2705
2706 case CONTEXT_PERSONAL:
21c9bace 2707 if (!$parent = get_context_instance(CONTEXT_SYSTEM)) {
957861f7 2708 return array();
2709 } else {
1cd03601 2710 $res = array($parent->id);
0de75c4c 2711 $pcontexts[$context->id] = $res;
1cd03601 2712 return $res;
957861f7 2713 }
bbbf2d40 2714 break;
eef868d1 2715
4b10f08b 2716 case CONTEXT_USER:
21c9bace 2717 if (!$parent = get_context_instance(CONTEXT_SYSTEM)) {
957861f7 2718 return array();
2719 } else {
1cd03601 2720 $res = array($parent->id);
0de75c4c 2721 $pcontexts[$context->id] = $res;
1cd03601 2722 return $res;
957861f7 2723 }
bbbf2d40 2724 break;
eef868d1 2725
bbbf2d40 2726 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
957861f7 2727 if (!$coursecat = get_record('course_categories','id',$context->instanceid)) {
2728 return array();
2729 }
c5ddc3fd 2730 if (!empty($coursecat->parent)) { // return parent value if exist
bbbf2d40 2731 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1cd03601 2732 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2733 $pcontexts[$context->id] = $res;
2734 return $res;
bbbf2d40 2735 } else { // else return site value
21c9bace 2736 $parent = get_context_instance(CONTEXT_SYSTEM);
1cd03601 2737 $res = array($parent->id);
2738 $pcontexts[$context->id] = $res;
2739 return $res;
bbbf2d40 2740 }
2741 break;
2742
2743 case CONTEXT_COURSE: // 1 to 1 to course cat
957861f7 2744 if (!$course = get_record('course','id',$context->instanceid)) {
2745 return array();
2746 }
1936c10e 2747 if ($course->id != SITEID) {
957861f7 2748 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1cd03601 2749 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2750 return $res;
957861f7 2751 } else {
40a2a15f 2752 // Yu: Separating site and site course context
2753 $parent = get_context_instance(CONTEXT_SYSTEM);
1cd03601 2754 $res = array($parent->id);
2755 $pcontexts[$context->id] = $res;
2756 return $res;
957861f7 2757 }
bbbf2d40 2758 break;
2759
2760 case CONTEXT_GROUP: // 1 to 1 to course
f3f7610c 2761 if (! $group = groups_get_group($context->instanceid)) {
957861f7 2762 return array();
2763 }
2764 if ($parent = get_context_instance(CONTEXT_COURSE, $group->courseid)) {
1cd03601 2765 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2766 $pcontexts[$context->id] = $res;
2767 return $res;
957861f7 2768 } else {
2769 return array();
2770 }
bbbf2d40 2771 break;
2772
2773 case CONTEXT_MODULE: // 1 to 1 to course
957861f7 2774 if (!$cm = get_record('course_modules','id',$context->instanceid)) {
2775 return array();
2776 }
2777 if ($parent = get_context_instance(CONTEXT_COURSE, $cm->course)) {
1cd03601 2778 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2779 $pcontexts[$context->id] = $res;
2780 return $res;
957861f7 2781 } else {
2782 return array();
2783 }
bbbf2d40 2784 break;
2785
2786 case CONTEXT_BLOCK: // 1 to 1 to course
957861f7 2787 if (!$block = get_record('block_instance','id',$context->instanceid)) {
2788 return array();
2789 }
2790 if ($parent = get_context_instance(CONTEXT_COURSE, $block->pageid)) {
1cd03601 2791 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2792 $pcontexts[$context->id] = $res;
2793 return $res;
957861f7 2794 } else {
2795 return array();
2796 }
bbbf2d40 2797 break;
2798
2799 default:
957861f7 2800 error('This is an unknown context!');
bbbf2d40 2801 return false;
2802 }
bbbf2d40 2803}
2804
759ac72d 2805
2806/**
2807 * Gets a string for sql calls, searching for stuff in this context or above
ea8158c1 2808 * @param object $context
2809 * @return string
2810 */
2811function get_related_contexts_string($context) {
2812 if ($parents = get_parent_contexts($context)) {
eef868d1 2813 return (' IN ('.$context->id.','.implode(',', $parents).')');
ea8158c1 2814 } else {
2815 return (' ='.$context->id);
2816 }
2817}
759ac72d 2818
2819
bbbf2d40 2820/**
2821 * This function gets the capability of a role in a given context.
2822 * It is needed when printing override forms.
2823 * @param int $contextid
bbbf2d40 2824 * @param string $capability
2825 * @param array $capabilities - array loaded using role_context_capabilities
2826 * @return int (allow, prevent, prohibit, inherit)
2827 */
bbbf2d40 2828function get_role_context_capability($contextid, $capability, $capabilities) {
759ac72d 2829 if (isset($capabilities[$contextid][$capability])) {
2830 return $capabilities[$contextid][$capability];
2831 }
2832 else {
2833 return false;
2834 }
bbbf2d40 2835}
2836
2837
cee0901c 2838/**
2839 * Returns the human-readable, translated version of the capability.
2840 * Basically a big switch statement.
2841 * @param $capabilityname - e.g. mod/choice:readresponses
2842 */
ceb83c70 2843function get_capability_string($capabilityname) {
eef868d1 2844
cee0901c 2845 // Typical capabilityname is mod/choice:readresponses
ceb83c70 2846
2847 $names = split('/', $capabilityname);
2848 $stringname = $names[1]; // choice:readresponses
eef868d1 2849 $components = split(':', $stringname);
ceb83c70 2850 $componentname = $components[0]; // choice
98882637 2851
2852 switch ($names[0]) {
2853 case 'mod':
ceb83c70 2854 $string = get_string($stringname, $componentname);
98882637 2855 break;
eef868d1 2856
98882637 2857 case 'block':
ceb83c70 2858 $string = get_string($stringname, 'block_'.$componentname);
98882637 2859 break;
ceb83c70 2860
98882637 2861 case 'moodle':
ceb83c70 2862 $string = get_string($stringname, 'role');
98882637 2863 break;
eef868d1 2864
98882637 2865 case 'enrol':
ceb83c70 2866 $string = get_string($stringname, 'enrol_'.$componentname);
eef868d1 2867 break;
ae628043 2868
2869 case 'format':
2870 $string = get_string($stringname, 'format_'.$componentname);
2871 break;
eef868d1 2872
98882637 2873 default:
ceb83c70 2874 $string = get_string($stringname);
eef868d1 2875 break;
2876
98882637 2877 }
ceb83c70 2878 return $string;
bbbf2d40 2879}
2880
2881
cee0901c 2882/**
2883 * This gets the mod/block/course/core etc strings.
2884 * @param $component
2885 * @param $contextlevel
2886 */
bbbf2d40 2887function get_component_string($component, $contextlevel) {
2888
98882637 2889 switch ($contextlevel) {
bbbf2d40 2890
98882637 2891 case CONTEXT_SYSTEM:
be382aaf 2892 if (preg_match('|^enrol/|', $component)) {
2893 $langname = str_replace('/', '_', $component);
2894 $string = get_string('enrolname', $langname);
f3652521 2895 } else if (preg_match('|^block/|', $component)) {
2896 $langname = str_replace('/', '_', $component);
2897 $string = get_string('blockname', $langname);
69eb59f2 2898 } else {
2899 $string = get_string('coresystem');
2900 }
bbbf2d40 2901 break;
2902
2903 case CONTEXT_PERSONAL:
98882637 2904 $string = get_string('personal');
bbbf2d40 2905 break;
2906
4b10f08b 2907 case CONTEXT_USER:
98882637 2908 $string = get_string('users');
bbbf2d40 2909 break;
2910
2911 case CONTEXT_COURSECAT:
98882637 2912 $string = get_string('categories');
bbbf2d40 2913 break;
2914
2915 case CONTEXT_COURSE:
98882637 2916 $string = get_string('course');
bbbf2d40 2917 break;
2918
2919 case CONTEXT_GROUP:
98882637 2920 $string = get_string('group');
bbbf2d40 2921 break;
2922
2923 case CONTEXT_MODULE:
98882637 2924 $string = get_string('modulename', basename($component));
bbbf2d40 2925 break;
2926
2927 case CONTEXT_BLOCK:
98882637 2928 $string = get_string('blockname', 'block_'.$component.'.php');
bbbf2d40 2929 break;
2930
2931 default:
2932 error ('This is an unknown context!');
2933 return false;
eef868d1 2934
98882637 2935 }
98882637 2936 return $string;
bbbf2d40 2937}
cee0901c 2938
759ac72d 2939/**
2940 * Gets the list of roles assigned to this context and up (parents)
945f88ca 2941 * @param object $context
3997cb40 2942 * @param view - set to true when roles are pulled for display only
2943 * this is so that we can filter roles with no visible
2944 * assignment, for example, you might want to "hide" all
2945 * course creators when browsing the course participants
2946 * list.
945f88ca 2947 * @return array
2948 */
3997cb40 2949function get_roles_used_in_context($context, $view = false) {
e4dd3222 2950
2951 global $CFG;
3997cb40 2952
2953 // filter for roles with all hidden assignments
2954 // no need to return when only pulling roles for reviewing
2955 // e.g. participants page.
d42c64ba 2956 $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':'';
2d9965e1 2957 $contextlist = get_related_contexts_string($context);
eef868d1 2958
759ac72d 2959 $sql = "SELECT DISTINCT r.id,
2960 r.name,
2961 r.shortname,
2962 r.sortorder
2963 FROM {$CFG->prefix}role_assignments ra,
eef868d1 2964 {$CFG->prefix}role r
2965 WHERE r.id = ra.roleid
759ac72d 2966 AND ra.contextid $contextlist
3997cb40 2967 $hiddensql
759ac72d 2968 ORDER BY r.sortorder ASC";
eef868d1 2969
759ac72d 2970 return get_records_sql($sql);
e4dd3222 2971}
2972
eef868d1 2973/** this function is used to print roles column in user profile page.
945f88ca 2974 * @param int userid
2975 * @param int contextid
2976 * @return string
2977 */
0a8a95c9 2978function get_user_roles_in_context($userid, $contextid){
2979 global $CFG;
eef868d1 2980
0a8a95c9 2981 $rolestring = '';
2982 $SQL = 'select * from '.$CFG->prefix.'role_assignments ra, '.$CFG->prefix.'role r where ra.userid='.$userid.' and ra.contextid='.$contextid.' and ra.roleid = r.id';
2983 if ($roles = get_records_sql($SQL)) {
2984 foreach ($roles as $userrole) {
2985 $rolestring .= '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$userrole->contextid.'&amp;roleid='.$userrole->roleid.'">'.$userrole->name.'</a>, ';
eef868d1 2986 }
2987
0a8a95c9 2988 }
2989 return rtrim($rolestring, ', ');
2990}
68c52526 2991
2992
945f88ca 2993/**
2994 * Checks if a user can override capabilities of a particular role in this context
2995 * @param object $context
2996 * @param int targetroleid - the id of the role you want to override
2997 * @return boolean
2998 */
68c52526 2999function user_can_override($context, $targetroleid) {
3000 // first check if user has override capability
3001 // if not return false;
3002 if (!has_capability('moodle/role:override', $context)) {
eef868d1 3003 return false;
68c52526 3004 }
3005 // pull out all active roles of this user from this context(or above)
c0614051 3006 if ($userroles = get_user_roles($context)) {
3007 foreach ($userroles as $userrole) {
3008 // if any in the role_allow_override table, then it's ok
3009 if (get_record('role_allow_override', 'roleid', $userrole->roleid, 'allowoverride', $targetroleid)) {
3010 return true;
3011 }
68c52526 3012 }
3013 }
eef868d1 3014
68c52526 3015 return false;
eef868d1 3016
68c52526 3017}
3018
945f88ca 3019/**
3020 * Checks if a user can assign users to a particular role in this context
3021 * @param object $context
3022 * @param int targetroleid - the id of the role you want to assign users to
3023 * @return boolean
3024 */
68c52526 3025function user_can_assign($context, $targetroleid) {
eef868d1 3026
68c52526 3027 // first check if user has override capability
3028 // if not return false;
3029 if (!has_capability('moodle/role:assign', $context)) {
eef868d1 3030 return false;
68c52526 3031 }
3032 // pull out all active roles of this user from this context(or above)
c0614051 3033 if ($userroles = get_user_roles($context)) {
3034 foreach ($userroles as $userrole) {
3035 // if any in the role_allow_override table, then it's ok
3036 if (get_record('role_allow_assign', 'roleid', $userrole->roleid, 'allowassign', $targetroleid)) {
3037 return true;
3038 }
68c52526 3039 }
3040 }
eef868d1 3041
3042 return false;
68c52526 3043}
3044
ece4945b 3045/** Returns all site roles in correct sort order.
3046 *
3047 */
3048function get_all_roles() {
3049 return get_records('role', '', '', 'sortorder ASC');
3050}
3051
945f88ca 3052/**
3053 * gets all the user roles assigned in this context, or higher contexts
3054 * this is mainly used when checking if a user can assign a role, or overriding a role
3055 * i.e. we need to know what this user holds, in order to verify against allow_assign and
3056 * allow_override tables
3057 * @param object $context
3058 * @param int $userid
b06334e8 3059 * @param view - set to true when roles are pulled for display only
3060 * this is so that we can filter roles with no visible
3061 * assignment, for example, you might want to "hide" all
3062 * course creators when browsing the course participants
3063 * list.
945f88ca 3064 * @return array
3065 */
b06334e8 3066function get_user_roles($context, $userid=0, $checkparentcontexts=true, $order='c.contextlevel DESC, r.sortorder ASC', $view=false) {
68c52526 3067
3068 global $USER, $CFG, $db;
c0614051 3069
3070 if (empty($userid)) {
3071 if (empty($USER->id)) {
3072 return array();
3073 }
3074 $userid = $USER->id;
3075 }
b06334e8 3076 // set up hidden sql
d42c64ba 3077 $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':'';
c0614051 3078
5b630667 3079 if ($checkparentcontexts && ($parents = get_parent_contexts($context))) {
3080 $contexts = ' ra.contextid IN ('.implode(',' , $parents).','.$context->id.')';
c0614051 3081 } else {
5b630667 3082 $contexts = ' ra.contextid = \''.$context->id.'\'';
c0614051 3083 }
3084
31f26796 3085 return get_records_sql('SELECT ra.*, r.name, r.shortname
5b630667 3086 FROM '.$CFG->prefix.'role_assignments ra,
ec6eb110 3087 '.$CFG->prefix.'role r,
3088 '.$CFG->prefix.'context c
c0614051 3089 WHERE ra.userid = '.$userid.
5b630667 3090 ' AND ra.roleid = r.id
ec6eb110 3091 AND ra.contextid = c.id
b06334e8 3092 AND '.$contexts . $hiddensql .
ae9761ec 3093 ' ORDER BY '.$order);
68c52526 3094}
3095
945f88ca 3096/**
eef868d1 3097 * Creates a record in the allow_override table
945f88ca 3098 * @param int sroleid - source roleid
3099 * @param int troleid - target roleid
3100 * @return int - id or false
3101 */
3102function allow_override($sroleid, $troleid) {
ece4945b 3103 $record = new object();
945f88ca 3104 $record->roleid = $sroleid;
3105 $record->allowoverride = $troleid;
3106 return insert_record('role_allow_override', $record);
3107}
3108
3109/**
eef868d1 3110 * Creates a record in the allow_assign table
945f88ca 3111 * @param int sroleid - source roleid
3112 * @param int troleid - target roleid
3113 * @return int - id or false
3114 */
3115function allow_assign($sroleid, $troleid) {
ff64aaea 3116 $record = new object;
945f88ca 3117 $record->roleid = $sroleid;
3118 $record->allowassign = $troleid;
3119 return insert_record('role_allow_assign', $record);
3120}
3121
3122/**
ff64aaea 3123 * Gets a list of roles that this user can assign in this context
945f88ca 3124 * @param object $context
3125 * @return array
3126 */
5e67946d 3127function get_assignable_roles ($context, $field="name") {
945f88ca 3128
945f88ca 3129 $options = array();
ff64aaea 3130
ece4945b 3131 if ($roles = get_all_roles()) {
ff64aaea 3132 foreach ($roles as $role) {
3133 if (user_can_assign($context, $role->id)) {
5e67946d 3134 $options[$role->id] = strip_tags(format_string($role->{$field}, true));
ff64aaea 3135 }
945f88ca 3136 }
3137 }
3138 return $options;
3139}
3140
3141/**
ff64aaea 3142 * Gets a list of roles that this user can override in this context
945f88ca 3143 * @param object $context
3144 * @return array
3145 */
3146function get_overridable_roles ($context) {
3147
945f88ca 3148 $options = array();
ff64aaea 3149
ece4945b 3150 if ($roles = get_all_roles()) {
ff64aaea 3151 foreach ($roles as $role) {
3152 if (user_can_override($context, $role->id)) {
65b0c132 3153 $options[$role->id] = strip_tags(format_string($role->name, true));
ff64aaea 3154 }
945f88ca 3155 }
ff64aaea 3156 }
eef868d1 3157
3158 return $options;
945f88ca 3159}
1648afb2 3160
b963384f 3161/*
3162 * Returns a role object that is the default role for new enrolments
3163 * in a given course
3164 *
eef868d1 3165 * @param object $course
b963384f 3166 * @return object $role
3167 */
3168function get_default_course_role($course) {
3169 global $CFG;
3170
3171/// First let's take the default role the course may have
3172 if (!empty($course->defaultrole)) {
3173 if ($role = get_record('role', 'id', $course->defaultrole)) {
3174 return $role;
3175 }
3176 }
3177
3178/// Otherwise the site setting should tell us
3179 if ($CFG->defaultcourseroleid) {
3180 if ($role = get_record('role', 'id', $CFG->defaultcourseroleid)) {
3181 return $role;
3182 }
3183 }
3184
3185/// It's unlikely we'll get here, but just in case, try and find a student role
3186 if ($studentroles = get_roles_with_capability('moodle/legacy:student', CAP_ALLOW)) {
3187 return array_shift($studentroles); /// Take the first one
3188 }
3189
3190 return NULL;
3191}
3192
1648afb2 3193
3194/**
3195 * who has this capability in this context
3196 * does not handling user level resolving!!!
40a2a15f 3197 * (!)pleaes note if $fields is empty this function attempts to get u.*
3198 * which can get rather large.
1648afb2 3199 * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly
3200