Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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