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]

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