MDL-19163 restore - avoid some notices. Merged from 19_STABLE
[moodle.git] / grade / lib.php
CommitLineData
3af29899 1<?php //$Id$
cbff94ba 2
8ad36f4c 3///////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Modular Object-Oriented Dynamic Learning Environment //
8// http://moodle.com //
9// //
10// Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
11// //
12// This program is free software; you can redistribute it and/or modify //
13// it under the terms of the GNU General Public License as published by //
14// the Free Software Foundation; either version 2 of the License, or //
15// (at your option) any later version. //
16// //
17// This program is distributed in the hope that it will be useful, //
18// but WITHOUT ANY WARRANTY; without even the implied warranty of //
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20// GNU General Public License for more details: //
21// //
22// http://www.gnu.org/copyleft/gpl.html //
23// //
24///////////////////////////////////////////////////////////////////////////
25
7a6b7acf 26require_once $CFG->libdir.'/gradelib.php';
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.
0f5660f7 31 */
32class graded_users_iterator {
d24832f9 33 public $course;
34 public $grade_items;
35 public $groupid;
36 public $users_rs;
37 public $grades_rs;
38 public $gradestack;
39 public $sortfield1;
40 public $sortorder1;
41 public $sortfield2;
42 public $sortorder2;
0f5660f7 43
44 /**
45 * Constructor
d08bba83 46 * @param $course object
0f5660f7 47 * @param array grade_items array of grade items, if not specified only user info returned
48 * @param int $groupid iterate only group users if present
d08bba83 49 * @param string $sortfield1 The first field of the users table by which the array of users will be sorted
50 * @param string $sortorder1 The order in which the first sorting field will be sorted (ASC or DESC)
51 * @param string $sortfield2 The second field of the users table by which the array of users will be sorted
52 * @param string $sortorder2 The order in which the second sorting field will be sorted (ASC or DESC)
0f5660f7 53 */
d24832f9 54 public function graded_users_iterator($course, $grade_items=null, $groupid=0, $sortfield1='lastname', $sortorder1='ASC', $sortfield2='firstname', $sortorder2='ASC') {
0f5660f7 55 $this->course = $course;
56 $this->grade_items = $grade_items;
57 $this->groupid = $groupid;
d08bba83 58 $this->sortfield1 = $sortfield1;
59 $this->sortorder1 = $sortorder1;
60 $this->sortfield2 = $sortfield2;
61 $this->sortorder2 = $sortorder2;
0f5660f7 62
63 $this->gradestack = array();
64 }
65
66 /**
67 * Initialise the iterator
68 * @return boolean success
69 */
d24832f9 70 public function init() {
71 global $CFG, $DB;
0f5660f7 72
73 $this->close();
74
75 grade_regrade_final_grades($this->course->id);
76 $course_item = grade_item::fetch_course_item($this->course->id);
77 if ($course_item->needsupdate) {
78 // can not calculate all final grades - sorry
79 return false;
80 }
81
e58fcb35 82 list($gradebookroles_sql, $params) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr0');
0f5660f7 83
84 $relatedcontexts = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $this->course->id));
85
86 if ($this->groupid) {
d24832f9 87 $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id";
e58fcb35 88 $groupwheresql = "AND gm.groupid = :groupid";
d24832f9 89 // $params contents: gradebookroles
e58fcb35 90 $params['groupid'] = $this->groupid;
0f5660f7 91 } else {
92 $groupsql = "";
93 $groupwheresql = "";
94 }
95
345674ca 96 if (empty($this->sortfield1)) {
97 // we must do some sorting even if not specified
98 $ofields = ", u.id AS usrt";
99 $order = "usrt ASC";
100
101 } else {
102 $ofields = ", u.$this->sortfield1 AS usrt1";
103 $order = "usrt1 $this->sortorder1";
104 if (!empty($this->sortfield2)) {
0febb12d 105 $ofields .= ", u.$this->sortfield2 AS usrt2";
345674ca 106 $order .= ", usrt2 $this->sortorder2";
107 }
108 if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') {
109 // user order MUST be the same in both queries, must include the only unique user->id if not already present
110 $ofields .= ", u.id AS usrt";
111 $order .= ", usrt ASC";
112 }
113 }
114
d24832f9 115 // $params contents: gradebookroles and groupid (for $groupwheresql)
345674ca 116 $users_sql = "SELECT u.* $ofields
d24832f9 117 FROM {user} u
118 INNER JOIN {role_assignments} ra ON u.id = ra.userid
0f5660f7 119 $groupsql
5c75a0a3 120 WHERE ra.roleid $gradebookroles_sql
0f5660f7 121 AND ra.contextid $relatedcontexts
345674ca 122 $groupwheresql
123 ORDER BY $order";
d08bba83 124
d24832f9 125 $this->users_rs = $DB->get_recordset_sql($users_sql, $params);
0f5660f7 126
127 if (!empty($this->grade_items)) {
128 $itemids = array_keys($this->grade_items);
e58fcb35 129 list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items0');
d24832f9 130 $params = array_merge($params, $grades_params);
0f5660f7 131
d24832f9 132 // $params contents: gradebookroles, groupid (for $groupwheresql) and itemids
345674ca 133 $grades_sql = "SELECT g.* $ofields
d24832f9 134 FROM {grade_grades} g
135 INNER JOIN {user} u ON g.userid = u.id
136 INNER JOIN {role_assignments} ra ON u.id = ra.userid
0f5660f7 137 $groupsql
d24832f9 138 WHERE ra.roleid $gradebookroles_sql
0f5660f7 139 AND ra.contextid $relatedcontexts
0f5660f7 140 $groupwheresql
d24832f9 141 AND g.itemid $itemidsql
345674ca 142 ORDER BY $order, g.itemid ASC";
d24832f9 143 $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params);
345674ca 144 } else {
145 $this->grades_rs = false;
0f5660f7 146 }
345674ca 147
0f5660f7 148 return true;
149 }
150
151 /**
152 * Returns information about the next user
153 * @return mixed array of user info, all grades and feedback or null when no more users found
154 */
155 function next_user() {
03cedd62 156 if (!$this->users_rs) {
0f5660f7 157 return false; // no users present
158 }
159
5c75a0a3 160 if (!$this->users_rs->valid()) {
345674ca 161 if ($current = $this->_pop()) {
162 // this is not good - user or grades updated between the two reads above :-(
163 }
164
0f5660f7 165 return false; // no more users
5c75a0a3 166 } else {
167 $user = $this->users_rs->current();
168 $this->users_rs->next();
0f5660f7 169 }
170
345674ca 171 // find grades of this user
0f5660f7 172 $grade_records = array();
173 while (true) {
174 if (!$current = $this->_pop()) {
175 break; // no more grades
176 }
177
5c75a0a3 178 if (empty($current->userid)) {
179 break;
180 }
181
345674ca 182 if ($current->userid != $user->id) {
183 // grade of the next user, we have all for this user
0f5660f7 184 $this->_push($current);
185 break;
186 }
187
188 $grade_records[$current->itemid] = $current;
189 }
190
191 $grades = array();
192 $feedbacks = array();
193
d08bba83 194 if (!empty($this->grade_items)) {
345674ca 195 foreach ($this->grade_items as $grade_item) {
196 if (array_key_exists($grade_item->id, $grade_records)) {
197 $feedbacks[$grade_item->id]->feedback = $grade_records[$grade_item->id]->feedback;
198 $feedbacks[$grade_item->id]->feedbackformat = $grade_records[$grade_item->id]->feedbackformat;
199 unset($grade_records[$grade_item->id]->feedback);
200 unset($grade_records[$grade_item->id]->feedbackformat);
201 $grades[$grade_item->id] = new grade_grade($grade_records[$grade_item->id], false);
202 } else {
203 $feedbacks[$grade_item->id]->feedback = '';
204 $feedbacks[$grade_item->id]->feedbackformat = FORMAT_MOODLE;
205 $grades[$grade_item->id] = new grade_grade(array('userid'=>$user->id, 'itemid'=>$grade_item->id), false);
206 }
0f5660f7 207 }
208 }
209
210 $result = new object();
211 $result->user = $user;
212 $result->grades = $grades;
213 $result->feedbacks = $feedbacks;
214
215 return $result;
216 }
217
218 /**
219 * Close the iterator, do not forget to call this function.
220 * @return void
221 */
222 function close() {
caffc55a 223 if ($this->users_rs) {
d24832f9 224 $this->users_rs->close();
caffc55a 225 $this->users_rs = null;
0f5660f7 226 }
caffc55a 227 if ($this->grades_rs) {
d24832f9 228 $this->grades_rs->close();
caffc55a 229 $this->grades_rs = null;
0f5660f7 230 }
231 $this->gradestack = array();
232 }
233
234 /**
235 * Internal function
236 */
237 function _push($grade) {
238 array_push($this->gradestack, $grade);
239 }
240
241 /**
242 * Internal function
243 */
244 function _pop() {
d24832f9 245 global $DB;
0f5660f7 246 if (empty($this->gradestack)) {
03cedd62 247 if (!$this->grades_rs) {
0f5660f7 248 return NULL; // no grades present
249 }
250
5c75a0a3 251 if ($this->grades_rs->next()) {
0f5660f7 252 return NULL; // no more grades
253 }
254
5c75a0a3 255 return $this->grades_rs->current();
0f5660f7 256 } else {
257 return array_pop($this->gradestack);
258 }
259 }
260}
261
d08bba83 262/**
263 * Print a selection popup form of the graded users in a course.
264 *
265 * @param int $courseid id of the course
266 * @param string $actionpage The page receiving the data from the popoup form
267 * @param int $userid id of the currently selected user (or 'all' if they are all selected)
772229f3 268 * @param int $groupid id of requested group, 0 means all
7ac88172 269 * @param int $includeall bool include all option
d08bba83 270 * @param bool $return If true, will return the HTML, otherwise, will print directly
271 * @return null
272 */
7ac88172 273function print_graded_users_selector($course, $actionpage, $userid=0, $groupid=0, $includeall=true, $return=false) {
879c99bb 274 global $CFG, $USER;
345674ca 275
879c99bb 276 if (is_null($userid)) {
277 $userid = $USER->id;
278 }
d08bba83 279
280 $context = get_context_instance(CONTEXT_COURSE, $course->id);
281
282 $menu = array(); // Will be a list of userid => user name
283
772229f3 284 $gui = new graded_users_iterator($course, null, $groupid);
d08bba83 285 $gui->init();
10f5c046 286
345674ca 287
10f5c046 288 $label = get_string('selectauser', 'grades');
7ac88172 289 if ($includeall) {
879c99bb 290 $menu[0] = get_string('allusers', 'grades');
10f5c046 291 $label = get_string('selectalloroneuser', 'grades');
d08bba83 292 }
345674ca 293
5c75a0a3 294 $nextuser = $gui->next_user();
295
d08bba83 296 while ($userdata = $gui->next_user()) {
297 $user = $userdata->user;
298 $menu[$user->id] = fullname($user);
299 }
300
301 $gui->close();
302
7ac88172 303 if ($includeall) {
879c99bb 304 $menu[0] .= " (" . (count($menu) - 1) . ")";
305 }
306
bb776ef4 307 return popup_form($CFG->wwwroot.'/grade/' . $actionpage . '&amp;userid=', $menu, 'choosegradeduser', $userid, null, '', '',
10f5c046 308 $return, 'self', $label);
d08bba83 309}
310
0610812a 311/**
312 * Print grading plugin selection popup form.
313 *
314 * @param int $courseid id of course
315 * @param string $active_type type of plugin on current page - import, export, report or edit
316 * @param string $active_plugin active plugin type - grader, user, cvs, ...
317 * @param boolean $return return as string
318 * @return nothing or string if $return true
319 */
dc482cfa 320function print_grade_plugin_selector($plugin_info, $return=false) {
321 global $CFG;
322
323 $menu = array();
324 $count = 0;
325 $active = '';
326
327 foreach ($plugin_info as $plugin_type => $plugins) {
328 if ($plugin_type == 'strings') {
329 continue;
330 }
331
7981d537 332 $first_plugin = reset($plugins);
dc482cfa 333
38a9fc55 334 $menu[$first_plugin['link'].'&amp;'] = '--'.$plugin_info['strings'][$plugin_type];
7981d537 335
336 if (empty($plugins['id'])) {
dc482cfa 337 foreach ($plugins as $plugin) {
34f00627 338 $menu[$plugin['link']] = $plugin['string'];
dc482cfa 339 $count++;
340 }
341 }
342 }
343
344/// finally print/return the popup form
345 if ($count > 1) {
7981d537 346 $select = popup_form('', $menu, 'choosepluginreport', '', get_string('chooseaction', 'grades'), '', '', true, 'self');
7981d537 347 if ($return) {
348 return $select;
349 } else {
350 echo $select;
351 }
dc482cfa 352 } else {
353 // only one option - no plugin selector needed
354 return '';
355 }
356}
357
358/**
359 * Print grading plugin selection tab-based navigation.
360 *
361 * @param int $courseid id of course
362 * @param string $active_type type of plugin on current page - import, export, report or edit
363 * @param string $active_plugin active plugin type - grader, user, cvs, ...
364 * @param boolean $return return as string
dc482cfa 365 * @return nothing or string if $return true
366 */
b827784e 367function grade_print_tabs($active_type, $active_plugin, $plugin_info, $return=false) {
dc482cfa 368 global $CFG, $COURSE;
369
370 if (!isset($currenttab)) {
371 $currenttab = '';
372 }
373
374 $tabs = array();
375 $top_row = array();
376 $bottom_row = array();
377 $inactive = array($active_plugin);
378 $activated = array();
379
380 $count = 0;
381 $active = '';
382
383 foreach ($plugin_info as $plugin_type => $plugins) {
384 if ($plugin_type == 'strings') {
385 continue;
386 }
387
388 // If $plugins is actually the definition of a child-less parent link:
389 if (!empty($plugins['id'])) {
27eef3bb 390 $string = $plugins['string'];
391 if (!empty($plugin_info[$active_type]['parent'])) {
392 $string = $plugin_info[$active_type]['parent']['string'];
393 }
394
395 $top_row[] = new tabobject($plugin_type, $plugins['link'], $string);
dc482cfa 396 continue;
397 }
398
399 $first_plugin = reset($plugins);
400 $url = $first_plugin['link'];
401
402 if ($plugin_type == 'report') {
403 $url = $CFG->wwwroot.'/grade/report/index.php?id='.$COURSE->id;
404 }
405
406 $top_row[] = new tabobject($plugin_type, $url, $plugin_info['strings'][$plugin_type]);
407
408 if ($active_type == $plugin_type) {
409 foreach ($plugins as $plugin) {
410 $bottom_row[] = new tabobject($plugin['id'], $plugin['link'], $plugin['string']);
411 if ($plugin['id'] == $active_plugin) {
412 $inactive = array($plugin['id']);
dc482cfa 413 }
414 }
415 }
416 }
417
418 $tabs[] = $top_row;
419 $tabs[] = $bottom_row;
420
421 if ($return) {
422 return print_tabs($tabs, $active_type, $inactive, $activated, true);
423 } else {
424 print_tabs($tabs, $active_type, $inactive, $activated);
425 }
426}
427
428function grade_get_plugin_info($courseid, $active_type, $active_plugin) {
cbff94ba 429 global $CFG;
cbff94ba 430
3af29899 431 $context = get_context_instance(CONTEXT_COURSE, $courseid);
cbff94ba 432
dc482cfa 433 $plugin_info = array();
6e2f3121 434 $count = 0;
3af29899 435 $active = '';
dc482cfa 436 $url_prefix = $CFG->wwwroot . '/grade/';
437
438 // Language strings
439 $plugin_info['strings'] = array(
440 'report' => get_string('view'),
441 'edittree' => get_string('edittree', 'grades'),
442 'scale' => get_string('scales'),
443 'outcome' => get_string('outcomes', 'grades'),
444 'letter' => get_string('letters', 'grades'),
445 'export' => get_string('export', 'grades'),
b827784e 446 'import' => get_string('import'),
54c4a2cb 447 'preferences' => get_string('mypreferences', 'grades'),
b827784e 448 'settings' => get_string('settings'));
449
450 // Settings tab first
451 if (has_capability('moodle/course:update', $context)) {
452 $url = $url_prefix.'edit/settings/index.php?id='.$courseid;
453
454 if ($active_type == 'settings' and $active_plugin == 'course') {
455 $active = $url;
456 }
457
38a9fc55 458 $plugin_info['settings'] = array();
459 $plugin_info['settings']['course'] = array('id' => 'coursesettings', 'link' => $url, 'string' => get_string('course'));
b827784e 460 $count++;
461 }
462
cbff94ba 463
3af29899 464/// report plugins with its special structure
465 if ($reports = get_list_of_plugins('grade/report', 'CVS')) { // Get all installed reports
466 foreach ($reports as $key => $plugin) { // Remove ones we can't see
467 if (!has_capability('gradereport/'.$plugin.':view', $context)) {
468 unset($reports[$key]);
cbff94ba 469 }
470 }
04678d8e 471 }
b827784e 472
3af29899 473 $reportnames = array();
b827784e 474
3af29899 475 if (!empty($reports)) {
476 foreach ($reports as $plugin) {
dc482cfa 477 $url = $url_prefix.'report/'.$plugin.'/index.php?id='.$courseid;
3af29899 478 if ($active_type == 'report' and $active_plugin == $plugin ) {
479 $active = $url;
cbff94ba 480 }
dc482cfa 481 $reportnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradereport_'.$plugin));
b827784e 482
483 // Add link to preferences tab if such a page exists
484 if (file_exists($CFG->dirroot . '/grade/report/'.$plugin.'/preferences.php')) {
485 $pref_url = $url_prefix.'report/'.$plugin.'/preferences.php?id='.$courseid;
54c4a2cb 486 $plugin_info['preferences'][$plugin] = array('id' => $plugin, 'link' => $pref_url, 'string' => get_string('modulename', 'gradereport_'.$plugin));
b827784e 487 }
488
6e2f3121 489 $count++;
cbff94ba 490 }
3af29899 491 asort($reportnames);
cbff94ba 492 }
3af29899 493 if (!empty($reportnames)) {
dc482cfa 494 $plugin_info['report']=$reportnames;
495 }
496
497/// editing scripts - not real plugins
498 if (has_capability('moodle/grade:manage', $context)
499 or has_capability('moodle/grade:manageletters', $context)
500 or has_capability('moodle/course:managescales', $context)
501 or has_capability('moodle/course:update', $context)) {
502
503 if (has_capability('moodle/grade:manage', $context)) {
504 $url = $url_prefix.'edit/tree/index.php?sesskey='.sesskey().'&amp;showadvanced=0&amp;id='.$courseid;
505 $url_adv = $url_prefix.'edit/tree/index.php?sesskey='.sesskey().'&amp;showadvanced=1&amp;id='.$courseid;
506
507 if ($active_type == 'edittree' and $active_plugin == 'simpleview') {
508 $active = $url;
509 } elseif ($active_type == 'edittree' and $active_plugin == 'fullview') {
510 $active = $url_adv;
511 }
512
513 $plugin_info['edittree'] = array();
514 $plugin_info['edittree']['simpleview'] = array('id' => 'simpleview', 'link' => $url, 'string' => get_string('simpleview', 'grades'));
515 $plugin_info['edittree']['fullview'] = array('id' => 'fullview', 'link' => $url_adv, 'string' => get_string('fullview', 'grades'));
516 $count++;
517 }
518
519 if (has_capability('moodle/course:managescales', $context)) {
520 $url = $url_prefix.'edit/scale/index.php?id='.$courseid;
27eef3bb 521
dc482cfa 522 if ($active_type == 'scale' and is_null($active_plugin)) {
523 $active = $url;
524 }
27eef3bb 525
38a9fc55 526 $plugin_info['scale'] = array();
527
27eef3bb 528 if ($active_type == 'scale' and $active_plugin == 'edit') {
529 $edit_url = $url_prefix.'edit/scale/edit.php?courseid='.$courseid.'&amp;id='.optional_param('id', 0, PARAM_INT);
530 $active = $edit_url;
38a9fc55 531 $plugin_info['scale']['view'] = array('id' => 'edit', 'link' => $edit_url, 'string' => get_string('edit'),
27eef3bb 532 'parent' => array('id' => 'scale', 'link' => $url, 'string' => get_string('scales')));
533 } else {
38a9fc55 534 $plugin_info['scale']['view'] = array('id' => 'scale', 'link' => $url, 'string' => get_string('view'));
27eef3bb 535 }
536
dc482cfa 537 $count++;
538 }
539
540 if (!empty($CFG->enableoutcomes) && (has_capability('moodle/grade:manage', $context) or
541 has_capability('moodle/course:update', $context))) {
542
543 $url_course = $url_prefix.'edit/outcome/course.php?id='.$courseid;
544 $url_edit = $url_prefix.'edit/outcome/index.php?id='.$courseid;
545
546 $plugin_info['outcome'] = array();
547
548 if (has_capability('moodle/course:update', $context)) { // Default to course assignment
549 $plugin_info['outcome']['course'] = array('id' => 'course', 'link' => $url_course, 'string' => get_string('outcomescourse', 'grades'));
550 $plugin_info['outcome']['edit'] = array('id' => 'edit', 'link' => $url_edit, 'string' => get_string('editoutcomes', 'grades'));
551 } else {
552 $plugin_info['outcome'] = array('id' => 'edit', 'link' => $url_course, 'string' => get_string('outcomescourse', 'grades'));
553 }
554
555 if ($active_type == 'outcome' and is_null($active_plugin)) {
556 $active = $url_edit;
557 } elseif ($active_type == 'outcome' and $active_plugin == 'course' ) {
558 $active = $url_course;
559 } elseif ($active_type == 'outcome' and $active_plugin == 'edit' ) {
560 $active = $url_edit;
27eef3bb 561 } elseif ($active_type == 'outcome' and $active_plugin == 'import') {
562 $plugin_info['outcome']['import'] = array('id' => 'import', 'link' => null, 'string' => get_string('importoutcomes', 'grades'));
dc482cfa 563 }
564
565 $count++;
566 }
567
568 if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:manageletters', $context)) {
569 $course_context = get_context_instance(CONTEXT_COURSE, $courseid);
570 $url = $url_prefix.'edit/letter/index.php?id='.$courseid;
571 $url_edit = $url_prefix.'edit/letter/edit.php?id='.$course_context->id;
572
573 if ($active_type == 'letter' and $active_plugin == 'view' ) {
574 $active = $url;
575 } elseif ($active_type == 'letter' and $active_plugin == 'edit' ) {
576 $active = $url_edit;
577 }
578
579 $plugin_info['letter'] = array();
580 $plugin_info['letter']['view'] = array('id' => 'view', 'link' => $url, 'string' => get_string('view'));
581 $plugin_info['letter']['edit'] = array('id' => 'edit', 'link' => $url_edit, 'string' => get_string('edit'));
582 $count++;
583 }
cbff94ba 584 }
cbff94ba 585
3af29899 586/// standard import plugins
e2008be2 587 if ($imports = get_list_of_plugins('grade/import', 'CVS')) { // Get all installed import plugins
3af29899 588 foreach ($imports as $key => $plugin) { // Remove ones we can't see
589 if (!has_capability('gradeimport/'.$plugin.':view', $context)) {
590 unset($imports[$key]);
cbff94ba 591 }
592 }
593 }
3af29899 594 $importnames = array();
595 if (!empty($imports)) {
596 foreach ($imports as $plugin) {
dc482cfa 597 $url = $url_prefix.'import/'.$plugin.'/index.php?id='.$courseid;
65dd61bd 598 if ($active_type == 'import' and $active_plugin == $plugin ) {
3af29899 599 $active = $url;
600 }
dc482cfa 601 $importnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradeimport_'.$plugin));
6e2f3121 602 $count++;
281ffa4a 603 }
3af29899 604 asort($importnames);
281ffa4a 605 }
3af29899 606 if (!empty($importnames)) {
dc482cfa 607 $plugin_info['import']=$importnames;
281ffa4a 608 }
281ffa4a 609
3af29899 610/// standard export plugins
e2008be2 611 if ($exports = get_list_of_plugins('grade/export', 'CVS')) { // Get all installed export plugins
3af29899 612 foreach ($exports as $key => $plugin) { // Remove ones we can't see
613 if (!has_capability('gradeexport/'.$plugin.':view', $context)) {
614 unset($exports[$key]);
281ffa4a 615 }
616 }
cbff94ba 617 }
3af29899 618 $exportnames = array();
619 if (!empty($exports)) {
620 foreach ($exports as $plugin) {
dc482cfa 621 $url = $url_prefix.'export/'.$plugin.'/index.php?id='.$courseid;
65dd61bd 622 if ($active_type == 'export' and $active_plugin == $plugin ) {
3af29899 623 $active = $url;
624 }
dc482cfa 625 $exportnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradeexport_'.$plugin));
6e2f3121 626 $count++;
281ffa4a 627 }
3af29899 628 asort($exportnames);
cbff94ba 629 }
dc482cfa 630
3af29899 631 if (!empty($exportnames)) {
dc482cfa 632 $plugin_info['export']=$exportnames;
281ffa4a 633 }
cbff94ba 634
27eef3bb 635 // Key managers
636 if ($CFG->gradepublishing) {
637 $keymanager_url = $url_prefix.'export/keymanager.php?id='.$courseid;
638 $plugin_info['export']['keymanager'] = array('id' => 'keymanager', 'link' => $keymanager_url, 'string' => get_string('keymanager', 'grades'));
639 if ($active_type == 'export' and $active_plugin == 'keymanager' ) {
640 $active = $keymanager_url;
641 }
642 $count++;
643
644 $keymanager_url = $url_prefix.'import/keymanager.php?id='.$courseid;
645 $plugin_info['import']['keymanager'] = array('id' => 'keymanager', 'link' => $keymanager_url, 'string' => get_string('keymanager', 'grades'));
646 if ($active_type == 'import' and $active_plugin == 'keymanager' ) {
647 $active = $keymanager_url;
648 }
649 $count++;
650 }
651
652
dc482cfa 653 foreach ($plugin_info as $plugin_type => $plugins) {
654 if (!empty($plugins['id']) && $active_plugin == $plugins['id']) {
655 $plugin_info['strings']['active_plugin_str'] = $plugins['string'];
656 break;
78ad5f3f 657 }
dc482cfa 658 foreach ($plugins as $plugin) {
659 if ($active_plugin == $plugin['id']) {
660 $plugin_info['strings']['active_plugin_str'] = $plugin['string'];
78ad5f3f 661 }
78ad5f3f 662 }
dc482cfa 663 }
78ad5f3f 664
8354e716 665 // Put settings last
54c4a2cb 666 if (!empty($plugin_info['settings'])) {
667 $settings = $plugin_info['settings'];
668 unset($plugin_info['settings']);
669 $plugin_info['settings'] = $settings;
670 }
671
672 // Put preferences last
673 if (!empty($plugin_info['preferences'])) {
674 $prefs = $plugin_info['preferences'];
675 unset($plugin_info['preferences']);
676 $plugin_info['preferences'] = $prefs;
677 }
678
134fbea2 679 // Check import and export caps
680 if (!has_capability('moodle/grade:export', $context)) {
681 unset($plugin_info['export']);
682 }
683 if (!has_capability('moodle/grade:import', $context)) {
684 unset($plugin_info['import']);
685 }
dc482cfa 686 return $plugin_info;
687}
284abb09 688
de0300ea 689/**
690 * Prints the page headers, breadcrumb trail, page heading, (optional) dropdown navigation menu and
691 * (optional) navigation tabs for any gradebook page. All gradebook pages MUST use these functions
692 * in favour of the usual print_header(), print_header_simple(), print_heading() etc.
693 * !IMPORTANT! Use of tabs.php file in gradebook pages is forbidden unless tabs are switched off at
694 * the site level for the gradebook ($CFG->grade_navmethod = GRADE_NAVMETHOD_DROPDOWN).
695 *
696 * @param int $courseid
697 * @param string $active_type The type of the current page (report, settings, import, export, scales, outcomes, letters)
698 * @param string $active_plugin The plugin of the current page (grader, fullview etc...)
699 * @param string $heading The heading of the page. Tries to guess if none is given
700 * @param boolean $return Whether to return (true) or echo (false) the HTML generated by this function
701 * @param string $bodytags Additional attributes that will be added to the <body> tag
702 * @param string $buttons Additional buttons to display on the page
703 *
704 * @return string HTML code or nothing if $return == false
705 */
653a8648 706function print_grade_page_head($courseid, $active_type, $active_plugin=null, $heading = false, $return=false, $bodytags='', $buttons=false, $extracss=array()) {
dc482cfa 707 global $CFG, $COURSE;
708 $strgrades = get_string('grades');
709 $plugin_info = grade_get_plugin_info($courseid, $active_type, $active_plugin);
284abb09 710
dc482cfa 711 // Determine the string of the active plugin
712 $stractive_plugin = ($active_plugin) ? $plugin_info['strings']['active_plugin_str'] : $heading;
713 $stractive_type = $plugin_info['strings'][$active_type];
e0724506 714
dc482cfa 715 $navlinks = array();
de0300ea 716 $first_link = '';
717
64f4942f 718 if ($active_type == 'settings' && $active_plugin != 'coursesettings') {
de0300ea 719 $first_link = $plugin_info['report'][$active_plugin]['link'];
720 } elseif ($active_type != 'report') {
721 $first_link = $CFG->wwwroot.'/grade/index.php?id='.$COURSE->id;
722 }
723
27eef3bb 724 if ($active_type == 'preferences') {
725 $CFG->stylesheets[] = $CFG->wwwroot . '/grade/report/styles.css';
726 }
727
653a8648 728 foreach ($extracss as $css_url) {
729 $CFG->stylesheets[] = $css_url;
730 }
731
dc482cfa 732 $navlinks[] = array('name' => $strgrades,
de0300ea 733 'link' => $first_link,
dc482cfa 734 'type' => 'misc');
735
736 $active_type_link = '';
737
de0300ea 738 if (!empty($plugin_info[$active_type]['link']) && $plugin_info[$active_type]['link'] != qualified_me()) {
dc482cfa 739 $active_type_link = $plugin_info[$active_type]['link'];
281ffa4a 740 }
741
27eef3bb 742 if (!empty($plugin_info[$active_type]['parent']['link'])) {
743 $active_type_link = $plugin_info[$active_type]['parent']['link'];
744 $navlinks[] = array('name' => $stractive_type, 'link' => $active_type_link, 'type' => 'misc');
745 }
746
c4c97a6d 747 if (empty($plugin_info[$active_type]['id'])) {
748 $navlinks[] = array('name' => $stractive_type, 'link' => $active_type_link, 'type' => 'misc');
749 }
750
de0300ea 751 $navlinks[] = array('name' => $stractive_plugin, 'link' => null, 'type' => 'misc');
dc482cfa 752
753 $navigation = build_navigation($navlinks);
754
c4c97a6d 755 $title = ': ' . $stractive_plugin;
27eef3bb 756 if (empty($plugin_info[$active_type]['id']) || !empty($plugin_info[$active_type]['parent'])) {
c4c97a6d 757 $title = ': ' . $stractive_type . ': ' . $stractive_plugin;
758 }
759
760 $returnval = print_header_simple($strgrades . ': ' . $stractive_type, $title, $navigation, '',
de0300ea 761 $bodytags, true, $buttons, navmenu($COURSE), false, '', $return);
dc482cfa 762
763 // Guess heading if not given explicitly
764 if (!$heading) {
765 $heading = $stractive_plugin;
766 }
767
90dc9c4b 768 if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_DROPDOWN) {
8354e716 769 $returnval .= print_grade_plugin_selector($plugin_info, $return);
770 }
dc482cfa 771 $returnval .= print_heading($heading);
8354e716 772
90dc9c4b 773 if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_TABS) {
de0300ea 774 $returnval .= grade_print_tabs($active_type, $active_plugin, $plugin_info, $return);
8354e716 775 }
dc482cfa 776
777 if ($return) {
778 return $returnval;
6e2f3121 779 }
cbff94ba 780}
781
0610812a 782/**
7a6b7acf 783 * Utility class used for return tracking when using edit and other forms in grade plugins
0610812a 784 */
3af29899 785class grade_plugin_return {
d24832f9 786 public $type;
787 public $plugin;
788 public $courseid;
789 public $userid;
790 public $page;
281ffa4a 791
0610812a 792 /**
793 * Constructor
794 * @param array $params - associative array with return parameters, if null parameter are taken from _GET or _POST
795 */
d24832f9 796 public function grade_plugin_return ($params=null) {
3af29899 797 if (empty($params)) {
798 $this->type = optional_param('gpr_type', null, PARAM_SAFEDIR);
799 $this->plugin = optional_param('gpr_plugin', null, PARAM_SAFEDIR);
800 $this->courseid = optional_param('gpr_courseid', null, PARAM_INT);
801 $this->userid = optional_param('gpr_userid', null, PARAM_INT);
802 $this->page = optional_param('gpr_page', null, PARAM_INT);
a983b6ec 803
a983b6ec 804 } else {
3af29899 805 foreach ($params as $key=>$value) {
806 if (array_key_exists($key, $this)) {
807 $this->$key = $value;
808 }
cbff94ba 809 }
810 }
6cd8c592 811 }
812
0610812a 813 /**
814 * Returns return parameters as options array suitable for buttons.
815 * @return array options
816 */
d24832f9 817 public function get_options() {
7a6b7acf 818 if (empty($this->type)) {
3af29899 819 return array();
865e9a82 820 }
6cd8c592 821
3af29899 822 $params = array();
6cd8c592 823
7a6b7acf 824 if (!empty($this->plugin)) {
825 $params['plugin'] = $this->plugin;
826 }
6cd8c592 827
3af29899 828 if (!empty($this->courseid)) {
829 $params['id'] = $this->courseid;
6cd8c592 830 }
9c61ba4d 831
3af29899 832 if (!empty($this->userid)) {
833 $params['userid'] = $this->userid;
9c61ba4d 834 }
9c61ba4d 835
3af29899 836 if (!empty($this->page)) {
837 $params['page'] = $this->page;
cbff94ba 838 }
865e9a82 839
3af29899 840 return $params;
cbff94ba 841 }
cbff94ba 842
0610812a 843 /**
844 * Returns return url
845 * @param string $default default url when params not set
846 * @return string url
847 */
d24832f9 848 public function get_return_url($default, $extras=null) {
3af29899 849 global $CFG;
cbff94ba 850
3af29899 851 if (empty($this->type) or empty($this->plugin)) {
852 return $default;
cbff94ba 853 }
854
65dd61bd 855 $url = $CFG->wwwroot.'/grade/'.$this->type.'/'.$this->plugin.'/index.php';
856 $glue = '?';
cbff94ba 857
3af29899 858 if (!empty($this->courseid)) {
859 $url .= $glue.'id='.$this->courseid;
860 $glue = '&amp;';
cbff94ba 861 }
cbff94ba 862
3af29899 863 if (!empty($this->userid)) {
864 $url .= $glue.'userid='.$this->userid;
865 $glue = '&amp;';
cbff94ba 866 }
7e2d7c92 867
3af29899 868 if (!empty($this->page)) {
869 $url .= $glue.'page='.$this->page;
65dd61bd 870 $glue = '&amp;';
871 }
872
873 if (!empty($extras)) {
874 foreach($extras as $key=>$value) {
875 $url .= $glue.$key.'='.$value;
876 $glue = '&amp;';
7a6b7acf 877 }
cbff94ba 878 }
cbff94ba 879
3af29899 880 return $url;
cbff94ba 881 }
cbff94ba 882
0610812a 883 /**
884 * Returns string with hidden return tracking form elements.
885 * @return string
886 */
d24832f9 887 public function get_form_fields() {
7a6b7acf 888 if (empty($this->type)) {
3af29899 889 return '';
cbff94ba 890 }
cbff94ba 891
3af29899 892 $result = '<input type="hidden" name="gpr_type" value="'.$this->type.'" />';
7a6b7acf 893
894 if (!empty($this->plugin)) {
895 $result .= '<input type="hidden" name="gpr_plugin" value="'.$this->plugin.'" />';
896 }
0ca5abd6 897
3af29899 898 if (!empty($this->courseid)) {
899 $result .= '<input type="hidden" name="gpr_courseid" value="'.$this->courseid.'" />';
cbff94ba 900 }
cbff94ba 901
3af29899 902 if (!empty($this->userid)) {
903 $result .= '<input type="hidden" name="gpr_userid" value="'.$this->userid.'" />';
cbff94ba 904 }
cbff94ba 905
3af29899 906 if (!empty($this->page)) {
907 $result .= '<input type="hidden" name="gpr_page" value="'.$this->page.'" />';
cbff94ba 908 }
909 }
cbff94ba 910
0610812a 911 /**
912 * Add hidden elements into mform
913 * @param object $mform moodle form object
914 * @return void
915 */
d24832f9 916 public function add_mform_elements(&$mform) {
7a6b7acf 917 if (empty($this->type)) {
3af29899 918 return;
cbff94ba 919 }
cbff94ba 920
3af29899 921 $mform->addElement('hidden', 'gpr_type', $this->type);
922 $mform->setType('gpr_type', PARAM_SAFEDIR);
cbff94ba 923
7a6b7acf 924 if (!empty($this->plugin)) {
925 $mform->addElement('hidden', 'gpr_plugin', $this->plugin);
926 $mform->setType('gpr_plugin', PARAM_SAFEDIR);
927 }
97033c86 928
3af29899 929 if (!empty($this->courseid)) {
930 $mform->addElement('hidden', 'gpr_courseid', $this->courseid);
931 $mform->setType('gpr_courseid', PARAM_INT);
cbff94ba 932 }
cbff94ba 933
3af29899 934 if (!empty($this->userid)) {
935 $mform->addElement('hidden', 'gpr_userid', $this->userid);
936 $mform->setType('gpr_userid', PARAM_INT);
cbff94ba 937 }
cbff94ba 938
3af29899 939 if (!empty($this->page)) {
940 $mform->addElement('hidden', 'gpr_page', $this->page);
941 $mform->setType('gpr_page', PARAM_INT);
cbff94ba 942 }
943 }
281ffa4a 944
0610812a 945 /**
946 * Add return tracking params into url
947 * @param string $url
948 * @return string $url with erturn tracking params
949 */
d24832f9 950 public function add_url_params($url) {
7a6b7acf 951 if (empty($this->type)) {
3af29899 952 return $url;
cbff94ba 953 }
5609f9e6 954
3af29899 955 if (strpos($url, '?') === false) {
956 $url .= '?gpr_type='.$this->type;
957 } else {
958 $url .= '&amp;gpr_type='.$this->type;
cbff94ba 959 }
cbff94ba 960
7a6b7acf 961 if (!empty($this->plugin)) {
962 $url .= '&amp;gpr_plugin='.$this->plugin;
963 }
cbff94ba 964
3af29899 965 if (!empty($this->courseid)) {
966 $url .= '&amp;gpr_courseid='.$this->courseid;
cbff94ba 967 }
cbff94ba 968
3af29899 969 if (!empty($this->userid)) {
970 $url .= '&amp;gpr_userid='.$this->userid;
cbff94ba 971 }
0a8a95c9 972
3af29899 973 if (!empty($this->page)) {
974 $url .= '&amp;gpr_page='.$this->page;
0a8a95c9 975 }
5a412dbf 976
3af29899 977 return $url;
5a412dbf 978 }
5a412dbf 979}
7a6b7acf 980
826c5f86 981/**
982 * Function central to gradebook for building and printing the navigation (breadcrumb trail).
983 * @param string $path The path of the calling script (using __FILE__?)
984 * @param string $pagename The language string to use as the last part of the navigation (non-link)
985 * @param mixed $id Either a plain integer (assuming the key is 'id') or an array of keys and values (e.g courseid => $courseid, itemid...)
986 * @return string
987 */
988function grade_build_nav($path, $pagename=null, $id=null) {
989 global $CFG, $COURSE;
990
991 $strgrades = get_string('grades', 'grades');
992
993 // Parse the path and build navlinks from its elements
994 $dirroot_length = strlen($CFG->dirroot) + 1; // Add 1 for the first slash
995 $path = substr($path, $dirroot_length);
996 $path = str_replace('\\', '/', $path);
997
998 $path_elements = explode('/', $path);
999
1000 $path_elements_count = count($path_elements);
1001
826c5f86 1002 // First link is always 'grade'
1003 $navlinks = array();
1004 $navlinks[] = array('name' => $strgrades,
1005 'link' => $CFG->wwwroot.'/grade/index.php?id='.$COURSE->id,
1006 'type' => 'misc');
1007
1008 $link = '';
1009 $numberofelements = 3;
1010
1011 // Prepare URL params string
1012 $id_string = '?';
1013 if (!is_null($id)) {
1014 if (is_array($id)) {
1015 foreach ($id as $idkey => $idvalue) {
1016 $id_string .= "$idkey=$idvalue&amp;";
1017 }
1018 } else {
1019 $id_string .= "id=$id";
1020 }
1021 }
1022
1023 $navlink4 = null;
1024
0f78c4de 1025 // Remove file extensions from filenames
1026 foreach ($path_elements as $key => $filename) {
1027 $path_elements[$key] = str_replace('.php', '', $filename);
1028 }
1029
826c5f86 1030 // Second level links
1031 switch ($path_elements[1]) {
1032 case 'edit': // No link
1033 if ($path_elements[3] != 'index.php') {
1034 $numberofelements = 4;
1035 }
1036 break;
1037 case 'import': // No link
1038 break;
1039 case 'export': // No link
1040 break;
1041 case 'report':
1042 // $id is required for this link. Do not print it if $id isn't given
1043 if (!is_null($id)) {
1044 $link = $CFG->wwwroot . '/grade/report/index.php' . $id_string;
1045 }
1046
1047 if ($path_elements[2] == 'grader') {
1048 $numberofelements = 4;
1049 }
1050 break;
1051
1052 default:
1053 // If this element isn't among the ones already listed above, it isn't supported, throw an error.
1054 debugging("grade_build_nav() doesn't support ". $path_elements[1] . " as the second path element after 'grade'.");
1055 return false;
1056 }
1057
1058 $navlinks[] = array('name' => get_string($path_elements[1], 'grades'), 'link' => $link, 'type' => 'misc');
1059
1060 // Third level links
1061 if (empty($pagename)) {
1062 $pagename = get_string($path_elements[2], 'grades');
1063 }
1064
1065 switch ($numberofelements) {
1066 case 3:
1067 $navlinks[] = array('name' => $pagename, 'link' => $link, 'type' => 'misc');
1068 break;
1069 case 4:
1070
1071 if ($path_elements[2] == 'grader' AND $path_elements[3] != 'index.php') {
3cf6a6d5 1072 $navlinks[] = array('name' => get_string('modulename', 'gradereport_grader'),
826c5f86 1073 'link' => "$CFG->wwwroot/grade/report/grader/index.php$id_string",
1074 'type' => 'misc');
1075 }
1076 $navlinks[] = array('name' => $pagename, 'link' => '', 'type' => 'misc');
1077 break;
1078 }
1079 $navigation = build_navigation($navlinks);
1080
1081 return $navigation;
d4795a07 1082}
7a6b7acf 1083
e98871a2 1084/**
6cc3e350 1085 * General structure representing grade items in course
e98871a2 1086 */
6cc3e350 1087class grade_structure {
d24832f9 1088 public $context;
e98871a2 1089
d24832f9 1090 public $courseid;
e98871a2 1091
1092 /**
1093 * 1D array of grade items only
1094 */
d24832f9 1095 public $items;
e98871a2 1096
6391ebe7 1097 /**
6cc3e350 1098 * Returns icon of element
1099 * @param object $element
1100 * @param bool $spacerifnone return spacer if no icon found
1101 * @return string icon or spacer
6391ebe7 1102 */
d24832f9 1103 public function get_element_icon(&$element, $spacerifnone=false) {
6cc3e350 1104 global $CFG;
1105
1106 switch ($element['type']) {
1107 case 'item':
1108 case 'courseitem':
1109 case 'categoryitem':
1110 if ($element['object']->is_calculated()) {
dc482cfa 1111 $strcalc = get_string('calculatedgrade', 'grades');
1edd08ab 1112 return '<img src="'.$CFG->pixpath.'/i/calc.gif" class="icon itemicon" title="'.s($strcalc).'" alt="'.s($strcalc).'"/>';
6cc3e350 1113
1114 } else if (($element['object']->is_course_item() or $element['object']->is_category_item())
1115 and ($element['object']->gradetype == GRADE_TYPE_SCALE or $element['object']->gradetype == GRADE_TYPE_VALUE)) {
1116 if ($category = $element['object']->get_item_category()) {
1117 switch ($category->aggregation) {
1118 case GRADE_AGGREGATE_MEAN:
1119 case GRADE_AGGREGATE_MEDIAN:
1120 case GRADE_AGGREGATE_WEIGHTED_MEAN:
1426edac 1121 case GRADE_AGGREGATE_WEIGHTED_MEAN2:
6cc3e350 1122 case GRADE_AGGREGATE_EXTRACREDIT_MEAN:
dc482cfa 1123 $stragg = get_string('aggregation', 'grades');
1edd08ab 1124 return '<img src="'.$CFG->pixpath.'/i/agg_mean.gif" class="icon itemicon" title="'.s($stragg).'" alt="'.s($stragg).'"/>';
0758a08e 1125 case GRADE_AGGREGATE_SUM:
dc482cfa 1126 $stragg = get_string('aggregation', 'grades');
1edd08ab 1127 return '<img src="'.$CFG->pixpath.'/i/agg_sum.gif" class="icon itemicon" title="'.s($stragg).'" alt="'.s($stragg).'"/>';
6cc3e350 1128 }
1129 }
1130
1131 } else if ($element['object']->itemtype == 'mod') {
dc482cfa 1132 $strmodname = get_string('modulename', $element['object']->itemmodule);
1edd08ab 1133 return '<img src="'.$CFG->modpixpath.'/'.$element['object']->itemmodule.'/icon.gif" class="icon itemicon" title="' .s($strmodname).'" alt="' .s($strmodname).'"/>';
6cc3e350 1134
1135 } else if ($element['object']->itemtype == 'manual') {
1136 if ($element['object']->is_outcome_item()) {
dc482cfa 1137 $stroutcome = get_string('outcome', 'grades');
1edd08ab 1138 return '<img src="'.$CFG->pixpath.'/i/outcomes.gif" class="icon itemicon" title="'.s($stroutcome).'" alt="'.s($stroutcome).'"/>';
6cc3e350 1139 } else {
dc482cfa 1140 $strmanual = get_string('manualitem', 'grades');
1edd08ab 1141 return '<img src="'.$CFG->pixpath.'/t/manual_item.gif" class="icon itemicon" title="'.s($strmanual).'" alt="'.s($strmanual).'"/>';
6cc3e350 1142 }
1143 }
1144 break;
1145
1146 case 'category':
dc482cfa 1147 $strcat = get_string('category', 'grades');
1edd08ab 1148 return '<img src="'.$CFG->pixpath.'/f/folder.gif" class="icon itemicon" title="'.s($strcat).'" alt="'.s($strcat).'" />';
6cc3e350 1149 }
1150
1151 if ($spacerifnone) {
1152 return '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="icon itemicon" alt=""/>';
1153 } else {
1154 return '';
1155 }
1156 }
6391ebe7 1157
e98871a2 1158 /**
6cc3e350 1159 * Returns name of element optionally with icon and link
1160 * @param object $element
1161 * @param bool $withlinks
1162 * @param bool $icons
1163 * @param bool $spacerifnone return spacer if no icon found
1164 * @return header string
e98871a2 1165 */
d24832f9 1166 public function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false) {
6cc3e350 1167 global $CFG;
1168
1169 $header = '';
1170
1171 if ($icon) {
1172 $header .= $this->get_element_icon($element, $spacerifnone);
1173 }
1174
dc482cfa 1175 $header .= $element['object']->get_name();
6cc3e350 1176
1177 if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and $element['type'] != 'courseitem') {
1178 return $header;
1179 }
1180
1181 $itemtype = $element['object']->itemtype;
1182 $itemmodule = $element['object']->itemmodule;
1183 $iteminstance = $element['object']->iteminstance;
1184
1185 if ($withlink and $itemtype=='mod' and $iteminstance and $itemmodule) {
46d1af82 1186 if ($cm = get_coursemodule_from_instance($itemmodule, $iteminstance, $this->courseid)) {
6cc3e350 1187
dc482cfa 1188 $a->name = get_string('modulename', $element['object']->itemmodule);
1189 $title = get_string('linktoactivity', 'grades', $a);
46d1af82 1190 $dir = $CFG->dirroot.'/mod/'.$itemmodule;
6cc3e350 1191
46d1af82 1192 if (file_exists($dir.'/grade.php')) {
1193 $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/grade.php?id='.$cm->id;
1194 } else {
1195 $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/view.php?id='.$cm->id;
1196 }
6cc3e350 1197
1edd08ab 1198 $header = '<a href="'.$url.'" title="'.s($title).'">'.$header.'</a>';
46d1af82 1199 }
6cc3e350 1200 }
1201
1202 return $header;
1203 }
1204
1205 /**
1206 * Returns the grade eid - the grade may not exist yet.
1207 * @param $grade_grade object
1208 * @return string eid
1209 */
d24832f9 1210 public function get_grade_eid($grade_grade) {
6cc3e350 1211 if (empty($grade_grade->id)) {
1212 return 'n'.$grade_grade->itemid.'u'.$grade_grade->userid;
1213 } else {
1214 return 'g'.$grade_grade->id;
1215 }
1216 }
1217
1218 /**
1219 * Returns the grade_item eid
1220 * @param $grade_item object
1221 * @return string eid
1222 */
d24832f9 1223 public function get_item_eid($grade_item) {
6cc3e350 1224 return 'i'.$grade_item->id;
1225 }
1226
9ecd4386 1227 function get_params_for_iconstr($element) {
1228 $strparams = new stdClass();
1229 $strparams->category = '';
1230 $strparams->itemname = '';
1231 $strparams->itemmodule = '';
1232 if (!method_exists($element['object'], 'get_name')) {
1233 return $strparams;
1234 }
345674ca 1235
9ecd4386 1236 $strparams->itemname = $element['object']->get_name();
1237
1238 // If element name is categorytotal, get the name of the parent category
1239 if ($strparams->itemname == get_string('categorytotal', 'grades')) {
1240 $parent = $element['object']->get_parent_category();
1241 $strparams->category = $parent->get_name() . ' ';
1242 } else {
1243 $strparams->category = '';
1244 }
1245
1246 $strparams->itemmodule = null;
1247 if (isset($element['object']->itemmodule)) {
1248 $strparams->itemmodule = $element['object']->itemmodule;
345674ca 1249 }
9ecd4386 1250 return $strparams;
1251 }
1252
6cc3e350 1253 /**
1254 * Return edit icon for give element
1255 * @param object $element
1256 * @return string
1257 */
d24832f9 1258 public function get_edit_icon($element, $gpr) {
6cc3e350 1259 global $CFG;
1260
1261 if (!has_capability('moodle/grade:manage', $this->context)) {
1262 if ($element['type'] == 'grade' and has_capability('moodle/grade:edit', $this->context)) {
1263 // oki - let them override grade
1264 } else {
1265 return '';
1266 }
1267 }
1268
05aba805 1269 static $strfeedback = null;
1270 static $streditgrade = null;
1271 if (is_null($streditgrade)) {
1272 $streditgrade = get_string('editgrade', 'grades');
1273 $strfeedback = get_string('feedback');
6cc3e350 1274 }
1275
9ecd4386 1276 $strparams = $this->get_params_for_iconstr($element);
345674ca 1277 if ($element['type'] == 'item' or $element['type'] == 'category') {
9ecd4386 1278 }
345674ca 1279
6cc3e350 1280 $object = $element['object'];
6cc3e350 1281
1282 switch ($element['type']) {
1283 case 'item':
1284 case 'categoryitem':
1285 case 'courseitem':
9ecd4386 1286 $stredit = get_string('editverbose', 'grades', $strparams);
6cc3e350 1287 if (empty($object->outcomeid) || empty($CFG->enableoutcomes)) {
1288 $url = $CFG->wwwroot.'/grade/edit/tree/item.php?courseid='.$this->courseid.'&amp;id='.$object->id;
1289 } else {
1290 $url = $CFG->wwwroot.'/grade/edit/tree/outcomeitem.php?courseid='.$this->courseid.'&amp;id='.$object->id;
1291 }
1292 $url = $gpr->add_url_params($url);
1293 break;
1294
1295 case 'category':
9ecd4386 1296 $stredit = get_string('editverbose', 'grades', $strparams);
6cc3e350 1297 $url = $CFG->wwwroot.'/grade/edit/tree/category.php?courseid='.$this->courseid.'&amp;id='.$object->id;
1298 $url = $gpr->add_url_params($url);
1299 break;
1300
1301 case 'grade':
05aba805 1302 $stredit = $streditgrade;
6cc3e350 1303 if (empty($object->id)) {
1304 $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&amp;itemid='.$object->itemid.'&amp;userid='.$object->userid;
1305 } else {
1306 $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&amp;id='.$object->id;
1307 }
1308 $url = $gpr->add_url_params($url);
1309 if (!empty($object->feedback)) {
1310 $feedback = addslashes_js(trim(format_string($object->feedback, $object->feedbackformat)));
6cc3e350 1311 }
1312 break;
1313
1314 default:
1315 $url = null;
1316 }
1317
1318 if ($url) {
1edd08ab 1319 return '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.s($stredit).'" title="'.s($stredit).'"/></a>';
6cc3e350 1320
1321 } else {
1322 return '';
1323 }
1324 }
1325
1326 /**
1327 * Return hiding icon for give element
1328 * @param object $element
1329 * @return string
1330 */
d24832f9 1331 public function get_hiding_icon($element, $gpr) {
6cc3e350 1332 global $CFG;
1333
1334 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:hide', $this->context)) {
1335 return '';
1336 }
1337
345674ca 1338 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1339 $strshow = get_string('showverbose', 'grades', $strparams);
345674ca 1340 $strhide = get_string('hideverbose', 'grades', $strparams);
6cc3e350 1341
1342 if ($element['object']->is_hidden()) {
1343 $icon = 'show';
1344 $tooltip = $strshow;
1345
1346 if ($element['type'] != 'category' and $element['object']->get_hidden() > 1) { // Change the icon and add a tooltip showing the date
1347 $icon = 'hiddenuntil';
1348 $tooltip = get_string('hiddenuntildate', 'grades', userdate($element['object']->get_hidden()));
1349 }
1350
1351 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&amp;action=show&amp;sesskey='.sesskey()
1352 . '&amp;eid='.$element['eid'];
1353 $url = $gpr->add_url_params($url);
1edd08ab 1354 $action = '<a href="'.$url.'" class="hide"><img alt="'.s($strshow).'" src="'.$CFG->pixpath.'/t/'.$icon.'.gif" class="iconsmall" title="'.s($tooltip).'"/></a>';
6cc3e350 1355
1356 } else {
1357 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&amp;action=hide&amp;sesskey='.sesskey()
1358 . '&amp;eid='.$element['eid'];
1359 $url = $gpr->add_url_params($url);
1edd08ab 1360 $action = '<a href="'.$url.'" class="hide"><img src="'.$CFG->pixpath.'/t/hide.gif" class="iconsmall" alt="'.s($strhide).'" title="'.s($strhide).'"/></a>';
6cc3e350 1361 }
1362 return $action;
1363 }
1364
1365 /**
2673c733 1366 * Return locking icon for given element
6cc3e350 1367 * @param object $element
1368 * @return string
1369 */
d24832f9 1370 public function get_locking_icon($element, $gpr) {
6cc3e350 1371 global $CFG;
1372
345674ca 1373 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1374 $strunlock = get_string('unlockverbose', 'grades', $strparams);
1375 $strlock = get_string('lockverbose', 'grades', $strparams);
d24832f9 1376
2673c733 1377 // Don't allow an unlocking action for a grade whose grade item is locked: just print a state icon
1378 if ($element['type'] == 'grade' && $element['object']->grade_item->is_locked()) {
1379 $strparamobj = new stdClass();
1380 $strparamobj->itemname = $element['object']->grade_item->itemname;
1381 $strnonunlockable = get_string('nonunlockableverbose', 'grades', $strparamobj);
1edd08ab 1382 $action = '<img src="'.$CFG->pixpath.'/t/unlock_gray.gif" alt="'.s($strnonunlockable).'" class="iconsmall" title="'.s($strnonunlockable).'"/>';
2673c733 1383 } elseif ($element['object']->is_locked()) {
6cc3e350 1384 $icon = 'unlock';
1385 $tooltip = $strunlock;
1386
1387 if ($element['type'] != 'category' and $element['object']->get_locktime() > 1) { // Change the icon and add a tooltip showing the date
1388 $icon = 'locktime';
1389 $tooltip = get_string('locktimedate', 'grades', userdate($element['object']->get_locktime()));
1390 }
1391
1392 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:unlock', $this->context)) {
1393 return '';
1394 }
1395 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&amp;action=unlock&amp;sesskey='.sesskey()
1396 . '&amp;eid='.$element['eid'];
1397 $url = $gpr->add_url_params($url);
1edd08ab 1398 $action = '<a href="'.$url.'" class="lock"><img src="'.$CFG->pixpath.'/t/'.$icon.'.gif" alt="'.s($strunlock).'" class="iconsmall" title="'.s($tooltip).'"/></a>';
6cc3e350 1399
1400 } else {
1401 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:lock', $this->context)) {
1402 return '';
1403 }
1404 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&amp;action=lock&amp;sesskey='.sesskey()
1405 . '&amp;eid='.$element['eid'];
1406 $url = $gpr->add_url_params($url);
1edd08ab 1407 $action = '<a href="'.$url.'" class="lock"><img src="'.$CFG->pixpath.'/t/lock.gif" class="iconsmall" alt="'.s($strlock).'" title="'
1408 . s($strlock).'"/></a>';
6cc3e350 1409 }
1410 return $action;
1411 }
1412
1413 /**
1414 * Return calculation icon for given element
1415 * @param object $element
1416 * @return string
1417 */
d24832f9 1418 public function get_calculation_icon($element, $gpr) {
6cc3e350 1419 global $CFG;
1420 if (!has_capability('moodle/grade:manage', $this->context)) {
1421 return '';
1422 }
1423
1424 $calculation_icon = '';
1425
1426 $type = $element['type'];
1427 $object = $element['object'];
1428
9ecd4386 1429
6cc3e350 1430 if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') {
345674ca 1431 $strparams = $this->get_params_for_iconstr($element);
9ecd4386 1432 $streditcalculation = get_string('editcalculationverbose', 'grades', $strparams);
6cc3e350 1433
1434 // show calculation icon only when calculation possible
ff1bc603 1435 if (!$object->is_external_item() and ($object->gradetype == GRADE_TYPE_SCALE or $object->gradetype == GRADE_TYPE_VALUE)) {
6cc3e350 1436 if ($object->is_calculated()) {
1437 $icon = 'calc.gif';
1438 } else {
1439 $icon = 'calc_off.gif';
1440 }
1441 $url = $CFG->wwwroot.'/grade/edit/tree/calculation.php?courseid='.$this->courseid.'&amp;id='.$object->id;
1442 $url = $gpr->add_url_params($url);
b244b9b7 1443 $calculation_icon = '<a href="'. $url.'" class="calculation"><img src="'.$CFG->pixpath.'/t/'.$icon.'" class="iconsmall" alt="'
1edd08ab 1444 . s($streditcalculation).'" title="'.s($streditcalculation).'" /></a>'. "\n";
6cc3e350 1445 }
1446 }
1447
1448 return $calculation_icon;
1449 }
1450}
1451
1452/**
1453 * Flat structure similar to grade tree.
1454 */
1455class grade_seq extends grade_structure {
1456
1457 /**
1458 * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class.
1459 * @var string $commonvars
1460 */
d24832f9 1461 public $commonvars;
6cc3e350 1462
1463 /**
1464 * 1D array of elements
1465 */
d24832f9 1466 public $elements;
e98871a2 1467
1468 /**
1469 * Constructor, retrieves and stores array of all grade_category and grade_item
1470 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed.
1471 * @param int $courseid
1472 * @param boolean $category_grade_last category grade item is the last child
1473 * @param array $collapsed array of collapsed categories
1474 */
d24832f9 1475 public function grade_seq($courseid, $category_grade_last=false, $nooutcomes=false) {
e98871a2 1476 global $USER, $CFG;
1477
1478 $this->courseid = $courseid;
973d2660 1479 $this->commonvars = "&amp;sesskey=".sesskey()."&amp;id=$this->courseid";
e98871a2 1480 $this->context = get_context_instance(CONTEXT_COURSE, $courseid);
1481
1482 // get course grade tree
1483 $top_element = grade_category::fetch_course_tree($courseid, true);
1484
6391ebe7 1485 $this->elements = grade_seq::flatten($top_element, $category_grade_last, $nooutcomes);
1486
1487 foreach ($this->elements as $key=>$unused) {
b89a70ce 1488 $this->items[$this->elements[$key]['object']->id] =& $this->elements[$key]['object'];
6391ebe7 1489 }
e98871a2 1490 }
1491
1492 /**
1493 * Static recursive helper - makes the grade_item for category the last children
1494 * @static
1495 * @param array $element The seed of the recursion
1496 * @return void
1497 */
d24832f9 1498 public function flatten(&$element, $category_grade_last, $nooutcomes) {
e98871a2 1499 if (empty($element['children'])) {
1500 return array();
1501 }
1502 $children = array();
1503
1504 foreach ($element['children'] as $sortorder=>$unused) {
1505 if ($nooutcomes and $element['type'] != 'category' and $element['children'][$sortorder]['object']->is_outcome_item()) {
1506 continue;
1507 }
1508 $children[] = $element['children'][$sortorder];
1509 }
1510 unset($element['children']);
1511
1512 if ($category_grade_last and count($children) > 1) {
1513 $cat_item = array_shift($children);
1514 array_push($children, $cat_item);
1515 }
1516
1517 $result = array();
1518 foreach ($children as $child) {
e0724506 1519 if ($child['type'] == 'category') {
6391ebe7 1520 $result = $result + grade_seq::flatten($child, $category_grade_last, $nooutcomes);
e98871a2 1521 } else {
1522 $child['eid'] = 'i'.$child['object']->id;
6391ebe7 1523 $result[$child['object']->id] = $child;
e98871a2 1524 }
1525 }
1526
1527 return $result;
1528 }
1529
1530 /**
1531 * Parses the array in search of a given eid and returns a element object with
1532 * information about the element it has found.
1533 * @param int $eid
1534 * @return object element
1535 */
d24832f9 1536 public function locate_element($eid) {
e98871a2 1537 // it is a grade - construct a new object
1538 if (strpos($eid, 'n') === 0) {
1539 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) {
1540 return null;
1541 }
1542
1543 $itemid = $matches[1];
1544 $userid = $matches[2];
1545
1546 //extra security check - the grade item must be in this tree
1547 if (!$item_el = $this->locate_element('i'.$itemid)) {
1548 return null;
1549 }
1550
1551 // $gradea->id may be null - means does not exist yet
1552 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid));
1553
1554 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1555 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade');
1556
1557 } else if (strpos($eid, 'g') === 0) {
1558 $id = (int)substr($eid, 1);
1559 if (!$grade = grade_grade::fetch(array('id'=>$id))) {
1560 return null;
1561 }
1562 //extra security check - the grade item must be in this tree
1563 if (!$item_el = $this->locate_element('i'.$grade->itemid)) {
1564 return null;
1565 }
1566 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1567 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade');
1568 }
1569
1570 // it is a category or item
6391ebe7 1571 foreach ($this->elements as $element) {
6cc3e350 1572 if ($element['eid'] == $eid) {
1573 return $element;
1574 }
e98871a2 1575 }
6cc3e350 1576
1577 return null;
e98871a2 1578 }
e98871a2 1579}
1580
7a6b7acf 1581/**
1582 * This class represents a complete tree of categories, grade_items and final grades,
1583 * organises as an array primarily, but which can also be converted to other formats.
1584 * It has simple method calls with complex implementations, allowing for easy insertion,
1585 * deletion and moving of items and categories within the tree.
1586 */
6cc3e350 1587class grade_tree extends grade_structure {
7a6b7acf 1588
1589 /**
1590 * The basic representation of the tree as a hierarchical, 3-tiered array.
1591 * @var object $top_element
1592 */
d24832f9 1593 public $top_element;
7a6b7acf 1594
1595 /**
1596 * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class.
1597 * @var string $commonvars
1598 */
d24832f9 1599 public $commonvars;
7a6b7acf 1600
1601 /**
1602 * 2D array of grade items and categories
1603 */
d24832f9 1604 public $levels;
7a6b7acf 1605
b89a70ce 1606 /**
1607 * Grade items
1608 */
d24832f9 1609 public $items;
b89a70ce 1610
7a6b7acf 1611 /**
1612 * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item
e98871a2 1613 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed.
7a6b7acf 1614 * @param int $courseid
1615 * @param boolean $fillers include fillers and colspans, make the levels var "rectangular"
1616 * @param boolean $category_grade_last category grade item is the last child
4faf5f99 1617 * @param array $collapsed array of collapsed categories
7a6b7acf 1618 */
d24832f9 1619 public function grade_tree($courseid, $fillers=true, $category_grade_last=false, $collapsed=null, $nooutcomes=false) {
7a6b7acf 1620 global $USER, $CFG;
1621
1622 $this->courseid = $courseid;
973d2660 1623 $this->commonvars = "&amp;sesskey=".sesskey()."&amp;id=$this->courseid";
7a6b7acf 1624 $this->levels = array();
2cc773f5 1625 $this->context = get_context_instance(CONTEXT_COURSE, $courseid);
7a6b7acf 1626
1627 // get course grade tree
1628 $this->top_element = grade_category::fetch_course_tree($courseid, true);
1629
4faf5f99 1630 // collapse the categories if requested
1631 if (!empty($collapsed)) {
1632 grade_tree::category_collapse($this->top_element, $collapsed);
1633 }
1634
aea4df41 1635 // no otucomes if requested
1636 if (!empty($nooutcomes)) {
1637 grade_tree::no_outcomes($this->top_element);
1638 }
1639
4faf5f99 1640 // move category item to last position in category
7a6b7acf 1641 if ($category_grade_last) {
1642 grade_tree::category_grade_last($this->top_element);
1643 }
1644
1645 if ($fillers) {
1646 // inject fake categories == fillers
1647 grade_tree::inject_fillers($this->top_element, 0);
1648 // add colspans to categories and fillers
1649 grade_tree::inject_colspans($this->top_element);
1650 }
1651
1652 grade_tree::fill_levels($this->levels, $this->top_element, 0);
d297269d 1653
7a6b7acf 1654 }
1655
4faf5f99 1656 /**
1657 * Static recursive helper - removes items from collapsed categories
1658 * @static
1659 * @param array $element The seed of the recursion
1660 * @param array $collapsed array of collapsed categories
1661 * @return void
1662 */
d24832f9 1663 public function category_collapse(&$element, $collapsed) {
4faf5f99 1664 if ($element['type'] != 'category') {
1665 return;
1666 }
1667 if (empty($element['children']) or count($element['children']) < 2) {
1668 return;
1669 }
1670
384960dd 1671 if (in_array($element['object']->id, $collapsed['aggregatesonly'])) {
4faf5f99 1672 $category_item = reset($element['children']); //keep only category item
1673 $element['children'] = array(key($element['children'])=>$category_item);
1674
1675 } else {
384960dd 1676 if (in_array($element['object']->id, $collapsed['gradesonly'])) { // Remove category item
1677 reset($element['children']);
1678 $first_key = key($element['children']);
1679 unset($element['children'][$first_key]);
1680 }
1681 foreach ($element['children'] as $sortorder=>$child) { // Recurse through the element's children
4faf5f99 1682 grade_tree::category_collapse($element['children'][$sortorder], $collapsed);
1683 }
1684 }
1685 }
7a6b7acf 1686
aea4df41 1687 /**
1688 * Static recursive helper - removes all outcomes
1689 * @static
1690 * @param array $element The seed of the recursion
1691 * @return void
1692 */
d24832f9 1693 public function no_outcomes(&$element) {
aea4df41 1694 if ($element['type'] != 'category') {
1695 return;
1696 }
1697 foreach ($element['children'] as $sortorder=>$child) {
1698 if ($element['children'][$sortorder]['type'] == 'item'
1699 and $element['children'][$sortorder]['object']->is_outcome_item()) {
1700 unset($element['children'][$sortorder]);
1701
d4795a07 1702 } else if ($element['children'][$sortorder]['type'] == 'category') {
aea4df41 1703 grade_tree::no_outcomes($element['children'][$sortorder]);
1704 }
1705 }
1706 }
1707
7a6b7acf 1708 /**
1709 * Static recursive helper - makes the grade_item for category the last children
1710 * @static
1711 * @param array $element The seed of the recursion
1712 * @return void
1713 */
d24832f9 1714 public function category_grade_last(&$element) {
7a6b7acf 1715 if (empty($element['children'])) {
1716 return;
1717 }
1718 if (count($element['children']) < 2) {
1719 return;
1720 }
3e0e2436 1721 $first_item = reset($element['children']);
4a3dfd9a 1722 if ($first_item['type'] == 'categoryitem' or $first_item['type'] == 'courseitem') {
3e0e2436 1723 // the category item might have been already removed
1724 $order = key($element['children']);
1725 unset($element['children'][$order]);
1726 $element['children'][$order] =& $first_item;
1727 }
206f9953 1728 foreach ($element['children'] as $sortorder => $child) {
7a6b7acf 1729 grade_tree::category_grade_last($element['children'][$sortorder]);
1730 }
1731 }
1732
1733 /**
1734 * Static recursive helper - fills the levels array, useful when accessing tree elements of one level
1735 * @static
1736 * @param int $levels
1737 * @param array $element The seed of the recursion
1738 * @param int $depth
1739 * @return void
1740 */
d24832f9 1741 public function fill_levels(&$levels, &$element, $depth) {
7a6b7acf 1742 if (!array_key_exists($depth, $levels)) {
1743 $levels[$depth] = array();
1744 }
1745
1746 // prepare unique identifier
1747 if ($element['type'] == 'category') {
1748 $element['eid'] = 'c'.$element['object']->id;
1749 } else if (in_array($element['type'], array('item', 'courseitem', 'categoryitem'))) {
1750 $element['eid'] = 'i'.$element['object']->id;
b89a70ce 1751 $this->items[$element['object']->id] =& $element['object'];
7a6b7acf 1752 }
1753
1754 $levels[$depth][] =& $element;
1755 $depth++;
1756 if (empty($element['children'])) {
1757 return;
1758 }
1759 $prev = 0;
1760 foreach ($element['children'] as $sortorder=>$child) {
1761 grade_tree::fill_levels($levels, $element['children'][$sortorder], $depth);
1762 $element['children'][$sortorder]['prev'] = $prev;
1763 $element['children'][$sortorder]['next'] = 0;
1764 if ($prev) {
1765 $element['children'][$prev]['next'] = $sortorder;
1766 }
1767 $prev = $sortorder;
1768 }
1769 }
1770
1771 /**
1772 * Static recursive helper - makes full tree (all leafes are at the same level)
1773 */
d24832f9 1774 public function inject_fillers(&$element, $depth) {
7a6b7acf 1775 $depth++;
1776
1777 if (empty($element['children'])) {
1778 return $depth;
1779 }
1780 $chdepths = array();
1781 $chids = array_keys($element['children']);
1782 $last_child = end($chids);
1783 $first_child = reset($chids);
1784
1785 foreach ($chids as $chid) {
1786 $chdepths[$chid] = grade_tree::inject_fillers($element['children'][$chid], $depth);
1787 }
1788 arsort($chdepths);
1789
1790 $maxdepth = reset($chdepths);
1791 foreach ($chdepths as $chid=>$chd) {
1792 if ($chd == $maxdepth) {
1793 continue;
1794 }
1795 for ($i=0; $i < $maxdepth-$chd; $i++) {
1796 if ($chid == $first_child) {
1797 $type = 'fillerfirst';
1798 } else if ($chid == $last_child) {
1799 $type = 'fillerlast';
1800 } else {
1801 $type = 'filler';
1802 }
1803 $oldchild =& $element['children'][$chid];
1804 $element['children'][$chid] = array('object'=>'filler', 'type'=>$type, 'eid'=>'', 'depth'=>$element['object']->depth,'children'=>array($oldchild));
1805 }
1806 }
1807
1808 return $maxdepth;
1809 }
1810
1811 /**
1812 * Static recursive helper - add colspan information into categories
1813 */
d24832f9 1814 public function inject_colspans(&$element) {
7a6b7acf 1815 if (empty($element['children'])) {
1816 return 1;
1817 }
1818 $count = 0;
1819 foreach ($element['children'] as $key=>$child) {
1820 $count += grade_tree::inject_colspans($element['children'][$key]);
1821 }
1822 $element['colspan'] = $count;
1823 return $count;
1824 }
1825
1826 /**
1827 * Parses the array in search of a given eid and returns a element object with
1828 * information about the element it has found.
1829 * @param int $eid
1830 * @return object element
1831 */
d24832f9 1832 public function locate_element($eid) {
d3c3da1b 1833 // it is a grade - construct a new object
1834 if (strpos($eid, 'n') === 0) {
1835 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) {
1836 return null;
1837 }
1838
1839 $itemid = $matches[1];
1840 $userid = $matches[2];
1841
1842 //extra security check - the grade item must be in this tree
1843 if (!$item_el = $this->locate_element('i'.$itemid)) {
1844 return null;
1845 }
1846
1847 // $gradea->id may be null - means does not exist yet
1848 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid));
1849
1850 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1851 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade');
1852
1853 } else if (strpos($eid, 'g') === 0) {
7a6b7acf 1854 $id = (int)substr($eid, 1);
1855 if (!$grade = grade_grade::fetch(array('id'=>$id))) {
1856 return null;
1857 }
1858 //extra security check - the grade item must be in this tree
1859 if (!$item_el = $this->locate_element('i'.$grade->itemid)) {
1860 return null;
1861 }
1862 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods!
1863 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade');
1864 }
1865
1866 // it is a category or item
1867 foreach ($this->levels as $row) {
1868 foreach ($row as $element) {
1869 if ($element['type'] == 'filler') {
1870 continue;
1871 }
1872 if ($element['eid'] == $eid) {
1873 return $element;
1874 }
1875 }
1876 }
1877
1878 return null;
1879 }
d24832f9 1880
b244b9b7 1881 /**
1882 * Returns a well-formed XML representation of the grade-tree using recursion.
1883 * @param array $root The current element in the recursion. If null, starts at the top of the tree.
1884 * @return string $xml
1885 */
d24832f9 1886 public function exportToXML($root=null, $tabs="\t") {
b244b9b7 1887 $xml = null;
1888 $first = false;
1889 if (is_null($root)) {
1890 $root = $this->top_element;
1891 $xml = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n";
1892 $xml .= "<gradetree>\n";
1893 $first = true;
1894 }
d24832f9 1895
b244b9b7 1896 $type = 'undefined';
1897 if (strpos($root['object']->table, 'grade_categories') !== false) {
1898 $type = 'category';
1899 } elseif (strpos($root['object']->table, 'grade_items') !== false) {
1900 $type = 'item';
1901 } elseif (strpos($root['object']->table, 'grade_outcomes') !== false) {
1902 $type = 'outcome';
1903 }
d24832f9 1904
b244b9b7 1905 $xml .= "$tabs<element type=\"$type\">\n";
1906 foreach ($root['object'] as $var => $value) {
1907 if (!is_object($value) && !is_array($value) && !empty($value)) {
1908 $xml .= "$tabs\t<$var>$value</$var>\n";
1909 }
1910 }
1911
1912 if (!empty($root['children'])) {
1913 $xml .= "$tabs\t<children>\n";
1914 foreach ($root['children'] as $sortorder => $child) {
1915 $xml .= $this->exportToXML($child, $tabs."\t\t");
1916 }
1917 $xml .= "$tabs\t</children>\n";
1918 }
d24832f9 1919
b244b9b7 1920 $xml .= "$tabs</element>\n";
1921
1922 if ($first) {
1923 $xml .= "</gradetree>";
1924 }
d24832f9 1925
b244b9b7 1926 return $xml;
1927 }
d24832f9 1928
b244b9b7 1929 /**
1930 * Returns a JSON representation of the grade-tree using recursion.
1931 * @param array $root The current element in the recursion. If null, starts at the top of the tree.
1932 * @param string $tabs Tab characters used to indent the string nicely for humans to enjoy
1933 * @param int $switch The position (first or last) of the aggregations
1934 * @return string $xml
1935 */
d24832f9 1936 public function exportToJSON($root=null, $tabs="\t") {
b244b9b7 1937 $json = null;
1938 $first = false;
1939 if (is_null($root)) {
1940 $root = $this->top_element;
1941 $first = true;
1942 }
d24832f9 1943
b244b9b7 1944 $name = '';
1945
1946
1947 if (strpos($root['object']->table, 'grade_categories') !== false) {
1948 $name = $root['object']->fullname;
1949 if ($name == '?') {
d24832f9 1950 $name = $root['object']->get_name();
b244b9b7 1951 }
1952 } elseif (strpos($root['object']->table, 'grade_items') !== false) {
1953 $name = $root['object']->itemname;
1954 } elseif (strpos($root['object']->table, 'grade_outcomes') !== false) {
1955 $name = $root['object']->itemname;
1956 }
d24832f9 1957
b244b9b7 1958 $json .= "$tabs {\n";
1959 $json .= "$tabs\t \"type\": \"{$root['type']}\",\n";
1960 $json .= "$tabs\t \"name\": \"$name\",\n";
1961
1962 foreach ($root['object'] as $var => $value) {
1963 if (!is_object($value) && !is_array($value) && !empty($value)) {
1964 $json .= "$tabs\t \"$var\": \"$value\",\n";
1965 }
1966 }
d24832f9 1967
b244b9b7 1968 $json = substr($json, 0, strrpos($json, ','));
d24832f9 1969
b244b9b7 1970 if (!empty($root['children'])) {
1971 $json .= ",\n$tabs\t\"children\": [\n";
1972 foreach ($root['children'] as $sortorder => $child) {
1973 $json .= $this->exportToJSON($child, $tabs."\t\t");
1974 }
1975 $json = substr($json, 0, strrpos($json, ','));
1976 $json .= "\n$tabs\t]\n";
d24832f9 1977 }
b244b9b7 1978
1979 if ($first) {
1980 $json .= "\n}";
1981 } else {
1982 $json .= "\n$tabs},\n";
1983 }
d24832f9 1984
b244b9b7 1985 return $json;
1986 }
d24832f9 1987
1988 public function get_levels() {
1989 return $this->levels;
1990 }
1991
1992 public function get_items() {
1993 return $this->items;
1994 }
1995
1996 public function get_item($itemid) {
1997 if (array_key_exists($itemid, $this->items)) {
1998 return $this->items[$itemid];
1999 } else {
2000 return false;
2001 }
2002 }
7a6b7acf 2003}
2004
e2008be2 2005?>