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