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