Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Course completion progress report 19 * 20 * @package report 21 * @subpackage completion 22 * @copyright 2009 Catalyst IT Ltd 23 * @author Aaron Barnes <aaronb@catalyst.net.nz> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 use core\report_helper; 28 29 require_once(__DIR__.'/../../config.php'); 30 require_once("{$CFG->libdir}/completionlib.php"); 31 32 /** 33 * Configuration 34 */ 35 define('COMPLETION_REPORT_PAGE', 25); 36 define('COMPLETION_REPORT_COL_TITLES', true); 37 38 /* 39 * Setup page, check permissions 40 */ 41 42 // Get course 43 $courseid = required_param('course', PARAM_INT); 44 $format = optional_param('format','',PARAM_ALPHA); 45 $sort = optional_param('sort','',PARAM_ALPHA); 46 $edituser = optional_param('edituser', 0, PARAM_INT); 47 48 49 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); 50 $context = context_course::instance($course->id); 51 52 $url = new moodle_url('/report/completion/index.php', array('course'=>$course->id)); 53 $PAGE->set_url($url); 54 $PAGE->set_pagelayout('report'); 55 56 $firstnamesort = ($sort == 'firstname'); 57 $excel = ($format == 'excelcsv'); 58 $csv = ($format == 'csv' || $excel); 59 60 // Load CSV library 61 if ($csv) { 62 require_once("{$CFG->libdir}/csvlib.class.php"); 63 } 64 65 // Paging 66 $start = optional_param('start', 0, PARAM_INT); 67 $sifirst = optional_param('sifirst', 'all', PARAM_NOTAGS); 68 $silast = optional_param('silast', 'all', PARAM_NOTAGS); 69 70 // Whether to show extra user identity information 71 // TODO Does not support custom user profile fields (MDL-70456). 72 $extrafields = \core_user\fields::get_identity_fields($context, false); 73 $leftcols = 1 + count($extrafields); 74 75 // Check permissions 76 require_login($course); 77 78 require_capability('report/completion:view', $context); 79 80 // Get group mode 81 $group = groups_get_course_group($course, true); // Supposed to verify group 82 if ($group === 0 && $course->groupmode == SEPARATEGROUPS) { 83 require_capability('moodle/site:accessallgroups',$context); 84 } 85 86 /** 87 * Load data 88 */ 89 90 // Retrieve course_module data for all modules in the course 91 $modinfo = get_fast_modinfo($course); 92 93 // Get criteria for course 94 $completion = new completion_info($course); 95 96 if (!$completion->has_criteria()) { 97 print_error('nocriteriaset', 'completion', $CFG->wwwroot.'/course/report.php?id='.$course->id); 98 } 99 100 // Get criteria and put in correct order 101 $criteria = array(); 102 103 foreach ($completion->get_criteria(COMPLETION_CRITERIA_TYPE_COURSE) as $criterion) { 104 $criteria[] = $criterion; 105 } 106 107 foreach ($completion->get_criteria(COMPLETION_CRITERIA_TYPE_ACTIVITY) as $criterion) { 108 $criteria[] = $criterion; 109 } 110 111 foreach ($completion->get_criteria() as $criterion) { 112 if (!in_array($criterion->criteriatype, array( 113 COMPLETION_CRITERIA_TYPE_COURSE, COMPLETION_CRITERIA_TYPE_ACTIVITY))) { 114 $criteria[] = $criterion; 115 } 116 } 117 118 // Can logged in user mark users as complete? 119 // (if the logged in user has a role defined in the role criteria) 120 $allow_marking = false; 121 $allow_marking_criteria = null; 122 123 if (!$csv) { 124 // Get role criteria 125 $rcriteria = $completion->get_criteria(COMPLETION_CRITERIA_TYPE_ROLE); 126 127 if (!empty($rcriteria)) { 128 129 foreach ($rcriteria as $rcriterion) { 130 $users = get_role_users($rcriterion->role, $context, true); 131 132 // If logged in user has this role, allow marking complete 133 if ($users && in_array($USER->id, array_keys($users))) { 134 $allow_marking = true; 135 $allow_marking_criteria = $rcriterion->id; 136 break; 137 } 138 } 139 } 140 } 141 142 /* 143 * Setup page header 144 */ 145 if ($csv) { 146 147 $shortname = format_string($course->shortname, true, array('context' => $context)); 148 $shortname = preg_replace('/[^a-z0-9-]/', '_',core_text::strtolower(strip_tags($shortname))); 149 150 $export = new csv_export_writer('comma', '"', 'application/download', $excel); 151 $export->set_filename('completion-'.$shortname); 152 153 } else { 154 // Navigation and header 155 $strcompletion = get_string('coursecompletion'); 156 157 $PAGE->set_title($strcompletion); 158 $PAGE->set_heading($course->fullname); 159 160 echo $OUTPUT->header(); 161 // Print the selected dropdown. 162 $pluginname = get_string('pluginname', 'report_completion'); 163 report_helper::print_report_selector($pluginname); 164 165 // Handle groups (if enabled) 166 groups_print_course_menu($course, $CFG->wwwroot.'/report/completion/index.php?course='.$course->id); 167 } 168 169 if ($sifirst !== 'all') { 170 set_user_preference('ifirst', $sifirst); 171 } 172 if ($silast !== 'all') { 173 set_user_preference('ilast', $silast); 174 } 175 176 if (!empty($USER->preference['ifirst'])) { 177 $sifirst = $USER->preference['ifirst']; 178 } else { 179 $sifirst = 'all'; 180 } 181 182 if (!empty($USER->preference['ilast'])) { 183 $silast = $USER->preference['ilast']; 184 } else { 185 $silast = 'all'; 186 } 187 188 // Generate where clause 189 $where = array(); 190 $where_params = array(); 191 192 if ($sifirst !== 'all') { 193 $where[] = $DB->sql_like('u.firstname', ':sifirst', false, false); 194 $where_params['sifirst'] = $sifirst.'%'; 195 } 196 197 if ($silast !== 'all') { 198 $where[] = $DB->sql_like('u.lastname', ':silast', false, false); 199 $where_params['silast'] = $silast.'%'; 200 } 201 202 // Get user match count 203 $total = $completion->get_num_tracked_users(implode(' AND ', $where), $where_params, $group); 204 205 // Total user count 206 $grandtotal = $completion->get_num_tracked_users('', array(), $group); 207 208 // If no users in this course what-so-ever 209 if (!$grandtotal) { 210 echo $OUTPUT->container(get_string('err_nousers', 'completion'), 'errorbox errorboxcontent'); 211 echo $OUTPUT->footer(); 212 exit; 213 } 214 215 // Get user data 216 $progress = array(); 217 218 if ($total) { 219 $progress = $completion->get_progress_all( 220 implode(' AND ', $where), 221 $where_params, 222 $group, 223 $firstnamesort ? 'u.firstname ASC' : 'u.lastname ASC', 224 $csv ? 0 : COMPLETION_REPORT_PAGE, 225 $csv ? 0 : $start, 226 $context 227 ); 228 } 229 230 // Build link for paging 231 $link = $CFG->wwwroot.'/report/completion/index.php?course='.$course->id; 232 if (strlen($sort)) { 233 $link .= '&sort='.$sort; 234 } 235 $link .= '&start='; 236 237 $pagingbar = ''; 238 239 // Initials bar. 240 $prefixfirst = 'sifirst'; 241 $prefixlast = 'silast'; 242 $pagingbar .= $OUTPUT->initials_bar($sifirst, 'firstinitial', get_string('firstname'), $prefixfirst, $url); 243 $pagingbar .= $OUTPUT->initials_bar($silast, 'lastinitial', get_string('lastname'), $prefixlast, $url); 244 245 // Do we need a paging bar? 246 if ($total > COMPLETION_REPORT_PAGE) { 247 248 // Paging bar 249 $pagingbar .= '<div class="paging">'; 250 $pagingbar .= get_string('page').': '; 251 252 $sistrings = array(); 253 if ($sifirst != 'all') { 254 $sistrings[] = "sifirst={$sifirst}"; 255 } 256 if ($silast != 'all') { 257 $sistrings[] = "silast={$silast}"; 258 } 259 $sistring = !empty($sistrings) ? '&'.implode('&', $sistrings) : ''; 260 261 // Display previous link 262 if ($start > 0) { 263 $pstart = max($start - COMPLETION_REPORT_PAGE, 0); 264 $pagingbar .= "(<a class=\"previous\" href=\"{$link}{$pstart}{$sistring}\">".get_string('previous').'</a>) '; 265 } 266 267 // Create page links 268 $curstart = 0; 269 $curpage = 0; 270 while ($curstart < $total) { 271 $curpage++; 272 273 if ($curstart == $start) { 274 $pagingbar .= ' '.$curpage.' '; 275 } 276 else { 277 $pagingbar .= " <a href=\"{$link}{$curstart}{$sistring}\">$curpage</a> "; 278 } 279 280 $curstart += COMPLETION_REPORT_PAGE; 281 } 282 283 // Display next link 284 $nstart = $start + COMPLETION_REPORT_PAGE; 285 if ($nstart < $total) { 286 $pagingbar .= " (<a class=\"next\" href=\"{$link}{$nstart}{$sistring}\">".get_string('next').'</a>)'; 287 } 288 289 $pagingbar .= '</div>'; 290 } 291 292 /* 293 * Draw table header 294 */ 295 296 // Start of table 297 if (!$csv) { 298 print '<br class="clearer"/>'; // ugh 299 300 $total_header = ($total == $grandtotal) ? $total : "{$total}/{$grandtotal}"; 301 echo $OUTPUT->heading(get_string('allparticipants').": {$total_header}", 3); 302 303 print $pagingbar; 304 305 if (!$total) { 306 echo $OUTPUT->heading(get_string('nothingtodisplay'), 2); 307 echo $OUTPUT->footer(); 308 exit; 309 } 310 311 print '<table id="completion-progress" class="table table-bordered generaltable flexible boxaligncenter 312 completionreport" style="text-align: left" cellpadding="5" border="1">'; 313 314 // Print criteria group names 315 print PHP_EOL.'<thead><tr style="vertical-align: top">'; 316 echo '<th scope="row" class="rowheader" colspan="' . $leftcols . '">' . 317 get_string('criteriagroup', 'completion') . '</th>'; 318 319 $current_group = false; 320 $col_count = 0; 321 for ($i = 0; $i <= count($criteria); $i++) { 322 323 if (isset($criteria[$i])) { 324 $criterion = $criteria[$i]; 325 326 if ($current_group && $criterion->criteriatype === $current_group->criteriatype) { 327 ++$col_count; 328 continue; 329 } 330 } 331 332 // Print header cell 333 if ($col_count) { 334 print '<th scope="col" colspan="'.$col_count.'" class="colheader criteriagroup">'.$current_group->get_type_title().'</th>'; 335 } 336 337 if (isset($criteria[$i])) { 338 // Move to next criteria type 339 $current_group = $criterion; 340 $col_count = 1; 341 } 342 } 343 344 // Overall course completion status 345 print '<th style="text-align: center;">'.get_string('course').'</th>'; 346 347 print '</tr>'; 348 349 // Print aggregation methods 350 print PHP_EOL.'<tr style="vertical-align: top">'; 351 echo '<th scope="row" class="rowheader" colspan="' . $leftcols . '">' . 352 get_string('aggregationmethod', 'completion').'</th>'; 353 354 $current_group = false; 355 $col_count = 0; 356 for ($i = 0; $i <= count($criteria); $i++) { 357 358 if (isset($criteria[$i])) { 359 $criterion = $criteria[$i]; 360 361 if ($current_group && $criterion->criteriatype === $current_group->criteriatype) { 362 ++$col_count; 363 continue; 364 } 365 } 366 367 // Print header cell 368 if ($col_count) { 369 $has_agg = array( 370 COMPLETION_CRITERIA_TYPE_COURSE, 371 COMPLETION_CRITERIA_TYPE_ACTIVITY, 372 COMPLETION_CRITERIA_TYPE_ROLE, 373 ); 374 375 if (in_array($current_group->criteriatype, $has_agg)) { 376 // Try load a aggregation method 377 $method = $completion->get_aggregation_method($current_group->criteriatype); 378 379 $method = $method == 1 ? get_string('all') : get_string('any'); 380 381 } else { 382 $method = '-'; 383 } 384 385 print '<th scope="col" colspan="'.$col_count.'" class="colheader aggheader">'.$method.'</th>'; 386 } 387 388 if (isset($criteria[$i])) { 389 // Move to next criteria type 390 $current_group = $criterion; 391 $col_count = 1; 392 } 393 } 394 395 // Overall course aggregation method 396 print '<th scope="col" class="colheader aggheader aggcriteriacourse">'; 397 398 // Get course aggregation 399 $method = $completion->get_aggregation_method(); 400 401 print $method == 1 ? get_string('all') : get_string('any'); 402 print '</th>'; 403 404 print '</tr>'; 405 406 // Print criteria titles 407 if (COMPLETION_REPORT_COL_TITLES) { 408 409 print PHP_EOL.'<tr>'; 410 echo '<th scope="row" class="rowheader" colspan="' . $leftcols . '">' . 411 get_string('criteria', 'completion') . '</th>'; 412 413 foreach ($criteria as $criterion) { 414 // Get criteria details 415 $details = $criterion->get_title_detailed(); 416 print '<th scope="col" class="colheader criterianame">'; 417 print '<div class="rotated-text-container"><span class="rotated-text">'.$details.'</span></div>'; 418 print '</th>'; 419 } 420 421 // Overall course completion status 422 print '<th scope="col" class="colheader criterianame">'; 423 print '<div class="rotated-text-container"><span class="rotated-text">'.get_string('coursecomplete', 'completion').'</span></div>'; 424 print '</th></tr>'; 425 } 426 427 // Print user heading and icons 428 print '<tr>'; 429 430 // User heading / sort option 431 print '<th scope="col" class="completion-sortchoice" style="clear: both;">'; 432 433 $sistring = "&silast={$silast}&sifirst={$sifirst}"; 434 435 if ($firstnamesort) { 436 print 437 get_string('firstname')." / <a href=\"./index.php?course={$course->id}{$sistring}\">". 438 get_string('lastname').'</a>'; 439 } else { 440 print "<a href=\"./index.php?course={$course->id}&sort=firstname{$sistring}\">". 441 get_string('firstname').'</a> / '. 442 get_string('lastname'); 443 } 444 print '</th>'; 445 446 // Print user identity columns 447 foreach ($extrafields as $field) { 448 echo '<th scope="col" class="completion-identifyfield">' . 449 \core_user\fields::get_display_name($field) . '</th>'; 450 } 451 452 /// 453 /// Print criteria icons 454 /// 455 foreach ($criteria as $criterion) { 456 457 // Generate icon details 458 $iconlink = ''; 459 $iconalt = ''; // Required 460 $iconattributes = array('class' => 'icon'); 461 switch ($criterion->criteriatype) { 462 463 case COMPLETION_CRITERIA_TYPE_ACTIVITY: 464 465 // Display icon 466 $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$criterion->moduleinstance; 467 $iconattributes['title'] = $modinfo->cms[$criterion->moduleinstance]->get_formatted_name(); 468 $iconalt = get_string('modulename', $criterion->module); 469 break; 470 471 case COMPLETION_CRITERIA_TYPE_COURSE: 472 // Load course 473 $crs = $DB->get_record('course', array('id' => $criterion->courseinstance)); 474 475 // Display icon 476 $iconlink = $CFG->wwwroot.'/course/view.php?id='.$criterion->courseinstance; 477 $iconattributes['title'] = format_string($crs->fullname, true, array('context' => context_course::instance($crs->id, MUST_EXIST))); 478 $iconalt = format_string($crs->shortname, true, array('context' => context_course::instance($crs->id))); 479 break; 480 481 case COMPLETION_CRITERIA_TYPE_ROLE: 482 // Load role 483 $role = $DB->get_record('role', array('id' => $criterion->role)); 484 485 // Display icon 486 $iconalt = $role->name; 487 break; 488 } 489 490 // Create icon alt if not supplied 491 if (!$iconalt) { 492 $iconalt = $criterion->get_title(); 493 } 494 495 // Print icon and cell 496 print '<th class="criteriaicon">'; 497 498 print ($iconlink ? '<a href="'.$iconlink.'" title="'.$iconattributes['title'].'">' : ''); 499 print $OUTPUT->render($criterion->get_icon($iconalt, $iconattributes)); 500 print ($iconlink ? '</a>' : ''); 501 502 print '</th>'; 503 } 504 505 // Overall course completion status 506 print '<th class="criteriaicon">'; 507 print $OUTPUT->pix_icon('i/course', get_string('coursecomplete', 'completion')); 508 print '</th>'; 509 510 print '</tr></thead>'; 511 512 echo '<tbody>'; 513 } else { 514 // The CSV headers 515 $row = array(); 516 517 $row[] = get_string('id', 'report_completion'); 518 $row[] = get_string('name', 'report_completion'); 519 foreach ($extrafields as $field) { 520 $row[] = \core_user\fields::get_display_name($field); 521 } 522 523 // Add activity headers 524 foreach ($criteria as $criterion) { 525 526 // Handle activity completion differently 527 if ($criterion->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) { 528 529 // Load activity 530 $mod = $criterion->get_mod_instance(); 531 $row[] = $formattedname = format_string($mod->name, true, 532 array('context' => context_module::instance($criterion->moduleinstance))); 533 $row[] = $formattedname . ' - ' . get_string('completiondate', 'report_completion'); 534 } 535 else { 536 // Handle all other criteria 537 $row[] = strip_tags($criterion->get_title_detailed()); 538 } 539 } 540 541 $row[] = get_string('coursecomplete', 'completion'); 542 543 $export->add_data($row); 544 } 545 546 /// 547 /// Display a row for each user 548 /// 549 foreach ($progress as $user) { 550 551 // User name 552 if ($csv) { 553 $row = array(); 554 $row[] = $user->id; 555 $row[] = fullname($user, has_capability('moodle/site:viewfullnames', $context)); 556 foreach ($extrafields as $field) { 557 $row[] = $user->{$field}; 558 } 559 } else { 560 print PHP_EOL.'<tr id="user-'.$user->id.'">'; 561 562 if (completion_can_view_data($user->id, $course)) { 563 $userurl = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id, 'user' => $user->id)); 564 } else { 565 $userurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id)); 566 } 567 568 print '<th scope="row"><a href="' . $userurl->out() . '">' . 569 fullname($user, has_capability('moodle/site:viewfullnames', $context)) . '</a></th>'; 570 foreach ($extrafields as $field) { 571 echo '<td>'.s($user->{$field}).'</td>'; 572 } 573 } 574 575 // Progress for each course completion criteria 576 foreach ($criteria as $criterion) { 577 578 $criteria_completion = $completion->get_user_completion($user->id, $criterion); 579 $is_complete = $criteria_completion->is_complete(); 580 581 // Handle activity completion differently 582 if ($criterion->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) { 583 584 // Load activity 585 $activity = $modinfo->cms[$criterion->moduleinstance]; 586 587 // Get progress information and state 588 if (array_key_exists($activity->id, $user->progress)) { 589 $state = $user->progress[$activity->id]->completionstate; 590 } else if ($is_complete) { 591 $state = COMPLETION_COMPLETE; 592 } else { 593 $state = COMPLETION_INCOMPLETE; 594 } 595 if ($is_complete) { 596 $date = userdate($criteria_completion->timecompleted, get_string('strftimedatetimeshort', 'langconfig')); 597 } else { 598 $date = ''; 599 } 600 601 // Work out how it corresponds to an icon 602 switch($state) { 603 case COMPLETION_INCOMPLETE : $completiontype = 'n'; break; 604 case COMPLETION_COMPLETE : $completiontype = 'y'; break; 605 case COMPLETION_COMPLETE_PASS : $completiontype = 'pass'; break; 606 case COMPLETION_COMPLETE_FAIL : $completiontype = 'fail'; break; 607 } 608 609 $auto = $activity->completion == COMPLETION_TRACKING_AUTOMATIC; 610 $completionicon = 'completion-'.($auto ? 'auto' : 'manual').'-'.$completiontype; 611 612 $describe = get_string('completion-'.$completiontype, 'completion'); 613 $a = new StdClass(); 614 $a->state = $describe; 615 $a->date = $date; 616 $a->user = fullname($user); 617 $a->activity = $activity->get_formatted_name(); 618 $fulldescribe = get_string('progress-title', 'completion', $a); 619 620 if ($csv) { 621 $row[] = $describe; 622 $row[] = $date; 623 } else { 624 print '<td class="completion-progresscell">'; 625 626 print $OUTPUT->pix_icon('i/' . $completionicon, $fulldescribe); 627 628 print '</td>'; 629 } 630 631 continue; 632 } 633 634 // Handle all other criteria 635 $completiontype = $is_complete ? 'y' : 'n'; 636 $completionicon = 'completion-auto-'.$completiontype; 637 638 $describe = get_string('completion-'.$completiontype, 'completion'); 639 640 $a = new stdClass(); 641 $a->state = $describe; 642 643 if ($is_complete) { 644 $a->date = userdate($criteria_completion->timecompleted, get_string('strftimedatetimeshort', 'langconfig')); 645 } else { 646 $a->date = ''; 647 } 648 649 $a->user = fullname($user); 650 $a->activity = strip_tags($criterion->get_title()); 651 $fulldescribe = get_string('progress-title', 'completion', $a); 652 653 if ($csv) { 654 $row[] = $a->date; 655 } else { 656 657 print '<td class="completion-progresscell">'; 658 659 if ($allow_marking_criteria === $criterion->id) { 660 $describe = get_string('completion-'.$completiontype, 'completion'); 661 662 $toggleurl = new moodle_url( 663 '/course/togglecompletion.php', 664 array( 665 'user' => $user->id, 666 'course' => $course->id, 667 'rolec' => $allow_marking_criteria, 668 'sesskey' => sesskey() 669 ) 670 ); 671 672 print '<a href="'.$toggleurl->out().'" title="'.s(get_string('clicktomarkusercomplete', 'report_completion')).'">' . 673 $OUTPUT->pix_icon('i/completion-manual-' . ($is_complete ? 'y' : 'n'), $describe) . '</a></td>'; 674 } else { 675 print $OUTPUT->pix_icon('i/' . $completionicon, $fulldescribe) . '</td>'; 676 } 677 678 print '</td>'; 679 } 680 } 681 682 // Handle overall course completion 683 684 // Load course completion 685 $params = array( 686 'userid' => $user->id, 687 'course' => $course->id 688 ); 689 690 $ccompletion = new completion_completion($params); 691 $completiontype = $ccompletion->is_complete() ? 'y' : 'n'; 692 693 $describe = get_string('completion-'.$completiontype, 'completion'); 694 695 $a = new StdClass; 696 697 if ($ccompletion->is_complete()) { 698 $a->date = userdate($ccompletion->timecompleted, get_string('strftimedatetimeshort', 'langconfig')); 699 } else { 700 $a->date = ''; 701 } 702 703 $a->state = $describe; 704 $a->user = fullname($user); 705 $a->activity = strip_tags(get_string('coursecomplete', 'completion')); 706 $fulldescribe = get_string('progress-title', 'completion', $a); 707 708 if ($csv) { 709 $row[] = $a->date; 710 } else { 711 712 print '<td class="completion-progresscell">'; 713 714 // Display course completion status icon 715 print $OUTPUT->pix_icon('i/completion-auto-' . $completiontype, $fulldescribe); 716 717 print '</td>'; 718 } 719 720 if ($csv) { 721 $export->add_data($row); 722 } else { 723 print '</tr>'; 724 } 725 } 726 727 if ($csv) { 728 $export->download_file(); 729 } else { 730 echo '</tbody>'; 731 } 732 733 print '</table>'; 734 735 $csvurl = new moodle_url('/report/completion/index.php', array('course' => $course->id, 'format' => 'csv')); 736 $excelurl = new moodle_url('/report/completion/index.php', array('course' => $course->id, 'format' => 'excelcsv')); 737 738 print '<ul class="export-actions">'; 739 print '<li><a href="'.$csvurl->out().'">'.get_string('csvdownload','completion').'</a></li>'; 740 print '<li><a href="'.$excelurl->out().'">'.get_string('excelcsvdownload','completion').'</a></li>'; 741 print '</ul>'; 742 743 echo $OUTPUT->footer($course); 744 745 // Trigger a report viewed event. 746 $event = \report_completion\event\report_viewed::create(array('context' => $context)); 747 $event->trigger();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body