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