Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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