e98e5948e770ac3c23a95245b36d7648f7b2bb02
[moodle.git] / mod / quiz / editlib.php
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/>.
18 /**
19  * This contains functions that are called from within the quiz module only
20  * Functions that are also called by core Moodle are in {@link lib.php}
21  * This script also loads the code in {@link questionlib.php} which holds
22  * the module-indpendent code for handling questions and which in turn
23  * initialises all the questiontype classes.
24  *
25  * @package    mod_quiz
26  * @copyright  1999 onwards Martin Dougiamas and others {@link http://moodle.com}
27  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28  */
31 defined('MOODLE_INTERNAL') || die();
33 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
35 define('NUM_QS_TO_SHOW_IN_RANDOM', 3);
37 /**
38  * Verify that the question exists, and the user has permission to use it.
39  * Does not return. Throws an exception if the question cannot be used.
40  * @param int $questionid The id of the question.
41  */
42 function quiz_require_question_use($questionid) {
43     global $DB;
44     $question = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
45     question_require_capability_on($question, 'use');
46 }
48 /**
49  * Verify that the question exists, and the user has permission to use it.
50  * @param object $quiz the quiz settings.
51  * @param int $slot which question in the quiz to test.
52  * @return bool whether the user can use this question.
53  */
54 function quiz_has_question_use($quiz, $slot) {
55     global $DB;
56     $question = $DB->get_record_sql("
57             SELECT q.*
58               FROM {quiz_slots} slot
59               JOIN {question} q ON q.id = slot.questionid
60              WHERE slot.quizid = ? AND slot.slot = ?", array($quiz->id, $slot));
61     if (!$question) {
62         return false;
63     }
64     return question_has_capability_on($question, 'use');
65 }
67 /**
68  * Remove a question from a quiz
69  * @param object $quiz the quiz object.
70  * @param int $questionid The id of the question to be deleted.
71  */
72 function quiz_remove_slot($quiz, $slotnumber) {
73     global $DB;
75     $slot = $DB->get_record('quiz_slots', array('quizid' => $quiz->id, 'slot' => $slotnumber));
76     $maxslot = $DB->get_field_sql('SELECT MAX(slot) FROM {quiz_slots} WHERE quizid = ?', array($quiz->id));
77     if (!$slot) {
78         return;
79     }
81     $trans = $DB->start_delegated_transaction();
82     $DB->delete_records('quiz_slots', array('id' => $slot->id));
83     for ($i = $slot->slot + 1; $i <= $maxslot; $i++) {
84         $DB->set_field('quiz_slots', 'slot', $i - 1,
85                 array('quizid' => $quiz->id, 'slot' => $i));
86     }
88     $qtype = $DB->get_field('question', 'qtype', array('id' => $slot->questionid));
89     if ($qtype === 'random') {
90         // This function automatically checks if the question is in use, and won't delete if it is.
91         question_delete_question($slot->questionid);
92     }
94     $trans->allow_commit();
95 }
97 /**
98  * Remove an empty page from the quiz layout. If that is not possible, do nothing.
99  * @param object $quiz the quiz settings.
100  * @param int $pagenumber the page number to delete.
101  */
102 function quiz_delete_empty_page($quiz, $pagenumber) {
103     global $DB;
105     if ($DB->record_exists('quiz_slots', array('quizid' => $quiz->id, 'page' => $pagenumber))) {
106         // This was not an empty page.
107         return;
108     }
110     $DB->execute('UPDATE {quiz_slots} SET page = page - 1 WHERE quizid = ? AND page > ?',
111             array($quiz->id, $pagenumber));
114 /**
115  * Add a question to a quiz
116  *
117  * Adds a question to a quiz by updating $quiz as well as the
118  * quiz and quiz_slots tables. It also adds a page break if required.
119  * @param int $questionid The id of the question to be added
120  * @param object $quiz The extended quiz object as used by edit.php
121  *      This is updated by this function
122  * @param int $page Which page in quiz to add the question on. If 0 (default),
123  *      add at the end
124  * @param float $maxmark The maximum mark to set for this question. (Optional,
125  *      defaults to question.defaultmark.
126  * @return bool false if the question was already in the quiz
127  */
128 function quiz_add_quiz_question($questionid, $quiz, $page = 0, $maxmark = null) {
129     global $DB;
130     $slots = $DB->get_records('quiz_slots', array('quizid' => $quiz->id),
131             'slot', 'questionid, slot, page, id');
132     if (array_key_exists($questionid, $slots)) {
133         return false;
134     }
136     $trans = $DB->start_delegated_transaction();
138     $maxpage = 1;
139     $numonlastpage = 0;
140     foreach ($slots as $slot) {
141         if ($slot->page > $maxpage) {
142             $maxpage = $slot->page;
143             $numonlastpage = 1;
144         } else {
145             $numonlastpage += 1;
146         }
147     }
149     // Add the new question instance.
150     $slot = new stdClass();
151     $slot->quizid = $quiz->id;
152     $slot->questionid = $questionid;
154     if ($maxmark !== null) {
155         $slot->maxmark = $maxmark;
156     } else {
157         $slot->maxmark = $DB->get_field('question', 'defaultmark', array('id' => $questionid));
158     }
160     if (is_int($page) && $page >= 1) {
161         // Adding on a given page.
162         $lastslotbefore = 0;
163         foreach (array_reverse($slots) as $otherslot) {
164             if ($otherslot->page > $page) {
165                 $DB->set_field('quiz_slots', 'slot', $otherslot->slot + 1, array('id' => $otherslot->id));
166             } else {
167                 $lastslotbefore = $otherslot->slot;
168                 break;
169             }
170         }
171         $slot->slot = $lastslotbefore + 1;
172         $slot->page = min($page, $maxpage + 1);
174     } else {
175         $lastslot = end($slots);
176         if ($lastslot) {
177             $slot->slot = $lastslot->slot + 1;
178         } else {
179             $slot->slot = 1;
180         }
181         if ($quiz->questionsperpage && $numonlastpage >= $quiz->questionsperpage) {
182             $slot->page = $maxpage + 1;
183         } else {
184             $slot->page = $maxpage;
185         }
186     }
188     $DB->insert_record('quiz_slots', $slot);
189     $trans->allow_commit();
192 /**
193  * Add a random question to the quiz at a given point.
194  * @param object $quiz the quiz settings.
195  * @param int $addonpage the page on which to add the question.
196  * @param int $categoryid the question category to add the question from.
197  * @param int $number the number of random questions to add.
198  * @param bool $includesubcategories whether to include questoins from subcategories.
199  */
200 function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number,
201         $includesubcategories) {
202     global $DB;
204     $category = $DB->get_record('question_categories', array('id' => $categoryid));
205     if (!$category) {
206         print_error('invalidcategoryid', 'error');
207     }
209     $catcontext = context::instance_by_id($category->contextid);
210     require_capability('moodle/question:useall', $catcontext);
212     // Find existing random questions in this category that are
213     // not used by any quiz.
214     if ($existingquestions = $DB->get_records_sql(
215             "SELECT q.id, q.qtype FROM {question} q
216             WHERE qtype = 'random'
217                 AND category = ?
218                 AND " . $DB->sql_compare_text('questiontext') . " = ?
219                 AND NOT EXISTS (
220                         SELECT *
221                           FROM {quiz_slots}
222                          WHERE questionid = q.id)
223             ORDER BY id", array($category->id, ($includesubcategories ? '1' : '0')))) {
224         // Take as many of these as needed.
225         while (($existingquestion = array_shift($existingquestions)) && $number > 0) {
226             quiz_add_quiz_question($existingquestion->id, $quiz, $addonpage);
227             $number -= 1;
228         }
229     }
231     if ($number <= 0) {
232         return;
233     }
235     // More random questions are needed, create them.
236     for ($i = 0; $i < $number; $i += 1) {
237         $form = new stdClass();
238         $form->questiontext = array('text' => ($includesubcategories ? '1' : '0'), 'format' => 0);
239         $form->category = $category->id . ',' . $category->contextid;
240         $form->defaultmark = 1;
241         $form->hidden = 1;
242         $form->stamp = make_unique_id_code(); // Set the unique code (not to be changed).
243         $question = new stdClass();
244         $question->qtype = 'random';
245         $question = question_bank::get_qtype('random')->save_question($question, $form);
246         if (!isset($question->id)) {
247             print_error('cannotinsertrandomquestion', 'quiz');
248         }
249         quiz_add_quiz_question($question->id, $quiz, $addonpage);
250     }
253 /**
254  * Add a page break after a particular slot.
255  * @param object $quiz the quiz settings.
256  * @param int $slot the slot to add the page break after.
257  */
258 function quiz_add_page_break_after_slot($quiz, $slot) {
259     global $DB;
261     $DB->execute('UPDATE {quiz_slots} SET page = page + 1 WHERE quizid = ? AND slot > ?',
262             array($quiz->id, $slot));
265 /**
266  * Change the max mark for a slot.
267  *
268  * Saves changes to the question grades in the quiz_slots table and any
269  * corresponding question_attempts.
270  * It does not update 'sumgrades' in the quiz table.
271  *
272  * @param stdClass $slot    row from the quiz_slots table.
273  * @param float    $maxmark the new maxmark.
274  */
275 function quiz_update_slot_maxmark($slot, $maxmark) {
276     global $DB;
278     if (abs($maxmark - $slot->maxmark) < 1e-7) {
279         // Grade has not changed. Nothing to do.
280         return;
281     }
283     $slot->maxmark = $maxmark;
284     $DB->update_record('quiz_slots', $slot);
285     question_engine::set_max_mark_in_attempts(new qubaids_for_quiz($slot->quizid),
286             $slot->slot, $maxmark);
289 /**
290  * Private function used by the following two.
291  * @param object $quiz the quiz settings.
292  * @param int $slotnumber the slot to move up.
293  * @param int $shift +1 means move down, -1 means move up.
294  */
295 function _quiz_move_question($quiz, $slotnumber, $shift) {
296     global $DB;
298     if (!$slotnumber || !($shift == 1 || $shift == -1)) {
299         return;
300     }
302     $slot = $DB->get_record('quiz_slots',
303             array('quizid' => $quiz->id, 'slot' => $slotnumber));
304     if (!$slot) {
305         return;
306     }
308     $otherslot = $DB->get_record('quiz_slots',
309             array('quizid' => $quiz->id, 'slot' => $slotnumber + $shift));
310     if (!$otherslot) {
311         // Must be first or last question being moved further that way if we can.
312         if ($shift + $slot->page > 0) {
313             $DB->set_field('quiz_slots', 'page', $slot->page + $shift, array('id' => $slot->id));
314         }
315         return;
316     }
318     if ($otherslot->page != $slot->page) {
319         $DB->set_field('quiz_slots', 'page', $slot->page + $shift, array('id' => $slot->id));
320         return;
321     }
323     $trans = $DB->start_delegated_transaction();
324     $DB->set_field('quiz_slots', 'slot', -1,               array('id' => $slot->id));
325     $DB->set_field('quiz_slots', 'slot', $slot->slot,      array('id' => $otherslot->id));
326     $DB->set_field('quiz_slots', 'slot', $otherslot->slot, array('id' => $slot->id));
327     $trans->allow_commit();
330 /**
331  * Move a particular question one space earlier in the quiz.
332  * If that is not possible, do nothing.
333  * @param object $quiz the quiz settings.
334  * @param int $slot the slot to move up.
335  */
336 function quiz_move_question_up($quiz, $slot) {
337     _quiz_move_question($quiz, $slot, -1);
340 /**
341  * Move a particular question one space later in the quiz.
342  * If that is not possible, do nothing.
343  * @param object $quiz the quiz settings.
344  * @param int $slot the slot to move down.
345  */
346 function quiz_move_question_down($quiz, $slot) {
347     return _quiz_move_question($quiz, $slot, 1);
350 /**
351  * Prints a list of quiz questions for the edit.php main view for edit
352  * ($reordertool = false) and order and paging ($reordertool = true) tabs
353  *
354  * @param object $quiz The quiz settings.
355  * @param moodle_url $pageurl The url of the current page with the parameters required
356  *     for links returning to the current page, as a moodle_url object
357  * @param bool $allowdelete Indicates whether the delete icons should be displayed
358  * @param bool $reordertool  Indicates whether the reorder tool should be displayed
359  * @param bool $quiz_qbanktool  Indicates whether the question bank should be displayed
360  * @param bool $hasattempts  Indicates whether the quiz has attempts
361  * @param object $defaultcategoryobj
362  * @param bool $canaddquestion is the user able to add and use questions anywere?
363  * @param bool $canaddrandom is the user able to add random questions anywere?
364  */
365 function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
366         $quiz_qbanktool, $hasattempts, $defaultcategoryobj, $canaddquestion, $canaddrandom) {
367     global $CFG, $DB, $OUTPUT;
368     $strorder = get_string('order');
369     $strquestionname = get_string('questionname', 'quiz');
370     $strmaxmark = get_string('markedoutof', 'question');
371     $strremove = get_string('remove', 'quiz');
372     $stredit = get_string('edit');
373     $strview = get_string('view');
374     $straction = get_string('action');
375     $strmove = get_string('move');
376     $strmoveup = get_string('moveup');
377     $strmovedown = get_string('movedown');
378     $strsave = get_string('save', 'quiz');
379     $strreorderquestions = get_string('reorderquestions', 'quiz');
381     $strselectall = get_string('selectall', 'quiz');
382     $strselectnone = get_string('selectnone', 'quiz');
383     $strtype = get_string('type', 'quiz');
384     $strpreview = get_string('preview', 'quiz');
386     $questions = $DB->get_records_sql("SELECT slot.slot, q.*, qc.contextid, slot.page, slot.maxmark
387                           FROM {quiz_slots} slot
388                      LEFT JOIN {question} q ON q.id = slot.questionid
389                      LEFT JOIN {question_categories} qc ON qc.id = q.category
390                          WHERE slot.quizid = ?
391                       ORDER BY slot.slot", array($quiz->id));
393     $lastindex = count($questions) - 1;
395     $disabled = '';
396     $pagingdisabled = '';
397     if ($hasattempts) {
398         $disabled = 'disabled="disabled"';
399     }
400     if ($hasattempts || $quiz->shufflequestions) {
401         $pagingdisabled = 'disabled="disabled"';
402     }
404     $reordercontrolssetdefaultsubmit = '<div style="display:none;">' .
405         '<input type="submit" name="savechanges" value="' .
406         $strreorderquestions . '" ' . $pagingdisabled . ' /></div>';
407     $reordercontrols1 = '<div class="addnewpagesafterselected">' .
408         '<input type="submit" name="addnewpagesafterselected" value="' .
409         get_string('addnewpagesafterselected', 'quiz') . '"  ' .
410         $pagingdisabled . ' /></div>';
411     $reordercontrols1 .= '<div class="quizdeleteselected">' .
412         '<input type="submit" name="quizdeleteselected" ' .
413         'onclick="return confirm(\'' .
414         get_string('areyousureremoveselected', 'quiz') . '\');" value="' .
415         get_string('removeselected', 'quiz') . '"  ' . $disabled . ' /></div>';
417     $a = '<input name="moveselectedonpagetop" type="text" size="2" ' .
418         $pagingdisabled . ' />';
419     $b = '<input name="moveselectedonpagebottom" type="text" size="2" ' .
420         $pagingdisabled . ' />';
422     $reordercontrols2top = '<div class="moveselectedonpage">' .
423         '<label>' . get_string('moveselectedonpage', 'quiz', $a) . '</label>' .
424         '<input type="submit" name="savechanges" value="' .
425         $strmove . '"  ' . $pagingdisabled . ' />' . '
426         <br /><input type="submit" name="savechanges" value="' .
427         $strreorderquestions . '" /></div>';
428     $reordercontrols2bottom = '<div class="moveselectedonpage">' .
429         '<input type="submit" name="savechanges" value="' .
430         $strreorderquestions . '" /><br />' .
431         '<label>' . get_string('moveselectedonpage', 'quiz', $b) . '</label>' .
432         '<input type="submit" name="savechanges" value="' .
433         $strmove . '"  ' . $pagingdisabled . ' /> ' . '</div>';
435     $reordercontrols3 = '<a href="javascript:select_all_in(\'FORM\', null, ' .
436             '\'quizquestions\');">' .
437             $strselectall . '</a> /';
438     $reordercontrols3.=    ' <a href="javascript:deselect_all_in(\'FORM\', ' .
439             'null, \'quizquestions\');">' .
440             $strselectnone . '</a>';
442     $reordercontrolstop = '<div class="reordercontrols">' .
443             $reordercontrolssetdefaultsubmit .
444             $reordercontrols1 . $reordercontrols2top . $reordercontrols3 . "</div>";
445     $reordercontrolsbottom = '<div class="reordercontrols">' .
446             $reordercontrolssetdefaultsubmit .
447             $reordercontrols2bottom . $reordercontrols1 . $reordercontrols3 . "</div>";
449     if ($reordertool) {
450         echo '<form method="post" action="edit.php" id="quizquestions"><div>';
452         echo html_writer::input_hidden_params($pageurl);
453         echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
455         echo $reordercontrolstop;
456     }
458     // Build fake order for backwards compatibility.
459     $currentpage = 1;
460     $order = array();
461     foreach ($questions as $question) {
462         while ($question->page > $currentpage) {
463             $currentpage += 1;
464             $order[] = 0;
465         }
466         $order[] = $question->slot;
467     }
468     $order[] = 0;
470     // The current question ordinal (no descriptions).
471     $qno = 1;
472     // The current question (includes questions and descriptions).
473     $questioncount = 0;
474     // The current page number in iteration.
475     $pagecount = 0;
477     $pageopen = false;
478     $lastslot = 0;
480     $returnurl = $pageurl->out_as_local_url(false);
481     $questiontotalcount = count($order);
483     $lastquestion = new stdClass();
484     $lastquestion->slot = 0; // Used to get the add page here buttons right.
485     foreach ($order as $count => $qnum) { // Note: $qnum is acutally slot number, if it is not 0.
487         $reordercheckbox = '';
488         $reordercheckboxlabel = '';
489         $reordercheckboxlabelclose = '';
491         // If the questiontype is missing change the question type.
492         if ($qnum && $questions[$qnum]->qtype === null) {
493             $questions[$qnum]->id = $qnum;
494             $questions[$qnum]->category = 0;
495             $questions[$qnum]->qtype = 'missingtype';
496             $questions[$qnum]->name = get_string('missingquestion', 'quiz');
497             $questions[$qnum]->questiontext = ' ';
498             $questions[$qnum]->questiontextformat = FORMAT_HTML;
499             $questions[$qnum]->length = 1;
501         } else if ($qnum && !question_bank::qtype_exists($questions[$qnum]->qtype)) {
502             $questions[$qnum]->qtype = 'missingtype';
503         }
505         if ($qnum != 0 || ($qnum == 0 && !$pageopen)) {
506             // This is either a question or a page break after another (no page is currently open).
507             if (!$pageopen) {
508                 // If no page is open, start display of a page.
509                 $pagecount++;
510                 echo  '<div class="quizpage"><span class="pagetitle">' .
511                         get_string('page') . '&nbsp;' . $pagecount .
512                         '</span><div class="pagecontent">';
513                 $pageopen = true;
514             }
515             if ($qnum == 0) {
516                 // This is the second successive page break. Tell the user the page is empty.
517                 echo '<div class="pagestatus">';
518                 print_string('noquestionsonpage', 'quiz');
519                 echo '</div>';
520                 if ($allowdelete) {
521                     echo '<div class="quizpagedelete">';
522                     echo $OUTPUT->action_icon($pageurl->out(true,
523                             array('deleteemptypage' => $pagecount, 'sesskey' => sesskey())),
524                             new pix_icon('t/delete', $strremove),
525                             new component_action('click',
526                                     'M.core_scroll_manager.save_scroll_action'),
527                             array('title' => $strremove));
528                     echo '</div>';
529                 }
530             }
532             if ($qnum != 0) {
533                 $question = $questions[$qnum];
534                 $questionparams = array(
535                         'returnurl' => $returnurl,
536                         'cmid' => $quiz->cmid,
537                         'id' => $question->id);
538                 $questionurl = new moodle_url('/question/question.php',
539                         $questionparams);
540                 $questioncount++;
542                 // This is an actual question.
543                 ?>
544 <div class="question">
545     <div class="questioncontainer <?php echo $question->qtype; ?>">
546         <div class="qnum">
547                 <?php
548                 $reordercheckbox = '';
549                 $reordercheckboxlabel = '';
550                 $reordercheckboxlabelclose = '';
551                 if ($reordertool) {
552                     $reordercheckbox = '<input type="checkbox" name="s' . $question->slot .
553                         '" id="s' . $question->slot . '" />';
554                     $reordercheckboxlabel = '<label for="s' . $question->slot . '">';
555                     $reordercheckboxlabelclose = '</label>';
556                 }
557                 if ($question->length == 0) {
558                     $qnodisplay = get_string('infoshort', 'quiz');
559                 } else if ($quiz->shufflequestions) {
560                     $qnodisplay = '?';
561                 } else {
562                     if ($qno > 999 || ($reordertool && $qno > 99)) {
563                         $qnodisplay = html_writer::tag('small', $qno);
564                     } else {
565                         $qnodisplay = $qno;
566                     }
567                     $qno += $question->length;
568                 }
569                 echo $reordercheckboxlabel . $qnodisplay . $reordercheckboxlabelclose .
570                         $reordercheckbox;
572                 ?>
573         </div>
574         <div class="content">
575             <div class="questioncontrols">
576                 <?php
577                 if ($count != 0) {
578                     if (!$hasattempts) {
579                         $upbuttonclass = '';
580                         echo $OUTPUT->action_icon($pageurl->out(true,
581                                 array('up' => $question->slot, 'sesskey' => sesskey())),
582                                 new pix_icon('t/up', $strmoveup),
583                                 new component_action('click',
584                                         'M.core_scroll_manager.save_scroll_action'),
585                                 array('title' => $strmoveup));
586                     }
588                 }
589                 if (!$hasattempts) {
590                     echo $OUTPUT->action_icon($pageurl->out(true,
591                             array('down' => $question->slot, 'sesskey' => sesskey())),
592                             new pix_icon('t/down', $strmovedown),
593                             new component_action('click',
594                                     'M.core_scroll_manager.save_scroll_action'),
595                             array('title' => $strmovedown));
596                 }
597                 if ($allowdelete && ($question->qtype == 'missingtype' ||
598                         question_has_capability_on($question, 'use', $question->category))) {
599                     // Remove from quiz, not question delete.
600                     if (!$hasattempts) {
601                         echo $OUTPUT->action_icon($pageurl->out(true,
602                                 array('remove' => $question->slot, 'sesskey' => sesskey())),
603                                 new pix_icon('t/delete', $strremove),
604                                 new component_action('click',
605                                         'M.core_scroll_manager.save_scroll_action'),
606                                 array('title' => $strremove));
607                     }
608                 }
609                 ?>
610             </div><?php
611                 if (!in_array($question->qtype, array('description', 'missingtype')) && !$reordertool) {
612                     ?>
613 <div class="points">
614 <form method="post" action="edit.php" class="quizsavegradesform"><div>
615     <fieldset class="invisiblefieldset" style="display: block;">
616     <label for="<?php echo 'inputq' . $question->slot; ?>"><?php echo $strmaxmark; ?></label>:<br />
617     <input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
618     <?php echo html_writer::input_hidden_params($pageurl); ?>
619     <input type="hidden" name="savechanges" value="save" />
620                     <?php
621                     echo '<input type="text" name="g' . $question->slot .
622                             '" id="inputq' . $question->slot .
623                             '" size="' . ($quiz->decimalpoints + 2) .
624                             '" value="' . (0 + $question->maxmark) .
625                             '" tabindex="' . ($lastindex + $qno) . '" />';
626                     ?>
627         <input type="submit" class="pointssubmitbutton" value="<?php echo $strsave; ?>" />
628     </fieldset>
629                     <?php
630                     if ($question->qtype == 'random') {
631                         echo '<a href="' . $questionurl->out() .
632                                 '" class="configurerandomquestion">' .
633                                 get_string("configurerandomquestion", "quiz") . '</a>';
634                     }
636                     ?>
637 </div>
638 </form>
640             </div>
641                     <?php
642                 } else if ($reordertool) {
643                     if ($qnum) {
644                         ?>
645 <div class="qorder">
646                         <?php
647                         echo '<label class="accesshide" for="o' . $question->slot . '">' .
648                                 get_string('questionposition', 'quiz', $qnodisplay) . '</label>';
649                         echo '<input type="text" name="o' . $question->slot .
650                                 '" id="o' . $question->id . '"' .
651                                 '" size="2" value="' . (10*$count + 10) .
652                                 '" tabindex="' . ($lastindex + $qno) . '" />';
653                         ?>
654 </div>
655                         <?php
656                     }
657                 }
658                 ?>
659             <div class="questioncontentcontainer">
660                 <?php
661                 if ($question->qtype == 'random') { // It is a random question.
662                     if (!$reordertool) {
663                         quiz_print_randomquestion($question, $pageurl, $quiz, $quiz_qbanktool);
664                     } else {
665                         quiz_print_randomquestion_reordertool($question, $pageurl, $quiz);
666                     }
667                 } else { // It is a single question.
668                     if (!$reordertool) {
669                         quiz_print_singlequestion($question, $returnurl, $quiz);
670                     } else {
671                         quiz_print_singlequestion_reordertool($question, $returnurl, $quiz);
672                     }
673                 }
674                 ?>
675             </div>
676         </div>
677     </div>
678 </div>
680                 <?php
681             }
682         }
683         // A page break: end the existing page.
684         if ($qnum == 0) {
685             if ($pageopen) {
686                 if (!$reordertool && !($quiz->shufflequestions &&
687                         $count < $questiontotalcount - 1)) {
688                     quiz_print_pagecontrols($quiz, $pageurl, $pagecount,
689                             $hasattempts, $defaultcategoryobj, $canaddquestion, $canaddrandom);
690                 } else if ($count < $questiontotalcount - 1) {
691                     // Do not include the last page break for reordering
692                     // to avoid creating a new extra page in the end.
693                     echo '<input type="hidden" name="opg' . $pagecount . '" size="2" value="' .
694                             (10*$count + 10) . '" />';
695                 }
696                 echo "</div></div>";
698                 if (!$reordertool && !$quiz->shufflequestions && $count < $questiontotalcount - 1) {
699                     echo $OUTPUT->container_start('addpage');
700                     $url = new moodle_url($pageurl->out_omit_querystring(),
701                             array('cmid' => $quiz->cmid, 'courseid' => $quiz->course,
702                                     'addpage' => $lastquestion->slot, 'sesskey' => sesskey()));
703                     echo $OUTPUT->single_button($url, get_string('addpagehere', 'quiz'), 'post',
704                             array('disabled' => $hasattempts,
705                             'actions' => array(new component_action('click',
706                                     'M.core_scroll_manager.save_scroll_action'))));
707                     echo $OUTPUT->container_end();
708                 }
709                 $pageopen = false;
710                 $count++;
711             }
712         }
714         if ($qnum != 0) {
715             $lastquestion = $question;
716         }
718     }
719     if ($reordertool) {
720         echo $reordercontrolsbottom;
721         echo '</div></form>';
722     }
725 /**
726  * Print all the controls for adding questions directly into the
727  * specific page in the edit tab of edit.php
728  *
729  * @param object $quiz The quiz settings.
730  * @param moodle_url $pageurl The url of the current page with the parameters required
731  *     for links returning to the current page, as a moodle_url object
732  * @param int $page the current page number.
733  * @param bool $hasattempts  Indicates whether the quiz has attempts
734  * @param object $defaultcategoryobj
735  * @param bool $canaddquestion is the user able to add and use questions anywere?
736  * @param bool $canaddrandom is the user able to add random questions anywere?
737  */
738 function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts,
739         $defaultcategoryobj, $canaddquestion, $canaddrandom) {
740     global $CFG, $OUTPUT;
741     static $randombuttoncount = 0;
742     $randombuttoncount++;
743     echo '<div class="pagecontrols">';
745     // Get the current context.
746     $thiscontext = context_course::instance($quiz->course);
747     $contexts = new question_edit_contexts($thiscontext);
749     // Get the default category.
750     list($defaultcategoryid) = explode(',', $pageurl->param('cat'));
751     if (empty($defaultcategoryid)) {
752         $defaultcategoryid = $defaultcategoryobj->id;
753     }
755     if ($canaddquestion) {
756         // Create the url the question page will return to.
757         $returnurladdtoquiz = new moodle_url($pageurl, array('addonpage' => $page));
759         // Print a button linking to the choose question type page.
760         $returnurladdtoquiz = $returnurladdtoquiz->out_as_local_url(false);
761         $newquestionparams = array('returnurl' => $returnurladdtoquiz,
762                 'cmid' => $quiz->cmid, 'appendqnumstring' => 'addquestion');
763         create_new_question_button($defaultcategoryid, $newquestionparams,
764                 get_string('addaquestion', 'quiz'),
765                 get_string('createquestionandadd', 'quiz'), $hasattempts);
766     }
768     if ($hasattempts) {
769         $disabled = 'disabled="disabled"';
770     } else {
771         $disabled = '';
772     }
773     if ($canaddrandom) {
774     ?>
775     <div class="singlebutton">
776         <form class="randomquestionform" action="<?php echo $CFG->wwwroot;
777                 ?>/mod/quiz/addrandom.php" method="get">
778             <div>
779                 <input type="hidden" class="addonpage_formelement" name="addonpage" value="<?php
780                         echo $page; ?>" />
781                 <input type="hidden" name="cmid" value="<?php echo $quiz->cmid; ?>" />
782                 <input type="hidden" name="courseid" value="<?php echo $quiz->course; ?>" />
783                 <input type="hidden" name="category" value="<?php
784                         echo $pageurl->param('cat'); ?>" />
785                 <input type="hidden" name="returnurl" value="<?php
786                         echo s(str_replace($CFG->wwwroot, '', $pageurl->out(false))); ?>" />
787                 <input type="submit" id="addrandomdialoglaunch_<?php
788                         echo $randombuttoncount; ?>" value="<?php
789                         echo get_string('addarandomquestion', 'quiz'); ?>" <?php
790                         echo " $disabled"; ?> />
791             </div>
792         </form>
793     </div>
794     <?php echo $OUTPUT->help_icon('addarandomquestion', 'quiz');
795     }
796     echo "\n</div>";
799 /**
800  * Print a given single question in quiz for the edit tab of edit.php.
801  * Meant to be used from quiz_print_question_list()
802  *
803  * @param object $question A question object from the database questions table
804  * @param object $returnurl The url to get back to this page, for example after editing.
805  * @param object $quiz The quiz in the context of which the question is being displayed
806  */
807 function quiz_print_singlequestion($question, $returnurl, $quiz) {
808     echo '<div class="singlequestion ' . $question->qtype . '">';
809     echo quiz_question_edit_button($quiz->cmid, $question, $returnurl,
810             quiz_question_tostring($question) . ' ');
811     echo '<span class="questiontype">';
812     echo print_question_icon($question);
813     echo ' ' . question_bank::get_qtype_name($question->qtype) . '</span>';
814     echo '<span class="questionpreview">' .
815             quiz_question_preview_button($quiz, $question, true) . '</span>';
816     echo "</div>\n";
818 /**
819  * Print a given random question in quiz for the edit tab of edit.php.
820  * Meant to be used from quiz_print_question_list()
821  *
822  * @param object $question A question object from the database questions table
823  * @param object $questionurl The url of the question editing page as a moodle_url object
824  * @param object $quiz The quiz in the context of which the question is being displayed
825  * @param bool $quiz_qbanktool Indicate to this function if the question bank window open
826  */
827 function quiz_print_randomquestion($question, $pageurl, $quiz, $quiz_qbanktool) {
828     global $DB, $OUTPUT;
829     echo '<div class="quiz_randomquestion">';
831     if (!$category = $DB->get_record('question_categories',
832             array('id' => $question->category))) {
833         echo $OUTPUT->notification('Random question category not found!');
834         return;
835     }
837     echo '<div class="randomquestionfromcategory">';
838     echo print_question_icon($question);
839     print_random_option_icon($question);
840     echo ' ' . get_string('randomfromcategory', 'quiz') . '</div>';
842     $a = new stdClass();
843     $a->arrow = $OUTPUT->rarrow();
844     $strshowcategorycontents = get_string('showcategorycontents', 'quiz', $a);
846     $openqbankurl = $pageurl->out(true, array('qbanktool' => 1,
847             'cat' => $category->id . ',' . $category->contextid));
848     $linkcategorycontents = ' <a href="' . $openqbankurl . '">' . $strshowcategorycontents . '</a>';
850     echo '<div class="randomquestioncategory">';
851     echo '<a href="' . $openqbankurl . '" title="' . $strshowcategorycontents . '">' .
852             $category->name . '</a>';
853     echo '<span class="questionpreview">' .
854             quiz_question_preview_button($quiz, $question, true) . '</span>';
855     echo '</div>';
857     $questionids = question_bank::get_qtype('random')->get_available_questions_from_category(
858             $category->id, $question->questiontext == '1', '0');
859     $questioncount = count($questionids);
861     echo '<div class="randomquestionqlist">';
862     if ($questioncount == 0) {
863         // No questions in category, give an error plus instructions.
864         echo '<span class="error">';
865         print_string('noquestionsnotinuse', 'quiz');
866         echo '</span>';
867         echo '<br />';
869         // Embed the link into the string with instructions.
870         $a = new stdClass();
871         $a->catname = '<strong>' . $category->name . '</strong>';
872         $a->link = $linkcategorycontents;
873         echo get_string('addnewquestionsqbank', 'quiz', $a);
875     } else {
876         // Category has questions.
878         // Get a sample from the database.
879         $questionidstoshow = array_slice($questionids, 0, NUM_QS_TO_SHOW_IN_RANDOM);
880         $questionstoshow = $DB->get_records_list('question', 'id', $questionidstoshow,
881                 '', 'id, qtype, name, questiontext, questiontextformat');
883         // Then list them.
884         echo '<ul>';
885         foreach ($questionstoshow as $subquestion) {
886             echo '<li>' . quiz_question_tostring($subquestion, true) . '</li>';
887         }
889         // Finally display the total number.
890         echo '<li class="totalquestionsinrandomqcategory">';
891         if ($questioncount > NUM_QS_TO_SHOW_IN_RANDOM) {
892             echo '... ';
893         }
894         print_string('totalquestionsinrandomqcategory', 'quiz', $questioncount);
895         echo ' ' . $linkcategorycontents;
896         echo '</li>';
897         echo '</ul>';
898     }
900     echo '</div>';
901     echo '<div class="randomquestioncategorycount">';
902     echo '</div>';
903     echo '</div>';
906 /**
907  * Print a given single question in quiz for the reordertool tab of edit.php.
908  * Meant to be used from quiz_print_question_list()
909  *
910  * @param object $question A question object from the database questions table
911  * @param object $questionurl The url of the question editing page as a moodle_url object
912  * @param object $quiz The quiz in the context of which the question is being displayed
913  */
914 function quiz_print_singlequestion_reordertool($question, $returnurl, $quiz) {
915     echo '<div class="singlequestion ' . $question->qtype . '">';
916     echo '<label for="s' . $question->id . '">';
917     echo print_question_icon($question);
918     echo ' ' . quiz_question_tostring($question);
919     echo '</label>';
920     echo '<span class="questionpreview">' .
921             quiz_question_action_icons($quiz, $quiz->cmid, $question, $returnurl) . '</span>';
922     echo "</div>\n";
925 /**
926  * Print a given random question in quiz for the reordertool tab of edit.php.
927  * Meant to be used from quiz_print_question_list()
928  *
929  * @param object $question A question object from the database questions table
930  * @param object $questionurl The url of the question editing page as a moodle_url object
931  * @param object $quiz The quiz in the context of which the question is being displayed
932  */
933 function quiz_print_randomquestion_reordertool($question, $pageurl, $quiz) {
934     global $DB, $OUTPUT;
936     // Load the category, and the number of available questions in it.
937     if (!$category = $DB->get_record('question_categories', array('id' => $question->category))) {
938         echo $OUTPUT->notification('Random question category not found!');
939         return;
940     }
941     $questioncount = count(question_bank::get_qtype(
942             'random')->get_available_questions_from_category(
943             $category->id, $question->questiontext == '1', '0'));
945     $reordercheckboxlabel = '<label for="s' . $question->id . '">';
946     $reordercheckboxlabelclose = '</label>';
948     echo '<div class="quiz_randomquestion">';
949     echo '<div class="randomquestionfromcategory">';
950     echo $reordercheckboxlabel;
951     echo print_question_icon($question);
952     print_random_option_icon($question);
954     if ($questioncount == 0) {
955         echo '<span class="error">';
956         print_string('empty', 'quiz');
957         echo '</span> ';
958     }
960     print_string('random', 'quiz');
961     echo ": $reordercheckboxlabelclose</div>";
963     echo '<div class="randomquestioncategory">';
964     echo $reordercheckboxlabel . $category->name . $reordercheckboxlabelclose;
965     echo '<span class="questionpreview">';
966     echo quiz_question_preview_button($quiz, $question, false);
967     echo '</span>';
968     echo "</div>";
970     echo '<div class="randomquestioncategorycount">';
971     echo '</div>';
972     echo '</div>';
975 /**
976  * Print an icon to indicate the 'include subcategories' state of a random question.
977  * @param $question the random question.
978  */
979 function print_random_option_icon($question) {
980     global $OUTPUT;
981     if (!empty($question->questiontext)) {
982         $icon = 'withsubcat';
983         $tooltip = get_string('randomwithsubcat', 'quiz');
984     } else {
985         $icon = 'nosubcat';
986         $tooltip = get_string('randomnosubcat', 'quiz');
987     }
988     echo '<img src="' . $OUTPUT->pix_url('i/' . $icon) . '" alt="' .
989             $tooltip . '" title="' . $tooltip . '" class="uihint" />';
992 /**
993  * Creates a textual representation of a question for display.
994  *
995  * @param object $question A question object from the database questions table
996  * @param bool $showicon If true, show the question's icon with the question. False by default.
997  * @param bool $showquestiontext If true (default), show question text after question name.
998  *       If false, show only question name.
999  * @param bool $return If true (default), return the output. If false, print it.
1000  */
1001 function quiz_question_tostring($question, $showicon = false,
1002         $showquestiontext = true, $return = true) {
1003     global $COURSE;
1004     $result = '';
1005     $result .= '<span class="questionname">';
1006     if ($showicon) {
1007         $result .= print_question_icon($question, true);
1008         echo ' ';
1009     }
1010     $result .= shorten_text(format_string($question->name), 200) . '</span>';
1011     if ($showquestiontext) {
1012         $questiontext = question_utils::to_plain_text($question->questiontext,
1013                 $question->questiontextformat, array('noclean' => true, 'para' => false));
1014         $questiontext = shorten_text($questiontext, 200);
1015         $result .= '<span class="questiontext">';
1016         if (!empty($questiontext)) {
1017             $result .= s($questiontext);
1018         } else {
1019             $result .= '<span class="error">';
1020             $result .= get_string('questiontextisempty', 'quiz');
1021             $result .= '</span>';
1022         }
1023         $result .= '</span>';
1024     }
1025     if ($return) {
1026         return $result;
1027     } else {
1028         echo $result;
1029     }
1032 /**
1033  * A column type for the add this question to the quiz.
1034  *
1035  * @copyright  2009 Tim Hunt
1036  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1037  */
1038 class question_bank_add_to_quiz_action_column extends question_bank_action_column_base {
1039     protected $stradd;
1041     public function init() {
1042         parent::init();
1043         $this->stradd = get_string('addtoquiz', 'quiz');
1044     }
1046     public function get_name() {
1047         return 'addtoquizaction';
1048     }
1050     protected function display_content($question, $rowclasses) {
1051         if (!question_has_capability_on($question, 'use')) {
1052             return;
1053         }
1054         // For RTL languages: switch right and left arrows.
1055         if (right_to_left()) {
1056             $movearrow = 't/removeright';
1057         } else {
1058             $movearrow = 't/moveleft';
1059         }
1060         $this->print_icon($movearrow, $this->stradd, $this->qbank->add_to_quiz_url($question->id));
1061     }
1063     public function get_required_fields() {
1064         return array('q.id');
1065     }
1068 /**
1069  * A column type for the name followed by the start of the question text.
1070  *
1071  * @copyright  2009 Tim Hunt
1072  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1073  */
1074 class question_bank_question_name_text_column extends question_bank_question_name_column {
1075     public function get_name() {
1076         return 'questionnametext';
1077     }
1079     protected function display_content($question, $rowclasses) {
1080         echo '<div>';
1081         $labelfor = $this->label_for($question);
1082         if ($labelfor) {
1083             echo '<label for="' . $labelfor . '">';
1084         }
1085         echo quiz_question_tostring($question, false, true, true);
1086         if ($labelfor) {
1087             echo '</label>';
1088         }
1089         echo '</div>';
1090     }
1092     public function get_required_fields() {
1093         $fields = parent::get_required_fields();
1094         $fields[] = 'q.questiontext';
1095         $fields[] = 'q.questiontextformat';
1096         return $fields;
1097     }
1100 /**
1101  * Subclass to customise the view of the question bank for the quiz editing screen.
1102  *
1103  * @copyright  2009 Tim Hunt
1104  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1105  */
1106 class quiz_question_bank_view extends core_question\bank\view {
1107     protected $quizhasattempts = false;
1108     /** @var object the quiz settings. */
1109     protected $quiz = false;
1110     /** @var int The maximum displayed length of the category info. */
1111     const MAX_TEXT_LENGTH = 200;
1113     /**
1114      * Constructor
1115      * @param question_edit_contexts $contexts
1116      * @param moodle_url $pageurl
1117      * @param object $course course settings
1118      * @param object $cm activity settings.
1119      * @param object $quiz quiz settings.
1120      */
1121     public function __construct($contexts, $pageurl, $course, $cm, $quiz) {
1122         parent::__construct($contexts, $pageurl, $course, $cm);
1123         $this->quiz = $quiz;
1124     }
1126     protected function wanted_columns() {
1127         global $CFG;
1129         if (empty($CFG->quizquestionbankcolumns)) {
1130             $quizquestionbankcolumns = array('add_to_quiz_action_column', 'checkbox_column', 'question_type_column',
1131                     'question_name_column', 'edit_action_column', 'preview_action_column');
1132         } else {
1133              $quizquestionbankcolumns = explode(',', $CFG->quizquestionbankcolumns);
1134         }
1136         foreach ($quizquestionbankcolumns as $fullname) {
1137             if (! class_exists($fullname)) {
1138                 if (class_exists('question_bank_' . $fullname)) {
1139                     $fullname = 'question_bank_' . $fullname;
1140                 } else {
1141                     throw new coding_exception("No such class exists: $fullname");
1142                 }
1143             }
1144             $this->requiredcolumns[$fullname] = new $fullname($this);
1145         }
1146         return $this->requiredcolumns;
1147     }
1149     /**
1150      * Specify the column heading
1151      *
1152      * @return string Column name for the heading
1153      */
1154     protected function heading_column() {
1155         return 'questionnametext';
1156     }
1158     protected function default_sort() {
1159         return array('question_bank_question_type_column' => 1, 'question_bank_question_name_column' => 1);
1160     }
1162     /**
1163      * Let the question bank display know whether the quiz has been attempted,
1164      * hence whether some bits of UI, like the add this question to the quiz icon,
1165      * should be displayed.
1166      * @param bool $quizhasattempts whether the quiz has attempts.
1167      */
1168     public function set_quiz_has_attempts($quizhasattempts) {
1169         $this->quizhasattempts = $quizhasattempts;
1170         if ($quizhasattempts && isset($this->visiblecolumns['addtoquizaction'])) {
1171             unset($this->visiblecolumns['addtoquizaction']);
1172         }
1173     }
1175     public function preview_question_url($question) {
1176         return quiz_question_preview_url($this->quiz, $question);
1177     }
1179     public function add_to_quiz_url($questionid) {
1180         global $CFG;
1181         $params = $this->baseurl->params();
1182         $params['addquestion'] = $questionid;
1183         $params['sesskey'] = sesskey();
1184         return new moodle_url('/mod/quiz/edit.php', $params);
1185     }
1187     public function display($tabname, $page, $perpage, $cat,
1188             $recurse, $showhidden, $showquestiontext) {
1189         global $OUTPUT;
1190         if ($this->process_actions_needing_ui()) {
1191             return;
1192         }
1194         $editcontexts = $this->contexts->having_one_edit_tab_cap($tabname);
1195         array_unshift($this->searchconditions,
1196                 new \core_question\bank\search\hidden_condition(!$showhidden));
1197         array_unshift($this->searchconditions,
1198                 new \core_question\bank\search\category_condition($cat, $recurse,
1199                         $editcontexts, $this->baseurl, $this->course, self::MAX_TEXT_LENGTH));
1201         echo $OUTPUT->box_start('generalbox questionbank');
1202         $this->display_options_form($showquestiontext);
1204         // Continues with list of questions.
1205         $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname),
1206                 $this->baseurl, $cat, $this->cm, $recurse, $page,
1207                 $perpage, $showhidden, $showquestiontext,
1208                 $this->contexts->having_cap('moodle/question:add'));
1210         echo $OUTPUT->box_end();
1211     }
1213     /**
1214      * prints a form to choose categories
1215      * @param string $categoryandcontext 'categoryID,contextID'.
1216      * @deprecated since Moodle 2.6 MDL-40313.
1217      * @see \core_question\bank\search\category_condition
1218      * @todo MDL-41978 This will be deleted in Moodle 2.8
1219      */
1220     protected function print_choose_category_message($categoryandcontext) {
1221         global $OUTPUT;
1222         debugging('print_choose_category_message() is deprecated, ' .
1223                 'please use \core_question\bank\search\category_condition instead.', DEBUG_DEVELOPER);
1224         echo $OUTPUT->box_start('generalbox questionbank');
1225         $this->display_category_form($this->contexts->having_one_edit_tab_cap('edit'),
1226                 $this->baseurl, $categoryandcontext);
1227         echo "<p style=\"text-align:center;\"><b>";
1228         print_string('selectcategoryabove', 'question');
1229         echo "</b></p>";
1230         echo $OUTPUT->box_end();
1231     }
1233     /**
1234      * Display the form with options for which questions are displayed and how they are displayed.
1235      * This differs from parent display_options_form only in that it does not have the checkbox to show the question text.
1236      * @param bool $showquestiontext Display the text of the question within the list. (Currently ignored)
1237      */
1238     protected function display_options_form($showquestiontext) {
1239         global $PAGE;
1240         echo html_writer::start_tag('form', array('method' => 'get',
1241                 'action' => new moodle_url('/mod/quiz/edit.php'), 'id' => 'displayoptions'));
1242         echo html_writer::start_div();
1243         foreach ($this->searchconditions as $searchcondition) {
1244             echo $searchcondition->display_options($this);
1245         }
1246         $this->display_advanced_search_form();
1247         $go = html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go')));
1248         echo html_writer::tag('noscript', html_writer::tag('div', $go), array('class' => 'inline'));
1249         echo html_writer::end_div();
1250         echo html_writer::end_tag('form');
1251         $PAGE->requires->yui_module('moodle-question-searchform', 'M.question.searchform.init');
1252     }
1254     protected function print_category_info($category) {
1255         $formatoptions = new stdClass();
1256         $formatoptions->noclean = true;
1257         $strcategory = get_string('category', 'quiz');
1258         echo '<div class="categoryinfo"><div class="categorynamefieldcontainer">' .
1259                 $strcategory;
1260         echo ': <span class="categorynamefield">';
1261         echo shorten_text(strip_tags(format_string($category->name)), 60);
1262         echo '</span></div><div class="categoryinfofieldcontainer">' .
1263                 '<span class="categoryinfofield">';
1264         echo shorten_text(strip_tags(format_text($category->info, $category->infoformat,
1265                 $formatoptions, $this->course->id)), 200);
1266         echo '</span></div></div>';
1267     }
1269     protected function display_options($recurse, $showhidden, $showquestiontext) {
1270         debugging('display_options() is deprecated, see display_options_form() instead.', DEBUG_DEVELOPER);
1271         echo '<form method="get" action="edit.php" id="displayoptions">';
1272         echo "<fieldset class='invisiblefieldset'>";
1273         echo html_writer::input_hidden_params($this->baseurl,
1274                 array('recurse', 'showhidden', 'qbshowtext'));
1275         $this->display_category_form_checkbox('recurse', $recurse,
1276                 get_string('includesubcategories', 'question'));
1277         $this->display_category_form_checkbox('showhidden', $showhidden,
1278                 get_string('showhidden', 'question'));
1279         echo '<noscript><div class="centerpara"><input type="submit" value="' .
1280                 get_string('go') . '" />';
1281         echo '</div></noscript></fieldset></form>';
1282     }
1285 /**
1286  * Prints the form for setting a quiz' overall grade
1287  *
1288  * @param object $quiz The quiz object of the quiz in question
1289  * @param object $pageurl The url of the current page with the parameters required
1290  *     for links returning to the current page, as a moodle_url object
1291  * @param int $tabindex The tabindex to start from for the form elements created
1292  * @return int The tabindex from which the calling page can continue, that is,
1293  *      the last value used +1.
1294  */
1295 function quiz_print_grading_form($quiz, $pageurl, $tabindex) {
1296     global $OUTPUT;
1297     $strsave = get_string('save', 'quiz');
1298     echo '<form method="post" action="edit.php" class="quizsavegradesform"><div>';
1299     echo '<fieldset class="invisiblefieldset" style="display: block;">';
1300     echo "<input type=\"hidden\" name=\"sesskey\" value=\"" . sesskey() . "\" />";
1301     echo html_writer::input_hidden_params($pageurl);
1302     $a = '<input type="text" id="inputmaxgrade" name="maxgrade" size="' .
1303             ($quiz->decimalpoints + 2) . '" tabindex="' . $tabindex
1304          . '" value="' . quiz_format_grade($quiz, $quiz->grade) . '" />';
1305     echo '<label for="inputmaxgrade">' . get_string('maximumgradex', '', $a) . "</label>";
1306     echo '<input type="hidden" name="savechanges" value="save" />';
1307     echo '<input type="submit" value="' . $strsave . '" />';
1308     echo '</fieldset>';
1309     echo "</div></form>\n";
1310     return $tabindex + 1;
1313 /**
1314  * Print the status bar
1315  *
1316  * @param object $quiz The quiz object of the quiz in question
1317  */
1318 function quiz_print_status_bar($quiz) {
1319     global $DB;
1321     $bits = array();
1323     $bits[] = html_writer::tag('span',
1324             get_string('totalmarksx', 'quiz', quiz_format_grade($quiz, $quiz->sumgrades)),
1325             array('class' => 'totalpoints'));
1327     $bits[] = html_writer::tag('span',
1328             get_string('numquestionsx', 'quiz', $DB->count_records('quiz_slots', array('quizid' => $quiz->id))),
1329             array('class' => 'numberofquestions'));
1331     $timenow = time();
1333     // Exact open and close dates for the tool-tip.
1334     $dates = array();
1335     if ($quiz->timeopen > 0) {
1336         if ($timenow > $quiz->timeopen) {
1337             $dates[] = get_string('quizopenedon', 'quiz', userdate($quiz->timeopen));
1338         } else {
1339             $dates[] = get_string('quizwillopen', 'quiz', userdate($quiz->timeopen));
1340         }
1341     }
1342     if ($quiz->timeclose > 0) {
1343         if ($timenow > $quiz->timeclose) {
1344             $dates[] = get_string('quizclosed', 'quiz', userdate($quiz->timeclose));
1345         } else {
1346             $dates[] = get_string('quizcloseson', 'quiz', userdate($quiz->timeclose));
1347         }
1348     }
1349     if (empty($dates)) {
1350         $dates[] = get_string('alwaysavailable', 'quiz');
1351     }
1352     $tooltip = implode(', ', $dates);
1354     // Brief summary on the page.
1355     if ($timenow < $quiz->timeopen) {
1356         $currentstatus = get_string('quizisclosedwillopen', 'quiz',
1357                 userdate($quiz->timeopen, get_string('strftimedatetimeshort', 'langconfig')));
1358     } else if ($quiz->timeclose && $timenow <= $quiz->timeclose) {
1359         $currentstatus = get_string('quizisopenwillclose', 'quiz',
1360                 userdate($quiz->timeclose, get_string('strftimedatetimeshort', 'langconfig')));
1361     } else if ($quiz->timeclose && $timenow > $quiz->timeclose) {
1362         $currentstatus = get_string('quizisclosed', 'quiz');
1363     } else {
1364         $currentstatus = get_string('quizisopen', 'quiz');
1365     }
1367     $bits[] = html_writer::tag('span', $currentstatus,
1368             array('class' => 'quizopeningstatus', 'title' => implode(', ', $dates)));
1370     echo html_writer::tag('div', implode(' | ', $bits), array('class' => 'statusbar'));