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