Automatically generated installer lang files
[moodle.git] / grade / lib.php
CommitLineData
cf72e2dd 1<?php
cf72e2dd 2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
cf72e2dd 17/**
18 * Functions used by gradebook plugins and reports.
19 *
a153c9f2 20 * @package core_grades
cf72e2dd 21 * @copyright 2009 Petr Skoda and Nicolas Connault
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
8ad36f4c 24
955a081e
MN
25require_once($CFG->libdir . '/gradelib.php');
26require_once($CFG->dirroot . '/grade/export/lib.php');
7a6b7acf 27
0f5660f7 28/**
29 * This class iterates over all users that are graded in a course.
b9f49659 30 * Returns detailed info about users and their grades.
cf72e2dd 31 *
32 * @author Petr Skoda <skodak@moodle.org>
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0f5660f7 34 */
35class graded_users_iterator {
728ff21b
AD
36
37 /**
38 * The couse whose users we are interested in
39 */
40 protected $course;
41
42 /**
43 * An array of grade items or null if only user data was requested
44 */
45 protected $grade_items;
46
47 /**
48 * The group ID we are interested in. 0 means all groups.
49 */
50 protected $groupid;
51
52 /**
53 * A recordset of graded users
54 */
55 protected $users_rs;
56
57 /**
58 * A recordset of user grades (grade_grade instances)
59 */
60 protected $grades_rs;
61
62 /**
63 * Array used when moving to next user while iterating through the grades recordset
64 */
65 protected $gradestack;
66
67 /**
68 * The first field of the users table by which the array of users will be sorted
69 */
70 protected $sortfield1;
71
72 /**
73 * Should sortfield1 be ASC or DESC
74 */
75 protected $sortorder1;
76
77 /**
78 * The second field of the users table by which the array of users will be sorted
79 */
80 protected $sortfield2;
81
82 /**
83 * Should sortfield2 be ASC or DESC
84 */
85 protected $sortorder2;
0f5660f7 86
78ab98bc
AD
87 /**
88 * Should users whose enrolment has been suspended be ignored?
89 */
90 protected $onlyactive = false;
91
0f5660f7 92 /**
93 * Constructor
cf72e2dd 94 *
95 * @param object $course A course object
96 * @param array $grade_items array of grade items, if not specified only user info returned
97 * @param int $groupid iterate only group users if present
d08bba83 98 * @param string $sortfield1 The first field of the users table by which the array of users will be sorted
99 * @param string $sortorder1 The order in which the first sorting field will be sorted (ASC or DESC)
100 * @param string $sortfield2 The second field of the users table by which the array of users will be sorted
101 * @param string $sortorder2 The order in which the second sorting field will be sorted (ASC or DESC)
0f5660f7 102 */
728ff21b 103 public function __construct($course, $grade_items=null, $groupid=0,
cf72e2dd 104 $sortfield1='lastname', $sortorder1='ASC',
105 $sortfield2='firstname', $sortorder2='ASC') {
0f5660f7 106 $this->course = $course;
107 $this->grade_items = $grade_items;
108 $this->groupid = $groupid;
d08bba83 109 $this->sortfield1 = $sortfield1;
110 $this->sortorder1 = $sortorder1;
111 $this->sortfield2 = $sortfield2;
112 $this->sortorder2 = $sortorder2;
0f5660f7 113
114 $this->gradestack = array();
115 }
116
117 /**
118 * Initialise the iterator
728ff21b 119 *
0f5660f7 120 * @return boolean success
121 */
d24832f9 122 public function init() {
123 global $CFG, $DB;
0f5660f7 124
125 $this->close();
126
955a081e 127 export_verify_grades($this->course->id);
0f5660f7 128 $course_item = grade_item::fetch_course_item($this->course->id);
129 if ($course_item->needsupdate) {
130 // can not calculate all final grades - sorry
131 return false;
132 }
133
656d17c2
AD
134 $coursecontext = get_context_instance(CONTEXT_COURSE, $this->course->id);
135 $relatedcontexts = get_related_contexts_string($coursecontext);
136
cf72e2dd 137 list($gradebookroles_sql, $params) =
cf717dc2 138 $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr');
78ab98bc 139 list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, '', 0, $this->onlyactive);
656d17c2
AD
140
141 $params = array_merge($params, $enrolledparams);
0f5660f7 142
143 if ($this->groupid) {
d24832f9 144 $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id";
e58fcb35 145 $groupwheresql = "AND gm.groupid = :groupid";
d24832f9 146 // $params contents: gradebookroles
e58fcb35 147 $params['groupid'] = $this->groupid;
0f5660f7 148 } else {
149 $groupsql = "";
150 $groupwheresql = "";
151 }
152
345674ca 153 if (empty($this->sortfield1)) {
154 // we must do some sorting even if not specified
155 $ofields = ", u.id AS usrt";
156 $order = "usrt ASC";
157
158 } else {
159 $ofields = ", u.$this->sortfield1 AS usrt1";
160 $order = "usrt1 $this->sortorder1";
161 if (!empty($this->sortfield2)) {
0febb12d 162 $ofields .= ", u.$this->sortfield2 AS usrt2";
345674ca 163 $order .= ", usrt2 $this->sortorder2";
164 }
165 if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') {
cf72e2dd 166 // user order MUST be the same in both queries,
167 // must include the only unique user->id if not already present
345674ca 168 $ofields .= ", u.id AS usrt";
169 $order .= ", usrt ASC";
170 }
171 }
172
d24832f9 173 // $params contents: gradebookroles and groupid (for $groupwheresql)
345674ca 174 $users_sql = "SELECT u.* $ofields
d24832f9 175 FROM {user} u
656d17c2 176 JOIN ($enrolledsql) je ON je.id = u.id
0f5660f7 177 $groupsql
656d17c2
AD
178 JOIN (
179 SELECT DISTINCT ra.userid
180 FROM {role_assignments} ra
181 WHERE ra.roleid $gradebookroles_sql
182 AND ra.contextid $relatedcontexts
183 ) rainner ON rainner.userid = u.id
184 WHERE u.deleted = 0
345674ca 185 $groupwheresql
186 ORDER BY $order";
d24832f9 187 $this->users_rs = $DB->get_recordset_sql($users_sql, $params);
0f5660f7 188
189 if (!empty($this->grade_items)) {
190 $itemids = array_keys($this->grade_items);
cf717dc2 191 list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items');
d24832f9 192 $params = array_merge($params, $grades_params);
656d17c2 193 // $params contents: gradebookroles, enrolledparams, groupid (for $groupwheresql) and itemids
0f5660f7 194
345674ca 195 $grades_sql = "SELECT g.* $ofields
d24832f9 196 FROM {grade_grades} g
656d17c2
AD
197 JOIN {user} u ON g.userid = u.id
198 JOIN ($enrolledsql) je ON je.id = u.id
0f5660f7 199 $groupsql
656d17c2
AD
200 JOIN (
201 SELECT DISTINCT ra.userid
202 FROM {role_assignments} ra
203 WHERE ra.roleid $gradebookroles_sql
204 AND ra.contextid $relatedcontexts
4f82032a 205 ) rainner ON rainner.userid = u.id
656d17c2
AD
206 WHERE u.deleted = 0
207 AND g.itemid $itemidsql
208 $groupwheresql
345674ca 209 ORDER BY $order, g.itemid ASC";
d24832f9 210 $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params);
345674ca 211 } else {
212 $this->grades_rs = false;
0f5660f7 213 }
345674ca 214
0f5660f7 215 return true;
216 }
217
218 /**
219 * Returns information about the next user
220 * @return mixed array of user info, all grades and feedback or null when no more users found
221 */
728ff21b 222 public function next_user() {
03cedd62 223 if (!$this->users_rs) {
0f5660f7 224 return false; // no users present
225 }
226
5c75a0a3 227 if (!$this->users_rs->valid()) {
345674ca 228 if ($current = $this->_pop()) {
229 // this is not good - user or grades updated between the two reads above :-(
230 }
231
0f5660f7 232 return false; // no more users
5c75a0a3 233 } else {
234 $user = $this->users_rs->current();
235 $this->users_rs->next();
0f5660f7 236 }
237
345674ca 238 // find grades of this user
0f5660f7 239 $grade_records = array();
240 while (true) {
241 if (!$current = $this->_pop()) {
242 break; // no more grades
243 }
244
5c75a0a3 245 if (empty($current->userid)) {
246 break;
247 }
248
345674ca 249 if ($current->userid != $user->id) {
250 // grade of the next user, we have all for this user
0f5660f7 251 $this->_push($current);
252 break;
253 }
254
255 $grade_records[$current->itemid] = $current;
256 }
257
258 $grades = array();
259 $feedbacks = array();
260
d08bba83 261 if (!empty($this->grade_items)) {
345674ca 262 foreach ($this->grade_items as $grade_item) {
6189c7b4
FM
263 if (!isset($feedbacks[$grade_item->id])) {
264 $feedbacks[$grade_item->id] = new stdClass();
265 }
345674ca 266 if (array_key_exists($grade_item->id, $grade_records)) {
267 $feedbacks[$grade_item->id]->feedback = $grade_records[$grade_item->id]->feedback;
268 $feedbacks[$grade_item->id]->feedbackformat = $grade_records[$grade_item->id]->feedbackformat;
269 unset($grade_records[$grade_item->id]->feedback);
270 unset($grade_records[$grade_item->id]->feedbackformat);
271 $grades[$grade_item->id] = new grade_grade($grade_records[$grade_item->id], false);
272 } else {
273 $feedbacks[$grade_item->id]->feedback = '';
274 $feedbacks[$grade_item->id]->feedbackformat = FORMAT_MOODLE;
cf72e2dd 275 $grades[$grade_item->id] =
276 new grade_grade(array('userid'=>$user->id, 'itemid'=>$grade_item->id), false);
345674ca 277 }
0f5660f7 278 }
279 }
280
ace9051c 281 $result = new stdClass();
0f5660f7 282 $result->user = $user;
283 $result->grades = $grades;
284 $result->feedbacks = $feedbacks;
0f5660f7 285 return $result;
286 }
287
288 /**
728ff21b 289 * Close the iterator, do not forget to call this function
0f5660f7 290 */
728ff21b 291 public function close() {
caffc55a 292 if ($this->users_rs) {
d24832f9 293 $this->users_rs->close();
caffc55a 294 $this->users_rs = null;
0f5660f7 295 }
caffc55a 296 if ($this->grades_rs) {
d24832f9 297 $this->grades_rs->close();
caffc55a 298 $this->grades_rs = null;
0f5660f7 299 }
300 $this->gradestack = array();
301 }
302
78ab98bc
AD
303 /**
304 * Should all enrolled users be exported or just those with an active enrolment?
305 *
306 * @param bool $onlyactive True to limit the export to users with an active enrolment
307 */
308 public function require_active_enrolment($onlyactive = true) {
309 if (!empty($this->users_rs)) {
310 debugging('Calling require_active_enrolment() has no effect unless you call init() again', DEBUG_DEVELOPER);
311 }
312 $this->onlyactive = $onlyactive;
313 }
314
cf72e2dd 315
0f5660f7 316 /**
728ff21b 317 * Add a grade_grade instance to the grade stack
cf72e2dd 318 *
319 * @param grade_grade $grade Grade object
320 *
321 * @return void
0f5660f7 322 */
728ff21b 323 private function _push($grade) {
0f5660f7 324 array_push($this->gradestack, $grade);
325 }
326
cf72e2dd 327
0f5660f7 328 /**
728ff21b 329 * Remove a grade_grade instance from the grade stack
cf72e2dd 330 *
728ff21b 331 * @return grade_grade current grade object
0f5660f7 332 */
728ff21b 333 private function _pop() {
d24832f9 334 global $DB;
0f5660f7 335 if (empty($this->gradestack)) {
8de7c8a7 336 if (empty($this->grades_rs) || !$this->grades_rs->valid()) {
cf72e2dd 337 return null; // no grades present
0f5660f7 338 }
339
7ce5df86
AD
340 $current = $this->grades_rs->current();
341
342 $this->grades_rs->next();
0f5660f7 343
7ce5df86 344 return $current;
0f5660f7 345 } else {
346 return array_pop($this->gradestack);
347 }
348 }
349}
350
d08bba83 351/**
352 * Print a selection popup form of the graded users in a course.
353 *
4d5059d4
SH
354 * @deprecated since 2.0
355 *
cf72e2dd 356 * @param int $course id of the course
d08bba83 357 * @param string $actionpage The page receiving the data from the popoup form
cf72e2dd 358 * @param int $userid id of the currently selected user (or 'all' if they are all selected)
359 * @param int $groupid id of requested group, 0 means all
360 * @param int $includeall bool include all option
361 * @param bool $return If true, will return the HTML, otherwise, will print directly
d08bba83 362 * @return null
363 */
7ac88172 364function print_graded_users_selector($course, $actionpage, $userid=0, $groupid=0, $includeall=true, $return=false) {
714b4745 365 global $CFG, $USER, $OUTPUT;
4d5059d4
SH
366 return $OUTPUT->render(grade_get_graded_users_select(substr($actionpage, 0, strpos($actionpage, '/')), $course, $userid, $groupid, $includeall));
367}
345674ca 368
4d5059d4 369function grade_get_graded_users_select($report, $course, $userid, $groupid, $includeall) {
f9510667
PS
370 global $USER;
371
879c99bb 372 if (is_null($userid)) {
373 $userid = $USER->id;
374 }
d08bba83 375
d08bba83 376 $menu = array(); // Will be a list of userid => user name
772229f3 377 $gui = new graded_users_iterator($course, null, $groupid);
d08bba83 378 $gui->init();
10f5c046 379 $label = get_string('selectauser', 'grades');
7ac88172 380 if ($includeall) {
879c99bb 381 $menu[0] = get_string('allusers', 'grades');
10f5c046 382 $label = get_string('selectalloroneuser', 'grades');
d08bba83 383 }
d08bba83 384 while ($userdata = $gui->next_user()) {
385 $user = $userdata->user;
386 $menu[$user->id] = fullname($user);
387 }
d08bba83 388 $gui->close();
389
7ac88172 390 if ($includeall) {
879c99bb 391 $menu[0] .= " (" . (count($menu) - 1) . ")";
392 }
4d5059d4 393 $select = new single_select(new moodle_url('/grade/report/'.$report.'/index.php', array('id'=>$course->id)), 'userid', $menu, $userid);
f8dab966
PS
394 $select->label = $label;
395 $select->formid = 'choosegradeuser';
4d5059d4 396 return $select;
d08bba83 397}
398
0610812a 399/**
400 * Print grading plugin selection popup form.
401 *
cf72e2dd 402 * @param array $plugin_info An array of plugins containing information for the selector
0610812a 403 * @param boolean $return return as string
cf72e2dd 404 *
0610812a 405 * @return nothing or string if $return true
406 */
f1a3e072 407function print_grade_plugin_selector($plugin_info, $active_type, $active_plugin, $return=false) {
714b4745 408 global $CFG, $OUTPUT, $PAGE;
dc482cfa 409
410 $menu = array();
411 $count = 0;
412 $active = '';
413
414 foreach ($plugin_info as $plugin_type => $plugins) {
415 if ($plugin_type == 'strings') {
416 continue;
417 }
418
7981d537 419 $first_plugin = reset($plugins);
dc482cfa 420
f1a3e072
PS
421 $sectionname = $plugin_info['strings'][$plugin_type];
422 $section = array();
d4dcfc6b 423
f1a3e072 424 foreach ($plugins as $plugin) {
d4dcfc6b 425 $link = $plugin->link->out(false);
f1a3e072
PS
426 $section[$link] = $plugin->string;
427 $count++;
428 if ($plugin_type === $active_type and $plugin->id === $active_plugin) {
429 $active = $link;
dc482cfa 430 }
431 }
f1a3e072
PS
432
433 if ($section) {
434 $menu[] = array($sectionname=>$section);
435 }
dc482cfa 436 }
437
cf72e2dd 438 // finally print/return the popup form
dc482cfa 439 if ($count > 1) {
f1a3e072 440 $select = new url_select($menu, $active, null, 'choosepluginreport');
aa15bd56 441 $select->set_label(get_string('gradereport', 'grades'), array('class' => 'accesshide'));
7981d537 442 if ($return) {
f1a3e072 443 return $OUTPUT->render($select);
7981d537 444 } else {
f1a3e072 445 echo $OUTPUT->render($select);
7981d537 446 }
dc482cfa 447 } else {
448 // only one option - no plugin selector needed
449 return '';
450 }
451}
452
453/**
454 * Print grading plugin selection tab-based navigation.
455 *
cf72e2dd 456 * @param string $active_type type of plugin on current page - import, export, report or edit
457 * @param string $active_plugin active plugin type - grader, user, cvs, ...
458 * @param array $plugin_info Array of plugins
dc482cfa 459 * @param boolean $return return as string
cf72e2dd 460 *
dc482cfa 461 * @return nothing or string if $return true
462 */
b827784e 463function grade_print_tabs($active_type, $active_plugin, $plugin_info, $return=false) {
dc482cfa 464 global $CFG, $COURSE;
1c1f64a2 465
e1513ca9 466 if (!isset($currenttab)) { //TODO: this is weird
dc482cfa 467 $currenttab = '';
468 }
469
470 $tabs = array();
471 $top_row = array();
472 $bottom_row = array();
473 $inactive = array($active_plugin);
474 $activated = array();
475
476 $count = 0;
477 $active = '';
478
479 foreach ($plugin_info as $plugin_type => $plugins) {
480 if ($plugin_type == 'strings') {
481 continue;
482 }
483
484 // If $plugins is actually the definition of a child-less parent link:
cf72e2dd 485 if (!empty($plugins->id)) {
486 $string = $plugins->string;
487 if (!empty($plugin_info[$active_type]->parent)) {
488 $string = $plugin_info[$active_type]->parent->string;
27eef3bb 489 }
490
cf72e2dd 491 $top_row[] = new tabobject($plugin_type, $plugins->link, $string);
dc482cfa 492 continue;
493 }
494
495 $first_plugin = reset($plugins);
cf72e2dd 496 $url = $first_plugin->link;
dc482cfa 497
498 if ($plugin_type == 'report') {
499 $url = $CFG->wwwroot.'/grade/report/index.php?id='.$COURSE->id;
500 }
501
502 $top_row[] = new tabobject($plugin_type, $url, $plugin_info['strings'][$plugin_type]);
503
504 if ($active_type == $plugin_type) {
505 foreach ($plugins as $plugin) {
cf72e2dd 506 $bottom_row[] = new tabobject($plugin->id, $plugin->link, $plugin->string);
507 if ($plugin->id == $active_plugin) {
508 $inactive = array($plugin->id);
dc482cfa 509 }
510 }
511 }
512 }
513
514 $tabs[] = $top_row;
515 $tabs[] = $bottom_row;
516
517 if ($return) {
518 return print_tabs($tabs, $active_type, $inactive, $activated, true);
519 } else {
520 print_tabs($tabs, $active_type, $inactive, $activated);
521 }
522}
523
cf72e2dd 524/**
525 * grade_get_plugin_info
526 *
527 * @param int $courseid The course id
528 * @param string $active_type type of plugin on current page - import, export, report or edit
529 * @param string $active_plugin active plugin type - grader, user, cvs, ...
530 *
531 * @return array
532 */
dc482cfa 533function grade_get_plugin_info($courseid, $active_type, $active_plugin) {
f7fcf4cd 534 global $CFG, $SITE;
cbff94ba 535
3af29899 536 $context = get_context_instance(CONTEXT_COURSE, $courseid);
cbff94ba 537
dc482cfa 538 $plugin_info = array();
6e2f3121 539 $count = 0;
3af29899 540 $active = '';
dc482cfa 541 $url_prefix = $CFG->wwwroot . '/grade/';
542
543 // Language strings
4d5059d4 544 $plugin_info['strings'] = grade_helper::get_plugin_strings();
b827784e 545
4d5059d4
SH
546 if ($reports = grade_helper::get_plugins_reports($courseid)) {
547 $plugin_info['report'] = $reports;
b827784e 548 }
c46aeeab 549
f7fcf4cd
AD
550 //showing grade categories and items make no sense if we're not within a course
551 if ($courseid!=$SITE->id) {
552 if ($edittree = grade_helper::get_info_edit_structure($courseid)) {
553 $plugin_info['edittree'] = $edittree;
554 }
04678d8e 555 }
b827784e 556
4d5059d4
SH
557 if ($scale = grade_helper::get_info_scales($courseid)) {
558 $plugin_info['scale'] = array('view'=>$scale);
cbff94ba 559 }
dc482cfa 560
4d5059d4
SH
561 if ($outcomes = grade_helper::get_info_outcomes($courseid)) {
562 $plugin_info['outcome'] = $outcomes;
cbff94ba 563 }
cbff94ba 564
4d5059d4
SH
565 if ($letters = grade_helper::get_info_letters($courseid)) {
566 $plugin_info['letter'] = $letters;
cbff94ba 567 }
4d5059d4
SH
568
569 if ($imports = grade_helper::get_plugins_import($courseid)) {
570 $plugin_info['import'] = $imports;
281ffa4a 571 }
c46aeeab 572
4d5059d4
SH
573 if ($exports = grade_helper::get_plugins_export($courseid)) {
574 $plugin_info['export'] = $exports;
281ffa4a 575 }
281ffa4a 576
4d5059d4
SH
577 foreach ($plugin_info as $plugin_type => $plugins) {
578 if (!empty($plugins->id) && $active_plugin == $plugins->id) {
579 $plugin_info['strings']['active_plugin_str'] = $plugins->string;
580 break;
281ffa4a 581 }
4d5059d4
SH
582 foreach ($plugins as $plugin) {
583 if (is_a($plugin, 'grade_plugin_info')) {
584 if ($active_plugin == $plugin->id) {
585 $plugin_info['strings']['active_plugin_str'] = $plugin->string;
586 }
3af29899 587 }
281ffa4a 588 }
cbff94ba 589 }
c46aeeab 590
f7fcf4cd
AD
591 //hide course settings if we're not in a course
592 if ($courseid!=$SITE->id) {
593 if ($setting = grade_helper::get_info_manage_settings($courseid)) {
594 $plugin_info['settings'] = array('course'=>$setting);
595 }
281ffa4a 596 }
cbff94ba 597
4d5059d4
SH
598 // Put preferences last
599 if ($preferences = grade_helper::get_plugins_report_preferences($courseid)) {
600 $plugin_info['preferences'] = $preferences;
27eef3bb 601 }
602
dc482cfa 603 foreach ($plugin_info as $plugin_type => $plugins) {
cf72e2dd 604 if (!empty($plugins->id) && $active_plugin == $plugins->id) {
605 $plugin_info['strings']['active_plugin_str'] = $plugins->string;
dc482cfa 606 break;
78ad5f3f 607 }
dc482cfa 608 foreach ($plugins as $plugin) {
cf72e2dd 609 if (is_a($plugin, 'grade_plugin_info')) {
610 if ($active_plugin == $plugin->id) {
611 $plugin_info['strings']['active_plugin_str'] = $plugin->string;
612 }
78ad5f3f 613 }
78ad5f3f 614 }
dc482cfa 615 }
78ad5f3f 616
dc482cfa 617 return $plugin_info;
618}
284abb09 619
cf72e2dd 620/**
621 * A simple class containing info about grade plugins.
622 * Can be subclassed for special rules
623 *
a153c9f2 624 * @package core_grades
cf72e2dd 625 * @copyright 2009 Nicolas Connault
626 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
627 */
628class grade_plugin_info {
629 /**
630 * A unique id for this plugin
631 *
632 * @var mixed
633 */
634 public $id;
635 /**
636 * A URL to access this plugin
637 *
638 * @var mixed
639 */
640 public $link;
641 /**
642 * The name of this plugin
643 *
644 * @var mixed
645 */
646 public $string;
647 /**
648 * Another grade_plugin_info object, parent of the current one
649 *
650 * @var mixed
651 */
652 public $parent;
653
654 /**
655 * Constructor
656 *
657 * @param int $id A unique id for this plugin
658 * @param string $link A URL to access this plugin
659 * @param string $string The name of this plugin
660 * @param object $parent Another grade_plugin_info object, parent of the current one
661 *
662 * @return void
663 */
664 public function __construct($id, $link, $string, $parent=null) {
665 $this->id = $id;
666 $this->link = $link;
667 $this->string = $string;
668 $this->parent = $parent;
669 }
670}
671
de0300ea 672/**
673 * Prints the page headers, breadcrumb trail, page heading, (optional) dropdown navigation menu and
674 * (optional) navigation tabs for any gradebook page. All gradebook pages MUST use these functions
675 * in favour of the usual print_header(), print_header_simple(), print_heading() etc.
676 * !IMPORTANT! Use of tabs.php file in gradebook pages is forbidden unless tabs are switched off at
677 * the site level for the gradebook ($CFG->grade_navmethod = GRADE_NAVMETHOD_DROPDOWN).
678 *
cf72e2dd 679 * @param int $courseid Course id
680 * @param string $active_type The type of the current page (report, settings,
681 * import, export, scales, outcomes, letters)
682 * @param string $active_plugin The plugin of the current page (grader, fullview etc...)
683 * @param string $heading The heading of the page. Tries to guess if none is given
de0300ea 684 * @param boolean $return Whether to return (true) or echo (false) the HTML generated by this function
cf72e2dd 685 * @param string $bodytags Additional attributes that will be added to the <body> tag
686 * @param string $buttons Additional buttons to display on the page
5625d33d 687 * @param boolean $shownavigation should the gradebook navigation drop down (or tabs) be shown?
de0300ea 688 *
689 * @return string HTML code or nothing if $return == false
690 */
cf72e2dd 691function print_grade_page_head($courseid, $active_type, $active_plugin=null,
34a2777c 692 $heading = false, $return=false,
2821c495 693 $buttons=false, $shownavigation=true) {
4d5059d4 694 global $CFG, $OUTPUT, $PAGE;
c46aeeab 695
dc482cfa 696 $plugin_info = grade_get_plugin_info($courseid, $active_type, $active_plugin);
c46aeeab 697
dc482cfa 698 // Determine the string of the active plugin
699 $stractive_plugin = ($active_plugin) ? $plugin_info['strings']['active_plugin_str'] : $heading;
700 $stractive_type = $plugin_info['strings'][$active_type];
e0724506 701
cf72e2dd 702 if (empty($plugin_info[$active_type]->id) || !empty($plugin_info[$active_type]->parent)) {
4d5059d4
SH
703 $title = $PAGE->course->fullname.': ' . $stractive_type . ': ' . $stractive_plugin;
704 } else {
705 $title = $PAGE->course->fullname.': ' . $stractive_plugin;
c4c97a6d 706 }
707
367a75fa
SH
708 if ($active_type == 'report') {
709 $PAGE->set_pagelayout('report');
710 } else {
711 $PAGE->set_pagelayout('admin');
712 }
4d5059d4 713 $PAGE->set_title(get_string('grades') . ': ' . $stractive_type);
f3df5e14 714 $PAGE->set_heading($title);
4d5059d4
SH
715 if ($buttons instanceof single_button) {
716 $buttons = $OUTPUT->render($buttons);
717 }
f3df5e14 718 $PAGE->set_button($buttons);
4d5059d4
SH
719 grade_extend_settings($plugin_info, $courseid);
720
f3df5e14 721 $returnval = $OUTPUT->header();
722 if (!$return) {
723 echo $returnval;
724 }
dc482cfa 725
726 // Guess heading if not given explicitly
727 if (!$heading) {
728 $heading = $stractive_plugin;
729 }
730
2821c495
AD
731 if ($shownavigation) {
732 if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_DROPDOWN) {
733 $returnval .= print_grade_plugin_selector($plugin_info, $active_type, $active_plugin, $return);
734 }
5625d33d
AD
735
736 if ($return) {
737 $returnval .= $OUTPUT->heading($heading);
738 } else {
e59a1dd6 739 echo $OUTPUT->heading($heading);
5625d33d
AD
740 }
741
2821c495
AD
742 if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_TABS) {
743 $returnval .= grade_print_tabs($active_type, $active_plugin, $plugin_info, $return);
744 }
8354e716 745 }
dc482cfa 746
747 if ($return) {
748 return $returnval;
6e2f3121 749 }
cbff94ba 750}
751
0610812a 752/**
7a6b7acf 753 * Utility class used for return tracking when using edit and other forms in grade plugins
cf72e2dd 754 *
a153c9f2 755 * @package core_grades
cf72e2dd 756 * @copyright 2009 Nicolas Connault
757 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0610812a 758 */
3af29899 759class grade_plugin_return {
d24832f9 760 public $type;
761 public $plugin;
762 public $courseid;
763 public $userid;
764 public $page;
281ffa4a 765
0610812a 766 /**
767 * Constructor
cf72e2dd 768 *
0610812a 769 * @param array $params - associative array with return parameters, if null parameter are taken from _GET or _POST
770 */
cf72e2dd 771 public function grade_plugin_return($params = null) {
3af29899 772 if (empty($params)) {
773 $this->type = optional_param('gpr_type', null, PARAM_SAFEDIR);
aff24313 774 $this->plugin = optional_param('gpr_plugin', null, PARAM_PLUGIN);
3af29899 775 $this->courseid = optional_param('gpr_courseid', null, PARAM_INT);
776 $this->userid = optional_param('gpr_userid', null, PARAM_INT);
777 $this->page = optional_param('gpr_page', null, PARAM_INT);
a983b6ec 778
a983b6ec 779 } else {
3af29899 780 foreach ($params as $key=>$value) {
4cc977a6 781 if (property_exists($this, $key)) {
3af29899 782 $this->$key = $value;
783 }
cbff94ba 784 }
785 }
6cd8c592 786 }
787
0610812a 788 /**
789 * Returns return parameters as options array suitable for buttons.
790 * @return array options
791 */
d24832f9 792 public function get_options() {
7a6b7acf 793 if (empty($this->type)) {
3af29899 794 return array();
865e9a82 795 }
6cd8c592 796
3af29899 797 $params = array();
6cd8c592 798
7a6b7acf 799 if (!empty($this->plugin)) {
800 $params['plugin'] = $this->plugin;
801 }
6cd8c592 802
3af29899 803 if (!empty($this->courseid)) {
804 $params['id'] = $this->courseid;
6cd8c592 805 }
9c61ba4d 806
3af29899 807 if (!empty($this->userid)) {
808 $params['userid'] = $this->userid;
9c61ba4d 809 }
9c61ba4d 810
3af29899 811 if (!empty($this->page)) {
812 $params['page'] = $this->page;
cbff94ba 813 }
865e9a82 814
3af29899 815 return $params;
cbff94ba 816 }
cbff94ba 817
0610812a 818 /**
819 * Returns return url
cf72e2dd 820 *
0610812a 821 * @param string $default default url when params not set
cf72e2dd 822 * @param array $extras Extra URL parameters
823 *
0610812a 824 * @return string url
825 */
d24832f9 826 public function get_return_url($default, $extras=null) {
3af29899 827 global $CFG;
cbff94ba 828
3af29899 829 if (empty($this->type) or empty($this->plugin)) {
830 return $default;
cbff94ba 831 }
832
65dd61bd 833 $url = $CFG->wwwroot.'/grade/'.$this->type.'/'.$this->plugin.'/index.php';
834 $glue = '?';
cbff94ba 835
3af29899 836 if (!empty($this->courseid)) {
837 $url .= $glue.'id='.$this->courseid;
838 $glue = '&amp;';
cbff94ba 839 }
cbff94ba 840
3af29899 841 if (!empty($this->userid)) {
842 $url .= $glue.'userid='.$this->userid;
843 $glue = '&amp;';
cbff94ba 844 }
7e2d7c92 845
3af29899 846 if (!empty($this->page)) {
847 $url .= $glue.'page='.$this->page;
65dd61bd 848 $glue = '&amp;';
849 }
850
851 if (!empty($extras)) {
cf72e2dd 852 foreach ($extras as $key=>$value) {
65dd61bd 853 $url .= $glue.$key.'='.$value;
854 $glue = '&amp;';
7a6b7acf 855 }
cbff94ba 856 }
cbff94ba 857
3af29899 858 return $url;
cbff94ba 859 }
cbff94ba 860
0610812a 861 /**
862 * Returns string with hidden return tracking form elements.
863 * @return string
864 */
d24832f9 865 public function get_form_fields() {
7a6b7acf 866 if (empty($this->type)) {
3af29899 867 return '';
cbff94ba 868 }
cbff94ba 869
3af29899 870 $result = '<input type="hidden" name="gpr_type" value="'.$this->type.'" />';
7a6b7acf 871
872 if (!empty($this->plugin)) {
873 $result .= '<input type="hidden" name="gpr_plugin" value="'.$this->plugin.'" />';
874 }
0ca5abd6 875
3af29899 876 if (!empty($this->courseid)) {
877 $result .= '<input type="hidden" name="gpr_courseid" value="'.$this->courseid.'" />';
cbff94ba 878 }
cbff94ba 879
3af29899 880 if (!empty($this->userid)) {
881 $result .= '<input type="hidden" name="gpr_userid" value="'.$this->userid.'" />';
cbff94ba 882 }
cbff94ba 883
3af29899 884 if (!empty($this->page)) {
885 $result .= '<input type="hidden" name="gpr_page" value="'.$this->page.'" />';
cbff94ba 886 }
887 }
cbff94ba 888
0610812a 889 /**
890 * Add hidden elements into mform
cf72e2dd 891 *
892 * @param object &$mform moodle form object
893 *
0610812a 894 * @return void
895 */
d24832f9 896 public function add_mform_elements(&$mform) {
7a6b7acf 897 if (empty($this->type)) {
3af29899 898 return;
cbff94ba 899 }
cbff94ba 900
3af29899 901 $mform->addElement('hidden', 'gpr_type', $this->type);
902 $mform->setType('gpr_type', PARAM_SAFEDIR);
cbff94ba 903
7a6b7acf 904 if (!empty($this->plugin)) {
905 $mform->addElement('hidden', 'gpr_plugin', $this->plugin);
aff24313 906 $mform->setType('gpr_plugin', PARAM_PLUGIN);
7a6b7acf 907 }
97033c86 908
3af29899 909 if (!empty($this->courseid)) {
910 $mform->addElement('hidden', 'gpr_courseid', $this->courseid);
911 $mform->setType('gpr_courseid', PARAM_INT);
cbff94ba 912 }
cbff94ba 913
3af29899 914 if (!empty($this->userid)) {
915 $mform->addElement('hidden', 'gpr_userid', $this->userid);
916 $mform->setType('gpr_userid', PARAM_INT);
cbff94ba 917 }
cbff94ba 918
3af29899 919 if (!empty($this->page)) {
920 $mform->addElement('hidden', 'gpr_page', $this->page);
921 $mform->setType('gpr_page', PARAM_INT);
cbff94ba 922 }
923 }
281ffa4a 924
0610812a 925 /**
926 * Add return tracking params into url
cf72e2dd 927 *
1c1f64a2 928 * @param moodle_url $url A URL
cf72e2dd 929 *
6ef4878b 930 * @return string $url with return tracking params
0610812a 931 */
1c1f64a2 932 public function add_url_params(moodle_url $url) {
7a6b7acf 933 if (empty($this->type)) {
3af29899 934 return $url;
cbff94ba 935 }
5609f9e6 936
1c1f64a2 937 $url->param('gpr_type', $this->type);
cbff94ba 938
7a6b7acf 939 if (!empty($this->plugin)) {
1c1f64a2 940 $url->param('gpr_plugin', $this->plugin);
7a6b7acf 941 }
cbff94ba 942
3af29899 943 if (!empty($this->courseid)) {
1c1f64a2 944 $url->param('gpr_courseid' ,$this->courseid);
cbff94ba 945 }
cbff94ba 946
3af29899 947 if (!empty($this->userid)) {
1c1f64a2 948 $url->param('gpr_userid', $this->userid);
cbff94ba 949 }
0a8a95c9 950
3af29899 951 if (!empty($this->page)) {
1c1f64a2 952 $url->param('gpr_page', $this->page);
0a8a95c9 953 }
5a412dbf 954
3af29899 955 return $url;
5a412dbf 956 }
5a412dbf 957}
7a6b7acf 958
826c5f86 959/**
960 * Function central to gradebook for building and printing the navigation (breadcrumb trail).
cf72e2dd 961 *
826c5f86 962 * @param string $path The path of the calling script (using __FILE__?)
963 * @param string $pagename The language string to use as the last part of the navigation (non-link)
cf72e2dd 964 * @param mixed $id Either a plain integer (assuming the key is 'id') or
965 * an array of keys and values (e.g courseid => $courseid, itemid...)
966 *
826c5f86 967 * @return string
968 */
969function grade_build_nav($path, $pagename=null, $id=null) {
f3df5e14 970 global $CFG, $COURSE, $PAGE;
826c5f86 971
972 $strgrades = get_string('grades', 'grades');
973
974 // Parse the path and build navlinks from its elements
975 $dirroot_length = strlen($CFG->dirroot) + 1; // Add 1 for the first slash
976 $path = substr($path, $dirroot_length);
977 $path = str_replace('\\', '/', $path);
978
979 $path_elements = explode('/', $path);
980
981 $path_elements_count = count($path_elements);
982
826c5f86 983 // First link is always 'grade'
a6855934 984 $PAGE->navbar->add($strgrades, new moodle_url('/grade/index.php', array('id'=>$COURSE->id)));
826c5f86 985
f3df5e14 986 $link = null;
826c5f86 987 $numberofelements = 3;
988
989 // Prepare URL params string
f3df5e14 990 $linkparams = array();
826c5f86 991 if (!is_null($id)) {
992 if (is_array($id)) {
993 foreach ($id as $idkey => $idvalue) {
f3df5e14 994 $linkparams[$idkey] = $idvalue;
826c5f86 995 }
996 } else {
f3df5e14 997 $linkparams['id'] = $id;
826c5f86 998 }
999 }
1000
1001 $navlink4 = null;
1002
0f78c4de 1003 // Remove file extensions from filenames
1004 foreach ($path_elements as $key => $filename) {
1005 $path_elements[$key] = str_replace('.php', '', $filename);
1006 }
1007
826c5f86 1008 // Second level links
1009 switch ($path_elements[1]) {
1010 case 'edit': // No link
1011 if ($path_elements[3] != 'index.php') {
1012 $numberofelements = 4;
1013 }
1014 break;
1015 case 'import': // No link
1016 break;
1017 case 'export': // No link
1018 break;
1019 case 'report':
1020 // $id is required for this link. Do not print it if $id isn't given
1021 if (!is_null($id)) {
a6855934 1022 $link = new moodle_url('/grade/report/index.php', $linkparams);
826c5f86 1023 }
1024
1025 if ($path_elements[2] == 'grader') {
1026 $numberofelements = 4;
1027 }
1028 break;
1029
1030 default:
1031 // If this element isn't among the ones already listed above, it isn't supported, throw an error.
cf72e2dd 1032 debugging("grade_build_nav() doesn't support ". $path_elements[1] .
1033 " as the second path element after 'grade'.");
826c5f86 1034 return false;
1035 }
f3df5e14 1036 $PAGE->navbar->add(get_string($path_elements[1], 'grades'), $link);
826c5f86 1037
1038 // Third level links
1039 if (empty($pagename)) {
1040 $pagename = get_string($path_elements[2], 'grades');
1041 }
1042
1043 switch ($numberofelements) {
1044 case 3:
f3df5e14 1045 $PAGE->navbar->add($pagename, $link);
826c5f86 1046 break;
1047 case 4:
826c5f86 1048 if ($path_elements[2] == 'grader' AND $path_elements[3] != 'index.php') {
b5e7b2bf 1049 $PAGE->navbar->add(get_string('pluginname', 'gradereport_grader'), new moodle_url('/grade/report/grader/index.php', $linkparams));
826c5f86 1050 }
f3df5e14 1051 $PAGE->navbar->add($pagename);
826c5f86 1052 break;
1053 }
826c5f86 1054
f3df5e14 1055 return '';
d4795a07 1056}
7a6b7acf 1057
e98871a2 1058/**
6cc3e350 1059 * General structure representing grade items in course
cf72e2dd 1060 *
a153c9f2 1061 * @package core_grades
cf72e2dd 1062 * @copyright 2009 Nicolas Connault
1063 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
e98871a2 1064 */
6cc3e350 1065class grade_structure {
d24832f9 1066 public $context;
e98871a2 1067
d24832f9 1068 public $courseid;
e98871a2 1069
73ca5f01 1070 /**
1071 * Reference to modinfo for current course (for performance, to save
1072 * retrieving it from courseid every time). Not actually set except for
1073 * the grade_tree type.
1074 * @var course_modinfo
1075 */
1076 public $modinfo;
1077
e98871a2 1078 /**
1079 * 1D array of grade items only
1080 */
d24832f9 1081 public $items;
e98871a2 1082
6391ebe7 1083 /**
6cc3e350 1084 * Returns icon of element
cf72e2dd 1085 *
1086 * @param array &$element An array representing an element in the grade_tree
1087 * @param bool $spacerifnone return spacer if no icon found
1088 *
6cc3e350 1089 * @return string icon or spacer
6391ebe7 1090 */
d24832f9 1091 public function get_element_icon(&$element, $spacerifnone=false) {
6b608f8f 1092 global $CFG, $OUTPUT;
559276b1 1093 require_once $CFG->libdir.'/filelib.php';
6cc3e350 1094
1095 switch ($element['type']) {
1096 case 'item':
1097 case 'courseitem':
1098 case 'categoryitem':
cf72e2dd 1099 $is_course = $element['object']->is_course_item();
1100 $is_category = $element['object']->is_category_item();
1101 $is_scale = $element['object']->gradetype == GRADE_TYPE_SCALE;
1102 $is_value = $element['object']->gradetype == GRADE_TYPE_VALUE;
0077f571 1103 $is_outcome = !empty($element['object']->outcomeid);
cf72e2dd 1104
6cc3e350 1105 if ($element['object']->is_calculated()) {
dc482cfa 1106 $strcalc = get_string('calculatedgrade', 'grades');
b5d0cafc 1107 return '<img src="'.$OUTPUT->pix_url('i/calc') . '" class="icon itemicon" title="'.
cf72e2dd 1108 s($strcalc).'" alt="'.s($strcalc).'"/>';
6cc3e350 1109
cf72e2dd 1110 } else if (($is_course or $is_category) and ($is_scale or $is_value)) {
6cc3e350 1111 if ($category = $element['object']->get_item_category()) {
1112 switch ($category->aggregation) {
1113 case GRADE_AGGREGATE_MEAN:
1114 case GRADE_AGGREGATE_MEDIAN:
1115 case GRADE_AGGREGATE_WEIGHTED_MEAN:
1426edac 1116 case GRADE_AGGREGATE_WEIGHTED_MEAN2:
6cc3e350 1117 case GRADE_AGGREGATE_EXTRACREDIT_MEAN:
dc482cfa 1118 $stragg = get_string('aggregation', 'grades');
b5d0cafc 1119 return '<img src="'.$OUTPUT->pix_url('i/agg_mean') . '" ' .
cf72e2dd 1120 'class="icon itemicon" title="'.s($stragg).'" alt="'.s($stragg).'"/>';
0758a08e 1121 case GRADE_AGGREGATE_SUM:
dc482cfa 1122 $stragg = get_string('aggregation', 'grades');
b5d0cafc 1123 return '<img src="'.$OUTPUT->pix_url('i/agg_sum') . '" ' .
cf72e2dd 1124 'class="icon itemicon" title="'.s($stragg).'" alt="'.s($stragg).'"/>';
6cc3e350 1125 }
1126 }
1127
1128 } else if ($element['object']->itemtype == 'mod') {
0077f571
AD
1129 //prevent outcomes being displaying the same icon as the activity they are attached to
1130 if ($is_outcome) {
1131 $stroutcome = s(get_string('outcome', 'grades'));
1132 return '<img src="'.$OUTPUT->pix_url('i/outcomes') . '" ' .
1133 'class="icon itemicon" title="'.$stroutcome.
1134 '" alt="'.$stroutcome.'"/>';
1135 } else {
1136 $strmodname = get_string('modulename', $element['object']->itemmodule);
1137 return '<img src="'.$OUTPUT->pix_url('icon',
e63f88c9 1138 $element['object']->itemmodule) . '" ' .
cf72e2dd 1139 'class="icon itemicon" title="' .s($strmodname).
1140 '" alt="' .s($strmodname).'"/>';
0077f571 1141 }
6cc3e350 1142 } else if ($element['object']->itemtype == 'manual') {
1143 if ($element['object']->is_outcome_item()) {
dc482cfa 1144 $stroutcome = get_string('outcome', 'grades');
b5d0cafc 1145 return '<img src="'.$OUTPUT->pix_url('i/outcomes') . '" ' .
cf72e2dd 1146 'class="icon itemicon" title="'.s($stroutcome).
1147 '" alt="'.s($stroutcome).'"/>';
6cc3e350 1148 } else {
dc482cfa 1149 $strmanual = get_string('manualitem', 'grades');
b5d0cafc 1150 return '<img src="'.$OUTPUT->pix_url('t/manual_item') . '" '.
cf72e2dd 1151 'class="icon itemicon" title="'.s($strmanual).
1152 '" alt="'.s($strmanual).'"/>';
6cc3e350 1153 }
1154 }
1155 break;
1156
1157 case 'category':
dc482cfa 1158 $strcat = get_string('category', 'grades');
559276b1 1159 return '<img src="'.$OUTPUT->pix_url(file_folder_icon()) . '" class="icon itemicon" ' .
cf72e2dd 1160 'title="'.s($strcat).'" alt="'.s($strcat).'" />';
6cc3e350 1161 }
1162
1163 if ($spacerifnone) {
b11681e0 1164 return $OUTPUT->spacer().' ';
6cc3e350 1165 } else {
1166 return '';
1167 }
1168 }
6391ebe7 1169
e98871a2 1170 /**
6cc3e350 1171 * Returns name of element optionally with icon and link
cf72e2dd 1172 *
1173 * @param array &$element An array representing an element in the grade_tree
1174 * @param bool $withlink Whether or not this header has a link
1175 * @param bool $icon Whether or not to display an icon with this header
1176 * @param bool $spacerifnone return spacer if no icon found
1177 *
1178 * @return string header
e98871a2 1179 */
d24832f9 1180 public function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false) {
6cc3e350 1181 $header = '';
1182
1183 if ($icon) {
1184 $header .= $this->get_element_icon($element, $spacerifnone);
1185 }
1186
dc482cfa 1187 $header .= $element['object']->get_name();
6cc3e350 1188
cf72e2dd 1189 if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and
1190 $element['type'] != 'courseitem') {
6cc3e350 1191 return $header;
1192 }
1193
73ca5f01 1194 if ($withlink) {
1195 $url = $this->get_activity_link($element);
1196 if ($url) {
ace9051c 1197 $a = new stdClass();
45a639d5 1198 $a->name = get_string('modulename', $element['object']->itemmodule);
dc482cfa 1199 $title = get_string('linktoactivity', 'grades', $a);
6cc3e350 1200
73ca5f01 1201 $header = html_writer::link($url, $header, array('title' => $title));
46d1af82 1202 }
6cc3e350 1203 }
1204
1205 return $header;
1206 }
1207
73ca5f01 1208 private function get_activity_link($element) {
1209 global $CFG;
eff314b0
DM
1210 /** @var array static cache of the grade.php file existence flags */
1211 static $hasgradephp = array();
73ca5f01 1212
1213 $itemtype = $element['object']->itemtype;
1214 $itemmodule = $element['object']->itemmodule;
1215 $iteminstance = $element['object']->iteminstance;
25ab1d50 1216 $itemnumber = $element['object']->itemnumber;
73ca5f01 1217
1218 // Links only for module items that have valid instance, module and are
1219 // called from grade_tree with valid modinfo
1220 if ($itemtype != 'mod' || !$iteminstance || !$itemmodule || !$this->modinfo) {
1221 return null;
1222 }
1223
1224 // Get $cm efficiently and with visibility information using modinfo
1225 $instances = $this->modinfo->get_instances();
1226 if (empty($instances[$itemmodule][$iteminstance])) {
1227 return null;
1228 }
1229 $cm = $instances[$itemmodule][$iteminstance];
1230
1231 // Do not add link if activity is not visible to the current user
1232 if (!$cm->uservisible) {
1233 return null;
1234 }
1235
eff314b0
DM
1236 if (!array_key_exists($itemmodule, $hasgradephp)) {
1237 if (file_exists($CFG->dirroot . '/mod/' . $itemmodule . '/grade.php')) {
1238 $hasgradephp[$itemmodule] = true;
1239 } else {
1240 $hasgradephp[$itemmodule] = false;
1241 }
1242 }
1243
73ca5f01 1244 // If module has grade.php, link to that, otherwise view.php
eff314b0 1245 if ($hasgradephp[$itemmodule]) {
c97933ff
MG
1246 $args = array('id' => $cm->id, 'itemnumber' => $itemnumber);
1247 if (isset($element['userid'])) {
1248 $args['userid'] = $element['userid'];
1249 }
1250 return new moodle_url('/mod/' . $itemmodule . '/grade.php', $args);
73ca5f01 1251 } else {
1252 return new moodle_url('/mod/' . $itemmodule . '/view.php', array('id' => $cm->id));
1253 }
1254 }
1255
bb17ac1e
DM
1256 /**
1257 * Returns URL of a page that is supposed to contain detailed grade analysis
1258 *
bb17ac1e
DM
1259 * At the moment, only activity modules are supported. The method generates link
1260 * to the module's file grade.php with the parameters id (cmid), itemid, itemnumber,
eff314b0 1261 * gradeid and userid. If the grade.php does not exist, null is returned.
bb17ac1e
DM
1262 *
1263 * @return moodle_url|null URL or null if unable to construct it
1264 */
1265 public function get_grade_analysis_url(grade_grade $grade) {
eff314b0
DM
1266 global $CFG;
1267 /** @var array static cache of the grade.php file existence flags */
1268 static $hasgradephp = array();
bb17ac1e
DM
1269
1270 if (empty($grade->grade_item) or !($grade->grade_item instanceof grade_item)) {
1271 throw new coding_exception('Passed grade without the associated grade item');
1272 }
1273 $item = $grade->grade_item;
1274
1275 if (!$item->is_external_item()) {
1276 // at the moment, only activity modules are supported
1277 return null;
1278 }
1279 if ($item->itemtype !== 'mod') {
1280 throw new coding_exception('Unknown external itemtype: '.$item->itemtype);
1281 }
1282 if (empty($item->iteminstance) or empty($item->itemmodule) or empty($this->modinfo)) {
1283 return null;
1284 }
1285
eff314b0
DM
1286 if (!array_key_exists($item->itemmodule, $hasgradephp)) {
1287 if (file_exists($CFG->dirroot . '/mod/' . $item->itemmodule . '/grade.php')) {
1288 $hasgradephp[$item->itemmodule] = true;
1289 } else {
1290 $hasgradephp[$item->itemmodule] = false;
1291 }
1292 }
1293
1294 if (!$hasgradephp[$item->itemmodule]) {
1295 return null;
1296 }
1297
bb17ac1e
DM
1298 $instances = $this->modinfo->get_instances();
1299 if (empty($instances[$item->itemmodule][$item->iteminstance])) {
1300 return null;
1301 }
1302 $cm = $instances[$item->itemmodule][$item->iteminstance];
1303 if (!$cm->uservisible) {
1304 return null;
1305 }
1306
1307 $url = new moodle_url('/mod/'.$item->itemmodule.'/grade.php', array(
1308 'id' => $cm->id,
1309 'itemid' => $item->id,
1310 'itemnumber' => $item->itemnumber,
1311 'gradeid' => $grade->id,
1312 'userid' => $grade->userid,
1313 ));
1314
1315 return $url;
1316 }
1317
1318 /**
1319 * Returns an action icon leading to the grade analysis page
1320 *
1321 * @param grade_grade $grade
1322 * @return string
1323 */
1324 public function get_grade_analysis_icon(grade_grade $grade) {
1325 global $OUTPUT;
1326
1327 $url = $this->get_grade_analysis_url($grade);
1328 if (is_null($url)) {
1329 return '';
1330 }
1331
c97933ff 1332 return $OUTPUT->action_icon($url, new pix_icon('t/preview',
bb17ac1e
DM
1333 get_string('gradeanalysis', 'core_grades')));
1334 }
1335
6cc3e350 1336 /**
1337 * Returns the grade eid - the grade may not exist yet.
cf72e2dd 1338 *
1339 * @param grade_grade $grade_grade A grade_grade object
1340 *
6cc3e350 1341 * @return string eid
1342 */
d24832f9 1343 public function get_grade_eid($grade_grade) {
6cc3e350 1344 if (empty($grade_grade->id)) {
1345 return 'n'.$grade_grade->itemid.'u'.$grade_grade->userid;
1346 } else {
1347 return 'g'.$grade_grade->id;
1348 }
1349 }
1350
1351 /**
1352 * Returns the grade_item eid
cf72e2dd 1353 * @param grade_item $grade_item A grade_item object
6cc3e350 1354 * @return string eid
1355 */
d24832f9 1356 public function get_item_eid($grade_item) {
6cc3e350 1357 return 'i'.$grade_item->id;
1358 }
1359
cf72e2dd 1360 /**
1361 * Given a grade_tree element, returns an array of parameters
1362 * used to build an icon for that element.
1363 *
1364 * @param array $element An array representing an element in the grade_tree
1365 *
1366 * @return array
1367 */
1368 public function get_params_for_iconstr($element) {
9ecd4386 1369 $strparams = new stdClass();
1370 $strparams->category = '';
1371 $strparams->itemname = '';
1372 $strparams->itemmodule = '';
cf72e2dd 1373
9ecd4386 1374 if (!method_exists($element['object'], 'get_name')) {
1375 return $strparams;
1376 }
345674ca 1377
b285c5aa 1378 $strparams->itemname = html_to_text($element['object']->get_name());
9ecd4386 1379
1380 // If element name is categorytotal, get the name of the parent category
1381 if ($strparams->itemname == get_string('categorytotal', 'grades')) {
1382 $parent = $element['object']->get_parent_category();
1383 $strparams->category = $parent->get_name() . ' ';
1384 } else {
1385 $strparams->category = '';
1386 }
1387
1388 $strparams->itemmodule = null;
1389 if (isset($element['object']->itemmodule)) {
1390 $strparams->itemmodule = $element['object']->itemmodule;
345674ca 1391 }
9ecd4386 1392 return $strparams;
1393 }
1394
6cc3e350 1395 /**
1396 * Return edit icon for give element
cf72e2dd 1397 *
1398 * @param array $element An array representing an element in the grade_tree
1399 * @param object $gpr A grade_plugin_return object
1400 *
6cc3e350 1401 * @return string
1402 */
d24832f9 1403 public function get_edit_icon($element, $gpr) {
6b608f8f 1404 global $CFG, $OUTPUT;
6cc3e350 1405
1406 if (!has_capability('moodle/grade:manage', $this->context)) {
1407 if ($element['type'] == 'grade' and has_capability('moodle/grade:edit', $this->context)) {
1408 // oki - let them override grade
1409 } else {
1410 return '';
1411 }
1412 }
1413
05aba805 1414 static $strfeedback = null;
1415 static $streditgrade = null;
1416 if (is_null($streditgrade)) {
1417 $streditgrade = get_string('editgrade', 'grades');
1418 $strfeedback = get_string('feedback');
6cc3e350 1419 }
1420
9ecd4386 1421 $strparams = $this->get_params_for_iconstr($element);
6ef4878b 1422
6cc3e350 1423 $object = $element['object'];
6cc3e350 1424
1425 switch ($element['type']) {
1426 case 'item':
1427 case 'categoryitem':
1428 case 'courseitem':
9ecd4386 1429 $stredit = get_string('editverbose', 'grades', $strparams);
6cc3e350 1430 if (empty($object->outcomeid) || empty($CFG->enableoutcomes)) {
a6855934 1431 $url = new moodle_url('/grade/edit/tree/item.php',
1c1f64a2 1432 array('courseid' => $this->courseid, 'id' => $object->id));
6cc3e350 1433 } else {
a6855934 1434 $url = new moodle_url('/grade/edit/tree/outcomeitem.php',
1c1f64a2 1435 array('courseid' => $this->courseid, 'id' => $object->id));
6cc3e350 1436 }
6cc3e350 1437 break;
1438
1439 case 'category':
9ecd4386 1440 $stredit = get_string('editverbose', 'grades', $strparams);
a6855934 1441 $url = new moodle_url('/grade/edit/tree/category.php',
1c1f64a2 1442 array('courseid' => $this->courseid, 'id' => $object->id));
6cc3e350 1443 break;
1444
1445 case 'grade':
05aba805 1446 $stredit = $streditgrade;
6cc3e350 1447 if (empty($object->id)) {
a6855934 1448 $url = new moodle_url('/grade/edit/tree/grade.php',
1c1f64a2 1449 array('courseid' => $this->courseid, 'itemid' => $object->itemid, 'userid' => $object->userid));
6cc3e350 1450 } else {
a6855934 1451 $url = new moodle_url('/grade/edit/tree/grade.php',
1c1f64a2 1452 array('courseid' => $this->courseid, 'id' => $object->id));
6cc3e350 1453 }
6cc3e350 1454 if (!empty($object->feedback)) {
1455 $feedback = addslashes_js(trim(format_string($object->feedback, $object->feedbackformat)));
6cc3e350 1456 }
1457 break;
1458
1459 default:
1460 $url = null;
1461 }
1462
1463 if ($url) {
c63923bd 1464 return $OUTPUT->action_icon($gpr->add_url_params($url), new pix_icon('t/edit', $stredit));
6cc3e350 1465
1466 } else {
1467 return '';
1468 }
1469 }
1470
1471 /**
1472 * Return hiding icon for give element
cf72e2dd 1473 *
1474 * @param array $element An array representing an element in the grade_tree
1475 * @param object $gpr A grade_plugin_return object
1476 *
6cc3e350 1477 * @return string
1478 */
d24832f9 1479 public function get_hiding_icon($element, $gpr) {
5d3b9994 1480 global $CFG, $OUTPUT;
6cc3e350 1481
cf72e2dd 1482 if (!has_capability('moodle/grade:manage', $this->context) and
1483 !has_capability('moodle/grade:hide', $this->context)) {
6cc3e350 1484 return '';
1485 }
1486
345674ca 1487 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1488 $strshow = get_string('showverbose', 'grades', $strparams);
345674ca 1489 $strhide = get_string('hideverbose', 'grades', $strparams);
6cc3e350 1490
a6855934 1491 $url = new moodle_url('/grade/edit/tree/action.php', array('id' => $this->courseid, 'sesskey' => sesskey(), 'eid' => $element['eid']));
8ae8bf8a 1492 $url = $gpr->add_url_params($url);
1c1f64a2 1493
6cc3e350 1494 if ($element['object']->is_hidden()) {
8ae8bf8a 1495 $type = 'show';
6cc3e350 1496 $tooltip = $strshow;
1497
cf72e2dd 1498 // Change the icon and add a tooltip showing the date
1499 if ($element['type'] != 'category' and $element['object']->get_hidden() > 1) {
8ae8bf8a 1500 $type = 'hiddenuntil';
cf72e2dd 1501 $tooltip = get_string('hiddenuntildate', 'grades',
1502 userdate($element['object']->get_hidden()));
6cc3e350 1503 }
1504
8ae8bf8a 1505 $url->param('action', 'show');
b9cc756e 1506
33407de5 1507 $hideicon = $OUTPUT->action_icon($url, new pix_icon('t/'.$type, $tooltip, 'moodle', array('alt'=>$strshow, 'class'=>'smallicon')));
6cc3e350 1508
1509 } else {
8ae8bf8a 1510 $url->param('action', 'hide');
c63923bd 1511 $hideicon = $OUTPUT->action_icon($url, new pix_icon('t/hide', $strhide));
6cc3e350 1512 }
1c1f64a2 1513
8ae8bf8a 1514 return $hideicon;
6cc3e350 1515 }
1516
1517 /**
2673c733 1518 * Return locking icon for given element
cf72e2dd 1519 *
1520 * @param array $element An array representing an element in the grade_tree
1521 * @param object $gpr A grade_plugin_return object
1522 *
6cc3e350 1523 * @return string
1524 */
d24832f9 1525 public function get_locking_icon($element, $gpr) {
6b608f8f 1526 global $CFG, $OUTPUT;
6cc3e350 1527
345674ca 1528 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1529 $strunlock = get_string('unlockverbose', 'grades', $strparams);
1530 $strlock = get_string('lockverbose', 'grades', $strparams);
d24832f9 1531
a6855934 1532 $url = new moodle_url('/grade/edit/tree/action.php', array('id' => $this->courseid, 'sesskey' => sesskey(), 'eid' => $element['eid']));
8ae8bf8a 1533 $url = $gpr->add_url_params($url);
1c1f64a2 1534
2673c733 1535 // Don't allow an unlocking action for a grade whose grade item is locked: just print a state icon
1536 if ($element['type'] == 'grade' && $element['object']->grade_item->is_locked()) {
1537 $strparamobj = new stdClass();
1538 $strparamobj->itemname = $element['object']->grade_item->itemname;
1539 $strnonunlockable = get_string('nonunlockableverbose', 'grades', $strparamobj);
1c1f64a2 1540
000c278c 1541 $action = $OUTPUT->pix_icon('t/unlock_gray', $strnonunlockable);
8ae8bf8a 1542
cf72e2dd 1543 } else if ($element['object']->is_locked()) {
8ae8bf8a 1544 $type = 'unlock';
6cc3e350 1545 $tooltip = $strunlock;
1546
cf72e2dd 1547 // Change the icon and add a tooltip showing the date
1548 if ($element['type'] != 'category' and $element['object']->get_locktime() > 1) {
8ae8bf8a 1549 $type = 'locktime';
cf72e2dd 1550 $tooltip = get_string('locktimedate', 'grades',
1551 userdate($element['object']->get_locktime()));
6cc3e350 1552 }
1553
8ae8bf8a
PS
1554 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:unlock', $this->context)) {
1555 $action = '';
1556 } else {
1557 $url->param('action', 'unlock');
c63923bd 1558 $action = $OUTPUT->action_icon($url, new pix_icon('t/'.$type, $tooltip, 'moodle', array('alt'=>$strunlock, 'class'=>'smallicon')));
6cc3e350 1559 }
6cc3e350 1560
1561 } else {
8ae8bf8a
PS
1562 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:lock', $this->context)) {
1563 $action = '';
1564 } else {
1565 $url->param('action', 'lock');
c63923bd 1566 $action = $OUTPUT->action_icon($url, new pix_icon('t/lock', $strlock));
6cc3e350 1567 }
6cc3e350 1568 }
8ae8bf8a 1569
6cc3e350 1570 return $action;
1571 }
1572
1573 /**
1574 * Return calculation icon for given element
cf72e2dd 1575 *
1576 * @param array $element An array representing an element in the grade_tree
1577 * @param object $gpr A grade_plugin_return object
1578 *
6cc3e350 1579 * @return string
1580 */
d24832f9 1581 public function get_calculation_icon($element, $gpr) {
666e8458 1582 global $CFG, $OUTPUT;
6cc3e350 1583 if (!has_capability('moodle/grade:manage', $this->context)) {
1584 return '';
1585 }
1586
6cc3e350 1587 $type = $element['type'];
1588 $object = $element['object'];
1589
1590 if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') {
345674ca 1591 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1592 $streditcalculation = get_string('editcalculationverbose', 'grades', $strparams);
6cc3e350 1593
cf72e2dd 1594 $is_scale = $object->gradetype == GRADE_TYPE_SCALE;
1595 $is_value = $object->gradetype == GRADE_TYPE_VALUE;
1596
6cc3e350 1597 // show calculation icon only when calculation possible
cf72e2dd 1598 if (!$object->is_external_item() and ($is_scale or $is_value)) {
6cc3e350 1599 if ($object->is_calculated()) {
666e8458 1600 $icon = 't/calc';
6cc3e350 1601 } else {
666e8458 1602 $icon = 't/calc_off';
6cc3e350 1603 }
cf72e2dd 1604
a6855934 1605 $url = new moodle_url('/grade/edit/tree/calculation.php', array('courseid' => $this->courseid, 'id' => $object->id));
8ae8bf8a 1606 $url = $gpr->add_url_params($url);
c63923bd 1607 return $OUTPUT->action_icon($url, new pix_icon($icon, $streditcalculation)) . "\n";
6cc3e350 1608 }
1609 }
1610
1c1f64a2 1611 return '';
6cc3e350 1612 }
1613}
1614
1615/**
1616 * Flat structure similar to grade tree.
cf72e2dd 1617 *
1618 * @uses grade_structure
a153c9f2 1619 * @package core_grades
cf72e2dd 1620 * @copyright 2009 Nicolas Connault
1621 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6cc3e350 1622 */
1623class grade_seq extends grade_structure {
1624
6cc3e350 1625 /**
1626 * 1D array of elements
1627 */
d24832f9 1628 public $elements;
e98871a2 1629
1630 /**
1631 * Constructor, retrieves and stores array of all grade_category and grade_item
1632 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed.
cf72e2dd 1633 *
1634 * @param int $courseid The course id
1635 * @param bool $category_grade_last category grade item is the last child
1636 * @param bool $nooutcomes Whether or not outcomes should be included
e98871a2 1637 */
d24832f9 1638 public function grade_seq($courseid, $category_grade_last=false, $nooutcomes=false) {
e98871a2 1639 global $USER, $CFG;
1640
1641 $this->courseid = $courseid;
e98871a2 1642 $this->context = get_context_instance(CONTEXT_COURSE, $courseid);
1643
1644 // get course grade tree
1645 $top_element = grade_category::fetch_course_tree($courseid, true);
1646
6391ebe7 1647 $this->elements = grade_seq::flatten($top_element, $category_grade_last, $nooutcomes);
1648
1649 foreach ($this->elements as $key=>$unused) {
b89a70ce 1650 $this->items[$this->elements[$key]['object']->id] =& $this->elements[$key]['object'];
6391ebe7 1651 }
e98871a2 1652 }
1653
1654 /**
1655 * Static recursive helper - makes the grade_item for category the last children
cf72e2dd 1656 *
1657 * @param array &$element The seed of the recursion
1658 * @param bool $category_grade_last category grade item is the last child
1659 * @param bool $nooutcomes Whether or not outcomes should be included
1660 *
1661 * @return array
e98871a2 1662 */
d24832f9 1663 public function flatten(&$element, $category_grade_last, $nooutcomes) {
e98871a2 1664 if (empty($element['children'])) {
1665 return array();
1666 }
1667 $children = array();
1668
1669 foreach ($element['children'] as $sortorder=>$unused) {
cf72e2dd 1670 if ($nooutcomes and $element['type'] != 'category' and
1671 $element['children'][$sortorder]['object']->is_outcome_item()) {
e98871a2 1672 continue;
1673 }
1674 $children[] = $element['children'][$sortorder];
1675 }
1676 unset($element['children']);
1677
1678 if ($category_grade_last and count($children) > 1) {
1679 $cat_item = array_shift($children);
1680 array_push($children, $cat_item);
1681 }
1682
1683 $result = array();
1684 foreach ($children as $child) {
e0724506 1685 if ($child['type'] == 'category') {
6391ebe7 1686 $result = $result + grade_seq::flatten($child, $category_grade_last, $nooutcomes);
e98871a2 1687 } else {
1688 $child['eid'] = 'i'.$child['object']->id;
6391ebe7 1689 $result[$child['object']->id] = $child;
e98871a2 1690 }
1691 }
1692
1693 return $result;
1694 }
1695
1696 /**
1697 * Parses the array in search of a given eid and returns a element object with
1698 * information about the element it has found.
cf72e2dd 1699 *
1700 * @param int $eid Gradetree Element ID
1701 *
e98871a2 1702 * @return object element
1703 */
d24832f9 1704 public function locate_element($eid) {
e98871a2 1705 // it is a grade - construct a new object
1706 if (strpos($eid, 'n') === 0) {
1707 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) {
1708 return null;
1709 }
1710
1711 $itemid = $matches[1];
1712 $userid = $matches[2];
1713
1714 //extra security check - the grade item must be in this tree
1715 if (!$item_el = $this->locate_element('i'.$itemid)) {
1716 return null;
1717 }
1718
1719 // $gradea->id may be null - means does not exist yet
1720 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid));
1721
1722 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1723 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade');
1724
1725 } else if (strpos($eid, 'g') === 0) {
cf72e2dd 1726 $id = (int) substr($eid, 1);
e98871a2 1727 if (!$grade = grade_grade::fetch(array('id'=>$id))) {
1728 return null;
1729 }
1730 //extra security check - the grade item must be in this tree
1731 if (!$item_el = $this->locate_element('i'.$grade->itemid)) {
1732 return null;
1733 }
1734 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1735 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade');
1736 }
1737
1738 // it is a category or item
6391ebe7 1739 foreach ($this->elements as $element) {
6cc3e350 1740 if ($element['eid'] == $eid) {
1741 return $element;
1742 }
e98871a2 1743 }
6cc3e350 1744
1745 return null;
e98871a2 1746 }
e98871a2 1747}
1748
7a6b7acf 1749/**
1750 * This class represents a complete tree of categories, grade_items and final grades,
1751 * organises as an array primarily, but which can also be converted to other formats.
1752 * It has simple method calls with complex implementations, allowing for easy insertion,
1753 * deletion and moving of items and categories within the tree.
cf72e2dd 1754 *
1755 * @uses grade_structure
a153c9f2 1756 * @package core_grades
cf72e2dd 1757 * @copyright 2009 Nicolas Connault
1758 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7a6b7acf 1759 */
6cc3e350 1760class grade_tree extends grade_structure {
7a6b7acf 1761
1762 /**
1763 * The basic representation of the tree as a hierarchical, 3-tiered array.
1764 * @var object $top_element
1765 */
d24832f9 1766 public $top_element;
7a6b7acf 1767
7a6b7acf 1768 /**
1769 * 2D array of grade items and categories
cf72e2dd 1770 * @var array $levels
7a6b7acf 1771 */
d24832f9 1772 public $levels;
7a6b7acf 1773
b89a70ce 1774 /**
1775 * Grade items
cf72e2dd 1776 * @var array $items
b89a70ce 1777 */
d24832f9 1778 public $items;
b89a70ce 1779
7a6b7acf 1780 /**
1781 * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item
e98871a2 1782 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed.
cf72e2dd 1783 *
1784 * @param int $courseid The Course ID
1785 * @param bool $fillers include fillers and colspans, make the levels var "rectangular"
1786 * @param bool $category_grade_last category grade item is the last child
4faf5f99 1787 * @param array $collapsed array of collapsed categories
cf72e2dd 1788 * @param bool $nooutcomes Whether or not outcomes should be included
7a6b7acf 1789 */
cf72e2dd 1790 public function grade_tree($courseid, $fillers=true, $category_grade_last=false,
1791 $collapsed=null, $nooutcomes=false) {
73ca5f01 1792 global $USER, $CFG, $COURSE, $DB;
7a6b7acf 1793
1794 $this->courseid = $courseid;
7a6b7acf 1795 $this->levels = array();
2cc773f5 1796 $this->context = get_context_instance(CONTEXT_COURSE, $courseid);
7a6b7acf 1797
73ca5f01 1798 if (!empty($COURSE->id) && $COURSE->id == $this->courseid) {
1799 $course = $COURSE;
1800 } else {
1801 $course = $DB->get_record('course', array('id' => $this->courseid));
1802 }
1803 $this->modinfo = get_fast_modinfo($course);
1804
7a6b7acf 1805 // get course grade tree
1806 $this->top_element = grade_category::fetch_course_tree($courseid, true);
1807
4faf5f99 1808 // collapse the categories if requested
1809 if (!empty($collapsed)) {
1810 grade_tree::category_collapse($this->top_element, $collapsed);
1811 }
1812
aea4df41 1813 // no otucomes if requested
1814 if (!empty($nooutcomes)) {
1815 grade_tree::no_outcomes($this->top_element);
1816 }
1817
4faf5f99 1818 // move category item to last position in category
7a6b7acf 1819 if ($category_grade_last) {
1820 grade_tree::category_grade_last($this->top_element);
1821 }
1822
1823 if ($fillers) {
1824 // inject fake categories == fillers
1825 grade_tree::inject_fillers($this->top_element, 0);
1826 // add colspans to categories and fillers
1827 grade_tree::inject_colspans($this->top_element);
1828 }
1829
1830 grade_tree::fill_levels($this->levels, $this->top_element, 0);
d297269d 1831
7a6b7acf 1832 }
1833
4faf5f99 1834 /**
1835 * Static recursive helper - removes items from collapsed categories
cf72e2dd 1836 *
1837 * @param array &$element The seed of the recursion
4faf5f99 1838 * @param array $collapsed array of collapsed categories
cf72e2dd 1839 *
4faf5f99 1840 * @return void
1841 */
d24832f9 1842 public function category_collapse(&$element, $collapsed) {
4faf5f99 1843 if ($element['type'] != 'category') {
1844 return;
1845 }
1846 if (empty($element['children']) or count($element['children']) < 2) {
1847 return;
1848 }
1849
384960dd 1850 if (in_array($element['object']->id, $collapsed['aggregatesonly'])) {
4faf5f99 1851 $category_item = reset($element['children']); //keep only category item
1852 $element['children'] = array(key($element['children'])=>$category_item);
1853
1854 } else {
384960dd 1855 if (in_array($element['object']->id, $collapsed['gradesonly'])) { // Remove category item
1856 reset($element['children']);
1857 $first_key = key($element['children']);
1858 unset($element['children'][$first_key]);
1859 }
1860 foreach ($element['children'] as $sortorder=>$child) { // Recurse through the element's children
4faf5f99 1861 grade_tree::category_collapse($element['children'][$sortorder], $collapsed);
1862 }
1863 }
1864 }
7a6b7acf 1865
aea4df41 1866 /**
1867 * Static recursive helper - removes all outcomes
cf72e2dd 1868 *
1869 * @param array &$element The seed of the recursion
1870 *
aea4df41 1871 * @return void
1872 */
d24832f9 1873 public function no_outcomes(&$element) {
aea4df41 1874 if ($element['type'] != 'category') {
1875 return;
1876 }
1877 foreach ($element['children'] as $sortorder=>$child) {
1878 if ($element['children'][$sortorder]['type'] == 'item'
1879 and $element['children'][$sortorder]['object']->is_outcome_item()) {
1880 unset($element['children'][$sortorder]);
1881
d4795a07 1882 } else if ($element['children'][$sortorder]['type'] == 'category') {
aea4df41 1883 grade_tree::no_outcomes($element['children'][$sortorder]);
1884 }
1885 }
1886 }
1887
7a6b7acf 1888 /**
1889 * Static recursive helper - makes the grade_item for category the last children
cf72e2dd 1890 *
1891 * @param array &$element The seed of the recursion
1892 *
7a6b7acf 1893 * @return void
1894 */
d24832f9 1895 public function category_grade_last(&$element) {
7a6b7acf 1896 if (empty($element['children'])) {
1897 return;
1898 }
1899 if (count($element['children']) < 2) {
1900 return;
1901 }
3e0e2436 1902 $first_item = reset($element['children']);
4a3dfd9a 1903 if ($first_item['type'] == 'categoryitem' or $first_item['type'] == 'courseitem') {
3e0e2436 1904 // the category item might have been already removed
1905 $order = key($element['children']);
1906 unset($element['children'][$order]);
1907 $element['children'][$order] =& $first_item;
1908 }
206f9953 1909 foreach ($element['children'] as $sortorder => $child) {
7a6b7acf 1910 grade_tree::category_grade_last($element['children'][$sortorder]);
1911 }
1912 }
1913
1914 /**
1915 * Static recursive helper - fills the levels array, useful when accessing tree elements of one level
cf72e2dd 1916 *
1917 * @param array &$levels The levels of the grade tree through which to recurse
1918 * @param array &$element The seed of the recursion
1919 * @param int $depth How deep are we?
7a6b7acf 1920 * @return void
1921 */
d24832f9 1922 public function fill_levels(&$levels, &$element, $depth) {
7a6b7acf 1923 if (!array_key_exists($depth, $levels)) {
1924 $levels[$depth] = array();
1925 }
1926
1927 // prepare unique identifier
1928 if ($element['type'] == 'category') {
1929 $element['eid'] = 'c'.$element['object']->id;
1930 } else if (in_array($element['type'], array('item', 'courseitem', 'categoryitem'))) {
1931 $element['eid'] = 'i'.$element['object']->id;
b89a70ce 1932 $this->items[$element['object']->id] =& $element['object'];
7a6b7acf 1933 }
1934
1935 $levels[$depth][] =& $element;
1936 $depth++;
1937 if (empty($element['children'])) {
1938 return;
1939 }
1940 $prev = 0;
1941 foreach ($element['children'] as $sortorder=>$child) {
1942 grade_tree::fill_levels($levels, $element['children'][$sortorder], $depth);
1943 $element['children'][$sortorder]['prev'] = $prev;
1944 $element['children'][$sortorder]['next'] = 0;
1945 if ($prev) {
1946 $element['children'][$prev]['next'] = $sortorder;
1947 }
1948 $prev = $sortorder;
1949 }
1950 }
1951
1952 /**
1953 * Static recursive helper - makes full tree (all leafes are at the same level)
cf72e2dd 1954 *
1955 * @param array &$element The seed of the recursion
1956 * @param int $depth How deep are we?
1957 *
1958 * @return int
7a6b7acf 1959 */
d24832f9 1960 public function inject_fillers(&$element, $depth) {
7a6b7acf 1961 $depth++;
1962
1963 if (empty($element['children'])) {
1964 return $depth;
1965 }
1966 $chdepths = array();
1967 $chids = array_keys($element['children']);
1968 $last_child = end($chids);
1969 $first_child = reset($chids);
1970
1971 foreach ($chids as $chid) {
1972 $chdepths[$chid] = grade_tree::inject_fillers($element['children'][$chid], $depth);
1973 }
1974 arsort($chdepths);
1975
1976 $maxdepth = reset($chdepths);
1977 foreach ($chdepths as $chid=>$chd) {
1978 if ($chd == $maxdepth) {
1979 continue;
1980 }
1981 for ($i=0; $i < $maxdepth-$chd; $i++) {
1982 if ($chid == $first_child) {
1983 $type = 'fillerfirst';
1984 } else if ($chid == $last_child) {
1985 $type = 'fillerlast';
1986 } else {
1987 $type = 'filler';
1988 }
1989 $oldchild =& $element['children'][$chid];
cf72e2dd 1990 $element['children'][$chid] = array('object'=>'filler', 'type'=>$type,
1991 'eid'=>'', 'depth'=>$element['object']->depth,
1992 'children'=>array($oldchild));
7a6b7acf 1993 }
1994 }
1995
1996 return $maxdepth;
1997 }
1998
1999 /**
2000 * Static recursive helper - add colspan information into categories
cf72e2dd 2001 *
2002 * @param array &$element The seed of the recursion
2003 *
2004 * @return int
7a6b7acf 2005 */
d24832f9 2006 public function inject_colspans(&$element) {
7a6b7acf 2007 if (empty($element['children'])) {
2008 return 1;
2009 }
2010 $count = 0;
2011 foreach ($element['children'] as $key=>$child) {
2012 $count += grade_tree::inject_colspans($element['children'][$key]);
2013 }
2014 $element['colspan'] = $count;
2015 return $count;
2016 }
2017
2018 /**
2019 * Parses the array in search of a given eid and returns a element object with
2020 * information about the element it has found.
cf72e2dd 2021 * @param int $eid Gradetree Element ID
7a6b7acf 2022 * @return object element
2023 */
d24832f9 2024 public function locate_element($eid) {
d3c3da1b 2025 // it is a grade - construct a new object
2026 if (strpos($eid, 'n') === 0) {
2027 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) {
2028 return null;
2029 }
2030
2031 $itemid = $matches[1];
2032 $userid = $matches[2];
2033
2034 //extra security check - the grade item must be in this tree
2035 if (!$item_el = $this->locate_element('i'.$itemid)) {
2036 return null;
2037 }
2038
2039 // $gradea->id may be null - means does not exist yet
2040 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid));
2041
2042 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
2043 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade');
2044
2045 } else if (strpos($eid, 'g') === 0) {
cf72e2dd 2046 $id = (int) substr($eid, 1);
7a6b7acf 2047 if (!$grade = grade_grade::fetch(array('id'=>$id))) {
2048 return null;
2049 }
2050 //extra security check - the grade item must be in this tree
2051 if (!$item_el = $this->locate_element('i'.$grade->itemid)) {
2052 return null;
2053 }
2054 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
2055 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade');
2056 }
2057
2058 // it is a category or item
2059 foreach ($this->levels as $row) {
2060 foreach ($row as $element) {
2061 if ($element['type'] == 'filler') {
2062 continue;
2063 }
2064 if ($element['eid'] == $eid) {
2065 return $element;
2066 }
2067 }
2068 }
2069
2070 return null;
2071 }
d24832f9 2072
b244b9b7 2073 /**
2074 * Returns a well-formed XML representation of the grade-tree using recursion.
cf72e2dd 2075 *
2076 * @param array $root The current element in the recursion. If null, starts at the top of the tree.
2077 * @param string $tabs The control character to use for tabs
2078 *
b244b9b7 2079 * @return string $xml
2080 */
cf72e2dd 2081 public function exporttoxml($root=null, $tabs="\t") {
b244b9b7 2082 $xml = null;
2083 $first = false;
2084 if (is_null($root)) {
2085 $root = $this->top_element;
2086 $xml = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n";
2087 $xml .= "<gradetree>\n";
2088 $first = true;
2089 }
d24832f9 2090
b244b9b7 2091 $type = 'undefined';
2092 if (strpos($root['object']->table, 'grade_categories') !== false) {
2093 $type = 'category';
cf72e2dd 2094 } else if (strpos($root['object']->table, 'grade_items') !== false) {
b244b9b7 2095 $type = 'item';
cf72e2dd 2096 } else if (strpos($root['object']->table, 'grade_outcomes') !== false) {
b244b9b7 2097 $type = 'outcome';
2098 }
d24832f9 2099
b244b9b7 2100 $xml .= "$tabs<element type=\"$type\">\n";
2101 foreach ($root['object'] as $var => $value) {
2102 if (!is_object($value) && !is_array($value) && !empty($value)) {
2103 $xml .= "$tabs\t<$var>$value</$var>\n";
2104 }
2105 }
2106
2107 if (!empty($root['children'])) {
2108 $xml .= "$tabs\t<children>\n";
2109 foreach ($root['children'] as $sortorder => $child) {
2110 $xml .= $this->exportToXML($child, $tabs."\t\t");
2111 }
2112 $xml .= "$tabs\t</children>\n";
2113 }
d24832f9 2114
b244b9b7 2115 $xml .= "$tabs</element>\n";
2116
2117 if ($first) {
2118 $xml .= "</gradetree>";
2119 }
d24832f9 2120
b244b9b7 2121 return $xml;
2122 }
d24832f9 2123
b244b9b7 2124 /**
2125 * Returns a JSON representation of the grade-tree using recursion.
cf72e2dd 2126 *
b244b9b7 2127 * @param array $root The current element in the recursion. If null, starts at the top of the tree.
2128 * @param string $tabs Tab characters used to indent the string nicely for humans to enjoy
cf72e2dd 2129 *
2130 * @return string
b244b9b7 2131 */
cf72e2dd 2132 public function exporttojson($root=null, $tabs="\t") {
b244b9b7 2133 $json = null;
2134 $first = false;
2135 if (is_null($root)) {
2136 $root = $this->top_element;
2137 $first = true;
2138 }
d24832f9 2139
b244b9b7 2140 $name = '';
2141
2142
2143 if (strpos($root['object']->table, 'grade_categories') !== false) {
2144 $name = $root['object']->fullname;
2145 if ($name == '?') {
d24832f9 2146 $name = $root['object']->get_name();
b244b9b7 2147 }
cf72e2dd 2148 } else if (strpos($root['object']->table, 'grade_items') !== false) {
b244b9b7 2149 $name = $root['object']->itemname;
cf72e2dd 2150 } else if (strpos($root['object']->table, 'grade_outcomes') !== false) {
b244b9b7 2151 $name = $root['object']->itemname;
2152 }
d24832f9 2153
b244b9b7 2154 $json .= "$tabs {\n";
2155 $json .= "$tabs\t \"type\": \"{$root['type']}\",\n";
2156 $json .= "$tabs\t \"name\": \"$name\",\n";
2157
2158 foreach ($root['object'] as $var => $value) {
2159 if (!is_object($value) && !is_array($value) && !empty($value)) {
2160 $json .= "$tabs\t \"$var\": \"$value\",\n";
2161 }
2162 }
d24832f9 2163
b244b9b7 2164 $json = substr($json, 0, strrpos($json, ','));
d24832f9 2165
b244b9b7 2166 if (!empty($root['children'])) {
2167 $json .= ",\n$tabs\t\"children\": [\n";
2168 foreach ($root['children'] as $sortorder => $child) {
2169 $json .= $this->exportToJSON($child, $tabs."\t\t");
2170 }
2171 $json = substr($json, 0, strrpos($json, ','));
2172 $json .= "\n$tabs\t]\n";
d24832f9 2173 }
b244b9b7 2174
2175 if ($first) {
2176 $json .= "\n}";
2177 } else {
2178 $json .= "\n$tabs},\n";
2179 }
d24832f9 2180
b244b9b7 2181 return $json;
2182 }
d24832f9 2183
cf72e2dd 2184 /**
2185 * Returns the array of levels
2186 *
2187 * @return array
2188 */
d24832f9 2189 public function get_levels() {
2190 return $this->levels;
2191 }
2192
cf72e2dd 2193 /**
2194 * Returns the array of grade items
2195 *
2196 * @return array
2197 */
d24832f9 2198 public function get_items() {
2199 return $this->items;
2200 }
2201
cf72e2dd 2202 /**
2203 * Returns a specific Grade Item
2204 *
2205 * @param int $itemid The ID of the grade_item object
2206 *
2207 * @return grade_item
2208 */
d24832f9 2209 public function get_item($itemid) {
2210 if (array_key_exists($itemid, $this->items)) {
2211 return $this->items[$itemid];
2212 } else {
2213 return false;
2214 }
2215 }
7a6b7acf 2216}
eef00ade 2217
2218/**
2219 * Local shortcut function for creating an edit/delete button for a grade_* object.
4d27bc79 2220 * @param string $type 'edit' or 'delete'
eef00ade 2221 * @param int $courseid The Course ID
2222 * @param grade_* $object The grade_* object
2223 * @return string html
2224 */
2225function grade_button($type, $courseid, $object) {
2226 global $CFG, $OUTPUT;
2227 if (preg_match('/grade_(.*)/', get_class($object), $matches)) {
2228 $objectidstring = $matches[1] . 'id';
2229 } else {
2230 throw new coding_exception('grade_button() only accepts grade_* objects as third parameter!');
2231 }
2232
2233 $strdelete = get_string('delete');
2234 $stredit = get_string('edit');
2235
eef00ade 2236 if ($type == 'delete') {
8ae8bf8a 2237 $url = new moodle_url('index.php', array('id' => $courseid, $objectidstring => $object->id, 'action' => 'delete', 'sesskey' => sesskey()));
eef00ade 2238 } else if ($type == 'edit') {
8ae8bf8a 2239 $url = new moodle_url('edit.php', array('courseid' => $courseid, 'id' => $object->id));
eef00ade 2240 }
2241
c63923bd 2242 return $OUTPUT->action_icon($url, new pix_icon('t/'.$type, ${'str'.$type}));
eef00ade 2243
2244}
4d5059d4
SH
2245
2246/**
2247 * This method adds settings to the settings block for the grade system and its
2248 * plugins
2249 *
2250 * @global moodle_page $PAGE
2251 */
2252function grade_extend_settings($plugininfo, $courseid) {
2253 global $PAGE;
2254
2255 $gradenode = $PAGE->settingsnav->prepend(get_string('gradeadministration', 'grades'), null, navigation_node::TYPE_CONTAINER);
2256
2257 $strings = array_shift($plugininfo);
2258
2259 if ($reports = grade_helper::get_plugins_reports($courseid)) {
2260 foreach ($reports as $report) {
2261 $gradenode->add($report->string, $report->link, navigation_node::TYPE_SETTING, null, $report->id, new pix_icon('i/report', ''));
2262 }
2263 }
2264
2265 if ($imports = grade_helper::get_plugins_import($courseid)) {
2266 $importnode = $gradenode->add($strings['import'], null, navigation_node::TYPE_CONTAINER);
2267 foreach ($imports as $import) {
2268 $importnode->add($import->string, $import->link, navigation_node::TYPE_SETTING, null, $import->id, new pix_icon('i/restore', ''));
2269 }
2270 }
2271
2272 if ($exports = grade_helper::get_plugins_export($courseid)) {
2273 $exportnode = $gradenode->add($strings['export'], null, navigation_node::TYPE_CONTAINER);
2274 foreach ($exports as $export) {
2275 $exportnode->add($export->string, $export->link, navigation_node::TYPE_SETTING, null, $export->id, new pix_icon('i/backup', ''));
2276 }
2277 }
2278
2279 if ($setting = grade_helper::get_info_manage_settings($courseid)) {
2280 $gradenode->add(get_string('coursegradesettings', 'grades'), $setting->link, navigation_node::TYPE_SETTING, null, $setting->id, new pix_icon('i/settings', ''));
2281 }
2282
2283 if ($preferences = grade_helper::get_plugins_report_preferences($courseid)) {
2284 $preferencesnode = $gradenode->add(get_string('myreportpreferences', 'grades'), null, navigation_node::TYPE_CONTAINER);
2285 foreach ($preferences as $preference) {
2286 $preferencesnode->add($preference->string, $preference->link, navigation_node::TYPE_SETTING, null, $preference->id, new pix_icon('i/settings', ''));
2287 }
2288 }
2289
2290 if ($letters = grade_helper::get_info_letters($courseid)) {
2291 $letters = array_shift($letters);
2292 $gradenode->add($strings['letter'], $letters->link, navigation_node::TYPE_SETTING, null, $letters->id, new pix_icon('i/settings', ''));
2293 }
2294
2295 if ($outcomes = grade_helper::get_info_outcomes($courseid)) {
2296 $outcomes = array_shift($outcomes);
2297 $gradenode->add($strings['outcome'], $outcomes->link, navigation_node::TYPE_SETTING, null, $outcomes->id, new pix_icon('i/outcomes', ''));
2298 }
2299
2300 if ($scales = grade_helper::get_info_scales($courseid)) {
2301 $gradenode->add($strings['scale'], $scales->link, navigation_node::TYPE_SETTING, null, $scales->id, new pix_icon('i/scales', ''));
2302 }
2303
2304 if ($categories = grade_helper::get_info_edit_structure($courseid)) {
2305 $categoriesnode = $gradenode->add(get_string('categoriesanditems','grades'), null, navigation_node::TYPE_CONTAINER);
2306 foreach ($categories as $category) {
2307 $categoriesnode->add($category->string, $category->link, navigation_node::TYPE_SETTING, null, $category->id, new pix_icon('i/report', ''));
2308 }
2309 }
2310
2311 if ($gradenode->contains_active_node()) {
2312 // If the gradenode is active include the settings base node (gradeadministration) in
2313 // the navbar, typcially this is ignored.
2314 $PAGE->navbar->includesettingsbase = true;
2315
2316 // If we can get the course admin node make sure it is closed by default
2317 // as in this case the gradenode will be opened
2318 if ($coursenode = $PAGE->settingsnav->get('courseadmin', navigation_node::TYPE_COURSE)){
2319 $coursenode->make_inactive();
2320 $coursenode->forceopen = false;
2321 }
2322 }
2323}
2324
2325/**
2326 * Grade helper class
2327 *
2328 * This class provides several helpful functions that work irrespective of any
2329 * current state.
2330 *
2331 * @copyright 2010 Sam Hemelryk
2332 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2333 */
2334abstract class grade_helper {
2335 /**
2336 * Cached manage settings info {@see get_info_settings}
2337 * @var grade_plugin_info|false
2338 */
2339 protected static $managesetting = null;
2340 /**
2341 * Cached grade report plugins {@see get_plugins_reports}
2342 * @var array|false
2343 */
2344 protected static $gradereports = null;
2345 /**
2346 * Cached grade report plugins preferences {@see get_info_scales}
2347 * @var array|false
2348 */
2349 protected static $gradereportpreferences = null;
2350 /**
2351 * Cached scale info {@see get_info_scales}
2352 * @var grade_plugin_info|false
2353 */
2354 protected static $scaleinfo = null;
2355 /**
2356 * Cached outcome info {@see get_info_outcomes}
2357 * @var grade_plugin_info|false
2358 */
2359 protected static $outcomeinfo = null;
2360 /**
2361 * Cached info on edit structure {@see get_info_edit_structure}
2362 * @var array|false
2363 */
2364 protected static $edittree = null;
2365 /**
2366 * Cached leftter info {@see get_info_letters}
2367 * @var grade_plugin_info|false
2368 */
2369 protected static $letterinfo = null;
2370 /**
2371 * Cached grade import plugins {@see get_plugins_import}
2372 * @var array|false
2373 */
2374 protected static $importplugins = null;
2375 /**
2376 * Cached grade export plugins {@see get_plugins_export}
2377 * @var array|false
2378 */
2379 protected static $exportplugins = null;
2380 /**
2381 * Cached grade plugin strings
2382 * @var array
2383 */
2384 protected static $pluginstrings = null;
2385
2386 /**
2387 * Gets strings commonly used by the describe plugins
2388 *
2389 * report => get_string('view'),
2390 * edittree => get_string('edittree', 'grades'),
2391 * scale => get_string('scales'),
2392 * outcome => get_string('outcomes', 'grades'),
2393 * letter => get_string('letters', 'grades'),
2394 * export => get_string('export', 'grades'),
2395 * import => get_string('import'),
2396 * preferences => get_string('mypreferences', 'grades'),
2397 * settings => get_string('settings')
2398 *
2399 * @return array
2400 */
2401 public static function get_plugin_strings() {
2402 if (self::$pluginstrings === null) {
2403 self::$pluginstrings = array(
2404 'report' => get_string('view'),
2405 'edittree' => get_string('edittree', 'grades'),
2406 'scale' => get_string('scales'),
2407 'outcome' => get_string('outcomes', 'grades'),
2408 'letter' => get_string('letters', 'grades'),
2409 'export' => get_string('export', 'grades'),
2410 'import' => get_string('import'),
2411 'preferences' => get_string('mypreferences', 'grades'),
2412 'settings' => get_string('settings')
2413 );
2414 }
2415 return self::$pluginstrings;
2416 }
2417 /**
2418 * Get grade_plugin_info object for managing settings if the user can
2419 *
2420 * @param int $courseid
2421 * @return grade_plugin_info
2422 */
2423 public static function get_info_manage_settings($courseid) {
2424 if (self::$managesetting !== null) {
2425 return self::$managesetting;
2426 }
2427 $context = get_context_instance(CONTEXT_COURSE, $courseid);
ec934409 2428 if (has_capability('moodle/grade:manage', $context)) {
4d5059d4
SH
2429 self::$managesetting = new grade_plugin_info('coursesettings', new moodle_url('/grade/edit/settings/index.php', array('id'=>$courseid)), get_string('course'));
2430 } else {
2431 self::$managesetting = false;
2432 }
2433 return self::$managesetting;
2434 }
2435 /**
2436 * Returns an array of plugin reports as grade_plugin_info objects
2437 *
2438 * @param int $courseid
2439 * @return array
2440 */
2441 public static function get_plugins_reports($courseid) {
f7fcf4cd 2442 global $SITE;
cf717dc2 2443
4d5059d4
SH
2444 if (self::$gradereports !== null) {
2445 return self::$gradereports;
2446 }
2447 $context = get_context_instance(CONTEXT_COURSE, $courseid);
2448 $gradereports = array();
2449 $gradepreferences = array();
2450 foreach (get_plugin_list('gradereport') as $plugin => $plugindir) {
f7fcf4cd
AD
2451 //some reports make no sense if we're not within a course
2452 if ($courseid==$SITE->id && ($plugin=='grader' || $plugin=='user')) {
2453 continue;
2454 }
2455
4d5059d4
SH
2456 // Remove ones we can't see
2457 if (!has_capability('gradereport/'.$plugin.':view', $context)) {
2458 continue;
2459 }
2460
b5e7b2bf 2461 $pluginstr = get_string('pluginname', 'gradereport_'.$plugin);
4d5059d4
SH
2462 $url = new moodle_url('/grade/report/'.$plugin.'/index.php', array('id'=>$courseid));
2463 $gradereports[$plugin] = new grade_plugin_info($plugin, $url, $pluginstr);
2464
2465 // Add link to preferences tab if such a page exists
2466 if (file_exists($plugindir.'/preferences.php')) {
2467 $url = new moodle_url('/grade/report/'.$plugin.'/preferences.php', array('id'=>$courseid));
2468 $gradepreferences[$plugin] = new grade_plugin_info($plugin, $url, $pluginstr);
2469 }
2470 }
2471 if (count($gradereports) == 0) {
2472 $gradereports = false;
2473 $gradepreferences = false;
2474 } else if (count($gradepreferences) == 0) {
2475 $gradepreferences = false;
2476 asort($gradereports);
2477 } else {
2478 asort($gradereports);
2479 asort($gradepreferences);
2480 }
2481 self::$gradereports = $gradereports;
2482 self::$gradereportpreferences = $gradepreferences;
2483 return self::$gradereports;
2484 }
2485 /**
2486 * Returns an array of grade plugin report preferences for plugin reports that
2487 * support preferences
2488 * @param int $courseid
2489 * @return array
2490 */
2491 public static function get_plugins_report_preferences($courseid) {
2492 if (self::$gradereportpreferences !== null) {
2493 return self::$gradereportpreferences;
2494 }
2495 self::get_plugins_reports($courseid);
2496 return self::$gradereportpreferences;
2497 }
2498 /**
2499 * Get information on scales
2500 * @param int $courseid
2501 * @return grade_plugin_info
2502 */
2503 public static function get_info_scales($courseid) {
2504 if (self::$scaleinfo !== null) {
2505 return self::$scaleinfo;
2506 }
2507 if (has_capability('moodle/course:managescales', get_context_instance(CONTEXT_COURSE, $courseid))) {
2508 $url = new moodle_url('/grade/edit/scale/index.php', array('id'=>$courseid));
2509 self::$scaleinfo = new grade_plugin_info('scale', $url, get_string('view'));
2510 } else {
2511 self::$scaleinfo = false;
2512 }
2513 return self::$scaleinfo;
2514 }
2515 /**
2516 * Get information on outcomes
2517 * @param int $courseid
2518 * @return grade_plugin_info
2519 */
2520 public static function get_info_outcomes($courseid) {
f7fcf4cd 2521 global $CFG, $SITE;
4d5059d4
SH
2522
2523 if (self::$outcomeinfo !== null) {
2524 return self::$outcomeinfo;
2525 }
2526 $context = get_context_instance(CONTEXT_COURSE, $courseid);
2527 $canmanage = has_capability('moodle/grade:manage', $context);
2528 $canupdate = has_capability('moodle/course:update', $context);
2529 if (!empty($CFG->enableoutcomes) && ($canmanage || $canupdate)) {
2530 $outcomes = array();
2531 if ($canupdate) {
f7fcf4cd
AD
2532 if ($courseid!=$SITE->id) {
2533 $url = new moodle_url('/grade/edit/outcome/course.php', array('id'=>$courseid));
2534 $outcomes['course'] = new grade_plugin_info('course', $url, get_string('outcomescourse', 'grades'));
2535 }
4d5059d4
SH
2536 $url = new moodle_url('/grade/edit/outcome/index.php', array('id'=>$courseid));
2537 $outcomes['edit'] = new grade_plugin_info('edit', $url, get_string('editoutcomes', 'grades'));
c46aeeab
DC
2538 $url = new moodle_url('/grade/edit/outcome/import.php', array('courseid'=>$courseid));
2539 $outcomes['import'] = new grade_plugin_info('import', $url, get_string('importoutcomes', 'grades'));
4d5059d4 2540 } else {
f7fcf4cd
AD
2541 if ($courseid!=$SITE->id) {
2542 $url = new moodle_url('/grade/edit/outcome/course.php', array('id'=>$courseid));
2543 $outcomes['edit'] = new grade_plugin_info('edit', $url, get_string('outcomescourse', 'grades'));
2544 }
4d5059d4
SH
2545 }
2546 self::$outcomeinfo = $outcomes;
2547 } else {
2548 self::$outcomeinfo = false;
2549 }
2550 return self::$outcomeinfo;
2551 }
2552 /**
2553 * Get information on editing structures
2554 * @param int $courseid
2555 * @return array
2556 */
2557 public static function get_info_edit_structure($courseid) {
2558 if (self::$edittree !== null) {
2559 return self::$edittree;
2560 }
2561 if (has_capability('moodle/grade:manage', get_context_instance(CONTEXT_COURSE, $courseid))) {
2562 $url = new moodle_url('/grade/edit/tree/index.php', array('sesskey'=>sesskey(), 'showadvanced'=>'0', 'id'=>$courseid));
2563 self::$edittree = array(
2564 'simpleview' => new grade_plugin_info('simpleview', $url, get_string('simpleview', 'grades')),
2565 'fullview' => new grade_plugin_info('fullview', new moodle_url($url, array('showadvanced'=>'1')), get_string('fullview', 'grades'))
2566 );
2567 } else {
2568 self::$edittree = false;
2569 }
2570 return self::$edittree;
2571 }
2572 /**
2573 * Get information on letters
2574 * @param int $courseid
4d27bc79 2575 * @return array
4d5059d4
SH
2576 */
2577 public static function get_info_letters($courseid) {
9fc95b65 2578 global $SITE;
4d5059d4
SH
2579 if (self::$letterinfo !== null) {
2580 return self::$letterinfo;
2581 }
2582 $context = get_context_instance(CONTEXT_COURSE, $courseid);
2583 $canmanage = has_capability('moodle/grade:manage', $context);
2584 $canmanageletters = has_capability('moodle/grade:manageletters', $context);
2585 if ($canmanage || $canmanageletters) {
9fc95b65
AA
2586 // Redirect to system context when report is accessed from admin settings MDL-31633
2587 if ($context->instanceid == $SITE->id) {
2588 $param = array('edit' => 1);
2589 } else {
2590 $param = array('edit' => 1,'id' => $context->id);
2591 }
4d5059d4 2592 self::$letterinfo = array(
54caa598 2593 'view' => new grade_plugin_info('view', new moodle_url('/grade/edit/letter/index.php', array('id'=>$context->id)), get_string('view')),
9fc95b65 2594 'edit' => new grade_plugin_info('edit', new moodle_url('/grade/edit/letter/index.php', $param), get_string('edit'))
4d5059d4
SH
2595 );
2596 } else {
2597 self::$letterinfo = false;
2598 }
2599 return self::$letterinfo;
2600 }
2601 /**
2602 * Get information import plugins
2603 * @param int $courseid
2604 * @return array
2605 */
2606 public static function get_plugins_import($courseid) {
2607 global $CFG;
2608
2609 if (self::$importplugins !== null) {
2610 return self::$importplugins;
2611 }
2612 $importplugins = array();
2613 $context = get_context_instance(CONTEXT_COURSE, $courseid);
2614
2615 if (has_capability('moodle/grade:import', $context)) {
2616 foreach (get_plugin_list('gradeimport') as $plugin => $plugindir) {
2617 if (!has_capability('gradeimport/'.$plugin.':view', $context)) {
2618 continue;
2619 }
b5e7b2bf 2620 $pluginstr = get_string('pluginname', 'gradeimport_'.$plugin);
4d5059d4
SH
2621 $url = new moodle_url('/grade/import/'.$plugin.'/index.php', array('id'=>$courseid));
2622 $importplugins[$plugin] = new grade_plugin_info($plugin, $url, $pluginstr);
2623 }
2624
2625
2626 if ($CFG->gradepublishing) {
2627 $url = new moodle_url('/grade/import/keymanager.php', array('id'=>$courseid));
2628 $importplugins['keymanager'] = new grade_plugin_info('keymanager', $url, get_string('keymanager', 'grades'));
2629 }
2630 }
2631
2632 if (count($importplugins) > 0) {
2633 asort($importplugins);
2634 self::$importplugins = $importplugins;
2635 } else {
2636 self::$importplugins = false;
2637 }
2638 return self::$importplugins;
2639 }
2640 /**
2641 * Get information export plugins
2642 * @param int $courseid
2643 * @return array
2644 */
2645 public static function get_plugins_export($courseid) {
2646 global $CFG;
2647
2648 if (self::$exportplugins !== null) {
2649 return self::$exportplugins;
2650 }
2651 $context = get_context_instance(CONTEXT_COURSE, $courseid);
2652 $exportplugins = array();
2653 if (has_capability('moodle/grade:export', $context)) {
2654 foreach (get_plugin_list('gradeexport') as $plugin => $plugindir) {
2655 if (!has_capability('gradeexport/'.$plugin.':view', $context)) {
2656 continue;
2657 }
b5e7b2bf 2658 $pluginstr = get_string('pluginname', 'gradeexport_'.$plugin);
4d5059d4
SH
2659 $url = new moodle_url('/grade/export/'.$plugin.'/index.php', array('id'=>$courseid));
2660 $exportplugins[$plugin] = new grade_plugin_info($plugin, $url, $pluginstr);
2661 }
2662
2663 if ($CFG->gradepublishing) {
2664 $url = new moodle_url('/grade/export/keymanager.php', array('id'=>$courseid));
2665 $exportplugins['keymanager'] = new grade_plugin_info('keymanager', $url, get_string('keymanager', 'grades'));
2666 }
2667 }
2668 if (count($exportplugins) > 0) {
2669 asort($exportplugins);
2670 self::$exportplugins = $exportplugins;
2671 } else {
2672 self::$exportplugins = false;
2673 }
2674 return self::$exportplugins;
2675 }
d4dcfc6b 2676}