MDL-8343 reorganize frontpage settings in admintree - fixed installation bug and...
[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) {
bcf88cbb 876 while ($caprec = rs_fetch_next_record($rs)) {
877 $array = (array)$caprec;
9fccc080 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 }
9fccc080 887 $capabilities[] = $temprecord;
9fccc080 888 }
bcf88cbb 889 rs_close($rs);
b7e40271 890 }
bcf88cbb 891
9fccc080 892 // SQL for overrides
893 // this is take out because we have no way of making sure c1 is indeed related to c2 (parent)
894 // if we do not group by sum, it is possible to have multiple records of rc.capability, c1.id, c2.id, tuple having
895 // different values, we can maually sum it when we go through the list
0db6adc9 896
897 /*
898
9fccc080 899 $SQL2 = "SELECT rc.capability, c1.id as id1, c2.id as id2, (c1.contextlevel * 100 + c2.contextlevel) AS aggrlevel,
900 rc.permission AS sum
bbbf2d40 901 FROM
42ac3ecf 902 {$CFG->prefix}role_assignments ra,
903 {$CFG->prefix}role_capabilities rc,
904 {$CFG->prefix}context c1,
905 {$CFG->prefix}context c2
bbbf2d40 906 WHERE
d4649c76 907 ra.contextid=c1.id AND
eef868d1 908 ra.roleid=rc.roleid AND
909 ra.userid=$userid AND
910 rc.contextid=c2.id AND
5f70bcc3 911 $searchcontexts1
5f70bcc3 912 rc.contextid != $siteinstance->id
bbbf2d40 913 $capsearch
e38f38c3 914 $timesql
eef868d1 915
bbbf2d40 916 GROUP BY
21c9bace 917 rc.capability, (c1.contextlevel * 100 + c2.contextlevel), c1.id, c2.id, rc.permission
bbbf2d40 918 ORDER BY
75e84883 919 aggrlevel ASC
0db6adc9 920 ";*/
9fccc080 921
0db6adc9 922/*
9fccc080 923 if (!$rs = get_recordset_sql($SQL2)) {
75e84883 924 error("Query failed in load_user_capability.");
925 }
5cf38a57 926
bbbf2d40 927 if ($rs && $rs->RecordCount() > 0) {
bcf88cbb 928 while ($caprec = rs_fetch_next_record($rs)) {
929 $array = (array)$caprec;
75e84883 930 $temprecord = new object;
eef868d1 931
98882637 932 foreach ($array as $key=>$val) {
75e84883 933 if ($key == 'aggrlevel') {
aad2ba95 934 $temprecord->contextlevel = $val;
75e84883 935 } else {
936 $temprecord->{$key} = $val;
937 }
98882637 938 }
9fccc080 939 // for overrides, we have to make sure that context2 is a child of context1
940 // otherwise the combination makes no sense
0db6adc9 941 //if (is_parent_context($temprecord->id1, $temprecord->id2)) {
9fccc080 942 $capabilities[] = $temprecord;
0db6adc9 943 //} // only write if relevant
bbbf2d40 944 }
bcf88cbb 945 rs_close($rs);
bbbf2d40 946 }
0db6adc9 947
9fccc080 948 // this step sorts capabilities according to the contextlevel
949 // it is very important because the order matters when we
950 // go through each capabilities later. (i.e. higher level contextlevel
951 // will override lower contextlevel settings
952 usort($capabilities, 'roles_context_cmp');
0db6adc9 953*/
bbbf2d40 954 /* so up to this point we should have somethign like this
aad2ba95 955 * $capabilities[1] ->contextlevel = 1000
bbbf2d40 956 ->module = SITEID
957 ->capability = do_anything
958 ->id = 1 (id is the context id)
959 ->sum = 0
eef868d1 960
aad2ba95 961 * $capabilities[2] ->contextlevel = 1000
bbbf2d40 962 ->module = SITEID
963 ->capability = post_messages
964 ->id = 1
965 ->sum = -9000
966
aad2ba95 967 * $capabilittes[3] ->contextlevel = 3000
bbbf2d40 968 ->module = course
969 ->capability = view_course_activities
970 ->id = 25
971 ->sum = 1
972
aad2ba95 973 * $capabilittes[4] ->contextlevel = 3000
bbbf2d40 974 ->module = course
975 ->capability = view_course_activities
976 ->id = 26
977 ->sum = 0 (this is another course)
eef868d1 978
aad2ba95 979 * $capabilities[5] ->contextlevel = 3050
bbbf2d40 980 ->module = course
981 ->capability = view_course_activities
982 ->id = 25 (override in course 25)
983 ->sum = -1
984 * ....
985 * now we proceed to write the session array, going from top to bottom
986 * at anypoint, we need to go up and check parent to look for prohibit
987 */
988 // print_object($capabilities);
989
990 /* This is where we write to the actualy capabilities array
991 * what we need to do from here on is
992 * going down the array from lowest level to highest level
993 * 1) recursively check for prohibit,
994 * if any, we write prohibit
995 * else, we write the value
996 * 2) at an override level, we overwrite current level
997 * if it's not set to prohibit already, and if different
998 * ........ that should be it ........
999 */
efb58884 1000
1001 // This is the flag used for detecting the current context level. Since we are going through
1002 // the array in ascending order of context level. For normal capabilities, there should only
1003 // be 1 value per (capability, contextlevel, context), because they are already summed. But,
1004 // for overrides, since we are processing them separate, we need to sum the relevcant entries.
1005 // We set this flag when we hit a new level.
1006 // If the flag is already set, we keep adding (summing), otherwise, we just override previous
1007 // settings (from lower level contexts)
1008 $capflags = array(); // (contextid, contextlevel, capability)
98882637 1009 $usercap = array(); // for other user's capabilities
bbbf2d40 1010 foreach ($capabilities as $capability) {
1011
9fccc080 1012 if (!$context = get_context_instance_by_id($capability->id2)) {
7bfa3101 1013 continue; // incorrect stale context
1014 }
0468976c 1015
41811960 1016 if (!empty($otheruserid)) { // we are pulling out other user's capabilities, do not write to session
eef868d1 1017
0468976c 1018 if (capability_prohibits($capability->capability, $context, $capability->sum, $usercap)) {
9fccc080 1019 $usercap[$capability->id2][$capability->capability] = CAP_PROHIBIT;
98882637 1020 continue;
1021 }
efb58884 1022 if (isset($usercap[$capability->id2][$capability->capability])) { // use isset because it can be sum 0
1023 if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) {
1024 $usercap[$capability->id2][$capability->capability] += $capability->sum;
1025 } else { // else we override, and update flag
1026 $usercap[$capability->id2][$capability->capability] = $capability->sum;
1027 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
1028 }
9fccc080 1029 } else {
1030 $usercap[$capability->id2][$capability->capability] = $capability->sum;
efb58884 1031 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
9fccc080 1032 }
eef868d1 1033
98882637 1034 } else {
1035
0468976c 1036 if (capability_prohibits($capability->capability, $context, $capability->sum)) { // if any parent or parent's parent is set to prohibit
9fccc080 1037 $USER->capabilities[$capability->id2][$capability->capability] = CAP_PROHIBIT;
98882637 1038 continue;
1039 }
eef868d1 1040
98882637 1041 // if no parental prohibit set
1042 // just write to session, i am not sure this is correct yet
1043 // since 3050 shows up after 3000, and 3070 shows up after 3050,
1044 // it should be ok just to overwrite like this, provided that there's no
1045 // parental prohibits
98882637 1046 // we need to write even if it's 0, because it could be an inherit override
efb58884 1047 if (isset($USER->capabilities[$capability->id2][$capability->capability])) {
1048 if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) {
1049 $USER->capabilities[$capability->id2][$capability->capability] += $capability->sum;
1050 } else { // else we override, and update flag
1051 $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum;
1052 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
1053 }
9fccc080 1054 } else {
1055 $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum;
efb58884 1056 $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true;
9fccc080 1057 }
98882637 1058 }
bbbf2d40 1059 }
eef868d1 1060
bbbf2d40 1061 // now we don't care about the huge array anymore, we can dispose it.
1062 unset($capabilities);
efb58884 1063 unset($capflags);
eef868d1 1064
dbe7e582 1065 if (!empty($otheruserid)) {
eef868d1 1066 return $usercap; // return the array
bbbf2d40 1067 }
2f1a4248 1068}
1069
1070
1071/*
1072 * A convenience function to completely load all the capabilities
1073 * for the current user. This is what gets called from login, for example.
1074 */
1075function load_all_capabilities() {
1076 global $USER;
1077
1078 if (empty($USER->username)) {
1079 return;
1080 }
bbbf2d40 1081
8e82745a 1082 unset($USER->mycourses); // Reset a cache used by get_my_courses
1083
2f1a4248 1084 load_user_capability(); // Load basic capabilities assigned to this user
1085
1086 if ($USER->username == 'guest') {
1087 load_guest_role(); // All non-guest users get this by default
1088 } else {
1089 load_defaultuser_role(); // All non-guest users get this by default
1090 }
bbbf2d40 1091}
1092
2f1a4248 1093
64026e8c 1094/*
1095 * Check all the login enrolment information for the given user object
eef868d1 1096 * by querying the enrolment plugins
64026e8c 1097 */
1098function check_enrolment_plugins(&$user) {
1099 global $CFG;
1100
e4ec4e41 1101 static $inprogress; // To prevent this function being called more than once in an invocation
1102
218eb651 1103 if (!empty($inprogress[$user->id])) {
e4ec4e41 1104 return;
1105 }
1106
218eb651 1107 $inprogress[$user->id] = true; // Set the flag
e4ec4e41 1108
64026e8c 1109 require_once($CFG->dirroot .'/enrol/enrol.class.php');
eef868d1 1110
64026e8c 1111 if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
1112 $plugins = array($CFG->enrol);
1113 }
1114
1115 foreach ($plugins as $plugin) {
1116 $enrol = enrolment_factory::factory($plugin);
1117 if (method_exists($enrol, 'setup_enrolments')) { /// Plugin supports Roles (Moodle 1.7 and later)
1118 $enrol->setup_enrolments($user);
1119 } else { /// Run legacy enrolment methods
1120 if (method_exists($enrol, 'get_student_courses')) {
1121 $enrol->get_student_courses($user);
1122 }
1123 if (method_exists($enrol, 'get_teacher_courses')) {
1124 $enrol->get_teacher_courses($user);
1125 }
1126
1127 /// deal with $user->students and $user->teachers stuff
1128 unset($user->student);
1129 unset($user->teacher);
1130 }
1131 unset($enrol);
1132 }
e4ec4e41 1133
218eb651 1134 unset($inprogress[$user->id]); // Unset the flag
64026e8c 1135}
1136
bbbf2d40 1137
1138/**
1139 * This is a recursive function that checks whether the capability in this
1140 * context, or the parent capabilities are set to prohibit.
1141 *
1142 * At this point, we can probably just use the values already set in the
1143 * session variable, since we are going down the level. Any prohit set in
1144 * parents would already reflect in the session.
1145 *
1146 * @param $capability - capability name
1147 * @param $sum - sum of all capabilities values
0468976c 1148 * @param $context - the context object
bbbf2d40 1149 * @param $array - when loading another user caps, their caps are not stored in session but an array
1150 */
0468976c 1151function capability_prohibits($capability, $context, $sum='', $array='') {
bbbf2d40 1152 global $USER;
0468976c 1153
0db6adc9 1154 // caching, mainly to save unnecessary sqls
1155 static $prohibits; //[capability][contextid]
1156 if (isset($prohibits[$capability][$context->id])) {
1157 return $prohibits[$capability][$context->id];
1158 }
1159
2176adf1 1160 if (empty($context->id)) {
0db6adc9 1161 $prohibits[$capability][$context->id] = false;
2176adf1 1162 return false;
1163 }
1164
1165 if (empty($capability)) {
0db6adc9 1166 $prohibits[$capability][$context->id] = false;
2176adf1 1167 return false;
1168 }
1169
819e5a70 1170 if ($sum < (CAP_PROHIBIT/2)) {
bbbf2d40 1171 // If this capability is set to prohibit.
0db6adc9 1172 $prohibits[$capability][$context->id] = true;
bbbf2d40 1173 return true;
1174 }
eef868d1 1175
819e5a70 1176 if (!empty($array)) {
eef868d1 1177 if (isset($array[$context->id][$capability])
819e5a70 1178 && $array[$context->id][$capability] < (CAP_PROHIBIT/2)) {
0db6adc9 1179 $prohibits[$capability][$context->id] = true;
98882637 1180 return true;
eef868d1 1181 }
bbbf2d40 1182 } else {
98882637 1183 // Else if set in session.
eef868d1 1184 if (isset($USER->capabilities[$context->id][$capability])
819e5a70 1185 && $USER->capabilities[$context->id][$capability] < (CAP_PROHIBIT/2)) {
0db6adc9 1186 $prohibits[$capability][$context->id] = true;
98882637 1187 return true;
1188 }
bbbf2d40 1189 }
aad2ba95 1190 switch ($context->contextlevel) {
eef868d1 1191
bbbf2d40 1192 case CONTEXT_SYSTEM:
1193 // By now it's a definite an inherit.
1194 return 0;
1195 break;
1196
1197 case CONTEXT_PERSONAL:
2176adf1 1198 $parent = get_context_instance(CONTEXT_SYSTEM);
0db6adc9 1199 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1200 return $prohibits[$capability][$context->id];
bbbf2d40 1201 break;
1202
4b10f08b 1203 case CONTEXT_USER:
2176adf1 1204 $parent = get_context_instance(CONTEXT_SYSTEM);
0db6adc9 1205 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1206 return $prohibits[$capability][$context->id];
bbbf2d40 1207 break;
1208
1209 case CONTEXT_COURSECAT:
1210 // Coursecat -> coursecat or site.
2176adf1 1211 if (!$coursecat = get_record('course_categories','id',$context->instanceid)) {
0db6adc9 1212 $prohibits[$capability][$context->id] = false;
2176adf1 1213 return false;
40a2a15f 1214 }
41811960 1215 if (!empty($coursecat->parent)) {
bbbf2d40 1216 // return parent value if exist.
1217 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1218 } else {
1219 // Return site value.
2176adf1 1220 $parent = get_context_instance(CONTEXT_SYSTEM);
bbbf2d40 1221 }
0db6adc9 1222 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1223 return $prohibits[$capability][$context->id];
bbbf2d40 1224 break;
1225
1226 case CONTEXT_COURSE:
1227 // 1 to 1 to course cat.
1228 // Find the course cat, and return its value.
2176adf1 1229 if (!$course = get_record('course','id',$context->instanceid)) {
0db6adc9 1230 $prohibits[$capability][$context->id] = false;
2176adf1 1231 return false;
1232 }
40a2a15f 1233 // Yu: Separating site and site course context
1234 if ($course->id == SITEID) {
1235 $parent = get_context_instance(CONTEXT_SYSTEM);
1236 } else {
1237 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1238 }
0db6adc9 1239 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1240 return $prohibits[$capability][$context->id];
bbbf2d40 1241 break;
1242
1243 case CONTEXT_GROUP:
1244 // 1 to 1 to course.
f3f7610c 1245 if (!$courseid = groups_get_course($context->instanceid)) {
0db6adc9 1246 $prohibits[$capability][$context->id] = false;
2176adf1 1247 return false;
1248 }
f3f7610c 1249 $parent = get_context_instance(CONTEXT_COURSE, $courseid);
0db6adc9 1250 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1251 return $prohibits[$capability][$context->id];
bbbf2d40 1252 break;
1253
1254 case CONTEXT_MODULE:
1255 // 1 to 1 to course.
2176adf1 1256 if (!$cm = get_record('course_modules','id',$context->instanceid)) {
0db6adc9 1257 $prohibits[$capability][$context->id] = false;
2176adf1 1258 return false;
1259 }
bbbf2d40 1260 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
0db6adc9 1261 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1262 return $prohibits[$capability][$context->id];
bbbf2d40 1263 break;
1264
1265 case CONTEXT_BLOCK:
1266 // 1 to 1 to course.
2176adf1 1267 if (!$block = get_record('block_instance','id',$context->instanceid)) {
0db6adc9 1268 $prohibits[$capability][$context->id] = false;
2176adf1 1269 return false;
1270 }
bbbf2d40 1271 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
0db6adc9 1272 $prohibits[$capability][$context->id] = capability_prohibits($capability, $parent);
1273 return $prohibits[$capability][$context->id];
bbbf2d40 1274 break;
1275
1276 default:
2176adf1 1277 print_error('unknowncontext');
1278 return false;
bbbf2d40 1279 }
1280}
1281
1282
1283/**
1284 * A print form function. This should either grab all the capabilities from
1285 * files or a central table for that particular module instance, then present
1286 * them in check boxes. Only relevant capabilities should print for known
1287 * context.
1288 * @param $mod - module id of the mod
1289 */
1290function print_capabilities($modid=0) {
1291 global $CFG;
eef868d1 1292
bbbf2d40 1293 $capabilities = array();
1294
1295 if ($modid) {
1296 // We are in a module specific context.
1297
1298 // Get the mod's name.
1299 // Call the function that grabs the file and parse.
1300 $cm = get_record('course_modules', 'id', $modid);
1301 $module = get_record('modules', 'id', $cm->module);
eef868d1 1302
bbbf2d40 1303 } else {
1304 // Print all capabilities.
1305 foreach ($capabilities as $capability) {
1306 // Prints the check box component.
1307 }
1308 }
1309}
1310
1311
1312/**
1afecc03 1313 * Installs the roles system.
1314 * This function runs on a fresh install as well as on an upgrade from the old
1315 * hard-coded student/teacher/admin etc. roles to the new roles system.
bbbf2d40 1316 */
1afecc03 1317function moodle_install_roles() {
bbbf2d40 1318
1afecc03 1319 global $CFG, $db;
eef868d1 1320
459c1ff1 1321/// Create a system wide context for assignemnt.
21c9bace 1322 $systemcontext = $context = get_context_instance(CONTEXT_SYSTEM);
bbbf2d40 1323
1afecc03 1324
459c1ff1 1325/// Create default/legacy roles and capabilities.
1326/// (1 legacy capability per legacy role at system level).
1327
69aaada0 1328 $adminrole = create_role(addslashes(get_string('administrator')), 'admin',
1329 addslashes(get_string('administratordescription')), 'moodle/legacy:admin');
1330 $coursecreatorrole = create_role(addslashes(get_string('coursecreators')), 'coursecreator',
1331 addslashes(get_string('coursecreatorsdescription')), 'moodle/legacy:coursecreator');
1332 $editteacherrole = create_role(addslashes(get_string('defaultcourseteacher')), 'editingteacher',
1333 addslashes(get_string('defaultcourseteacherdescription')), 'moodle/legacy:editingteacher');
1334 $noneditteacherrole = create_role(addslashes(get_string('noneditingteacher')), 'teacher',
1335 addslashes(get_string('noneditingteacherdescription')), 'moodle/legacy:teacher');
1336 $studentrole = create_role(addslashes(get_string('defaultcoursestudent')), 'student',
1337 addslashes(get_string('defaultcoursestudentdescription')), 'moodle/legacy:student');
1338 $guestrole = create_role(addslashes(get_string('guest')), 'guest',
1339 addslashes(get_string('guestdescription')), 'moodle/legacy:guest');
2851ba9b 1340
17e5635c 1341/// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles
459c1ff1 1342
98882637 1343 if (!assign_capability('moodle/site:doanything', CAP_ALLOW, $adminrole, $systemcontext->id)) {
bbbf2d40 1344 error('Could not assign moodle/site:doanything to the admin role');
1345 }
250934b8 1346 if (!update_capabilities()) {
1347 error('Had trouble upgrading the core capabilities for the Roles System');
1348 }
1afecc03 1349
459c1ff1 1350/// Look inside user_admin, user_creator, user_teachers, user_students and
1351/// assign above new roles. If a user has both teacher and student role,
1352/// only teacher role is assigned. The assignment should be system level.
1353
1afecc03 1354 $dbtables = $db->MetaTables('TABLES');
eef868d1 1355
72da5046 1356/// Set up the progress bar
1357
1358 $usertables = array('user_admins', 'user_coursecreators', 'user_teachers', 'user_students');
1359
1360 $totalcount = $progresscount = 0;
1361 foreach ($usertables as $usertable) {
1362 if (in_array($CFG->prefix.$usertable, $dbtables)) {
1363 $totalcount += count_records($usertable);
1364 }
1365 }
1366
aae37b63 1367 print_progress(0, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1368
459c1ff1 1369/// Upgrade the admins.
1370/// Sort using id ASC, first one is primary admin.
1371
1afecc03 1372 if (in_array($CFG->prefix.'user_admins', $dbtables)) {
f1dcf000 1373 if ($rs = get_recordset_sql('SELECT * from '.$CFG->prefix.'user_admins ORDER BY ID ASC')) {
0f5dafff 1374 while ($admin = rs_fetch_next_record($rs)) {
1afecc03 1375 role_assign($adminrole, $admin->userid, 0, $systemcontext->id);
72da5046 1376 $progresscount++;
aae37b63 1377 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1378 }
0f5dafff 1379 rs_close($rs);
1afecc03 1380 }
1381 } else {
1382 // This is a fresh install.
bbbf2d40 1383 }
1afecc03 1384
1385
459c1ff1 1386/// Upgrade course creators.
1afecc03 1387 if (in_array($CFG->prefix.'user_coursecreators', $dbtables)) {
f1dcf000 1388 if ($rs = get_recordset('user_coursecreators')) {
0f5dafff 1389 while ($coursecreator = rs_fetch_next_record($rs)) {
56b4d70d 1390 role_assign($coursecreatorrole, $coursecreator->userid, 0, $systemcontext->id);
72da5046 1391 $progresscount++;
aae37b63 1392 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1393 }
0f5dafff 1394 rs_close($rs);
1afecc03 1395 }
bbbf2d40 1396 }
1397
1afecc03 1398
459c1ff1 1399/// Upgrade editting teachers and non-editting teachers.
1afecc03 1400 if (in_array($CFG->prefix.'user_teachers', $dbtables)) {
f1dcf000 1401 if ($rs = get_recordset('user_teachers')) {
0f5dafff 1402 while ($teacher = rs_fetch_next_record($rs)) {
d5511451 1403
1404 // removed code here to ignore site level assignments
1405 // since the contexts are separated now
1406
17d6a25e 1407 // populate the user_lastaccess table
ece4945b 1408 $access = new object();
17d6a25e 1409 $access->timeaccess = $teacher->timeaccess;
1410 $access->userid = $teacher->userid;
1411 $access->courseid = $teacher->course;
1412 insert_record('user_lastaccess', $access);
f1dcf000 1413
17d6a25e 1414 // assign the default student role
1afecc03 1415 $coursecontext = get_context_instance(CONTEXT_COURSE, $teacher->course); // needs cache
1416 if ($teacher->editall) { // editting teacher
1417 role_assign($editteacherrole, $teacher->userid, 0, $coursecontext->id);
1418 } else {
1419 role_assign($noneditteacherrole, $teacher->userid, 0, $coursecontext->id);
1420 }
72da5046 1421 $progresscount++;
aae37b63 1422 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1423 }
0f5dafff 1424 rs_close($rs);
bbbf2d40 1425 }
1426 }
1afecc03 1427
1428
459c1ff1 1429/// Upgrade students.
1afecc03 1430 if (in_array($CFG->prefix.'user_students', $dbtables)) {
f1dcf000 1431 if ($rs = get_recordset('user_students')) {
0f5dafff 1432 while ($student = rs_fetch_next_record($rs)) {
f1dcf000 1433
17d6a25e 1434 // populate the user_lastaccess table
f1dcf000 1435 $access = new object;
17d6a25e 1436 $access->timeaccess = $student->timeaccess;
1437 $access->userid = $student->userid;
1438 $access->courseid = $student->course;
1439 insert_record('user_lastaccess', $access);
f1dcf000 1440
17d6a25e 1441 // assign the default student role
1afecc03 1442 $coursecontext = get_context_instance(CONTEXT_COURSE, $student->course);
1443 role_assign($studentrole, $student->userid, 0, $coursecontext->id);
72da5046 1444 $progresscount++;
aae37b63 1445 print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1446 }
0f5dafff 1447 rs_close($rs);
1afecc03 1448 }
bbbf2d40 1449 }
1afecc03 1450
1451
459c1ff1 1452/// Upgrade guest (only 1 entry).
1afecc03 1453 if ($guestuser = get_record('user', 'username', 'guest')) {
1454 role_assign($guestrole, $guestuser->id, 0, $systemcontext->id);
1455 }
aae37b63 1456 print_progress($totalcount, $totalcount, 5, 1, 'Processing role assignments');
1afecc03 1457
459c1ff1 1458
1459/// Insert the correct records for legacy roles
945f88ca 1460 allow_assign($adminrole, $adminrole);
1461 allow_assign($adminrole, $coursecreatorrole);
1462 allow_assign($adminrole, $noneditteacherrole);
eef868d1 1463 allow_assign($adminrole, $editteacherrole);
945f88ca 1464 allow_assign($adminrole, $studentrole);
1465 allow_assign($adminrole, $guestrole);
eef868d1 1466
945f88ca 1467 allow_assign($coursecreatorrole, $noneditteacherrole);
1468 allow_assign($coursecreatorrole, $editteacherrole);
eef868d1 1469 allow_assign($coursecreatorrole, $studentrole);
945f88ca 1470 allow_assign($coursecreatorrole, $guestrole);
eef868d1 1471
1472 allow_assign($editteacherrole, $noneditteacherrole);
1473 allow_assign($editteacherrole, $studentrole);
945f88ca 1474 allow_assign($editteacherrole, $guestrole);
eef868d1 1475
459c1ff1 1476/// Set up default permissions for overrides
945f88ca 1477 allow_override($adminrole, $adminrole);
1478 allow_override($adminrole, $coursecreatorrole);
1479 allow_override($adminrole, $noneditteacherrole);
eef868d1 1480 allow_override($adminrole, $editteacherrole);
945f88ca 1481 allow_override($adminrole, $studentrole);
eef868d1 1482 allow_override($adminrole, $guestrole);
1afecc03 1483
746a04c5 1484
459c1ff1 1485/// Delete the old user tables when we are done
1486
83ea392e 1487 drop_table(new XMLDBTable('user_students'));
1488 drop_table(new XMLDBTable('user_teachers'));
1489 drop_table(new XMLDBTable('user_coursecreators'));
1490 drop_table(new XMLDBTable('user_admins'));
459c1ff1 1491
bbbf2d40 1492}
1493
bbbf2d40 1494/**
1495 * Assign the defaults found in this capabality definition to roles that have
1496 * the corresponding legacy capabilities assigned to them.
1497 * @param $legacyperms - an array in the format (example):
1498 * 'guest' => CAP_PREVENT,
1499 * 'student' => CAP_ALLOW,
1500 * 'teacher' => CAP_ALLOW,
1501 * 'editingteacher' => CAP_ALLOW,
1502 * 'coursecreator' => CAP_ALLOW,
1503 * 'admin' => CAP_ALLOW
1504 * @return boolean - success or failure.
1505 */
1506function assign_legacy_capabilities($capability, $legacyperms) {
eef868d1 1507
bbbf2d40 1508 foreach ($legacyperms as $type => $perm) {
eef868d1 1509
21c9bace 1510 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
eef868d1 1511
bbbf2d40 1512 // The legacy capabilities are:
1513 // 'moodle/legacy:guest'
1514 // 'moodle/legacy:student'
1515 // 'moodle/legacy:teacher'
1516 // 'moodle/legacy:editingteacher'
1517 // 'moodle/legacy:coursecreator'
1518 // 'moodle/legacy:admin'
eef868d1 1519
2e85fffe 1520 if ($roles = get_roles_with_capability('moodle/legacy:'.$type, CAP_ALLOW)) {
1521 foreach ($roles as $role) {
1522 // Assign a site level capability.
1523 if (!assign_capability($capability, $perm, $role->id, $systemcontext->id)) {
1524 return false;
1525 }
bbbf2d40 1526 }
1527 }
1528 }
1529 return true;
1530}
1531
1532
cee0901c 1533/**
1534 * Checks to see if a capability is a legacy capability.
1535 * @param $capabilityname
1536 * @return boolean
1537 */
bbbf2d40 1538function islegacy($capabilityname) {
98882637 1539 if (strstr($capabilityname, 'legacy') === false) {
eef868d1 1540 return false;
98882637 1541 } else {
eef868d1 1542 return true;
98882637 1543 }
bbbf2d40 1544}
1545
cee0901c 1546
1547
1548/**********************************
bbbf2d40 1549 * Context Manipulation functions *
1550 **********************************/
1551
bbbf2d40 1552/**
9991d157 1553 * Create a new context record for use by all roles-related stuff
bbbf2d40 1554 * @param $level
1555 * @param $instanceid
3ca2dea5 1556 *
1557 * @return object newly created context (or existing one with a debug warning)
bbbf2d40 1558 */
aad2ba95 1559function create_context($contextlevel, $instanceid) {
3ca2dea5 1560 if (!$context = get_record('context','contextlevel',$contextlevel,'instanceid',$instanceid)) {
1561 if (!validate_context($contextlevel, $instanceid)) {
1562 debugging('Error: Invalid context creation request for level "'.s($contextlevel).'", instance "'.s($instanceid).'".');
1563 return NULL;
1564 }
1565 $context = new object();
aad2ba95 1566 $context->contextlevel = $contextlevel;
bbbf2d40 1567 $context->instanceid = $instanceid;
3ca2dea5 1568 if ($id = insert_record('context',$context)) {
0db6adc9 1569 // we need to populate context_rel for every new context inserted
1570 $c = get_record('context','id',$id);
1571 insert_context_rel ($c);
1572 return $c;
3ca2dea5 1573 } else {
1574 debugging('Error: could not insert new context level "'.s($contextlevel).'", instance "'.s($instanceid).'".');
1575 return NULL;
1576 }
1577 } else {
1578 debugging('Warning: Context id "'.s($context->id).'" not created, because it already exists.');
1579 return $context;
bbbf2d40 1580 }
1581}
1582
9991d157 1583/**
1584 * Create a new context record for use by all roles-related stuff
1585 * @param $level
1586 * @param $instanceid
3ca2dea5 1587 *
1588 * @return true if properly deleted
9991d157 1589 */
1590function delete_context($contextlevel, $instanceid) {
0db6adc9 1591 if ($context = get_context_instance($contextlevel, $instanceid)) {
1592 delete_records('context_rel', 'c2', $context->id); // might not be a parent
9991d157 1593 return delete_records('context', 'id', $context->id) &&
1594 delete_records('role_assignments', 'contextid', $context->id) &&
0db6adc9 1595 delete_records('role_capabilities', 'contextid', $context->id) &&
1596 delete_records('context_rel', 'c1', $context->id);
9991d157 1597 }
1598 return true;
1599}
1600
3ca2dea5 1601/**
1602 * Validate that object with instanceid really exists in given context level.
1603 *
1604 * return if instanceid object exists
1605 */
1606function validate_context($contextlevel, $instanceid) {
1607 switch ($contextlevel) {
1608
1609 case CONTEXT_SYSTEM:
1610 return ($instanceid == SITEID);
1611
1612 case CONTEXT_PERSONAL:
1613 return (boolean)count_records('user', 'id', $instanceid);
1614
1615 case CONTEXT_USER:
1616 return (boolean)count_records('user', 'id', $instanceid);
1617
1618 case CONTEXT_COURSECAT:
1cd3eba9 1619 if ($instanceid == 0) {
1620 return true; // site course category
1621 }
3ca2dea5 1622 return (boolean)count_records('course_categories', 'id', $instanceid);
1623
1624 case CONTEXT_COURSE:
1625 return (boolean)count_records('course', 'id', $instanceid);
1626
1627 case CONTEXT_GROUP:
f3f7610c
ML
1628 //return (boolean)count_records('groups_groups', 'id', $instanceid); //TODO:DONOTCOMMIT:
1629 return groups_group_exists($instanceid);
3ca2dea5 1630
1631 case CONTEXT_MODULE:
1632 return (boolean)count_records('course_modules', 'id', $instanceid);
1633
1634 case CONTEXT_BLOCK:
1635 return (boolean)count_records('block_instance', 'id', $instanceid);
1636
1637 default:
1638 return false;
1639 }
1640}
bbbf2d40 1641
1642/**
1643 * Get the context instance as an object. This function will create the
1644 * context instance if it does not exist yet.
1645 * @param $level
1646 * @param $instance
1647 */
aad2ba95 1648function get_context_instance($contextlevel=NULL, $instance=SITEID) {
e5605780 1649
51195e6f 1650 global $context_cache, $context_cache_id, $CONTEXT;
a36a3a3f 1651 static $allowed_contexts = array(CONTEXT_SYSTEM, CONTEXT_PERSONAL, CONTEXT_USER, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_GROUP, CONTEXT_MODULE, CONTEXT_BLOCK);
d9a35e12 1652
b7cec865 1653 // This is really a systen context
40a2a15f 1654 // Yu: Separating site and site course context
1655 /*
b7cec865 1656 if ($contextlevel == CONTEXT_COURSE && $instance == SITEID) {
1657 $contextlevel = CONTEXT_SYSTEM;
40a2a15f 1658 }*/
b7cec865 1659
340ea4e8 1660/// If no level is supplied then return the current global context if there is one
aad2ba95 1661 if (empty($contextlevel)) {
340ea4e8 1662 if (empty($CONTEXT)) {
a36a3a3f 1663 //fatal error, code must be fixed
1664 error("Error: get_context_instance() called without a context");
340ea4e8 1665 } else {
1666 return $CONTEXT;
1667 }
e5605780 1668 }
1669
a36a3a3f 1670/// check allowed context levels
1671 if (!in_array($contextlevel, $allowed_contexts)) {
7bfa3101 1672 // fatal error, code must be fixed - probably typo or switched parameters
a36a3a3f 1673 error('Error: get_context_instance() called with incorrect context level "'.s($contextlevel).'"');
1674 }
1675
340ea4e8 1676/// Check the cache
aad2ba95 1677 if (isset($context_cache[$contextlevel][$instance])) { // Already cached
1678 return $context_cache[$contextlevel][$instance];
e5605780 1679 }
1680
340ea4e8 1681/// Get it from the database, or create it
aad2ba95 1682 if (!$context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance)) {
1683 create_context($contextlevel, $instance);
1684 $context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance);
e5605780 1685 }
1686
ccfc5ecc 1687/// Only add to cache if context isn't empty.
1688 if (!empty($context)) {
aad2ba95 1689 $context_cache[$contextlevel][$instance] = $context; // Cache it for later
ccfc5ecc 1690 $context_cache_id[$context->id] = $context; // Cache it for later
1691 }
0468976c 1692
bbbf2d40 1693 return $context;
1694}
1695
cee0901c 1696
340ea4e8 1697/**
1698 * Get a context instance as an object, from a given id.
1699 * @param $id
1700 */
1701function get_context_instance_by_id($id) {
1702
d9a35e12 1703 global $context_cache, $context_cache_id;
1704
340ea4e8 1705 if (isset($context_cache_id[$id])) { // Already cached
75e84883 1706 return $context_cache_id[$id];
340ea4e8 1707 }
1708
1709 if ($context = get_record('context', 'id', $id)) { // Update the cache and return
aad2ba95 1710 $context_cache[$context->contextlevel][$context->instanceid] = $context;
340ea4e8 1711 $context_cache_id[$context->id] = $context;
1712 return $context;
1713 }
1714
1715 return false;
1716}
1717
bbbf2d40 1718
8737be58 1719/**
1720 * Get the local override (if any) for a given capability in a role in a context
1721 * @param $roleid
0468976c 1722 * @param $contextid
1723 * @param $capability
8737be58 1724 */
1725function get_local_override($roleid, $contextid, $capability) {
1726 return get_record('role_capabilities', 'roleid', $roleid, 'capability', $capability, 'contextid', $contextid);
1727}
1728
1729
bbbf2d40 1730
1731/************************************
1732 * DB TABLE RELATED FUNCTIONS *
1733 ************************************/
1734
cee0901c 1735/**
bbbf2d40 1736 * function that creates a role
1737 * @param name - role name
31f26796 1738 * @param shortname - role short name
bbbf2d40 1739 * @param description - role description
1740 * @param legacy - optional legacy capability
1741 * @return id or false
1742 */
8420bee9 1743function create_role($name, $shortname, $description, $legacy='') {
eef868d1 1744
98882637 1745 // check for duplicate role name
eef868d1 1746
98882637 1747 if ($role = get_record('role','name', $name)) {
eef868d1 1748 error('there is already a role with this name!');
98882637 1749 }
eef868d1 1750
31f26796 1751 if ($role = get_record('role','shortname', $shortname)) {
eef868d1 1752 error('there is already a role with this shortname!');
31f26796 1753 }
1754
b5959f30 1755 $role = new object();
98882637 1756 $role->name = $name;
31f26796 1757 $role->shortname = $shortname;
98882637 1758 $role->description = $description;
eef868d1 1759
8420bee9 1760 //find free sortorder number
1761 $role->sortorder = count_records('role');
1762 while (get_record('role','sortorder', $role->sortorder)) {
1763 $role->sortorder += 1;
b5959f30 1764 }
1765
21c9bace 1766 if (!$context = get_context_instance(CONTEXT_SYSTEM)) {
1767 return false;
1768 }
eef868d1 1769
98882637 1770 if ($id = insert_record('role', $role)) {
eef868d1 1771 if ($legacy) {
1772 assign_capability($legacy, CAP_ALLOW, $id, $context->id);
98882637 1773 }
eef868d1 1774
ec7a8b79 1775 /// By default, users with role:manage at site level
1776 /// should be able to assign users to this new role, and override this new role's capabilities
eef868d1 1777
ec7a8b79 1778 // find all admin roles
e46c0987 1779 if ($adminroles = get_roles_with_capability('moodle/role:manage', CAP_ALLOW, $context)) {
1780 // foreach admin role
1781 foreach ($adminroles as $arole) {
1782 // write allow_assign and allow_overrid
1783 allow_assign($arole->id, $id);
eef868d1 1784 allow_override($arole->id, $id);
e46c0987 1785 }
ec7a8b79 1786 }
eef868d1 1787
98882637 1788 return $id;
1789 } else {
eef868d1 1790 return false;
98882637 1791 }
eef868d1 1792
bbbf2d40 1793}
1794
8420bee9 1795/**
1796 * function that deletes a role and cleanups up after it
1797 * @param roleid - id of role to delete
1798 * @return success
1799 */
1800function delete_role($roleid) {
1801 $success = true;
1802
1803// first unssign all users
1804 if (!role_unassign($roleid)) {
1805 debugging("Error while unassigning all users from role with ID $roleid!");
1806 $success = false;
1807 }
1808
1809// cleanup all references to this role, ignore errors
1810 if ($success) {
1811 delete_records('role_capabilities', 'roleid', $roleid);
1812 delete_records('role_allow_assign', 'roleid', $roleid);
1813 delete_records('role_allow_assign', 'allowassign', $roleid);
1814 delete_records('role_allow_override', 'roleid', $roleid);
1815 delete_records('role_allow_override', 'allowoverride', $roleid);
1816 delete_records('role_names', 'roleid', $roleid);
1817 }
1818
1819// finally delete the role itself
1820 if ($success and !delete_records('role', 'id', $roleid)) {
ece4945b 1821 debugging("Could not delete role record with ID $roleid!");
8420bee9 1822 $success = false;
1823 }
1824
1825 return $success;
1826}
1827
bbbf2d40 1828/**
1829 * Function to write context specific overrides, or default capabilities.
1830 * @param module - string name
1831 * @param capability - string name
1832 * @param contextid - context id
1833 * @param roleid - role id
1834 * @param permission - int 1,-1 or -1000
96986241 1835 * should not be writing if permission is 0
bbbf2d40 1836 */
e7876c1e 1837function assign_capability($capability, $permission, $roleid, $contextid, $overwrite=false) {
eef868d1 1838
98882637 1839 global $USER;
eef868d1 1840
96986241 1841 if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set
eef868d1 1842 unassign_capability($capability, $roleid, $contextid);
96986241 1843 return true;
98882637 1844 }
eef868d1 1845
2e85fffe 1846 $existing = get_record('role_capabilities', 'contextid', $contextid, 'roleid', $roleid, 'capability', $capability);
e7876c1e 1847
1848 if ($existing and !$overwrite) { // We want to keep whatever is there already
1849 return true;
1850 }
1851
bbbf2d40 1852 $cap = new object;
1853 $cap->contextid = $contextid;
1854 $cap->roleid = $roleid;
1855 $cap->capability = $capability;
1856 $cap->permission = $permission;
1857 $cap->timemodified = time();
9db12da7 1858 $cap->modifierid = empty($USER->id) ? 0 : $USER->id;
e7876c1e 1859
1860 if ($existing) {
1861 $cap->id = $existing->id;
1862 return update_record('role_capabilities', $cap);
1863 } else {
1864 return insert_record('role_capabilities', $cap);
1865 }
bbbf2d40 1866}
1867
1868
1869/**
1870 * Unassign a capability from a role.
1871 * @param $roleid - the role id
1872 * @param $capability - the name of the capability
1873 * @return boolean - success or failure
1874 */
1875function unassign_capability($capability, $roleid, $contextid=NULL) {
eef868d1 1876
98882637 1877 if (isset($contextid)) {
1878 $status = delete_records('role_capabilities', 'capability', $capability,
1879 'roleid', $roleid, 'contextid', $contextid);
1880 } else {
1881 $status = delete_records('role_capabilities', 'capability', $capability,
1882 'roleid', $roleid);
1883 }
1884 return $status;
bbbf2d40 1885}
1886
1887
1888/**
759ac72d 1889 * Get the roles that have a given capability assigned to it. This function
1890 * does not resolve the actual permission of the capability. It just checks
1891 * for assignment only.
bbbf2d40 1892 * @param $capability - capability name (string)
1893 * @param $permission - optional, the permission defined for this capability
1894 * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT
1895 * @return array or role objects
1896 */
ec7a8b79 1897function get_roles_with_capability($capability, $permission=NULL, $context='') {
1898
bbbf2d40 1899 global $CFG;
eef868d1 1900
ec7a8b79 1901 if ($context) {
1902 if ($contexts = get_parent_contexts($context)) {
1903 $listofcontexts = '('.implode(',', $contexts).')';
1904 } else {
21c9bace 1905 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
eef868d1 1906 $listofcontexts = '('.$sitecontext->id.')'; // must be site
1907 }
42ac3ecf 1908 $contextstr = "AND (rc.contextid = '$context->id' OR rc.contextid IN $listofcontexts)";
ec7a8b79 1909 } else {
1910 $contextstr = '';
1911 }
eef868d1 1912
1913 $selectroles = "SELECT r.*
42ac3ecf 1914 FROM {$CFG->prefix}role r,
1915 {$CFG->prefix}role_capabilities rc
bbbf2d40 1916 WHERE rc.capability = '$capability'
ec7a8b79 1917 AND rc.roleid = r.id $contextstr";
bbbf2d40 1918
1919 if (isset($permission)) {
1920 $selectroles .= " AND rc.permission = '$permission'";
1921 }
1922 return get_records_sql($selectroles);
1923}
1924
1925
1926/**
a9e1c058 1927 * This function makes a role-assignment (a role for a user or group in a particular context)
bbbf2d40 1928 * @param $roleid - the role of the id
1929 * @param $userid - userid
1930 * @param $groupid - group id
1931 * @param $contextid - id of the context
1932 * @param $timestart - time this assignment becomes effective
1933 * @param $timeend - time this assignemnt ceases to be effective
1934 * @uses $USER
1935 * @return id - new id of the assigment
1936 */
f44152f4 1937function role_assign($roleid, $userid, $groupid, $contextid, $timestart=0, $timeend=0, $hidden=0, $enrol='manual') {
aa311411 1938 global $USER, $CFG;
bbbf2d40 1939
7eb0b60a 1940 debugging("Assign roleid $roleid userid $userid contextid $contextid", DEBUG_DEVELOPER);
bbbf2d40 1941
a9e1c058 1942/// Do some data validation
1943
bbbf2d40 1944 if (empty($roleid)) {
9d829c68 1945 debugging('Role ID not provided');
a9e1c058 1946 return false;
bbbf2d40 1947 }
1948
1949 if (empty($userid) && empty($groupid)) {
9d829c68 1950 debugging('Either userid or groupid must be provided');
a9e1c058 1951 return false;
bbbf2d40 1952 }
eef868d1 1953
7700027f 1954 if ($userid && !record_exists('user', 'id', $userid)) {
82396e5b 1955 debugging('User ID '.intval($userid).' does not exist!');
7700027f 1956 return false;
1957 }
bbbf2d40 1958
f3f7610c 1959 if ($groupid && !groups_group_exists($groupid)) {
82396e5b 1960 debugging('Group ID '.intval($groupid).' does not exist!');
dc411d1b 1961 return false;
1962 }
1963
7700027f 1964 if (!$context = get_context_instance_by_id($contextid)) {
82396e5b 1965 debugging('Context ID '.intval($contextid).' does not exist!');
a9e1c058 1966 return false;
bbbf2d40 1967 }
1968
a9e1c058 1969 if (($timestart and $timeend) and ($timestart > $timeend)) {
9d829c68 1970 debugging('The end time can not be earlier than the start time');
a9e1c058 1971 return false;
1972 }
1973
7700027f 1974
a9e1c058 1975/// Check for existing entry
1976 if ($userid) {
7700027f 1977 $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'userid', $userid);
a9e1c058 1978 } else {
7700027f 1979 $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'groupid', $groupid);
a9e1c058 1980 }
1981
9ebcb4d2 1982
a9e1c058 1983 $newra = new object;
bbbf2d40 1984
a9e1c058 1985 if (empty($ra)) { // Create a new entry
1986 $newra->roleid = $roleid;
7700027f 1987 $newra->contextid = $context->id;
a9e1c058 1988 $newra->userid = $userid;
a9e1c058 1989 $newra->hidden = $hidden;
f44152f4 1990 $newra->enrol = $enrol;
a9e1c058 1991 $newra->timestart = $timestart;
1992 $newra->timeend = $timeend;
1993 $newra->timemodified = time();
115faa2f 1994 $newra->modifierid = empty($USER->id) ? 0 : $USER->id;
a9e1c058 1995
9ebcb4d2 1996 $success = insert_record('role_assignments', $newra);
a9e1c058 1997
1998 } else { // We already have one, just update it
1999
2000 $newra->id = $ra->id;
2001 $newra->hidden = $hidden;
f44152f4 2002 $newra->enrol = $enrol;
a9e1c058 2003 $newra->timestart = $timestart;
2004 $newra->timeend = $timeend;
2005 $newra->timemodified = time();
115faa2f 2006 $newra->modifierid = empty($USER->id) ? 0 : $USER->id;
a9e1c058 2007
9ebcb4d2 2008 $success = update_record('role_assignments', $newra);
2009 }
2010
7700027f 2011 if ($success) { /// Role was assigned, so do some other things
2012
2013 /// If the user is the current user, then reload the capabilities too.
2014 if (!empty($USER->id) && $USER->id == $userid) {
2f1a4248 2015 load_all_capabilities();
7700027f 2016 }
9b5d7a46 2017
0f161e1f 2018 /// Ask all the modules if anything needs to be done for this user
2019 if ($mods = get_list_of_plugins('mod')) {
2020 foreach ($mods as $mod) {
2021 include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php');
2022 $functionname = $mod.'_role_assign';
2023 if (function_exists($functionname)) {
2024 $functionname($userid, $context);
2025 }
2026 }
2027 }
2028
2029 /// Make sure they have an entry in user_lastaccess for courses they can access
2030 // role_add_lastaccess_entries($userid, $context);
a9e1c058 2031 }
eef868d1 2032
4e5f3064 2033 /// now handle metacourse role assignments if in course context
aad2ba95 2034 if ($success and $context->contextlevel == CONTEXT_COURSE) {
4e5f3064 2035 if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) {
2036 foreach ($parents as $parent) {
1aad4310 2037 sync_metacourse($parent->parent_course);
4e5f3064 2038 }
2039 }
2040 }
6eb4f823 2041
2042 return $success;
bbbf2d40 2043}
2044
2045
2046/**
1dc1f037 2047 * Deletes one or more role assignments. You must specify at least one parameter.
bbbf2d40 2048 * @param $roleid
2049 * @param $userid
2050 * @param $groupid
2051 * @param $contextid
2052 * @return boolean - success or failure
2053 */
1dc1f037 2054function role_unassign($roleid=0, $userid=0, $groupid=0, $contextid=0) {
d74067e8 2055
2056 global $USER, $CFG;
eef868d1 2057
4e5f3064 2058 $success = true;
d74067e8 2059
1dc1f037 2060 $args = array('roleid', 'userid', 'groupid', 'contextid');
2061 $select = array();
2062 foreach ($args as $arg) {
2063 if ($$arg) {
2064 $select[] = $arg.' = '.$$arg;
2065 }
2066 }
d74067e8 2067
1dc1f037 2068 if ($select) {
4e5f3064 2069 if ($ras = get_records_select('role_assignments', implode(' AND ', $select))) {
2070 $mods = get_list_of_plugins('mod');
2071 foreach($ras as $ra) {
86e2c51d 2072 /// infinite loop protection when deleting recursively
2073 if (!$ra = get_record('role_assignments', 'id', $ra->id)) {
2074 continue;
2075 }
4e5f3064 2076 $success = delete_records('role_assignments', 'id', $ra->id) and $success;
86e2c51d 2077
4e5f3064 2078 /// If the user is the current user, then reload the capabilities too.
2079 if (!empty($USER->id) && $USER->id == $ra->userid) {
2f1a4248 2080 load_all_capabilities();
4e5f3064 2081 }
2082 $context = get_record('context', 'id', $ra->contextid);
0f161e1f 2083
2084 /// Ask all the modules if anything needs to be done for this user
4e5f3064 2085 foreach ($mods as $mod) {
2086 include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php');
2087 $functionname = $mod.'_role_unassign';
2088 if (function_exists($functionname)) {
2089 $functionname($ra->userid, $context); // watch out, $context might be NULL if something goes wrong
2090 }
2091 }
2092
2093 /// now handle metacourse role unassigment and removing from goups if in course context
aad2ba95 2094 if (!empty($context) and $context->contextlevel == CONTEXT_COURSE) {
4e5f3064 2095 //remove from groups when user has no role
2096 $roles = get_user_roles($context, $ra->userid, true);
2097 if (empty($roles)) {
2098 if ($groups = get_groups($context->instanceid, $ra->userid)) {
2099 foreach ($groups as $group) {
2100 delete_records('groups_members', 'groupid', $group->id, 'userid', $ra->userid);
2101 }
2102 }
2103 }
1aad4310 2104 //unassign roles in metacourses if needed
4e5f3064 2105 if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) {
2106 foreach ($parents as $parent) {
1aad4310 2107 sync_metacourse($parent->parent_course);
0f161e1f 2108 }
2109 }
0f161e1f 2110 }
2111 }
d74067e8 2112 }
1dc1f037 2113 }
4e5f3064 2114
2115 return $success;
bbbf2d40 2116}
2117
eef868d1 2118/*
2119 * A convenience function to take care of the common case where you
b963384f 2120 * just want to enrol someone using the default role into a course
2121 *
2122 * @param object $course
2123 * @param object $user
2124 * @param string $enrol - the plugin used to do this enrolment
2125 */
2126function enrol_into_course($course, $user, $enrol) {
2127
2128 if ($course->enrolperiod) {
2129 $timestart = time();
2130 $timeend = time() + $course->enrolperiod;
2131 } else {
2132 $timestart = $timeend = 0;
2133 }
2134
2135 if ($role = get_default_course_role($course)) {
c4381ef5 2136
2137 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2138
e2183037 2139 if (!role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, $enrol)) {
b963384f 2140 return false;
2141 }
eef868d1 2142
b963384f 2143 email_welcome_message_to_user($course, $user);
eef868d1 2144
b963384f 2145 add_to_log($course->id, 'course', 'enrol', 'view.php?id='.$course->id, $user->id);
2146
2147 return true;
2148 }
2149
2150 return false;
2151}
2152
0f161e1f 2153/**
2154 * Add last access times to user_lastaccess as required
2155 * @param $userid
2156 * @param $context
2157 * @return boolean - success or failure
2158 */
2159function role_add_lastaccess_entries($userid, $context) {
2160
2161 global $USER, $CFG;
2162
aad2ba95 2163 if (empty($context->contextlevel)) {
0f161e1f 2164 return false;
2165 }
2166
2167 $lastaccess = new object; // Reusable object below
2168 $lastaccess->userid = $userid;
2169 $lastaccess->timeaccess = 0;
2170
aad2ba95 2171 switch ($context->contextlevel) {
0f161e1f 2172
2173 case CONTEXT_SYSTEM: // For the whole site
2174 if ($courses = get_record('course')) {
2175 foreach ($courses as $course) {
2176 $lastaccess->courseid = $course->id;
2177 role_set_lastaccess($lastaccess);
2178 }
2179 }
2180 break;
2181
2182 case CONTEXT_CATEGORY: // For a whole category
2183 if ($courses = get_record('course', 'category', $context->instanceid)) {
2184 foreach ($courses as $course) {
2185 $lastaccess->courseid = $course->id;
2186 role_set_lastaccess($lastaccess);
2187 }
2188 }
2189 if ($categories = get_record('course_categories', 'parent', $context->instanceid)) {
2190 foreach ($categories as $category) {
2191 $subcontext = get_context_instance(CONTEXT_CATEGORY, $category->id);
2192 role_add_lastaccess_entries($userid, $subcontext);
2193 }
2194 }
2195 break;
eef868d1 2196
0f161e1f 2197
2198 case CONTEXT_COURSE: // For a whole course
2199 if ($course = get_record('course', 'id', $context->instanceid)) {
2200 $lastaccess->courseid = $course->id;
2201 role_set_lastaccess($lastaccess);
2202 }
2203 break;
2204 }
2205}
2206
2207/**
2208 * Delete last access times from user_lastaccess as required
2209 * @param $userid
2210 * @param $context
2211 * @return boolean - success or failure
2212 */
2213function role_remove_lastaccess_entries($userid, $context) {
2214
2215 global $USER, $CFG;
2216
2217}
2218
bbbf2d40 2219
2220/**
2221 * Loads the capability definitions for the component (from file). If no
2222 * capabilities are defined for the component, we simply return an empty array.
2223 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2224 * @return array of capabilities
2225 */
2226function load_capability_def($component) {
2227 global $CFG;
2228
2229 if ($component == 'moodle') {
2230 $defpath = $CFG->libdir.'/db/access.php';
2231 $varprefix = 'moodle';
2232 } else {
0c4d9f49 2233 $compparts = explode('/', $component);
eef868d1 2234
0c4d9f49 2235 if ($compparts[0] == 'block') {
2236 // Blocks are an exception. Blocks directory is 'blocks', and not
2237 // 'block'. So we need to jump through hoops.
2238 $defpath = $CFG->dirroot.'/'.$compparts[0].
2239 's/'.$compparts[1].'/db/access.php';
2240 $varprefix = $compparts[0].'_'.$compparts[1];
ae628043 2241 } else if ($compparts[0] == 'format') {
2242 // Similar to the above, course formats are 'format' while they
2243 // are stored in 'course/format'.
2244 $defpath = $CFG->dirroot.'/course/'.$component.'/db/access.php';
2245 $varprefix = $compparts[0].'_'.$compparts[1];
0c4d9f49 2246 } else {
2247 $defpath = $CFG->dirroot.'/'.$component.'/db/access.php';
2248 $varprefix = str_replace('/', '_', $component);
2249 }
bbbf2d40 2250 }
2251 $capabilities = array();
eef868d1 2252
bbbf2d40 2253 if (file_exists($defpath)) {
dc268b2f 2254 require($defpath);
bbbf2d40 2255 $capabilities = ${$varprefix.'_capabilities'};
2256 }
2257 return $capabilities;
2258}
2259
2260
2261/**
2262 * Gets the capabilities that have been cached in the database for this
2263 * component.
2264 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2265 * @return array of capabilities
2266 */
2267function get_cached_capabilities($component='moodle') {
2268 if ($component == 'moodle') {
2269 $storedcaps = get_records_select('capabilities',
2270 "name LIKE 'moodle/%:%'");
2271 } else {
2272 $storedcaps = get_records_select('capabilities',
2273 "name LIKE '$component:%'");
2274 }
2275 return $storedcaps;
2276}
2277
2278
2279/**
2280 * Updates the capabilities table with the component capability definitions.
2281 * If no parameters are given, the function updates the core moodle
2282 * capabilities.
2283 *
2284 * Note that the absence of the db/access.php capabilities definition file
2285 * will cause any stored capabilities for the component to be removed from
eef868d1 2286 * the database.
bbbf2d40 2287 *
2288 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2289 * @return boolean
2290 */
2291function update_capabilities($component='moodle') {
eef868d1 2292
bbbf2d40 2293 $storedcaps = array();
be4486da 2294
2295 $filecaps = load_capability_def($component);
bbbf2d40 2296 $cachedcaps = get_cached_capabilities($component);
2297 if ($cachedcaps) {
2298 foreach ($cachedcaps as $cachedcap) {
2299 array_push($storedcaps, $cachedcap->name);
17e5635c 2300 // update risk bitmasks in existing capabilities if needed
be4486da 2301 if (array_key_exists($cachedcap->name, $filecaps)) {
2302 if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) {
2b531945 2303 $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified
be4486da 2304 }
2305 if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) {
2306 $updatecap = new object;
2307 $updatecap->id = $cachedcap->id;
2308 $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask'];
2309 if (!update_record('capabilities', $updatecap)) {
2310 return false;
2311 }
2312 }
2313 }
bbbf2d40 2314 }
2315 }
be4486da 2316
bbbf2d40 2317 // Are there new capabilities in the file definition?
2318 $newcaps = array();
eef868d1 2319
bbbf2d40 2320 foreach ($filecaps as $filecap => $def) {
eef868d1 2321 if (!$storedcaps ||
bbbf2d40 2322 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
2b531945 2323 if (!array_key_exists('riskbitmask', $def)) {
2324 $def['riskbitmask'] = 0; // no risk if not specified
2325 }
bbbf2d40 2326 $newcaps[$filecap] = $def;
2327 }
2328 }
2329 // Add new capabilities to the stored definition.
2330 foreach ($newcaps as $capname => $capdef) {
2331 $capability = new object;
2332 $capability->name = $capname;
2333 $capability->captype = $capdef['captype'];
2334 $capability->contextlevel = $capdef['contextlevel'];
2335 $capability->component = $component;
be4486da 2336 $capability->riskbitmask = $capdef['riskbitmask'];
eef868d1 2337
bbbf2d40 2338 if (!insert_record('capabilities', $capability, false, 'id')) {
2339 return false;
2340 }
eef868d1 2341
bbbf2d40 2342 // Do we need to assign the new capabilities to roles that have the
2343 // legacy capabilities moodle/legacy:* as well?
2344 if (isset($capdef['legacy']) && is_array($capdef['legacy']) &&
2345 !assign_legacy_capabilities($capname, $capdef['legacy'])) {
2e85fffe 2346 notify('Could not assign legacy capabilities for '.$capname);
bbbf2d40 2347 }
2348 }
2349 // Are there any capabilities that have been removed from the file
2350 // definition that we need to delete from the stored capabilities and
2351 // role assignments?
2352 capabilities_cleanup($component, $filecaps);
eef868d1 2353
bbbf2d40 2354 return true;
2355}
2356
2357
2358/**
2359 * Deletes cached capabilities that are no longer needed by the component.
2360 * Also unassigns these capabilities from any roles that have them.
2361 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
2362 * @param $newcapdef - array of the new capability definitions that will be
2363 * compared with the cached capabilities
2364 * @return int - number of deprecated capabilities that have been removed
2365 */
2366function capabilities_cleanup($component, $newcapdef=NULL) {
eef868d1 2367
bbbf2d40 2368 $removedcount = 0;
eef868d1 2369
bbbf2d40 2370 if ($cachedcaps = get_cached_capabilities($component)) {
2371 foreach ($cachedcaps as $cachedcap) {
2372 if (empty($newcapdef) ||
2373 array_key_exists($cachedcap->name, $newcapdef) === false) {
eef868d1 2374
bbbf2d40 2375 // Remove from capabilities cache.
2376 if (!delete_records('capabilities', 'name', $cachedcap->name)) {
2377 error('Could not delete deprecated capability '.$cachedcap->name);
2378 } else {
2379 $removedcount++;
2380 }
2381 // Delete from roles.
2382 if($roles = get_roles_with_capability($cachedcap->name)) {
2383 foreach($roles as $role) {
46943f7b 2384 if (!unassign_capability($cachedcap->name, $role->id)) {
bbbf2d40 2385 error('Could not unassign deprecated capability '.
2386 $cachedcap->name.' from role '.$role->name);
2387 }
2388 }
2389 }
2390 } // End if.
2391 }
2392 }
2393 return $removedcount;
2394}
2395
2396
2397
cee0901c 2398/****************
2399 * UI FUNCTIONS *
2400 ****************/
bbbf2d40 2401
2402
2403/**
2404 * prints human readable context identifier.
2405 */
0468976c 2406function print_context_name($context) {
340ea4e8 2407
ec0810ee 2408 $name = '';
aad2ba95 2409 switch ($context->contextlevel) {
ec0810ee 2410
bbbf2d40 2411 case CONTEXT_SYSTEM: // by now it's a definite an inherit
ec0810ee 2412 $name = get_string('site');
340ea4e8 2413 break;
bbbf2d40 2414
2415 case CONTEXT_PERSONAL:
ec0810ee 2416 $name = get_string('personal');
340ea4e8 2417 break;
2418
4b10f08b 2419 case CONTEXT_USER:
ec0810ee 2420 if ($user = get_record('user', 'id', $context->instanceid)) {
2421 $name = get_string('user').': '.fullname($user);
2422 }
340ea4e8 2423 break;
2424
bbbf2d40 2425 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
ec0810ee 2426 if ($category = get_record('course_categories', 'id', $context->instanceid)) {
2427 $name = get_string('category').': '.$category->name;
2428 }
340ea4e8 2429 break;
bbbf2d40 2430
2431 case CONTEXT_COURSE: // 1 to 1 to course cat
ec0810ee 2432 if ($course = get_record('course', 'id', $context->instanceid)) {
2433 $name = get_string('course').': '.$course->fullname;
2434 }
340ea4e8 2435 break;
bbbf2d40 2436
2437 case CONTEXT_GROUP: // 1 to 1 to course
f3f7610c
ML
2438 if ($name = groups_get_group_name($context->instanceid)) {
2439 $name = get_string('group').': '. $name;
ec0810ee 2440 }
340ea4e8 2441 break;
bbbf2d40 2442
2443 case CONTEXT_MODULE: // 1 to 1 to course
98882637 2444 if ($cm = get_record('course_modules','id',$context->instanceid)) {
2445 if ($module = get_record('modules','id',$cm->module)) {
2446 if ($mod = get_record($module->name, 'id', $cm->instance)) {
ec0810ee 2447 $name = get_string('activitymodule').': '.$mod->name;
98882637 2448 }
ec0810ee 2449 }
2450 }
340ea4e8 2451 break;
bbbf2d40 2452
2453 case CONTEXT_BLOCK: // 1 to 1 to course
98882637 2454 if ($blockinstance = get_record('block_instance','id',$context->instanceid)) {
2455 if ($block = get_record('block','id',$blockinstance->blockid)) {
91be52d7 2456 global $CFG;
2457 require_once("$CFG->dirroot/blocks/moodleblock.class.php");
2458 require_once("$CFG->dirroot/blocks/$block->name/block_$block->name.php");
2459 $blockname = "block_$block->name";
2460 if ($blockobject = new $blockname()) {
2461 $name = $blockobject->title.' ('.get_string('block').')';
2462 }
ec0810ee 2463 }
2464 }
340ea4e8 2465 break;
bbbf2d40 2466
2467 default:
2468 error ('This is an unknown context!');
340ea4e8 2469 return false;
2470
2471 }
340ea4e8 2472 return $name;
bbbf2d40 2473}
2474
2475
2476/**
eef868d1 2477 * Extracts the relevant capabilities given a contextid.
bbbf2d40 2478 * All case based, example an instance of forum context.
2479 * Will fetch all forum related capabilities, while course contexts
2480 * Will fetch all capabilities
0468976c 2481 * @param object context
bbbf2d40 2482 * @return array();
2483 *
2484 * capabilities
2485 * `name` varchar(150) NOT NULL,
2486 * `captype` varchar(50) NOT NULL,
2487 * `contextlevel` int(10) NOT NULL,
2488 * `component` varchar(100) NOT NULL,
2489 */
0468976c 2490function fetch_context_capabilities($context) {
eef868d1 2491
98882637 2492 global $CFG;
bbbf2d40 2493
2494 $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display
eef868d1 2495
aad2ba95 2496 switch ($context->contextlevel) {
bbbf2d40 2497
98882637 2498 case CONTEXT_SYSTEM: // all
2499 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2500 break;
2501
2502 case CONTEXT_PERSONAL:
0a8a95c9 2503 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_PERSONAL;
bbbf2d40 2504 break;
eef868d1 2505
4b10f08b 2506 case CONTEXT_USER:
8020a016 2507 $SQL = "SELECT *
2508 FROM {$CFG->prefix}capabilities
2509 WHERE contextlevel = ".CONTEXT_USER;
bbbf2d40 2510 break;
eef868d1 2511
bbbf2d40 2512 case CONTEXT_COURSECAT: // all
98882637 2513 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2514 break;
2515
2516 case CONTEXT_COURSE: // all
98882637 2517 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 2518 break;
2519
2520 case CONTEXT_GROUP: // group caps
2521 break;
2522
2523 case CONTEXT_MODULE: // mod caps
98882637 2524 $cm = get_record('course_modules', 'id', $context->instanceid);
2525 $module = get_record('modules', 'id', $cm->module);
eef868d1 2526
98882637 2527 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE."
2528 and component = 'mod/$module->name'";
bbbf2d40 2529 break;
2530
2531 case CONTEXT_BLOCK: // block caps
98882637 2532 $cb = get_record('block_instance', 'id', $context->instanceid);
2533 $block = get_record('block', 'id', $cb->blockid);
eef868d1 2534
98882637 2535 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK."
2536 and component = 'block/$block->name'";
bbbf2d40 2537 break;
2538
2539 default:
2540 return false;
2541 }
2542
16e2e2f3 2543 if (!$records = get_records_sql($SQL.' '.$sort)) {
2544 $records = array();
2545 }
ba8d8027 2546
2547/// the rest of code is a bit hacky, think twice before modifying it :-(
69eb59f2 2548
2549 // special sorting of core system capabiltites and enrollments
e9c82dca 2550 if (in_array($context->contextlevel, array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE))) {
69eb59f2 2551 $first = array();
2552 foreach ($records as $key=>$record) {
2553 if (preg_match('|^moodle/|', $record->name) and $record->contextlevel == CONTEXT_SYSTEM) {
2554 $first[$key] = $record;
2555 unset($records[$key]);
2556 } else if (count($first)){
2557 break;
2558 }
2559 }
2560 if (count($first)) {
2561 $records = $first + $records; // merge the two arrays keeping the keys
2562 }
ba8d8027 2563 } else {
2564 $contextindependentcaps = fetch_context_independent_capabilities();
2565 $records = array_merge($contextindependentcaps, $records);
69eb59f2 2566 }
ba8d8027 2567
bbbf2d40 2568 return $records;
eef868d1 2569
bbbf2d40 2570}
2571
2572
759ac72d 2573/**
2574 * Gets the context-independent capabilities that should be overrridable in
2575 * any context.
2576 * @return array of capability records from the capabilities table.
2577 */
2578function fetch_context_independent_capabilities() {
eef868d1 2579
17e5635c 2580 //only CONTEXT_SYSTEM capabilities here or it will break the hack in fetch_context_capabilities()
759ac72d 2581 $contextindependentcaps = array(
2582 'moodle/site:accessallgroups'
2583 );
2584
2585 $records = array();
eef868d1 2586
759ac72d 2587 foreach ($contextindependentcaps as $capname) {
2588 $record = get_record('capabilities', 'name', $capname);
2589 array_push($records, $record);
2590 }
2591 return $records;
2592}
2593
2594
bbbf2d40 2595/**
2596 * This function pulls out all the resolved capabilities (overrides and
759ac72d 2597 * defaults) of a role used in capability overrides in contexts at a given
bbbf2d40 2598 * context.
0a8a95c9 2599 * @param obj $context
bbbf2d40 2600 * @param int $roleid
dc558690 2601 * @param bool self - if set to true, resolve till this level, else stop at immediate parent level
bbbf2d40 2602 * @return array
2603 */
1648afb2 2604function role_context_capabilities($roleid, $context, $cap='') {
dc558690 2605 global $CFG;
eef868d1 2606
8521d83a 2607 $contexts = get_parent_contexts($context);
2608 $contexts[] = $context->id;
98882637 2609 $contexts = '('.implode(',', $contexts).')';
eef868d1 2610
1648afb2 2611 if ($cap) {
e4697bf7 2612 $search = " AND rc.capability = '$cap' ";
1648afb2 2613 } else {
eef868d1 2614 $search = '';
1648afb2 2615 }
eef868d1 2616
2617 $SQL = "SELECT rc.*
2618 FROM {$CFG->prefix}role_capabilities rc,
dc558690 2619 {$CFG->prefix}context c
2620 WHERE rc.contextid in $contexts
2621 AND rc.roleid = $roleid
2622 AND rc.contextid = c.id $search
aad2ba95 2623 ORDER BY c.contextlevel DESC,
eef868d1 2624 rc.capability DESC";
759ac72d 2625
98882637 2626 $capabilities = array();
eef868d1 2627
4729012f 2628 if ($records = get_records_sql($SQL)) {
2629 // We are traversing via reverse order.
2630 foreach ($records as $record) {
2631 // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
2632 if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
2633 $capabilities[$record->capability] = $record->permission;
eef868d1 2634 }
4729012f 2635 }
98882637 2636 }
2637 return $capabilities;
bbbf2d40 2638}
2639
bbbf2d40 2640/**
eef868d1 2641 * Recursive function which, given a context, find all parent context ids,
bbbf2d40 2642 * and return the array in reverse order, i.e. parent first, then grand
2643 * parent, etc.
2644 * @param object $context
2645 * @return array()
2646 */
bbbf2d40 2647function get_parent_contexts($context) {
759ac72d 2648
1cd03601 2649 static $pcontexts; // cache
2650 if (isset($pcontexts[$context->id])) {
2651 return ($pcontexts[$context->id]);
2652 }
2653
aad2ba95 2654 switch ($context->contextlevel) {
bbbf2d40 2655
2656 case CONTEXT_SYSTEM: // no parent
957861f7 2657 return array();
bbbf2d40 2658 break;
2659
2660 case CONTEXT_PERSONAL:
21c9bace 2661 if (!$parent = get_context_instance(CONTEXT_SYSTEM)) {
957861f7 2662 return array();
2663 } else {
1cd03601 2664 $res = array($parent->id);
0de75c4c 2665 $pcontexts[$context->id] = $res;
1cd03601 2666 return $res;
957861f7 2667 }
bbbf2d40 2668 break;
eef868d1 2669
4b10f08b 2670 case CONTEXT_USER:
21c9bace 2671 if (!$parent = get_context_instance(CONTEXT_SYSTEM)) {
957861f7 2672 return array();
2673 } else {
1cd03601 2674 $res = array($parent->id);
0de75c4c 2675 $pcontexts[$context->id] = $res;
1cd03601 2676 return $res;
957861f7 2677 }
bbbf2d40 2678 break;
eef868d1 2679
bbbf2d40 2680 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
957861f7 2681 if (!$coursecat = get_record('course_categories','id',$context->instanceid)) {
2682 return array();
2683 }
c5ddc3fd 2684 if (!empty($coursecat->parent)) { // return parent value if exist
bbbf2d40 2685 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1cd03601 2686 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2687 $pcontexts[$context->id] = $res;
2688 return $res;
bbbf2d40 2689 } else { // else return site value
21c9bace 2690 $parent = get_context_instance(CONTEXT_SYSTEM);
1cd03601 2691 $res = array($parent->id);
2692 $pcontexts[$context->id] = $res;
2693 return $res;
bbbf2d40 2694 }
2695 break;
2696
2697 case CONTEXT_COURSE: // 1 to 1 to course cat
957861f7 2698 if (!$course = get_record('course','id',$context->instanceid)) {
2699 return array();
2700 }
1936c10e 2701 if ($course->id != SITEID) {
957861f7 2702 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1cd03601 2703 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2704 return $res;
957861f7 2705 } else {
40a2a15f 2706 // Yu: Separating site and site course context
2707 $parent = get_context_instance(CONTEXT_SYSTEM);
1cd03601 2708 $res = array($parent->id);
2709 $pcontexts[$context->id] = $res;
2710 return $res;
957861f7 2711 }
bbbf2d40 2712 break;
2713
2714 case CONTEXT_GROUP: // 1 to 1 to course
f3f7610c 2715 if (! $group = groups_get_group($context->instanceid)) {
957861f7 2716 return array();
2717 }
2718 if ($parent = get_context_instance(CONTEXT_COURSE, $group->courseid)) {
1cd03601 2719 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2720 $pcontexts[$context->id] = $res;
2721 return $res;
957861f7 2722 } else {
2723 return array();
2724 }
bbbf2d40 2725 break;
2726
2727 case CONTEXT_MODULE: // 1 to 1 to course
957861f7 2728 if (!$cm = get_record('course_modules','id',$context->instanceid)) {
2729 return array();
2730 }
2731 if ($parent = get_context_instance(CONTEXT_COURSE, $cm->course)) {
1cd03601 2732 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2733 $pcontexts[$context->id] = $res;
2734 return $res;
957861f7 2735 } else {
2736 return array();
2737 }
bbbf2d40 2738 break;
2739
2740 case CONTEXT_BLOCK: // 1 to 1 to course
957861f7 2741 if (!$block = get_record('block_instance','id',$context->instanceid)) {
2742 return array();
2743 }
2744 if ($parent = get_context_instance(CONTEXT_COURSE, $block->pageid)) {
1cd03601 2745 $res = array_merge(array($parent->id), get_parent_contexts($parent));
2746 $pcontexts[$context->id] = $res;
2747 return $res;
957861f7 2748 } else {
2749 return array();
2750 }
bbbf2d40 2751 break;
2752
2753 default:
957861f7 2754 error('This is an unknown context!');
bbbf2d40 2755 return false;
2756 }
bbbf2d40 2757}
2758
759ac72d 2759
2760/**
2761 * Gets a string for sql calls, searching for stuff in this context or above
ea8158c1 2762 * @param object $context
2763 * @return string
2764 */
2765function get_related_contexts_string($context) {
2766 if ($parents = get_parent_contexts($context)) {
eef868d1 2767 return (' IN ('.$context->id.','.implode(',', $parents).')');
ea8158c1 2768 } else {
2769 return (' ='.$context->id);
2770 }
2771}
759ac72d 2772
2773
bbbf2d40 2774/**
2775 * This function gets the capability of a role in a given context.
2776 * It is needed when printing override forms.
2777 * @param int $contextid
bbbf2d40 2778 * @param string $capability
2779 * @param array $capabilities - array loaded using role_context_capabilities
2780 * @return int (allow, prevent, prohibit, inherit)
2781 */
bbbf2d40 2782function get_role_context_capability($contextid, $capability, $capabilities) {
759ac72d 2783 if (isset($capabilities[$contextid][$capability])) {
2784 return $capabilities[$contextid][$capability];
2785 }
2786 else {
2787 return false;
2788 }
bbbf2d40 2789}
2790
2791
cee0901c 2792/**
2793 * Returns the human-readable, translated version of the capability.
2794 * Basically a big switch statement.
2795 * @param $capabilityname - e.g. mod/choice:readresponses
2796 */
ceb83c70 2797function get_capability_string($capabilityname) {
eef868d1 2798
cee0901c 2799 // Typical capabilityname is mod/choice:readresponses
ceb83c70 2800
2801 $names = split('/', $capabilityname);
2802 $stringname = $names[1]; // choice:readresponses
eef868d1 2803 $components = split(':', $stringname);
ceb83c70 2804 $componentname = $components[0]; // choice
98882637 2805
2806 switch ($names[0]) {
2807 case 'mod':
ceb83c70 2808 $string = get_string($stringname, $componentname);
98882637 2809 break;
eef868d1 2810
98882637 2811 case 'block':
ceb83c70 2812 $string = get_string($stringname, 'block_'.$componentname);
98882637 2813 break;
ceb83c70 2814
98882637 2815 case 'moodle':
ceb83c70 2816 $string = get_string($stringname, 'role');
98882637 2817 break;
eef868d1 2818
98882637 2819 case 'enrol':
ceb83c70 2820 $string = get_string($stringname, 'enrol_'.$componentname);
eef868d1 2821 break;
ae628043 2822
2823 case 'format':
2824 $string = get_string($stringname, 'format_'.$componentname);
2825 break;
eef868d1 2826
98882637 2827 default:
ceb83c70 2828 $string = get_string($stringname);
eef868d1 2829 break;
2830
98882637 2831 }
ceb83c70 2832 return $string;
bbbf2d40 2833}
2834
2835
cee0901c 2836/**
2837 * This gets the mod/block/course/core etc strings.
2838 * @param $component
2839 * @param $contextlevel
2840 */
bbbf2d40 2841function get_component_string($component, $contextlevel) {
2842
98882637 2843 switch ($contextlevel) {
bbbf2d40 2844
98882637 2845 case CONTEXT_SYSTEM:
be382aaf 2846 if (preg_match('|^enrol/|', $component)) {
2847 $langname = str_replace('/', '_', $component);
2848 $string = get_string('enrolname', $langname);
f3652521 2849 } else if (preg_match('|^block/|', $component)) {
2850 $langname = str_replace('/', '_', $component);
2851 $string = get_string('blockname', $langname);
69eb59f2 2852 } else {
2853 $string = get_string('coresystem');
2854 }
bbbf2d40 2855 break;
2856
2857 case CONTEXT_PERSONAL:
98882637 2858 $string = get_string('personal');
bbbf2d40 2859 break;
2860
4b10f08b 2861 case CONTEXT_USER:
98882637 2862 $string = get_string('users');
bbbf2d40 2863 break;
2864
2865 case CONTEXT_COURSECAT:
98882637 2866 $string = get_string('categories');
bbbf2d40 2867 break;
2868
2869 case CONTEXT_COURSE:
98882637 2870 $string = get_string('course');
bbbf2d40 2871 break;
2872
2873 case CONTEXT_GROUP:
98882637 2874 $string = get_string('group');
bbbf2d40 2875 break;
2876
2877 case CONTEXT_MODULE:
98882637 2878 $string = get_string('modulename', basename($component));
bbbf2d40 2879 break;
2880
2881 case CONTEXT_BLOCK:
98882637 2882 $string = get_string('blockname', 'block_'.$component.'.php');
bbbf2d40 2883 break;
2884
2885 default:
2886 error ('This is an unknown context!');
2887 return false;
eef868d1 2888
98882637 2889 }
98882637 2890 return $string;
bbbf2d40 2891}
cee0901c 2892
759ac72d 2893/**
2894 * Gets the list of roles assigned to this context and up (parents)
945f88ca 2895 * @param object $context
3997cb40 2896 * @param view - set to true when roles are pulled for display only
2897 * this is so that we can filter roles with no visible
2898 * assignment, for example, you might want to "hide" all
2899 * course creators when browsing the course participants
2900 * list.
945f88ca 2901 * @return array
2902 */
3997cb40 2903function get_roles_used_in_context($context, $view = false) {
e4dd3222 2904
2905 global $CFG;
3997cb40 2906
2907 // filter for roles with all hidden assignments
2908 // no need to return when only pulling roles for reviewing
2909 // e.g. participants page.
d42c64ba 2910 $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':'';
2d9965e1 2911 $contextlist = get_related_contexts_string($context);
eef868d1 2912
759ac72d 2913 $sql = "SELECT DISTINCT r.id,
2914 r.name,
2915 r.shortname,
2916 r.sortorder
2917 FROM {$CFG->prefix}role_assignments ra,
eef868d1 2918 {$CFG->prefix}role r
2919 WHERE r.id = ra.roleid
759ac72d 2920 AND ra.contextid $contextlist
3997cb40 2921 $hiddensql
759ac72d 2922 ORDER BY r.sortorder ASC";
eef868d1 2923
759ac72d 2924 return get_records_sql($sql);
e4dd3222 2925}
2926
eef868d1 2927/** this function is used to print roles column in user profile page.
945f88ca 2928 * @param int userid
2929 * @param int contextid
2930 * @return string
2931 */
0a8a95c9 2932function get_user_roles_in_context($userid, $contextid){
2933 global $CFG;
eef868d1 2934
0a8a95c9 2935 $rolestring = '';
2936 $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';
2937 if ($roles = get_records_sql($SQL)) {
2938 foreach ($roles as $userrole) {
2939 $rolestring .= '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$userrole->contextid.'&amp;roleid='.$userrole->roleid.'">'.$userrole->name.'</a>, ';
eef868d1 2940 }
2941
0a8a95c9 2942 }
2943 return rtrim($rolestring, ', ');
2944}
68c52526 2945
2946
945f88ca 2947/**
2948 * Checks if a user can override capabilities of a particular role in this context
2949 * @param object $context
2950 * @param int targetroleid - the id of the role you want to override
2951 * @return boolean
2952 */
68c52526 2953function user_can_override($context, $targetroleid) {
2954 // first check if user has override capability
2955 // if not return false;
2956 if (!has_capability('moodle/role:override', $context)) {
eef868d1 2957 return false;
68c52526 2958 }
2959 // pull out all active roles of this user from this context(or above)
c0614051 2960 if ($userroles = get_user_roles($context)) {
2961 foreach ($userroles as $userrole) {
2962 // if any in the role_allow_override table, then it's ok
2963 if (get_record('role_allow_override', 'roleid', $userrole->roleid, 'allowoverride', $targetroleid)) {
2964 return true;
2965 }
68c52526 2966 }
2967 }
eef868d1 2968
68c52526 2969 return false;
eef868d1 2970
68c52526 2971}
2972
945f88ca 2973/**
2974 * Checks if a user can assign users to a particular role in this context
2975 * @param object $context
2976 * @param int targetroleid - the id of the role you want to assign users to
2977 * @return boolean
2978 */
68c52526 2979function user_can_assign($context, $targetroleid) {
eef868d1 2980
68c52526 2981 // first check if user has override capability
2982 // if not return false;
2983 if (!has_capability('moodle/role:assign', $context)) {
eef868d1 2984 return false;
68c52526 2985 }
2986 // pull out all active roles of this user from this context(or above)
c0614051 2987 if ($userroles = get_user_roles($context)) {
2988 foreach ($userroles as $userrole) {
2989 // if any in the role_allow_override table, then it's ok
2990 if (get_record('role_allow_assign', 'roleid', $userrole->roleid, 'allowassign', $targetroleid)) {
2991 return true;
2992 }
68c52526 2993 }
2994 }
eef868d1 2995
2996 return false;
68c52526 2997}
2998
ece4945b 2999/** Returns all site roles in correct sort order.
3000 *
3001 */
3002function get_all_roles() {
3003 return get_records('role', '', '', 'sortorder ASC');
3004}
3005
945f88ca 3006/**
3007 * gets all the user roles assigned in this context, or higher contexts
3008 * this is mainly used when checking if a user can assign a role, or overriding a role
3009 * i.e. we need to know what this user holds, in order to verify against allow_assign and
3010 * allow_override tables
3011 * @param object $context
3012 * @param int $userid
b06334e8 3013 * @param view - set to true when roles are pulled for display only
3014 * this is so that we can filter roles with no visible
3015 * assignment, for example, you might want to "hide" all
3016 * course creators when browsing the course participants
3017 * list.
945f88ca 3018 * @return array
3019 */
b06334e8 3020function get_user_roles($context, $userid=0, $checkparentcontexts=true, $order='c.contextlevel DESC, r.sortorder ASC', $view=false) {
68c52526 3021
3022 global $USER, $CFG, $db;
c0614051 3023
3024 if (empty($userid)) {
3025 if (empty($USER->id)) {
3026 return array();
3027 }
3028 $userid = $USER->id;
3029 }
b06334e8 3030 // set up hidden sql
d42c64ba 3031 $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':'';
c0614051 3032
5b630667 3033 if ($checkparentcontexts && ($parents = get_parent_contexts($context))) {
3034 $contexts = ' ra.contextid IN ('.implode(',' , $parents).','.$context->id.')';
c0614051 3035 } else {
5b630667 3036 $contexts = ' ra.contextid = \''.$context->id.'\'';
c0614051 3037 }
3038
31f26796 3039 return get_records_sql('SELECT ra.*, r.name, r.shortname
5b630667 3040 FROM '.$CFG->prefix.'role_assignments ra,
ec6eb110 3041 '.$CFG->prefix.'role r,
3042 '.$CFG->prefix.'context c
c0614051 3043 WHERE ra.userid = '.$userid.
5b630667 3044 ' AND ra.roleid = r.id
ec6eb110 3045 AND ra.contextid = c.id
b06334e8 3046 AND '.$contexts . $hiddensql .
ae9761ec 3047 ' ORDER BY '.$order);
68c52526 3048}
3049
945f88ca 3050/**
eef868d1 3051 * Creates a record in the allow_override table
945f88ca 3052 * @param int sroleid - source roleid
3053 * @param int troleid - target roleid
3054 * @return int - id or false
3055 */
3056function allow_override($sroleid, $troleid) {
ece4945b 3057 $record = new object();
945f88ca 3058 $record->roleid = $sroleid;
3059 $record->allowoverride = $troleid;
3060 return insert_record('role_allow_override', $record);
3061}
3062
3063/**
eef868d1 3064 * Creates a record in the allow_assign table
945f88ca 3065 * @param int sroleid - source roleid
3066 * @param int troleid - target roleid
3067 * @return int - id or false
3068 */
3069function allow_assign($sroleid, $troleid) {
ff64aaea 3070 $record = new object;
945f88ca 3071 $record->roleid = $sroleid;
3072 $record->allowassign = $troleid;
3073 return insert_record('role_allow_assign', $record);
3074}
3075
3076/**
ff64aaea 3077 * Gets a list of roles that this user can assign in this context
945f88ca 3078 * @param object $context
3079 * @return array
3080 */
5e67946d 3081function get_assignable_roles ($context, $field="name") {
945f88ca 3082
945f88ca 3083 $options = array();
ff64aaea 3084
ece4945b 3085 if ($roles = get_all_roles()) {
ff64aaea 3086 foreach ($roles as $role) {
3087 if (user_can_assign($context, $role->id)) {
5e67946d 3088 $options[$role->id] = strip_tags(format_string($role->{$field}, true));
ff64aaea 3089 }
945f88ca 3090 }
3091 }
3092 return $options;
3093}
3094
3095/**
ff64aaea 3096 * Gets a list of roles that this user can override in this context
945f88ca 3097 * @param object $context
3098 * @return array
3099 */
3100function get_overridable_roles ($context) {
3101
945f88ca 3102 $options = array();
ff64aaea 3103
ece4945b 3104 if ($roles = get_all_roles()) {
ff64aaea 3105 foreach ($roles as $role) {
3106 if (user_can_override($context, $role->id)) {
65b0c132 3107 $options[$role->id] = strip_tags(format_string($role->name, true));
ff64aaea 3108 }
945f88ca 3109 }
ff64aaea 3110 }
eef868d1 3111
3112 return $options;
945f88ca 3113}
1648afb2 3114
b963384f 3115/*
3116 * Returns a role object that is the default role for new enrolments
3117 * in a given course
3118 *
eef868d1 3119 * @param object $course
b963384f 3120 * @return object $role
3121 */
3122function get_default_course_role($course) {
3123 global $CFG;
3124
3125/// First let's take the default role the course may have
3126 if (!empty($course->defaultrole)) {
3127 if ($role = get_record('role', 'id', $course->defaultrole)) {
3128 return $role;
3129 }
3130 }
3131
3132/// Otherwise the site setting should tell us
3133 if ($CFG->defaultcourseroleid) {
3134 if ($role = get_record('role', 'id', $CFG->defaultcourseroleid)) {
3135 return $role;
3136 }
3137 }
3138
3139/// It's unlikely we'll get here, but just in case, try and find a student role
3140 if ($studentroles = get_roles_with_capability('moodle/legacy:student', CAP_ALLOW)) {
3141 return array_shift($studentroles); /// Take the first one
3142 }
3143
3144 return NULL;
3145}
3146
1648afb2 3147
3148/**
3149 * who has this capability in this context
3150 * does not handling user level resolving!!!
40a2a15f 3151 * (!)pleaes note if $fields is empty this function attempts to get u.*
3152 * which can get rather large.
1648afb2 3153 * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly
3154 * @param $context - object
3155 * @param $capability - string capability
3156 * @param $fields - fields to be pulled
3157 * @param $sort - the sort order
04417640 3158 * @param $limitfrom - number of records to skip (offset)
eef868d1 3159 * @param $limitnum - number of records to fetch
1c45e42e 3160 * @param $groups - single group or array of groups - group(s) user is in
71dea306 3161 * @param $exceptions - list of users to exclude
b06334e8 3162 * @param view - set to true when roles are pulled for display only
3163 * this is so that we can filter roles with no visible
3164 * assignment, for example, you might want to "hide" all
3165 * course creators when browsing the course participants
3166 * list.
1648afb2 3167 */
eef868d1 3168function get_users_by_capability($context, $capability, $fields='', $sort='',
b06334e8 3169 $limitfrom='', $limitnum='', $groups='', $exceptions='', $doanything=true, $view=false) {
1648afb2 3170 global $CFG;
eef868d1 3171
64026e8c 3172/// Sorting out groups
1c45e42e 3173 if ($groups) {
71dea306 3174 $groupjoin = 'INNER JOIN '.$CFG->prefix.'groups_members gm ON gm.userid = ra.userid';
eef868d1 3175
1c45e42e 3176 if (is_array($groups)) {
a05708ad 3177 $groupsql = 'AND gm.groupid IN ('.implode(',', $groups).')';
1c45e42e 3178 } else {
eef868d1 3179 $groupsql = 'AND gm.groupid = '.$groups;
1c45e42e 3180 }
3181 } else {
3182 $groupjoin = '';
eef868d1 3183 $groupsql = '';
1c45e42e 3184 }
eef868d1 3185
64026e8c 3186/// Sorting out exceptions
5081e786 3187 $exceptionsql = $exceptions ? "AND u.id NOT IN ($exceptions)" : '';
64026e8c 3188
3189/// Set up default fields
3190 if (empty($fields)) {
5b630667 3191 $fields = 'u.*, ul.timeaccess as lastaccess, ra.hidden';
64026e8c