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