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