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