Commit | Line | Data |
---|---|---|
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 | |
e49e61bf | 18 | define('CAP_INHERIT', 0); |
bbbf2d40 | 19 | define('CAP_ALLOW', 1); |
20 | define('CAP_PREVENT', -1); | |
21 | define('CAP_PROHIBIT', -1000); | |
22 | ||
bbbf2d40 | 23 | // context definitions |
24 | define('CONTEXT_SYSTEM', 10); | |
25 | define('CONTEXT_PERSONAL', 20); | |
4b10f08b | 26 | define('CONTEXT_USER', 30); |
bbbf2d40 | 27 | define('CONTEXT_COURSECAT', 40); |
28 | define('CONTEXT_COURSE', 50); | |
29 | define('CONTEXT_GROUP', 60); | |
30 | define('CONTEXT_MODULE', 70); | |
31 | define('CONTEXT_BLOCK', 80); | |
32 | ||
21b6db6e | 33 | // capability risks - see http://docs.moodle.org/en/Hardening_new_Roles_system |
34 | define('RISK_MANAGETRUST', 0x0001); | |
a6b02b65 | 35 | define('RISK_CONFIG', 0x0002); |
21b6db6e | 36 | define('RISK_XSS', 0x0004); |
37 | define('RISK_PERSONAL', 0x0008); | |
38 | define('RISK_SPAM', 0x0010); | |
39 | ||
f3f7610c | 40 | require_once($CFG->dirroot.'/group/lib.php'); |
21b6db6e | 41 | |
340ea4e8 | 42 | $context_cache = array(); // Cache of all used context objects for performance (by level and instance) |
43 | $context_cache_id = array(); // Index to above cache by id | |
bbbf2d40 | 44 | |
7700027f | 45 | |
e7876c1e | 46 | /** |
47 | * Loads the capabilities for the default guest role to the current user in a specific context | |
48 | * @return object | |
49 | */ | |
50 | function load_guest_role($context=NULL) { | |
51 | global $USER; | |
52 | ||
53 | static $guestrole; | |
54 | ||
55 | if (!isloggedin()) { | |
56 | return false; | |
57 | } | |
58 | ||
21c9bace | 59 | if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { |
e7876c1e | 60 | return false; |
61 | } | |
62 | ||
63 | if (empty($context)) { | |
64 | $context = $sitecontext; | |
65 | } | |
66 | ||
67 | if (empty($guestrole)) { | |
8f8ed475 | 68 | if (!$guestrole = get_guest_role()) { |
e7876c1e | 69 | return false; |
70 | } | |
71 | } | |
72 | ||
eef868d1 | 73 | if ($capabilities = get_records_select('role_capabilities', |
e7876c1e | 74 | "roleid = $guestrole->id AND contextid = $sitecontext->id")) { |
75 | foreach ($capabilities as $capability) { | |
eef868d1 | 76 | $USER->capabilities[$context->id][$capability->capability] = $capability->permission; |
e7876c1e | 77 | } |
99ad7633 | 78 | has_capability('clearcache'); |
e7876c1e | 79 | } |
80 | ||
81 | return true; | |
82 | } | |
83 | ||
7700027f | 84 | /** |
85 | * Load default not logged in role capabilities when user is not logged in | |
eef868d1 | 86 | * @return bool |
7700027f | 87 | */ |
20aeb4b8 | 88 | function load_notloggedin_role() { |
89 | global $CFG, $USER; | |
90 | ||
21c9bace | 91 | if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { |
7700027f | 92 | return false; |
35a518c5 | 93 | } |
94 | ||
7700027f | 95 | if (empty($CFG->notloggedinroleid)) { // Let's set the default to the guest role |
8f8ed475 | 96 | if ($role = get_guest_role()) { |
7700027f | 97 | set_config('notloggedinroleid', $role->id); |
98 | } else { | |
99 | return false; | |
100 | } | |
101 | } | |
20aeb4b8 | 102 | |
eef868d1 | 103 | if ($capabilities = get_records_select('role_capabilities', |
7700027f | 104 | "roleid = $CFG->notloggedinroleid AND contextid = $sitecontext->id")) { |
105 | foreach ($capabilities as $capability) { | |
eef868d1 | 106 | $USER->capabilities[$sitecontext->id][$capability->capability] = $capability->permission; |
7700027f | 107 | } |
99ad7633 | 108 | has_capability('clearcache'); |
20aeb4b8 | 109 | } |
110 | ||
111 | return true; | |
112 | } | |
cee0901c | 113 | |
8f8ed475 | 114 | /** |
115 | * Load default not logged in role capabilities when user is not logged in | |
eef868d1 | 116 | * @return bool |
8f8ed475 | 117 | */ |
118 | function load_defaultuser_role() { | |
119 | global $CFG, $USER; | |
120 | ||
21c9bace | 121 | if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { |
8f8ed475 | 122 | return false; |
123 | } | |
124 | ||
125 | if (empty($CFG->defaultuserroleid)) { // Let's set the default to the guest role | |
126 | if ($role = get_guest_role()) { | |
127 | set_config('defaultuserroleid', $role->id); | |
128 | } else { | |
129 | return false; | |
130 | } | |
131 | } | |
132 | ||
eef868d1 | 133 | if ($capabilities = get_records_select('role_capabilities', |
0163b1d0 | 134 | "roleid = $CFG->defaultuserroleid AND contextid = $sitecontext->id AND permission <> 0")) { |
40d86709 | 135 | // Find out if this default role is a guest role, for the hack below |
136 | $defaultisguestrole=false; | |
8f8ed475 | 137 | foreach ($capabilities as $capability) { |
40d86709 | 138 | if($capability->capability=='moodle/legacy:guest') { |
139 | $defaultisguestrole=true; | |
140 | } | |
141 | } | |
142 | foreach ($capabilities as $capability) { | |
143 | // If the default role is a guest role, then don't copy legacy:guest, | |
144 | // otherwise this user could get confused with a REAL guest. Also don't copy | |
145 | // course:view, which is a hack that's necessary because guest roles are | |
146 | // not really handled properly (see MDL-7513) | |
147 | if($defaultisguestrole && $USER->username!='guest' && | |
148 | ($capability->capability=='moodle/legacy:guest' || | |
149 | $capability->capability=='moodle/course:view')) { | |
150 | continue; | |
151 | } | |
152 | ||
39dee8f2 | 153 | // Don't overwrite capabilities from real role... |
40d86709 | 154 | if (!isset($USER->capabilities[$sitecontext->id][$capability->capability])) { |
eef868d1 | 155 | $USER->capabilities[$sitecontext->id][$capability->capability] = $capability->permission; |
ca23ffdb | 156 | } |
8f8ed475 | 157 | } |
99ad7633 | 158 | has_capability('clearcache'); |
8f8ed475 | 159 | } |
160 | ||
161 | return true; | |
162 | } | |
163 | ||
164 | ||
165 | /** | |
166 | * Get the default guest role | |
167 | * @return object role | |
168 | */ | |
169 | function get_guest_role() { | |
170 | if ($roles = get_roles_with_capability('moodle/legacy:guest', CAP_ALLOW)) { | |
171 | return array_shift($roles); // Pick the first one | |
172 | } else { | |
173 | return false; | |
174 | } | |
175 | } | |
176 | ||
177 | ||
bbbf2d40 | 178 | /** |
179 | * This functions get all the course categories in proper order | |
40a2a15f | 180 | * (!)note this only gets course category contexts, and not the site |
181 | * context | |
d963740d | 182 | * @param object $context |
bbbf2d40 | 183 | * @param int $type |
184 | * @return array of contextids | |
185 | */ | |
0468976c | 186 | function get_parent_cats($context, $type) { |
eef868d1 | 187 | |
98882637 | 188 | $parents = array(); |
eef868d1 | 189 | |
c5ddc3fd | 190 | switch ($type) { |
40a2a15f | 191 | // a category can be the parent of another category |
192 | // there is no limit of depth in this case | |
98882637 | 193 | case CONTEXT_COURSECAT: |
c5ddc3fd | 194 | if (!$cat = get_record('course_categories','id',$context->instanceid)) { |
195 | break; | |
196 | } | |
197 | ||
198 | while (!empty($cat->parent)) { | |
199 | if (!$context = get_context_instance(CONTEXT_COURSECAT, $cat->parent)) { | |
200 | break; | |
201 | } | |
98882637 | 202 | $parents[] = $context->id; |
203 | $cat = get_record('course_categories','id',$cat->parent); | |
204 | } | |
98882637 | 205 | break; |
40a2a15f | 206 | |
207 | // a course always fall into a category, unless it's a site course | |
208 | // this happens when SITEID = $course->id | |
209 | // in this case the parent of the course is site context | |
98882637 | 210 | case CONTEXT_COURSE: |
c5ddc3fd | 211 | if (!$course = get_record('course', 'id', $context->instanceid)) { |
212 | break; | |
213 | } | |
214 | if (!$catinstance = get_context_instance(CONTEXT_COURSECAT, $course->category)) { | |
215 | break; | |
216 | } | |
217 | ||
98882637 | 218 | $parents[] = $catinstance->id; |
c5ddc3fd | 219 | |
220 | if (!$cat = get_record('course_categories','id',$course->category)) { | |
221 | break; | |
222 | } | |
40a2a15f | 223 | // Yu: Separating site and site course context |
224 | if ($course->id == SITEID) { | |
225 | break; | |
226 | } | |
c5ddc3fd | 227 | |
228 | while (!empty($cat->parent)) { | |
229 | if (!$context = get_context_instance(CONTEXT_COURSECAT, $cat->parent)) { | |
230 | break; | |
231 | } | |
98882637 | 232 | $parents[] = $context->id; |
233 | $cat = get_record('course_categories','id',$cat->parent); | |
234 | } | |
235 | break; | |
eef868d1 | 236 | |
98882637 | 237 | default: |
238 | break; | |
98882637 | 239 | } |
98882637 | 240 | return array_reverse($parents); |
bbbf2d40 | 241 | } |
242 | ||
243 | ||
cee0901c | 244 | |
0468976c | 245 | /** |
246 | * This function checks for a capability assertion being true. If it isn't | |
247 | * then the page is terminated neatly with a standard error message | |
248 | * @param string $capability - name of the capability | |
249 | * @param object $context - a context object (record from context table) | |
250 | * @param integer $userid - a userid number | |
d74067e8 | 251 | * @param bool $doanything - if false, ignore do anything |
0468976c | 252 | * @param string $errorstring - an errorstring |
d74067e8 | 253 | * @param string $stringfile - which stringfile to get it from |
0468976c | 254 | */ |
eef868d1 | 255 | function require_capability($capability, $context=NULL, $userid=NULL, $doanything=true, |
71483894 | 256 | $errormessage='nopermissions', $stringfile='') { |
a9e1c058 | 257 | |
71483894 | 258 | global $USER, $CFG; |
a9e1c058 | 259 | |
71483894 | 260 | /// If the current user is not logged in, then make sure they are (if needed) |
a9e1c058 | 261 | |
6605128e | 262 | if (empty($userid) and empty($USER->capabilities)) { |
aad2ba95 | 263 | if ($context && ($context->contextlevel == CONTEXT_COURSE)) { |
a9e1c058 | 264 | require_login($context->instanceid); |
11ac79ff | 265 | } else if ($context && ($context->contextlevel == CONTEXT_MODULE)) { |
266 | if ($cm = get_record('course_modules','id',$context->instanceid)) { | |
71483894 | 267 | if (!$course = get_record('course', 'id', $cm->course)) { |
268 | error('Incorrect course.'); | |
269 | } | |
270 | require_course_login($course, true, $cm); | |
271 | ||
11ac79ff | 272 | } else { |
273 | require_login(); | |
274 | } | |
71483894 | 275 | } else if ($context && ($context->contextlevel == CONTEXT_SYSTEM)) { |
276 | if (!empty($CFG->forcelogin)) { | |
277 | require_login(); | |
278 | } | |
279 | ||
a9e1c058 | 280 | } else { |
281 | require_login(); | |
282 | } | |
283 | } | |
eef868d1 | 284 | |
a9e1c058 | 285 | /// OK, if they still don't have the capability then print a nice error message |
286 | ||
d74067e8 | 287 | if (!has_capability($capability, $context, $userid, $doanything)) { |
0468976c | 288 | $capabilityname = get_capability_string($capability); |
289 | print_error($errormessage, $stringfile, '', $capabilityname); | |
290 | } | |
291 | } | |
292 | ||
293 | ||
bbbf2d40 | 294 | /** |
295 | * This function returns whether the current user has the capability of performing a function | |
296 | * For example, we can do has_capability('mod/forum:replypost',$cm) in forum | |
297 | * only one of the 4 (moduleinstance, courseid, site, userid) would be set at 1 time | |
298 | * This is a recursive funciton. | |
bbbf2d40 | 299 | * @uses $USER |
caac8977 | 300 | * @param string $capability - name of the capability (or debugcache or clearcache) |
0468976c | 301 | * @param object $context - a context object (record from context table) |
302 | * @param integer $userid - a userid number | |
20aeb4b8 | 303 | * @param bool $doanything - if false, ignore do anything |
bbbf2d40 | 304 | * @return bool |
305 | */ | |
d74067e8 | 306 | function has_capability($capability, $context=NULL, $userid=NULL, $doanything=true) { |
bbbf2d40 | 307 | |
20aeb4b8 | 308 | global $USER, $CONTEXT, $CFG; |
bbbf2d40 | 309 | |
922633bd | 310 | static $capcache = array(); // Cache of capabilities |
311 | ||
caac8977 | 312 | |
313 | /// Cache management | |
314 | ||
315 | if ($capability == 'clearcache') { | |
316 | $capcache = array(); // Clear ALL the capability cache | |
317 | return false; | |
318 | } | |
319 | ||
922633bd | 320 | /// Some sanity checks |
321 | if (debugging()) { | |
322 | if ($capability == 'debugcache') { | |
323 | print_object($capcache); | |
324 | return true; | |
325 | } | |
326 | if (!record_exists('capabilities', 'name', $capability)) { | |
327 | debugging('Capability "'.$capability.'" was not found! This should be fixed in code.'); | |
328 | } | |
329 | if ($doanything != true and $doanything != false) { | |
330 | debugging('Capability parameter "doanything" is wierd ("'.$doanything.'"). This should be fixed in code.'); | |
331 | } | |
332 | if (!is_object($context) && $context !== NULL) { | |
333 | debugging('Incorrect context parameter "'.$context.'" for has_capability(), object expected! This should be fixed in code.'); | |
334 | } | |
a8a7300a | 335 | } |
336 | ||
922633bd | 337 | /// Make sure we know the current context |
338 | if (empty($context)) { // Use default CONTEXT if none specified | |
339 | if (empty($CONTEXT)) { | |
340 | return false; | |
341 | } else { | |
342 | $context = $CONTEXT; | |
343 | } | |
344 | } else { // A context was given to us | |
345 | if (empty($CONTEXT)) { | |
346 | $CONTEXT = $context; // Store FIRST used context in this global as future default | |
347 | } | |
348 | } | |
349 | ||
350 | /// Check and return cache in case we've processed this one before. | |
351 | ||
352 | $cachekey = $capability.'_'.$context->id.'_'.intval($userid).'_'.intval($doanything); | |
caac8977 | 353 | |
922633bd | 354 | if (isset($capcache[$cachekey])) { |
355 | return $capcache[$cachekey]; | |
356 | } | |
357 | ||
358 | /// Set up user's default roles | |
8f8ed475 | 359 | if (empty($userid) && empty($USER->capabilities)) { // Real user, first time here |
360 | if (isloggedin()) { | |
361 | load_defaultuser_role(); // All users get this by default | |
362 | } else { | |
363 | load_notloggedin_role(); // others get this by default | |
364 | } | |
20aeb4b8 | 365 | } |
366 | ||
922633bd | 367 | /// Load up the capabilities list or item as necessary |
013f2e7c | 368 | if ($userid && (($USER === NULL) or ($userid != $USER->id))) { |
9425b25f | 369 | if (empty($USER->id) or ($userid != $USER->id)) { |
922633bd | 370 | $capabilities = load_user_capability($capability, $context, $userid); // expensive |
9425b25f | 371 | } else { //$USER->id == $userid |
372 | $capabilities = empty($USER->capabilities) ? NULL : $USER->capabilities; | |
373 | } | |
374 | } else { // no userid | |
375 | $capabilities = empty($USER->capabilities) ? NULL : $USER->capabilities; | |
922633bd | 376 | $userid = $USER->id; |
98882637 | 377 | } |
9425b25f | 378 | |
caac8977 | 379 | /// We act a little differently when switchroles is active |
380 | ||
381 | $switchroleactive = false; // Assume it isn't active in this context | |
382 | ||
bbbf2d40 | 383 | |
922633bd | 384 | /// First deal with the "doanything" capability |
5fe9a11d | 385 | |
20aeb4b8 | 386 | if ($doanything) { |
2d07587b | 387 | |
f4e2d38a | 388 | /// First make sure that we aren't in a "switched role" |
389 | ||
f4e2d38a | 390 | if (!empty($USER->switchrole)) { // Switchrole is active somewhere! |
391 | if (!empty($USER->switchrole[$context->id])) { // Because of current context | |
392 | $switchroleactive = true; | |
393 | } else { // Check parent contexts | |
394 | if ($parentcontextids = get_parent_contexts($context)) { | |
395 | foreach ($parentcontextids as $parentcontextid) { | |
396 | if (!empty($USER->switchrole[$parentcontextid])) { // Yep, switchroles active here | |
397 | $switchroleactive = true; | |
398 | break; | |
399 | } | |
400 | } | |
401 | } | |
402 | } | |
403 | } | |
404 | ||
405 | /// Check the site context for doanything (most common) first | |
406 | ||
407 | if (empty($switchroleactive)) { // Ignore site setting if switchrole is active | |
21c9bace | 408 | $sitecontext = get_context_instance(CONTEXT_SYSTEM); |
2d07587b | 409 | if (isset($capabilities[$sitecontext->id]['moodle/site:doanything'])) { |
922633bd | 410 | $result = (0 < $capabilities[$sitecontext->id]['moodle/site:doanything']); |
411 | $capcache[$cachekey] = $result; | |
412 | return $result; | |
2d07587b | 413 | } |
20aeb4b8 | 414 | } |
40a2a15f | 415 | /// If it's not set at site level, it is possible to be set on other levels |
416 | /// Though this usage is not common and can cause risks | |
aad2ba95 | 417 | switch ($context->contextlevel) { |
eef868d1 | 418 | |
20aeb4b8 | 419 | case CONTEXT_COURSECAT: |
420 | // Check parent cats. | |
421 | $parentcats = get_parent_cats($context, CONTEXT_COURSECAT); | |
422 | foreach ($parentcats as $parentcat) { | |
423 | if (isset($capabilities[$parentcat]['moodle/site:doanything'])) { | |
922633bd | 424 | $result = (0 < $capabilities[$parentcat]['moodle/site:doanything']); |
425 | $capcache[$cachekey] = $result; | |
426 | return $result; | |
20aeb4b8 | 427 | } |
cee0901c | 428 | } |
20aeb4b8 | 429 | break; |
bbbf2d40 | 430 | |
20aeb4b8 | 431 | case CONTEXT_COURSE: |
432 | // Check parent cat. | |
433 | $parentcats = get_parent_cats($context, CONTEXT_COURSE); | |
98882637 | 434 | |
20aeb4b8 | 435 | foreach ($parentcats as $parentcat) { |
436 | if (isset($capabilities[$parentcat]['do_anything'])) { | |
922633bd | 437 | $result = (0 < $capabilities[$parentcat]['do_anything']); |
438 | $capcache[$cachekey] = $result; | |
439 | return $result; | |
20aeb4b8 | 440 | } |
9425b25f | 441 | } |
20aeb4b8 | 442 | break; |
bbbf2d40 | 443 | |
20aeb4b8 | 444 | case CONTEXT_GROUP: |
445 | // Find course. | |
f3f7610c ML |
446 | $courseid = groups_get_course($context->instanceid); |
447 | $courseinstance = get_context_instance(CONTEXT_COURSE, $courseid); | |
9425b25f | 448 | |
20aeb4b8 | 449 | $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE); |
450 | foreach ($parentcats as $parentcat) { | |
b4a1805a | 451 | if (isset($capabilities[$parentcat]['do_anything'])) { |
452 | $result = (0 < $capabilities[$parentcat]['do_anything']); | |
922633bd | 453 | $capcache[$cachekey] = $result; |
454 | return $result; | |
20aeb4b8 | 455 | } |
9425b25f | 456 | } |
9425b25f | 457 | |
20aeb4b8 | 458 | $coursecontext = ''; |
459 | if (isset($capabilities[$courseinstance->id]['do_anything'])) { | |
922633bd | 460 | $result = (0 < $capabilities[$courseinstance->id]['do_anything']); |
461 | $capcache[$cachekey] = $result; | |
462 | return $result; | |
20aeb4b8 | 463 | } |
9425b25f | 464 | |
20aeb4b8 | 465 | break; |
bbbf2d40 | 466 | |
20aeb4b8 | 467 | case CONTEXT_MODULE: |
468 | // Find course. | |
469 | $cm = get_record('course_modules', 'id', $context->instanceid); | |
470 | $courseinstance = get_context_instance(CONTEXT_COURSE, $cm->course); | |
9425b25f | 471 | |
20aeb4b8 | 472 | if ($parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE)) { |
473 | foreach ($parentcats as $parentcat) { | |
474 | if (isset($capabilities[$parentcat]['do_anything'])) { | |
922633bd | 475 | $result = (0 < $capabilities[$parentcat]['do_anything']); |
476 | $capcache[$cachekey] = $result; | |
477 | return $result; | |
20aeb4b8 | 478 | } |
cee0901c | 479 | } |
9425b25f | 480 | } |
98882637 | 481 | |
20aeb4b8 | 482 | if (isset($capabilities[$courseinstance->id]['do_anything'])) { |
922633bd | 483 | $result = (0 < $capabilities[$courseinstance->id]['do_anything']); |
484 | $capcache[$cachekey] = $result; | |
485 | return $result; | |
20aeb4b8 | 486 | } |
bbbf2d40 | 487 | |
20aeb4b8 | 488 | break; |
bbbf2d40 | 489 | |
20aeb4b8 | 490 | case CONTEXT_BLOCK: |
2d95f702 | 491 | // not necessarily 1 to 1 to course. |
20aeb4b8 | 492 | $block = get_record('block_instance','id',$context->instanceid); |
2d95f702 | 493 | if ($block->pagetype == 'course-view') { |
494 | $courseinstance = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check | |
495 | $parentcats = get_parent_cats($courseinstance, CONTEXT_COURSE); | |
496 | ||
497 | foreach ($parentcats as $parentcat) { | |
498 | if (isset($capabilities[$parentcat]['do_anything'])) { | |
499 | $result = (0 < $capabilities[$parentcat]['do_anything']); | |
500 | $capcache[$cachekey] = $result; | |
501 | return $result; | |
502 | } | |
503 | } | |
504 | ||
505 | if (isset($capabilities[$courseinstance->id]['do_anything'])) { | |
506 | $result = (0 < $capabilities[$courseinstance->id]['do_anything']); | |
507 | $capcache[$cachekey] = $result; | |
508 | return $result; | |
509 | } | |
510 | } else { // if not course-view type of blocks, check site | |
511 | if (isset($capabilities[$sitecontext->id]['do_anything'])) { | |
512 | $result = (0 < $capabilities[$sitecontext->id]['do_anything']); | |
922633bd | 513 | $capcache[$cachekey] = $result; |
514 | return $result; | |
20aeb4b8 | 515 | } |
20aeb4b8 | 516 | } |
517 | break; | |
bbbf2d40 | 518 | |
20aeb4b8 | 519 | default: |
4b10f08b | 520 | // CONTEXT_SYSTEM: CONTEXT_PERSONAL: CONTEXT_USER: |
40a2a15f | 521 | // Do nothing, because the parents are site context |
522 | // which has been checked already | |
20aeb4b8 | 523 | break; |
524 | } | |
bbbf2d40 | 525 | |
20aeb4b8 | 526 | // Last: check self. |
527 | if (isset($capabilities[$context->id]['do_anything'])) { | |
922633bd | 528 | $result = (0 < $capabilities[$context->id]['do_anything']); |
529 | $capcache[$cachekey] = $result; | |
530 | return $result; | |
20aeb4b8 | 531 | } |
98882637 | 532 | } |
caac8977 | 533 | // do_anything has not been set, we now look for it the normal way. |
534 | $result = (0 < capability_search($capability, $context, $capabilities, $switchroleactive)); | |
922633bd | 535 | $capcache[$cachekey] = $result; |
536 | return $result; | |
bbbf2d40 | 537 | |
9425b25f | 538 | } |
bbbf2d40 | 539 | |
540 | ||
541 | /** | |
542 | * In a separate function so that we won't have to deal with do_anything. | |
40a2a15f | 543 | * again. Used by function has_capability(). |
bbbf2d40 | 544 | * @param $capability - capability string |
0468976c | 545 | * @param $context - the context object |
40a2a15f | 546 | * @param $capabilities - either $USER->capability or loaded array (for other users) |
bbbf2d40 | 547 | * @return permission (int) |
548 | */ | |
caac8977 | 549 | function capability_search($capability, $context, $capabilities, $switchroleactive=false) { |
759ac72d | 550 | |
bbbf2d40 | 551 | global $USER, $CFG; |
0468976c | 552 | |
11ac79ff | 553 | if (!isset($context->id)) { |
554 | return 0; | |
555 | } | |
40a2a15f | 556 | // if already set in the array explicitly, no need to look for it in parent |
557 | // context any longer | |
0468976c | 558 | if (isset($capabilities[$context->id][$capability])) { |
559 | return ($capabilities[$context->id][$capability]); | |
bbbf2d40 | 560 | } |
9425b25f | 561 | |
bbbf2d40 | 562 | /* Then, we check the cache recursively */ |
9425b25f | 563 | $permission = 0; |
564 | ||
aad2ba95 | 565 | switch ($context->contextlevel) { |
bbbf2d40 | 566 | |
567 | case CONTEXT_SYSTEM: // by now it's a definite an inherit | |
568 | $permission = 0; | |
569 | break; | |
570 | ||
571 | case CONTEXT_PERSONAL: | |
21c9bace | 572 | $parentcontext = get_context_instance(CONTEXT_SYSTEM); |
0468976c | 573 | $permission = capability_search($capability, $parentcontext, $capabilities); |
bbbf2d40 | 574 | break; |
9425b25f | 575 | |
4b10f08b | 576 | case CONTEXT_USER: |
21c9bace | 577 | $parentcontext = get_context_instance(CONTEXT_SYSTEM); |
0468976c | 578 | $permission = capability_search($capability, $parentcontext, $capabilities); |
bbbf2d40 | 579 | break; |
9425b25f | 580 | |
bbbf2d40 | 581 | case CONTEXT_COURSECAT: // Coursecat -> coursecat or site |
582 | $coursecat = get_record('course_categories','id',$context->instanceid); | |
0468976c | 583 | if (!empty($coursecat->parent)) { // return parent value if it exists |
584 | $parentcontext = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent); | |
bbbf2d40 | 585 | } else { // else return site value |
21c9bace | 586 | $parentcontext = get_context_instance(CONTEXT_SYSTEM); |
bbbf2d40 | 587 | } |
0468976c | 588 | $permission = capability_search($capability, $parentcontext, $capabilities); |
bbbf2d40 | 589 | break; |
590 | ||
591 | case CONTEXT_COURSE: // 1 to 1 to course cat | |
caac8977 | 592 | if (empty($switchroleactive)) { |
593 | // find the course cat, and return its value | |
594 | $course = get_record('course','id',$context->instanceid); | |
595 | if ($course->id == SITEID) { // In 1.8 we've separated site course and system | |
596 | $parentcontext = get_context_instance(CONTEXT_SYSTEM); | |
597 | } else { | |
598 | $parentcontext = get_context_instance(CONTEXT_COURSECAT, $course->category); | |
599 | } | |
600 | $permission = capability_search($capability, $parentcontext, $capabilities); | |
40a2a15f | 601 | } |
bbbf2d40 | 602 | break; |
603 | ||
604 | case CONTEXT_GROUP: // 1 to 1 to course | |
f3f7610c ML |
605 | $courseid = groups_get_course($context->instanceid); |
606 | $parentcontext = get_context_instance(CONTEXT_COURSE, $courseid); | |
0468976c | 607 | $permission = capability_search($capability, $parentcontext, $capabilities); |
bbbf2d40 | 608 | break; |
609 | ||
610 | case CONTEXT_MODULE: // 1 to 1 to course | |
611 | $cm = get_record('course_modules','id',$context->instanceid); | |
0468976c | 612 | $parentcontext = get_context_instance(CONTEXT_COURSE, $cm->course); |
613 | $permission = capability_search($capability, $parentcontext, $capabilities); | |
bbbf2d40 | 614 | break; |
615 | ||
2d95f702 | 616 | case CONTEXT_BLOCK: // not necessarily 1 to 1 to course |
bbbf2d40 | 617 | $block = get_record('block_instance','id',$context->instanceid); |
2d95f702 | 618 | if ($block->pagetype == 'course-view') { |
619 | $parentcontext = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check | |
620 | } else { | |
621 | $parentcontext = get_context_instance(CONTEXT_SYSTEM); | |
622 | } | |
0468976c | 623 | $permission = capability_search($capability, $parentcontext, $capabilities); |
bbbf2d40 | 624 | break; |
625 | ||
626 | default: | |
627 | error ('This is an unknown context!'); | |
628 | return false; | |
629 | } | |
9425b25f | 630 | |
98882637 | 631 | return $permission; |
bbbf2d40 | 632 | } |
633 | ||
40a2a15f | 634 | /** |
635 | * auxillary function for load_user_capabilities() | |
636 | * checks if context c1 is a parent (or itself) of context c2 | |
637 | * @param int $c1 - context id of context 1 | |
638 | * @param int $c2 - context id of context 2 | |
639 | * @return bool | |
640 | */ | |
9fccc080 | 641 | function is_parent_context($c1, $c2) { |
642 | static $parentsarray; | |
643 | ||
644 | // context can be itself and this is ok | |
645 | if ($c1 == $c2) { | |
646 | return true; | |
647 | } | |
648 | // hit in cache? | |
649 | if (isset($parentsarray[$c1][$c2])) { | |
650 | return $parentsarray[$c1][$c2]; | |
651 | } | |
652 | ||
653 | if (!$co2 = get_record('context', 'id', $c2)) { | |
654 | return false; | |
655 | } | |
656 | ||
657 | if (!$parents = get_parent_contexts($co2)) { | |
658 | return false; | |
659 | } | |
660 | ||
661 | foreach ($parents as $parent) { | |
662 | $parentsarray[$parent][$c2] = true; | |
663 | } | |
664 | ||
665 | if (in_array($c1, $parents)) { | |
666 | return true; | |
667 | } else { // else not a parent, set the cache anyway | |
668 | $parentsarray[$c1][$c2] = false; | |
669 | return false; | |
670 | } | |
671 | } | |
672 | ||
673 | ||
674 | /* | |
675 | * auxillary function for load_user_capabilities() | |
676 | * handler in usort() to sort contexts according to level | |
40a2a15f | 677 | * @param object contexta |
678 | * @param object contextb | |
679 | * @return int | |
9fccc080 | 680 | */ |
681 | function roles_context_cmp($contexta, $contextb) { | |
682 | if ($contexta->contextlevel == $contextb->contextlevel) { | |
683 | return 0; | |
684 | } | |
685 | return ($contexta->contextlevel < $contextb->contextlevel) ? -1 : 1; | |
686 | } | |
687 | ||
bbbf2d40 | 688 | /** |
689 | * This function should be called immediately after a login, when $USER is set. | |
690 | * It will build an array of all the capabilities at each level | |
691 | * i.e. site/metacourse/course_category/course/moduleinstance | |
692 | * Note we should only load capabilities if they are explicitly assigned already, | |
693 | * we should not load all module's capability! | |
21c9bace | 694 | * |
bbbf2d40 | 695 | * [Capabilities] => [26][forum_post] = 1 |
696 | * [26][forum_start] = -8990 | |
697 | * [26][forum_edit] = -1 | |
698 | * [273][blah blah] = 1 | |
699 | * [273][blah blah blah] = 2 | |
21c9bace | 700 | * |
701 | * @param $capability string - Only get a specific capability (string) | |
702 | * @param $context object - Only get capabilities for a specific context object | |
703 | * @param $userid integer - the id of the user whose capabilities we want to load | |
704 | * @return array of permissions (or nothing if they get assigned to $USER) | |
bbbf2d40 | 705 | */ |
21c9bace | 706 | function load_user_capability($capability='', $context = NULL, $userid='') { |
d140ad3f | 707 | |
98882637 | 708 | global $USER, $CFG; |
40a2a15f | 709 | // this flag has not been set! |
710 | // (not clean install, or upgraded successfully to 1.7 and up) | |
2f1a4248 | 711 | if (empty($CFG->rolesactive)) { |
712 | return false; | |
713 | } | |
714 | ||
bbbf2d40 | 715 | if (empty($userid)) { |
dc411d1b | 716 | if (empty($USER->id)) { // We have no user to get capabilities for |
64026e8c | 717 | debugging('User not logged in for load_user_capability!'); |
dc411d1b | 718 | return false; |
719 | } | |
64026e8c | 720 | unset($USER->capabilities); // We don't want possible older capabilites hanging around |
721 | ||
722 | check_enrolment_plugins($USER); // Call "enrol" system to ensure that we have the correct picture | |
8f8ed475 | 723 | |
bbbf2d40 | 724 | $userid = $USER->id; |
dc411d1b | 725 | $otheruserid = false; |
bbbf2d40 | 726 | } else { |
64026e8c | 727 | if (!$user = get_record('user', 'id', $userid)) { |
728 | debugging('Non-existent userid in load_user_capability!'); | |
729 | return false; | |
730 | } | |
731 | ||
732 | check_enrolment_plugins($user); // Ensure that we have the correct picture | |
733 | ||
9425b25f | 734 | $otheruserid = $userid; |
bbbf2d40 | 735 | } |
9425b25f | 736 | |
5f70bcc3 | 737 | |
738 | /// First we generate a list of all relevant contexts of the user | |
739 | ||
740 | $usercontexts = array(); | |
bbbf2d40 | 741 | |
0468976c | 742 | if ($context) { // if context is specified |
eef868d1 | 743 | $usercontexts = get_parent_contexts($context); |
9343a733 | 744 | $usercontexts[] = $context->id; // Add the current context as well |
98882637 | 745 | } else { // else, we load everything |
5f70bcc3 | 746 | if ($userroles = get_records('role_assignments','userid',$userid)) { |
747 | foreach ($userroles as $userrole) { | |
748 | $usercontexts[] = $userrole->contextid; | |
749 | } | |
98882637 | 750 | } |
5f70bcc3 | 751 | } |
752 | ||
753 | /// Set up SQL fragments for searching contexts | |
754 | ||
755 | if ($usercontexts) { | |
0468976c | 756 | $listofcontexts = '('.implode(',', $usercontexts).')'; |
5f70bcc3 | 757 | $searchcontexts1 = "c1.id IN $listofcontexts AND"; |
5f70bcc3 | 758 | } else { |
c76e095f | 759 | $searchcontexts1 = ''; |
bbbf2d40 | 760 | } |
3ca2dea5 | 761 | |
64026e8c | 762 | if ($capability) { |
763 | $capsearch = " AND rc.capability = '$capability' "; | |
764 | } else { | |
eef868d1 | 765 | $capsearch =""; |
64026e8c | 766 | } |
767 | ||
e38f38c3 | 768 | /// Set up SQL fragments for timestart, timeend etc |
769 | $now = time(); | |
85f101fa | 770 | $timesql = "AND ((ra.timestart = 0 OR ra.timestart < $now) AND (ra.timeend = 0 OR ra.timeend > $now))"; |
e38f38c3 | 771 | |
5f70bcc3 | 772 | /// Then we use 1 giant SQL to bring out all relevant capabilities. |
773 | /// The first part gets the capabilities of orginal role. | |
774 | /// The second part gets the capabilities of overriden roles. | |
bbbf2d40 | 775 | |
21c9bace | 776 | $siteinstance = get_context_instance(CONTEXT_SYSTEM); |
9fccc080 | 777 | $capabilities = array(); // Reinitialize. |
778 | ||
779 | // SQL for normal capabilities | |
780 | $SQL1 = "SELECT rc.capability, c1.id as id1, c1.id as id2, (c1.contextlevel * 100) AS aggrlevel, | |
bbbf2d40 | 781 | SUM(rc.permission) AS sum |
782 | FROM | |
eef868d1 | 783 | {$CFG->prefix}role_assignments ra, |
42ac3ecf | 784 | {$CFG->prefix}role_capabilities rc, |
785 | {$CFG->prefix}context c1 | |
bbbf2d40 | 786 | WHERE |
d4649c76 | 787 | ra.contextid=c1.id AND |
788 | ra.roleid=rc.roleid AND | |
bbbf2d40 | 789 | ra.userid=$userid AND |
5f70bcc3 | 790 | $searchcontexts1 |
eef868d1 | 791 | rc.contextid=$siteinstance->id |
98882637 | 792 | $capsearch |
e38f38c3 | 793 | $timesql |
bbbf2d40 | 794 | GROUP BY |
aad2ba95 | 795 | rc.capability, (c1.contextlevel * 100), c1.id |
bbbf2d40 | 796 | HAVING |
9fccc080 | 797 | SUM(rc.permission) != 0 |
798 | ORDER BY | |
799 | aggrlevel ASC"; | |
800 | ||
801 | if (!$rs = get_recordset_sql($SQL1)) { | |
802 | error("Query failed in load_user_capability."); | |
803 | } | |
bbbf2d40 | 804 | |
9fccc080 | 805 | if ($rs && $rs->RecordCount() > 0) { |
806 | while (!$rs->EOF) { | |
807 | $array = $rs->fields; | |
808 | $temprecord = new object; | |
809 | ||
810 | foreach ($array as $key=>$val) { | |
811 | if ($key == 'aggrlevel') { | |
812 | $temprecord->contextlevel = $val; | |
813 | } else { | |
814 | $temprecord->{$key} = $val; | |
815 | } | |
816 | } | |
817 | ||
818 | $capabilities[] = $temprecord; | |
819 | $rs->MoveNext(); | |
820 | } | |
b7e40271 | 821 | } |
4cd12678 | 822 | |
823 | ||
9fccc080 | 824 | // SQL for overrides |
825 | // this is take out because we have no way of making sure c1 is indeed related to c2 (parent) | |
826 | // if we do not group by sum, it is possible to have multiple records of rc.capability, c1.id, c2.id, tuple having | |
827 | // different values, we can maually sum it when we go through the list | |
828 | $SQL2 = "SELECT rc.capability, c1.id as id1, c2.id as id2, (c1.contextlevel * 100 + c2.contextlevel) AS aggrlevel, | |
829 | rc.permission AS sum | |
bbbf2d40 | 830 | FROM |
42ac3ecf | 831 | {$CFG->prefix}role_assignments ra, |
832 | {$CFG->prefix}role_capabilities rc, | |
833 | {$CFG->prefix}context c1, | |
834 | {$CFG->prefix}context c2 | |
bbbf2d40 | 835 | WHERE |
d4649c76 | 836 | ra.contextid=c1.id AND |
eef868d1 | 837 | ra.roleid=rc.roleid AND |
838 | ra.userid=$userid AND | |
839 | rc.contextid=c2.id AND | |
5f70bcc3 | 840 | $searchcontexts1 |
5f70bcc3 | 841 | rc.contextid != $siteinstance->id |
bbbf2d40 | 842 | $capsearch |
e38f38c3 | 843 | $timesql |
eef868d1 | 844 | |
bbbf2d40 | 845 | GROUP BY |
21c9bace | 846 | rc.capability, (c1.contextlevel * 100 + c2.contextlevel), c1.id, c2.id, rc.permission |
bbbf2d40 | 847 | ORDER BY |
75e84883 | 848 | aggrlevel ASC |
bbbf2d40 | 849 | "; |
850 | ||
9fccc080 | 851 | |
852 | if (!$rs = get_recordset_sql($SQL2)) { | |
75e84883 | 853 | error("Query failed in load_user_capability."); |
854 | } | |
5cf38a57 | 855 | |
bbbf2d40 | 856 | if ($rs && $rs->RecordCount() > 0) { |
857 | while (!$rs->EOF) { | |
75e84883 | 858 | $array = $rs->fields; |
859 | $temprecord = new object; | |
eef868d1 | 860 | |
98882637 | 861 | foreach ($array as $key=>$val) { |
75e84883 | 862 | if ($key == 'aggrlevel') { |
aad2ba95 | 863 | $temprecord->contextlevel = $val; |
75e84883 | 864 | } else { |
865 | $temprecord->{$key} = $val; | |
866 | } | |
98882637 | 867 | } |
9fccc080 | 868 | // for overrides, we have to make sure that context2 is a child of context1 |
869 | // otherwise the combination makes no sense | |
870 | if (is_parent_context($temprecord->id1, $temprecord->id2)) { | |
871 | $capabilities[] = $temprecord; | |
872 | } // only write if relevant | |
bbbf2d40 | 873 | $rs->MoveNext(); |
874 | } | |
875 | } | |
4cd12678 | 876 | |
9fccc080 | 877 | // this step sorts capabilities according to the contextlevel |
878 | // it is very important because the order matters when we | |
879 | // go through each capabilities later. (i.e. higher level contextlevel | |
880 | // will override lower contextlevel settings | |
881 | usort($capabilities, 'roles_context_cmp'); | |
eef868d1 | 882 | |
bbbf2d40 | 883 | /* so up to this point we should have somethign like this |
aad2ba95 | 884 | * $capabilities[1] ->contextlevel = 1000 |
bbbf2d40 | 885 | ->module = SITEID |
886 | ->capability = do_anything | |
887 | ->id = 1 (id is the context id) | |
888 | ->sum = 0 | |
eef868d1 | 889 | |
aad2ba95 | 890 | * $capabilities[2] ->contextlevel = 1000 |
bbbf2d40 | 891 | ->module = SITEID |
892 | ->capability = post_messages | |
893 | ->id = 1 | |
894 | ->sum = -9000 | |
895 | ||
aad2ba95 | 896 | * $capabilittes[3] ->contextlevel = 3000 |
bbbf2d40 | 897 | ->module = course |
898 | ->capability = view_course_activities | |
899 | ->id = 25 | |
900 | ->sum = 1 | |
901 | ||
aad2ba95 | 902 | * $capabilittes[4] ->contextlevel = 3000 |
bbbf2d40 | 903 | ->module = course |
904 | ->capability = view_course_activities | |
905 | ->id = 26 | |
906 | ->sum = 0 (this is another course) | |
eef868d1 | 907 | |
aad2ba95 | 908 | * $capabilities[5] ->contextlevel = 3050 |
bbbf2d40 | 909 | ->module = course |
910 | ->capability = view_course_activities | |
911 | ->id = 25 (override in course 25) | |
912 | ->sum = -1 | |
913 | * .... | |
914 | * now we proceed to write the session array, going from top to bottom | |
915 | * at anypoint, we need to go up and check parent to look for prohibit | |
916 | */ | |
917 | // print_object($capabilities); | |
918 | ||
919 | /* This is where we write to the actualy capabilities array | |
920 | * what we need to do from here on is | |
921 | * going down the array from lowest level to highest level | |
922 | * 1) recursively check for prohibit, | |
923 | * if any, we write prohibit | |
924 | * else, we write the value | |
925 | * 2) at an override level, we overwrite current level | |
926 | * if it's not set to prohibit already, and if different | |
927 | * ........ that should be it ........ | |
928 | */ | |
efb58884 | 929 | |
930 | // This is the flag used for detecting the current context level. Since we are going through | |
931 | // the array in ascending order of context level. For normal capabilities, there should only | |
932 | // be 1 value per (capability, contextlevel, context), because they are already summed. But, | |
933 | // for overrides, since we are processing them separate, we need to sum the relevcant entries. | |
934 | // We set this flag when we hit a new level. | |
935 | // If the flag is already set, we keep adding (summing), otherwise, we just override previous | |
936 | // settings (from lower level contexts) | |
937 | $capflags = array(); // (contextid, contextlevel, capability) | |
98882637 | 938 | $usercap = array(); // for other user's capabilities |
bbbf2d40 | 939 | foreach ($capabilities as $capability) { |
940 | ||
9fccc080 | 941 | if (!$context = get_context_instance_by_id($capability->id2)) { |
7bfa3101 | 942 | continue; // incorrect stale context |
943 | } | |
0468976c | 944 | |
41811960 | 945 | if (!empty($otheruserid)) { // we are pulling out other user's capabilities, do not write to session |
eef868d1 | 946 | |
0468976c | 947 | if (capability_prohibits($capability->capability, $context, $capability->sum, $usercap)) { |
9fccc080 | 948 | $usercap[$capability->id2][$capability->capability] = CAP_PROHIBIT; |
98882637 | 949 | continue; |
950 | } | |
efb58884 | 951 | if (isset($usercap[$capability->id2][$capability->capability])) { // use isset because it can be sum 0 |
952 | if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) { | |
953 | $usercap[$capability->id2][$capability->capability] += $capability->sum; | |
954 | } else { // else we override, and update flag | |
955 | $usercap[$capability->id2][$capability->capability] = $capability->sum; | |
956 | $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true; | |
957 | } | |
9fccc080 | 958 | } else { |
959 | $usercap[$capability->id2][$capability->capability] = $capability->sum; | |
efb58884 | 960 | $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true; |
9fccc080 | 961 | } |
eef868d1 | 962 | |
98882637 | 963 | } else { |
964 | ||
0468976c | 965 | if (capability_prohibits($capability->capability, $context, $capability->sum)) { // if any parent or parent's parent is set to prohibit |
9fccc080 | 966 | $USER->capabilities[$capability->id2][$capability->capability] = CAP_PROHIBIT; |
98882637 | 967 | continue; |
968 | } | |
eef868d1 | 969 | |
98882637 | 970 | // if no parental prohibit set |
971 | // just write to session, i am not sure this is correct yet | |
972 | // since 3050 shows up after 3000, and 3070 shows up after 3050, | |
973 | // it should be ok just to overwrite like this, provided that there's no | |
974 | // parental prohibits | |
98882637 | 975 | // we need to write even if it's 0, because it could be an inherit override |
efb58884 | 976 | if (isset($USER->capabilities[$capability->id2][$capability->capability])) { |
977 | if (!empty($capflags[$capability->id2][$capability->contextlevel][$capability->capability])) { | |
978 | $USER->capabilities[$capability->id2][$capability->capability] += $capability->sum; | |
979 | } else { // else we override, and update flag | |
980 | $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum; | |
981 | $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true; | |
982 | } | |
9fccc080 | 983 | } else { |
984 | $USER->capabilities[$capability->id2][$capability->capability] = $capability->sum; | |
efb58884 | 985 | $capflags[$capability->id2][$capability->contextlevel][$capability->capability] = true; |
9fccc080 | 986 | } |
98882637 | 987 | } |
bbbf2d40 | 988 | } |
eef868d1 | 989 | |
bbbf2d40 | 990 | // now we don't care about the huge array anymore, we can dispose it. |
991 | unset($capabilities); | |
efb58884 | 992 | unset($capflags); |
eef868d1 | 993 | |
dbe7e582 | 994 | if (!empty($otheruserid)) { |
eef868d1 | 995 | return $usercap; // return the array |
bbbf2d40 | 996 | } |
2f1a4248 | 997 | } |
998 | ||
999 | ||
1000 | /* | |
1001 | * A convenience function to completely load all the capabilities | |
1002 | * for the current user. This is what gets called from login, for example. | |
1003 | */ | |
1004 | function load_all_capabilities() { | |
1005 | global $USER; | |
1006 | ||
1007 | if (empty($USER->username)) { | |
1008 | return; | |
1009 | } | |
bbbf2d40 | 1010 | |
2f1a4248 | 1011 | load_user_capability(); // Load basic capabilities assigned to this user |
1012 | ||
1013 | if ($USER->username == 'guest') { | |
1014 | load_guest_role(); // All non-guest users get this by default | |
1015 | } else { | |
1016 | load_defaultuser_role(); // All non-guest users get this by default | |
1017 | } | |
bbbf2d40 | 1018 | } |
1019 | ||
2f1a4248 | 1020 | |
64026e8c | 1021 | /* |
1022 | * Check all the login enrolment information for the given user object | |
eef868d1 | 1023 | * by querying the enrolment plugins |
64026e8c | 1024 | */ |
1025 | function check_enrolment_plugins(&$user) { | |
1026 | global $CFG; | |
1027 | ||
e4ec4e41 | 1028 | static $inprogress; // To prevent this function being called more than once in an invocation |
1029 | ||
218eb651 | 1030 | if (!empty($inprogress[$user->id])) { |
e4ec4e41 | 1031 | return; |
1032 | } | |
1033 | ||
218eb651 | 1034 | $inprogress[$user->id] = true; // Set the flag |
e4ec4e41 | 1035 | |
64026e8c | 1036 | require_once($CFG->dirroot .'/enrol/enrol.class.php'); |
eef868d1 | 1037 | |
64026e8c | 1038 | if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) { |
1039 | $plugins = array($CFG->enrol); | |
1040 | } | |
1041 | ||
1042 | foreach ($plugins as $plugin) { | |
1043 | $enrol = enrolment_factory::factory($plugin); | |
1044 | if (method_exists($enrol, 'setup_enrolments')) { /// Plugin supports Roles (Moodle 1.7 and later) | |
1045 | $enrol->setup_enrolments($user); | |
1046 | } else { /// Run legacy enrolment methods | |
1047 | if (method_exists($enrol, 'get_student_courses')) { | |
1048 | $enrol->get_student_courses($user); | |
1049 | } | |
1050 | if (method_exists($enrol, 'get_teacher_courses')) { | |
1051 | $enrol->get_teacher_courses($user); | |
1052 | } | |
1053 | ||
1054 | /// deal with $user->students and $user->teachers stuff | |
1055 | unset($user->student); | |
1056 | unset($user->teacher); | |
1057 | } | |
1058 | unset($enrol); | |
1059 | } | |
e4ec4e41 | 1060 | |
218eb651 | 1061 | unset($inprogress[$user->id]); // Unset the flag |
64026e8c | 1062 | } |
1063 | ||
bbbf2d40 | 1064 | |
1065 | /** | |
1066 | * This is a recursive function that checks whether the capability in this | |
1067 | * context, or the parent capabilities are set to prohibit. | |
1068 | * | |
1069 | * At this point, we can probably just use the values already set in the | |
1070 | * session variable, since we are going down the level. Any prohit set in | |
1071 | * parents would already reflect in the session. | |
1072 | * | |
1073 | * @param $capability - capability name | |
1074 | * @param $sum - sum of all capabilities values | |
0468976c | 1075 | * @param $context - the context object |
bbbf2d40 | 1076 | * @param $array - when loading another user caps, their caps are not stored in session but an array |
1077 | */ | |
0468976c | 1078 | function capability_prohibits($capability, $context, $sum='', $array='') { |
bbbf2d40 | 1079 | global $USER; |
0468976c | 1080 | |
2176adf1 | 1081 | if (empty($context->id)) { |
1082 | return false; | |
1083 | } | |
1084 | ||
1085 | if (empty($capability)) { | |
1086 | return false; | |
1087 | } | |
1088 | ||
819e5a70 | 1089 | if ($sum < (CAP_PROHIBIT/2)) { |
bbbf2d40 | 1090 | // If this capability is set to prohibit. |
1091 | return true; | |
1092 | } | |
eef868d1 | 1093 | |
819e5a70 | 1094 | if (!empty($array)) { |
eef868d1 | 1095 | if (isset($array[$context->id][$capability]) |
819e5a70 | 1096 | && $array[$context->id][$capability] < (CAP_PROHIBIT/2)) { |
98882637 | 1097 | return true; |
eef868d1 | 1098 | } |
bbbf2d40 | 1099 | } else { |
98882637 | 1100 | // Else if set in session. |
eef868d1 | 1101 | if (isset($USER->capabilities[$context->id][$capability]) |
819e5a70 | 1102 | && $USER->capabilities[$context->id][$capability] < (CAP_PROHIBIT/2)) { |
98882637 | 1103 | return true; |
1104 | } | |
bbbf2d40 | 1105 | } |
aad2ba95 | 1106 | switch ($context->contextlevel) { |
eef868d1 | 1107 | |
bbbf2d40 | 1108 | case CONTEXT_SYSTEM: |
1109 | // By now it's a definite an inherit. | |
1110 | return 0; | |
1111 | break; | |
1112 | ||
1113 | case CONTEXT_PERSONAL: | |
2176adf1 | 1114 | $parent = get_context_instance(CONTEXT_SYSTEM); |
0468976c | 1115 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1116 | break; |
1117 | ||
4b10f08b | 1118 | case CONTEXT_USER: |
2176adf1 | 1119 | $parent = get_context_instance(CONTEXT_SYSTEM); |
0468976c | 1120 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1121 | break; |
1122 | ||
1123 | case CONTEXT_COURSECAT: | |
1124 | // Coursecat -> coursecat or site. | |
2176adf1 | 1125 | if (!$coursecat = get_record('course_categories','id',$context->instanceid)) { |
1126 | return false; | |
40a2a15f | 1127 | } |
41811960 | 1128 | if (!empty($coursecat->parent)) { |
bbbf2d40 | 1129 | // return parent value if exist. |
1130 | $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent); | |
1131 | } else { | |
1132 | // Return site value. | |
2176adf1 | 1133 | $parent = get_context_instance(CONTEXT_SYSTEM); |
bbbf2d40 | 1134 | } |
0468976c | 1135 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1136 | break; |
1137 | ||
1138 | case CONTEXT_COURSE: | |
1139 | // 1 to 1 to course cat. | |
1140 | // Find the course cat, and return its value. | |
2176adf1 | 1141 | if (!$course = get_record('course','id',$context->instanceid)) { |
1142 | return false; | |
1143 | } | |
40a2a15f | 1144 | // Yu: Separating site and site course context |
1145 | if ($course->id == SITEID) { | |
1146 | $parent = get_context_instance(CONTEXT_SYSTEM); | |
1147 | } else { | |
1148 | $parent = get_context_instance(CONTEXT_COURSECAT, $course->category); | |
1149 | } | |
0468976c | 1150 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1151 | break; |
1152 | ||
1153 | case CONTEXT_GROUP: | |
1154 | // 1 to 1 to course. | |
f3f7610c | 1155 | if (!$courseid = groups_get_course($context->instanceid)) { |
2176adf1 | 1156 | return false; |
1157 | } | |
f3f7610c | 1158 | $parent = get_context_instance(CONTEXT_COURSE, $courseid); |
0468976c | 1159 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1160 | break; |
1161 | ||
1162 | case CONTEXT_MODULE: | |
1163 | // 1 to 1 to course. | |
2176adf1 | 1164 | if (!$cm = get_record('course_modules','id',$context->instanceid)) { |
1165 | return false; | |
1166 | } | |
bbbf2d40 | 1167 | $parent = get_context_instance(CONTEXT_COURSE, $cm->course); |
0468976c | 1168 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1169 | break; |
1170 | ||
1171 | case CONTEXT_BLOCK: | |
1172 | // 1 to 1 to course. | |
2176adf1 | 1173 | if (!$block = get_record('block_instance','id',$context->instanceid)) { |
1174 | return false; | |
1175 | } | |
bbbf2d40 | 1176 | $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check |
0468976c | 1177 | return capability_prohibits($capability, $parent); |
bbbf2d40 | 1178 | break; |
1179 | ||
1180 | default: | |
2176adf1 | 1181 | print_error('unknowncontext'); |
1182 | return false; | |
bbbf2d40 | 1183 | } |
1184 | } | |
1185 | ||
1186 | ||
1187 | /** | |
1188 | * A print form function. This should either grab all the capabilities from | |
1189 | * files or a central table for that particular module instance, then present | |
1190 | * them in check boxes. Only relevant capabilities should print for known | |
1191 | * context. | |
1192 | * @param $mod - module id of the mod | |
1193 | */ | |
1194 | function print_capabilities($modid=0) { | |
1195 | global $CFG; | |
eef868d1 | 1196 | |
bbbf2d40 | 1197 | $capabilities = array(); |
1198 | ||
1199 | if ($modid) { | |
1200 | // We are in a module specific context. | |
1201 | ||
1202 | // Get the mod's name. | |
1203 | // Call the function that grabs the file and parse. | |
1204 | $cm = get_record('course_modules', 'id', $modid); | |
1205 | $module = get_record('modules', 'id', $cm->module); | |
eef868d1 | 1206 | |
bbbf2d40 | 1207 | } else { |
1208 | // Print all capabilities. | |
1209 | foreach ($capabilities as $capability) { | |
1210 | // Prints the check box component. | |
1211 | } | |
1212 | } | |
1213 | } | |
1214 | ||
1215 | ||
1216 | /** | |
1afecc03 | 1217 | * Installs the roles system. |
1218 | * This function runs on a fresh install as well as on an upgrade from the old | |
1219 | * hard-coded student/teacher/admin etc. roles to the new roles system. | |
bbbf2d40 | 1220 | */ |
1afecc03 | 1221 | function moodle_install_roles() { |
bbbf2d40 | 1222 | |
1afecc03 | 1223 | global $CFG, $db; |
eef868d1 | 1224 | |
459c1ff1 | 1225 | /// Create a system wide context for assignemnt. |
21c9bace | 1226 | $systemcontext = $context = get_context_instance(CONTEXT_SYSTEM); |
bbbf2d40 | 1227 | |
1afecc03 | 1228 | |
459c1ff1 | 1229 | /// Create default/legacy roles and capabilities. |
1230 | /// (1 legacy capability per legacy role at system level). | |
1231 | ||
69aaada0 | 1232 | $adminrole = create_role(addslashes(get_string('administrator')), 'admin', |
1233 | addslashes(get_string('administratordescription')), 'moodle/legacy:admin'); | |
1234 | $coursecreatorrole = create_role(addslashes(get_string('coursecreators')), 'coursecreator', | |
1235 | addslashes(get_string('coursecreatorsdescription')), 'moodle/legacy:coursecreator'); | |
1236 | $editteacherrole = create_role(addslashes(get_string('defaultcourseteacher')), 'editingteacher', | |
1237 | addslashes(get_string('defaultcourseteacherdescription')), 'moodle/legacy:editingteacher'); | |
1238 | $noneditteacherrole = create_role(addslashes(get_string('noneditingteacher')), 'teacher', | |
1239 | addslashes(get_string('noneditingteacherdescription')), 'moodle/legacy:teacher'); | |
1240 | $studentrole = create_role(addslashes(get_string('defaultcoursestudent')), 'student', | |
1241 | addslashes(get_string('defaultcoursestudentdescription')), 'moodle/legacy:student'); | |
1242 | $guestrole = create_role(addslashes(get_string('guest')), 'guest', | |
1243 | addslashes(get_string('guestdescription')), 'moodle/legacy:guest'); | |
2851ba9b | 1244 | |
17e5635c | 1245 | /// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles |
459c1ff1 | 1246 | |
98882637 | 1247 | if (!assign_capability('moodle/site:doanything', CAP_ALLOW, $adminrole, $systemcontext->id)) { |
bbbf2d40 | 1248 | error('Could not assign moodle/site:doanything to the admin role'); |
1249 | } | |
250934b8 | 1250 | if (!update_capabilities()) { |
1251 | error('Had trouble upgrading the core capabilities for the Roles System'); | |
1252 | } | |
1afecc03 | 1253 | |
459c1ff1 | 1254 | /// Look inside user_admin, user_creator, user_teachers, user_students and |
1255 | /// assign above new roles. If a user has both teacher and student role, | |
1256 | /// only teacher role is assigned. The assignment should be system level. | |
1257 | ||
1afecc03 | 1258 | $dbtables = $db->MetaTables('TABLES'); |
eef868d1 | 1259 | |
72da5046 | 1260 | /// Set up the progress bar |
1261 | ||
1262 | $usertables = array('user_admins', 'user_coursecreators', 'user_teachers', 'user_students'); | |
1263 | ||
1264 | $totalcount = $progresscount = 0; | |
1265 | foreach ($usertables as $usertable) { | |
1266 | if (in_array($CFG->prefix.$usertable, $dbtables)) { | |
1267 | $totalcount += count_records($usertable); | |
1268 | } | |
1269 | } | |
1270 | ||
aae37b63 | 1271 | print_progress(0, $totalcount, 5, 1, 'Processing role assignments'); |
1afecc03 | 1272 | |
459c1ff1 | 1273 | /// Upgrade the admins. |
1274 | /// Sort using id ASC, first one is primary admin. | |
1275 | ||
1afecc03 | 1276 | if (in_array($CFG->prefix.'user_admins', $dbtables)) { |
f1dcf000 | 1277 | if ($rs = get_recordset_sql('SELECT * from '.$CFG->prefix.'user_admins ORDER BY ID ASC')) { |
1278 | while (! $rs->EOF) { | |
1279 | $admin = $rs->FetchObj(); | |
1afecc03 | 1280 | role_assign($adminrole, $admin->userid, 0, $systemcontext->id); |
72da5046 | 1281 | $progresscount++; |
aae37b63 | 1282 | print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments'); |
f1dcf000 | 1283 | $rs->MoveNext(); |
1afecc03 | 1284 | } |
1285 | } | |
1286 | } else { | |
1287 | // This is a fresh install. | |
bbbf2d40 | 1288 | } |
1afecc03 | 1289 | |
1290 | ||
459c1ff1 | 1291 | /// Upgrade course creators. |
1afecc03 | 1292 | if (in_array($CFG->prefix.'user_coursecreators', $dbtables)) { |
f1dcf000 | 1293 | if ($rs = get_recordset('user_coursecreators')) { |
1294 | while (! $rs->EOF) { | |
1295 | $coursecreator = $rs->FetchObj(); | |
56b4d70d | 1296 | role_assign($coursecreatorrole, $coursecreator->userid, 0, $systemcontext->id); |
72da5046 | 1297 | $progresscount++; |
aae37b63 | 1298 | print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments'); |
f1dcf000 | 1299 | $rs->MoveNext(); |
1afecc03 | 1300 | } |
1301 | } | |
bbbf2d40 | 1302 | } |
1303 | ||
1afecc03 | 1304 | |
459c1ff1 | 1305 | /// Upgrade editting teachers and non-editting teachers. |
1afecc03 | 1306 | if (in_array($CFG->prefix.'user_teachers', $dbtables)) { |
f1dcf000 | 1307 | if ($rs = get_recordset('user_teachers')) { |
1308 | while (! $rs->EOF) { | |
1309 | $teacher = $rs->FetchObj(); | |
d5511451 | 1310 | |
1311 | // removed code here to ignore site level assignments | |
1312 | // since the contexts are separated now | |
1313 | ||
17d6a25e | 1314 | // populate the user_lastaccess table |
ece4945b | 1315 | $access = new object(); |
17d6a25e | 1316 | $access->timeaccess = $teacher->timeaccess; |
1317 | $access->userid = $teacher->userid; | |
1318 | $access->courseid = $teacher->course; | |
1319 | insert_record('user_lastaccess', $access); | |
f1dcf000 | 1320 | |
17d6a25e | 1321 | // assign the default student role |
1afecc03 | 1322 | $coursecontext = get_context_instance(CONTEXT_COURSE, $teacher->course); // needs cache |
1323 | if ($teacher->editall) { // editting teacher | |
1324 | role_assign($editteacherrole, $teacher->userid, 0, $coursecontext->id); | |
1325 | } else { | |
1326 | role_assign($noneditteacherrole, $teacher->userid, 0, $coursecontext->id); | |
1327 | } | |
72da5046 | 1328 | $progresscount++; |
aae37b63 | 1329 | print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments'); |
f1dcf000 | 1330 | |
1331 | $rs->MoveNext(); | |
1afecc03 | 1332 | } |
bbbf2d40 | 1333 | } |
1334 | } | |
1afecc03 | 1335 | |
1336 | ||
459c1ff1 | 1337 | /// Upgrade students. |
1afecc03 | 1338 | if (in_array($CFG->prefix.'user_students', $dbtables)) { |
f1dcf000 | 1339 | if ($rs = get_recordset('user_students')) { |
1340 | while (! $rs->EOF) { | |
1341 | $student = $rs->FetchObj(); | |
1342 | ||
17d6a25e | 1343 | // populate the user_lastaccess table |
f1dcf000 | 1344 | $access = new object; |
17d6a25e | 1345 | $access->timeaccess = $student->timeaccess; |
1346 | $access->userid = $student->userid; | |
1347 | $access->courseid = $student->course; | |
1348 | insert_record('user_lastaccess', $access); | |
f1dcf000 | 1349 | |
17d6a25e | 1350 | // assign the default student role |
1afecc03 | 1351 | $coursecontext = get_context_instance(CONTEXT_COURSE, $student->course); |
1352 | role_assign($studentrole, $student->userid, 0, $coursecontext->id); | |
72da5046 | 1353 | $progresscount++; |
aae37b63 | 1354 | print_progress($progresscount, $totalcount, 5, 1, 'Processing role assignments'); |
f1dcf000 | 1355 | |
1356 | $rs->MoveNext(); | |
1afecc03 | 1357 | } |
1358 | } | |
bbbf2d40 | 1359 | } |
1afecc03 | 1360 | |
1361 | ||
459c1ff1 | 1362 | /// Upgrade guest (only 1 entry). |
1afecc03 | 1363 | if ($guestuser = get_record('user', 'username', 'guest')) { |
1364 | role_assign($guestrole, $guestuser->id, 0, $systemcontext->id); | |
1365 | } | |
aae37b63 | 1366 | print_progress($totalcount, $totalcount, 5, 1, 'Processing role assignments'); |
1afecc03 | 1367 | |
459c1ff1 | 1368 | |
1369 | /// Insert the correct records for legacy roles | |
945f88ca | 1370 | allow_assign($adminrole, $adminrole); |
1371 | allow_assign($adminrole, $coursecreatorrole); | |
1372 | allow_assign($adminrole, $noneditteacherrole); | |
eef868d1 | 1373 | allow_assign($adminrole, $editteacherrole); |
945f88ca | 1374 | allow_assign($adminrole, $studentrole); |
1375 | allow_assign($adminrole, $guestrole); | |
eef868d1 | 1376 | |
945f88ca | 1377 | allow_assign($coursecreatorrole, $noneditteacherrole); |
1378 | allow_assign($coursecreatorrole, $editteacherrole); | |
eef868d1 | 1379 | allow_assign($coursecreatorrole, $studentrole); |
945f88ca | 1380 | allow_assign($coursecreatorrole, $guestrole); |
eef868d1 | 1381 | |
1382 | allow_assign($editteacherrole, $noneditteacherrole); | |
1383 | allow_assign($editteacherrole, $studentrole); | |
945f88ca | 1384 | allow_assign($editteacherrole, $guestrole); |
eef868d1 | 1385 | |
459c1ff1 | 1386 | /// Set up default permissions for overrides |
945f88ca | 1387 | allow_override($adminrole, $adminrole); |
1388 | allow_override($adminrole, $coursecreatorrole); | |
1389 | allow_override($adminrole, $noneditteacherrole); | |
eef868d1 | 1390 | allow_override($adminrole, $editteacherrole); |
945f88ca | 1391 | allow_override($adminrole, $studentrole); |
eef868d1 | 1392 | allow_override($adminrole, $guestrole); |
1afecc03 | 1393 | |
746a04c5 | 1394 | |
459c1ff1 | 1395 | /// Delete the old user tables when we are done |
1396 | ||
83ea392e | 1397 | drop_table(new XMLDBTable('user_students')); |
1398 | drop_table(new XMLDBTable('user_teachers')); | |
1399 | drop_table(new XMLDBTable('user_coursecreators')); | |
1400 | drop_table(new XMLDBTable('user_admins')); | |
459c1ff1 | 1401 | |
bbbf2d40 | 1402 | } |
1403 | ||
bbbf2d40 | 1404 | /** |
1405 | * Assign the defaults found in this capabality definition to roles that have | |
1406 | * the corresponding legacy capabilities assigned to them. | |
1407 | * @param $legacyperms - an array in the format (example): | |
1408 | * 'guest' => CAP_PREVENT, | |
1409 | * 'student' => CAP_ALLOW, | |
1410 | * 'teacher' => CAP_ALLOW, | |
1411 | * 'editingteacher' => CAP_ALLOW, | |
1412 | * 'coursecreator' => CAP_ALLOW, | |
1413 | * 'admin' => CAP_ALLOW | |
1414 | * @return boolean - success or failure. | |
1415 | */ | |
1416 | function assign_legacy_capabilities($capability, $legacyperms) { | |
eef868d1 | 1417 | |
bbbf2d40 | 1418 | foreach ($legacyperms as $type => $perm) { |
eef868d1 | 1419 | |
21c9bace | 1420 | $systemcontext = get_context_instance(CONTEXT_SYSTEM); |
eef868d1 | 1421 | |
bbbf2d40 | 1422 | // The legacy capabilities are: |
1423 | // 'moodle/legacy:guest' | |
1424 | // 'moodle/legacy:student' | |
1425 | // 'moodle/legacy:teacher' | |
1426 | // 'moodle/legacy:editingteacher' | |
1427 | // 'moodle/legacy:coursecreator' | |
1428 | // 'moodle/legacy:admin' | |
eef868d1 | 1429 | |
2e85fffe | 1430 | if ($roles = get_roles_with_capability('moodle/legacy:'.$type, CAP_ALLOW)) { |
1431 | foreach ($roles as $role) { | |
1432 | // Assign a site level capability. | |
1433 | if (!assign_capability($capability, $perm, $role->id, $systemcontext->id)) { | |
1434 | return false; | |
1435 | } | |
bbbf2d40 | 1436 | } |
1437 | } | |
1438 | } | |
1439 | return true; | |
1440 | } | |
1441 | ||
1442 | ||
cee0901c | 1443 | /** |
1444 | * Checks to see if a capability is a legacy capability. | |
1445 | * @param $capabilityname | |
1446 | * @return boolean | |
1447 | */ | |
bbbf2d40 | 1448 | function islegacy($capabilityname) { |
98882637 | 1449 | if (strstr($capabilityname, 'legacy') === false) { |
eef868d1 | 1450 | return false; |
98882637 | 1451 | } else { |
eef868d1 | 1452 | return true; |
98882637 | 1453 | } |
bbbf2d40 | 1454 | } |
1455 | ||
cee0901c | 1456 | |
1457 | ||
1458 | /********************************** | |
bbbf2d40 | 1459 | * Context Manipulation functions * |
1460 | **********************************/ | |
1461 | ||
bbbf2d40 | 1462 | /** |
9991d157 | 1463 | * Create a new context record for use by all roles-related stuff |
bbbf2d40 | 1464 | * @param $level |
1465 | * @param $instanceid | |
3ca2dea5 | 1466 | * |
1467 | * @return object newly created context (or existing one with a debug warning) | |
bbbf2d40 | 1468 | */ |
aad2ba95 | 1469 | function create_context($contextlevel, $instanceid) { |
3ca2dea5 | 1470 | if (!$context = get_record('context','contextlevel',$contextlevel,'instanceid',$instanceid)) { |
1471 | if (!validate_context($contextlevel, $instanceid)) { | |
1472 | debugging('Error: Invalid context creation request for level "'.s($contextlevel).'", instance "'.s($instanceid).'".'); | |
1473 | return NULL; | |
1474 | } | |
1475 | $context = new object(); | |
aad2ba95 | 1476 | $context->contextlevel = $contextlevel; |
bbbf2d40 | 1477 | $context->instanceid = $instanceid; |
3ca2dea5 | 1478 | if ($id = insert_record('context',$context)) { |
1479 | return get_record('context','id',$id); | |
1480 | } else { | |
1481 | debugging('Error: could not insert new context level "'.s($contextlevel).'", instance "'.s($instanceid).'".'); | |
1482 | return NULL; | |
1483 | } | |
1484 | } else { | |
1485 | debugging('Warning: Context id "'.s($context->id).'" not created, because it already exists.'); | |
1486 | return $context; | |
bbbf2d40 | 1487 | } |
1488 | } | |
1489 | ||
9991d157 | 1490 | /** |
1491 | * Create a new context record for use by all roles-related stuff | |
1492 | * @param $level | |
1493 | * @param $instanceid | |
3ca2dea5 | 1494 | * |
1495 | * @return true if properly deleted | |
9991d157 | 1496 | */ |
1497 | function delete_context($contextlevel, $instanceid) { | |
1498 | if ($context = get_context_instance($contextlevel, $instanceid)) { | |
1499 | return delete_records('context', 'id', $context->id) && | |
1500 | delete_records('role_assignments', 'contextid', $context->id) && | |
9b5d7a46 | 1501 | delete_records('role_capabilities', 'contextid', $context->id); |
9991d157 | 1502 | } |
1503 | return true; | |
1504 | } | |
1505 | ||
3ca2dea5 | 1506 | /** |
1507 | * Validate that object with instanceid really exists in given context level. | |
1508 | * | |
1509 | * return if instanceid object exists | |
1510 | */ | |
1511 | function validate_context($contextlevel, $instanceid) { | |
1512 | switch ($contextlevel) { | |
1513 | ||
1514 | case CONTEXT_SYSTEM: | |
1515 | return ($instanceid == SITEID); | |
1516 | ||
1517 | case CONTEXT_PERSONAL: | |
1518 | return (boolean)count_records('user', 'id', $instanceid); | |
1519 | ||
1520 | case CONTEXT_USER: | |
1521 | return (boolean)count_records('user', 'id', $instanceid); | |
1522 | ||
1523 | case CONTEXT_COURSECAT: | |
1cd3eba9 | 1524 | if ($instanceid == 0) { |
1525 | return true; // site course category | |
1526 | } | |
3ca2dea5 | 1527 | return (boolean)count_records('course_categories', 'id', $instanceid); |
1528 | ||
1529 | case CONTEXT_COURSE: | |
1530 | return (boolean)count_records('course', 'id', $instanceid); | |
1531 | ||
1532 | case CONTEXT_GROUP: | |
f3f7610c ML |
1533 | //return (boolean)count_records('groups_groups', 'id', $instanceid); //TODO:DONOTCOMMIT: |
1534 | return groups_group_exists($instanceid); | |
3ca2dea5 | 1535 | |
1536 | case CONTEXT_MODULE: | |
1537 | return (boolean)count_records('course_modules', 'id', $instanceid); | |
1538 | ||
1539 | case CONTEXT_BLOCK: | |
1540 | return (boolean)count_records('block_instance', 'id', $instanceid); | |
1541 | ||
1542 | default: | |
1543 | return false; | |
1544 | } | |
1545 | } | |
bbbf2d40 | 1546 | |
1547 | /** | |
1548 | * Get the context instance as an object. This function will create the | |
1549 | * context instance if it does not exist yet. | |
1550 | * @param $level | |
1551 | * @param $instance | |
1552 | */ | |
aad2ba95 | 1553 | function get_context_instance($contextlevel=NULL, $instance=SITEID) { |
e5605780 | 1554 | |
51195e6f | 1555 | global $context_cache, $context_cache_id, $CONTEXT; |
a36a3a3f | 1556 | static $allowed_contexts = array(CONTEXT_SYSTEM, CONTEXT_PERSONAL, CONTEXT_USER, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_GROUP, CONTEXT_MODULE, CONTEXT_BLOCK); |
d9a35e12 | 1557 | |
b7cec865 | 1558 | // This is really a systen context |
40a2a15f | 1559 | // Yu: Separating site and site course context |
1560 | /* | |
b7cec865 | 1561 | if ($contextlevel == CONTEXT_COURSE && $instance == SITEID) { |
1562 | $contextlevel = CONTEXT_SYSTEM; | |
40a2a15f | 1563 | }*/ |
b7cec865 | 1564 | |
340ea4e8 | 1565 | /// If no level is supplied then return the current global context if there is one |
aad2ba95 | 1566 | if (empty($contextlevel)) { |
340ea4e8 | 1567 | if (empty($CONTEXT)) { |
a36a3a3f | 1568 | //fatal error, code must be fixed |
1569 | error("Error: get_context_instance() called without a context"); | |
340ea4e8 | 1570 | } else { |
1571 | return $CONTEXT; | |
1572 | } | |
e5605780 | 1573 | } |
1574 | ||
a36a3a3f | 1575 | /// check allowed context levels |
1576 | if (!in_array($contextlevel, $allowed_contexts)) { | |
7bfa3101 | 1577 | // fatal error, code must be fixed - probably typo or switched parameters |
a36a3a3f | 1578 | error('Error: get_context_instance() called with incorrect context level "'.s($contextlevel).'"'); |
1579 | } | |
1580 | ||
340ea4e8 | 1581 | /// Check the cache |
aad2ba95 | 1582 | if (isset($context_cache[$contextlevel][$instance])) { // Already cached |
1583 | return $context_cache[$contextlevel][$instance]; | |
e5605780 | 1584 | } |
1585 | ||
340ea4e8 | 1586 | /// Get it from the database, or create it |
aad2ba95 | 1587 | if (!$context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance)) { |
1588 | create_context($contextlevel, $instance); | |
1589 | $context = get_record('context', 'contextlevel', $contextlevel, 'instanceid', $instance); | |
e5605780 | 1590 | } |
1591 | ||
ccfc5ecc | 1592 | /// Only add to cache if context isn't empty. |
1593 | if (!empty($context)) { | |
aad2ba95 | 1594 | $context_cache[$contextlevel][$instance] = $context; // Cache it for later |
ccfc5ecc | 1595 | $context_cache_id[$context->id] = $context; // Cache it for later |
1596 | } | |
0468976c | 1597 | |
bbbf2d40 | 1598 | return $context; |
1599 | } | |
1600 | ||
cee0901c | 1601 | |
340ea4e8 | 1602 | /** |
1603 | * Get a context instance as an object, from a given id. | |
1604 | * @param $id | |
1605 | */ | |
1606 | function get_context_instance_by_id($id) { | |
1607 | ||
d9a35e12 | 1608 | global $context_cache, $context_cache_id; |
1609 | ||
340ea4e8 | 1610 | if (isset($context_cache_id[$id])) { // Already cached |
75e84883 | 1611 | return $context_cache_id[$id]; |
340ea4e8 | 1612 | } |
1613 | ||
1614 | if ($context = get_record('context', 'id', $id)) { // Update the cache and return | |
aad2ba95 | 1615 | $context_cache[$context->contextlevel][$context->instanceid] = $context; |
340ea4e8 | 1616 | $context_cache_id[$context->id] = $context; |
1617 | return $context; | |
1618 | } | |
1619 | ||
1620 | return false; | |
1621 | } | |
1622 | ||
bbbf2d40 | 1623 | |
8737be58 | 1624 | /** |
1625 | * Get the local override (if any) for a given capability in a role in a context | |
1626 | * @param $roleid | |
0468976c | 1627 | * @param $contextid |
1628 | * @param $capability | |
8737be58 | 1629 | */ |
1630 | function get_local_override($roleid, $contextid, $capability) { | |
1631 | return get_record('role_capabilities', 'roleid', $roleid, 'capability', $capability, 'contextid', $contextid); | |
1632 | } | |
1633 | ||
1634 | ||
bbbf2d40 | 1635 | |
1636 | /************************************ | |
1637 | * DB TABLE RELATED FUNCTIONS * | |
1638 | ************************************/ | |
1639 | ||
cee0901c | 1640 | /** |
bbbf2d40 | 1641 | * function that creates a role |
1642 | * @param name - role name | |
31f26796 | 1643 | * @param shortname - role short name |
bbbf2d40 | 1644 | * @param description - role description |
1645 | * @param legacy - optional legacy capability | |
1646 | * @return id or false | |
1647 | */ | |
8420bee9 | 1648 | function create_role($name, $shortname, $description, $legacy='') { |
eef868d1 | 1649 | |
98882637 | 1650 | // check for duplicate role name |
eef868d1 | 1651 | |
98882637 | 1652 | if ($role = get_record('role','name', $name)) { |
eef868d1 | 1653 | error('there is already a role with this name!'); |
98882637 | 1654 | } |
eef868d1 | 1655 | |
31f26796 | 1656 | if ($role = get_record('role','shortname', $shortname)) { |
eef868d1 | 1657 | error('there is already a role with this shortname!'); |
31f26796 | 1658 | } |
1659 | ||
b5959f30 | 1660 | $role = new object(); |
98882637 | 1661 | $role->name = $name; |
31f26796 | 1662 | $role->shortname = $shortname; |
98882637 | 1663 | $role->description = $description; |
eef868d1 | 1664 | |
8420bee9 | 1665 | //find free sortorder number |
1666 | $role->sortorder = count_records('role'); | |
1667 | while (get_record('role','sortorder', $role->sortorder)) { | |
1668 | $role->sortorder += 1; | |
b5959f30 | 1669 | } |
1670 | ||
21c9bace | 1671 | if (!$context = get_context_instance(CONTEXT_SYSTEM)) { |
1672 | return false; | |
1673 | } | |
eef868d1 | 1674 | |
98882637 | 1675 | if ($id = insert_record('role', $role)) { |
eef868d1 | 1676 | if ($legacy) { |
1677 | assign_capability($legacy, CAP_ALLOW, $id, $context->id); | |
98882637 | 1678 | } |
eef868d1 | 1679 | |
ec7a8b79 | 1680 | /// By default, users with role:manage at site level |
1681 | /// should be able to assign users to this new role, and override this new role's capabilities | |
eef868d1 | 1682 | |
ec7a8b79 | 1683 | // find all admin roles |
e46c0987 | 1684 | if ($adminroles = get_roles_with_capability('moodle/role:manage', CAP_ALLOW, $context)) { |
1685 | // foreach admin role | |
1686 | foreach ($adminroles as $arole) { | |
1687 | // write allow_assign and allow_overrid | |
1688 | allow_assign($arole->id, $id); | |
eef868d1 | 1689 | allow_override($arole->id, $id); |
e46c0987 | 1690 | } |
ec7a8b79 | 1691 | } |
eef868d1 | 1692 | |
98882637 | 1693 | return $id; |
1694 | } else { | |
eef868d1 | 1695 | return false; |
98882637 | 1696 | } |
eef868d1 | 1697 | |
bbbf2d40 | 1698 | } |
1699 | ||
8420bee9 | 1700 | /** |
1701 | * function that deletes a role and cleanups up after it | |
1702 | * @param roleid - id of role to delete | |
1703 | * @return success | |
1704 | */ | |
1705 | function delete_role($roleid) { | |
1706 | $success = true; | |
1707 | ||
1708 | // first unssign all users | |
1709 | if (!role_unassign($roleid)) { | |
1710 | debugging("Error while unassigning all users from role with ID $roleid!"); | |
1711 | $success = false; | |
1712 | } | |
1713 | ||
1714 | // cleanup all references to this role, ignore errors | |
1715 | if ($success) { | |
1716 | delete_records('role_capabilities', 'roleid', $roleid); | |
1717 | delete_records('role_allow_assign', 'roleid', $roleid); | |
1718 | delete_records('role_allow_assign', 'allowassign', $roleid); | |
1719 | delete_records('role_allow_override', 'roleid', $roleid); | |
1720 | delete_records('role_allow_override', 'allowoverride', $roleid); | |
1721 | delete_records('role_names', 'roleid', $roleid); | |
1722 | } | |
1723 | ||
1724 | // finally delete the role itself | |
1725 | if ($success and !delete_records('role', 'id', $roleid)) { | |
ece4945b | 1726 | debugging("Could not delete role record with ID $roleid!"); |
8420bee9 | 1727 | $success = false; |
1728 | } | |
1729 | ||
1730 | return $success; | |
1731 | } | |
1732 | ||
bbbf2d40 | 1733 | /** |
1734 | * Function to write context specific overrides, or default capabilities. | |
1735 | * @param module - string name | |
1736 | * @param capability - string name | |
1737 | * @param contextid - context id | |
1738 | * @param roleid - role id | |
1739 | * @param permission - int 1,-1 or -1000 | |
96986241 | 1740 | * should not be writing if permission is 0 |
bbbf2d40 | 1741 | */ |
e7876c1e | 1742 | function assign_capability($capability, $permission, $roleid, $contextid, $overwrite=false) { |
eef868d1 | 1743 | |
98882637 | 1744 | global $USER; |
eef868d1 | 1745 | |
96986241 | 1746 | if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set |
eef868d1 | 1747 | unassign_capability($capability, $roleid, $contextid); |
96986241 | 1748 | return true; |
98882637 | 1749 | } |
eef868d1 | 1750 | |
2e85fffe | 1751 | $existing = get_record('role_capabilities', 'contextid', $contextid, 'roleid', $roleid, 'capability', $capability); |
e7876c1e | 1752 | |
1753 | if ($existing and !$overwrite) { // We want to keep whatever is there already | |
1754 | return true; | |
1755 | } | |
1756 | ||
bbbf2d40 | 1757 | $cap = new object; |
1758 | $cap->contextid = $contextid; | |
1759 | $cap->roleid = $roleid; | |
1760 | $cap->capability = $capability; | |
1761 | $cap->permission = $permission; | |
1762 | $cap->timemodified = time(); | |
9db12da7 | 1763 | $cap->modifierid = empty($USER->id) ? 0 : $USER->id; |
e7876c1e | 1764 | |
1765 | if ($existing) { | |
1766 | $cap->id = $existing->id; | |
1767 | return update_record('role_capabilities', $cap); | |
1768 | } else { | |
1769 | return insert_record('role_capabilities', $cap); | |
1770 | } | |
bbbf2d40 | 1771 | } |
1772 | ||
1773 | ||
1774 | /** | |
1775 | * Unassign a capability from a role. | |
1776 | * @param $roleid - the role id | |
1777 | * @param $capability - the name of the capability | |
1778 | * @return boolean - success or failure | |
1779 | */ | |
1780 | function unassign_capability($capability, $roleid, $contextid=NULL) { | |
eef868d1 | 1781 | |
98882637 | 1782 | if (isset($contextid)) { |
1783 | $status = delete_records('role_capabilities', 'capability', $capability, | |
1784 | 'roleid', $roleid, 'contextid', $contextid); | |
1785 | } else { | |
1786 | $status = delete_records('role_capabilities', 'capability', $capability, | |
1787 | 'roleid', $roleid); | |
1788 | } | |
1789 | return $status; | |
bbbf2d40 | 1790 | } |
1791 | ||
1792 | ||
1793 | /** | |
759ac72d | 1794 | * Get the roles that have a given capability assigned to it. This function |
1795 | * does not resolve the actual permission of the capability. It just checks | |
1796 | * for assignment only. | |
bbbf2d40 | 1797 | * @param $capability - capability name (string) |
1798 | * @param $permission - optional, the permission defined for this capability | |
1799 | * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT | |
1800 | * @return array or role objects | |
1801 | */ | |
ec7a8b79 | 1802 | function get_roles_with_capability($capability, $permission=NULL, $context='') { |
1803 | ||
bbbf2d40 | 1804 | global $CFG; |
eef868d1 | 1805 | |
ec7a8b79 | 1806 | if ($context) { |
1807 | if ($contexts = get_parent_contexts($context)) { | |
1808 | $listofcontexts = '('.implode(',', $contexts).')'; | |
1809 | } else { | |
21c9bace | 1810 | $sitecontext = get_context_instance(CONTEXT_SYSTEM); |
eef868d1 | 1811 | $listofcontexts = '('.$sitecontext->id.')'; // must be site |
1812 | } | |
42ac3ecf | 1813 | $contextstr = "AND (rc.contextid = '$context->id' OR rc.contextid IN $listofcontexts)"; |
ec7a8b79 | 1814 | } else { |
1815 | $contextstr = ''; | |
1816 | } | |
eef868d1 | 1817 | |
1818 | $selectroles = "SELECT r.* | |
42ac3ecf | 1819 | FROM {$CFG->prefix}role r, |
1820 | {$CFG->prefix}role_capabilities rc | |
bbbf2d40 | 1821 | WHERE rc.capability = '$capability' |
ec7a8b79 | 1822 | AND rc.roleid = r.id $contextstr"; |
bbbf2d40 | 1823 | |
1824 | if (isset($permission)) { | |
1825 | $selectroles .= " AND rc.permission = '$permission'"; | |
1826 | } | |
1827 | return get_records_sql($selectroles); | |
1828 | } | |
1829 | ||
1830 | ||
1831 | /** | |
a9e1c058 | 1832 | * This function makes a role-assignment (a role for a user or group in a particular context) |
bbbf2d40 | 1833 | * @param $roleid - the role of the id |
1834 | * @param $userid - userid | |
1835 | * @param $groupid - group id | |
1836 | * @param $contextid - id of the context | |
1837 | * @param $timestart - time this assignment becomes effective | |
1838 | * @param $timeend - time this assignemnt ceases to be effective | |
1839 | * @uses $USER | |
1840 | * @return id - new id of the assigment | |
1841 | */ | |
f44152f4 | 1842 | function role_assign($roleid, $userid, $groupid, $contextid, $timestart=0, $timeend=0, $hidden=0, $enrol='manual') { |
aa311411 | 1843 | global $USER, $CFG; |
bbbf2d40 | 1844 | |
7eb0b60a | 1845 | debugging("Assign roleid $roleid userid $userid contextid $contextid", DEBUG_DEVELOPER); |
bbbf2d40 | 1846 | |
a9e1c058 | 1847 | /// Do some data validation |
1848 | ||
bbbf2d40 | 1849 | if (empty($roleid)) { |
9d829c68 | 1850 | debugging('Role ID not provided'); |
a9e1c058 | 1851 | return false; |
bbbf2d40 | 1852 | } |
1853 | ||
1854 | if (empty($userid) && empty($groupid)) { | |
9d829c68 | 1855 | debugging('Either userid or groupid must be provided'); |
a9e1c058 | 1856 | return false; |
bbbf2d40 | 1857 | } |
eef868d1 | 1858 | |
7700027f | 1859 | if ($userid && !record_exists('user', 'id', $userid)) { |
82396e5b | 1860 | debugging('User ID '.intval($userid).' does not exist!'); |
7700027f | 1861 | return false; |
1862 | } | |
bbbf2d40 | 1863 | |
f3f7610c | 1864 | if ($groupid && !groups_group_exists($groupid)) { |
82396e5b | 1865 | debugging('Group ID '.intval($groupid).' does not exist!'); |
dc411d1b | 1866 | return false; |
1867 | } | |
1868 | ||
7700027f | 1869 | if (!$context = get_context_instance_by_id($contextid)) { |
82396e5b | 1870 | debugging('Context ID '.intval($contextid).' does not exist!'); |
a9e1c058 | 1871 | return false; |
bbbf2d40 | 1872 | } |
1873 | ||
a9e1c058 | 1874 | if (($timestart and $timeend) and ($timestart > $timeend)) { |
9d829c68 | 1875 | debugging('The end time can not be earlier than the start time'); |
a9e1c058 | 1876 | return false; |
1877 | } | |
1878 | ||
7700027f | 1879 | |
a9e1c058 | 1880 | /// Check for existing entry |
1881 | if ($userid) { | |
7700027f | 1882 | $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'userid', $userid); |
a9e1c058 | 1883 | } else { |
7700027f | 1884 | $ra = get_record('role_assignments', 'roleid', $roleid, 'contextid', $context->id, 'groupid', $groupid); |
a9e1c058 | 1885 | } |
1886 | ||
9ebcb4d2 | 1887 | |
a9e1c058 | 1888 | $newra = new object; |
bbbf2d40 | 1889 | |
a9e1c058 | 1890 | if (empty($ra)) { // Create a new entry |
1891 | $newra->roleid = $roleid; | |
7700027f | 1892 | $newra->contextid = $context->id; |
a9e1c058 | 1893 | $newra->userid = $userid; |
a9e1c058 | 1894 | $newra->hidden = $hidden; |
f44152f4 | 1895 | $newra->enrol = $enrol; |
a9e1c058 | 1896 | $newra->timestart = $timestart; |
1897 | $newra->timeend = $timeend; | |
1898 | $newra->timemodified = time(); | |
115faa2f | 1899 | $newra->modifierid = empty($USER->id) ? 0 : $USER->id; |
a9e1c058 | 1900 | |
9ebcb4d2 | 1901 | $success = insert_record('role_assignments', $newra); |
a9e1c058 | 1902 | |
1903 | } else { // We already have one, just update it | |
1904 | ||
1905 | $newra->id = $ra->id; | |
1906 | $newra->hidden = $hidden; | |
f44152f4 | 1907 | $newra->enrol = $enrol; |
a9e1c058 | 1908 | $newra->timestart = $timestart; |
1909 | $newra->timeend = $timeend; | |
1910 | $newra->timemodified = time(); | |
115faa2f | 1911 | $newra->modifierid = empty($USER->id) ? 0 : $USER->id; |
a9e1c058 | 1912 | |
9ebcb4d2 | 1913 | $success = update_record('role_assignments', $newra); |
1914 | } | |
1915 | ||
7700027f | 1916 | if ($success) { /// Role was assigned, so do some other things |
1917 | ||
1918 | /// If the user is the current user, then reload the capabilities too. | |
1919 | if (!empty($USER->id) && $USER->id == $userid) { | |
2f1a4248 | 1920 | load_all_capabilities(); |
7700027f | 1921 | } |
9b5d7a46 | 1922 | |
0f161e1f | 1923 | /// Ask all the modules if anything needs to be done for this user |
1924 | if ($mods = get_list_of_plugins('mod')) { | |
1925 | foreach ($mods as $mod) { | |
1926 | include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php'); | |
1927 | $functionname = $mod.'_role_assign'; | |
1928 | if (function_exists($functionname)) { | |
1929 | $functionname($userid, $context); | |
1930 | } | |
1931 | } | |
1932 | } | |
1933 | ||
1934 | /// Make sure they have an entry in user_lastaccess for courses they can access | |
1935 | // role_add_lastaccess_entries($userid, $context); | |
a9e1c058 | 1936 | } |
eef868d1 | 1937 | |
4e5f3064 | 1938 | /// now handle metacourse role assignments if in course context |
aad2ba95 | 1939 | if ($success and $context->contextlevel == CONTEXT_COURSE) { |
4e5f3064 | 1940 | if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) { |
1941 | foreach ($parents as $parent) { | |
1aad4310 | 1942 | sync_metacourse($parent->parent_course); |
4e5f3064 | 1943 | } |
1944 | } | |
1945 | } | |
6eb4f823 | 1946 | |
1947 | return $success; | |
bbbf2d40 | 1948 | } |
1949 | ||
1950 | ||
1951 | /** | |
1dc1f037 | 1952 | * Deletes one or more role assignments. You must specify at least one parameter. |
bbbf2d40 | 1953 | * @param $roleid |
1954 | * @param $userid | |
1955 | * @param $groupid | |
1956 | * @param $contextid | |
1957 | * @return boolean - success or failure | |
1958 | */ | |
1dc1f037 | 1959 | function role_unassign($roleid=0, $userid=0, $groupid=0, $contextid=0) { |
d74067e8 | 1960 | |
1961 | global $USER, $CFG; | |
eef868d1 | 1962 | |
4e5f3064 | 1963 | $success = true; |
d74067e8 | 1964 | |
1dc1f037 | 1965 | $args = array('roleid', 'userid', 'groupid', 'contextid'); |
1966 | $select = array(); | |
1967 | foreach ($args as $arg) { | |
1968 | if ($$arg) { | |
1969 | $select[] = $arg.' = '.$$arg; | |
1970 | } | |
1971 | } | |
d74067e8 | 1972 | |
1dc1f037 | 1973 | if ($select) { |
4e5f3064 | 1974 | if ($ras = get_records_select('role_assignments', implode(' AND ', $select))) { |
1975 | $mods = get_list_of_plugins('mod'); | |
1976 | foreach($ras as $ra) { | |
86e2c51d | 1977 | /// infinite loop protection when deleting recursively |
1978 | if (!$ra = get_record('role_assignments', 'id', $ra->id)) { | |
1979 | continue; | |
1980 | } | |
4e5f3064 | 1981 | $success = delete_records('role_assignments', 'id', $ra->id) and $success; |
86e2c51d | 1982 | |
4e5f3064 | 1983 | /// If the user is the current user, then reload the capabilities too. |
1984 | if (!empty($USER->id) && $USER->id == $ra->userid) { | |
2f1a4248 | 1985 | load_all_capabilities(); |
4e5f3064 | 1986 | } |
1987 | $context = get_record('context', 'id', $ra->contextid); | |
0f161e1f | 1988 | |
1989 | /// Ask all the modules if anything needs to be done for this user | |
4e5f3064 | 1990 | foreach ($mods as $mod) { |
1991 | include_once($CFG->dirroot.'/mod/'.$mod.'/lib.php'); | |
1992 | $functionname = $mod.'_role_unassign'; | |
1993 | if (function_exists($functionname)) { | |
1994 | $functionname($ra->userid, $context); // watch out, $context might be NULL if something goes wrong | |
1995 | } | |
1996 | } | |
1997 | ||
1998 | /// now handle metacourse role unassigment and removing from goups if in course context | |
aad2ba95 | 1999 | if (!empty($context) and $context->contextlevel == CONTEXT_COURSE) { |
4e5f3064 | 2000 | //remove from groups when user has no role |
2001 | $roles = get_user_roles($context, $ra->userid, true); | |
2002 | if (empty($roles)) { | |
2003 | if ($groups = get_groups($context->instanceid, $ra->userid)) { | |
2004 | foreach ($groups as $group) { | |
2005 | delete_records('groups_members', 'groupid', $group->id, 'userid', $ra->userid); | |
2006 | } | |
2007 | } | |
2008 | } | |
1aad4310 | 2009 | //unassign roles in metacourses if needed |
4e5f3064 | 2010 | if ($parents = get_records('course_meta', 'child_course', $context->instanceid)) { |
2011 | foreach ($parents as $parent) { | |
1aad4310 | 2012 | sync_metacourse($parent->parent_course); |
0f161e1f | 2013 | } |
2014 | } | |
0f161e1f | 2015 | } |
2016 | } | |
d74067e8 | 2017 | } |
1dc1f037 | 2018 | } |
4e5f3064 | 2019 | |
2020 | return $success; | |
bbbf2d40 | 2021 | } |
2022 | ||
eef868d1 | 2023 | /* |
2024 | * A convenience function to take care of the common case where you | |
b963384f | 2025 | * just want to enrol someone using the default role into a course |
2026 | * | |
2027 | * @param object $course | |
2028 | * @param object $user | |
2029 | * @param string $enrol - the plugin used to do this enrolment | |
2030 | */ | |
2031 | function enrol_into_course($course, $user, $enrol) { | |
2032 | ||
2033 | if ($course->enrolperiod) { | |
2034 | $timestart = time(); | |
2035 | $timeend = time() + $course->enrolperiod; | |
2036 | } else { | |
2037 | $timestart = $timeend = 0; | |
2038 | } | |
2039 | ||
2040 | if ($role = get_default_course_role($course)) { | |
c4381ef5 | 2041 | |
2042 | $context = get_context_instance(CONTEXT_COURSE, $course->id); | |
2043 | ||
e2183037 | 2044 | if (!role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, $enrol)) { |
b963384f | 2045 | return false; |
2046 | } | |
eef868d1 | 2047 | |
b963384f | 2048 | email_welcome_message_to_user($course, $user); |
eef868d1 | 2049 | |
b963384f | 2050 | add_to_log($course->id, 'course', 'enrol', 'view.php?id='.$course->id, $user->id); |
2051 | ||
2052 | return true; | |
2053 | } | |
2054 | ||
2055 | return false; | |
2056 | } | |
2057 | ||
0f161e1f | 2058 | /** |
2059 | * Add last access times to user_lastaccess as required | |
2060 | * @param $userid | |
2061 | * @param $context | |
2062 | * @return boolean - success or failure | |
2063 | */ | |
2064 | function role_add_lastaccess_entries($userid, $context) { | |
2065 | ||
2066 | global $USER, $CFG; | |
2067 | ||
aad2ba95 | 2068 | if (empty($context->contextlevel)) { |
0f161e1f | 2069 | return false; |
2070 | } | |
2071 | ||
2072 | $lastaccess = new object; // Reusable object below | |
2073 | $lastaccess->userid = $userid; | |
2074 | $lastaccess->timeaccess = 0; | |
2075 | ||
aad2ba95 | 2076 | switch ($context->contextlevel) { |
0f161e1f | 2077 | |
2078 | case CONTEXT_SYSTEM: // For the whole site | |
2079 | if ($courses = get_record('course')) { | |
2080 | foreach ($courses as $course) { | |
2081 | $lastaccess->courseid = $course->id; | |
2082 | role_set_lastaccess($lastaccess); | |
2083 | } | |
2084 | } | |
2085 | break; | |
2086 | ||
2087 | case CONTEXT_CATEGORY: // For a whole category | |
2088 | if ($courses = get_record('course', 'category', $context->instanceid)) { | |
2089 | foreach ($courses as $course) { | |
2090 | $lastaccess->courseid = $course->id; | |
2091 | role_set_lastaccess($lastaccess); | |
2092 | } | |
2093 | } | |
2094 | if ($categories = get_record('course_categories', 'parent', $context->instanceid)) { | |
2095 | foreach ($categories as $category) { | |
2096 | $subcontext = get_context_instance(CONTEXT_CATEGORY, $category->id); | |
2097 | role_add_lastaccess_entries($userid, $subcontext); | |
2098 | } | |
2099 | } | |
2100 | break; | |
eef868d1 | 2101 | |
0f161e1f | 2102 | |
2103 | case CONTEXT_COURSE: // For a whole course | |
2104 | if ($course = get_record('course', 'id', $context->instanceid)) { | |
2105 | $lastaccess->courseid = $course->id; | |
2106 | role_set_lastaccess($lastaccess); | |
2107 | } | |
2108 | break; | |
2109 | } | |
2110 | } | |
2111 | ||
2112 | /** | |
2113 | * Delete last access times from user_lastaccess as required | |
2114 | * @param $userid | |
2115 | * @param $context | |
2116 | * @return boolean - success or failure | |
2117 | */ | |
2118 | function role_remove_lastaccess_entries($userid, $context) { | |
2119 | ||
2120 | global $USER, $CFG; | |
2121 | ||
2122 | } | |
2123 | ||
bbbf2d40 | 2124 | |
2125 | /** | |
2126 | * Loads the capability definitions for the component (from file). If no | |
2127 | * capabilities are defined for the component, we simply return an empty array. | |
2128 | * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results' | |
2129 | * @return array of capabilities | |
2130 | */ | |
2131 | function load_capability_def($component) { | |
2132 | global $CFG; | |
2133 | ||
2134 | if ($component == 'moodle') { | |
2135 | $defpath = $CFG->libdir.'/db/access.php'; | |
2136 | $varprefix = 'moodle'; | |
2137 | } else { | |
0c4d9f49 | 2138 | $compparts = explode('/', $component); |
eef868d1 | 2139 | |
0c4d9f49 | 2140 | if ($compparts[0] == 'block') { |
2141 | // Blocks are an exception. Blocks directory is 'blocks', and not | |
2142 | // 'block'. So we need to jump through hoops. | |
2143 | $defpath = $CFG->dirroot.'/'.$compparts[0]. | |
2144 | 's/'.$compparts[1].'/db/access.php'; | |
2145 | $varprefix = $compparts[0].'_'.$compparts[1]; | |
ae628043 | 2146 | } else if ($compparts[0] == 'format') { |
2147 | // Similar to the above, course formats are 'format' while they | |
2148 | // are stored in 'course/format'. | |
2149 | $defpath = $CFG->dirroot.'/course/'.$component.'/db/access.php'; | |
2150 | $varprefix = $compparts[0].'_'.$compparts[1]; | |
0c4d9f49 | 2151 | } else { |
2152 | $defpath = $CFG->dirroot.'/'.$component.'/db/access.php'; | |
2153 | $varprefix = str_replace('/', '_', $component); | |
2154 | } | |
bbbf2d40 | 2155 | } |
2156 | $capabilities = array(); | |
eef868d1 | 2157 | |
bbbf2d40 | 2158 | if (file_exists($defpath)) { |
dc268b2f | 2159 | require($defpath); |
bbbf2d40 | 2160 | $capabilities = ${$varprefix.'_capabilities'}; |
2161 | } | |
2162 | return $capabilities; | |
2163 | } | |
2164 | ||
2165 | ||
2166 | /** | |
2167 | * Gets the capabilities that have been cached in the database for this | |
2168 | * component. | |
2169 | * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results' | |
2170 | * @return array of capabilities | |
2171 | */ | |
2172 | function get_cached_capabilities($component='moodle') { | |
2173 | if ($component == 'moodle') { | |
2174 | $storedcaps = get_records_select('capabilities', | |
2175 | "name LIKE 'moodle/%:%'"); | |
2176 | } else { | |
2177 | $storedcaps = get_records_select('capabilities', | |
2178 | "name LIKE '$component:%'"); | |
2179 | } | |
2180 | return $storedcaps; | |
2181 | } | |
2182 | ||
2183 | ||
2184 | /** | |
2185 | * Updates the capabilities table with the component capability definitions. | |
2186 | * If no parameters are given, the function updates the core moodle | |
2187 | * capabilities. | |
2188 | * | |
2189 | * Note that the absence of the db/access.php capabilities definition file | |
2190 | * will cause any stored capabilities for the component to be removed from | |
eef868d1 | 2191 | * the database. |
bbbf2d40 | 2192 | * |
2193 | * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results' | |
2194 | * @return boolean | |
2195 | */ | |
2196 | function update_capabilities($component='moodle') { | |
eef868d1 | 2197 | |
bbbf2d40 | 2198 | $storedcaps = array(); |
be4486da | 2199 | |
2200 | $filecaps = load_capability_def($component); | |
bbbf2d40 | 2201 | $cachedcaps = get_cached_capabilities($component); |
2202 | if ($cachedcaps) { | |
2203 | foreach ($cachedcaps as $cachedcap) { | |
2204 | array_push($storedcaps, $cachedcap->name); | |
17e5635c | 2205 | // update risk bitmasks in existing capabilities if needed |
be4486da | 2206 | if (array_key_exists($cachedcap->name, $filecaps)) { |
2207 | if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) { | |
2b531945 | 2208 | $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified |
be4486da | 2209 | } |
2210 | if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) { | |
2211 | $updatecap = new object; | |
2212 | $updatecap->id = $cachedcap->id; | |
2213 | $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask']; | |
2214 | if (!update_record('capabilities', $updatecap)) { | |
2215 | return false; | |
2216 | } | |
2217 | } | |
2218 | } | |
bbbf2d40 | 2219 | } |
2220 | } | |
be4486da | 2221 | |
bbbf2d40 | 2222 | // Are there new capabilities in the file definition? |
2223 | $newcaps = array(); | |
eef868d1 | 2224 | |
bbbf2d40 | 2225 | foreach ($filecaps as $filecap => $def) { |
eef868d1 | 2226 | if (!$storedcaps || |
bbbf2d40 | 2227 | ($storedcaps && in_array($filecap, $storedcaps) === false)) { |
2b531945 | 2228 | if (!array_key_exists('riskbitmask', $def)) { |
2229 | $def['riskbitmask'] = 0; // no risk if not specified | |
2230 | } | |
bbbf2d40 | 2231 | $newcaps[$filecap] = $def; |
2232 | } | |
2233 | } | |
2234 | // Add new capabilities to the stored definition. | |
2235 | foreach ($newcaps as $capname => $capdef) { | |
2236 | $capability = new object; | |
2237 | $capability->name = $capname; | |
2238 | $capability->captype = $capdef['captype']; | |
2239 | $capability->contextlevel = $capdef['contextlevel']; | |
2240 | $capability->component = $component; | |
be4486da | 2241 | $capability->riskbitmask = $capdef['riskbitmask']; |
eef868d1 | 2242 | |
bbbf2d40 | 2243 | if (!insert_record('capabilities', $capability, false, 'id')) { |
2244 | return false; | |
2245 | } | |
eef868d1 | 2246 | |
bbbf2d40 | 2247 | // Do we need to assign the new capabilities to roles that have the |
2248 | // legacy capabilities moodle/legacy:* as well? | |
2249 | if (isset($capdef['legacy']) && is_array($capdef['legacy']) && | |
2250 | !assign_legacy_capabilities($capname, $capdef['legacy'])) { | |
2e85fffe | 2251 | notify('Could not assign legacy capabilities for '.$capname); |
bbbf2d40 | 2252 | } |
2253 | } | |
2254 | // Are there any capabilities that have been removed from the file | |
2255 | // definition that we need to delete from the stored capabilities and | |
2256 | // role assignments? | |
2257 | capabilities_cleanup($component, $filecaps); | |
eef868d1 | 2258 | |
bbbf2d40 | 2259 | return true; |
2260 | } | |
2261 | ||
2262 | ||
2263 | /** | |
2264 | * Deletes cached capabilities that are no longer needed by the component. | |
2265 | * Also unassigns these capabilities from any roles that have them. | |
2266 | * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results' | |
2267 | * @param $newcapdef - array of the new capability definitions that will be | |
2268 | * compared with the cached capabilities | |
2269 | * @return int - number of deprecated capabilities that have been removed | |
2270 | */ | |
2271 | function capabilities_cleanup($component, $newcapdef=NULL) { | |
eef868d1 | 2272 | |
bbbf2d40 | 2273 | $removedcount = 0; |
eef868d1 | 2274 | |
bbbf2d40 | 2275 | if ($cachedcaps = get_cached_capabilities($component)) { |
2276 | foreach ($cachedcaps as $cachedcap) { | |
2277 | if (empty($newcapdef) || | |
2278 | array_key_exists($cachedcap->name, $newcapdef) === false) { | |
eef868d1 | 2279 | |
bbbf2d40 | 2280 | // Remove from capabilities cache. |
2281 | if (!delete_records('capabilities', 'name', $cachedcap->name)) { | |
2282 | error('Could not delete deprecated capability '.$cachedcap->name); | |
2283 | } else { | |
2284 | $removedcount++; | |
2285 | } | |
2286 | // Delete from roles. | |
2287 | if($roles = get_roles_with_capability($cachedcap->name)) { | |
2288 | foreach($roles as $role) { | |
46943f7b | 2289 | if (!unassign_capability($cachedcap->name, $role->id)) { |
bbbf2d40 | 2290 | error('Could not unassign deprecated capability '. |
2291 | $cachedcap->name.' from role '.$role->name); | |
2292 | } | |
2293 | } | |
2294 | } | |
2295 | } // End if. | |
2296 | } | |
2297 | } | |
2298 | return $removedcount; | |
2299 | } | |
2300 | ||
2301 | ||
2302 | ||
cee0901c | 2303 | /**************** |
2304 | * UI FUNCTIONS * | |
2305 | ****************/ | |
bbbf2d40 | 2306 | |
2307 | ||
2308 | /** | |
2309 | * prints human readable context identifier. | |
2310 | */ | |
0468976c | 2311 | function print_context_name($context) { |
340ea4e8 | 2312 | |
ec0810ee | 2313 | $name = ''; |
aad2ba95 | 2314 | switch ($context->contextlevel) { |
ec0810ee | 2315 | |
bbbf2d40 | 2316 | case CONTEXT_SYSTEM: // by now it's a definite an inherit |
ec0810ee | 2317 | $name = get_string('site'); |
340ea4e8 | 2318 | break; |
bbbf2d40 | 2319 | |
2320 | case CONTEXT_PERSONAL: | |
ec0810ee | 2321 | $name = get_string('personal'); |
340ea4e8 | 2322 | break; |
2323 | ||
4b10f08b | 2324 | case CONTEXT_USER: |
ec0810ee | 2325 | if ($user = get_record('user', 'id', $context->instanceid)) { |
2326 | $name = get_string('user').': '.fullname($user); | |
2327 | } | |
340ea4e8 | 2328 | break; |
2329 | ||
bbbf2d40 | 2330 | case CONTEXT_COURSECAT: // Coursecat -> coursecat or site |
ec0810ee | 2331 | if ($category = get_record('course_categories', 'id', $context->instanceid)) { |
2332 | $name = get_string('category').': '.$category->name; | |
2333 | } | |
340ea4e8 | 2334 | break; |
bbbf2d40 | 2335 | |
2336 | case CONTEXT_COURSE: // 1 to 1 to course cat | |
ec0810ee | 2337 | if ($course = get_record('course', 'id', $context->instanceid)) { |
2338 | $name = get_string('course').': '.$course->fullname; | |
2339 | } | |
340ea4e8 | 2340 | break; |
bbbf2d40 | 2341 | |
2342 | case CONTEXT_GROUP: // 1 to 1 to course | |
f3f7610c ML |
2343 | if ($name = groups_get_group_name($context->instanceid)) { |
2344 | $name = get_string('group').': '. $name; | |
ec0810ee | 2345 | } |
340ea4e8 | 2346 | break; |
bbbf2d40 | 2347 | |
2348 | case CONTEXT_MODULE: // 1 to 1 to course | |
98882637 | 2349 | if ($cm = get_record('course_modules','id',$context->instanceid)) { |
2350 | if ($module = get_record('modules','id',$cm->module)) { | |
2351 | if ($mod = get_record($module->name, 'id', $cm->instance)) { | |
ec0810ee | 2352 | $name = get_string('activitymodule').': '.$mod->name; |
98882637 | 2353 | } |
ec0810ee | 2354 | } |
2355 | } | |
340ea4e8 | 2356 | break; |
bbbf2d40 | 2357 | |
2358 | case CONTEXT_BLOCK: // 1 to 1 to course | |
98882637 | 2359 | if ($blockinstance = get_record('block_instance','id',$context->instanceid)) { |
2360 | if ($block = get_record('block','id',$blockinstance->blockid)) { | |
91be52d7 | 2361 | global $CFG; |
2362 | require_once("$CFG->dirroot/blocks/moodleblock.class.php"); | |
2363 | require_once("$CFG->dirroot/blocks/$block->name/block_$block->name.php"); | |
2364 | $blockname = "block_$block->name"; | |
2365 | if ($blockobject = new $blockname()) { | |
2366 | $name = $blockobject->title.' ('.get_string('block').')'; | |
2367 | } | |
ec0810ee | 2368 | } |
2369 | } | |
340ea4e8 | 2370 | break; |
bbbf2d40 | 2371 | |
2372 | default: | |
2373 | error ('This is an unknown context!'); | |
340ea4e8 | 2374 | return false; |
2375 | ||
2376 | } | |
340ea4e8 | 2377 | return $name; |
bbbf2d40 | 2378 | } |
2379 | ||
2380 | ||
2381 | /** | |
eef868d1 | 2382 | * Extracts the relevant capabilities given a contextid. |
bbbf2d40 | 2383 | * All case based, example an instance of forum context. |
2384 | * Will fetch all forum related capabilities, while course contexts | |
2385 | * Will fetch all capabilities | |
0468976c | 2386 | * @param object context |
bbbf2d40 | 2387 | * @return array(); |
2388 | * | |
2389 | * capabilities | |
2390 | * `name` varchar(150) NOT NULL, | |
2391 | * `captype` varchar(50) NOT NULL, | |
2392 | * `contextlevel` int(10) NOT NULL, | |
2393 | * `component` varchar(100) NOT NULL, | |
2394 | */ | |
0468976c | 2395 | function fetch_context_capabilities($context) { |
eef868d1 | 2396 | |
98882637 | 2397 | global $CFG; |
bbbf2d40 | 2398 | |
2399 | $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display | |
eef868d1 | 2400 | |
aad2ba95 | 2401 | switch ($context->contextlevel) { |
bbbf2d40 | 2402 | |
98882637 | 2403 | case CONTEXT_SYSTEM: // all |
2404 | $SQL = "select * from {$CFG->prefix}capabilities"; | |
bbbf2d40 | 2405 | break; |
2406 | ||
2407 | case CONTEXT_PERSONAL: | |
0a8a95c9 | 2408 | $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_PERSONAL; |
bbbf2d40 | 2409 | break; |
eef868d1 | 2410 | |
4b10f08b | 2411 | case CONTEXT_USER: |
8020a016 | 2412 | $SQL = "SELECT * |
2413 | FROM {$CFG->prefix}capabilities | |
2414 | WHERE contextlevel = ".CONTEXT_USER; | |
bbbf2d40 | 2415 | break; |
eef868d1 | 2416 | |
bbbf2d40 | 2417 | case CONTEXT_COURSECAT: // all |
98882637 | 2418 | $SQL = "select * from {$CFG->prefix}capabilities"; |
bbbf2d40 | 2419 | break; |
2420 | ||
2421 | case CONTEXT_COURSE: // all | |
98882637 | 2422 | $SQL = "select * from {$CFG->prefix}capabilities"; |
bbbf2d40 | 2423 | break; |
2424 | ||
2425 | case CONTEXT_GROUP: // group caps | |
2426 | break; | |
2427 | ||
2428 | case CONTEXT_MODULE: // mod caps | |
98882637 | 2429 | $cm = get_record('course_modules', 'id', $context->instanceid); |
2430 | $module = get_record('modules', 'id', $cm->module); | |
eef868d1 | 2431 | |
98882637 | 2432 | $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE." |
2433 | and component = 'mod/$module->name'"; | |
bbbf2d40 | 2434 | break; |
2435 | ||
2436 | case CONTEXT_BLOCK: // block caps | |
98882637 | 2437 | $cb = get_record('block_instance', 'id', $context->instanceid); |
2438 | $block = get_record('block', 'id', $cb->blockid); | |
eef868d1 | 2439 | |
98882637 | 2440 | $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK." |
2441 | and component = 'block/$block->name'"; | |
bbbf2d40 | 2442 | break; |
2443 | ||
2444 | default: | |
2445 | return false; | |
2446 | } | |
2447 | ||
16e2e2f3 | 2448 | if (!$records = get_records_sql($SQL.' '.$sort)) { |
2449 | $records = array(); | |
2450 | } | |
ba8d8027 | 2451 | |
2452 | /// the rest of code is a bit hacky, think twice before modifying it :-( | |
69eb59f2 | 2453 | |
2454 | // special sorting of core system capabiltites and enrollments | |
e9c82dca | 2455 | if (in_array($context->contextlevel, array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE))) { |
69eb59f2 | 2456 | $first = array(); |
2457 | foreach ($records as $key=>$record) { | |
2458 | if (preg_match('|^moodle/|', $record->name) and $record->contextlevel == CONTEXT_SYSTEM) { | |
2459 | $first[$key] = $record; | |
2460 | unset($records[$key]); | |
2461 | } else if (count($first)){ | |
2462 | break; | |
2463 | } | |
2464 | } | |
2465 | if (count($first)) { | |
2466 | $records = $first + $records; // merge the two arrays keeping the keys | |
2467 | } | |
ba8d8027 | 2468 | } else { |
2469 | $contextindependentcaps = fetch_context_independent_capabilities(); | |
2470 | $records = array_merge($contextindependentcaps, $records); | |
69eb59f2 | 2471 | } |
ba8d8027 | 2472 | |
bbbf2d40 | 2473 | return $records; |
eef868d1 | 2474 | |
bbbf2d40 | 2475 | } |
2476 | ||
2477 | ||
759ac72d | 2478 | /** |
2479 | * Gets the context-independent capabilities that should be overrridable in | |
2480 | * any context. | |
2481 | * @return array of capability records from the capabilities table. | |
2482 | */ | |
2483 | function fetch_context_independent_capabilities() { | |
eef868d1 | 2484 | |
17e5635c | 2485 | //only CONTEXT_SYSTEM capabilities here or it will break the hack in fetch_context_capabilities() |
759ac72d | 2486 | $contextindependentcaps = array( |
2487 | 'moodle/site:accessallgroups' | |
2488 | ); | |
2489 | ||
2490 | $records = array(); | |
eef868d1 | 2491 | |
759ac72d | 2492 | foreach ($contextindependentcaps as $capname) { |
2493 | $record = get_record('capabilities', 'name', $capname); | |
2494 | array_push($records, $record); | |
2495 | } | |
2496 | return $records; | |
2497 | } | |
2498 | ||
2499 | ||
bbbf2d40 | 2500 | /** |
2501 | * This function pulls out all the resolved capabilities (overrides and | |
759ac72d | 2502 | * defaults) of a role used in capability overrides in contexts at a given |
bbbf2d40 | 2503 | * context. |
0a8a95c9 | 2504 | * @param obj $context |
bbbf2d40 | 2505 | * @param int $roleid |
dc558690 | 2506 | * @param bool self - if set to true, resolve till this level, else stop at immediate parent level |
bbbf2d40 | 2507 | * @return array |
2508 | */ | |
1648afb2 | 2509 | function role_context_capabilities($roleid, $context, $cap='') { |
dc558690 | 2510 | global $CFG; |
eef868d1 | 2511 | |
8521d83a | 2512 | $contexts = get_parent_contexts($context); |
2513 | $contexts[] = $context->id; | |
98882637 | 2514 | $contexts = '('.implode(',', $contexts).')'; |
eef868d1 | 2515 | |
1648afb2 | 2516 | if ($cap) { |
e4697bf7 | 2517 | $search = " AND rc.capability = '$cap' "; |
1648afb2 | 2518 | } else { |
eef868d1 | 2519 | $search = ''; |
1648afb2 | 2520 | } |
eef868d1 | 2521 | |
2522 | $SQL = "SELECT rc.* | |
2523 | FROM {$CFG->prefix}role_capabilities rc, | |
dc558690 | 2524 | {$CFG->prefix}context c |
2525 | WHERE rc.contextid in $contexts | |
2526 | AND rc.roleid = $roleid | |
2527 | AND rc.contextid = c.id $search | |
aad2ba95 | 2528 | ORDER BY c.contextlevel DESC, |
eef868d1 | 2529 | rc.capability DESC"; |
759ac72d | 2530 | |
98882637 | 2531 | $capabilities = array(); |
eef868d1 | 2532 | |
4729012f | 2533 | if ($records = get_records_sql($SQL)) { |
2534 | // We are traversing via reverse order. | |
2535 | foreach ($records as $record) { | |
2536 | // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit | |
2537 | if (!isset($capabilities[$record->capability]) || $record->permission<-500) { | |
2538 | $capabilities[$record->capability] = $record->permission; | |
eef868d1 | 2539 | } |
4729012f | 2540 | } |
98882637 | 2541 | } |
2542 | return $capabilities; | |
bbbf2d40 | 2543 | } |
2544 | ||
bbbf2d40 | 2545 | /** |
eef868d1 | 2546 | * Recursive function which, given a context, find all parent context ids, |
bbbf2d40 | 2547 | * and return the array in reverse order, i.e. parent first, then grand |
2548 | * parent, etc. | |
2549 | * @param object $context | |
2550 | * @return array() | |
2551 | */ | |
bbbf2d40 | 2552 | function get_parent_contexts($context) { |
759ac72d | 2553 | |
1cd03601 | 2554 | static $pcontexts; // cache |
2555 | if (isset($pcontexts[$context->id])) { | |
2556 | return ($pcontexts[$context->id]); | |
2557 | } | |
2558 | ||
aad2ba95 | 2559 | switch ($context->contextlevel) { |
bbbf2d40 | 2560 | |
2561 | case CONTEXT_SYSTEM: // no parent | |
957861f7 | 2562 | return array(); |
bbbf2d40 | 2563 | break; |
2564 | ||
2565 | case CONTEXT_PERSONAL: | |
21c9bace | 2566 | if (!$parent = get_context_instance(CONTEXT_SYSTEM)) { |
957861f7 | 2567 | return array(); |
2568 | } else { | |
1cd03601 | 2569 | $res = array($parent->id); |
0de75c4c | 2570 | $pcontexts[$context->id] = $res; |
1cd03601 | 2571 | return $res; |
957861f7 | 2572 | } |
bbbf2d40 | 2573 | break; |
eef868d1 | 2574 | |
4b10f08b | 2575 | case CONTEXT_USER: |
21c9bace | 2576 | if (!$parent = get_context_instance(CONTEXT_SYSTEM)) { |
957861f7 | 2577 | return array(); |
2578 | } else { | |
1cd03601 | 2579 | $res = array($parent->id); |
0de75c4c | 2580 | $pcontexts[$context->id] = $res; |
1cd03601 | 2581 | return $res; |
957861f7 | 2582 | } |
bbbf2d40 | 2583 | break; |
eef868d1 | 2584 | |
bbbf2d40 | 2585 | case CONTEXT_COURSECAT: // Coursecat -> coursecat or site |
957861f7 | 2586 | if (!$coursecat = get_record('course_categories','id',$context->instanceid)) { |
2587 | return array(); | |
2588 | } | |
c5ddc3fd | 2589 | if (!empty($coursecat->parent)) { // return parent value if exist |
bbbf2d40 | 2590 | $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent); |
1cd03601 | 2591 | $res = array_merge(array($parent->id), get_parent_contexts($parent)); |
2592 | $pcontexts[$context->id] = $res; | |
2593 | return $res; | |
bbbf2d40 | 2594 | } else { // else return site value |
21c9bace | 2595 | $parent = get_context_instance(CONTEXT_SYSTEM); |
1cd03601 | 2596 | $res = array($parent->id); |
2597 | $pcontexts[$context->id] = $res; | |
2598 | return $res; | |
bbbf2d40 | 2599 | } |
2600 | break; | |
2601 | ||
2602 | case CONTEXT_COURSE: // 1 to 1 to course cat | |
957861f7 | 2603 | if (!$course = get_record('course','id',$context->instanceid)) { |
2604 | return array(); | |
2605 | } | |
1936c10e | 2606 | if ($course->id != SITEID) { |
957861f7 | 2607 | $parent = get_context_instance(CONTEXT_COURSECAT, $course->category); |
1cd03601 | 2608 | $res = array_merge(array($parent->id), get_parent_contexts($parent)); |
2609 | return $res; | |
957861f7 | 2610 | } else { |
40a2a15f | 2611 | // Yu: Separating site and site course context |
2612 | $parent = get_context_instance(CONTEXT_SYSTEM); | |
1cd03601 | 2613 | $res = array($parent->id); |
2614 | $pcontexts[$context->id] = $res; | |
2615 | return $res; | |
957861f7 | 2616 | } |
bbbf2d40 | 2617 | break; |
2618 | ||
2619 | case CONTEXT_GROUP: // 1 to 1 to course | |
f3f7610c | 2620 | if (! $group = groups_get_group($context->instanceid)) { |
957861f7 | 2621 | return array(); |
2622 | } | |
2623 | if ($parent = get_context_instance(CONTEXT_COURSE, $group->courseid)) { | |
1cd03601 | 2624 | $res = array_merge(array($parent->id), get_parent_contexts($parent)); |
2625 | $pcontexts[$context->id] = $res; | |
2626 | return $res; | |
957861f7 | 2627 | } else { |
2628 | return array(); | |
2629 | } | |
bbbf2d40 | 2630 | break; |
2631 | ||
2632 | case CONTEXT_MODULE: // 1 to 1 to course | |
957861f7 | 2633 | if (!$cm = get_record('course_modules','id',$context->instanceid)) { |
2634 | return array(); | |
2635 | } | |
2636 | if ($parent = get_context_instance(CONTEXT_COURSE, $cm->course)) { | |
1cd03601 | 2637 | $res = array_merge(array($parent->id), get_parent_contexts($parent)); |
2638 | $pcontexts[$context->id] = $res; | |
2639 | return $res; | |
957861f7 | 2640 | } else { |
2641 | return array(); | |
2642 | } | |
bbbf2d40 | 2643 | break; |
2644 | ||
2645 | case CONTEXT_BLOCK: // 1 to 1 to course | |
957861f7 | 2646 | if (!$block = get_record('block_instance','id',$context->instanceid)) { |
2647 | return array(); | |
2648 | } | |
2649 | if ($parent = get_context_instance(CONTEXT_COURSE, $block->pageid)) { | |
1cd03601 | 2650 | $res = array_merge(array($parent->id), get_parent_contexts($parent)); |
2651 | $pcontexts[$context->id] = $res; | |
2652 | return $res; | |
957861f7 | 2653 | } else { |
2654 | return array(); | |
2655 | } | |
bbbf2d40 | 2656 | break; |
2657 | ||
2658 | default: | |
957861f7 | 2659 | error('This is an unknown context!'); |
bbbf2d40 | 2660 | return false; |
2661 | } | |
bbbf2d40 | 2662 | } |
2663 | ||
759ac72d | 2664 | |
2665 | /** | |
2666 | * Gets a string for sql calls, searching for stuff in this context or above | |
ea8158c1 | 2667 | * @param object $context |
2668 | * @return string | |
2669 | */ | |
2670 | function get_related_contexts_string($context) { | |
2671 | if ($parents = get_parent_contexts($context)) { | |
eef868d1 | 2672 | return (' IN ('.$context->id.','.implode(',', $parents).')'); |
ea8158c1 | 2673 | } else { |
2674 | return (' ='.$context->id); | |
2675 | } | |
2676 | } | |
759ac72d | 2677 | |
2678 | ||
bbbf2d40 | 2679 | /** |
2680 | * This function gets the capability of a role in a given context. | |
2681 | * It is needed when printing override forms. | |
2682 | * @param int $contextid | |
bbbf2d40 | 2683 | * @param string $capability |
2684 | * @param array $capabilities - array loaded using role_context_capabilities | |
2685 | * @return int (allow, prevent, prohibit, inherit) | |
2686 | */ | |
bbbf2d40 | 2687 | function get_role_context_capability($contextid, $capability, $capabilities) { |
759ac72d | 2688 | if (isset($capabilities[$contextid][$capability])) { |
2689 | return $capabilities[$contextid][$capability]; | |
2690 | } | |
2691 | else { | |
2692 | return false; | |
2693 | } | |
bbbf2d40 | 2694 | } |
2695 | ||
2696 | ||
cee0901c | 2697 | /** |
2698 | * Returns the human-readable, translated version of the capability. | |
2699 | * Basically a big switch statement. | |
2700 | * @param $capabilityname - e.g. mod/choice:readresponses | |
2701 | */ | |
ceb83c70 | 2702 | function get_capability_string($capabilityname) { |
eef868d1 | 2703 | |
cee0901c | 2704 | // Typical capabilityname is mod/choice:readresponses |
ceb83c70 | 2705 | |
2706 | $names = split('/', $capabilityname); | |
2707 | $stringname = $names[1]; // choice:readresponses | |
eef868d1 | 2708 | $components = split(':', $stringname); |
ceb83c70 | 2709 | $componentname = $components[0]; // choice |
98882637 | 2710 | |
2711 | switch ($names[0]) { | |
2712 | case 'mod': | |
ceb83c70 | 2713 | $string = get_string($stringname, $componentname); |
98882637 | 2714 | break; |
eef868d1 | 2715 | |
98882637 | 2716 | case 'block': |
ceb83c70 | 2717 | $string = get_string($stringname, 'block_'.$componentname); |
98882637 | 2718 | break; |
ceb83c70 | 2719 | |
98882637 | 2720 | case 'moodle': |
ceb83c70 | 2721 | $string = get_string($stringname, 'role'); |
98882637 | 2722 | break; |
eef868d1 | 2723 | |
98882637 | 2724 | case 'enrol': |
ceb83c70 | 2725 | $string = get_string($stringname, 'enrol_'.$componentname); |
eef868d1 | 2726 | break; |
ae628043 | 2727 | |
2728 | case 'format': | |
2729 | $string = get_string($stringname, 'format_'.$componentname); | |
2730 | break; | |
eef868d1 | 2731 | |
98882637 | 2732 | default: |
ceb83c70 | 2733 | $string = get_string($stringname); |
eef868d1 | 2734 | break; |
2735 | ||
98882637 | 2736 | } |
ceb83c70 | 2737 | return $string; |
bbbf2d40 | 2738 | } |
2739 | ||
2740 | ||
cee0901c | 2741 | /** |
2742 | * This gets the mod/block/course/core etc strings. | |
2743 | * @param $component | |
2744 | * @param $contextlevel | |
2745 | */ | |
bbbf2d40 | 2746 | function get_component_string($component, $contextlevel) { |
2747 | ||
98882637 | 2748 | switch ($contextlevel) { |
bbbf2d40 | 2749 | |
98882637 | 2750 | case CONTEXT_SYSTEM: |
be382aaf | 2751 | if (preg_match('|^enrol/|', $component)) { |
2752 | $langname = str_replace('/', '_', $component); | |
2753 | $string = get_string('enrolname', $langname); | |
f3652521 | 2754 | } else if (preg_match('|^block/|', $component)) { |
2755 | $langname = str_replace('/', '_', $component); | |
2756 | $string = get_string('blockname', $langname); | |
69eb59f2 | 2757 | } else { |
2758 | $string = get_string('coresystem'); | |
2759 | } | |
bbbf2d40 | 2760 | break; |
2761 | ||
2762 | case CONTEXT_PERSONAL: | |
98882637 | 2763 | $string = get_string('personal'); |
bbbf2d40 | 2764 | break; |
2765 | ||
4b10f08b | 2766 | case CONTEXT_USER: |
98882637 | 2767 | $string = get_string('users'); |
bbbf2d40 | 2768 | break; |
2769 | ||
2770 | case CONTEXT_COURSECAT: | |
98882637 | 2771 | $string = get_string('categories'); |
bbbf2d40 | 2772 | break; |
2773 | ||
2774 | case CONTEXT_COURSE: | |
98882637 | 2775 | $string = get_string('course'); |
bbbf2d40 | 2776 | break; |
2777 | ||
2778 | case CONTEXT_GROUP: | |
98882637 | 2779 | $string = get_string('group'); |
bbbf2d40 | 2780 | break; |
2781 | ||
2782 | case CONTEXT_MODULE: | |
98882637 | 2783 | $string = get_string('modulename', basename($component)); |
bbbf2d40 | 2784 | break; |
2785 | ||
2786 | case CONTEXT_BLOCK: | |
98882637 | 2787 | $string = get_string('blockname', 'block_'.$component.'.php'); |
bbbf2d40 | 2788 | break; |
2789 | ||
2790 | default: | |
2791 | error ('This is an unknown context!'); | |
2792 | return false; | |
eef868d1 | 2793 | |
98882637 | 2794 | } |
98882637 | 2795 | return $string; |
bbbf2d40 | 2796 | } |
cee0901c | 2797 | |
759ac72d | 2798 | /** |
2799 | * Gets the list of roles assigned to this context and up (parents) | |
945f88ca | 2800 | * @param object $context |
3997cb40 | 2801 | * @param view - set to true when roles are pulled for display only |
2802 | * this is so that we can filter roles with no visible | |
2803 | * assignment, for example, you might want to "hide" all | |
2804 | * course creators when browsing the course participants | |
2805 | * list. | |
945f88ca | 2806 | * @return array |
2807 | */ | |
3997cb40 | 2808 | function get_roles_used_in_context($context, $view = false) { |
e4dd3222 | 2809 | |
2810 | global $CFG; | |
3997cb40 | 2811 | |
2812 | // filter for roles with all hidden assignments | |
2813 | // no need to return when only pulling roles for reviewing | |
2814 | // e.g. participants page. | |
d42c64ba | 2815 | $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':''; |
2d9965e1 | 2816 | $contextlist = get_related_contexts_string($context); |
eef868d1 | 2817 | |
759ac72d | 2818 | $sql = "SELECT DISTINCT r.id, |
2819 | r.name, | |
2820 | r.shortname, | |
2821 | r.sortorder | |
2822 | FROM {$CFG->prefix}role_assignments ra, | |
eef868d1 | 2823 | {$CFG->prefix}role r |
2824 | WHERE r.id = ra.roleid | |
759ac72d | 2825 | AND ra.contextid $contextlist |
3997cb40 | 2826 | $hiddensql |
759ac72d | 2827 | ORDER BY r.sortorder ASC"; |
eef868d1 | 2828 | |
759ac72d | 2829 | return get_records_sql($sql); |
e4dd3222 | 2830 | } |
2831 | ||
eef868d1 | 2832 | /** this function is used to print roles column in user profile page. |
945f88ca | 2833 | * @param int userid |
2834 | * @param int contextid | |
2835 | * @return string | |
2836 | */ | |
0a8a95c9 | 2837 | function get_user_roles_in_context($userid, $contextid){ |
2838 | global $CFG; | |
eef868d1 | 2839 | |
0a8a95c9 | 2840 | $rolestring = ''; |
2841 | $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'; | |
2842 | if ($roles = get_records_sql($SQL)) { | |
2843 | foreach ($roles as $userrole) { | |
2844 | $rolestring .= '<a href="'.$CFG->wwwroot.'/user/index.php?contextid='.$userrole->contextid.'&roleid='.$userrole->roleid.'">'.$userrole->name.'</a>, '; | |
eef868d1 | 2845 | } |
2846 | ||
0a8a95c9 | 2847 | } |
2848 | return rtrim($rolestring, ', '); | |
2849 | } | |
68c52526 | 2850 | |
2851 | ||
945f88ca | 2852 | /** |
2853 | * Checks if a user can override capabilities of a particular role in this context | |
2854 | * @param object $context | |
2855 | * @param int targetroleid - the id of the role you want to override | |
2856 | * @return boolean | |
2857 | */ | |
68c52526 | 2858 | function user_can_override($context, $targetroleid) { |
2859 | // first check if user has override capability | |
2860 | // if not return false; | |
2861 | if (!has_capability('moodle/role:override', $context)) { | |
eef868d1 | 2862 | return false; |
68c52526 | 2863 | } |
2864 | // pull out all active roles of this user from this context(or above) | |
c0614051 | 2865 | if ($userroles = get_user_roles($context)) { |
2866 | foreach ($userroles as $userrole) { | |
2867 | // if any in the role_allow_override table, then it's ok | |
2868 | if (get_record('role_allow_override', 'roleid', $userrole->roleid, 'allowoverride', $targetroleid)) { | |
2869 | return true; | |
2870 | } | |
68c52526 | 2871 | } |
2872 | } | |
eef868d1 | 2873 | |
68c52526 | 2874 | return false; |
eef868d1 | 2875 | |
68c52526 | 2876 | } |
2877 | ||
945f88ca | 2878 | /** |
2879 | * Checks if a user can assign users to a particular role in this context | |
2880 | * @param object $context | |
2881 | * @param int targetroleid - the id of the role you want to assign users to | |
2882 | * @return boolean | |
2883 | */ | |
68c52526 | 2884 | function user_can_assign($context, $targetroleid) { |
eef868d1 | 2885 | |
68c52526 | 2886 | // first check if user has override capability |
2887 | // if not return false; | |
2888 | if (!has_capability('moodle/role:assign', $context)) { | |
eef868d1 | 2889 | return false; |
68c52526 | 2890 | } |
2891 | // pull out all active roles of this user from this context(or above) | |
c0614051 | 2892 | if ($userroles = get_user_roles($context)) { |
2893 | foreach ($userroles as $userrole) { | |
2894 | // if any in the role_allow_override table, then it's ok | |
2895 | if (get_record('role_allow_assign', 'roleid', $userrole->roleid, 'allowassign', $targetroleid)) { | |
2896 | return true; | |
2897 | } | |
68c52526 | 2898 | } |
2899 | } | |
eef868d1 | 2900 | |
2901 | return false; | |
68c52526 | 2902 | } |
2903 | ||
ece4945b | 2904 | /** Returns all site roles in correct sort order. |
2905 | * | |
2906 | */ | |
2907 | function get_all_roles() { | |
2908 | return get_records('role', '', '', 'sortorder ASC'); | |
2909 | } | |
2910 | ||
945f88ca | 2911 | /** |
2912 | * gets all the user roles assigned in this context, or higher contexts | |
2913 | * this is mainly used when checking if a user can assign a role, or overriding a role | |
2914 | * i.e. we need to know what this user holds, in order to verify against allow_assign and | |
2915 | * allow_override tables | |
2916 | * @param object $context | |
2917 | * @param int $userid | |
b06334e8 | 2918 | * @param view - set to true when roles are pulled for display only |
2919 | * this is so that we can filter roles with no visible | |
2920 | * assignment, for example, you might want to "hide" all | |
2921 | * course creators when browsing the course participants | |
2922 | * list. | |
945f88ca | 2923 | * @return array |
2924 | */ | |
b06334e8 | 2925 | function get_user_roles($context, $userid=0, $checkparentcontexts=true, $order='c.contextlevel DESC, r.sortorder ASC', $view=false) { |
68c52526 | 2926 | |
2927 | global $USER, $CFG, $db; | |
c0614051 | 2928 | |
2929 | if (empty($userid)) { | |
2930 | if (empty($USER->id)) { | |
2931 | return array(); | |
2932 | } | |
2933 | $userid = $USER->id; | |
2934 | } | |
b06334e8 | 2935 | // set up hidden sql |
d42c64ba | 2936 | $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':''; |
c0614051 | 2937 | |
5b630667 | 2938 | if ($checkparentcontexts && ($parents = get_parent_contexts($context))) { |
2939 | $contexts = ' ra.contextid IN ('.implode(',' , $parents).','.$context->id.')'; | |
c0614051 | 2940 | } else { |
5b630667 | 2941 | $contexts = ' ra.contextid = \''.$context->id.'\''; |
c0614051 | 2942 | } |
2943 | ||
31f26796 | 2944 | return get_records_sql('SELECT ra.*, r.name, r.shortname |
5b630667 | 2945 | FROM '.$CFG->prefix.'role_assignments ra, |
ec6eb110 | 2946 | '.$CFG->prefix.'role r, |
2947 | '.$CFG->prefix.'context c | |
c0614051 | 2948 | WHERE ra.userid = '.$userid. |
5b630667 | 2949 | ' AND ra.roleid = r.id |
ec6eb110 | 2950 | AND ra.contextid = c.id |
b06334e8 | 2951 | AND '.$contexts . $hiddensql . |
ae9761ec | 2952 | ' ORDER BY '.$order); |
68c52526 | 2953 | } |
2954 | ||
945f88ca | 2955 | /** |
eef868d1 | 2956 | * Creates a record in the allow_override table |
945f88ca | 2957 | * @param int sroleid - source roleid |
2958 | * @param int troleid - target roleid | |
2959 | * @return int - id or false | |
2960 | */ | |
2961 | function allow_override($sroleid, $troleid) { | |
ece4945b | 2962 | $record = new object(); |
945f88ca | 2963 | $record->roleid = $sroleid; |
2964 | $record->allowoverride = $troleid; | |
2965 | return insert_record('role_allow_override', $record); | |
2966 | } | |
2967 | ||
2968 | /** | |
eef868d1 | 2969 | * Creates a record in the allow_assign table |
945f88ca | 2970 | * @param int sroleid - source roleid |
2971 | * @param int troleid - target roleid | |
2972 | * @return int - id or false | |
2973 | */ | |
2974 | function allow_assign($sroleid, $troleid) { | |
ff64aaea | 2975 | $record = new object; |
945f88ca | 2976 | $record->roleid = $sroleid; |
2977 | $record->allowassign = $troleid; | |
2978 | return insert_record('role_allow_assign', $record); | |
2979 | } | |
2980 | ||
2981 | /** | |
ff64aaea | 2982 | * Gets a list of roles that this user can assign in this context |
945f88ca | 2983 | * @param object $context |
2984 | * @return array | |
2985 | */ | |
5e67946d | 2986 | function get_assignable_roles ($context, $field="name") { |
945f88ca | 2987 | |
945f88ca | 2988 | $options = array(); |
ff64aaea | 2989 | |
ece4945b | 2990 | if ($roles = get_all_roles()) { |
ff64aaea | 2991 | foreach ($roles as $role) { |
2992 | if (user_can_assign($context, $role->id)) { | |
5e67946d | 2993 | $options[$role->id] = strip_tags(format_string($role->{$field}, true)); |
ff64aaea | 2994 | } |
945f88ca | 2995 | } |
2996 | } | |
2997 | return $options; | |
2998 | } | |
2999 | ||
3000 | /** | |
ff64aaea | 3001 | * Gets a list of roles that this user can override in this context |
945f88ca | 3002 | * @param object $context |
3003 | * @return array | |
3004 | */ | |
3005 | function get_overridable_roles ($context) { | |
3006 | ||
945f88ca | 3007 | $options = array(); |
ff64aaea | 3008 | |
ece4945b | 3009 | if ($roles = get_all_roles()) { |
ff64aaea | 3010 | foreach ($roles as $role) { |
3011 | if (user_can_override($context, $role->id)) { | |
65b0c132 | 3012 | $options[$role->id] = strip_tags(format_string($role->name, true)); |
ff64aaea | 3013 | } |
945f88ca | 3014 | } |
ff64aaea | 3015 | } |
eef868d1 | 3016 | |
3017 | return $options; | |
945f88ca | 3018 | } |
1648afb2 | 3019 | |
b963384f | 3020 | /* |
3021 | * Returns a role object that is the default role for new enrolments | |
3022 | * in a given course | |
3023 | * | |
eef868d1 | 3024 | * @param object $course |
b963384f | 3025 | * @return object $role |
3026 | */ | |
3027 | function get_default_course_role($course) { | |
3028 | global $CFG; | |
3029 | ||
3030 | /// First let's take the default role the course may have | |
3031 | if (!empty($course->defaultrole)) { | |
3032 | if ($role = get_record('role', 'id', $course->defaultrole)) { | |
3033 | return $role; | |
3034 | } | |
3035 | } | |
3036 | ||
3037 | /// Otherwise the site setting should tell us | |
3038 | if ($CFG->defaultcourseroleid) { | |
3039 | if ($role = get_record('role', 'id', $CFG->defaultcourseroleid)) { | |
3040 | return $role; | |
3041 | } | |
3042 | } | |
3043 | ||
3044 | /// It's unlikely we'll get here, but just in case, try and find a student role | |
3045 | if ($studentroles = get_roles_with_capability('moodle/legacy:student', CAP_ALLOW)) { | |
3046 | return array_shift($studentroles); /// Take the first one | |
3047 | } | |
3048 | ||
3049 | return NULL; | |
3050 | } | |
3051 | ||
1648afb2 | 3052 | |
3053 | /** | |
3054 | * who has this capability in this context | |
3055 | * does not handling user level resolving!!! | |
40a2a15f | 3056 | * (!)pleaes note if $fields is empty this function attempts to get u.* |
3057 | * which can get rather large. | |
1648afb2 | 3058 | * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly |
3059 | * @param $context - object | |
3060 | * @param $capability - string capability | |
3061 | * @param $fields - fields to be pulled | |
3062 | * @param $sort - the sort order | |
04417640 | 3063 | * @param $limitfrom - number of records to skip (offset) |
eef868d1 | 3064 | * @param $limitnum - number of records to fetch |
1c45e42e | 3065 | * @param $groups - single group or array of groups - group(s) user is in |
71dea306 | 3066 | * @param $exceptions - list of users to exclude |
b06334e8 | 3067 | * @param view - set to true when roles are pulled for display only |
3068 | * this is so that we can filter roles with no visible | |
3069 | * assignment, for example, you might want to "hide" all | |
3070 | * course creators when browsing the course participants | |
3071 | * list. | |
1648afb2 | 3072 | */ |
eef868d1 | 3073 | function get_users_by_capability($context, $capability, $fields='', $sort='', |
b06334e8 | 3074 | $limitfrom='', $limitnum='', $groups='', $exceptions='', $doanything=true, $view=false) { |
1648afb2 | 3075 | global $CFG; |
eef868d1 | 3076 | |
64026e8c | 3077 | /// Sorting out groups |
1c45e42e | 3078 | if ($groups) { |
71dea306 | 3079 | $groupjoin = 'INNER JOIN '.$CFG->prefix.'groups_members gm ON gm.userid = ra.userid'; |
eef868d1 | 3080 | |
1c45e42e | 3081 | if (is_array($groups)) { |
a05708ad | 3082 | $groupsql = 'AND gm.groupid IN ('.implode(',', $groups).')'; |
1c45e42e | 3083 | } else { |
eef868d1 | 3084 | $groupsql = 'AND gm.groupid = '.$groups; |
1c45e42e | 3085 | } |
3086 | } else { | |
3087 | $groupjoin = ''; | |
eef868d1 | 3088 | $groupsql = ''; |
1c45e42e | 3089 | } |
eef868d1 | 3090 | |
64026e8c | 3091 | /// Sorting out exceptions |
5081e786 | 3092 | $exceptionsql = $exceptions ? "AND u.id NOT IN ($exceptions)" : ''; |
64026e8c | 3093 | |
3094 | /// Set up default fields | |
3095 | if (empty($fields)) { | |
5b630667 | 3096 | $fields = 'u.*, ul.timeaccess as lastaccess, ra.hidden'; |
64026e8c | 3097 | } |
3098 | ||
3099 | /// Set up default sort | |
3100 | if (empty($sort)) { | |
3101 | $sort = 'ul.timeaccess'; | |
3102 | } | |
3103 | ||
eef868d1 | 3104 | $sortby = $sort ? " ORDER BY $sort " : ''; |
b06334e8 | 3105 | /// Set up hidden sql |
d42c64ba | 3106 | $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':''; |
eef868d1 | 3107 | |
64026e8c | 3108 | /// If context is a course, then construct sql for ul |
aad2ba95 | 3109 | if ($context->contextlevel == CONTEXT_COURSE) { |
71dea306 | 3110 | $courseid = $context->instanceid; |
ae9761ec | 3111 | $coursesql1 = "AND ul.courseid = $courseid"; |
5081e786 | 3112 | } else { |
ae9761ec | 3113 | $coursesql1 = ''; |
71dea306 | 3114 | } |
64026e8c | 3115 | |
3116 | /// Sorting out roles with this capability set | |
9d829c68 | 3117 | if ($possibleroles = get_roles_with_capability($capability, CAP_ALLOW, $context)) { |
1d546bb1 | 3118 | if (!$doanything) { |
21c9bace | 3119 | if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { |
1d546bb1 | 3120 | return false; // Something is seriously wrong |
3121 | } | |
3122 | $doanythingroles = get_roles_with_capability('moodle/site:doanything', CAP_ALLOW, $sitecontext); | |
e836a7dd | 3123 | } |
e836a7dd | 3124 | |
9d829c68 | 3125 | $validroleids = array(); |
e836a7dd | 3126 | foreach ($possibleroles as $possiblerole) { |
1d546bb1 | 3127 | if (!$doanything) { |
3128 | if (isset($doanythingroles[$possiblerole->id])) { // We don't want these included | |
3129 | continue; | |
3130 | } | |
e836a7dd | 3131 | } |
bccdf227 | 3132 | if ($caps = role_context_capabilities($possiblerole->id, $context, $capability)) { // resolved list |
3133 | if (isset($caps[$capability]) && $caps[$capability] > 0) { // resolved capability > 0 | |
3134 | $validroleids[] = $possiblerole->id; | |
3135 | } | |
9d829c68 | 3136 | } |
1648afb2 | 3137 | } |
1d546bb1 | 3138 | if (empty($validroleids)) { |
3139 | return false; | |
3140 | } | |
9d829c68 | 3141 | $roleids = '('.implode(',', $validroleids).')'; |
3142 | } else { | |
3143 | return false; // No need to continue, since no roles have this capability set | |
eef868d1 | 3144 | } |
64026e8c | 3145 | |
3146 | /// Construct the main SQL | |
71dea306 | 3147 | $select = " SELECT $fields"; |
eef868d1 | 3148 | $from = " FROM {$CFG->prefix}user u |
3149 | INNER JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id | |
0a3e9703 | 3150 | INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid |
ae9761ec | 3151 | LEFT OUTER JOIN {$CFG->prefix}user_lastaccess ul ON (ul.userid = u.id $coursesql1) |
71dea306 | 3152 | $groupjoin"; |
eef868d1 | 3153 | $where = " WHERE ra.contextid ".get_related_contexts_string($context)." |
3154 | AND u.deleted = 0 | |
3155 | AND ra.roleid in $roleids | |
71dea306 | 3156 | $exceptionsql |
b06334e8 | 3157 | $groupsql |
3158 | $hiddensql"; | |
3159 | ||
eef868d1 | 3160 | return get_records_sql($select.$from.$where.$sortby, $limitfrom, $limitnum); |
1648afb2 | 3161 | } |
7700027f | 3162 | |
ab5c9044 | 3163 | /** |
3164 | * gets all the users assigned this role in this context or higher | |
3165 | * @param int roleid | |
3166 | * @param int contextid | |
3167 | * @param bool parent if true, get list of users assigned in higher context too | |
3168 | * @return array() | |
3169 | */ | |
d42c64ba | 3170 | function get_role_users($roleid, $context, $parent=false, $fields='', $sort='u.lastname ASC', $view=false) { |
ab5c9044 | 3171 | global $CFG; |
eef868d1 | 3172 | |
2851ba9b | 3173 | if (empty($fields)) { |
3174 | $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '. | |
3175 | 'u.maildisplay, u.mailformat, u.maildigest, u.email, u.city, '. | |
3176 | 'u.country, u.picture, u.idnumber, u.department, u.institution, '. | |
3177 | 'u.emailstop, u.lang, u.timezone'; | |
3178 | } | |
3179 | ||
d42c64ba | 3180 | // whether this assignment is hidden |
3181 | $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND r.hidden = 0 ':''; | |
ab5c9044 | 3182 | if ($parent) { |
3183 | if ($contexts = get_parent_contexts($context)) { | |
7ea02fe9 | 3184 | $parentcontexts = ' OR r.contextid IN ('.implode(',', $contexts).')'; |
ab5c9044 | 3185 | } else { |
eef868d1 | 3186 | $parentcontexts = ''; |
ab5c9044 | 3187 | } |
3188 | } else { | |
eef868d1 | 3189 | $parentcontexts = ''; |
3190 | } | |
3191 | ||
b16de849 | 3192 | if ($roleid) { |
3193 | $roleselect = "AND r.roleid = $roleid"; | |
3194 | } else { | |
3195 | $roleselect = ''; | |
3196 | } | |
3197 | ||
7ea02fe9 | 3198 | $SQL = "SELECT $fields |
3199 | FROM {$CFG->prefix}role_assignments r, | |
eef868d1 | 3200 | {$CFG->prefix}user u |
7ea02fe9 | 3201 | WHERE (r.contextid = $context->id $parentcontexts) |