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