| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php //$Id: lib.php,v 1.120.2.21 2008/03/11 23:58:51 skodak Exp $ 2 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 26 require_once $CFG->libdir.'/gradelib.php'; 27 28 /** 29 * This class iterates over all users that are graded in a course. 30 * Returns detailed info about users and their grades. 31 */ 32 class graded_users_iterator { 33 var $course; 34 var $grade_items; 35 var $groupid; 36 var $users_rs; 37 var $grades_rs; 38 var $gradestack; 39 var $sortfield1; 40 var $sortorder1; 41 var $sortfield2; 42 var $sortorder2; 43 44 /** 45 * Constructor 46 * @param $course object 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 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) 53 */ 54 function graded_users_iterator($course, $grade_items=null, $groupid=0, $sortfield1='lastname', $sortorder1='ASC', $sortfield2='firstname', $sortorder2='ASC') { 55 $this->course = $course; 56 $this->grade_items = $grade_items; 57 $this->groupid = $groupid; 58 $this->sortfield1 = $sortfield1; 59 $this->sortorder1 = $sortorder1; 60 $this->sortfield2 = $sortfield2; 61 $this->sortorder2 = $sortorder2; 62 63 $this->gradestack = array(); 64 } 65 66 /** 67 * Initialise the iterator 68 * @return boolean success 69 */ 70 function init() { 71 global $CFG; 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 82 if (strpos($CFG->gradebookroles, ',') === false) { 83 $gradebookroles = " = {$CFG->gradebookroles}"; 84 } else { 85 $gradebookroles = " IN ({$CFG->gradebookroles})"; 86 } 87 88 $relatedcontexts = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $this->course->id)); 89 90 if ($this->groupid) { 91 $groupsql = "INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id"; 92 $groupwheresql = "AND gm.groupid = {$this->groupid}"; 93 } else { 94 $groupsql = ""; 95 $groupwheresql = ""; 96 } 97 98 if (empty($this->sortfield1)) { 99 // we must do some sorting even if not specified 100 $ofields = ", u.id AS usrt"; 101 $order = "usrt ASC"; 102 103 } else { 104 $ofields = ", u.$this->sortfield1 AS usrt1"; 105 $order = "usrt1 $this->sortorder1"; 106 if (!empty($this->sortfield2)) { 107 $ofields .= ", u.$this->sortfield1 AS usrt2"; 108 $order .= ", usrt2 $this->sortorder2"; 109 } 110 if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { 111 // user order MUST be the same in both queries, must include the only unique user->id if not already present 112 $ofields .= ", u.id AS usrt"; 113 $order .= ", usrt ASC"; 114 } 115 } 116 117 $users_sql = "SELECT u.* $ofields 118 FROM {$CFG->prefix}user u 119 INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid 120 $groupsql 121 WHERE ra.roleid $gradebookroles 122 AND ra.contextid $relatedcontexts 123 $groupwheresql 124 ORDER BY $order"; 125 126 $this->users_rs = get_recordset_sql($users_sql); 127 128 if (!empty($this->grade_items)) { 129 $itemids = array_keys($this->grade_items); 130 $itemids = implode(',', $itemids); 131 132 $grades_sql = "SELECT g.* $ofields 133 FROM {$CFG->prefix}grade_grades g 134 INNER JOIN {$CFG->prefix}user u ON g.userid = u.id 135 INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid 136 $groupsql 137 WHERE ra.roleid $gradebookroles 138 AND ra.contextid $relatedcontexts 139 AND g.itemid IN ($itemids) 140 $groupwheresql 141 ORDER BY $order, g.itemid ASC"; 142 $this->grades_rs = get_recordset_sql($grades_sql); 143 } else { 144 $this->grades_rs = false; 145 } 146 147 return true; 148 } 149 150 /** 151 * Returns information about the next user 152 * @return mixed array of user info, all grades and feedback or null when no more users found 153 */ 154 function next_user() { 155 if (!$this->users_rs) { 156 return false; // no users present 157 } 158 159 if (!$user = rs_fetch_next_record($this->users_rs)) { 160 if ($current = $this->_pop()) { 161 // this is not good - user or grades updated between the two reads above :-( 162 } 163 164 return false; // no more users 165 } 166 167 // find grades of this user 168 $grade_records = array(); 169 while (true) { 170 if (!$current = $this->_pop()) { 171 break; // no more grades 172 } 173 174 if ($current->userid != $user->id) { 175 // grade of the next user, we have all for this user 176 $this->_push($current); 177 break; 178 } 179 180 $grade_records[$current->itemid] = $current; 181 } 182 183 $grades = array(); 184 $feedbacks = array(); 185 186 if (!empty($this->grade_items)) { 187 foreach ($this->grade_items as $grade_item) { 188 if (array_key_exists($grade_item->id, $grade_records)) { 189 $feedbacks[$grade_item->id]->feedback = $grade_records[$grade_item->id]->feedback; 190 $feedbacks[$grade_item->id]->feedbackformat = $grade_records[$grade_item->id]->feedbackformat; 191 unset($grade_records[$grade_item->id]->feedback); 192 unset($grade_records[$grade_item->id]->feedbackformat); 193 $grades[$grade_item->id] = new grade_grade($grade_records[$grade_item->id], false); 194 } else { 195 $feedbacks[$grade_item->id]->feedback = ''; 196 $feedbacks[$grade_item->id]->feedbackformat = FORMAT_MOODLE; 197 $grades[$grade_item->id] = new grade_grade(array('userid'=>$user->id, 'itemid'=>$grade_item->id), false); 198 } 199 } 200 } 201 202 $result = new object(); 203 $result->user = $user; 204 $result->grades = $grades; 205 $result->feedbacks = $feedbacks; 206 207 return $result; 208 } 209 210 /** 211 * Close the iterator, do not forget to call this function. 212 * @return void 213 */ 214 function close() { 215 if ($this->users_rs) { 216 rs_close($this->users_rs); 217 $this->users_rs = null; 218 } 219 if ($this->grades_rs) { 220 rs_close($this->grades_rs); 221 $this->grades_rs = null; 222 } 223 $this->gradestack = array(); 224 } 225 226 /** 227 * Internal function 228 */ 229 function _push($grade) { 230 array_push($this->gradestack, $grade); 231 } 232 233 /** 234 * Internal function 235 */ 236 function _pop() { 237 if (empty($this->gradestack)) { 238 if (!$this->grades_rs) { 239 return NULL; // no grades present 240 } 241 242 if (!$grade = rs_fetch_next_record($this->grades_rs)) { 243 return NULL; // no more grades 244 } 245 246 return $grade; 247 } else { 248 return array_pop($this->gradestack); 249 } 250 } 251 } 252 253 /** 254 * Print a selection popup form of the graded users in a course. 255 * 256 * @param int $courseid id of the course 257 * @param string $actionpage The page receiving the data from the popoup form 258 * @param int $userid id of the currently selected user (or 'all' if they are all selected) 259 * @param bool $return If true, will return the HTML, otherwise, will print directly 260 * @return null 261 */ 262 function print_graded_users_selector($course, $actionpage, $userid=null, $return=false) { 263 global $CFG, $USER; 264 265 if (is_null($userid)) { 266 $userid = $USER->id; 267 } 268 269 $context = get_context_instance(CONTEXT_COURSE, $course->id); 270 271 $menu = array(); // Will be a list of userid => user name 272 273 $gui = new graded_users_iterator($course); 274 $gui->init(); 275 276 if ($userid !== 0) { 277 $menu[0] = get_string('allusers', 'grades'); 278 } 279 280 while ($userdata = $gui->next_user()) { 281 $user = $userdata->user; 282 $menu[$user->id] = fullname($user); 283 } 284 285 $gui->close(); 286 287 if ($userid !== 0) { 288 $menu[0] .= " (" . (count($menu) - 1) . ")"; 289 } 290 291 return popup_form($CFG->wwwroot.'/grade/' . $actionpage . '&userid=', $menu, 'choosegradeduser', $userid, 'choose', '', '', 292 $return, 'self', get_string('selectalloroneuser', 'grades')); 293 } 294 295 /** 296 * Print grading plugin selection popup form. 297 * 298 * @param int $courseid id of course 299 * @param string $active_type type of plugin on current page - import, export, report or edit 300 * @param string $active_plugin active plugin type - grader, user, cvs, ... 301 * @param boolean $return return as string 302 * @return nothing or string if $return true 303 */ 304 function print_grade_plugin_selector($courseid, $active_type, $active_plugin, $return=false) { 305 global $CFG; 306 307 $context = get_context_instance(CONTEXT_COURSE, $courseid); 308 309 $menu = array(); 310 $count = 0; 311 $active = ''; 312 313 /// report plugins with its special structure 314 if ($reports = get_list_of_plugins('grade/report', 'CVS')) { // Get all installed reports 315 foreach ($reports as $key => $plugin) { // Remove ones we can't see 316 if (!has_capability('gradereport/'.$plugin.':view', $context)) { 317 unset($reports[$key]); 318 } 319 } 320 } 321 $reportnames = array(); 322 if (!empty($reports)) { 323 foreach ($reports as $plugin) { 324 $url = 'report/'.$plugin.'/index.php?id='.$courseid; 325 if ($active_type == 'report' and $active_plugin == $plugin ) { 326 $active = $url; 327 } 328 $reportnames[$url] = get_string('modulename', 'gradereport_'.$plugin); 329 $count++; 330 } 331 asort($reportnames); 332 } 333 if (!empty($reportnames)) { 334 $menu['reportgroup']='--'.get_string('view'); 335 $menu = $menu+$reportnames; 336 } 337 338 /// standard import plugins 339 if ($imports = get_list_of_plugins('grade/import', 'CVS')) { // Get all installed import plugins 340 foreach ($imports as $key => $plugin) { // Remove ones we can't see 341 if (!has_capability('gradeimport/'.$plugin.':view', $context)) { 342 unset($imports[$key]); 343 } 344 } 345 } 346 $importnames = array(); 347 if (!empty($imports)) { 348 foreach ($imports as $plugin) { 349 $url = 'import/'.$plugin.'/index.php?id='.$courseid; 350 if ($active_type == 'import' and $active_plugin == $plugin ) { 351 $active = $url; 352 } 353 $importnames[$url] = get_string('modulename', 'gradeimport_'.$plugin); 354 $count++; 355 } 356 asort($importnames); 357 } 358 if (!empty($importnames)) { 359 $menu['importgroup']='--'.get_string('importfrom', 'grades'); 360 $menu = $menu+$importnames; 361 } 362 363 /// standard export plugins 364 if ($exports = get_list_of_plugins('grade/export', 'CVS')) { // Get all installed export plugins 365 foreach ($exports as $key => $plugin) { // Remove ones we can't see 366 if (!has_capability('gradeexport/'.$plugin.':view', $context)) { 367 unset($exports[$key]); 368 } 369 } 370 } 371 $exportnames = array(); 372 if (!empty($exports)) { 373 foreach ($exports as $plugin) { 374 $url = 'export/'.$plugin.'/index.php?id='.$courseid; 375 if ($active_type == 'export' and $active_plugin == $plugin ) { 376 $active = $url; 377 } 378 $exportnames[$url] = get_string('modulename', 'gradeexport_'.$plugin); 379 $count++; 380 } 381 asort($exportnames); 382 } 383 if (!empty($exportnames)) { 384 $menu['exportgroup']='--'.get_string('exportto', 'grades'); 385 $menu = $menu+$exportnames; 386 } 387 388 /// editing scripts - not real plugins 389 if (has_capability('moodle/grade:manage', $context) 390 or has_capability('moodle/grade:manageletters', $context) 391 or has_capability('moodle/course:managescales', $context) 392 or has_capability('moodle/course:update', $context)) { 393 $menu['edit']='--'.get_string('edit'); 394 395 if (has_capability('moodle/grade:manage', $context)) { 396 $url = 'edit/tree/index.php?id='.$courseid; 397 if ($active_type == 'edit' and $active_plugin == 'tree' ) { 398 $active = $url; 399 } 400 $menu[$url] = get_string('edittree', 'grades'); 401 $count++; 402 } 403 404 if (has_capability('moodle/course:managescales', $context)) { 405 $url = 'edit/scale/index.php?id='.$courseid; 406 if ($active_type == 'edit' and $active_plugin == 'scale' ) { 407 $active = $url; 408 } 409 $menu[$url] = get_string('scales'); 410 $count++; 411 } 412 413 if (!empty($CFG->enableoutcomes) && (has_capability('moodle/grade:manage', $context) or 414 has_capability('moodle/course:update', $context))) { 415 if (has_capability('moodle/course:update', $context)) { // Default to course assignment 416 $url = 'edit/outcome/course.php?id='.$courseid; 417 } else { 418 $url = 'edit/outcome/index.php?id='.$courseid; 419 } 420 if ($active_type == 'edit' and $active_plugin == 'outcome' ) { 421 $active = $url; 422 } 423 $menu[$url] = get_string('outcomes', 'grades'); 424 $count++; 425 } 426 427 if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:manageletters', $context)) { 428 $url = 'edit/letter/index.php?id='.$courseid; 429 if ($active_type == 'edit' and $active_plugin == 'letter' ) { 430 $active = $url; 431 } 432 $menu[$url] = get_string('letters', 'grades'); 433 $count++; 434 } 435 436 if (has_capability('moodle/grade:manage', $context)) { 437 $url = 'edit/settings/index.php?id='.$courseid; 438 if ($active_type == 'edit' and $active_plugin == 'settings' ) { 439 $active = $url; 440 } 441 $menu[$url] = get_string('coursesettings', 'grades'); 442 $count++; 443 } 444 445 } 446 447 /// finally print/return the popup form 448 if ($count > 1) { 449 return popup_form($CFG->wwwroot.'/grade/', $menu, 'choosepluginreport', '', 450 get_string('chooseaction', 'grades'), '', '', $return, 'self'); 451 } else { 452 // only one option - no plugin selector needed 453 return ''; 454 } 455 } 456 457 /** 458 * Utility class used for return tracking when using edit and other forms in grade plugins 459 */ 460 class grade_plugin_return { 461 var $type; 462 var $plugin; 463 var $courseid; 464 var $userid; 465 var $page; 466 467 /** 468 * Constructor 469 * @param array $params - associative array with return parameters, if null parameter are taken from _GET or _POST 470 */ 471 function grade_plugin_return ($params=null) { 472 if (empty($params)) { 473 $this->type = optional_param('gpr_type', null, PARAM_SAFEDIR); 474 $this->plugin = optional_param('gpr_plugin', null, PARAM_SAFEDIR); 475 $this->courseid = optional_param('gpr_courseid', null, PARAM_INT); 476 $this->userid = optional_param('gpr_userid', null, PARAM_INT); 477 $this->page = optional_param('gpr_page', null, PARAM_INT); 478 479 } else { 480 foreach ($params as $key=>$value) { 481 if (array_key_exists($key, $this)) { 482 $this->$key = $value; 483 } 484 } 485 } 486 } 487 488 /** 489 * Returns return parameters as options array suitable for buttons. 490 * @return array options 491 */ 492 function get_options() { 493 if (empty($this->type)) { 494 return array(); 495 } 496 497 $params = array(); 498 499 if (!empty($this->plugin)) { 500 $params['plugin'] = $this->plugin; 501 } 502 503 if (!empty($this->courseid)) { 504 $params['id'] = $this->courseid; 505 } 506 507 if (!empty($this->userid)) { 508 $params['userid'] = $this->userid; 509 } 510 511 if (!empty($this->page)) { 512 $params['page'] = $this->page; 513 } 514 515 return $params; 516 } 517 518 /** 519 * Returns return url 520 * @param string $default default url when params not set 521 * @return string url 522 */ 523 function get_return_url($default, $extras=null) { 524 global $CFG; 525 526 if (empty($this->type) or empty($this->plugin)) { 527 return $default; 528 } 529 530 $url = $CFG->wwwroot.'/grade/'.$this->type.'/'.$this->plugin.'/index.php'; 531 $glue = '?'; 532 533 if (!empty($this->courseid)) { 534 $url .= $glue.'id='.$this->courseid; 535 $glue = '&'; 536 } 537 538 if (!empty($this->userid)) { 539 $url .= $glue.'userid='.$this->userid; 540 $glue = '&'; 541 } 542 543 if (!empty($this->page)) { 544 $url .= $glue.'page='.$this->page; 545 $glue = '&'; 546 } 547 548 if (!empty($extras)) { 549 foreach($extras as $key=>$value) { 550 $url .= $glue.$key.'='.$value; 551 $glue = '&'; 552 } 553 } 554 555 return $url; 556 } 557 558 /** 559 * Returns string with hidden return tracking form elements. 560 * @return string 561 */ 562 function get_form_fields() { 563 if (empty($this->type)) { 564 return ''; 565 } 566 567 $result = '<input type="hidden" name="gpr_type" value="'.$this->type.'" />'; 568 569 if (!empty($this->plugin)) { 570 $result .= '<input type="hidden" name="gpr_plugin" value="'.$this->plugin.'" />'; 571 } 572 573 if (!empty($this->courseid)) { 574 $result .= '<input type="hidden" name="gpr_courseid" value="'.$this->courseid.'" />'; 575 } 576 577 if (!empty($this->userid)) { 578 $result .= '<input type="hidden" name="gpr_userid" value="'.$this->userid.'" />'; 579 } 580 581 if (!empty($this->page)) { 582 $result .= '<input type="hidden" name="gpr_page" value="'.$this->page.'" />'; 583 } 584 } 585 586 /** 587 * Add hidden elements into mform 588 * @param object $mform moodle form object 589 * @return void 590 */ 591 function add_mform_elements(&$mform) { 592 if (empty($this->type)) { 593 return; 594 } 595 596 $mform->addElement('hidden', 'gpr_type', $this->type); 597 $mform->setType('gpr_type', PARAM_SAFEDIR); 598 599 if (!empty($this->plugin)) { 600 $mform->addElement('hidden', 'gpr_plugin', $this->plugin); 601 $mform->setType('gpr_plugin', PARAM_SAFEDIR); 602 } 603 604 if (!empty($this->courseid)) { 605 $mform->addElement('hidden', 'gpr_courseid', $this->courseid); 606 $mform->setType('gpr_courseid', PARAM_INT); 607 } 608 609 if (!empty($this->userid)) { 610 $mform->addElement('hidden', 'gpr_userid', $this->userid); 611 $mform->setType('gpr_userid', PARAM_INT); 612 } 613 614 if (!empty($this->page)) { 615 $mform->addElement('hidden', 'gpr_page', $this->page); 616 $mform->setType('gpr_page', PARAM_INT); 617 } 618 } 619 620 /** 621 * Add return tracking params into url 622 * @param string $url 623 * @return string $url with erturn tracking params 624 */ 625 function add_url_params($url) { 626 if (empty($this->type)) { 627 return $url; 628 } 629 630 if (strpos($url, '?') === false) { 631 $url .= '?gpr_type='.$this->type; 632 } else { 633 $url .= '&gpr_type='.$this->type; 634 } 635 636 if (!empty($this->plugin)) { 637 $url .= '&gpr_plugin='.$this->plugin; 638 } 639 640 if (!empty($this->courseid)) { 641 $url .= '&gpr_courseid='.$this->courseid; 642 } 643 644 if (!empty($this->userid)) { 645 $url .= '&gpr_userid='.$this->userid; 646 } 647 648 if (!empty($this->page)) { 649 $url .= '&gpr_page='.$this->page; 650 } 651 652 return $url; 653 } 654 } 655 656 /** 657 * Function central to gradebook for building and printing the navigation (breadcrumb trail). 658 * @param string $path The path of the calling script (using __FILE__?) 659 * @param string $pagename The language string to use as the last part of the navigation (non-link) 660 * @param mixed $id Either a plain integer (assuming the key is 'id') or an array of keys and values (e.g courseid => $courseid, itemid...) 661 * @return string 662 */ 663 function grade_build_nav($path, $pagename=null, $id=null) { 664 global $CFG, $COURSE; 665 666 $strgrades = get_string('grades', 'grades'); 667 668 // Parse the path and build navlinks from its elements 669 $dirroot_length = strlen($CFG->dirroot) + 1; // Add 1 for the first slash 670 $path = substr($path, $dirroot_length); 671 $path = str_replace('\\', '/', $path); 672 673 $path_elements = explode('/', $path); 674 675 $path_elements_count = count($path_elements); 676 677 // First link is always 'grade' 678 $navlinks = array(); 679 $navlinks[] = array('name' => $strgrades, 680 'link' => $CFG->wwwroot.'/grade/index.php?id='.$COURSE->id, 681 'type' => 'misc'); 682 683 $link = ''; 684 $numberofelements = 3; 685 686 // Prepare URL params string 687 $id_string = '?'; 688 if (!is_null($id)) { 689 if (is_array($id)) { 690 foreach ($id as $idkey => $idvalue) { 691 $id_string .= "$idkey=$idvalue&"; 692 } 693 } else { 694 $id_string .= "id=$id"; 695 } 696 } 697 698 $navlink4 = null; 699 700 // Remove file extensions from filenames 701 foreach ($path_elements as $key => $filename) { 702 $path_elements[$key] = str_replace('.php', '', $filename); 703 } 704 705 // Second level links 706 switch ($path_elements[1]) { 707 case 'edit': // No link 708 if ($path_elements[3] != 'index.php') { 709 $numberofelements = 4; 710 } 711 break; 712 case 'import': // No link 713 break; 714 case 'export': // No link 715 break; 716 case 'report': 717 // $id is required for this link. Do not print it if $id isn't given 718 if (!is_null($id)) { 719 $link = $CFG->wwwroot . '/grade/report/index.php' . $id_string; 720 } 721 722 if ($path_elements[2] == 'grader') { 723 $numberofelements = 4; 724 } 725 break; 726 727 default: 728 // If this element isn't among the ones already listed above, it isn't supported, throw an error. 729 debugging("grade_build_nav() doesn't support ". $path_elements[1] . " as the second path element after 'grade'."); 730 return false; 731 } 732 733 $navlinks[] = array('name' => get_string($path_elements[1], 'grades'), 'link' => $link, 'type' => 'misc'); 734 735 // Third level links 736 if (empty($pagename)) { 737 $pagename = get_string($path_elements[2], 'grades'); 738 } 739 740 switch ($numberofelements) { 741 case 3: 742 $navlinks[] = array('name' => $pagename, 'link' => $link, 'type' => 'misc'); 743 break; 744 case 4: 745 746 if ($path_elements[2] == 'grader' AND $path_elements[3] != 'index.php') { 747 $navlinks[] = array('name' => get_string('modulename', 'gradereport_grader'), 748 'link' => "$CFG->wwwroot/grade/report/grader/index.php$id_string", 749 'type' => 'misc'); 750 } 751 $navlinks[] = array('name' => $pagename, 'link' => '', 'type' => 'misc'); 752 break; 753 } 754 $navigation = build_navigation($navlinks); 755 756 return $navigation; 757 } 758 759 /** 760 * General structure representing grade items in course 761 */ 762 class grade_structure { 763 var $context; 764 765 var $courseid; 766 767 /** 768 * 1D array of grade items only 769 */ 770 var $items; 771 772 /** 773 * Returns icon of element 774 * @param object $element 775 * @param bool $spacerifnone return spacer if no icon found 776 * @return string icon or spacer 777 */ 778 function get_element_icon(&$element, $spacerifnone=false) { 779 global $CFG; 780 781 switch ($element['type']) { 782 case 'item': 783 case 'courseitem': 784 case 'categoryitem': 785 if ($element['object']->is_calculated()) { 786 return '<img src="'.$CFG->pixpath.'/i/calc.gif" class="icon itemicon" alt="'.get_string('calculation', 'grades').'"/>'; 787 788 } else if (($element['object']->is_course_item() or $element['object']->is_category_item()) 789 and ($element['object']->gradetype == GRADE_TYPE_SCALE or $element['object']->gradetype == GRADE_TYPE_VALUE)) { 790 if ($category = $element['object']->get_item_category()) { 791 switch ($category->aggregation) { 792 case GRADE_AGGREGATE_MEAN: 793 case GRADE_AGGREGATE_MEDIAN: 794 case GRADE_AGGREGATE_WEIGHTED_MEAN: 795 case GRADE_AGGREGATE_WEIGHTED_MEAN2: 796 case GRADE_AGGREGATE_EXTRACREDIT_MEAN: 797 return '<img src="'.$CFG->pixpath.'/i/agg_mean.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>'; 798 case GRADE_AGGREGATE_SUM: 799 return '<img src="'.$CFG->pixpath.'/i/agg_sum.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>'; 800 } 801 } 802 803 } else if ($element['object']->itemtype == 'mod') { 804 return '<img src="'.$CFG->modpixpath.'/'.$element['object']->itemmodule.'/icon.gif" class="icon itemicon" alt="' 805 .get_string('modulename', $element['object']->itemmodule).'"/>'; 806 807 } else if ($element['object']->itemtype == 'manual') { 808 if ($element['object']->is_outcome_item()) { 809 return '<img src="'.$CFG->pixpath.'/i/outcomes.gif" class="icon itemicon" alt="'.get_string('outcome', 'grades').'"/>'; 810 } else { 811 return '<img src="'.$CFG->pixpath.'/t/manual_item.gif" class="icon itemicon" alt="'.get_string('manualitem', 'grades').'"/>'; 812 } 813 } 814 break; 815 816 case 'category': 817 return '<img src="'.$CFG->pixpath.'/f/folder.gif" class="icon itemicon" alt="'.get_string('category', 'grades').'"/>'; 818 } 819 820 if ($spacerifnone) { 821 return '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="icon itemicon" alt=""/>'; 822 } else { 823 return ''; 824 } 825 } 826 827 /** 828 * Returns name of element optionally with icon and link 829 * @param object $element 830 * @param bool $withlinks 831 * @param bool $icons 832 * @param bool $spacerifnone return spacer if no icon found 833 * @return header string 834 */ 835 function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false) { 836 global $CFG; 837 838 $header = ''; 839 840 if ($icon) { 841 $header .= $this->get_element_icon($element, $spacerifnone); 842 } 843 844 $header .= $element['object']->get_name(); 845 846 if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and $element['type'] != 'courseitem') { 847 return $header; 848 } 849 850 $itemtype = $element['object']->itemtype; 851 $itemmodule = $element['object']->itemmodule; 852 $iteminstance = $element['object']->iteminstance; 853 854 if ($withlink and $itemtype=='mod' and $iteminstance and $itemmodule) { 855 if ($cm = get_coursemodule_from_instance($itemmodule, $iteminstance, $this->courseid)) { 856 857 $dir = $CFG->dirroot.'/mod/'.$itemmodule; 858 859 if (file_exists($dir.'/grade.php')) { 860 $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/grade.php?id='.$cm->id; 861 } else { 862 $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/view.php?id='.$cm->id; 863 } 864 865 $header = '<a href="'.$url.'">'.$header.'</a>'; 866 } 867 } 868 869 return $header; 870 } 871 872 /** 873 * Returns the grade eid - the grade may not exist yet. 874 * @param $grade_grade object 875 * @return string eid 876 */ 877 function get_grade_eid($grade_grade) { 878 if (empty($grade_grade->id)) { 879 return 'n'.$grade_grade->itemid.'u'.$grade_grade->userid; 880 } else { 881 return 'g'.$grade_grade->id; 882 } 883 } 884 885 /** 886 * Returns the grade_item eid 887 * @param $grade_item object 888 * @return string eid 889 */ 890 function get_item_eid($grade_item) { 891 return 'i'.$grade_item->id; 892 } 893 894 function get_params_for_iconstr($element) { 895 $strparams = new stdClass(); 896 $strparams->category = ''; 897 $strparams->itemname = ''; 898 $strparams->itemmodule = ''; 899 if (!method_exists($element['object'], 'get_name')) { 900 return $strparams; 901 } 902 903 $strparams->itemname = $element['object']->get_name(); 904 905 // If element name is categorytotal, get the name of the parent category 906 if ($strparams->itemname == get_string('categorytotal', 'grades')) { 907 $parent = $element['object']->get_parent_category(); 908 $strparams->category = $parent->get_name() . ' '; 909 } else { 910 $strparams->category = ''; 911 } 912 913 $strparams->itemmodule = null; 914 if (isset($element['object']->itemmodule)) { 915 $strparams->itemmodule = $element['object']->itemmodule; 916 } 917 return $strparams; 918 } 919 920 /** 921 * Return edit icon for give element 922 * @param object $element 923 * @return string 924 */ 925 function get_edit_icon($element, $gpr) { 926 global $CFG; 927 928 if (!has_capability('moodle/grade:manage', $this->context)) { 929 if ($element['type'] == 'grade' and has_capability('moodle/grade:edit', $this->context)) { 930 // oki - let them override grade 931 } else { 932 return ''; 933 } 934 } 935 936 static $strfeedback = null; 937 static $streditgrade = null; 938 if (is_null($streditgrade)) { 939 $streditgrade = get_string('editgrade', 'grades'); 940 $strfeedback = get_string('feedback'); 941 } 942 943 $strparams = $this->get_params_for_iconstr($element); 944 if ($element['type'] == 'item' or $element['type'] == 'category') { 945 } 946 947 $object = $element['object']; 948 $overlib = ''; 949 950 switch ($element['type']) { 951 case 'item': 952 case 'categoryitem': 953 case 'courseitem': 954 $stredit = get_string('editverbose', 'grades', $strparams); 955 if (empty($object->outcomeid) || empty($CFG->enableoutcomes)) { 956 $url = $CFG->wwwroot.'/grade/edit/tree/item.php?courseid='.$this->courseid.'&id='.$object->id; 957 } else { 958 $url = $CFG->wwwroot.'/grade/edit/tree/outcomeitem.php?courseid='.$this->courseid.'&id='.$object->id; 959 } 960 $url = $gpr->add_url_params($url); 961 break; 962 963 case 'category': 964 $stredit = get_string('editverbose', 'grades', $strparams); 965 $url = $CFG->wwwroot.'/grade/edit/tree/category.php?courseid='.$this->courseid.'&id='.$object->id; 966 $url = $gpr->add_url_params($url); 967 break; 968 969 case 'grade': 970 $stredit = $streditgrade; 971 if (empty($object->id)) { 972 $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&itemid='.$object->itemid.'&userid='.$object->userid; 973 } else { 974 $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&id='.$object->id; 975 } 976 $url = $gpr->add_url_params($url); 977 if (!empty($object->feedback)) { 978 $feedback = addslashes_js(trim(format_string($object->feedback, $object->feedbackformat))); 979 $function = "return overlib('$feedback', BORDER, 0, FGCLASS, 'feedback', " 980 ."CAPTIONFONTCLASS, 'caption', CAPTION, '$strfeedback');"; 981 $overlib = 'onmouseover="'.s($function).'" onmouseout="return nd();"'; 982 } 983 break; 984 985 default: 986 $url = null; 987 } 988 989 if ($url) { 990 return '<a href="'.$url.'"><img '.$overlib.' src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" title="'.$stredit.'"/></a>'; 991 992 } else { 993 return ''; 994 } 995 } 996 997 /** 998 * Return hiding icon for give element 999 * @param object $element 1000 * @return string 1001 */ 1002 function get_hiding_icon($element, $gpr) { 1003 global $CFG; 1004 1005 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:hide', $this->context)) { 1006 return ''; 1007 } 1008 1009 $strparams = $this->get_params_for_iconstr($element); 1010 $strshow = get_string('showverbose', 'grades', $strparams); 1011 $strhide = get_string('hideverbose', 'grades', $strparams); 1012 1013 if ($element['object']->is_hidden()) { 1014 $icon = 'show'; 1015 $tooltip = $strshow; 1016 1017 if ($element['type'] != 'category' and $element['object']->get_hidden() > 1) { // Change the icon and add a tooltip showing the date 1018 $icon = 'hiddenuntil'; 1019 $tooltip = get_string('hiddenuntildate', 'grades', userdate($element['object']->get_hidden())); 1020 } 1021 1022 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=show&sesskey='.sesskey() 1023 . '&eid='.$element['eid']; 1024 $url = $gpr->add_url_params($url); 1025 $action = '<a href="'.$url.'"><img alt="'.$strshow.'" src="'.$CFG->pixpath.'/t/'.$icon.'.gif" class="iconsmall" title="'.$tooltip.'"/></a>'; 1026 1027 } else { 1028 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=hide&sesskey='.sesskey() 1029 . '&eid='.$element['eid']; 1030 $url = $gpr->add_url_params($url); 1031 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/hide.gif" class="iconsmall" alt="'.$strhide.'" title="'.$strhide.'"/></a>'; 1032 } 1033 return $action; 1034 } 1035 1036 /** 1037 * Return locking icon for given element 1038 * @param object $element 1039 * @return string 1040 */ 1041 function get_locking_icon($element, $gpr) { 1042 global $CFG; 1043 1044 $strparams = $this->get_params_for_iconstr($element); 1045 $strunlock = get_string('unlockverbose', 'grades', $strparams); 1046 $strlock = get_string('lockverbose', 'grades', $strparams); 1047 1048 // Don't allow an unlocking action for a grade whose grade item is locked: just print a state icon 1049 if ($element['type'] == 'grade' && $element['object']->grade_item->is_locked()) { 1050 $strparamobj = new stdClass(); 1051 $strparamobj->itemname = $element['object']->grade_item->itemname; 1052 $strnonunlockable = get_string('nonunlockableverbose', 'grades', $strparamobj); 1053 $action = '<img src="'.$CFG->pixpath.'/t/unlock_gray.gif" alt="'.$strnonunlockable.'" class="iconsmall" title="'.$strnonunlockable.'"/>'; 1054 } elseif ($element['object']->is_locked()) { 1055 $icon = 'unlock'; 1056 $tooltip = $strunlock; 1057 1058 if ($element['type'] != 'category' and $element['object']->get_locktime() > 1) { // Change the icon and add a tooltip showing the date 1059 $icon = 'locktime'; 1060 $tooltip = get_string('locktimedate', 'grades', userdate($element['object']->get_locktime())); 1061 } 1062 1063 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:unlock', $this->context)) { 1064 return ''; 1065 } 1066 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=unlock&sesskey='.sesskey() 1067 . '&eid='.$element['eid']; 1068 $url = $gpr->add_url_params($url); 1069 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/'.$icon.'.gif" alt="'.$strunlock.'" class="iconsmall" title="'.$tooltip.'"/></a>'; 1070 1071 } else { 1072 if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:lock', $this->context)) { 1073 return ''; 1074 } 1075 $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=lock&sesskey='.sesskey() 1076 . '&eid='.$element['eid']; 1077 $url = $gpr->add_url_params($url); 1078 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/lock.gif" class="iconsmall" alt="'.$strlock.'" title="' 1079 . $strlock.'"/></a>'; 1080 } 1081 return $action; 1082 } 1083 1084 /** 1085 * Return calculation icon for given element 1086 * @param object $element 1087 * @return string 1088 */ 1089 function get_calculation_icon($element, $gpr) { 1090 global $CFG; 1091 if (!has_capability('moodle/grade:manage', $this->context)) { 1092 return ''; 1093 } 1094 1095 $calculation_icon = ''; 1096 1097 $type = $element['type']; 1098 $object = $element['object']; 1099 1100 1101 if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') { 1102 $strparams = $this->get_params_for_iconstr($element); 1103 $streditcalculation = get_string('editcalculationverbose', 'grades', $strparams); 1104 1105 // show calculation icon only when calculation possible 1106 if (!$object->is_external_item() and ($object->gradetype == GRADE_TYPE_SCALE or $object->gradetype == GRADE_TYPE_VALUE)) { 1107 if ($object->is_calculated()) { 1108 $icon = 'calc.gif'; 1109 } else { 1110 $icon = 'calc_off.gif'; 1111 } 1112 $url = $CFG->wwwroot.'/grade/edit/tree/calculation.php?courseid='.$this->courseid.'&id='.$object->id; 1113 $url = $gpr->add_url_params($url); 1114 $calculation_icon = '<a href="'. $url.'"><img src="'.$CFG->pixpath.'/t/'.$icon.'" class="iconsmall" alt="' 1115 . $streditcalculation.'" title="'.$streditcalculation.'" /></a>'. "\n"; 1116 } 1117 } 1118 1119 return $calculation_icon; 1120 } 1121 } 1122 1123 /** 1124 * Flat structure similar to grade tree. 1125 */ 1126 class grade_seq extends grade_structure { 1127 1128 /** 1129 * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class. 1130 * @var string $commonvars 1131 */ 1132 var $commonvars; 1133 1134 /** 1135 * 1D array of elements 1136 */ 1137 var $elements; 1138 1139 /** 1140 * Constructor, retrieves and stores array of all grade_category and grade_item 1141 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed. 1142 * @param int $courseid 1143 * @param boolean $category_grade_last category grade item is the last child 1144 * @param array $collapsed array of collapsed categories 1145 */ 1146 function grade_seq($courseid, $category_grade_last=false, $nooutcomes=false) { 1147 global $USER, $CFG; 1148 1149 $this->courseid = $courseid; 1150 $this->commonvars = "&sesskey=$USER->sesskey&id=$this->courseid"; 1151 $this->context = get_context_instance(CONTEXT_COURSE, $courseid); 1152 1153 // get course grade tree 1154 $top_element = grade_category::fetch_course_tree($courseid, true); 1155 1156 $this->elements = grade_seq::flatten($top_element, $category_grade_last, $nooutcomes); 1157 1158 foreach ($this->elements as $key=>$unused) { 1159 $this->items[$this->elements[$key]['object']->id] =& $this->elements[$key]['object']; 1160 } 1161 } 1162 1163 /** 1164 * Static recursive helper - makes the grade_item for category the last children 1165 * @static 1166 * @param array $element The seed of the recursion 1167 * @return void 1168 */ 1169 function flatten(&$element, $category_grade_last, $nooutcomes) { 1170 if (empty($element['children'])) { 1171 return array(); 1172 } 1173 $children = array(); 1174 1175 foreach ($element['children'] as $sortorder=>$unused) { 1176 if ($nooutcomes and $element['type'] != 'category' and $element['children'][$sortorder]['object']->is_outcome_item()) { 1177 continue; 1178 } 1179 $children[] = $element['children'][$sortorder]; 1180 } 1181 unset($element['children']); 1182 1183 if ($category_grade_last and count($children) > 1) { 1184 $cat_item = array_shift($children); 1185 array_push($children, $cat_item); 1186 } 1187 1188 $result = array(); 1189 foreach ($children as $child) { 1190 if ($child['type'] == 'category') { 1191 $result = $result + grade_seq::flatten($child, $category_grade_last, $nooutcomes); 1192 } else { 1193 $child['eid'] = 'i'.$child['object']->id; 1194 $result[$child['object']->id] = $child; 1195 } 1196 } 1197 1198 return $result; 1199 } 1200 1201 /** 1202 * Parses the array in search of a given eid and returns a element object with 1203 * information about the element it has found. 1204 * @param int $eid 1205 * @return object element 1206 */ 1207 function locate_element($eid) { 1208 // it is a grade - construct a new object 1209 if (strpos($eid, 'n') === 0) { 1210 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) { 1211 return null; 1212 } 1213 1214 $itemid = $matches[1]; 1215 $userid = $matches[2]; 1216 1217 //extra security check - the grade item must be in this tree 1218 if (!$item_el = $this->locate_element('i'.$itemid)) { 1219 return null; 1220 } 1221 1222 // $gradea->id may be null - means does not exist yet 1223 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid)); 1224 1225 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! 1226 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade'); 1227 1228 } else if (strpos($eid, 'g') === 0) { 1229 $id = (int)substr($eid, 1); 1230 if (!$grade = grade_grade::fetch(array('id'=>$id))) { 1231 return null; 1232 } 1233 //extra security check - the grade item must be in this tree 1234 if (!$item_el = $this->locate_element('i'.$grade->itemid)) { 1235 return null; 1236 } 1237 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! 1238 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade'); 1239 } 1240 1241 // it is a category or item 1242 foreach ($this->elements as $element) { 1243 if ($element['eid'] == $eid) { 1244 return $element; 1245 } 1246 } 1247 1248 return null; 1249 } 1250 } 1251 1252 /** 1253 * This class represents a complete tree of categories, grade_items and final grades, 1254 * organises as an array primarily, but which can also be converted to other formats. 1255 * It has simple method calls with complex implementations, allowing for easy insertion, 1256 * deletion and moving of items and categories within the tree. 1257 */ 1258 class grade_tree extends grade_structure { 1259 1260 /** 1261 * The basic representation of the tree as a hierarchical, 3-tiered array. 1262 * @var object $top_element 1263 */ 1264 var $top_element; 1265 1266 /** 1267 * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class. 1268 * @var string $commonvars 1269 */ 1270 var $commonvars; 1271 1272 /** 1273 * 2D array of grade items and categories 1274 */ 1275 var $levels; 1276 1277 /** 1278 * Grade items 1279 */ 1280 var $items; 1281 1282 /** 1283 * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item 1284 * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed. 1285 * @param int $courseid 1286 * @param boolean $fillers include fillers and colspans, make the levels var "rectangular" 1287 * @param boolean $category_grade_last category grade item is the last child 1288 * @param array $collapsed array of collapsed categories 1289 */ 1290 function grade_tree($courseid, $fillers=true, $category_grade_last=false, $collapsed=null, $nooutcomes=false) { 1291 global $USER, $CFG; 1292 1293 $this->courseid = $courseid; 1294 $this->commonvars = "&sesskey=$USER->sesskey&id=$this->courseid"; 1295 $this->levels = array(); 1296 $this->context = get_context_instance(CONTEXT_COURSE, $courseid); 1297 1298 // get course grade tree 1299 $this->top_element = grade_category::fetch_course_tree($courseid, true); 1300 1301 // collapse the categories if requested 1302 if (!empty($collapsed)) { 1303 grade_tree::category_collapse($this->top_element, $collapsed); 1304 } 1305 1306 // no otucomes if requested 1307 if (!empty($nooutcomes)) { 1308 grade_tree::no_outcomes($this->top_element); 1309 } 1310 1311 // move category item to last position in category 1312 if ($category_grade_last) { 1313 grade_tree::category_grade_last($this->top_element); 1314 } 1315 1316 if ($fillers) { 1317 // inject fake categories == fillers 1318 grade_tree::inject_fillers($this->top_element, 0); 1319 // add colspans to categories and fillers 1320 grade_tree::inject_colspans($this->top_element); 1321 } 1322 1323 grade_tree::fill_levels($this->levels, $this->top_element, 0); 1324 1325 } 1326 1327 /** 1328 * Static recursive helper - removes items from collapsed categories 1329 * @static 1330 * @param array $element The seed of the recursion 1331 * @param array $collapsed array of collapsed categories 1332 * @return void 1333 */ 1334 function category_collapse(&$element, $collapsed) { 1335 if ($element['type'] != 'category') { 1336 return; 1337 } 1338 if (empty($element['children']) or count($element['children']) < 2) { 1339 return; 1340 } 1341 1342 if (in_array($element['object']->id, $collapsed['aggregatesonly'])) { 1343 $category_item = reset($element['children']); //keep only category item 1344 $element['children'] = array(key($element['children'])=>$category_item); 1345 1346 } else { 1347 if (in_array($element['object']->id, $collapsed['gradesonly'])) { // Remove category item 1348 reset($element['children']); 1349 $first_key = key($element['children']); 1350 unset($element['children'][$first_key]); 1351 } 1352 foreach ($element['children'] as $sortorder=>$child) { // Recurse through the element's children 1353 grade_tree::category_collapse($element['children'][$sortorder], $collapsed); 1354 } 1355 } 1356 } 1357 1358 /** 1359 * Static recursive helper - removes all outcomes 1360 * @static 1361 * @param array $element The seed of the recursion 1362 * @return void 1363 */ 1364 function no_outcomes(&$element) { 1365 if ($element['type'] != 'category') { 1366 return; 1367 } 1368 foreach ($element['children'] as $sortorder=>$child) { 1369 if ($element['children'][$sortorder]['type'] == 'item' 1370 and $element['children'][$sortorder]['object']->is_outcome_item()) { 1371 unset($element['children'][$sortorder]); 1372 1373 } else if ($element['children'][$sortorder]['type'] == 'category') { 1374 grade_tree::no_outcomes($element['children'][$sortorder]); 1375 } 1376 } 1377 } 1378 1379 /** 1380 * Static recursive helper - makes the grade_item for category the last children 1381 * @static 1382 * @param array $element The seed of the recursion 1383 * @return void 1384 */ 1385 function category_grade_last(&$element) { 1386 if (empty($element['children'])) { 1387 return; 1388 } 1389 if (count($element['children']) < 2) { 1390 return; 1391 } 1392 $first_item = reset($element['children']); 1393 if ($first_item['type'] == 'categoryitem' or $first_item['type'] == 'courseitem') { 1394 // the category item might have been already removed 1395 $order = key($element['children']); 1396 unset($element['children'][$order]); 1397 $element['children'][$order] =& $first_item; 1398 } 1399 foreach ($element['children'] as $sortorder => $child) { 1400 grade_tree::category_grade_last($element['children'][$sortorder]); 1401 } 1402 } 1403 1404 /** 1405 * Static recursive helper - fills the levels array, useful when accessing tree elements of one level 1406 * @static 1407 * @param int $levels 1408 * @param array $element The seed of the recursion 1409 * @param int $depth 1410 * @return void 1411 */ 1412 function fill_levels(&$levels, &$element, $depth) { 1413 if (!array_key_exists($depth, $levels)) { 1414 $levels[$depth] = array(); 1415 } 1416 1417 // prepare unique identifier 1418 if ($element['type'] == 'category') { 1419 $element['eid'] = 'c'.$element['object']->id; 1420 } else if (in_array($element['type'], array('item', 'courseitem', 'categoryitem'))) { 1421 $element['eid'] = 'i'.$element['object']->id; 1422 $this->items[$element['object']->id] =& $element['object']; 1423 } 1424 1425 $levels[$depth][] =& $element; 1426 $depth++; 1427 if (empty($element['children'])) { 1428 return; 1429 } 1430 $prev = 0; 1431 foreach ($element['children'] as $sortorder=>$child) { 1432 grade_tree::fill_levels($levels, $element['children'][$sortorder], $depth); 1433 $element['children'][$sortorder]['prev'] = $prev; 1434 $element['children'][$sortorder]['next'] = 0; 1435 if ($prev) { 1436 $element['children'][$prev]['next'] = $sortorder; 1437 } 1438 $prev = $sortorder; 1439 } 1440 } 1441 1442 /** 1443 * Static recursive helper - makes full tree (all leafes are at the same level) 1444 */ 1445 function inject_fillers(&$element, $depth) { 1446 $depth++; 1447 1448 if (empty($element['children'])) { 1449 return $depth; 1450 } 1451 $chdepths = array(); 1452 $chids = array_keys($element['children']); 1453 $last_child = end($chids); 1454 $first_child = reset($chids); 1455 1456 foreach ($chids as $chid) { 1457 $chdepths[$chid] = grade_tree::inject_fillers($element['children'][$chid], $depth); 1458 } 1459 arsort($chdepths); 1460 1461 $maxdepth = reset($chdepths); 1462 foreach ($chdepths as $chid=>$chd) { 1463 if ($chd == $maxdepth) { 1464 continue; 1465 } 1466 for ($i=0; $i < $maxdepth-$chd; $i++) { 1467 if ($chid == $first_child) { 1468 $type = 'fillerfirst'; 1469 } else if ($chid == $last_child) { 1470 $type = 'fillerlast'; 1471 } else { 1472 $type = 'filler'; 1473 } 1474 $oldchild =& $element['children'][$chid]; 1475 $element['children'][$chid] = array('object'=>'filler', 'type'=>$type, 'eid'=>'', 'depth'=>$element['object']->depth,'children'=>array($oldchild)); 1476 } 1477 } 1478 1479 return $maxdepth; 1480 } 1481 1482 /** 1483 * Static recursive helper - add colspan information into categories 1484 */ 1485 function inject_colspans(&$element) { 1486 if (empty($element['children'])) { 1487 return 1; 1488 } 1489 $count = 0; 1490 foreach ($element['children'] as $key=>$child) { 1491 $count += grade_tree::inject_colspans($element['children'][$key]); 1492 } 1493 $element['colspan'] = $count; 1494 return $count; 1495 } 1496 1497 /** 1498 * Parses the array in search of a given eid and returns a element object with 1499 * information about the element it has found. 1500 * @param int $eid 1501 * @return object element 1502 */ 1503 function locate_element($eid) { 1504 // it is a grade - construct a new object 1505 if (strpos($eid, 'n') === 0) { 1506 if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) { 1507 return null; 1508 } 1509 1510 $itemid = $matches[1]; 1511 $userid = $matches[2]; 1512 1513 //extra security check - the grade item must be in this tree 1514 if (!$item_el = $this->locate_element('i'.$itemid)) { 1515 return null; 1516 } 1517 1518 // $gradea->id may be null - means does not exist yet 1519 $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid)); 1520 1521 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! 1522 return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade'); 1523 1524 } else if (strpos($eid, 'g') === 0) { 1525 $id = (int)substr($eid, 1); 1526 if (!$grade = grade_grade::fetch(array('id'=>$id))) { 1527 return null; 1528 } 1529 //extra security check - the grade item must be in this tree 1530 if (!$item_el = $this->locate_element('i'.$grade->itemid)) { 1531 return null; 1532 } 1533 $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! 1534 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade'); 1535 } 1536 1537 // it is a category or item 1538 foreach ($this->levels as $row) { 1539 foreach ($row as $element) { 1540 if ($element['type'] == 'filler') { 1541 continue; 1542 } 1543 if ($element['eid'] == $eid) { 1544 return $element; 1545 } 1546 } 1547 } 1548 1549 return null; 1550 } 1551 } 1552 1553 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |