Tabs and some random formatting fixes
[moodle.git] / lib / accesslib.php
CommitLineData
bbbf2d40 1<?php
2 /* capability session information format
3 * 2 x 2 array
4 * [context][capability]
5 * where context is the context id of the table 'context'
6 * and capability is a string defining the capability
7 * e.g.
8 *
9 * [Capabilities] => [26][mod/forum:viewpost] = 1
10 * [26][mod/forum:startdiscussion] = -8990
11 * [26][mod/forum:editallpost] = -1
12 * [273][moodle:blahblah] = 1
13 * [273][moodle:blahblahblah] = 2
14 */
15
16
17// permission definitions
18define('CAP_ALLOW', 1);
19define('CAP_PREVENT', -1);
20define('CAP_PROHIBIT', -1000);
21
22
23// context definitions
24define('CONTEXT_SYSTEM', 10);
25define('CONTEXT_PERSONAL', 20);
26define('CONTEXT_USERID', 30);
27define('CONTEXT_COURSECAT', 40);
28define('CONTEXT_COURSE', 50);
29define('CONTEXT_GROUP', 60);
30define('CONTEXT_MODULE', 70);
31define('CONTEXT_BLOCK', 80);
32
33
34/**
35 * This functions get all the course categories in proper order
36 * @param int $contextid
37 * @param int $type
38 * @return array of contextids
39 */
40function get_parent_cats($contextid, $type) {
41
42 $parents = array();
43 $context = get_record('context', 'id', $contextid);
44
45 switch($type) {
46
47 case CONTEXT_COURSECAT:
48
49 $cat = get_record('course_categories','id',$context->instanceid);
50 while ($cat->parent) {
51
52 $context = get_context_instance(CONTEXT_COURSECAT, $cat->parent);
53 $parents[] = $context->id;
54 $cat = get_record('course_categories','id',$cat->parent);
55 }
56
57 break;
58
59 case CONTEXT_COURSE:
60
61 $course = get_record('course', 'id', $context->instanceid);
62 $cat = get_record('course_categories','id',$course->category);
63 $catinstance = get_context_instance(CONTEXT_COURSECAT, $course->category);
64 $parents[] = $catinstance->id;
65
66 // what to do with cat 0?
67 while ($cat->parent) {
68 $context = get_context_instance(CONTEXT_COURSECAT, $cat->parent);
69 $parents[] = $context->id;
70 $cat = get_record('course_categories','id',$cat->parent);
71 }
72 break;
73
74 default:
75 break;
76
77 }
78
79 return array_reverse($parents);
80}
81
82
83/* Functions for Roles & Capabilites */
84
85
86/**
87 * This function returns whether the current user has the capability of performing a function
88 * For example, we can do has_capability('mod/forum:replypost',$cm) in forum
89 * only one of the 4 (moduleinstance, courseid, site, userid) would be set at 1 time
90 * This is a recursive funciton.
91 * Might change to require_capability, and throw an error if not authorized.
92 * @uses $USER
93 * @param string $capability - name of the capability
94 * @param int $contextid
95 * @param kill bool - if set, kill when the user has no capability
96 * @return bool
97 */
98function has_capability($capability, $contextid, $kill=false, $userid=NULL) {
99
100 global $USER;
101
102 if ($userid && $userid != $USER->id) { // loading other user's capability
103 $capabilities = load_user_capability($capability, $contextid, $userid);
104 } else {
105 $capabilities = $USER->capabilities;
106 }
107
108 //echo ("capablity is ".$capability);
109 //print_object($contextid);
110
111 $context = get_record('context','id',$contextid);
112
113 // Check site
114 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
115 if (isset($capabilities[$sitecontext->id]['moodle/site:doanything'])) {
116 return ($capabilities[$sitecontext->id]['moodle/site:doanything']);
117 }
118
119 switch (context_level($contextid)) {
120
121 case CONTEXT_COURSECAT:
122 // Check parent cats.
123 $parentcats = get_parent_cats($contextid, CONTEXT_COURSECAT);
124 foreach ($parentcats as $parentcat) {
125 if (isset($capabilities[$parentcat]['moodle/site:doanything'])) {
126 return ($capabilities[$parentcat]['moodle/site:doanything']);
127 }
128 }
129 break;
130
131 case CONTEXT_COURSE:
132 // Check parent cat.
133 $parentcats = get_parent_cats($contextid, CONTEXT_COURSE);
134
135 foreach ($parentcats as $parentcat) {
136 if (isset($capabilities[$parentcat]['do_anything'])) {
137 return ($capabilities[$parentcat]['do_anything']);
138 }
139 }
140 break;
141
142 case CONTEXT_GROUP:
143 // Find course.
144 $group = get_record('groups','id',$context->instanceid);
145 $courseinstance = get_context_instance(CONTEXT_COURSE, $group->courseid);
146
147 $parentcats = get_parent_cats($courseinstance->id, CONTEXT_COURSE);
148 foreach ($parentcats as $parentcat) {
149 if (isset($capabilities[$parentcat->id]['do_anything'])) {
150 return ($capabilities[$parentcat->id]['do_anything']);
151 }
152 }
153
154 $coursecontext = '';
155 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
156 return ($capabilities[$courseinstance->id]['do_anything']);
157 }
158
159 break;
160
161 case CONTEXT_MODULE:
162 // Find course.
163 $cm = get_record('course_modules', 'id', $context->instanceid);
164 $courseinstance = get_context_instance(CONTEXT_COURSE, $cm->course);
165
166 if ($parentcats = get_parent_cats($courseinstance->id, CONTEXT_COURSE)) {
167 foreach ($parentcats as $parentcat) {
168 if (isset($capabilities[$parentcat]['do_anything'])) {
169 return ($capabilities[$parentcat]['do_anything']);
170 }
171 }
172 }
173
174 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
175 return ($capabilities[$courseinstance->id]['do_anything']);
176 }
177
178 break;
179
180 case CONTEXT_BLOCK:
181 // 1 to 1 to course.
182 // Find course.
183 $block = get_record('block_instance','id',$context->instanceid);
184 $courseinstance = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
185
186 $parentcats = get_parent_cats($courseinstance->id, CONTEXT_COURSE);
187 foreach ($parentcats as $parentcat) {
188 if (isset($capabilities[$parentcat]['do_anything'])) {
189 return ($capabilities[$parentcat]['do_anything']);
190 }
191 }
192
193 if (isset($capabilities[$courseinstance->id]['do_anything'])) {
194 return ($capabilities[$courseinstance->id]['do_anything']);
195 }
196 break;
197
198 default:
199 // CONTEXT_SYSTEM: CONTEXT_PERSONAL: CONTEXT_USERID:
200 // Do nothing.
201 break;
202 }
203
204 // Last: check self.
205 if (isset($capabilities[$contextid]['do_anything'])) {
206 return ($capabilities[$contextid]['do_anything']);
207 }
208
209 // do_anything has not been set, we now look for it the normal way.
210 return capability_search($capability, $contextid, $kill, $capabilities);
211
212}
213
214
215/**
216 * In a separate function so that we won't have to deal with do_anything.
217 * again. Used by function has_capability.
218 * @param $capability - capability string
219 * @param $contextid - the context id
220 * @param $kill - boolean. Error out and exit if the user doesn't have the
221 * capability?
222 * @param $capabilities - either $USER->capability or loaded array
223 * @return permission (int)
224 */
225function capability_search($capability, $contextid, $kill=false, $capabilities) {
226 global $USER, $CFG;
227
228 if ($CFG->debug) {
229 notify("We are looking for $capability in context $contextid", 'notifytiny');
230 }
231
232 if (isset($capabilities[$contextid][$capability])) {
233 return ($capabilities[$contextid][$capability]);
234 }
235
236 /* Then, we check the cache recursively */
237 $context = get_record('context','id',$contextid); // shared
238 $permission = 0;
239
240 switch (context_level($contextid)) {
241
242 case CONTEXT_SYSTEM: // by now it's a definite an inherit
243 $permission = 0;
244 break;
245
246 case CONTEXT_PERSONAL:
247 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
248 $permission = (capability_search($capability, $parent->id, false, $capabilities));
249 break;
250
251 case CONTEXT_USERID:
252 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
253 $permission = (capability_search($capability, $parent->id, false, $capabilities));
254 break;
255
256 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
257 $coursecat = get_record('course_categories','id',$context->instanceid);
258 if ($coursecat->parent) { // return parent value if exist
259 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
260 } else { // else return site value
261 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
262 }
263 $permission = (capability_search($capability, $parent->id, false, $capabilities));
264 break;
265
266 case CONTEXT_COURSE: // 1 to 1 to course cat
267 // find the course cat, and return its value
268 $course = get_record('course','id',$context->instanceid);
269 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
270 $permission = (capability_search($capability, $parent->id, false, $capabilities));
271 break;
272
273 case CONTEXT_GROUP: // 1 to 1 to course
274 $group = get_record('groups','id',$context->instanceid);
275 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
276 $permission = (capability_search($capability, $parent->id, false, $capabilities));
277 break;
278
279 case CONTEXT_MODULE: // 1 to 1 to course
280 $cm = get_record('course_modules','id',$context->instanceid);
281 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
282 $permission = (capability_search($capability, $parent->id, false, $capabilities));
283 break;
284
285 case CONTEXT_BLOCK: // 1 to 1 to course
286 $block = get_record('block_instance','id',$context->instanceid);
287 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
288 $permission = (capability_search($capability, $parent->id, false, $capabilities));
289 break;
290
291 default:
292 error ('This is an unknown context!');
293 return false;
294 }
295
296 if ($kill && ($permission <= 0)) {
297 error ('You do not have the required capability '.$capability);
298 }
299 return $permission;
300}
301
302
303/**
304 * This function should be called immediately after a login, when $USER is set.
305 * It will build an array of all the capabilities at each level
306 * i.e. site/metacourse/course_category/course/moduleinstance
307 * Note we should only load capabilities if they are explicitly assigned already,
308 * we should not load all module's capability!
309 * @param $userid - the id of the user whose capabilities we want to load
310 * @return array
311 * possible just s simple 2D array with [contextid][capabilityname]
312 * [Capabilities] => [26][forum_post] = 1
313 * [26][forum_start] = -8990
314 * [26][forum_edit] = -1
315 * [273][blah blah] = 1
316 * [273][blah blah blah] = 2
317 */
318function load_user_capability($capability='', $contextid ='', $userid='') {
319
320 global $USER, $CFG;
321
322 if (empty($userid)) {
323 $userid = $USER->id;
324 } else {
325 $otheruserid = $userid;
326 }
327
328 if ($capability) {
329 $capsearch = ' AND rc.capability = '.$capability.' ';
330 } else {
331 $capsearch ='';
332 }
333 // First we generate a list of all relevant contexts of the user
334
335 if ($contextid) { // if context is specified
336 $context = get_record('context', 'id', $contextid);
337
338 $usercontexts = get_parent_contexts($context->id);
339 $listofcontexts = '('.implode(',', $usercontexts).')';
340 } else { // else, we load everything
341 $usercontexts = get_records('role_assignments','userid',$userid);
342 $listofcontexts = '(';
343 foreach ($usercontexts as $usercontext) {
344 $listofcontexts .= $usercontext->contextid;
345 $listofcontexts .= ',';
346 }
347 $listofcontexts = rtrim ($listofcontexts, ",");
348 $listofcontexts .= ')';
349 }
350
351 // Then we use 1 giant SQL to bring out all relevant capabilities.
352 // The first part gets the capabilities of orginal role.
353 // The second part gets the capabilities of overriden roles.
354
355 $siteinstance = get_context_instance(CONTEXT_SYSTEM, SITEID);
356
357 $SQL = " SELECT rc.capability, c1.id, (c1.level * 100) AS level,
358 SUM(rc.permission) AS sum
359 FROM
360 {$CFG->prefix}role_assignments AS ra
361 INNER JOIN {$CFG->prefix}role_capabilities AS rc ON ra.roleid=rc.roleid
362 INNER JOIN {$CFG->prefix}context AS c1 ON ra.contextid=c1.id
363 WHERE
364 ra.userid=$userid AND
365 c1.id IN $listofcontexts AND
366 rc.contextid=$siteinstance->id
367 $capsearch
368 GROUP BY
369 rc.capability,level,c1.id
370 HAVING
371 sum != 0
372 UNION
373
374 SELECT rc.capability, c1.id, (c1.level * 100 + c2.level) AS level,
375 SUM(rc.permission) AS sum
376 FROM
377 {$CFG->prefix}role_assignments AS ra
378 INNER JOIN {$CFG->prefix}role_capabilities AS rc ON ra.roleid=rc.roleid
379 INNER JOIN {$CFG->prefix}context AS c1 ON ra.contextid=c1.id
380 LEFT OUTER JOIN {$CFG->prefix}context AS c2 ON rc.contextid=c2.id
381 WHERE
382 ra.userid=$userid AND
383 c1.id IN $listofcontexts AND
384 c2.id IN $listofcontexts AND rc.contextid != $siteinstance->id
385 $capsearch
386
387 GROUP BY
388 rc.capability, level, c1.id
389 HAVING
390 sum != 0
391 ORDER BY
392 level ASC
393 ";
394
395// echo "$SQL"; // debug
396
397 $capabilities = array(); // Reinitialize.
398 $rs = get_recordset_sql($SQL);
399
400 if ($rs && $rs->RecordCount() > 0) {
401 while (!$rs->EOF) {
402 $array = $rs->fields;
403 $temprecord = new object;
404
405 foreach ($array as $key=>$val) {
406 $temprecord->{$key} = $val;
407 }
408 $capabilities[] = $temprecord;
409 $rs->MoveNext();
410 }
411 }
412
413 /* so up to this point we should have somethign like this
414 * $capabilities[1] ->level = 1000
415 ->module = SITEID
416 ->capability = do_anything
417 ->id = 1 (id is the context id)
418 ->sum = 0
419
420 * $capabilities[2] ->level = 1000
421 ->module = SITEID
422 ->capability = post_messages
423 ->id = 1
424 ->sum = -9000
425
426 * $capabilittes[3] ->level = 3000
427 ->module = course
428 ->capability = view_course_activities
429 ->id = 25
430 ->sum = 1
431
432 * $capabilittes[4] ->level = 3000
433 ->module = course
434 ->capability = view_course_activities
435 ->id = 26
436 ->sum = 0 (this is another course)
437
438 * $capabilities[5] ->level = 3050
439 ->module = course
440 ->capability = view_course_activities
441 ->id = 25 (override in course 25)
442 ->sum = -1
443 * ....
444 * now we proceed to write the session array, going from top to bottom
445 * at anypoint, we need to go up and check parent to look for prohibit
446 */
447 // print_object($capabilities);
448
449 /* This is where we write to the actualy capabilities array
450 * what we need to do from here on is
451 * going down the array from lowest level to highest level
452 * 1) recursively check for prohibit,
453 * if any, we write prohibit
454 * else, we write the value
455 * 2) at an override level, we overwrite current level
456 * if it's not set to prohibit already, and if different
457 * ........ that should be it ........
458 */
459 $usercap = array(); // for other user's capabilities
460 foreach ($capabilities as $capability) {
461
462 if ($otheruserid) { // we are pulling out other user's capabilities, do not write to session
463
464 if (capability_prohibits($capability->capability, $capability->id, $capability->sum, $usercap)) {
465 $usercap[$capability->id][$capability->capability] = -9000;
466 continue;
467 }
468
469 $usercap[$capability->id][$capability->capability] = $capability->sum;
470
471 } else {
472
473 if (capability_prohibits($capability->capability, $capability->id, $capability->sum)) { // if any parent or parent's parent is set to prohibit
474 $USER->capabilities[$capability->id][$capability->capability] = -9000;
475 continue;
476 }
477
478 // if no parental prohibit set
479 // just write to session, i am not sure this is correct yet
480 // since 3050 shows up after 3000, and 3070 shows up after 3050,
481 // it should be ok just to overwrite like this, provided that there's no
482 // parental prohibits
483 // no point writing 0, since 0 = inherit
484 // we need to write even if it's 0, because it could be an inherit override
485 $USER->capabilities[$capability->id][$capability->capability] = $capability->sum;
486 }
487 }
488
489 // now we don't care about the huge array anymore, we can dispose it.
490 unset($capabilities);
491
492 if ($otheruseid) {
493 return $usercap; // return the array
494 }
495 // see array in session to see what it looks like
496
497}
498
499
500/**
501 * This is a recursive function that checks whether the capability in this
502 * context, or the parent capabilities are set to prohibit.
503 *
504 * At this point, we can probably just use the values already set in the
505 * session variable, since we are going down the level. Any prohit set in
506 * parents would already reflect in the session.
507 *
508 * @param $capability - capability name
509 * @param $sum - sum of all capabilities values
510 * @param $contextid - the context id
511 * @param $array - when loading another user caps, their caps are not stored in session but an array
512 */
513function capability_prohibits($capability, $contextid, $sum='', $array='') {
514 global $USER;
515 if ($sum < -8000) {
516 // If this capability is set to prohibit.
517 return true;
518 }
519
520 if (isset($array)) {
521 if (isset($array[$contextid][$capability])
522 && $array[$contextid][$capability] < -8000) {
523 return true;
524 }
525 } else {
526 // Else if set in session.
527 if (isset($USER->capabilities[$contextid][$capability])
528 && $USER->capabilities[$contextid][$capability] < -8000) {
529 return true;
530 }
531 }
532 $context = get_record('context', 'id', $contextid);
533 switch (context_level($contextid)) {
534
535 case CONTEXT_SYSTEM:
536 // By now it's a definite an inherit.
537 return 0;
538 break;
539
540 case CONTEXT_PERSONAL:
541 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
542 return (capability_prohibits($capability, $parent->id));
543 break;
544
545 case CONTEXT_USERID:
546 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
547 return (capability_prohibits($capability, $parent->id));
548 break;
549
550 case CONTEXT_COURSECAT:
551 // Coursecat -> coursecat or site.
552 $coursecat = get_record('course_categories','id',$context->instanceid);
553 if ($coursecat->parent) {
554 // return parent value if exist.
555 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
556 } else {
557 // Return site value.
558 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
559 }
560 return (capability_prohibits($capability, $parent->id));
561 break;
562
563 case CONTEXT_COURSE:
564 // 1 to 1 to course cat.
565 // Find the course cat, and return its value.
566 $course = get_record('course','id',$context->instanceid);
567 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
568 return (capability_prohibits($capability, $parent->id));
569 break;
570
571 case CONTEXT_GROUP:
572 // 1 to 1 to course.
573 $group = get_record('groups','id',$context->instanceid);
574 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
575 return (capability_prohibits($capability, $parent->id));
576 break;
577
578 case CONTEXT_MODULE:
579 // 1 to 1 to course.
580 $cm = get_record('course_modules','id',$context->instanceid);
581 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
582 return (capability_prohibits($capability, $parent->id));
583 break;
584
585 case CONTEXT_BLOCK:
586 // 1 to 1 to course.
587 $block = get_record('block_instance','id',$context->instanceid);
588 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
589 return (capability_prohibits($capability, $parent->id));
590 break;
591
592 default:
593 error ('This is an unknown context!');
594 return false;
595 }
596}
597
598
599/**
600 * A print form function. This should either grab all the capabilities from
601 * files or a central table for that particular module instance, then present
602 * them in check boxes. Only relevant capabilities should print for known
603 * context.
604 * @param $mod - module id of the mod
605 */
606function print_capabilities($modid=0) {
607 global $CFG;
608
609 $capabilities = array();
610
611 if ($modid) {
612 // We are in a module specific context.
613
614 // Get the mod's name.
615 // Call the function that grabs the file and parse.
616 $cm = get_record('course_modules', 'id', $modid);
617 $module = get_record('modules', 'id', $cm->module);
618
619 } else {
620 // Print all capabilities.
621 foreach ($capabilities as $capability) {
622 // Prints the check box component.
623 }
624 }
625}
626
627
628/**
629 * This function handles the upgrade process of assigning default roles
630 * to the existing users
631 */
632function moodle_upgrade_roles_system_17() {
633
634 global $CFG;
635 // Create a system wide context for assignemnt.
636 $systemcontext = $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
637
638 // loading legacy roles and capabilities (1 legacy capability per legacy role at system levle)
639 $adminrole = create_role(get_string('administrator'), get_string('administratordescription'), 'moodle/legacy:admin');
640 if (!assign_capability('moodle/site:doanything', CAP_ALLOW, $adminrole, $systemcontext->id)) {
641 error('Could not assign moodle/site:doanything to the admin role');
642 }
643 $coursecreatorrole = create_role(get_string('coursecreators'), get_string('coursecreatorsdescription'), 'moodle/legacy:coursecreator');
644 $noneditteacherrole = create_role(get_string('noneditingteacher'), get_string('noneditingteacherdescription'), 'moodle/legacy:teacher');
645 $editteacherrole = create_role(get_string('defaultcourseteacher'), get_string('defaultcourseteacherdescription'), 'moodle/legacy:editingteacher');
646 $studentrole = create_role(get_string('defaultcoursestudent'), get_string('defaultcoursestudentdescription'), 'moodle/legacy:student');
647 $guestrole = create_role(get_string('guest'), get_string('guestdescription'), 'moodle/legacy:guest');
648
649 // Look inside user_admin, user_creator, $user_teachers, $user_students
650 // and assign roles. If a user has both teacher and student role, only
651 // teacher role is assigned. The assignment should be system level.
652
653 /**
654 * Upgrade the admins.
655 */
656
657 // sort using id asc, first one is primary admin
658 $useradmins = get_records_sql('SELECT * from '.$CFG->prefix.'user_admins ORDER BY ID ASC');
659 foreach ($useradmins as $admin) {
660 role_assign($adminrole, $admin->userid, 0, $systemcontext->id);
661 }
662 // Do we assign the capability moodle/doanything to the admin roles here?
663
664
665 /**
666 * Upgrade course creators.
667 */
668
669 $usercoursecreators = get_records('user_coursecreators');
670 foreach ($usercoursecreators as $coursecreator) {
671 role_assign($$coursecreatorrole, $coursecreator->userid, 0, $systemcontext->id);
672 }
673
674 /**
675 * Upgrade editting teachers and non-editting teachers.
676 */
677
678 $userteachers = get_records('user_teachers');
679 foreach ($userteachers as $teacher) {
680 $coursecontext = get_context_instance(CONTEXT_COURSE, $teacher->course); // needs cache
681 if ($teacher->editall) { // editting teacher
682 role_assign($editteacherrole, $teacher->userid, 0, $coursecontext->id);
683 } else {
684 role_assign($noneditteacherrole, $teacher->userid, 0, $coursecontext->id);
685 }
686 }
687
688 /**
689 * Upgrade students.
690 */
691 $userstudents = get_records('user_students');
692 foreach ($userstudents as $student) {
693 $coursecontext = get_context_instance(CONTEXT_COURSE, $student->course);
694 role_assign($studentrole, $student->userid, 0, $coursecontext->id);
695 }
696
697 /**
698 * Upgrade guest (only 1 entry).
699 */
700 $guestuser = get_record('user', 'username', 'guest');
701 role_assign($guestrole, $guestuser->id, 0, $systemcontext->id);
702
703 //
704 // Should we delete the tables after we are done?
705 //
706
707 set_config('rolesactive', 1);
708}
709
710
711/**
712 * Assign the defaults found in this capabality definition to roles that have
713 * the corresponding legacy capabilities assigned to them.
714 * @param $legacyperms - an array in the format (example):
715 * 'guest' => CAP_PREVENT,
716 * 'student' => CAP_ALLOW,
717 * 'teacher' => CAP_ALLOW,
718 * 'editingteacher' => CAP_ALLOW,
719 * 'coursecreator' => CAP_ALLOW,
720 * 'admin' => CAP_ALLOW
721 * @return boolean - success or failure.
722 */
723function assign_legacy_capabilities($capability, $legacyperms) {
724
725 foreach ($legacyperms as $type => $perm) {
726
727 $systemcontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
728
729 // The legacy capabilities are:
730 // 'moodle/legacy:guest'
731 // 'moodle/legacy:student'
732 // 'moodle/legacy:teacher'
733 // 'moodle/legacy:editingteacher'
734 // 'moodle/legacy:coursecreator'
735 // 'moodle/legacy:admin'
736
737 if (!$roles = get_roles_with_capability('moodle/legacy:'.$type, CAP_ALLOW)) {
738 return false;
739 }
740
741 foreach ($roles as $role) {
742 // Assign a site level capability.
743 if(!assign_capability($capability, $perm, $role->id, $systemcontext->id)) {
744 return false;
745 }
746 }
747 }
748 return true;
749}
750
751
752// checks to see if a capability is a legacy capability, returns bool
753function islegacy($capabilityname) {
754 if (strstr($capabilityname, 'legacy') === false) {
755 return false;
756 } else {
757 return true;
758 }
759}
760
761/************************************
762 * Context Manipulation functions *
763 **********************************/
764
765
766/**
767 * This should be called prolly everytime a user, group, module, course,
768 * coursecat or site is set up maybe?
769 * @param $level
770 * @param $instanceid
771 */
772function create_context($level, $instanceid) {
773 if (!get_record('context','level',$level,'instanceid',$instanceid)) {
774 $context = new object;
775 $context->level = $level;
776 $context->instanceid = $instanceid;
777 return insert_record('context',$context);
778 }
779}
780
781
782/**
783 * Get the context instance as an object. This function will create the
784 * context instance if it does not exist yet.
785 * @param $level
786 * @param $instance
787 */
788function get_context_instance($level, $instance=SITEID) {
789 // echo "getting level $level instance $instance";
790
791 // XXX TODO Add caching here
792 if (!$context = get_record('context', 'level', $level, 'instanceid', $instance)) {
793 //echo "creating ...";
794 create_context($level, $instance);
795 $context = get_record('context', 'level', $level, 'instanceid', $instance);
796 }
797 return $context;
798}
799
800
801/**
802 * Looks up the context level.
803 * @param int $contextid
804 * @return int
805 */
806function context_level($contextid) {
807 $context = get_record('context','id',$contextid);
808 return ($context->level);
809}
810
811
812
813/************************************
814 * DB TABLE RELATED FUNCTIONS *
815 ************************************/
816
817/**********************************************
818 * function that creates a role
819 * @param name - role name
820 * @param description - role description
821 * @param legacy - optional legacy capability
822 * @return id or false
823 */
824function create_role($name, $description, $legacy='') {
825
826 // check for duplicate role name
827
828 if ($role = get_record('role','name', $name)) {
829 print_object($role);
830 error('there is already a role with this name!');
831 }
832
833 $role->name = $name;
834 $role->description = $description;
835
836 if ($id = insert_record('role', $role)) {
837 if ($legacy) {
838 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
839 assign_capability($legacy, CAP_ALLOW, $id, $context->id);
840 }
841 return $id;
842 } else {
843 return false;
844 }
845
846}
847
848/**
849 * Function to write context specific overrides, or default capabilities.
850 * @param module - string name
851 * @param capability - string name
852 * @param contextid - context id
853 * @param roleid - role id
854 * @param permission - int 1,-1 or -1000
855 */
856function assign_capability($capability, $permission, $roleid, $contextid) {
857
858 global $USER;
859
860 if (empty($permission) || $permission == 0) { // if permission is not set
861 unassign_capability($capability, $roleid, $contextid);
862 }
863
864 $cap = new object;
865 $cap->contextid = $contextid;
866 $cap->roleid = $roleid;
867 $cap->capability = $capability;
868 $cap->permission = $permission;
869 $cap->timemodified = time();
870 $cap->modifierid = $USER->id;
871
872 return insert_record('role_capabilities', $cap);
873}
874
875
876/**
877 * Unassign a capability from a role.
878 * @param $roleid - the role id
879 * @param $capability - the name of the capability
880 * @return boolean - success or failure
881 */
882function unassign_capability($capability, $roleid, $contextid=NULL) {
883
884 if (isset($contextid)) {
885 $status = delete_records('role_capabilities', 'capability', $capability,
886 'roleid', $roleid, 'contextid', $contextid);
887 } else {
888 $status = delete_records('role_capabilities', 'capability', $capability,
889 'roleid', $roleid);
890 }
891 return $status;
892}
893
894
895/**
896 * Get the roles that have a given capability.
897 * @param $capability - capability name (string)
898 * @param $permission - optional, the permission defined for this capability
899 * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT
900 * @return array or role objects
901 */
902function get_roles_with_capability($capability, $permission=NULL) {
903
904 global $CFG;
905
906 $selectroles = "SELECT r.*
907 FROM {$CFG->prefix}role AS r,
908 {$CFG->prefix}role_capabilities AS rc
909 WHERE rc.capability = '$capability'
910 AND rc.roleid = r.id";
911
912 if (isset($permission)) {
913 $selectroles .= " AND rc.permission = '$permission'";
914 }
915 return get_records_sql($selectroles);
916}
917
918
919/**
920 * This function makes a role-assignment (user to a role)
921 * @param $roleid - the role of the id
922 * @param $userid - userid
923 * @param $groupid - group id
924 * @param $contextid - id of the context
925 * @param $timestart - time this assignment becomes effective
926 * @param $timeend - time this assignemnt ceases to be effective
927 * @uses $USER
928 * @return id - new id of the assigment
929 */
930function role_assign($roleid, $userid, $groupid, $contextid, $timestart=0, $timeend=0, $hidden=0) {
aa311411 931 global $USER, $CFG;
bbbf2d40 932
aa311411 933 if ($CFG->debug) {
934 notify("Assign roleid $roleid userid $userid contextid $contextid", 'notifytiny');
935 }
bbbf2d40 936
937 if (empty($roleid)) {
938 error ('you need to select a role');
939 }
940
941 if (empty($userid) && empty($groupid)) {
942 error ('you need to assign this role to a user or a group');
943 }
944
945 if (empty($contextid)) {
946 error ('you need to assign this role to a context, e.g. a course, or an activity');
947 }
948
949 $ra = new object;
950 $ra->roleid = $roleid;
951 $ra->contextid = $contextid;
952 $ra->userid = $userid;
953 $ra->hidden = $hidden;
954 $ra->groupid = $groupid;
955 $ra->timestart = $timestart;
956 $ra->timeend = $timeend;
957 $ra->timemodified = time();
958 $ra->modifier = $USER->id;
959
960 return insert_record('role_assignments', $ra);
961
962}
963
964
965/**
966 * Deletes a role assignment.
967 * @param $roleid
968 * @param $userid
969 * @param $groupid
970 * @param $contextid
971 * @return boolean - success or failure
972 */
973function role_unassign($roleid, $userid, $groupid, $contextid) {
974 if ($groupid) {
975 // do nothing yet as this is not implemented
976 }
977 else {
978 return delete_records('role_assignments', 'userid', $userid,
979 'roleid', $roleid, 'contextid', $contextid);
980 }
981}
982
983
984/**
985 * Loads the capability definitions for the component (from file). If no
986 * capabilities are defined for the component, we simply return an empty array.
987 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
988 * @return array of capabilities
989 */
990function load_capability_def($component) {
991 global $CFG;
992
993 if ($component == 'moodle') {
994 $defpath = $CFG->libdir.'/db/access.php';
995 $varprefix = 'moodle';
996 } else {
997 $defpath = $CFG->dirroot.'/'.$component.'/db/access.php';
998 $varprefix = str_replace('/', '_', $component);
999 }
1000 $capabilities = array();
1001
1002 if (file_exists($defpath)) {
1003 require_once($defpath);
1004 $capabilities = ${$varprefix.'_capabilities'};
1005 }
1006 return $capabilities;
1007}
1008
1009
1010/**
1011 * Gets the capabilities that have been cached in the database for this
1012 * component.
1013 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1014 * @return array of capabilities
1015 */
1016function get_cached_capabilities($component='moodle') {
1017 if ($component == 'moodle') {
1018 $storedcaps = get_records_select('capabilities',
1019 "name LIKE 'moodle/%:%'");
1020 } else {
1021 $storedcaps = get_records_select('capabilities',
1022 "name LIKE '$component:%'");
1023 }
1024 return $storedcaps;
1025}
1026
1027
1028/**
1029 * Updates the capabilities table with the component capability definitions.
1030 * If no parameters are given, the function updates the core moodle
1031 * capabilities.
1032 *
1033 * Note that the absence of the db/access.php capabilities definition file
1034 * will cause any stored capabilities for the component to be removed from
1035 * the database.
1036 *
1037 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1038 * @return boolean
1039 */
1040function update_capabilities($component='moodle') {
1041
1042 $storedcaps = array();
1043 $filecaps = array();
1044
1045 $cachedcaps = get_cached_capabilities($component);
1046 if ($cachedcaps) {
1047 foreach ($cachedcaps as $cachedcap) {
1048 array_push($storedcaps, $cachedcap->name);
1049 }
1050 }
1051
1052 $filecaps = load_capability_def($component);
1053
1054 // Are there new capabilities in the file definition?
1055 $newcaps = array();
1056
1057 foreach ($filecaps as $filecap => $def) {
1058 if (!$storedcaps ||
1059 ($storedcaps && in_array($filecap, $storedcaps) === false)) {
1060 $newcaps[$filecap] = $def;
1061 }
1062 }
1063 // Add new capabilities to the stored definition.
1064 foreach ($newcaps as $capname => $capdef) {
1065 $capability = new object;
1066 $capability->name = $capname;
1067 $capability->captype = $capdef['captype'];
1068 $capability->contextlevel = $capdef['contextlevel'];
1069 $capability->component = $component;
1070
1071 if (!insert_record('capabilities', $capability, false, 'id')) {
1072 return false;
1073 }
1074 // Do we need to assign the new capabilities to roles that have the
1075 // legacy capabilities moodle/legacy:* as well?
1076 if (isset($capdef['legacy']) && is_array($capdef['legacy']) &&
1077 !assign_legacy_capabilities($capname, $capdef['legacy'])) {
1078 error('Could not assign legacy capabilities');
1079 return false;
1080 }
1081 }
1082 // Are there any capabilities that have been removed from the file
1083 // definition that we need to delete from the stored capabilities and
1084 // role assignments?
1085 capabilities_cleanup($component, $filecaps);
1086
1087 return true;
1088}
1089
1090
1091/**
1092 * Deletes cached capabilities that are no longer needed by the component.
1093 * Also unassigns these capabilities from any roles that have them.
1094 * @param $component - examples: 'moodle', 'mod/forum', 'block/quiz_results'
1095 * @param $newcapdef - array of the new capability definitions that will be
1096 * compared with the cached capabilities
1097 * @return int - number of deprecated capabilities that have been removed
1098 */
1099function capabilities_cleanup($component, $newcapdef=NULL) {
1100
1101 $removedcount = 0;
1102
1103 if ($cachedcaps = get_cached_capabilities($component)) {
1104 foreach ($cachedcaps as $cachedcap) {
1105 if (empty($newcapdef) ||
1106 array_key_exists($cachedcap->name, $newcapdef) === false) {
1107
1108 // Remove from capabilities cache.
1109 if (!delete_records('capabilities', 'name', $cachedcap->name)) {
1110 error('Could not delete deprecated capability '.$cachedcap->name);
1111 } else {
1112 $removedcount++;
1113 }
1114 // Delete from roles.
1115 if($roles = get_roles_with_capability($cachedcap->name)) {
1116 foreach($roles as $role) {
1117 if (!unassign_capability($role->id, $cachedcap->name)) {
1118 error('Could not unassign deprecated capability '.
1119 $cachedcap->name.' from role '.$role->name);
1120 }
1121 }
1122 }
1123 } // End if.
1124 }
1125 }
1126 return $removedcount;
1127}
1128
1129
1130
1131
1132/************************************************************
1133 * * UI FUNCTIONS * *
1134 ************************************************************/
1135
1136
1137/**
1138 * prints human readable context identifier.
1139 */
1140function print_context_name($contextid) {
1141
ec0810ee 1142 $name = '';
1143
bbbf2d40 1144 $context = get_record('context', 'id', $contextid);
ec0810ee 1145
bbbf2d40 1146 switch ($context->level) {
1147
1148 case CONTEXT_SYSTEM: // by now it's a definite an inherit
ec0810ee 1149 $name = get_string('site');
bbbf2d40 1150 break;
1151
1152 case CONTEXT_PERSONAL:
ec0810ee 1153 $name = get_string('personal');
bbbf2d40 1154 break;
1155
1156 case CONTEXT_USERID:
ec0810ee 1157 if ($user = get_record('user', 'id', $context->instanceid)) {
1158 $name = get_string('user').': '.fullname($user);
1159 }
bbbf2d40 1160 break;
1161
1162 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
ec0810ee 1163 if ($category = get_record('course_categories', 'id', $context->instanceid)) {
1164 $name = get_string('category').': '.$category->name;
1165 }
bbbf2d40 1166 break;
1167
1168 case CONTEXT_COURSE: // 1 to 1 to course cat
ec0810ee 1169 if ($course = get_record('course', 'id', $context->instanceid)) {
1170 $name = get_string('course').': '.$course->fullname;
1171 }
bbbf2d40 1172 break;
1173
1174 case CONTEXT_GROUP: // 1 to 1 to course
ec0810ee 1175 if ($group = get_record('groups', 'id', $context->instanceid)) {
1176 $name = get_string('group').': '.$group->name;
1177 }
bbbf2d40 1178 break;
1179
1180 case CONTEXT_MODULE: // 1 to 1 to course
ec0810ee 1181 if ($cm = get_record('course_modules','id',$context->instanceid)) {
1182 if ($module = get_record('modules','id',$cm->module)) {
1183 if ($mod = get_record($module->name, 'id', $cm->instance)) {
1184 $name = get_string('activitymodule').': '.$mod->name;
1185 }
1186 }
1187 }
bbbf2d40 1188 break;
1189
1190 case CONTEXT_BLOCK: // 1 to 1 to course
ec0810ee 1191 if ($blockinstance = get_record('block_instance','id',$context->instanceid)) {
1192 if ($block = get_record('block','id',$blockinstance->blockid)) {
1193 $name = get_string('blocks').': '.get_string($block->name, 'block_'.$block->name);
1194 }
1195 }
bbbf2d40 1196 break;
1197
1198 default:
1199 error ('This is an unknown context!');
1200 return false;
1201
1202 }
1203
ec0810ee 1204 return $name;
bbbf2d40 1205}
1206
1207
1208/**
1209 * Extracts the relevant capabilities given a contextid.
1210 * All case based, example an instance of forum context.
1211 * Will fetch all forum related capabilities, while course contexts
1212 * Will fetch all capabilities
1213 * @param int contextid
1214 * @return array();
1215 *
1216 * capabilities
1217 * `name` varchar(150) NOT NULL,
1218 * `captype` varchar(50) NOT NULL,
1219 * `contextlevel` int(10) NOT NULL,
1220 * `component` varchar(100) NOT NULL,
1221 */
1222function fetch_context_capabilities($contextid) {
1223
1224 global $CFG;
1225
1226 $sort = 'ORDER BY contextlevel,component,id'; // To group them sensibly for display
1227
1228 switch (context_level($contextid)) {
1229
1230 case CONTEXT_SYSTEM: // all
1231 $SQL = "select * from {$CFG->prefix}capabilities";
1232 break;
1233
1234 case CONTEXT_PERSONAL:
1235 break;
1236
1237 case CONTEXT_USERID:
1238 break;
1239
1240 case CONTEXT_COURSECAT: // all
1241 $SQL = "select * from {$CFG->prefix}capabilities";
1242 break;
1243
1244 case CONTEXT_COURSE: // all
1245 $SQL = "select * from {$CFG->prefix}capabilities";
1246 break;
1247
1248 case CONTEXT_GROUP: // group caps
1249 break;
1250
1251 case CONTEXT_MODULE: // mod caps
1252 $context = get_record('context','id',$contextid);
1253 $cm = get_record('course_modules', 'id', $context->instanceid);
1254 $module = get_record('modules', 'id', $cm->module);
1255
1256 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_MODULE."
1257 and component = 'mod/$module->name'";
1258 break;
1259
1260 case CONTEXT_BLOCK: // block caps
1261 $context = get_record('context','id',$contextid);
1262 $cb = get_record('block_instance', 'id', $context->instanceid);
1263 $block = get_record('block', 'id', $cb->blockid);
1264
1265 $SQL = "select * from {$CFG->prefix}capabilities where contextlevel = ".CONTEXT_BLOCK."
1266 and component = 'block/$block->name'";
1267 break;
1268
1269 default:
1270 return false;
1271 }
1272
1273 $records = get_records_sql($SQL.' '.$sort);
1274 return $records;
1275
1276}
1277
1278
1279/**
1280 * This function pulls out all the resolved capabilities (overrides and
1281 * defaults) of a role used in capability overrieds in contexts at a given
1282 * context.
1283 * @param int $contextid
1284 * @param int $roleid
1285 * @return array
1286 */
1287function role_context_capabilities($roleid, $contextid) {
1288 global $CFG;
1289
1290 $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
1291 if ($sitecontext->id == $contextid) {
1292 return array();
1293 }
1294
1295 // first of all, figure out all parental contexts
1296 $context = get_record('context', 'id', $contextid);
1297 $contexts = array_reverse(get_parent_contexts($context));
1298 $contexts = '('.implode(',', $contexts).')';
1299
1300 $SQL = "SELECT rc.* FROM {$CFG->prefix}role_capabilities rc, {$CFG->prefix}context c
1301 where rc.contextid in $contexts
1302 and rc.roleid = $roleid
1303 and rc.contextid = c.id
1304 ORDER BY c.level DESC, rc.capability DESC";
1305
1306 $records = get_records_sql($SQL);
1307
1308 $capabilities = array();
1309
1310 // We are traversing via reverse order.
1311 foreach ($records as $record) {
1312 // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit
1313 if (!isset($capabilities[$record->capability]) || $record->permission<-500) {
1314 $capabilities[$record->capability] = $record->permission;
1315 }
1316 }
1317 return $capabilities;
1318}
1319
1320
1321/**
1322 * Recursive function which, given a contextid, find all parent context ids,
1323 * and return the array in reverse order, i.e. parent first, then grand
1324 * parent, etc.
1325 * @param object $context
1326 * @return array()
1327 */
1328
1329
1330function get_parent_contexts($context) {
1331
1332 switch (context_level($context->id)) {
1333
1334 case CONTEXT_SYSTEM: // no parent
1335 return null;
1336 break;
1337
1338 case CONTEXT_PERSONAL:
1339 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1340 return array($parent->id);
1341 break;
1342
1343 case CONTEXT_USERID:
1344 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1345 return array($parent->id);
1346 break;
1347
1348 case CONTEXT_COURSECAT: // Coursecat -> coursecat or site
1349 $coursecat = get_record('course_categories','id',$context->instanceid);
1350 if ($coursecat->parent) { // return parent value if exist
1351 $parent = get_context_instance(CONTEXT_COURSECAT, $coursecat->parent);
1352 return array_merge(array($parent->id), get_parent_contexts($parent));
1353 } else { // else return site value
1354 $parent = get_context_instance(CONTEXT_SYSTEM, SITEID);
1355 return array($parent->id);
1356 }
1357 break;
1358
1359 case CONTEXT_COURSE: // 1 to 1 to course cat
1360 // find the course cat, and return its value
1361 $course = get_record('course','id',$context->instanceid);
1362 $parent = get_context_instance(CONTEXT_COURSECAT, $course->category);
1363 return array_merge(array($parent->id), get_parent_contexts($parent));
1364 break;
1365
1366 case CONTEXT_GROUP: // 1 to 1 to course
1367 $group = get_record('groups','id',$context->instanceid);
1368 $parent = get_context_instance(CONTEXT_COURSE, $group->courseid);
1369 return array_merge(array($parent->id), get_parent_contexts($parent));
1370 break;
1371
1372 case CONTEXT_MODULE: // 1 to 1 to course
1373 $cm = get_record('course_modules','id',$context->instanceid);
1374 $parent = get_context_instance(CONTEXT_COURSE, $cm->course);
1375 return array_merge(array($parent->id), get_parent_contexts($parent));
1376 break;
1377
1378 case CONTEXT_BLOCK: // 1 to 1 to course
1379 $block = get_record('block_instance','id',$context->instanceid);
1380 $parent = get_context_instance(CONTEXT_COURSE, $block->pageid); // needs check
1381 return array_merge(array($parent->id), get_parent_contexts($parent));
1382 break;
1383
1384 default:
1385 error ('This is an unknown context!');
1386 return false;
1387 }
1388
1389}
1390
1391
1392/**
1393 * This function gets the capability of a role in a given context.
1394 * It is needed when printing override forms.
1395 * @param int $contextid
1396 * @param int $roleid // no need? since role is used in extraction in $capability
1397 * @param string $capability
1398 * @param array $capabilities - array loaded using role_context_capabilities
1399 * @return int (allow, prevent, prohibit, inherit)
1400 */
1401
1402
1403function get_role_context_capability($contextid, $capability, $capabilities) {
1404 return $capabilities[$contextid][$capability];
1405}
1406
1407
1408// a big switch statement
1409function get_capability_string($capname) {
1410
1411 $names = split('/', $capname);
1412 $componentname = split(':', $names[1]);
1413 $componentname = $componentname[0];
1414 $capability = split(':', $capname);
1415 $capability = 'capability_'.$capability[1];
1416
1417 switch ($names[0]) {
1418 case 'mod':
1419 $string = get_string($capability, $componentname);
1420 break;
1421
1422 case 'block':
1423 $string = get_string($capability, 'block_'.$componentname);
1424 break;
1425
1426 case 'moodle':
1427 $string = get_string($capability);
1428 break;
1429
1430 case 'enrol':
1431 $string = get_string($capability, 'enrol_'.$componentname);
1432
1433 default:
1434 $string = get_string($capability);
1435 break;
1436
1437
1438 }
1439
1440 return $string;
1441}
1442
1443
1444// this gets the mod/block/course/core etc strings
1445function get_component_string($component, $contextlevel) {
1446
1447 switch ($contextlevel) {
1448
1449 case CONTEXT_SYSTEM:
1450 $string = get_string('system');
1451 break;
1452
1453 case CONTEXT_PERSONAL:
1454 $string = get_string('personal');
1455 break;
1456
1457 case CONTEXT_USERID:
1458 $string = get_string('users');
1459 break;
1460
1461 case CONTEXT_COURSECAT:
1462 $string = get_string('categories');
1463 break;
1464
1465 case CONTEXT_COURSE:
1466 $string = get_string('course');
1467 break;
1468
1469 case CONTEXT_GROUP:
1470 $string = get_string('group');
1471 break;
1472
1473 case CONTEXT_MODULE:
1474 $string = get_string('modulename', basename($component));
1475 break;
1476
1477 case CONTEXT_BLOCK:
1478 $string = get_string('blockname', 'block_'.$component.'.php');
1479 break;
1480
1481 default:
1482 error ('This is an unknown context!');
1483 return false;
1484
1485 }
1486
1487 return $string;
1488
1489}
1490?>