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