Some robustness when loading capabilities
[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
18define('CAP_ALLOW', 1);
19define('CAP_PREVENT', -1);
20define('CAP_PROHIBIT', -1000);
21
bbbf2d40 22// context definitions
23define('CONTEXT_SYSTEM', 10);
24define('CONTEXT_PERSONAL', 20);
25define('CONTEXT_USERID', 30);
26define('CONTEXT_COURSECAT', 40);
27define('CONTEXT_COURSE', 50);
28define('CONTEXT_GROUP', 60);
29define('CONTEXT_MODULE', 70);
30define('CONTEXT_BLOCK', 80);
31
340ea4e8 32$context_cache = array(); // Cache of all used context objects for performance (by level and instance)
33$context_cache_id = array(); // Index to above cache by id
bbbf2d40 34
cee0901c 35
bbbf2d40 36/**
37 * This functions get all the course categories in proper order
0468976c 38 * @param int $context
bbbf2d40 39 * @param int $type
40 * @return array of contextids
41 */
0468976c 42function get_parent_cats($context, $type) {
98882637 43
44 $parents = array();
98882637 45
46 switch($type) {
47
48 case CONTEXT_COURSECAT:
49
50 $cat = get_record('course_categories','id',$context->instanceid);
51 while ($cat->parent) {
52
53 $context = get_context_instance(CONTEXT_COURSECAT, $cat->parent);
54 $parents[] = $context->id;
55 $cat = get_record('course_categories','id',$cat->parent);
56 }
57
58 break;
59
60 case CONTEXT_COURSE:
61
62 $course = get_record('course', 'id', $context->instanceid);
63 $cat = get_record('course_categories','id',$course->category);
64 $catinstance = get_context_instance(CONTEXT_COURSECAT, $course->category);
65 $parents[] = $catinstance->id;
66
67 // what to do with cat 0?
68 while ($cat->parent) {
69 $context = get_context_instance(CONTEXT_COURSECAT, $cat->parent);
70 $parents[] = $context->id;
71 $cat = get_record('course_categories','id',$cat->parent);
72 }
73 break;
74
75 default:
76 break;
77
78 }
79
80 return array_reverse($parents);
bbbf2d40 81}
82
83
cee0901c 84
85/*************************************
86 * Functions for Roles & Capabilites *
87 *************************************/
bbbf2d40 88
89
0468976c 90/**
91 * This function checks for a capability assertion being true. If it isn't
92 * then the page is terminated neatly with a standard error message
93 * @param string $capability - name of the capability
94 * @param object $context - a context object (record from context table)
95 * @param integer $userid - a userid number
96 * @param string $errorstring - an errorstring
97 */
98function require_capability($capability, $context=NULL, $userid=NULL, $errormessage="nopermissions", $stringfile='') {
99 if (!has_capability($capability, $context, $userid)) {
100 $capabilityname = get_capability_string($capability);
101 print_error($errormessage, $stringfile, '', $capabilityname);
102 }
103}
104
105
bbbf2d40 106/**
107 * This function returns whether the current user has the capability of performing a function
108 * For example, we can do has_capability('mod/forum:replypost',$cm) in forum
109 * only one of the 4 (moduleinstance, courseid, site, userid) would be set at 1 time
110 * This is a recursive funciton.
bbbf2d40 111 * @uses $USER
112 * @param string $capability - name of the capability
0468976c 113 * @param object $context - a context object (record from context table)
114 * @param integer $userid - a userid number
bbbf2d40 115 * @return bool
116 */
0468976c 117function has_capability($capability, $context=NULL, $userid=NULL) {
bbbf2d40 118
340ea4e8 119 global $USER, $CONTEXT;
bbbf2d40 120
9425b25f 121 if ($userid) {
122 if (empty($USER->id) or ($userid != $USER->id)) {
123 $capabilities = load_user_capability($capability, $context, $userid);
124 } else { //$USER->id == $userid
125 $capabilities = empty($USER->capabilities) ? NULL : $USER->capabilities;
126 }
127 } else { // no userid
128 $capabilities = empty($USER->capabilities) ? NULL : $USER->capabilities;
98882637 129 }
9425b25f 130
0468976c 131 if (empty($context)) { // Use default CONTEXT if none specified
340ea4e8 132 if (empty($CONTEXT)) {
133 return false;
134 } else {
135 $context = $CONTEXT;
136 }
0468976c 137 } else { // A context was given to us
138 if (empty($CONTEXT)) {
139 $CONTEXT = $context; // Store FIRST used context in this global as future default
140 }
340ea4e8 141 }
bbbf2d40 142
98882637 143 // Check site
144 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
145 if (isset($capabilities[$sitecontext->id]['moodle/site:doanything'])) {
9425b25f 146 return (0 < $capabilities[$sitecontext->id]['moodle/site:doanything']);
98882637 147 }
148
d140ad3f 149 switch ($context->aggregatelevel) {
bbbf2d40 150
151 case CONTEXT_COURSECAT:
98882637 152 // Check parent cats.
0468976c 153 $parentcats = get_parent_cats($context, CONTEXT_COURSECAT);
98882637 154 foreach ($parentcats as $parentcat) {
155 if (isset($capabilities[$parentcat]['moodle/site:doanything'])) {
9425b25f 156 return (0 < $capabilities[$parentcat]['moodle/site:doanything']);
cee0901c 157 }
98882637 158 }
bbbf2d40 159 break;
160
161 case CONTEXT_COURSE:
98882637 162 // Check parent cat.
0468976c 163 $parentcats = get_parent_cats($context, CONTEXT_COURSE);
98882637 164
165 foreach ($parentcats as $parentcat) {
166 if (isset($capabilities[$parentcat]['do_anything'])) {
9425b25f 167 return (0 < $capabilities[$parentcat]['do_anything']);
168 }
98882637 169 }
bbbf2d40 170 break;
171
172 case CONTEXT_GROUP:
98882637 173 // Find course.
174 $group = get_record('groups','id',$context->instanceid);
bbbf2d40 175 $courseinstance = get_context_instance(CONTEXT_COURSE, $group->courseid);
9425b25f 176
177 $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE);
98882637 178 foreach ($parentcats as $parentcat) {
179 if (isset($capabilities[$parentcat->id]['do_anything'])) {
9425b25f 180 return (0 < $capabilities[$parentcat->id]['do_anything']);
181 }
182 }
183
98882637 184 $coursecontext = '';
185 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
9425b25f 186 return (0 < $capabilities[$courseinstance->id]['do_anything']);
187 }
188
bbbf2d40 189 break;
190
191 case CONTEXT_MODULE:
192 // Find course.
193 $cm = get_record('course_modules', 'id', $context->instanceid);
98882637 194 $courseinstance = get_context_instance(CONTEXT_COURSE, $cm->course);
9425b25f 195
196 if ($parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE)) {
98882637 197 foreach ($parentcats as $parentcat) {
198 if (isset($capabilities[$parentcat]['do_anything'])) {
9425b25f 199 return (0 < $capabilities[$parentcat]['do_anything']);
cee0901c 200 }
9425b25f 201 }
202 }
98882637 203
204 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
9425b25f 205 return (0 < $capabilities[$courseinstance->id]['do_anything']);
cee0901c 206 }
bbbf2d40 207
208 break;
209
210 case CONTEXT_BLOCK:
211 // 1 to 1 to course.
212 // Find course.
213 $block = get_record('block_instance','id',$context->instanceid);
214 $courseinstance = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
9425b25f 215
216 $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE);
98882637 217 foreach ($parentcats as $parentcat) {
218 if (isset($capabilities[$parentcat]['do_anything'])) {
9425b25f 219 return (0 < $capabilities[$parentcat]['do_anything']);
cee0901c 220 }
221 }
9425b25f 222
98882637 223 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
9425b25f 224 return (0 < $capabilities[$courseinstance->id]['do_anything']);
cee0901c 225 }
bbbf2d40 226 break;
227
228 default:
229 // CONTEXT_SYSTEM: CONTEXT_PERSONAL: CONTEXT_USERID:
230 // Do nothing.
231 break;
98882637 232 }
bbbf2d40 233
98882637 234 // Last: check self.
0468976c 235 if (isset($capabilities[$context->id]['do_anything'])) {
9425b25f 236 return (0 < $capabilities[$context->id]['do_anything']);
98882637 237 }
9425b25f 238
98882637 239 // do_anything has not been set, we now look for it the normal way.
9425b25f 240 return (0 < capability_search($capability, $context, $capabilities));
bbbf2d40 241
9425b25f 242}
bbbf2d40 243
244
245/**
246 * In a separate function so that we won't have to deal with do_anything.
247 * again. Used by function has_capability.
248 * @param $capability - capability string
0468976c 249 * @param $context - the context object
bbbf2d40 250 * @param $capabilities - either $USER->capability or loaded array
251 * @return permission (int)
252 */
0468976c 253function capability_search($capability, $context, $capabilities) {
bbbf2d40 254 global $USER, $CFG;
0468976c 255
9425b25f 256
0468976c 257 if (isset($capabilities[$context->id][$capability])) {
9425b25f 258 if ($CFG->debug > 15) {
259 notify("Found $capability in context $context->id at level $context->aggregatelevel: ".$capabilities[$context->id][$capability], 'notifytiny');
260 }
0468976c 261 return ($capabilities[$context->id][$capability]);
bbbf2d40 262 }
9425b25f 263
bbbf2d40 264 /* Then, we check the cache recursively */
9425b25f 265 $permission = 0;
266
d140ad3f 267 switch ($context->aggregatelevel) {
bbbf2d40 268
269 case CONTEXT_SYSTEM: // by now it's a definite an inherit
270 $permission = 0;
271 break;
272
273 case CONTEXT_PERSONAL:
0468976c 274 $parentcontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
275 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 276 break;
9425b25f 277
bbbf2d40 278 case CONTEXT_USERID:
0468976c 279 $parentcontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
280 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 281 break;
9425b25f 282
bbbf2d40 283 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
284 $coursecat = get_record('course_categories','id',$context->instanceid);
0468976c 285 if (!empty($coursecat->parent)) { // return parent value if it exists
286 $parentcontext = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
bbbf2d40 287 } else { // else return site value
0468976c 288 $parentcontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
bbbf2d40 289 }
0468976c 290 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 291 break;
292
293 case CONTEXT_COURSE: // 1 to 1 to course cat
294 // find the course cat, and return its value
295 $course = get_record('course','id',$context->instanceid);
0468976c 296 $parentcontext = get_context_instance(CONTEXT_COURSECAT, $course->category);
297 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 298 break;
299
300 case CONTEXT_GROUP: // 1 to 1 to course
301 $group = get_record('groups','id',$context->instanceid);
0468976c 302 $parentcontext = get_context_instance(CONTEXT_COURSE, $group->courseid);
303 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 304 break;
305
306 case CONTEXT_MODULE: // 1 to 1 to course
307 $cm = get_record('course_modules','id',$context->instanceid);
0468976c 308 $parentcontext = get_context_instance(CONTEXT_COURSE, $cm->course);
309 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 310 break;
311
312 case CONTEXT_BLOCK: // 1 to 1 to course
313 $block = get_record('block_instance','id',$context->instanceid);
0468976c 314 $parentcontext = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
315 $permission = capability_search($capability, $parentcontext, $capabilities);
bbbf2d40 316 break;
317
318 default:
319 error ('This is an unknown context!');
320 return false;
321 }
9425b25f 322 if ($CFG->debug > 15) {
323 notify("Found $capability recursively from context $context->id at level $context->aggregatelevel: $permission", 'notifytiny');
324 }
325
98882637 326 return $permission;
bbbf2d40 327}
328
329
330/**
331 * This function should be called immediately after a login, when $USER is set.
332 * It will build an array of all the capabilities at each level
333 * i.e. site/metacourse/course_category/course/moduleinstance
334 * Note we should only load capabilities if they are explicitly assigned already,
335 * we should not load all module's capability!
336 * @param $userid - the id of the user whose capabilities we want to load
337 * @return array
338 * possible just s simple 2D array with [contextid][capabilityname]
339 * [Capabilities] => [26][forum_post] = 1
340 * [26][forum_start] = -8990
341 * [26][forum_edit] = -1
342 * [273][blah blah] = 1
343 * [273][blah blah blah] = 2
344 */
0468976c 345function load_user_capability($capability='', $context ='', $userid='') {
d140ad3f 346
98882637 347 global $USER, $CFG;
bbbf2d40 348
349 if (empty($userid)) {
350 $userid = $USER->id;
351 } else {
9425b25f 352 $otheruserid = $userid;
bbbf2d40 353 }
9425b25f 354
bbbf2d40 355 if ($capability) {
9425b25f 356 $capsearch = " AND rc.capability = '$capability' ";
bbbf2d40 357 } else {
9425b25f 358 $capsearch ="";
bbbf2d40 359 }
5f70bcc3 360
361/// First we generate a list of all relevant contexts of the user
362
363 $usercontexts = array();
bbbf2d40 364
0468976c 365 if ($context) { // if context is specified
366 $usercontexts = get_parent_contexts($context);
98882637 367 } else { // else, we load everything
5f70bcc3 368 if ($userroles = get_records('role_assignments','userid',$userid)) {
369 foreach ($userroles as $userrole) {
370 $usercontexts[] = $userrole->contextid;
371 }
98882637 372 }
5f70bcc3 373 }
374
375/// Set up SQL fragments for searching contexts
376
377 if ($usercontexts) {
0468976c 378 $listofcontexts = '('.implode(',', $usercontexts).')';
5f70bcc3 379 $searchcontexts1 = "c1.id IN $listofcontexts AND";
380 $searchcontexts2 = "c2.id IN $listofcontexts AND";
381 } else {
382 $listofcontexts = $searchcontexts1 = $searchcontexts2 = '';
bbbf2d40 383 }
0468976c 384
5f70bcc3 385/// Then we use 1 giant SQL to bring out all relevant capabilities.
386/// The first part gets the capabilities of orginal role.
387/// The second part gets the capabilities of overriden roles.
bbbf2d40 388
98882637 389 $siteinstance = get_context_instance(CONTEXT_SYSTEM, SITEID);
bbbf2d40 390
75e84883 391 $SQL = " SELECT rc.capability, c1.id, (c1.aggregatelevel * 100) AS aggrlevel,
bbbf2d40 392 SUM(rc.permission) AS sum
393 FROM
171948fd 394 {$CFG->prefix}role_assignments AS ra,
395 {$CFG->prefix}role_capabilities AS rc,
396 {$CFG->prefix}context AS c1
bbbf2d40 397 WHERE
171948fd 398 ra.contextid=c1.id AND
399 ra.roleid=rc.roleid AND
bbbf2d40 400 ra.userid=$userid AND
5f70bcc3 401 $searchcontexts1
bbbf2d40 402 rc.contextid=$siteinstance->id
98882637 403 $capsearch
bbbf2d40 404 GROUP BY
75e84883 405 rc.capability,aggrlevel,c1.id
bbbf2d40 406 HAVING
41811960 407 SUM(rc.permission) != 0
bbbf2d40 408 UNION
409
75e84883 410 SELECT rc.capability, c1.id, (c1.aggregatelevel * 100 + c2.aggregatelevel) AS aggrlevel,
bbbf2d40 411 SUM(rc.permission) AS sum
412 FROM
171948fd 413 {$CFG->prefix}role_assignments AS ra,
414 {$CFG->prefix}role_capabilities AS rc,
415 {$CFG->prefix}context AS c1,
416 {$CFG->prefix}context AS c2
bbbf2d40 417 WHERE
171948fd 418 ra.contextid=c1.id AND
419 ra.roleid=rc.roleid AND
420 ra.userid=$userid AND
421 rc.contextid=c2.id AND
5f70bcc3 422 $searchcontexts1
423 $searchcontexts2
424 rc.contextid != $siteinstance->id
bbbf2d40 425 $capsearch
426
427 GROUP BY
75e84883 428 rc.capability, aggrlevel, c1.id
bbbf2d40 429 HAVING
41811960 430 SUM(rc.permission) != 0
bbbf2d40 431 ORDER BY
75e84883 432 aggrlevel ASC
bbbf2d40 433 ";
434
98882637 435 $capabilities = array(); // Reinitialize.
75e84883 436 if (!$rs = get_recordset_sql($SQL)) {
437 error("Query failed in load_user_capability.");
438 }
5cf38a57 439
bbbf2d40 440 if ($rs && $rs->RecordCount() > 0) {
441 while (!$rs->EOF) {
75e84883 442 $array = $rs->fields;
443 $temprecord = new object;
98882637 444
445 foreach ($array as $key=>$val) {
75e84883 446 if ($key == 'aggrlevel') {
447 $temprecord->aggregatelevel = $val;
448 } else {
449 $temprecord->{$key} = $val;
450 }
98882637 451 }
bbbf2d40 452 $capabilities[] = $temprecord;
453 $rs->MoveNext();
454 }
455 }
d140ad3f 456
bbbf2d40 457 /* so up to this point we should have somethign like this
41811960 458 * $capabilities[1] ->aggregatelevel = 1000
bbbf2d40 459 ->module = SITEID
460 ->capability = do_anything
461 ->id = 1 (id is the context id)
462 ->sum = 0
463
41811960 464 * $capabilities[2] ->aggregatelevel = 1000
bbbf2d40 465 ->module = SITEID
466 ->capability = post_messages
467 ->id = 1
468 ->sum = -9000
469
41811960 470 * $capabilittes[3] ->aggregatelevel = 3000
bbbf2d40 471 ->module = course
472 ->capability = view_course_activities
473 ->id = 25
474 ->sum = 1
475
41811960 476 * $capabilittes[4] ->aggregatelevel = 3000
bbbf2d40 477 ->module = course
478 ->capability = view_course_activities
479 ->id = 26
480 ->sum = 0 (this is another course)
481
41811960 482 * $capabilities[5] ->aggregatelevel = 3050
bbbf2d40 483 ->module = course
484 ->capability = view_course_activities
485 ->id = 25 (override in course 25)
486 ->sum = -1
487 * ....
488 * now we proceed to write the session array, going from top to bottom
489 * at anypoint, we need to go up and check parent to look for prohibit
490 */
491 // print_object($capabilities);
492
493 /* This is where we write to the actualy capabilities array
494 * what we need to do from here on is
495 * going down the array from lowest level to highest level
496 * 1) recursively check for prohibit,
497 * if any, we write prohibit
498 * else, we write the value
499 * 2) at an override level, we overwrite current level
500 * if it's not set to prohibit already, and if different
501 * ........ that should be it ........
502 */
98882637 503 $usercap = array(); // for other user's capabilities
bbbf2d40 504 foreach ($capabilities as $capability) {
505
0468976c 506 $context = get_context_instance_by_id($capability->id);
507
41811960 508 if (!empty($otheruserid)) { // we are pulling out other user's capabilities, do not write to session
98882637 509
0468976c 510 if (capability_prohibits($capability->capability, $context, $capability->sum, $usercap)) {
98882637 511 $usercap[$capability->id][$capability->capability] = -9000;
512 continue;
513 }
514
515 $usercap[$capability->id][$capability->capability] = $capability->sum;
516
517 } else {
518
0468976c 519 if (capability_prohibits($capability->capability, $context, $capability->sum)) { // if any parent or parent's parent is set to prohibit
98882637 520 $USER->capabilities[$capability->id][$capability->capability] = -9000;
521 continue;
522 }
523
524 // if no parental prohibit set
525 // just write to session, i am not sure this is correct yet
526 // since 3050 shows up after 3000, and 3070 shows up after 3050,
527 // it should be ok just to overwrite like this, provided that there's no
528 // parental prohibits
529 // no point writing 0, since 0 = inherit
530 // we need to write even if it's 0, because it could be an inherit override
531 $USER->capabilities[$capability->id][$capability->capability] = $capability->sum;
532 }
bbbf2d40 533 }
534
535 // now we don't care about the huge array anymore, we can dispose it.
536 unset($capabilities);
537
41811960 538 if (!empty($otheruseid)) {
98882637 539 return $usercap; // return the array
bbbf2d40 540 }
541 // see array in session to see what it looks like
542
543}
544
545
546/**
547 * This is a recursive function that checks whether the capability in this
548 * context, or the parent capabilities are set to prohibit.
549 *
550 * At this point, we can probably just use the values already set in the
551 * session variable, since we are going down the level. Any prohit set in
552 * parents would already reflect in the session.
553 *
554 * @param $capability - capability name
555 * @param $sum - sum of all capabilities values
0468976c 556 * @param $context - the context object
bbbf2d40 557 * @param $array - when loading another user caps, their caps are not stored in session but an array
558 */
0468976c 559function capability_prohibits($capability, $context, $sum='', $array='') {
bbbf2d40 560 global $USER;
0468976c 561
bbbf2d40 562 if ($sum < -8000) {
563 // If this capability is set to prohibit.
564 return true;
565 }
566
567 if (isset($array)) {
0468976c 568 if (isset($array[$context->id][$capability])
569 && $array[$context->id][$capability] < -8000) {
98882637 570 return true;
571 }
bbbf2d40 572 } else {
98882637 573 // Else if set in session.
0468976c 574 if (isset($USER->capabilities[$context->id][$capability])
575 && $USER->capabilities[$context->id][$capability] < -8000) {
98882637 576 return true;
577 }
bbbf2d40 578 }
d140ad3f 579 switch ($context->aggregatelevel) {
bbbf2d40 580
581 case CONTEXT_SYSTEM:
582 // By now it's a definite an inherit.
583 return 0;
584 break;
585
586 case CONTEXT_PERSONAL:
587 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
0468976c 588 return capability_prohibits($capability, $parent);
bbbf2d40 589 break;
590
591 case CONTEXT_USERID:
592 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
0468976c 593 return capability_prohibits($capability, $parent);
bbbf2d40 594 break;
595
596 case CONTEXT_COURSECAT:
597 // Coursecat -> coursecat or site.
598 $coursecat = get_record('course_categories','id',$context->instanceid);
41811960 599 if (!empty($coursecat->parent)) {
bbbf2d40 600 // return parent value if exist.
601 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
602 } else {
603 // Return site value.
604 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
605 }
0468976c 606 return capability_prohibits($capability, $parent);
bbbf2d40 607 break;
608
609 case CONTEXT_COURSE:
610 // 1 to 1 to course cat.
611 // Find the course cat, and return its value.
612 $course = get_record('course','id',$context->instanceid);
613 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
0468976c 614 return capability_prohibits($capability, $parent);
bbbf2d40 615 break;
616
617 case CONTEXT_GROUP:
618 // 1 to 1 to course.
619 $group = get_record('groups','id',$context->instanceid);
620 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
0468976c 621 return capability_prohibits($capability, $parent);
bbbf2d40 622 break;
623
624 case CONTEXT_MODULE:
625 // 1 to 1 to course.
626 $cm = get_record('course_modules','id',$context->instanceid);
627 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
0468976c 628 return capability_prohibits($capability, $parent);
bbbf2d40 629 break;
630
631 case CONTEXT_BLOCK:
632 // 1 to 1 to course.
633 $block = get_record('block_instance','id',$context->instanceid);
634 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
0468976c 635 return capability_prohibits($capability, $parent);
bbbf2d40 636 break;
637
638 default:
639 error ('This is an unknown context!');
640 return false;
641 }
642}
643
644
645/**
646 * A print form function. This should either grab all the capabilities from
647 * files or a central table for that particular module instance, then present
648 * them in check boxes. Only relevant capabilities should print for known
649 * context.
650 * @param $mod - module id of the mod
651 */
652function print_capabilities($modid=0) {
653 global $CFG;
654
655 $capabilities = array();
656
657 if ($modid) {
658 // We are in a module specific context.
659
660 // Get the mod's name.
661 // Call the function that grabs the file and parse.
662 $cm = get_record('course_modules', 'id', $modid);
663 $module = get_record('modules', 'id', $cm->module);
664
665 } else {
666 // Print all capabilities.
667 foreach ($capabilities as $capability) {
668 // Prints the check box component.
669 }
670 }
671}
672
673
674/**
1afecc03 675 * Installs the roles system.
676 * This function runs on a fresh install as well as on an upgrade from the old
677 * hard-coded student/teacher/admin etc. roles to the new roles system.
bbbf2d40 678 */
1afecc03 679function moodle_install_roles() {
bbbf2d40 680
1afecc03 681 global $CFG, $db;
682
bbbf2d40 683 // Create a system wide context for assignemnt.
684 $systemcontext = $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
685
1afecc03 686
687 // Create default/legacy roles and capabilities.
688 // (1 legacy capability per legacy role at system level).
bbbf2d40 689 $adminrole = create_role(get_string('administrator'), get_string('administratordescription'), 'moodle/legacy:admin');
98882637 690 if (!assign_capability('moodle/site:doanything', CAP_ALLOW, $adminrole, $systemcontext->id)) {
bbbf2d40 691 error('Could not assign moodle/site:doanything to the admin role');
692 }
693 $coursecreatorrole = create_role(get_string('coursecreators'), get_string('coursecreatorsdescription'), 'moodle/legacy:coursecreator');
98882637 694 $noneditteacherrole = create_role(get_string('noneditingteacher'), get_string('noneditingteacherdescription'), 'moodle/legacy:teacher');
695 $editteacherrole = create_role(get_string('defaultcourseteacher'), get_string('defaultcourseteacherdescription'), 'moodle/legacy:editingteacher');
696 $studentrole = create_role(get_string('defaultcoursestudent'), get_string('defaultcoursestudentdescription'), 'moodle/legacy:student');
bbbf2d40 697 $guestrole = create_role(get_string('guest'), get_string('guestdescription'), 'moodle/legacy:guest');
1afecc03 698
699
700 // Look inside user_admin, user_creator, user_teachers, user_students and
701 // assign above new roles. If a user has both teacher and student role,
702 // only teacher role is assigned. The assignment should be system level.
703 $dbtables = $db->MetaTables('TABLES');
bbbf2d40 704
1afecc03 705
98882637 706 /**
bbbf2d40 707 * Upgrade the admins.
1afecc03 708 * Sort using id ASC, first one is primary admin.
bbbf2d40 709 */
1afecc03 710 if (in_array($CFG->prefix.'user_admins', $dbtables)) {
711 if ($useradmins = get_records_sql('SELECT * from '.$CFG->prefix.'user_admins ORDER BY ID ASC')) {
712 foreach ($useradmins as $admin) {
713 role_assign($adminrole, $admin->userid, 0, $systemcontext->id);
714 }
715 }
716 } else {
717 // This is a fresh install.
bbbf2d40 718 }
1afecc03 719
720
bbbf2d40 721 /**
722 * Upgrade course creators.
723 */
1afecc03 724 if (in_array($CFG->prefix.'user_coursecreators', $dbtables)) {
725 if ($usercoursecreators = get_records('user_coursecreators')) {
726 foreach ($usercoursecreators as $coursecreator) {
727 role_assign($$coursecreatorrole, $coursecreator->userid, 0, $systemcontext->id);
728 }
729 }
bbbf2d40 730 }
731
1afecc03 732
bbbf2d40 733 /**
734 * Upgrade editting teachers and non-editting teachers.
735 */
1afecc03 736 if (in_array($CFG->prefix.'user_teachers', $dbtables)) {
737 if ($userteachers = get_records('user_teachers')) {
738 foreach ($userteachers as $teacher) {
739 $coursecontext = get_context_instance(CONTEXT_COURSE, $teacher->course); // needs cache
740 if ($teacher->editall) { // editting teacher
741 role_assign($editteacherrole, $teacher->userid, 0, $coursecontext->id);
742 } else {
743 role_assign($noneditteacherrole, $teacher->userid, 0, $coursecontext->id);
744 }
745 }
bbbf2d40 746 }
747 }
1afecc03 748
749
bbbf2d40 750 /**
751 * Upgrade students.
752 */
1afecc03 753 if (in_array($CFG->prefix.'user_students', $dbtables)) {
754 if ($userstudents = get_records('user_students')) {
755 foreach ($userstudents as $student) {
756 $coursecontext = get_context_instance(CONTEXT_COURSE, $student->course);
757 role_assign($studentrole, $student->userid, 0, $coursecontext->id);
758 }
759 }
bbbf2d40 760 }
1afecc03 761
762
bbbf2d40 763 /**
764 * Upgrade guest (only 1 entry).
765 */
1afecc03 766 if ($guestuser = get_record('user', 'username', 'guest')) {
767 role_assign($guestrole, $guestuser->id, 0, $systemcontext->id);
768 }
769
945f88ca 770 /**
771 * Insert the correct records for legacy roles
772 */
773 allow_assign($adminrole, $adminrole);
774 allow_assign($adminrole, $coursecreatorrole);
775 allow_assign($adminrole, $noneditteacherrole);
776 allow_assign($adminrole, $editteacherrole);
777 allow_assign($adminrole, $studentrole);
778 allow_assign($adminrole, $guestrole);
779
780 allow_assign($coursecreatorrole, $noneditteacherrole);
781 allow_assign($coursecreatorrole, $editteacherrole);
782 allow_assign($coursecreatorrole, $studentrole);
783 allow_assign($coursecreatorrole, $guestrole);
784
785 allow_assign($editteacherrole, $noneditteacherrole);
786 allow_assign($editteacherrole, $studentrole);
787 allow_assign($editteacherrole, $guestrole);
788
789 /// overrides
790 allow_override($adminrole, $adminrole);
791 allow_override($adminrole, $coursecreatorrole);
792 allow_override($adminrole, $noneditteacherrole);
793 allow_override($adminrole, $editteacherrole);
794 allow_override($adminrole, $studentrole);
5769734f 795 allow_override($adminrole, $guestrole);
1afecc03 796
797 // Should we delete the tables after we are done? Not yet.
bbbf2d40 798}
799
bbbf2d40 800/**
801 * Assign the defaults found in this capabality definition to roles that have
802 * the corresponding legacy capabilities assigned to them.
803 * @param $legacyperms - an array in the format (example):
804 * 'guest' => CAP_PREVENT,
805 * 'student' => CAP_ALLOW,
806 * 'teacher' => CAP_ALLOW,
807 * 'editingteacher' => CAP_ALLOW,
808 * 'coursecreator' => CAP_ALLOW,
809 * 'admin' => CAP_ALLOW
810 * @return boolean - success or failure.
811 */
812function assign_legacy_capabilities($capability, $legacyperms) {
813
814 foreach ($legacyperms as $type => $perm) {
815
816 $systemcontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
817
818 // The legacy capabilities are:
819 // 'moodle/legacy:guest'
820 // 'moodle/legacy:student'
821 // 'moodle/legacy:teacher'
822 // 'moodle/legacy:editingteacher'
823 // 'moodle/legacy:coursecreator'
824 // 'moodle/legacy:admin'
825
826 if (!$roles = get_roles_with_capability('moodle/legacy:'.$type, CAP_ALLOW)) {
827 return false;
828 }
829
830 foreach ($roles as $role) {
831 // Assign a site level capability.
832 if(!assign_capability($capability, $perm, $role->id, $systemcontext->id)) {
833 return false;
834 }
835 }
836 }
837 return true;
838}
839
840
cee0901c 841/**
842 * Checks to see if a capability is a legacy capability.
843 * @param $capabilityname
844 * @return boolean
845 */
bbbf2d40 846function islegacy($capabilityname) {
98882637 847 if (strstr($capabilityname, 'legacy') === false) {
848 return false;
849 } else {
850 return true;
851 }
bbbf2d40 852}
853
cee0901c 854
855
856/**********************************
bbbf2d40 857 * Context Manipulation functions *
858 **********************************/
859
bbbf2d40 860/**
861 * This should be called prolly everytime a user, group, module, course,
862 * coursecat or site is set up maybe?
863 * @param $level
864 * @param $instanceid
865 */
d140ad3f 866function create_context($aggregatelevel, $instanceid) {
867 if (!get_record('context','aggregatelevel',$aggregatelevel,'instanceid',$instanceid)) {
bbbf2d40 868 $context = new object;
d140ad3f 869 $context->aggregatelevel = $aggregatelevel;
bbbf2d40 870 $context->instanceid = $instanceid;
871 return insert_record('context',$context);
872 }
873}
874
875
876/**
877 * Get the context instance as an object. This function will create the
878 * context instance if it does not exist yet.
879 * @param $level
880 * @param $instance
881 */
d140ad3f 882function get_context_instance($aggregatelevel=NULL, $instance=SITEID) {
e5605780 883
51195e6f 884 global $context_cache, $context_cache_id, $CONTEXT;
d9a35e12 885
340ea4e8 886/// If no level is supplied then return the current global context if there is one
d140ad3f 887 if (empty($aggregatelevel)) {
340ea4e8 888 if (empty($CONTEXT)) {
889 if ($CFG->debug > 7) {
890 notify("Error: get_context_instance() called without a context");
891 }
892 } else {
893 return $CONTEXT;
894 }
e5605780 895 }
896
340ea4e8 897/// Check the cache
d140ad3f 898 if (isset($context_cache[$aggregatelevel][$instance])) { // Already cached
899 return $context_cache[$aggregatelevel][$instance];
e5605780 900 }
901
340ea4e8 902/// Get it from the database, or create it
d140ad3f 903 if (!$context = get_record('context', 'aggregatelevel', $aggregatelevel, 'instanceid', $instance)) {
904 create_context($aggregatelevel, $instance);
905 $context = get_record('context', 'aggregatelevel', $aggregatelevel, 'instanceid', $instance);
e5605780 906 }
907
340ea4e8 908/// Update the cache
d140ad3f 909 $context_cache[$aggregatelevel][$instance] = $context; // Cache it for later
340ea4e8 910 $context_cache_id[$context->id] = $context; // Cache it for later
e5605780 911
0468976c 912
bbbf2d40 913 return $context;
914}
915
cee0901c 916
340ea4e8 917/**
918 * Get a context instance as an object, from a given id.
919 * @param $id
920 */
921function get_context_instance_by_id($id) {
922
d9a35e12 923 global $context_cache, $context_cache_id;
924
340ea4e8 925 if (isset($context_cache_id[$id])) { // Already cached
75e84883 926 return $context_cache_id[$id];
340ea4e8 927 }
928
929 if ($context = get_record('context', 'id', $id)) { // Update the cache and return
d140ad3f 930 $context_cache[$context->aggregatelevel][$context->instanceid] = $context;
340ea4e8 931 $context_cache_id[$context->id] = $context;
932 return $context;
933 }
934
935 return false;
936}
937
bbbf2d40 938
8737be58 939/**
940 * Get the local override (if any) for a given capability in a role in a context
941 * @param $roleid
0468976c 942 * @param $contextid
943 * @param $capability
8737be58 944 */
945function get_local_override($roleid, $contextid, $capability) {
946 return get_record('role_capabilities', 'roleid', $roleid, 'capability', $capability, 'contextid', $contextid);
947}
948
949
bbbf2d40 950
951/************************************
952 * DB TABLE RELATED FUNCTIONS *
953 ************************************/
954
cee0901c 955/**
bbbf2d40 956 * function that creates a role
957 * @param name - role name
958 * @param description - role description
959 * @param legacy - optional legacy capability
960 * @return id or false
961 */
962function create_role($name, $description, $legacy='') {
98882637 963
964 // check for duplicate role name
965
966 if ($role = get_record('role','name', $name)) {
98882637 967 error('there is already a role with this name!');
968 }
969
970 $role->name = $name;
971 $role->description = $description;
972
973 if ($id = insert_record('role', $role)) {
1afecc03 974 if ($legacy) {
975 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
976 assign_capability($legacy, CAP_ALLOW, $id, $context->id);
98882637 977 }
978 return $id;
979 } else {
980 return false;
981 }
bbbf2d40 982
983}
984
cee0901c 985
bbbf2d40 986/**
987 * Function to write context specific overrides, or default capabilities.
988 * @param module - string name
989 * @param capability - string name
990 * @param contextid - context id
991 * @param roleid - role id
992 * @param permission - int 1,-1 or -1000
993 */
994function assign_capability($capability, $permission, $roleid, $contextid) {
98882637 995
996 global $USER;
997
998 if (empty($permission) || $permission == 0) { // if permission is not set
999 unassign_capability($capability, $roleid, $contextid);
1000 }
bbbf2d40 1001
1002 $cap = new object;
1003 $cap->contextid = $contextid;
1004 $cap->roleid = $roleid;
1005 $cap->capability = $capability;
1006 $cap->permission = $permission;
1007 $cap->timemodified = time();
9db12da7 1008 $cap->modifierid = empty($USER->id) ? 0 : $USER->id;
bbbf2d40 1009
1010 return insert_record('role_capabilities', $cap);
1011}
1012
1013
1014/**
1015 * Unassign a capability from a role.
1016 * @param $roleid - the role id
1017 * @param $capability - the name of the capability
1018 * @return boolean - success or failure
1019 */
1020function unassign_capability($capability, $roleid, $contextid=NULL) {
98882637 1021
1022 if (isset($contextid)) {
1023 $status = delete_records('role_capabilities', 'capability', $capability,
1024 'roleid', $roleid, 'contextid', $contextid);
1025 } else {
1026 $status = delete_records('role_capabilities', 'capability', $capability,
1027 'roleid', $roleid);
1028 }
1029 return $status;
bbbf2d40 1030}
1031
1032
1033/**
1034 * Get the roles that have a given capability.
1035 * @param $capability - capability name (string)
1036 * @param $permission - optional, the permission defined for this capability
1037 * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT
1038 * @return array or role objects
1039 */
1040function get_roles_with_capability($capability, $permission=NULL) {
1041
1042 global $CFG;
1043
1044 $selectroles = "SELECT r.*
1045 FROM {$CFG->prefix}role AS r,
1046 {$CFG->prefix}role_capabilities AS rc
1047 WHERE rc.capability = '$capability'
1048 AND rc.roleid = r.id";
1049
1050 if (isset($permission)) {
1051 $selectroles .= " AND rc.permission = '$permission'";
1052 }
1053 return get_records_sql($selectroles);
1054}
1055
1056
1057/**
1058 * This function makes a role-assignment (user to a role)
1059 * @param $roleid - the role of the id
1060 * @param $userid - userid
1061 * @param $groupid - group id
1062 * @param $contextid - id of the context
1063 * @param $timestart - time this assignment becomes effective
1064 * @param $timeend - time this assignemnt ceases to be effective
1065 * @uses $USER
1066 * @return id - new id of the assigment
1067 */
1068function role_assign($roleid, $userid, $groupid, $contextid, $timestart=0, $timeend=0, $hidden=0) {
aa311411 1069 global $USER, $CFG;
bbbf2d40 1070
218564ac 1071 if ($CFG->debug > 7) {
98882637 1072 notify("Assign roleid $roleid userid $userid contextid $contextid", 'notifytiny');
aa311411 1073 }
bbbf2d40 1074
1075 if (empty($roleid)) {
1076 error ('you need to select a role');
1077 }
1078
1079 if (empty($userid) && empty($groupid)) {
1080 error ('you need to assign this role to a user or a group');
1081 }
1082
1083 if (empty($contextid)) {
1084 error ('you need to assign this role to a context, e.g. a course, or an activity');
1085 }
1086
1087 $ra = new object;
1088 $ra->roleid = $roleid;
1089 $ra->contextid = $contextid;
1090 $ra->userid = $userid;
1091 $ra->hidden = $hidden;
1092 $ra->groupid = $groupid;
1093 $ra->timestart = $timestart;
1094 $ra->timeend = $timeend;
1095 $ra->timemodified = time();
9db12da7 1096 $ra->modifier = empty($USER->id) ? 0 : $USER->id;
bbbf2d40 1097
1098 return insert_record('role_assignments', $ra);
1099
1100}
1101
1102
1103/**
1104 * Deletes a role assignment.
1105 * @param $roleid
1106 * @param $userid
1107 * @param $groupid
1108 * @param $contextid
1109 * @return boolean - success or failure
1110 */
1111function role_unassign($roleid, $userid, $groupid, $contextid) {
98882637 1112 if ($groupid) {
1113 // do nothing yet as this is not implemented
1114 }
1115 else {
1116 return delete_records('role_assignments', 'userid', $userid,
1117 'roleid', $roleid, 'contextid', $contextid);
1118 }
bbbf2d40 1119}
1120
1121
1122/**
1123 * Loads the capability definitions for the component (from file). If no
1124 * capabilities are defined for the component, we simply return an empty array.
1125 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1126 * @return array of capabilities
1127 */
1128function load_capability_def($component) {
1129 global $CFG;
1130
1131 if ($component == 'moodle') {
1132 $defpath = $CFG->libdir.'/db/access.php';
1133 $varprefix = 'moodle';
1134 } else {
1135 $defpath = $CFG->dirroot.'/'.$component.'/db/access.php';
1136 $varprefix = str_replace('/', '_', $component);
1137 }
1138 $capabilities = array();
1139
1140 if (file_exists($defpath)) {
1141 require_once($defpath);
1142 $capabilities = ${$varprefix.'_capabilities'};
1143 }
1144 return $capabilities;
1145}
1146
1147
1148/**
1149 * Gets the capabilities that have been cached in the database for this
1150 * component.
1151 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1152 * @return array of capabilities
1153 */
1154function get_cached_capabilities($component='moodle') {
1155 if ($component == 'moodle') {
1156 $storedcaps = get_records_select('capabilities',
1157 "name LIKE 'moodle/%:%'");
1158 } else {
1159 $storedcaps = get_records_select('capabilities',
1160 "name LIKE '$component:%'");
1161 }
1162 return $storedcaps;
1163}
1164
1165
1166/**
1167 * Updates the capabilities table with the component capability definitions.
1168 * If no parameters are given, the function updates the core moodle
1169 * capabilities.
1170 *
1171 * Note that the absence of the db/access.php capabilities definition file
1172 * will cause any stored capabilities for the component to be removed from
1173 * the database.
1174 *
1175 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1176 * @return boolean
1177 */
1178function update_capabilities($component='moodle') {
1179
1180 $storedcaps = array();
1181 $filecaps = array();
1182
1183 $cachedcaps = get_cached_capabilities($component);
1184 if ($cachedcaps) {
1185 foreach ($cachedcaps as $cachedcap) {
1186 array_push($storedcaps, $cachedcap->name);
1187 }
1188 }
1189
1190 $filecaps = load_capability_def($component);
1191
1192 // Are there new capabilities in the file definition?
1193 $newcaps = array();
1194
1195 foreach ($filecaps as $filecap => $def) {
1196 if (!$storedcaps ||
1197 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
1198 $newcaps[$filecap] = $def;
1199 }
1200 }
1201 // Add new capabilities to the stored definition.
1202 foreach ($newcaps as $capname => $capdef) {
1203 $capability = new object;
1204 $capability->name = $capname;
1205 $capability->captype = $capdef['captype'];
1206 $capability->contextlevel = $capdef['contextlevel'];
1207 $capability->component = $component;
1208
1209 if (!insert_record('capabilities', $capability, false, 'id')) {
1210 return false;
1211 }
1212 // Do we need to assign the new capabilities to roles that have the
1213 // legacy capabilities moodle/legacy:* as well?
1214 if (isset($capdef['legacy']) && is_array($capdef['legacy']) &&
1215 !assign_legacy_capabilities($capname, $capdef['legacy'])) {
1216 error('Could not assign legacy capabilities');
1217 return false;
1218 }
1219 }
1220 // Are there any capabilities that have been removed from the file
1221 // definition that we need to delete from the stored capabilities and
1222 // role assignments?
1223 capabilities_cleanup($component, $filecaps);
1224
1225 return true;
1226}
1227
1228
1229/**
1230 * Deletes cached capabilities that are no longer needed by the component.
1231 * Also unassigns these capabilities from any roles that have them.
1232 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1233 * @param $newcapdef - array of the new capability definitions that will be
1234 * compared with the cached capabilities
1235 * @return int - number of deprecated capabilities that have been removed
1236 */
1237function capabilities_cleanup($component, $newcapdef=NULL) {
1238
1239 $removedcount = 0;
1240
1241 if ($cachedcaps = get_cached_capabilities($component)) {
1242 foreach ($cachedcaps as $cachedcap) {
1243 if (empty($newcapdef) ||
1244 array_key_exists($cachedcap->name, $newcapdef) === false) {
1245
1246 // Remove from capabilities cache.
1247 if (!delete_records('capabilities', 'name', $cachedcap->name)) {
1248 error('Could not delete deprecated capability '.$cachedcap->name);
1249 } else {
1250 $removedcount++;
1251 }
1252 // Delete from roles.
1253 if($roles = get_roles_with_capability($cachedcap->name)) {
1254 foreach($roles as $role) {
1255 if (!unassign_capability($role->id, $cachedcap->name)) {
1256 error('Could not unassign deprecated capability '.
1257 $cachedcap->name.' from role '.$role->name);
1258 }
1259 }
1260 }
1261 } // End if.
1262 }
1263 }
1264 return $removedcount;
1265}
1266
1267
1268
cee0901c 1269/****************
1270 * UI FUNCTIONS *
1271 ****************/
bbbf2d40 1272
1273
1274/**
1275 * prints human readable context identifier.
1276 */
0468976c 1277function print_context_name($context) {
340ea4e8 1278
ec0810ee 1279 $name = '';
d140ad3f 1280 switch ($context->aggregatelevel) {
ec0810ee 1281
bbbf2d40 1282 case CONTEXT_SYSTEM: // by now it's a definite an inherit
ec0810ee 1283 $name = get_string('site');
340ea4e8 1284 break;
bbbf2d40 1285
1286 case CONTEXT_PERSONAL:
ec0810ee 1287 $name = get_string('personal');
340ea4e8 1288 break;
1289
bbbf2d40 1290 case CONTEXT_USERID:
ec0810ee 1291 if ($user = get_record('user', 'id', $context->instanceid)) {
1292 $name = get_string('user').': '.fullname($user);
1293 }
340ea4e8 1294 break;
1295
bbbf2d40 1296 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
ec0810ee 1297 if ($category = get_record('course_categories', 'id', $context->instanceid)) {
1298 $name = get_string('category').': '.$category->name;
1299 }
340ea4e8 1300 break;
bbbf2d40 1301
1302 case CONTEXT_COURSE: // 1 to 1 to course cat
ec0810ee 1303 if ($course = get_record('course', 'id', $context->instanceid)) {
1304 $name = get_string('course').': '.$course->fullname;
1305 }
340ea4e8 1306 break;
bbbf2d40 1307
1308 case CONTEXT_GROUP: // 1 to 1 to course
ec0810ee 1309 if ($group = get_record('groups', 'id', $context->instanceid)) {
1310 $name = get_string('group').': '.$group->name;
1311 }
340ea4e8 1312 break;
bbbf2d40 1313
1314 case CONTEXT_MODULE: // 1 to 1 to course
98882637 1315 if ($cm = get_record('course_modules','id',$context->instanceid)) {
1316 if ($module = get_record('modules','id',$cm->module)) {
1317 if ($mod = get_record($module->name, 'id', $cm->instance)) {
ec0810ee 1318 $name = get_string('activitymodule').': '.$mod->name;
98882637 1319 }
ec0810ee 1320 }
1321 }
340ea4e8 1322 break;
bbbf2d40 1323
1324 case CONTEXT_BLOCK: // 1 to 1 to course
98882637 1325 if ($blockinstance = get_record('block_instance','id',$context->instanceid)) {
1326 if ($block = get_record('block','id',$blockinstance->blockid)) {
ec0810ee 1327 $name = get_string('blocks').': '.get_string($block->name, 'block_'.$block->name);
1328 }
1329 }
340ea4e8 1330 break;
bbbf2d40 1331
1332 default:
1333 error ('This is an unknown context!');
340ea4e8 1334 return false;
1335
1336 }
340ea4e8 1337 return $name;
bbbf2d40 1338}
1339
1340
1341/**
1342 * Extracts the relevant capabilities given a contextid.
1343 * All case based, example an instance of forum context.
1344 * Will fetch all forum related capabilities, while course contexts
1345 * Will fetch all capabilities
0468976c 1346 * @param object context
bbbf2d40 1347 * @return array();
1348 *
1349 * capabilities
1350 * `name` varchar(150) NOT NULL,
1351 * `captype` varchar(50) NOT NULL,
1352 * `contextlevel` int(10) NOT NULL,
1353 * `component` varchar(100) NOT NULL,
1354 */
0468976c 1355function fetch_context_capabilities($context) {
98882637 1356
1357 global $CFG;
bbbf2d40 1358
1359 $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display
98882637 1360
d140ad3f 1361 switch ($context->aggregatelevel) {
bbbf2d40 1362
98882637 1363 case CONTEXT_SYSTEM: // all
1364 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1365 break;
1366
1367 case CONTEXT_PERSONAL:
0a8a95c9 1368 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_PERSONAL;
bbbf2d40 1369 break;
1370
1371 case CONTEXT_USERID:
0a8a95c9 1372 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_USERID;
bbbf2d40 1373 break;
1374
1375 case CONTEXT_COURSECAT: // all
98882637 1376 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1377 break;
1378
1379 case CONTEXT_COURSE: // all
98882637 1380 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1381 break;
1382
1383 case CONTEXT_GROUP: // group caps
1384 break;
1385
1386 case CONTEXT_MODULE: // mod caps
98882637 1387 $cm = get_record('course_modules', 'id', $context->instanceid);
1388 $module = get_record('modules', 'id', $cm->module);
bbbf2d40 1389
98882637 1390 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE."
1391 and component = 'mod/$module->name'";
bbbf2d40 1392 break;
1393
1394 case CONTEXT_BLOCK: // block caps
98882637 1395 $cb = get_record('block_instance', 'id', $context->instanceid);
1396 $block = get_record('block', 'id', $cb->blockid);
bbbf2d40 1397
98882637 1398 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK."
1399 and component = 'block/$block->name'";
bbbf2d40 1400 break;
1401
1402 default:
1403 return false;
1404 }
1405
1406 $records = get_records_sql($SQL.' '.$sort);
1407 return $records;
1408
1409}
1410
1411
1412/**
1413 * This function pulls out all the resolved capabilities (overrides and
1414 * defaults) of a role used in capability overrieds in contexts at a given
1415 * context.
0a8a95c9 1416 * @param obj $context
bbbf2d40 1417 * @param int $roleid
1418 * @return array
1419 */
1648afb2 1420function role_context_capabilities($roleid, $context, $cap='') {
98882637 1421 global $CFG;
1422
1423 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
0468976c 1424 if ($sitecontext->id == $context->id) {
98882637 1425 return array();
1426 }
1427
1428 // first of all, figure out all parental contexts
98882637 1429 $contexts = array_reverse(get_parent_contexts($context));
1430 $contexts = '('.implode(',', $contexts).')';
1431
1648afb2 1432 if ($cap) {
1433 $search = ' AND rc.capability = "'.$cap.'" ';
1434 } else {
1435 $search = '';
1436 }
1437
98882637 1438 $SQL = "SELECT rc.* FROM {$CFG->prefix}role_capabilities rc, {$CFG->prefix}context c
1439 where rc.contextid in $contexts
1440 and rc.roleid = $roleid
1648afb2 1441 and rc.contextid = c.id $search
d140ad3f 1442 ORDER BY c.aggregatelevel DESC, rc.capability DESC";
1648afb2 1443
98882637 1444 $records = get_records_sql($SQL);
98882637 1445 $capabilities = array();
1446
1447 // We are traversing via reverse order.
1448 foreach ($records as $record) {
1449 // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
1450 if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
1451 $capabilities[$record->capability] = $record->permission;
1452 }
1453 }
1454 return $capabilities;
bbbf2d40 1455}
1456
1457
1458/**
0468976c 1459 * Recursive function which, given a context, find all parent context ids,
bbbf2d40 1460 * and return the array in reverse order, i.e. parent first, then grand
1461 * parent, etc.
1462 * @param object $context
1463 * @return array()
1464 */
bbbf2d40 1465function get_parent_contexts($context) {
1466
d140ad3f 1467 switch ($context->aggregatelevel) {
bbbf2d40 1468
1469 case CONTEXT_SYSTEM: // no parent
98882637 1470 return null;
bbbf2d40 1471 break;
1472
1473 case CONTEXT_PERSONAL:
1474 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1475 return array($parent->id);
1476 break;
1477
1478 case CONTEXT_USERID:
1479 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1480 return array($parent->id);
1481 break;
1482
1483 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
1484 $coursecat = get_record('course_categories','id',$context->instanceid);
1485 if ($coursecat->parent) { // return parent value if exist
1486 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1487 return array_merge(array($parent->id), get_parent_contexts($parent));
1488 } else { // else return site value
1489 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1490 return array($parent->id);
1491 }
1492 break;
1493
1494 case CONTEXT_COURSE: // 1 to 1 to course cat
1495 // find the course cat, and return its value
1496 $course = get_record('course','id',$context->instanceid);
1497 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1498 return array_merge(array($parent->id), get_parent_contexts($parent));
1499 break;
1500
1501 case CONTEXT_GROUP: // 1 to 1 to course
1502 $group = get_record('groups','id',$context->instanceid);
1503 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
1504 return array_merge(array($parent->id), get_parent_contexts($parent));
1505 break;
1506
1507 case CONTEXT_MODULE: // 1 to 1 to course
1508 $cm = get_record('course_modules','id',$context->instanceid);
1509 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
1510 return array_merge(array($parent->id), get_parent_contexts($parent));
1511 break;
1512
1513 case CONTEXT_BLOCK: // 1 to 1 to course
1514 $block = get_record('block_instance','id',$context->instanceid);
1515 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
1516 return array_merge(array($parent->id), get_parent_contexts($parent));
1517 break;
1518
1519 default:
1520 error ('This is an unknown context!');
1521 return false;
1522 }
1523
1524}
1525
1526
1527/**
1528 * This function gets the capability of a role in a given context.
1529 * It is needed when printing override forms.
1530 * @param int $contextid
bbbf2d40 1531 * @param string $capability
1532 * @param array $capabilities - array loaded using role_context_capabilities
1533 * @return int (allow, prevent, prohibit, inherit)
1534 */
bbbf2d40 1535function get_role_context_capability($contextid, $capability, $capabilities) {
98882637 1536 return $capabilities[$contextid][$capability];
bbbf2d40 1537}
1538
1539
cee0901c 1540/**
1541 * Returns the human-readable, translated version of the capability.
1542 * Basically a big switch statement.
1543 * @param $capabilityname - e.g. mod/choice:readresponses
1544 */
ceb83c70 1545function get_capability_string($capabilityname) {
bbbf2d40 1546
cee0901c 1547 // Typical capabilityname is mod/choice:readresponses
ceb83c70 1548
1549 $names = split('/', $capabilityname);
1550 $stringname = $names[1]; // choice:readresponses
1551 $components = split(':', $stringname);
1552 $componentname = $components[0]; // choice
98882637 1553
1554 switch ($names[0]) {
1555 case 'mod':
ceb83c70 1556 $string = get_string($stringname, $componentname);
98882637 1557 break;
1558
1559 case 'block':
ceb83c70 1560 $string = get_string($stringname, 'block_'.$componentname);
98882637 1561 break;
ceb83c70 1562
98882637 1563 case 'moodle':
ceb83c70 1564 $string = get_string($stringname, 'role');
98882637 1565 break;
1566
1567 case 'enrol':
ceb83c70 1568 $string = get_string($stringname, 'enrol_'.$componentname);
1569 break;
98882637 1570
1571 default:
ceb83c70 1572 $string = get_string($stringname);
98882637 1573 break;
98882637 1574
1575 }
ceb83c70 1576 return $string;
bbbf2d40 1577}
1578
1579
cee0901c 1580/**
1581 * This gets the mod/block/course/core etc strings.
1582 * @param $component
1583 * @param $contextlevel
1584 */
bbbf2d40 1585function get_component_string($component, $contextlevel) {
1586
98882637 1587 switch ($contextlevel) {
bbbf2d40 1588
98882637 1589 case CONTEXT_SYSTEM:
ceb83c70 1590 $string = get_string('coresystem');
bbbf2d40 1591 break;
1592
1593 case CONTEXT_PERSONAL:
98882637 1594 $string = get_string('personal');
bbbf2d40 1595 break;
1596
1597 case CONTEXT_USERID:
98882637 1598 $string = get_string('users');
bbbf2d40 1599 break;
1600
1601 case CONTEXT_COURSECAT:
98882637 1602 $string = get_string('categories');
bbbf2d40 1603 break;
1604
1605 case CONTEXT_COURSE:
98882637 1606 $string = get_string('course');
bbbf2d40 1607 break;
1608
1609 case CONTEXT_GROUP:
98882637 1610 $string = get_string('group');
bbbf2d40 1611 break;
1612
1613 case CONTEXT_MODULE:
98882637 1614 $string = get_string('modulename', basename($component));
bbbf2d40 1615 break;
1616
1617 case CONTEXT_BLOCK:
98882637 1618 $string = get_string('blockname', 'block_'.$component.'.php');
bbbf2d40 1619 break;
1620
1621 default:
1622 error ('This is an unknown context!');
1623 return false;
98882637 1624
1625 }
98882637 1626 return $string;
bbbf2d40 1627}
cee0901c 1628
945f88ca 1629/** gets the list of roles assigned to this context
1630 * @param object $context
1631 * @return array
1632 */
e4dd3222 1633function get_roles_used_in_context($context) {
1634
1635 global $CFG;
1636
1637 return get_records_sql('SELECT distinct r.id, r.name
1638 FROM '.$CFG->prefix.'role_assignments ra,
1639 '.$CFG->prefix.'role r
1640 WHERE r.id = ra.roleid
1641 AND ra.contextid = '.$context->id.'
1642 ORDER BY r.sortorder ASC');
1643}
1644
945f88ca 1645/** this function is used to print roles column in user profile page.
1646 * @param int userid
1647 * @param int contextid
1648 * @return string
1649 */
0a8a95c9 1650function get_user_roles_in_context($userid, $contextid){
1651 global $CFG;
1652
1653 $rolestring = '';
1654 $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';
1655 if ($roles = get_records_sql($SQL)) {
1656 foreach ($roles as $userrole) {
1657 $rolestring .= '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$userrole->contextid.'&amp;roleid='.$userrole->roleid.'">'.$userrole->name.'</a>, ';
1658 }
1659
1660 }
1661 return rtrim($rolestring, ', ');
1662}
68c52526 1663
1664
945f88ca 1665/**
1666 * Checks if a user can override capabilities of a particular role in this context
1667 * @param object $context
1668 * @param int targetroleid - the id of the role you want to override
1669 * @return boolean
1670 */
68c52526 1671function user_can_override($context, $targetroleid) {
1672 // first check if user has override capability
1673 // if not return false;
1674 if (!has_capability('moodle/role:override', $context)) {
1675 return false;
1676 }
1677 // pull out all active roles of this user from this context(or above)
c0614051 1678 if ($userroles = get_user_roles($context)) {
1679 foreach ($userroles as $userrole) {
1680 // if any in the role_allow_override table, then it's ok
1681 if (get_record('role_allow_override', 'roleid', $userrole->roleid, 'allowoverride', $targetroleid)) {
1682 return true;
1683 }
68c52526 1684 }
1685 }
1686
1687 return false;
1688
1689}
1690
945f88ca 1691/**
1692 * Checks if a user can assign users to a particular role in this context
1693 * @param object $context
1694 * @param int targetroleid - the id of the role you want to assign users to
1695 * @return boolean
1696 */
68c52526 1697function user_can_assign($context, $targetroleid) {
1698
1699 // first check if user has override capability
1700 // if not return false;
1701 if (!has_capability('moodle/role:assign', $context)) {
1702 return false;
1703 }
1704 // pull out all active roles of this user from this context(or above)
c0614051 1705 if ($userroles = get_user_roles($context)) {
1706 foreach ($userroles as $userrole) {
1707 // if any in the role_allow_override table, then it's ok
1708 if (get_record('role_allow_assign', 'roleid', $userrole->roleid, 'allowassign', $targetroleid)) {
1709 return true;
1710 }
68c52526 1711 }
1712 }
1713
1714 return false;
1715}
1716
945f88ca 1717/**
1718 * gets all the user roles assigned in this context, or higher contexts
1719 * this is mainly used when checking if a user can assign a role, or overriding a role
1720 * i.e. we need to know what this user holds, in order to verify against allow_assign and
1721 * allow_override tables
1722 * @param object $context
1723 * @param int $userid
1724 * @return array
1725 */
c0614051 1726function get_user_roles($context, $userid=0) {
68c52526 1727
1728 global $USER, $CFG, $db;
c0614051 1729
1730 if (empty($userid)) {
1731 if (empty($USER->id)) {
1732 return array();
1733 }
1734 $userid = $USER->id;
1735 }
1736
1737 if ($parents = get_parent_contexts($context)) {
1738 $contexts = ' AND ra.contextid IN ('.implode(',' , $parents).')';
1739 } else {
1740 $contexts = ' AND ra.contextid = \''.$context->id.'\'';
1741 }
1742
68c52526 1743 return get_records_sql('SELECT *
1744 FROM '.$CFG->prefix.'role_assignments ra
c0614051 1745 WHERE ra.userid = '.$userid.
1746 $contexts);
68c52526 1747}
1748
945f88ca 1749/**
1750 * Creates a record in the allow_override table
1751 * @param int sroleid - source roleid
1752 * @param int troleid - target roleid
1753 * @return int - id or false
1754 */
1755function allow_override($sroleid, $troleid) {
1756 $record->roleid = $sroleid;
1757 $record->allowoverride = $troleid;
1758 return insert_record('role_allow_override', $record);
1759}
1760
1761/**
1762 * Creates a record in the allow_assign table
1763 * @param int sroleid - source roleid
1764 * @param int troleid - target roleid
1765 * @return int - id or false
1766 */
1767function allow_assign($sroleid, $troleid) {
1768 $record->roleid = $sroleid;
1769 $record->allowassign = $troleid;
1770 return insert_record('role_allow_assign', $record);
1771}
1772
1773/**
1774 * gets a list of roles assignalbe in this context for this user
1775 * @param object $context
1776 * @return array
1777 */
1778function get_assignable_roles ($context) {
1779
1780 $role = get_records('role');
1781 $options = array();
1782 foreach ($role as $rolex) {
1783 if (user_can_assign($context, $rolex->id)) {
1784 $options[$rolex->id] = $rolex->name;
1785 }
1786 }
1787 return $options;
1788}
1789
1790/**
1791 * gets a list of roles that can be overriden in this context by this user
1792 * @param object $context
1793 * @return array
1794 */
1795function get_overridable_roles ($context) {
1796
1797 $role = get_records('role');
1798 $options = array();
1799 foreach ($role as $rolex) {
1800 if (user_can_override($context, $rolex->id)) {
1801 $options[$rolex->id] = $rolex->name;
1802 }
1803 }
1804
1805 return $options;
1806
1807}
1648afb2 1808
1809
1810/**
1811 * who has this capability in this context
1812 * does not handling user level resolving!!!
1813 * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly
1814 * @param $context - object
1815 * @param $capability - string capability
1816 * @param $fields - fields to be pulled
1817 * @param $sort - the sort order
1818 */
1819function get_users_by_capability($context, $capability, $fields='distinct u.*', $sort='') {
1820
1821 global $CFG;
1822
1823 // first get all roles with this capability in this context, or above
1824 $possibleroles = get_roles_with_capability($capability, CAP_ALLOW);
1825 $validroleids = array();
1826 foreach ($possibleroles as $prole) {
1827 $caps = role_context_capabilities($prole->id, $context, $capability); // resolved list
1828 if ($caps[$capability] > 0) { // resolved capability > 0
1829 $validroleids[] = $prole->id;
1830 }
1831 }
1832
1833 if ($usercontexts = get_parent_contexts($context)) {
1834 $listofcontexts = '('.implode(',', $usercontexts).')';
1835 } else {
1836 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
1837 $listofcontexts = '('.$sitecontext->id.')'; // must be site
1838 }
1839
1840 $roleids = '('.implode(',', $validroleids).')';
1841
1842 $select = ' SELECT '.$fields;
1843 $from = ' FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'role_assignments ra ON ra.userid = u.id ';
1844 $where = ' WHERE (ra.contextid = '.$context->id.' OR ra.contextid in '.$listofcontexts.') AND u.deleted = 0 AND ra.roleid in '.$roleids.' ';
1845
1846 return get_records_sql($select.$from.$where);
1847
1848}
e4dd3222 1849?>