Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
/admin/ -> qtypes.php (source)

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

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Allows the admin to manage question types.
  20   *
  21   * @package    moodlecore
  22   * @subpackage questionbank
  23   * @copyright  2008 Tim Hunt
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  use core_question\local\bank\question_version_status;
  28  
  29  require_once(__DIR__ . '/../config.php');
  30  require_once($CFG->libdir . '/questionlib.php');
  31  require_once($CFG->libdir . '/adminlib.php');
  32  require_once($CFG->libdir . '/tablelib.php');
  33  
  34  admin_externalpage_setup('manageqtypes');
  35  
  36  $systemcontext = context_system::instance();
  37  require_capability('moodle/question:config', $systemcontext);
  38  $canviewreports = has_capability('report/questioninstances:view', $systemcontext);
  39  
  40  $thispageurl = new moodle_url('/admin/qtypes.php');
  41  
  42  $qtypes = question_bank::get_all_qtypes();
  43  $pluginmanager = core_plugin_manager::instance();
  44  
  45  // Get some data we will need - question counts and which types are needed.
  46  // The second JOIN on question_versions (qv2) is to get the latest version of each question. 
  47  // (Using this sort of JOIN is a known trick for doing this in the fastest possible way.)
  48  $counts = $DB->get_records_sql("
  49          SELECT q.qtype,
  50                 COUNT(qv.id) AS numquestions,
  51                 SUM(CASE WHEN qv.status = :hiddenstatus THEN 1 ELSE 0 END) AS numhidden,
  52                 SUM(CASE WHEN qv.status = :draftstatus THEN 1 ELSE 0 END) AS numdraft
  53            FROM {question} q
  54            JOIN {question_versions} qv ON q.id = qv.questionid
  55       LEFT JOIN {question_versions} qv2 ON qv.questionbankentryid = qv2.questionbankentryid AND qv.version < qv2.version
  56           WHERE qv2.questionbankentryid IS NULL
  57        GROUP BY q.qtype
  58      ", [
  59          'hiddenstatus' => question_version_status::QUESTION_STATUS_HIDDEN,
  60          'draftstatus' => question_version_status::QUESTION_STATUS_DRAFT,
  61      ]);
  62  
  63  $needed = [];
  64  foreach ($qtypes as $qtypename => $qtype) {
  65      if (!isset($counts[$qtypename])) {
  66          $counts[$qtypename] = new stdClass;
  67          $counts[$qtypename]->numquestions = 0;
  68          $counts[$qtypename]->numhidden = 0;
  69          $counts[$qtypename]->numdraft = 0;
  70      }
  71      $needed[$qtypename] = $counts[$qtypename]->numquestions > 0 ||
  72              $pluginmanager->other_plugins_that_require($qtype->plugin_name());
  73      $counts[$qtypename]->numquestions -= ($counts[$qtypename]->numhidden + $counts[$qtypename]->numdraft);
  74  }
  75  $needed['missingtype'] = true; // The system needs the missing question type.
  76  foreach ($counts as $qtypename => $count) {
  77      if (!isset($qtypes[$qtypename])) {
  78          $counts['missingtype']->numquestions += $count->numquestions - ($count->numhidden + $count->numdraft);
  79          $counts['missingtype']->numhidden += $count->numhidden;
  80          $counts['missingtype']->numdraft += $count->numdraft;
  81      }
  82  }
  83  
  84  // Work of the correct sort order.
  85  $config = get_config('question');
  86  $sortedqtypes = array();
  87  foreach ($qtypes as $qtypename => $qtype) {
  88      $sortedqtypes[$qtypename] = $qtype->local_name();
  89  }
  90  $sortedqtypes = question_bank::sort_qtype_array($sortedqtypes, $config);
  91  
  92  // Process actions ============================================================
  93  
  94  // Disable.
  95  if (($disable = optional_param('disable', '', PARAM_PLUGIN)) && confirm_sesskey()) {
  96      if (!isset($qtypes[$disable])) {
  97          throw new \moodle_exception('unknownquestiontype', 'question', $thispageurl, $disable);
  98      }
  99  
 100      $class = \core_plugin_manager::resolve_plugininfo_class('qtype');
 101      $class::enable_plugin($disable, false);
 102      redirect($thispageurl);
 103  }
 104  
 105  // Enable.
 106  if (($enable = optional_param('enable', '', PARAM_PLUGIN)) && confirm_sesskey()) {
 107      if (!isset($qtypes[$enable])) {
 108          throw new \moodle_exception('unknownquestiontype', 'question', $thispageurl, $enable);
 109      }
 110  
 111      if (!$qtypes[$enable]->menu_name()) {
 112          throw new \moodle_exception('cannotenable', 'question', $thispageurl, $enable);
 113      }
 114  
 115      $class = \core_plugin_manager::resolve_plugininfo_class('qtype');
 116      $class::enable_plugin($enable, true);
 117      redirect($thispageurl);
 118  }
 119  
 120  // Move up in order.
 121  if (($up = optional_param('up', '', PARAM_PLUGIN)) && confirm_sesskey()) {
 122      if (!isset($qtypes[$up])) {
 123          throw new \moodle_exception('unknownquestiontype', 'question', $thispageurl, $up);
 124      }
 125  
 126      $neworder = question_reorder_qtypes($sortedqtypes, $up, -1);
 127      question_save_qtype_order($neworder, $config);
 128      redirect($thispageurl);
 129  }
 130  
 131  // Move down in order.
 132  if (($down = optional_param('down', '', PARAM_PLUGIN)) && confirm_sesskey()) {
 133      if (!isset($qtypes[$down])) {
 134          throw new \moodle_exception('unknownquestiontype', 'question', $thispageurl, $down);
 135      }
 136  
 137      $neworder = question_reorder_qtypes($sortedqtypes, $down, +1);
 138      question_save_qtype_order($neworder, $config);
 139      redirect($thispageurl);
 140  }
 141  
 142  // End of process actions ==================================================
 143  
 144  // Print the page heading.
 145  echo $OUTPUT->header();
 146  echo $OUTPUT->heading(get_string('manageqtypes', 'admin'));
 147  
 148  // Set up the table.
 149  $table = new flexible_table('qtypeadmintable');
 150  $table->define_baseurl($thispageurl);
 151  $table->define_columns(array('questiontype', 'numquestions', 'version', 'requires',
 152          'availableto', 'uninstall', 'settings'));
 153  $table->define_headers(array(get_string('questiontype', 'question'), get_string('numquestions', 'question'),
 154          get_string('version'), get_string('requires', 'admin'), get_string('availableq', 'question'),
 155          get_string('settings'), get_string('uninstallplugin', 'core_admin')));
 156  $table->set_attribute('id', 'qtypes');
 157  $table->set_attribute('class', 'admintable generaltable');
 158  $table->setup();
 159  
 160  // Add a row for each question type.
 161  $createabletypes = question_bank::get_creatable_qtypes();
 162  foreach ($sortedqtypes as $qtypename => $localname) {
 163      $qtype = $qtypes[$qtypename];
 164      $row = array();
 165  
 166      // Question icon and name.
 167      $fakequestion = new stdClass;
 168      $fakequestion->qtype = $qtypename;
 169      $icon = print_question_icon($fakequestion, true);
 170      $row[] = $icon . ' ' . $localname;
 171  
 172      // Number of questions of this type.
 173      if ($counts[$qtypename]->numquestions + $counts[$qtypename]->numhidden + $counts[$qtypename]->numdraft > 0) {
 174          if ($counts[$qtypename]->numhidden + $counts[$qtypename]->numdraft > 0) {
 175              $strcount = get_string('numquestionsandhidden', 'question', $counts[$qtypename]);
 176          } else {
 177              $strcount = $counts[$qtypename]->numquestions;
 178          }
 179          if ($canviewreports) {
 180              $row[] = html_writer::link(new moodle_url('/report/questioninstances/index.php',
 181                      array('qtype' => $qtypename)), $strcount, array('title' => get_string('showdetails', 'admin')));
 182          } else {
 183              $strcount;
 184          }
 185      } else {
 186          $row[] = 0;
 187      }
 188  
 189      // Question version number.
 190      $version = get_config('qtype_' . $qtypename, 'version');
 191      if ($version) {
 192          $row[] = $version;
 193      } else {
 194          $row[] = html_writer::tag('span', get_string('nodatabase', 'admin'), array('class' => 'text-muted'));
 195      }
 196  
 197      // Other question types required by this one.
 198      $plugin = $pluginmanager->get_plugin_info($qtype->plugin_name());
 199      $requiredtypes = $plugin->get_other_required_plugins();
 200      $strtypes = array();
 201      if (!empty($requiredtypes)) {
 202          foreach ($requiredtypes as $required => $notused) {
 203              $strtypes[] = $pluginmanager->plugin_name($required);
 204          }
 205          $row[] = implode(', ', $strtypes);
 206      } else {
 207          $row[] = '';
 208      }
 209  
 210      // Are people allowed to create new questions of this type?
 211      $rowclass = '';
 212      if ($qtype->menu_name()) {
 213          $createable = isset($createabletypes[$qtypename]);
 214          $icons = question_types_enable_disable_icons($qtypename, $createable);
 215          if (!$createable) {
 216              $rowclass = 'dimmed_text';
 217          }
 218      } else {
 219          $icons = $OUTPUT->spacer();
 220      }
 221  
 222      // Move icons.
 223      $icons .= question_type_icon_html('up', $qtypename, 't/up', get_string('up'), '');
 224      $icons .= question_type_icon_html('down', $qtypename, 't/down', get_string('down'), '');
 225      $row[] = $icons;
 226  
 227      // Settings link, if available.
 228      $settings = admin_get_root()->locate('qtypesetting' . $qtypename);
 229      if ($settings instanceof admin_externalpage) {
 230          $row[] = html_writer::link($settings->url, get_string('settings'));
 231      } else if ($settings instanceof admin_settingpage) {
 232          $row[] = html_writer::link(new moodle_url('/admin/settings.php',
 233                  array('section' => 'qtypesetting' . $qtypename)), get_string('settings'));
 234      } else {
 235          $row[] = '';
 236      }
 237  
 238      // Uninstall link, if available.
 239      if ($needed[$qtypename]) {
 240          $row[] = '';
 241      } else {
 242          $uninstallurl = core_plugin_manager::instance()->get_uninstall_url('qtype_'.$qtypename, 'manage');
 243          if ($uninstallurl) {
 244              $row[] = html_writer::link($uninstallurl, get_string('uninstallplugin', 'core_admin'),
 245                  array('title' => get_string('uninstallqtype', 'question')));
 246          }
 247      }
 248  
 249      $table->add_data($row, $rowclass);
 250  }
 251  
 252  $table->finish_output();
 253  
 254  echo $OUTPUT->footer();
 255  
 256  function question_types_enable_disable_icons($qtypename, $createable) {
 257      if ($createable) {
 258          return question_type_icon_html('disable', $qtypename, 't/hide',
 259                  get_string('enabled', 'question'), get_string('disable'));
 260      } else {
 261          return question_type_icon_html('enable', $qtypename, 't/show',
 262                  get_string('disabled', 'question'), get_string('enable'));
 263      }
 264  }
 265  
 266  function question_type_icon_html($action, $qtypename, $icon, $alt, $tip) {
 267      global $OUTPUT;
 268      return $OUTPUT->action_icon(new moodle_url('/admin/qtypes.php',
 269              array($action => $qtypename, 'sesskey' => sesskey())),
 270              new pix_icon($icon, $alt, 'moodle', array('title' => '', 'class' => 'iconsmall')),
 271              null, array('title' => $tip));
 272  }