MDL-20636 Remove all references to global $QTYPES.
[moodle.git] / question / type / random / questiontype.php
CommitLineData
aeb15530 1<?php
f9b0500f
TH
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
1976496e 18/**
f9b0500f 19 * Question type class for the random question type.
f34488b2 20 *
7764183a
TH
21 * @package qtype
22 * @subpackage random
23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
f9b0500f
TH
25 */
26
27
a17b297d
TH
28defined('MOODLE_INTERNAL') || die();
29
30
f9b0500f
TH
31/**
32 * The random question type.
33 *
34 * This question type does not have a question definition class, nor any
35 * renderers. When you load a question of this type, it actually loads a
36 * question chosen randomly from a particular category in the question bank.
f34488b2 37 *
7764183a
TH
38 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
53a4d39f 40 */
f9b0500f
TH
41class qtype_random extends question_type {
42 /** @var string comma-separated list of qytpe names not to select, can be used in SQL. */
f24493ec 43 protected $excludedqtypes = null;
516cf3eb 44
f9b0500f
TH
45 /** @var string comma-separated list of manually graded qytpe names, can be used in SQL. */
46 protected $manualqtypes = null;
516cf3eb 47
f9b0500f
TH
48 /**
49 * Cache of availabe question ids from a particular category.
50 * @var array two-dimensional array. The first key is a category id, the
51 * second key is wether subcategories should be included.
52 */
53 private $availablequestionsbycategory = array();
516cf3eb 54
f9b0500f 55 public function menu_name() {
8b192edb 56 // Don't include this question type in the 'add new question' menu.
a2156789 57 return false;
58 }
59
f9b0500f 60 public function is_manual_graded() {
f24493ec 61 return true;
62 }
63
f9b0500f
TH
64 public function is_usable_by_random() {
65 return false;
f24493ec 66 }
67
f9b0500f 68 public function is_question_manual_graded($question, $otherquestionsinuse) {
f24493ec 69 global $DB;
70 // We take our best shot at working whether a particular question is manually
71 // graded follows: We look to see if any of the questions that this random
72 // question might select if of a manually graded type. If a category contains
73 // a mixture of manual and non-manual questions, and if all the attempts so
74 // far selected non-manual ones, this will give the wrong answer, but we
75 // don't care. Even so, this is an expensive calculation!
76 $this->init_qtype_lists();
77 if (!$this->manualqtypes) {
78 return false;
79 }
80 if ($question->questiontext) {
81 $categorylist = question_categorylist($question->category);
82 } else {
83 $categorylist = $question->category;
84 }
85 return $DB->record_exists_select('question',
86 "category IN ($categorylist)
87 AND parent = 0
88 AND hidden = 0
89 AND id NOT IN ($otherquestionsinuse)
90 AND qtype IN ($this->manualqtypes)");
91 }
92
f24493ec 93 /**
94 * This method needs to be called before the ->excludedqtypes and
95 * ->manualqtypes fields can be used.
96 */
f9b0500f
TH
97 protected function init_qtype_lists() {
98 if (!is_null($this->excludedqtypes)) {
99 return; // Already done.
100 }
101 $excludedqtypes = array();
102 $manualqtypes = array();
103 foreach (question_bank::get_all_qtypes() as $qtype) {
104 $quotedname = "'" . $qtype->name() . "'";
105 if (!$qtype->is_usable_by_random()) {
106 $excludedqtypes[] = $quotedname;
107 } else if ($qtype->is_manual_graded()) {
108 $manualqtypes[] = $quotedname;
f24493ec 109 }
f24493ec 110 }
f9b0500f
TH
111 $this->excludedqtypes = implode(',', $excludedqtypes);
112 $this->manualqtypes = implode(',', $manualqtypes);
f24493ec 113 }
114
bcc234b0 115 function display_question_editing_page(&$mform, $question, $wizardnow){
fe6ce234 116 global $OUTPUT;
4995b9c1 117 $heading = $this->get_heading(empty($question->id));
4bcc5118 118 echo $OUTPUT->heading_with_help($heading, $this->name(), $this->plugin_name());
bcc234b0 119 $mform->display();
120 }
121
f9b0500f 122 public function get_question_options($question) {
516cf3eb 123 return true;
124 }
125
f59dba84 126 /**
127 * Random questions always get a question name that is Random (cateogryname).
128 * This function is a centralised place to calculate that, given the category.
3cac440b 129 * @param object $category the category this question picks from. (Only $category->name is used.)
f7970e3c 130 * @param bool $includesubcategories whether this question also picks from subcategories.
3cac440b 131 * @return string the name this question should have.
f59dba84 132 */
f9b0500f 133 public function question_name($category, $includesubcategories) {
3cac440b 134 if ($includesubcategories) {
135 $string = 'randomqplusname';
136 } else {
137 $string = 'randomqname';
138 }
139 return get_string($string, 'qtype_random', $category->name);
f59dba84 140 }
141
f9b0500f 142 protected function set_selected_question_name($question, $randomname) {
0ff4bd08 143 $a = new stdClass();
f9b0500f
TH
144 $a->randomname = $randomname;
145 $a->questionname = $question->name;
146 $question->name = get_string('selectedby', 'qtype_random', $a);
5348b899 147 }
148
a13d4fbd 149 public function save_question($question, $form) {
f9b0500f
TH
150 $form->name = '';
151 // Name is not a required field for random questions, but
152 // parent::save_question Assumes that it is.
a13d4fbd 153 return parent::save_question($question, $form);
f9b0500f 154 }
24e8b9b6 155
f9b0500f 156 public function save_question_options($question) {
a13d4fbd
TH
157 global $DB;
158
24e8b9b6 159 // No options, as such, but we set the parent field to the question's
160 // own id. Setting the parent field has the effect of hiding this
161 // question in various places.
0ff4bd08 162 $updateobject = new stdClass();
24e8b9b6 163 $updateobject->id = $question->id;
164 $updateobject->parent = $question->id;
165
166 // We also force the question name to be 'Random (categoryname)'.
f9b0500f 167 $category = $DB->get_record('question_categories', array('id' => $question->category), '*', MUST_EXIST);
3cac440b 168 $updateobject->name = $this->question_name($category, !empty($question->questiontext));
24e8b9b6 169 return $DB->update_record('question', $updateobject);
516cf3eb 170 }
171
339ef4c2 172 /**
173 * Get all the usable questions from a particular question category.
174 *
f7970e3c
TH
175 * @param int $categoryid the id of a question category.
176 * @param bool whether to include questions from subcategories.
339ef4c2 177 * @param string $questionsinuse comma-separated list of question ids to exclude from consideration.
178 * @return array of question records.
179 */
f9b0500f
TH
180 public function get_available_questions_from_category($categoryid, $subcategories) {
181 if (isset($this->availablequestionsbycategory[$categoryid][$subcategories])) {
182 return $this->availablequestionsbycategory[$categoryid][$subcategories];
183 }
184
f24493ec 185 $this->init_qtype_lists();
339ef4c2 186 if ($subcategories) {
f9b0500f 187 $categoryids = question_categorylist($categoryid);
339ef4c2 188 } else {
f9b0500f 189 $categoryids = $categoryid;
516cf3eb 190 }
191
f9b0500f
TH
192 $questionids = question_bank::get_finder()->get_questions_from_categories(
193 $categoryids, 'qtype NOT IN (' . $this->excludedqtypes . ')');
194 $this->availablequestionsbycategory[$categoryid][$subcategories] = $questionids;
195 return $questionids;
516cf3eb 196 }
197
f9b0500f
TH
198 public function make_question($questiondata) {
199 return $this->choose_other_question($questiondata, array());
516cf3eb 200 }
201
f9b0500f
TH
202 /**
203 * Load the definition of another question picked randomly by this question.
204 * @param object $questiondata the data defining a random question.
205 * @param array $excludedquestions of question ids. We will no pick any question whose id is in this list.
f7970e3c 206 * @param bool $allowshuffle if false, then any shuffle option on the selected quetsion is disabled.
f9b0500f
TH
207 * @return question_definition|null the definition of the question that was
208 * selected, or null if no suitable question could be found.
209 */
210 public function choose_other_question($questiondata, $excludedquestions, $allowshuffle = true) {
211 $available = $this->get_available_questions_from_category($questiondata->category,
212 !empty($questiondata->questiontext));
213 shuffle($available);
214
215 foreach ($available as $questionid) {
216 if (in_array($questionid, $excludedquestions)) {
217 continue;
218 }
516cf3eb 219
f9b0500f
TH
220 $question = question_bank::load_question($questionid, $allowshuffle);
221 $this->set_selected_question_name($question, $questiondata->name);
222 return $question;
516cf3eb 223 }
f9b0500f 224 return null;
516cf3eb 225 }
226
f9b0500f
TH
227 function get_random_guess_score($questiondata) {
228 return null;
516cf3eb 229 }
516cf3eb 230}