Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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   * This page handles listing of quiz overrides
  19   *
  20   * @package    mod_quiz
  21   * @copyright  2010 Matt Petro
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  require_once(__DIR__ . '/../../config.php');
  26  require_once($CFG->dirroot.'/mod/quiz/lib.php');
  27  require_once($CFG->dirroot.'/mod/quiz/locallib.php');
  28  require_once($CFG->dirroot.'/mod/quiz/override_form.php');
  29  
  30  
  31  $cmid = required_param('cmid', PARAM_INT);
  32  $mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', default is 'group'.
  33  
  34  list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'quiz');
  35  $quiz = $DB->get_record('quiz', ['id' => $cm->instance], '*', MUST_EXIST);
  36  
  37  require_login($course, false, $cm);
  38  
  39  $context = context_module::instance($cm->id);
  40  
  41  // Check the user has the required capabilities to list overrides.
  42  $canedit = has_capability('mod/quiz:manageoverrides', $context);
  43  if (!$canedit) {
  44      require_capability('mod/quiz:viewoverrides', $context);
  45  }
  46  
  47  $quizgroupmode = groups_get_activity_groupmode($cm);
  48  $showallgroups = ($quizgroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $context);
  49  
  50  // Get the course groups that the current user can access.
  51  $groups = $showallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
  52  
  53  // Default mode is "group", unless there are no groups.
  54  if ($mode != "user" and $mode != "group") {
  55      if (!empty($groups)) {
  56          $mode = "group";
  57      } else {
  58          $mode = "user";
  59      }
  60  }
  61  $groupmode = ($mode == "group");
  62  
  63  $url = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $cm->id, 'mode' => $mode]);
  64  
  65  $title = get_string('overridesforquiz', 'quiz',
  66          format_string($quiz->name, true, ['context' => $context]));
  67  $PAGE->set_url($url);
  68  $PAGE->set_pagelayout('admin');
  69  $PAGE->set_title($title);
  70  $PAGE->set_heading($course->fullname);
  71  
  72  // Delete orphaned group overrides.
  73  $sql = 'SELECT o.id
  74            FROM {quiz_overrides} o
  75       LEFT JOIN {groups} g ON o.groupid = g.id
  76           WHERE o.groupid IS NOT NULL
  77                 AND g.id IS NULL
  78                 AND o.quiz = ?';
  79  $params = [$quiz->id];
  80  $orphaned = $DB->get_records_sql($sql, $params);
  81  if (!empty($orphaned)) {
  82      $DB->delete_records_list('quiz_overrides', 'id', array_keys($orphaned));
  83  }
  84  
  85  $overrides = [];
  86  $colclasses = [];
  87  $headers = [];
  88  
  89  // Fetch all overrides.
  90  if ($groupmode) {
  91      $headers[] = get_string('group');
  92      // To filter the result by the list of groups that the current user has access to.
  93      if ($groups) {
  94          $params = ['quizid' => $quiz->id];
  95          list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
  96          $params += $inparams;
  97  
  98          $sql = "SELECT o.*, g.name
  99                    FROM {quiz_overrides} o
 100                    JOIN {groups} g ON o.groupid = g.id
 101                   WHERE o.quiz = :quizid AND g.id $insql
 102                ORDER BY g.name";
 103  
 104          $overrides = $DB->get_records_sql($sql, $params);
 105      }
 106  
 107  } else {
 108      // User overrides.
 109      $colclasses[] = 'colname';
 110      $headers[] = get_string('user');
 111      // TODO Does not support custom user profile fields (MDL-70456).
 112      $userfieldsapi = \core_user\fields::for_identity($context, false)->with_name()->with_userpic();
 113      $extrauserfields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
 114      foreach ($extrauserfields as $field) {
 115          $colclasses[] = 'col' . $field;
 116          $headers[] = \core_user\fields::get_display_name($field);
 117      }
 118  
 119      list($sort, $params) = users_order_by_sql('u');
 120      $params['quizid'] = $quiz->id;
 121      $userfields = $userfieldsapi->get_sql('u', true, '', 'userid', false)->selects;
 122  
 123      if ($showallgroups) {
 124          $groupsjoin = '';
 125          $groupswhere = '';
 126  
 127      } else if ($groups) {
 128          list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
 129          $groupsjoin = 'JOIN {groups_members} gm ON u.id = gm.userid';
 130          $groupswhere = ' AND gm.groupid ' . $insql;
 131          $params += $inparams;
 132  
 133      } else {
 134          // User cannot see any data.
 135          $groupsjoin = '';
 136          $groupswhere = ' AND 1 = 2';
 137      }
 138  
 139      $overrides = $DB->get_records_sql("
 140              SELECT o.*, $userfields
 141                FROM {quiz_overrides} o
 142                JOIN {user} u ON o.userid = u.id
 143                $groupsjoin
 144               WHERE o.quiz = :quizid
 145                 $groupswhere
 146               ORDER BY $sort
 147              ", $params);
 148  }
 149  
 150  // Initialise table.
 151  $table = new html_table();
 152  $table->head = $headers;
 153  $table->colclasses = $colclasses;
 154  $table->headspan = array_fill(0, count($headers), 1);
 155  
 156  $table->head[] = get_string('overrides', 'quiz');
 157  $table->colclasses[] = 'colsetting';
 158  $table->colclasses[] = 'colvalue';
 159  $table->headspan[] = 2;
 160  
 161  if ($canedit) {
 162      $table->head[] = get_string('action');
 163      $table->colclasses[] = 'colaction';
 164      $table->headspan[] = 1;
 165  }
 166  $userurl = new moodle_url('/user/view.php', []);
 167  $groupurl = new moodle_url('/group/overview.php', ['id' => $cm->course]);
 168  
 169  $overridedeleteurl = new moodle_url('/mod/quiz/overridedelete.php');
 170  $overrideediturl = new moodle_url('/mod/quiz/overrideedit.php');
 171  
 172  $hasinactive = false; // Whether there are any inactive overrides.
 173  
 174  foreach ($overrides as $override) {
 175  
 176      // Check if this override is active.
 177      $active = true;
 178      if (!$groupmode) {
 179          if (!has_capability('mod/quiz:attempt', $context, $override->userid)) {
 180              // User not allowed to take the quiz.
 181              $active = false;
 182          } else if (!\core_availability\info_module::is_user_visible($cm, $override->userid)) {
 183              // User cannot access the module.
 184              $active = false;
 185          }
 186      }
 187      if (!$active) {
 188          $hasinactive = true;
 189      }
 190  
 191      // Prepare the information about which settings are overridden.
 192      $fields = [];
 193      $values = [];
 194  
 195      // Format timeopen.
 196      if (isset($override->timeopen)) {
 197          $fields[] = get_string('quizopens', 'quiz');
 198          $values[] = $override->timeopen > 0 ?
 199                  userdate($override->timeopen) : get_string('noopen', 'quiz');
 200      }
 201      // Format timeclose.
 202      if (isset($override->timeclose)) {
 203          $fields[] = get_string('quizcloses', 'quiz');
 204          $values[] = $override->timeclose > 0 ?
 205                  userdate($override->timeclose) : get_string('noclose', 'quiz');
 206      }
 207      // Format timelimit.
 208      if (isset($override->timelimit)) {
 209          $fields[] = get_string('timelimit', 'quiz');
 210          $values[] = $override->timelimit > 0 ?
 211                  format_time($override->timelimit) : get_string('none', 'quiz');
 212      }
 213      // Format number of attempts.
 214      if (isset($override->attempts)) {
 215          $fields[] = get_string('attempts', 'quiz');
 216          $values[] = $override->attempts > 0 ?
 217                  $override->attempts : get_string('unlimited');
 218      }
 219      // Format password.
 220      if (isset($override->password)) {
 221          $fields[] = get_string('requirepassword', 'quiz');
 222          $values[] = $override->password !== '' ?
 223                  get_string('enabled', 'quiz') : get_string('none', 'quiz');
 224      }
 225  
 226      // Prepare the information about who this override applies to.
 227      $extranamebit = $active ? '' : '*';
 228      $usercells = [];
 229      if ($groupmode) {
 230          $groupcell = new html_table_cell();
 231          $groupcell->rowspan = count($fields);
 232          $groupcell->text = html_writer::link(new moodle_url($groupurl, ['group' => $override->groupid]),
 233                  $override->name . $extranamebit);
 234          $usercells[] = $groupcell;
 235      } else {
 236          $usercell = new html_table_cell();
 237          $usercell->rowspan = count($fields);
 238          $usercell->text = html_writer::link(new moodle_url($userurl, ['id' => $override->userid]),
 239                  fullname($override) . $extranamebit);
 240          $usercells[] = $usercell;
 241  
 242          foreach ($extrauserfields as $field) {
 243              $usercell = new html_table_cell();
 244              $usercell->rowspan = count($fields);
 245              $usercell->text = s($override->$field);
 246              $usercells[] = $usercell;
 247          }
 248      }
 249  
 250      // Prepare the actions.
 251      if ($canedit) {
 252          // Icons.
 253          $iconstr = '';
 254  
 255          // Edit.
 256          $editurlstr = $overrideediturl->out(true, ['id' => $override->id]);
 257          $iconstr = '<a title="' . get_string('edit') . '" href="' . $editurlstr . '">' .
 258                  $OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a> ';
 259          // Duplicate.
 260          $copyurlstr = $overrideediturl->out(true,
 261                  ['id' => $override->id, 'action' => 'duplicate']);
 262          $iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
 263                  $OUTPUT->pix_icon('t/copy', get_string('copy')) . '</a> ';
 264          // Delete.
 265          $deleteurlstr = $overridedeleteurl->out(true,
 266                  ['id' => $override->id, 'sesskey' => sesskey()]);
 267          $iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
 268                  $OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a> ';
 269  
 270          $actioncell = new html_table_cell();
 271          $actioncell->rowspan = count($fields);
 272          $actioncell->text = $iconstr;
 273      }
 274  
 275      // Add the data to the table.
 276      for ($i = 0; $i < count($fields); ++$i) {
 277          $row = new html_table_row();
 278          if (!$active) {
 279              $row->attributes['class'] = 'dimmed_text';
 280          }
 281  
 282          if ($i == 0) {
 283              $row->cells = $usercells;
 284          }
 285  
 286          $labelcell = new html_table_cell();
 287          $labelcell->text = $fields[$i];
 288          $row->cells[] = $labelcell;
 289          $valuecell = new html_table_cell();
 290          $valuecell->text = $values[$i];
 291          $row->cells[] = $valuecell;
 292  
 293          if ($canedit && $i == 0) {
 294              $row->cells[] = $actioncell;
 295          }
 296  
 297          $table->data[] = $row;
 298      }
 299  }
 300  
 301  // Display a list of overrides.
 302  echo $OUTPUT->header();
 303  echo $OUTPUT->heading($title);
 304  
 305  // Output the table and button.
 306  echo html_writer::start_tag('div', ['id' => 'quizoverrides']);
 307  if (count($table->data)) {
 308      echo html_writer::table($table);
 309  } else {
 310      if ($groupmode) {
 311          echo $OUTPUT->notification(get_string('overridesnoneforgroups', 'quiz'), 'info', false);
 312      } else {
 313          echo $OUTPUT->notification(get_string('overridesnoneforusers', 'quiz'), 'info', false);
 314      }
 315  }
 316  if ($hasinactive) {
 317      echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'quiz'), 'info', false);
 318  }
 319  
 320  if ($canedit) {
 321      echo html_writer::start_tag('div', ['class' => 'buttons']);
 322      $options = [];
 323      if ($groupmode) {
 324          if (empty($groups)) {
 325              // There are no groups.
 326              echo $OUTPUT->notification(get_string('groupsnone', 'quiz'), 'error');
 327              $options['disabled'] = true;
 328          }
 329          echo $OUTPUT->single_button($overrideediturl->out(true,
 330                  ['action' => 'addgroup', 'cmid' => $cm->id]),
 331                  get_string('addnewgroupoverride', 'quiz'), 'post', $options);
 332      } else {
 333          $users = [];
 334          // See if there are any students in the quiz.
 335          if ($showallgroups) {
 336              $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id');
 337              $nousermessage = get_string('usersnone', 'quiz');
 338          } else if ($groups) {
 339              $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id', '', '', '', array_keys($groups));
 340              $nousermessage = get_string('usersnone', 'quiz');
 341          } else {
 342              $nousermessage = get_string('groupsnone', 'quiz');
 343          }
 344          $info = new \core_availability\info_module($cm);
 345          $users = $info->filter_user_list($users);
 346  
 347          if (empty($users)) {
 348              // There are no students.
 349              echo $OUTPUT->notification($nousermessage, 'error');
 350              $options['disabled'] = true;
 351          }
 352          echo $OUTPUT->single_button($overrideediturl->out(true,
 353                  ['action' => 'adduser', 'cmid' => $cm->id]),
 354                  get_string('addnewuseroverride', 'quiz'), 'get', $options);
 355      }
 356      echo html_writer::end_tag('div');
 357  }
 358  
 359  echo html_writer::end_tag('div');
 360  
 361  // Finish the page.
 362  echo $OUTPUT->footer();