* New strings for Authorize.net eCheck.
[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
9ebcb4d2 1132
a9e1c058 1133 $newra = new object;
bbbf2d40 1134
a9e1c058 1135 if (empty($ra)) { // Create a new entry
1136 $newra->roleid = $roleid;
1137 $newra->contextid = $contextid;
1138 $newra->userid = $userid;
1139 $newra->groupid = $groupid;
1140
1141 $newra->hidden = $hidden;
f44152f4 1142 $newra->enrol = $enrol;
a9e1c058 1143 $newra->timestart = $timestart;
1144 $newra->timeend = $timeend;
1145 $newra->timemodified = time();
1146 $newra->modifier = empty($USER->id) ? 0 : $USER->id;
1147
9ebcb4d2 1148 $success = insert_record('role_assignments', $newra);
a9e1c058 1149
1150 } else { // We already have one, just update it
1151
1152 $newra->id = $ra->id;
1153 $newra->hidden = $hidden;
f44152f4 1154 $newra->enrol = $enrol;
a9e1c058 1155 $newra->timestart = $timestart;
1156 $newra->timeend = $timeend;
1157 $newra->timemodified = time();
1158 $newra->modifier = empty($USER->id) ? 0 : $USER->id;
1159
9ebcb4d2 1160 $success = update_record('role_assignments', $newra);
1161 }
1162
1163/// If the user is the current user, then reload the capabilities too.
1164 if ($success && !empty($USER->id) && $USER->id == $userid) {
1165 load_user_capability();
a9e1c058 1166 }
bbbf2d40 1167}
1168
1169
1170/**
1dc1f037 1171 * Deletes one or more role assignments. You must specify at least one parameter.
bbbf2d40 1172 * @param $roleid
1173 * @param $userid
1174 * @param $groupid
1175 * @param $contextid
1176 * @return boolean - success or failure
1177 */
1dc1f037 1178function role_unassign($roleid=0, $userid=0, $groupid=0, $contextid=0) {
1179 $args = array('roleid', 'userid', 'groupid', 'contextid');
1180 $select = array();
1181 foreach ($args as $arg) {
1182 if ($$arg) {
1183 $select[] = $arg.' = '.$$arg;
1184 }
1185 }
1186 if ($select) {
1187 return delete_records_select('role_assignments', implode(' AND ', $select));
1188 }
1189 return false;
bbbf2d40 1190}
1191
1192
1193/**
1194 * Loads the capability definitions for the component (from file). If no
1195 * capabilities are defined for the component, we simply return an empty array.
1196 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1197 * @return array of capabilities
1198 */
1199function load_capability_def($component) {
1200 global $CFG;
1201
1202 if ($component == 'moodle') {
1203 $defpath = $CFG->libdir.'/db/access.php';
1204 $varprefix = 'moodle';
1205 } else {
1206 $defpath = $CFG->dirroot.'/'.$component.'/db/access.php';
1207 $varprefix = str_replace('/', '_', $component);
1208 }
1209 $capabilities = array();
1210
1211 if (file_exists($defpath)) {
1212 require_once($defpath);
1213 $capabilities = ${$varprefix.'_capabilities'};
1214 }
1215 return $capabilities;
1216}
1217
1218
1219/**
1220 * Gets the capabilities that have been cached in the database for this
1221 * component.
1222 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1223 * @return array of capabilities
1224 */
1225function get_cached_capabilities($component='moodle') {
1226 if ($component == 'moodle') {
1227 $storedcaps = get_records_select('capabilities',
1228 "name LIKE 'moodle/%:%'");
1229 } else {
1230 $storedcaps = get_records_select('capabilities',
1231 "name LIKE '$component:%'");
1232 }
1233 return $storedcaps;
1234}
1235
1236
1237/**
1238 * Updates the capabilities table with the component capability definitions.
1239 * If no parameters are given, the function updates the core moodle
1240 * capabilities.
1241 *
1242 * Note that the absence of the db/access.php capabilities definition file
1243 * will cause any stored capabilities for the component to be removed from
1244 * the database.
1245 *
1246 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1247 * @return boolean
1248 */
1249function update_capabilities($component='moodle') {
1250
1251 $storedcaps = array();
be4486da 1252
1253 $filecaps = load_capability_def($component);
bbbf2d40 1254 $cachedcaps = get_cached_capabilities($component);
1255 if ($cachedcaps) {
1256 foreach ($cachedcaps as $cachedcap) {
1257 array_push($storedcaps, $cachedcap->name);
be4486da 1258 // update risk bitmasks in existing capabilitites if needed
1259 if (array_key_exists($cachedcap->name, $filecaps)) {
1260 if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) {
2b531945 1261 $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified
be4486da 1262 }
1263 if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) {
1264 $updatecap = new object;
1265 $updatecap->id = $cachedcap->id;
1266 $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask'];
1267 if (!update_record('capabilities', $updatecap)) {
1268 return false;
1269 }
1270 }
1271 }
bbbf2d40 1272 }
1273 }
be4486da 1274
bbbf2d40 1275 // Are there new capabilities in the file definition?
1276 $newcaps = array();
1277
1278 foreach ($filecaps as $filecap => $def) {
1279 if (!$storedcaps ||
1280 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
2b531945 1281 if (!array_key_exists('riskbitmask', $def)) {
1282 $def['riskbitmask'] = 0; // no risk if not specified
1283 }
bbbf2d40 1284 $newcaps[$filecap] = $def;
1285 }
1286 }
1287 // Add new capabilities to the stored definition.
1288 foreach ($newcaps as $capname => $capdef) {
1289 $capability = new object;
1290 $capability->name = $capname;
1291 $capability->captype = $capdef['captype'];
1292 $capability->contextlevel = $capdef['contextlevel'];
1293 $capability->component = $component;
be4486da 1294 $capability->riskbitmask = $capdef['riskbitmask'];
bbbf2d40 1295
1296 if (!insert_record('capabilities', $capability, false, 'id')) {
1297 return false;
1298 }
1299 // Do we need to assign the new capabilities to roles that have the
1300 // legacy capabilities moodle/legacy:* as well?
1301 if (isset($capdef['legacy']) && is_array($capdef['legacy']) &&
1302 !assign_legacy_capabilities($capname, $capdef['legacy'])) {
1303 error('Could not assign legacy capabilities');
1304 return false;
1305 }
1306 }
1307 // Are there any capabilities that have been removed from the file
1308 // definition that we need to delete from the stored capabilities and
1309 // role assignments?
1310 capabilities_cleanup($component, $filecaps);
1311
1312 return true;
1313}
1314
1315
1316/**
1317 * Deletes cached capabilities that are no longer needed by the component.
1318 * Also unassigns these capabilities from any roles that have them.
1319 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1320 * @param $newcapdef - array of the new capability definitions that will be
1321 * compared with the cached capabilities
1322 * @return int - number of deprecated capabilities that have been removed
1323 */
1324function capabilities_cleanup($component, $newcapdef=NULL) {
1325
1326 $removedcount = 0;
1327
1328 if ($cachedcaps = get_cached_capabilities($component)) {
1329 foreach ($cachedcaps as $cachedcap) {
1330 if (empty($newcapdef) ||
1331 array_key_exists($cachedcap->name, $newcapdef) === false) {
1332
1333 // Remove from capabilities cache.
1334 if (!delete_records('capabilities', 'name', $cachedcap->name)) {
1335 error('Could not delete deprecated capability '.$cachedcap->name);
1336 } else {
1337 $removedcount++;
1338 }
1339 // Delete from roles.
1340 if($roles = get_roles_with_capability($cachedcap->name)) {
1341 foreach($roles as $role) {
1342 if (!unassign_capability($role->id, $cachedcap->name)) {
1343 error('Could not unassign deprecated capability '.
1344 $cachedcap->name.' from role '.$role->name);
1345 }
1346 }
1347 }
1348 } // End if.
1349 }
1350 }
1351 return $removedcount;
1352}
1353
1354
1355
cee0901c 1356/****************
1357 * UI FUNCTIONS *
1358 ****************/
bbbf2d40 1359
1360
1361/**
1362 * prints human readable context identifier.
1363 */
0468976c 1364function print_context_name($context) {
340ea4e8 1365
ec0810ee 1366 $name = '';
d140ad3f 1367 switch ($context->aggregatelevel) {
ec0810ee 1368
bbbf2d40 1369 case CONTEXT_SYSTEM: // by now it's a definite an inherit
ec0810ee 1370 $name = get_string('site');
340ea4e8 1371 break;
bbbf2d40 1372
1373 case CONTEXT_PERSONAL:
ec0810ee 1374 $name = get_string('personal');
340ea4e8 1375 break;
1376
bbbf2d40 1377 case CONTEXT_USERID:
ec0810ee 1378 if ($user = get_record('user', 'id', $context->instanceid)) {
1379 $name = get_string('user').': '.fullname($user);
1380 }
340ea4e8 1381 break;
1382
bbbf2d40 1383 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
ec0810ee 1384 if ($category = get_record('course_categories', 'id', $context->instanceid)) {
1385 $name = get_string('category').': '.$category->name;
1386 }
340ea4e8 1387 break;
bbbf2d40 1388
1389 case CONTEXT_COURSE: // 1 to 1 to course cat
ec0810ee 1390 if ($course = get_record('course', 'id', $context->instanceid)) {
1391 $name = get_string('course').': '.$course->fullname;
1392 }
340ea4e8 1393 break;
bbbf2d40 1394
1395 case CONTEXT_GROUP: // 1 to 1 to course
ec0810ee 1396 if ($group = get_record('groups', 'id', $context->instanceid)) {
1397 $name = get_string('group').': '.$group->name;
1398 }
340ea4e8 1399 break;
bbbf2d40 1400
1401 case CONTEXT_MODULE: // 1 to 1 to course
98882637 1402 if ($cm = get_record('course_modules','id',$context->instanceid)) {
1403 if ($module = get_record('modules','id',$cm->module)) {
1404 if ($mod = get_record($module->name, 'id', $cm->instance)) {
ec0810ee 1405 $name = get_string('activitymodule').': '.$mod->name;
98882637 1406 }
ec0810ee 1407 }
1408 }
340ea4e8 1409 break;
bbbf2d40 1410
1411 case CONTEXT_BLOCK: // 1 to 1 to course
98882637 1412 if ($blockinstance = get_record('block_instance','id',$context->instanceid)) {
1413 if ($block = get_record('block','id',$blockinstance->blockid)) {
ec0810ee 1414 $name = get_string('blocks').': '.get_string($block->name, 'block_'.$block->name);
1415 }
1416 }
340ea4e8 1417 break;
bbbf2d40 1418
1419 default:
1420 error ('This is an unknown context!');
340ea4e8 1421 return false;
1422
1423 }
340ea4e8 1424 return $name;
bbbf2d40 1425}
1426
1427
1428/**
1429 * Extracts the relevant capabilities given a contextid.
1430 * All case based, example an instance of forum context.
1431 * Will fetch all forum related capabilities, while course contexts
1432 * Will fetch all capabilities
0468976c 1433 * @param object context
bbbf2d40 1434 * @return array();
1435 *
1436 * capabilities
1437 * `name` varchar(150) NOT NULL,
1438 * `captype` varchar(50) NOT NULL,
1439 * `contextlevel` int(10) NOT NULL,
1440 * `component` varchar(100) NOT NULL,
1441 */
0468976c 1442function fetch_context_capabilities($context) {
98882637 1443
1444 global $CFG;
bbbf2d40 1445
1446 $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display
98882637 1447
d140ad3f 1448 switch ($context->aggregatelevel) {
bbbf2d40 1449
98882637 1450 case CONTEXT_SYSTEM: // all
1451 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1452 break;
1453
1454 case CONTEXT_PERSONAL:
0a8a95c9 1455 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_PERSONAL;
bbbf2d40 1456 break;
1457
1458 case CONTEXT_USERID:
0a8a95c9 1459 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_USERID;
bbbf2d40 1460 break;
1461
1462 case CONTEXT_COURSECAT: // all
98882637 1463 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1464 break;
1465
1466 case CONTEXT_COURSE: // all
98882637 1467 $SQL = "select * from {$CFG->prefix}capabilities";
bbbf2d40 1468 break;
1469
1470 case CONTEXT_GROUP: // group caps
1471 break;
1472
1473 case CONTEXT_MODULE: // mod caps
98882637 1474 $cm = get_record('course_modules', 'id', $context->instanceid);
1475 $module = get_record('modules', 'id', $cm->module);
bbbf2d40 1476
98882637 1477 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE."
1478 and component = 'mod/$module->name'";
bbbf2d40 1479 break;
1480
1481 case CONTEXT_BLOCK: // block caps
98882637 1482 $cb = get_record('block_instance', 'id', $context->instanceid);
1483 $block = get_record('block', 'id', $cb->blockid);
bbbf2d40 1484
98882637 1485 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK."
1486 and component = 'block/$block->name'";
bbbf2d40 1487 break;
1488
1489 default:
1490 return false;
1491 }
1492
1493 $records = get_records_sql($SQL.' '.$sort);
1494 return $records;
1495
1496}
1497
1498
1499/**
1500 * This function pulls out all the resolved capabilities (overrides and
1501 * defaults) of a role used in capability overrieds in contexts at a given
1502 * context.
0a8a95c9 1503 * @param obj $context
bbbf2d40 1504 * @param int $roleid
1505 * @return array
1506 */
1648afb2 1507function role_context_capabilities($roleid, $context, $cap='') {
98882637 1508 global $CFG;
1509
1510 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
0468976c 1511 if ($sitecontext->id == $context->id) {
98882637 1512 return array();
1513 }
1514
1515 // first of all, figure out all parental contexts
98882637 1516 $contexts = array_reverse(get_parent_contexts($context));
1517 $contexts = '('.implode(',', $contexts).')';
1518
1648afb2 1519 if ($cap) {
1520 $search = ' AND rc.capability = "'.$cap.'" ';
1521 } else {
1522 $search = '';
1523 }
1524
98882637 1525 $SQL = "SELECT rc.* FROM {$CFG->prefix}role_capabilities rc, {$CFG->prefix}context c
1526 where rc.contextid in $contexts
1527 and rc.roleid = $roleid
1648afb2 1528 and rc.contextid = c.id $search
d140ad3f 1529 ORDER BY c.aggregatelevel DESC, rc.capability DESC";
1648afb2 1530
98882637 1531 $records = get_records_sql($SQL);
98882637 1532 $capabilities = array();
1533
1534 // We are traversing via reverse order.
1535 foreach ($records as $record) {
1536 // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
1537 if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
1538 $capabilities[$record->capability] = $record->permission;
1539 }
1540 }
1541 return $capabilities;
bbbf2d40 1542}
1543
1544
1545/**
0468976c 1546 * Recursive function which, given a context, find all parent context ids,
bbbf2d40 1547 * and return the array in reverse order, i.e. parent first, then grand
1548 * parent, etc.
1549 * @param object $context
1550 * @return array()
1551 */
bbbf2d40 1552function get_parent_contexts($context) {
1553
d140ad3f 1554 switch ($context->aggregatelevel) {
bbbf2d40 1555
1556 case CONTEXT_SYSTEM: // no parent
98882637 1557 return null;
bbbf2d40 1558 break;
1559
1560 case CONTEXT_PERSONAL:
1561 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1562 return array($parent->id);
1563 break;
1564
1565 case CONTEXT_USERID:
1566 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1567 return array($parent->id);
1568 break;
1569
1570 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
1571 $coursecat = get_record('course_categories','id',$context->instanceid);
c5ddc3fd 1572 if (!empty($coursecat->parent)) { // return parent value if exist
bbbf2d40 1573 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1574 return array_merge(array($parent->id), get_parent_contexts($parent));
1575 } else { // else return site value
1576 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1577 return array($parent->id);
1578 }
1579 break;
1580
1581 case CONTEXT_COURSE: // 1 to 1 to course cat
1582 // find the course cat, and return its value
1583 $course = get_record('course','id',$context->instanceid);
1584 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1585 return array_merge(array($parent->id), get_parent_contexts($parent));
1586 break;
1587
1588 case CONTEXT_GROUP: // 1 to 1 to course
1589 $group = get_record('groups','id',$context->instanceid);
1590 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
1591 return array_merge(array($parent->id), get_parent_contexts($parent));
1592 break;
1593
1594 case CONTEXT_MODULE: // 1 to 1 to course
1595 $cm = get_record('course_modules','id',$context->instanceid);
1596 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
1597 return array_merge(array($parent->id), get_parent_contexts($parent));
1598 break;
1599
1600 case CONTEXT_BLOCK: // 1 to 1 to course
1601 $block = get_record('block_instance','id',$context->instanceid);
1602 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
1603 return array_merge(array($parent->id), get_parent_contexts($parent));
1604 break;
1605
1606 default:
1607 error ('This is an unknown context!');
1608 return false;
1609 }
1610
1611}
1612
1613
1614/**
1615 * This function gets the capability of a role in a given context.
1616 * It is needed when printing override forms.
1617 * @param int $contextid
bbbf2d40 1618 * @param string $capability
1619 * @param array $capabilities - array loaded using role_context_capabilities
1620 * @return int (allow, prevent, prohibit, inherit)
1621 */
bbbf2d40 1622function get_role_context_capability($contextid, $capability, $capabilities) {
98882637 1623 return $capabilities[$contextid][$capability];
bbbf2d40 1624}
1625
1626
cee0901c 1627/**
1628 * Returns the human-readable, translated version of the capability.
1629 * Basically a big switch statement.
1630 * @param $capabilityname - e.g. mod/choice:readresponses
1631 */
ceb83c70 1632function get_capability_string($capabilityname) {
bbbf2d40 1633
cee0901c 1634 // Typical capabilityname is mod/choice:readresponses
ceb83c70 1635
1636 $names = split('/', $capabilityname);
1637 $stringname = $names[1]; // choice:readresponses
1638 $components = split(':', $stringname);
1639 $componentname = $components[0]; // choice
98882637 1640
1641 switch ($names[0]) {
1642 case 'mod':
ceb83c70 1643 $string = get_string($stringname, $componentname);
98882637 1644 break;
1645
1646 case 'block':
ceb83c70 1647 $string = get_string($stringname, 'block_'.$componentname);
98882637 1648 break;
ceb83c70 1649
98882637 1650 case 'moodle':
ceb83c70 1651 $string = get_string($stringname, 'role');
98882637 1652 break;
1653
1654 case 'enrol':
ceb83c70 1655 $string = get_string($stringname, 'enrol_'.$componentname);
1656 break;
98882637 1657
1658 default:
ceb83c70 1659 $string = get_string($stringname);
98882637 1660 break;
98882637 1661
1662 }
ceb83c70 1663 return $string;
bbbf2d40 1664}
1665
1666
cee0901c 1667/**
1668 * This gets the mod/block/course/core etc strings.
1669 * @param $component
1670 * @param $contextlevel
1671 */
bbbf2d40 1672function get_component_string($component, $contextlevel) {
1673
98882637 1674 switch ($contextlevel) {
bbbf2d40 1675
98882637 1676 case CONTEXT_SYSTEM:
ceb83c70 1677 $string = get_string('coresystem');
bbbf2d40 1678 break;
1679
1680 case CONTEXT_PERSONAL:
98882637 1681 $string = get_string('personal');
bbbf2d40 1682 break;
1683
1684 case CONTEXT_USERID:
98882637 1685 $string = get_string('users');
bbbf2d40 1686 break;
1687
1688 case CONTEXT_COURSECAT:
98882637 1689 $string = get_string('categories');
bbbf2d40 1690 break;
1691
1692 case CONTEXT_COURSE:
98882637 1693 $string = get_string('course');
bbbf2d40 1694 break;
1695
1696 case CONTEXT_GROUP:
98882637 1697 $string = get_string('group');
bbbf2d40 1698 break;
1699
1700 case CONTEXT_MODULE:
98882637 1701 $string = get_string('modulename', basename($component));
bbbf2d40 1702 break;
1703
1704 case CONTEXT_BLOCK:
98882637 1705 $string = get_string('blockname', 'block_'.$component.'.php');
bbbf2d40 1706 break;
1707
1708 default:
1709 error ('This is an unknown context!');
1710 return false;
98882637 1711
1712 }
98882637 1713 return $string;
bbbf2d40 1714}
cee0901c 1715
945f88ca 1716/** gets the list of roles assigned to this context
1717 * @param object $context
1718 * @return array
1719 */
e4dd3222 1720function get_roles_used_in_context($context) {
1721
1722 global $CFG;
1723
1724 return get_records_sql('SELECT distinct r.id, r.name
1725 FROM '.$CFG->prefix.'role_assignments ra,
1726 '.$CFG->prefix.'role r
1727 WHERE r.id = ra.roleid
1728 AND ra.contextid = '.$context->id.'
1729 ORDER BY r.sortorder ASC');
1730}
1731
945f88ca 1732/** this function is used to print roles column in user profile page.
1733 * @param int userid
1734 * @param int contextid
1735 * @return string
1736 */
0a8a95c9 1737function get_user_roles_in_context($userid, $contextid){
1738 global $CFG;
1739
1740 $rolestring = '';
1741 $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';
1742 if ($roles = get_records_sql($SQL)) {
1743 foreach ($roles as $userrole) {
1744 $rolestring .= '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$userrole->contextid.'&amp;roleid='.$userrole->roleid.'">'.$userrole->name.'</a>, ';
1745 }
1746
1747 }
1748 return rtrim($rolestring, ', ');
1749}
68c52526 1750
1751
945f88ca 1752/**
1753 * Checks if a user can override capabilities of a particular role in this context
1754 * @param object $context
1755 * @param int targetroleid - the id of the role you want to override
1756 * @return boolean
1757 */
68c52526 1758function user_can_override($context, $targetroleid) {
1759 // first check if user has override capability
1760 // if not return false;
1761 if (!has_capability('moodle/role:override', $context)) {
1762 return false;
1763 }
1764 // pull out all active roles of this user from this context(or above)
c0614051 1765 if ($userroles = get_user_roles($context)) {
1766 foreach ($userroles as $userrole) {
1767 // if any in the role_allow_override table, then it's ok
1768 if (get_record('role_allow_override', 'roleid', $userrole->roleid, 'allowoverride', $targetroleid)) {
1769 return true;
1770 }
68c52526 1771 }
1772 }
1773
1774 return false;
1775
1776}
1777
945f88ca 1778/**
1779 * Checks if a user can assign users to a particular role in this context
1780 * @param object $context
1781 * @param int targetroleid - the id of the role you want to assign users to
1782 * @return boolean
1783 */
68c52526 1784function user_can_assign($context, $targetroleid) {
1785
1786 // first check if user has override capability
1787 // if not return false;
1788 if (!has_capability('moodle/role:assign', $context)) {
1789 return false;
1790 }
1791 // pull out all active roles of this user from this context(or above)
c0614051 1792 if ($userroles = get_user_roles($context)) {
1793 foreach ($userroles as $userrole) {
1794 // if any in the role_allow_override table, then it's ok
1795 if (get_record('role_allow_assign', 'roleid', $userrole->roleid, 'allowassign', $targetroleid)) {
1796 return true;
1797 }
68c52526 1798 }
1799 }
1800
1801 return false;
1802}
1803
945f88ca 1804/**
1805 * gets all the user roles assigned in this context, or higher contexts
1806 * this is mainly used when checking if a user can assign a role, or overriding a role
1807 * i.e. we need to know what this user holds, in order to verify against allow_assign and
1808 * allow_override tables
1809 * @param object $context
1810 * @param int $userid
1811 * @return array
1812 */
c0614051 1813function get_user_roles($context, $userid=0) {
68c52526 1814
1815 global $USER, $CFG, $db;
c0614051 1816
1817 if (empty($userid)) {
1818 if (empty($USER->id)) {
1819 return array();
1820 }
1821 $userid = $USER->id;
1822 }
1823
1824 if ($parents = get_parent_contexts($context)) {
1825 $contexts = ' AND ra.contextid IN ('.implode(',' , $parents).')';
1826 } else {
1827 $contexts = ' AND ra.contextid = \''.$context->id.'\'';
1828 }
1829
68c52526 1830 return get_records_sql('SELECT *
1831 FROM '.$CFG->prefix.'role_assignments ra
c0614051 1832 WHERE ra.userid = '.$userid.
1833 $contexts);
68c52526 1834}
1835
945f88ca 1836/**
1837 * Creates a record in the allow_override table
1838 * @param int sroleid - source roleid
1839 * @param int troleid - target roleid
1840 * @return int - id or false
1841 */
1842function allow_override($sroleid, $troleid) {
1843 $record->roleid = $sroleid;
1844 $record->allowoverride = $troleid;
1845 return insert_record('role_allow_override', $record);
1846}
1847
1848/**
1849 * Creates a record in the allow_assign table
1850 * @param int sroleid - source roleid
1851 * @param int troleid - target roleid
1852 * @return int - id or false
1853 */
1854function allow_assign($sroleid, $troleid) {
1855 $record->roleid = $sroleid;
1856 $record->allowassign = $troleid;
1857 return insert_record('role_allow_assign', $record);
1858}
1859
1860/**
1861 * gets a list of roles assignalbe in this context for this user
1862 * @param object $context
1863 * @return array
1864 */
1865function get_assignable_roles ($context) {
1866
1867 $role = get_records('role');
1868 $options = array();
1869 foreach ($role as $rolex) {
1870 if (user_can_assign($context, $rolex->id)) {
1871 $options[$rolex->id] = $rolex->name;
1872 }
1873 }
1874 return $options;
1875}
1876
1877/**
1878 * gets a list of roles that can be overriden in this context by this user
1879 * @param object $context
1880 * @return array
1881 */
1882function get_overridable_roles ($context) {
1883
1884 $role = get_records('role');
1885 $options = array();
1886 foreach ($role as $rolex) {
1887 if (user_can_override($context, $rolex->id)) {
1888 $options[$rolex->id] = $rolex->name;
1889 }
1890 }
1891
1892 return $options;
1893
1894}
1648afb2 1895
1896
1897/**
1898 * who has this capability in this context
1899 * does not handling user level resolving!!!
1900 * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly
1901 * @param $context - object
1902 * @param $capability - string capability
1903 * @param $fields - fields to be pulled
1904 * @param $sort - the sort order
1905 */
1906function get_users_by_capability($context, $capability, $fields='distinct u.*', $sort='') {
1907
1908 global $CFG;
1909
1910 // first get all roles with this capability in this context, or above
1911 $possibleroles = get_roles_with_capability($capability, CAP_ALLOW);
1912 $validroleids = array();
1913 foreach ($possibleroles as $prole) {
1914 $caps = role_context_capabilities($prole->id, $context, $capability); // resolved list
1915 if ($caps[$capability] > 0) { // resolved capability > 0
1916 $validroleids[] = $prole->id;
1917 }
1918 }
1919
1920 if ($usercontexts = get_parent_contexts($context)) {
1921 $listofcontexts = '('.implode(',', $usercontexts).')';
1922 } else {
1923 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
1924 $listofcontexts = '('.$sitecontext->id.')'; // must be site
1925 }
1926
1927 $roleids = '('.implode(',', $validroleids).')';
1928
1929 $select = ' SELECT '.$fields;
1930 $from = ' FROM '.$CFG->prefix.'user u LEFT JOIN '.$CFG->prefix.'role_assignments ra ON ra.userid = u.id ';
1931 $where = ' WHERE (ra.contextid = '.$context->id.' OR ra.contextid in '.$listofcontexts.') AND u.deleted = 0 AND ra.roleid in '.$roleids.' ';
1932
1933 return get_records_sql($select.$from.$where);
1934
1935}
1dc1f037 1936?>