|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00017 00032 defined('MOODLE_INTERNAL') || die(); 00033 00034 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 00035 00036 define('NUM_QS_TO_SHOW_IN_RANDOM', 3); 00037 00043 function quiz_remove_question($quiz, $questionid) { 00044 global $DB; 00045 00046 $questionids = explode(',', $quiz->questions); 00047 $key = array_search($questionid, $questionids); 00048 if ($key === false) { 00049 return; 00050 } 00051 00052 unset($questionids[$key]); 00053 $quiz->questions = implode(',', $questionids); 00054 $DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id)); 00055 $DB->delete_records('quiz_question_instances', 00056 array('quiz' => $quiz->instance, 'question' => $questionid)); 00057 } 00058 00065 function quiz_delete_empty_page($layout, $index) { 00066 $questionids = explode(',', $layout); 00067 00068 if ($index < -1 || $index >= count($questionids) - 1) { 00069 return $layout; 00070 } 00071 00072 if (($index >= 0 && $questionids[$index] != 0) || $questionids[$index + 1] != 0) { 00073 return $layout; // This was not an empty page. 00074 } 00075 00076 unset($questionids[$index + 1]); 00077 00078 return implode(',', $questionids); 00079 } 00080 00094 function quiz_add_quiz_question($id, $quiz, $page = 0) { 00095 global $DB; 00096 $questions = explode(',', quiz_clean_layout($quiz->questions)); 00097 if (in_array($id, $questions)) { 00098 return false; 00099 } 00100 00101 // remove ending page break if it is not needed 00102 if ($breaks = array_keys($questions, 0)) { 00103 // determine location of the last two page breaks 00104 $end = end($breaks); 00105 $last = prev($breaks); 00106 $last = $last ? $last : -1; 00107 if (!$quiz->questionsperpage || (($end - $last - 1) < $quiz->questionsperpage)) { 00108 array_pop($questions); 00109 } 00110 } 00111 if (is_int($page) && $page >= 1) { 00112 $numofpages = quiz_number_of_pages($quiz->questions); 00113 if ($numofpages<$page) { 00114 //the page specified does not exist in quiz 00115 $page = 0; 00116 } else { 00117 // add ending page break - the following logic requires doing 00118 //this at this point 00119 $questions[] = 0; 00120 $currentpage = 1; 00121 $addnow = false; 00122 foreach ($questions as $question) { 00123 if ($question == 0) { 00124 $currentpage++; 00125 //The current page is the one after the one we want to add on, 00126 //so we add the question before adding the current page. 00127 if ($currentpage == $page + 1) { 00128 $questions_new[] = $id; 00129 } 00130 } 00131 $questions_new[] = $question; 00132 } 00133 $questions = $questions_new; 00134 } 00135 } 00136 if ($page == 0) { 00137 // add question 00138 $questions[] = $id; 00139 // add ending page break 00140 $questions[] = 0; 00141 } 00142 00143 // Save new questionslist in database 00144 $quiz->questions = implode(',', $questions); 00145 $DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id)); 00146 00147 // Add the new question instance. 00148 $instance = new stdClass(); 00149 $instance->quiz = $quiz->id; 00150 $instance->question = $id; 00151 $instance->grade = $DB->get_field('question', 'defaultmark', array('id' => $id)); 00152 $DB->insert_record('quiz_question_instances', $instance); 00153 } 00154 00155 function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number, 00156 $includesubcategories) { 00157 global $DB; 00158 00159 $category = $DB->get_record('question_categories', array('id' => $categoryid)); 00160 if (!$category) { 00161 print_error('invalidcategoryid', 'error'); 00162 } 00163 00164 $catcontext = get_context_instance_by_id($category->contextid); 00165 require_capability('moodle/question:useall', $catcontext); 00166 00167 // Find existing random questions in this category that are 00168 // not used by any quiz. 00169 if ($existingquestions = $DB->get_records_sql( 00170 "SELECT q.id, q.qtype FROM {question} q 00171 WHERE qtype = 'random' 00172 AND category = ? 00173 AND " . $DB->sql_compare_text('questiontext') . " = ? 00174 AND NOT EXISTS ( 00175 SELECT * 00176 FROM {quiz_question_instances} 00177 WHERE question = q.id) 00178 ORDER BY id", array($category->id, $includesubcategories))) { 00179 // Take as many of these as needed. 00180 while (($existingquestion = array_shift($existingquestions)) && $number > 0) { 00181 quiz_add_quiz_question($existingquestion->id, $quiz, $addonpage); 00182 $number -= 1; 00183 } 00184 } 00185 00186 if ($number <= 0) { 00187 return; 00188 } 00189 00190 // More random questions are needed, create them. 00191 for ($i = 0; $i < $number; $i += 1) { 00192 $form = new stdClass(); 00193 $form->questiontext = array('text' => $includesubcategories, 'format' => 0); 00194 $form->category = $category->id . ',' . $category->contextid; 00195 $form->defaultmark = 1; 00196 $form->hidden = 1; 00197 $form->stamp = make_unique_id_code(); // Set the unique code (not to be changed) 00198 $question = new stdClass(); 00199 $question->qtype = 'random'; 00200 $question = question_bank::get_qtype('random')->save_question($question, $form); 00201 if (!isset($question->id)) { 00202 print_error('cannotinsertrandomquestion', 'quiz'); 00203 } 00204 quiz_add_quiz_question($question->id, $quiz, $addonpage); 00205 } 00206 } 00207 00214 function quiz_add_page_break_at($layout, $index) { 00215 $questionids = explode(',', $layout); 00216 if ($index < 0 || $index >= count($questionids)) { 00217 return $layout; 00218 } 00219 00220 array_splice($questionids, $index, 0, '0'); 00221 00222 return implode(',', $questionids); 00223 } 00224 00231 function quiz_add_page_break_after($layout, $questionid) { 00232 $questionids = explode(',', $layout); 00233 $key = array_search($questionid, $questionids); 00234 if ($key === false || !$questionid) { 00235 return $layout; 00236 } 00237 00238 array_splice($questionids, $key + 1, 0, '0'); 00239 00240 return implode(',', $questionids); 00241 } 00242 00248 function quiz_save_new_layout($quiz) { 00249 global $DB; 00250 $DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id)); 00251 quiz_delete_previews($quiz); 00252 quiz_update_sumgrades($quiz); 00253 } 00254 00265 function quiz_update_question_instance($grade, $questionid, $quiz) { 00266 global $DB; 00267 $instance = $DB->get_record('quiz_question_instances', array('quiz' => $quiz->id, 00268 'question' => $questionid)); 00269 $slot = quiz_get_slot_for_question($quiz, $questionid); 00270 00271 if (!$instance || !$slot) { 00272 throw new coding_exception('Attempt to change the grade of a quesion not in the quiz.'); 00273 } 00274 00275 if (abs($grade - $instance->grade) < 1e-7) { 00276 // Grade has not changed. Nothing to do. 00277 return; 00278 } 00279 00280 $instance->grade = $grade; 00281 $DB->update_record('quiz_question_instances', $instance); 00282 question_engine::set_max_mark_in_attempts(new qubaids_for_quiz($quiz->id), 00283 $slot, $grade); 00284 } 00285 00286 // Private function used by the following two. 00287 function _quiz_move_question($layout, $questionid, $shift) { 00288 if (!$questionid || !($shift == 1 || $shift == -1)) { 00289 return $layout; 00290 } 00291 00292 $questionids = explode(',', $layout); 00293 $key = array_search($questionid, $questionids); 00294 if ($key === false) { 00295 return $layout; 00296 } 00297 00298 $otherkey = $key + $shift; 00299 if ($otherkey < 0 || $otherkey >= count($questionids) - 1) { 00300 return $layout; 00301 } 00302 00303 $temp = $questionids[$otherkey]; 00304 $questionids[$otherkey] = $questionids[$key]; 00305 $questionids[$key] = $temp; 00306 00307 return implode(',', $questionids); 00308 } 00309 00317 function quiz_move_question_up($layout, $questionid) { 00318 return _quiz_move_question($layout, $questionid, -1); 00319 } 00320 00328 function quiz_move_question_down($layout, $questionid) { 00329 return _quiz_move_question($layout, $questionid, +1); 00330 } 00331 00347 function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool, 00348 $quiz_qbanktool, $hasattempts, $defaultcategoryobj) { 00349 global $CFG, $DB, $OUTPUT; 00350 $strorder = get_string('order'); 00351 $strquestionname = get_string('questionname', 'quiz'); 00352 $strgrade = get_string('grade'); 00353 $strremove = get_string('remove', 'quiz'); 00354 $stredit = get_string('edit'); 00355 $strview = get_string('view'); 00356 $straction = get_string('action'); 00357 $strmove = get_string('move'); 00358 $strmoveup = get_string('moveup'); 00359 $strmovedown = get_string('movedown'); 00360 $strsave = get_string('save', 'quiz'); 00361 $strreorderquestions = get_string('reorderquestions', 'quiz'); 00362 00363 $strselectall = get_string('selectall', 'quiz'); 00364 $strselectnone = get_string('selectnone', 'quiz'); 00365 $strtype = get_string('type', 'quiz'); 00366 $strpreview = get_string('preview', 'quiz'); 00367 00368 if ($quiz->questions) { 00369 list($usql, $params) = $DB->get_in_or_equal(explode(',', $quiz->questions)); 00370 $params[] = $quiz->id; 00371 $questions = $DB->get_records_sql("SELECT q.*, qc.contextid, qqi.grade as maxmark 00372 FROM {question} q 00373 JOIN {question_categories} qc ON qc.id = q.category 00374 JOIN {quiz_question_instances} qqi ON qqi.question = q.id 00375 WHERE q.id $usql AND qqi.quiz = ?", $params); 00376 } else { 00377 $questions = array(); 00378 } 00379 00380 $layout = quiz_clean_layout($quiz->questions); 00381 $order = explode(',', $layout); 00382 $lastindex = count($order) - 1; 00383 00384 $disabled = ''; 00385 $pagingdisabled = ''; 00386 if ($hasattempts) { 00387 $disabled = 'disabled="disabled"'; 00388 } 00389 if ($hasattempts || $quiz->shufflequestions) { 00390 $pagingdisabled = 'disabled="disabled"'; 00391 } 00392 00393 $reordercontrolssetdefaultsubmit = '<div style="display:none;">' . 00394 '<input type="submit" name="savechanges" value="' . 00395 $strreorderquestions . '" ' . $pagingdisabled . ' /></div>'; 00396 $reordercontrols1 = '<div class="addnewpagesafterselected">' . 00397 '<input type="submit" name="addnewpagesafterselected" value="' . 00398 get_string('addnewpagesafterselected', 'quiz') . '" ' . 00399 $pagingdisabled . ' /></div>'; 00400 $reordercontrols1 .= '<div class="quizdeleteselected">' . 00401 '<input type="submit" name="quizdeleteselected" ' . 00402 'onclick="return confirm(\'' . 00403 get_string('areyousureremoveselected', 'quiz') . '\');" value="' . 00404 get_string('removeselected', 'quiz') . '" ' . $disabled . ' /></div>'; 00405 00406 $a = '<input name="moveselectedonpagetop" type="text" size="2" ' . 00407 $pagingdisabled . ' />'; 00408 $b = '<input name="moveselectedonpagebottom" type="text" size="2" ' . 00409 $pagingdisabled . ' />'; 00410 00411 $reordercontrols2top = '<div class="moveselectedonpage">' . 00412 get_string('moveselectedonpage', 'quiz', $a) . 00413 '<input type="submit" name="savechanges" value="' . 00414 $strmove . '" ' . $pagingdisabled . ' />' . ' 00415 <br /><input type="submit" name="savechanges" value="' . 00416 $strreorderquestions . '" /></div>'; 00417 $reordercontrols2bottom = '<div class="moveselectedonpage">' . 00418 '<input type="submit" name="savechanges" value="' . 00419 $strreorderquestions . '" /><br />' . 00420 get_string('moveselectedonpage', 'quiz', $b) . 00421 '<input type="submit" name="savechanges" value="' . 00422 $strmove . '" ' . $pagingdisabled . ' /> ' . '</div>'; 00423 00424 $reordercontrols3 = '<a href="javascript:select_all_in(\'FORM\', null, ' . 00425 '\'quizquestions\');">' . 00426 $strselectall . '</a> /'; 00427 $reordercontrols3.= ' <a href="javascript:deselect_all_in(\'FORM\', ' . 00428 'null, \'quizquestions\');">' . 00429 $strselectnone . '</a>'; 00430 00431 $reordercontrolstop = '<div class="reordercontrols">' . 00432 $reordercontrolssetdefaultsubmit . 00433 $reordercontrols1 . $reordercontrols2top . $reordercontrols3 . "</div>"; 00434 $reordercontrolsbottom = '<div class="reordercontrols">' . 00435 $reordercontrolssetdefaultsubmit . 00436 $reordercontrols2bottom . $reordercontrols1 . $reordercontrols3 . "</div>"; 00437 00438 if ($reordertool) { 00439 echo '<form method="post" action="edit.php" id="quizquestions"><div>'; 00440 00441 echo html_writer::input_hidden_params($pageurl); 00442 echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />'; 00443 00444 echo $reordercontrolstop; 00445 } 00446 00447 //the current question ordinal (no descriptions) 00448 $qno = 1; 00449 //the current question (includes questions and descriptions) 00450 $questioncount = 0; 00451 //the current page number in iteration 00452 $pagecount = 0; 00453 00454 $pageopen = false; 00455 00456 $returnurl = str_replace($CFG->wwwroot, '', $pageurl->out(false)); 00457 $questiontotalcount = count($order); 00458 00459 foreach ($order as $count => $qnum) { 00460 00461 $reordercheckbox = ''; 00462 $reordercheckboxlabel = ''; 00463 $reordercheckboxlabelclose = ''; 00464 00465 // If the questiontype is missing change the question type 00466 if ($qnum && !array_key_exists($qnum, $questions)) { 00467 $fakequestion = new stdClass(); 00468 $fakequestion->id = $qnum; 00469 $fakequestion->category = 0; 00470 $fakequestion->qtype = 'missingtype'; 00471 $fakequestion->name = get_string('missingquestion', 'quiz'); 00472 $fakequestion->questiontext = ' '; 00473 $fakequestion->questiontextformat = FORMAT_HTML; 00474 $fakequestion->length = 1; 00475 $questions[$qnum] = $fakequestion; 00476 $quiz->grades[$qnum] = 0; 00477 00478 } else if ($qnum && !question_bank::qtype_exists($questions[$qnum]->qtype)) { 00479 $questions[$qnum]->qtype = 'missingtype'; 00480 } 00481 00482 if ($qnum != 0 || ($qnum == 0 && !$pageopen)) { 00483 //this is either a question or a page break after another 00484 // (no page is currently open) 00485 if (!$pageopen) { 00486 //if no page is open, start display of a page 00487 $pagecount++; 00488 echo '<div class="quizpage"><span class="pagetitle">' . 00489 get_string('page') . ' ' . $pagecount . 00490 '</span><div class="pagecontent">'; 00491 $pageopen = true; 00492 } 00493 if ($qnum == 0 && $count < $questiontotalcount) { 00494 // This is the second successive page break. Tell the user the page is empty. 00495 echo '<div class="pagestatus">'; 00496 print_string('noquestionsonpage', 'quiz'); 00497 echo '</div>'; 00498 if ($allowdelete) { 00499 echo '<div class="quizpagedelete">'; 00500 echo $OUTPUT->action_icon($pageurl->out(true, 00501 array('deleteemptypage' => $count - 1, 'sesskey'=>sesskey())), 00502 new pix_icon('t/delete', $strremove), 00503 new component_action('click', 00504 'M.core_scroll_manager.save_scroll_action'), 00505 array('title' => $strremove)); 00506 echo '</div>'; 00507 } 00508 } 00509 00510 if ($qnum != 0) { 00511 $question = $questions[$qnum]; 00512 $questionparams = array( 00513 'returnurl' => $returnurl, 00514 'cmid' => $quiz->cmid, 00515 'id' => $question->id); 00516 $questionurl = new moodle_url('/question/question.php', 00517 $questionparams); 00518 $questioncount++; 00519 //this is an actual question 00520 00521 /* Display question start */ 00522 ?> 00523 <div class="question"> 00524 <div class="questioncontainer <?php echo $question->qtype; ?>"> 00525 <div class="qnum"> 00526 <?php 00527 $reordercheckbox = ''; 00528 $reordercheckboxlabel = ''; 00529 $reordercheckboxlabelclose = ''; 00530 if ($reordertool) { 00531 $reordercheckbox = '<input type="checkbox" name="s' . $question->id . 00532 '" id="s' . $question->id . '" />'; 00533 $reordercheckboxlabel = '<label for="s' . $question->id . '">'; 00534 $reordercheckboxlabelclose = '</label>'; 00535 } 00536 if ($question->length == 0) { 00537 $qnodisplay = get_string('infoshort', 'quiz'); 00538 } else if ($quiz->shufflequestions) { 00539 $qnodisplay = '?'; 00540 } else { 00541 if ($qno > 999 || ($reordertool && $qno > 99)) { 00542 $qnodisplay = html_writer::tag('small', $qno); 00543 } else { 00544 $qnodisplay = $qno; 00545 } 00546 $qno += $question->length; 00547 } 00548 echo $reordercheckboxlabel . $qnodisplay . $reordercheckboxlabelclose . 00549 $reordercheckbox; 00550 00551 ?> 00552 </div> 00553 <div class="content"> 00554 <div class="questioncontrols"> 00555 <?php 00556 if ($count != 0) { 00557 if (!$hasattempts) { 00558 $upbuttonclass = ''; 00559 if ($count >= $lastindex - 1) { 00560 $upbuttonclass = 'upwithoutdown'; 00561 } 00562 echo $OUTPUT->action_icon($pageurl->out(true, 00563 array('up' => $question->id, 'sesskey'=>sesskey())), 00564 new pix_icon('t/up', $strmoveup), 00565 new component_action('click', 00566 'M.core_scroll_manager.save_scroll_action'), 00567 array('title' => $strmoveup)); 00568 } 00569 00570 } 00571 if ($count < $lastindex - 1) { 00572 if (!$hasattempts) { 00573 echo $OUTPUT->action_icon($pageurl->out(true, 00574 array('down' => $question->id, 'sesskey'=>sesskey())), 00575 new pix_icon('t/down', $strmovedown), 00576 new component_action('click', 00577 'M.core_scroll_manager.save_scroll_action'), 00578 array('title' => $strmovedown)); 00579 } 00580 } 00581 if ($allowdelete && ($question->qtype == 'missingtype' || 00582 question_has_capability_on($question, 'use', $question->category))) { 00583 // remove from quiz, not question delete. 00584 if (!$hasattempts) { 00585 echo $OUTPUT->action_icon($pageurl->out(true, 00586 array('remove' => $question->id, 'sesskey'=>sesskey())), 00587 new pix_icon('t/delete', $strremove), 00588 new component_action('click', 00589 'M.core_scroll_manager.save_scroll_action'), 00590 array('title' => $strremove)); 00591 } 00592 } 00593 ?> 00594 </div><?php 00595 if (!in_array($question->qtype, array('description', 'missingtype')) && !$reordertool) { 00596 ?> 00597 <div class="points"> 00598 <form method="post" action="edit.php" class="quizsavegradesform"><div> 00599 <fieldset class="invisiblefieldset" style="display: block;"> 00600 <label for="<?php echo "inputq$question->id" ?>"><?php echo $strgrade; ?></label>:<br /> 00601 <input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" /> 00602 <?php echo html_writer::input_hidden_params($pageurl); ?> 00603 <input type="hidden" name="savechanges" value="save" /> 00604 <?php 00605 echo '<input type="text" name="g' . $question->id . 00606 '" id="inputq' . $question->id . 00607 '" size="' . ($quiz->decimalpoints + 2) . 00608 '" value="' . (0 + $quiz->grades[$qnum]) . 00609 '" tabindex="' . ($lastindex + $qno) . '" />'; 00610 ?> 00611 <input type="submit" class="pointssubmitbutton" value="<?php echo $strsave; ?>" /> 00612 </fieldset> 00613 <?php 00614 if ($question->qtype == 'random') { 00615 echo '<a href="' . $questionurl->out() . 00616 '" class="configurerandomquestion">' . 00617 get_string("configurerandomquestion", "quiz") . '</a>'; 00618 } 00619 00620 ?> 00621 </div> 00622 </form> 00623 00624 </div> 00625 <?php 00626 } else if ($reordertool) { 00627 if ($qnum) { 00628 ?> 00629 <div class="qorder"> 00630 <?php 00631 echo '<input type="text" name="o' . $question->id . 00632 '" size="2" value="' . (10*$count + 10) . 00633 '" tabindex="' . ($lastindex + $qno) . '" />'; 00634 ?> 00635 </div> 00636 <?php 00637 } 00638 } 00639 ?> 00640 <div class="questioncontentcontainer"> 00641 <?php 00642 if ($question->qtype == 'random') { // it is a random question 00643 if (!$reordertool) { 00644 quiz_print_randomquestion($question, $pageurl, $quiz, $quiz_qbanktool); 00645 } else { 00646 quiz_print_randomquestion_reordertool($question, $pageurl, $quiz); 00647 } 00648 } else { // it is a single question 00649 if (!$reordertool) { 00650 quiz_print_singlequestion($question, $returnurl, $quiz); 00651 } else { 00652 quiz_print_singlequestion_reordertool($question, $returnurl, $quiz); 00653 } 00654 } 00655 ?> 00656 </div> 00657 </div> 00658 </div> 00659 </div> 00660 00661 <?php 00662 } 00663 } 00664 //a page break: end the existing page. 00665 if ($qnum == 0) { 00666 if ($pageopen) { 00667 if (!$reordertool && !($quiz->shufflequestions && 00668 $count < $questiontotalcount - 1)) { 00669 quiz_print_pagecontrols($quiz, $pageurl, $pagecount, 00670 $hasattempts, $defaultcategoryobj); 00671 } else if ($count < $questiontotalcount - 1) { 00672 //do not include the last page break for reordering 00673 //to avoid creating a new extra page in the end 00674 echo '<input type="hidden" name="opg' . $pagecount . '" size="2" value="' . 00675 (10*$count + 10) . '" />'; 00676 } 00677 echo "</div></div>"; 00678 00679 if (!$reordertool && !$quiz->shufflequestions) { 00680 echo $OUTPUT->container_start('addpage'); 00681 $url = new moodle_url($pageurl->out_omit_querystring(), 00682 array('cmid' => $quiz->cmid, 'courseid' => $quiz->course, 00683 'addpage' => $count, 'sesskey' => sesskey())); 00684 echo $OUTPUT->single_button($url, get_string('addpagehere', 'quiz'), 'post', 00685 array('disabled' => $hasattempts, 00686 'actions' => array(new component_action('click', 00687 'M.core_scroll_manager.save_scroll_action')))); 00688 echo $OUTPUT->container_end(); 00689 } 00690 $pageopen = false; 00691 $count++; 00692 } 00693 } 00694 00695 } 00696 if ($reordertool) { 00697 echo $reordercontrolsbottom; 00698 echo '</div></form>'; 00699 } 00700 } 00701 00711 function quiz_print_pagecontrols($quiz, $pageurl, $page, $hasattempts, $defaultcategoryobj) { 00712 global $CFG, $OUTPUT; 00713 static $randombuttoncount = 0; 00714 $randombuttoncount++; 00715 echo '<div class="pagecontrols">'; 00716 00717 // Get the current context 00718 $thiscontext = get_context_instance(CONTEXT_COURSE, $quiz->course); 00719 $contexts = new question_edit_contexts($thiscontext); 00720 00721 // Get the default category. 00722 list($defaultcategoryid) = explode(',', $pageurl->param('cat')); 00723 if (empty($defaultcategoryid)) { 00724 $defaultcategoryid = $defaultcategoryobj->id; 00725 } 00726 00727 // Create the url the question page will return to 00728 $returnurladdtoquiz = new moodle_url($pageurl, array('addonpage' => $page)); 00729 00730 // Print a button linking to the choose question type page. 00731 $returnurladdtoquiz = str_replace($CFG->wwwroot, '', $returnurladdtoquiz->out(false)); 00732 $newquestionparams = array('returnurl' => $returnurladdtoquiz, 00733 'cmid' => $quiz->cmid, 'appendqnumstring' => 'addquestion'); 00734 create_new_question_button($defaultcategoryid, $newquestionparams, 00735 get_string('addaquestion', 'quiz'), 00736 get_string('createquestionandadd', 'quiz'), $hasattempts); 00737 00738 if ($hasattempts) { 00739 $disabled = 'disabled="disabled"'; 00740 } else { 00741 $disabled = ''; 00742 } 00743 ?> 00744 <div class="singlebutton"> 00745 <form class="randomquestionform" action="<?php echo $CFG->wwwroot; 00746 ?>/mod/quiz/addrandom.php" method="get"> 00747 <div> 00748 <input type="hidden" class="addonpage_formelement" name="addonpage" value="<?php 00749 echo $page; ?>" /> 00750 <input type="hidden" name="cmid" value="<?php echo $quiz->cmid; ?>" /> 00751 <input type="hidden" name="courseid" value="<?php echo $quiz->course; ?>" /> 00752 <input type="hidden" name="category" value="<?php 00753 echo $pageurl->param('cat'); ?>" /> 00754 <input type="hidden" name="returnurl" value="<?php 00755 echo s(str_replace($CFG->wwwroot, '', $pageurl->out(false))); ?>" /> 00756 <input type="submit" id="addrandomdialoglaunch_<?php 00757 echo $randombuttoncount; ?>" value="<?php 00758 echo get_string('addarandomquestion', 'quiz'); ?>" <?php 00759 echo " $disabled"; ?> /> 00760 </div> 00761 </form> 00762 </div> 00763 <?php echo $OUTPUT->help_icon('addarandomquestion', 'quiz'); ?> 00764 <?php 00765 echo "\n</div>"; 00766 } 00767 00776 function quiz_print_singlequestion($question, $returnurl, $quiz) { 00777 echo '<div class="singlequestion ' . $question->qtype . '">'; 00778 echo quiz_question_edit_button($quiz->cmid, $question, $returnurl, 00779 quiz_question_tostring($question) . ' '); 00780 echo '<span class="questiontype">'; 00781 echo print_question_icon($question); 00782 echo ' ' . question_bank::get_qtype_name($question->qtype) . '</span>'; 00783 echo '<span class="questionpreview">' . 00784 quiz_question_preview_button($quiz, $question, true) . '</span>'; 00785 echo "</div>\n"; 00786 } 00796 function quiz_print_randomquestion(&$question, &$pageurl, &$quiz, $quiz_qbanktool) { 00797 global $DB, $OUTPUT; 00798 echo '<div class="quiz_randomquestion">'; 00799 00800 if (!$category = $DB->get_record('question_categories', 00801 array('id' => $question->category))) { 00802 echo $OUTPUT->notification('Random question category not found!'); 00803 return; 00804 } 00805 00806 echo '<div class="randomquestionfromcategory">'; 00807 echo print_question_icon($question); 00808 print_random_option_icon($question); 00809 echo ' ' . get_string('randomfromcategory', 'quiz') . '</div>'; 00810 00811 $a = new stdClass(); 00812 $a->arrow = $OUTPUT->rarrow(); 00813 $strshowcategorycontents = get_string('showcategorycontents', 'quiz', $a); 00814 00815 $openqbankurl = $pageurl->out(true, array('qbanktool' => 1, 00816 'cat' => $category->id . ',' . $category->contextid)); 00817 $linkcategorycontents = ' <a href="' . $openqbankurl . '">' . $strshowcategorycontents . '</a>'; 00818 00819 echo '<div class="randomquestioncategory">'; 00820 echo '<a href="' . $openqbankurl . '" title="' . $strshowcategorycontents . '">' . 00821 $category->name . '</a>'; 00822 echo '<span class="questionpreview">' . 00823 quiz_question_preview_button($quiz, $question, true) . '</span>'; 00824 echo '</div>'; 00825 00826 $questionids = question_bank::get_qtype('random')->get_available_questions_from_category( 00827 $category->id, $question->questiontext == '1', '0'); 00828 $questioncount = count($questionids); 00829 00830 echo '<div class="randomquestionqlist">'; 00831 if ($questioncount == 0) { 00832 // No questions in category, give an error plus instructions 00833 echo '<span class="error">'; 00834 print_string('noquestionsnotinuse', 'quiz'); 00835 echo '</span>'; 00836 echo '<br />'; 00837 00838 // Embed the link into the string with instructions 00839 $a = new stdClass(); 00840 $a->catname = '<strong>' . $category->name . '</strong>'; 00841 $a->link = $linkcategorycontents; 00842 echo get_string('addnewquestionsqbank', 'quiz', $a); 00843 00844 } else { 00845 // Category has questions 00846 00847 // Get a sample from the database, 00848 $questionidstoshow = array_slice($questionids, 0, NUM_QS_TO_SHOW_IN_RANDOM); 00849 $questionstoshow = $DB->get_records_list('question', 'id', $questionidstoshow, 00850 '', 'id, qtype, name, questiontext, questiontextformat'); 00851 00852 // list them, 00853 echo '<ul>'; 00854 foreach ($questionstoshow as $question) { 00855 echo '<li>' . quiz_question_tostring($question, true) . '</li>'; 00856 } 00857 00858 // and then display the total number. 00859 echo '<li class="totalquestionsinrandomqcategory">'; 00860 if ($questioncount > NUM_QS_TO_SHOW_IN_RANDOM) { 00861 echo '... '; 00862 } 00863 print_string('totalquestionsinrandomqcategory', 'quiz', $questioncount); 00864 echo ' ' . $linkcategorycontents; 00865 echo '</li>'; 00866 echo '</ul>'; 00867 } 00868 00869 echo '</div>'; 00870 echo '<div class="randomquestioncategorycount">'; 00871 echo '</div>'; 00872 echo '</div>'; 00873 } 00874 00883 function quiz_print_singlequestion_reordertool($question, $returnurl, $quiz) { 00884 echo '<div class="singlequestion ' . $question->qtype . '">'; 00885 echo '<label for="s' . $question->id . '">'; 00886 echo print_question_icon($question); 00887 echo ' ' . quiz_question_tostring($question); 00888 echo '</label>'; 00889 echo '<span class="questionpreview">' . 00890 quiz_question_action_icons($quiz, $quiz->cmid, $question, $returnurl) . '</span>'; 00891 echo "</div>\n"; 00892 } 00893 00902 function quiz_print_randomquestion_reordertool(&$question, &$pageurl, &$quiz) { 00903 global $DB, $OUTPUT; 00904 00905 // Load the category, and the number of available questions in it. 00906 if (!$category = $DB->get_record('question_categories', array('id' => $question->category))) { 00907 echo $OUTPUT->notification('Random question category not found!'); 00908 return; 00909 } 00910 $questioncount = count(question_bank::get_qtype( 00911 'random')->get_available_questions_from_category( 00912 $category->id, $question->questiontext == '1', '0')); 00913 00914 $reordercheckboxlabel = '<label for="s' . $question->id . '">'; 00915 $reordercheckboxlabelclose = '</label>'; 00916 00917 echo '<div class="quiz_randomquestion">'; 00918 echo '<div class="randomquestionfromcategory">'; 00919 echo $reordercheckboxlabel; 00920 echo print_question_icon($question); 00921 print_random_option_icon($question); 00922 00923 if ($questioncount == 0) { 00924 echo '<span class="error">'; 00925 print_string('empty', 'quiz'); 00926 echo '</span> '; 00927 } 00928 00929 print_string('random', 'quiz'); 00930 echo ": $reordercheckboxlabelclose</div>"; 00931 00932 echo '<div class="randomquestioncategory">'; 00933 echo $reordercheckboxlabel . $category->name . $reordercheckboxlabelclose; 00934 echo '<span class="questionpreview">'; 00935 echo quiz_question_preview_button($quiz, $question, false); 00936 echo '</span>'; 00937 echo "</div>"; 00938 00939 echo '<div class="randomquestioncategorycount">'; 00940 echo '</div>'; 00941 echo '</div>'; 00942 } 00943 00948 function print_random_option_icon($question) { 00949 global $OUTPUT; 00950 if (!empty($question->questiontext)) { 00951 $icon = 'withsubcat'; 00952 $tooltip = get_string('randomwithsubcat', 'quiz'); 00953 } else { 00954 $icon = 'nosubcat'; 00955 $tooltip = get_string('randomnosubcat', 'quiz'); 00956 } 00957 echo '<img src="' . $OUTPUT->pix_url('i/' . $icon) . '" alt="' . 00958 $tooltip . '" title="' . $tooltip . '" class="uihint" />'; 00959 } 00960 00970 function quiz_question_tostring($question, $showicon = false, 00971 $showquestiontext = true, $return = true) { 00972 global $COURSE; 00973 $result = ''; 00974 $result .= '<span class="questionname">'; 00975 if ($showicon) { 00976 $result .= print_question_icon($question, true); 00977 echo ' '; 00978 } 00979 $result .= shorten_text(format_string($question->name), 200) . '</span>'; 00980 if ($showquestiontext) { 00981 $formatoptions = new stdClass(); 00982 $formatoptions->noclean = true; 00983 $formatoptions->para = false; 00984 $questiontext = strip_tags(format_text($question->questiontext, 00985 $question->questiontextformat, 00986 $formatoptions, $COURSE->id)); 00987 $questiontext = shorten_text($questiontext, 200); 00988 $result .= '<span class="questiontext">'; 00989 if (!empty($questiontext)) { 00990 $result .= $questiontext; 00991 } else { 00992 $result .= '<span class="error">'; 00993 $result .= get_string('questiontextisempty', 'quiz'); 00994 $result .= '</span>'; 00995 } 00996 $result .= '</span>'; 00997 } 00998 if ($return) { 00999 return $result; 01000 } else { 01001 echo $result; 01002 } 01003 } 01004 01011 class question_bank_add_to_quiz_action_column extends question_bank_action_column_base { 01012 protected $stradd; 01013 01014 public function init() { 01015 parent::init(); 01016 $this->stradd = get_string('addtoquiz', 'quiz'); 01017 } 01018 01019 public function get_name() { 01020 return 'addtoquizaction'; 01021 } 01022 01023 protected function display_content($question, $rowclasses) { 01024 // for RTL languages: switch right and left arrows 01025 if (right_to_left()) { 01026 $movearrow = 't/removeright'; 01027 } else { 01028 $movearrow = 't/moveleft'; 01029 } 01030 $this->print_icon($movearrow, $this->stradd, $this->qbank->add_to_quiz_url($question->id)); 01031 } 01032 01033 public function get_required_fields() { 01034 return array('q.id'); 01035 } 01036 } 01037 01044 class question_bank_question_name_text_column extends question_bank_question_name_column { 01045 public function get_name() { 01046 return 'questionnametext'; 01047 } 01048 01049 protected function display_content($question, $rowclasses) { 01050 echo '<div>'; 01051 $labelfor = $this->label_for($question); 01052 if ($labelfor) { 01053 echo '<label for="' . $labelfor . '">'; 01054 } 01055 echo quiz_question_tostring($question, false, true, true); 01056 if ($labelfor) { 01057 echo '</label>'; 01058 } 01059 echo '</div>'; 01060 } 01061 01062 public function get_required_fields() { 01063 $fields = parent::get_required_fields(); 01064 $fields[] = 'q.questiontext'; 01065 $fields[] = 'q.questiontextformat'; 01066 return $fields; 01067 } 01068 } 01069 01076 class quiz_question_bank_view extends question_bank_view { 01077 protected $quizhasattempts = false; 01079 protected $quiz = false; 01080 01089 public function __construct($contexts, $pageurl, $course, $cm, $quiz) { 01090 parent::__construct($contexts, $pageurl, $course, $cm); 01091 $this->quiz = $quiz; 01092 } 01093 01094 protected function known_field_types() { 01095 $types = parent::known_field_types(); 01096 $types[] = new question_bank_add_to_quiz_action_column($this); 01097 $types[] = new question_bank_question_name_text_column($this); 01098 return $types; 01099 } 01100 01101 protected function wanted_columns() { 01102 return array('addtoquizaction', 'checkbox', 'qtype', 'questionnametext', 01103 'editaction', 'previewaction'); 01104 } 01105 01106 protected function default_sort() { 01107 return array('qtype' => 1, 'questionnametext' => 1); 01108 } 01109 01116 public function set_quiz_has_attempts($quizhasattempts) { 01117 $this->quizhasattempts = $quizhasattempts; 01118 if ($quizhasattempts && isset($this->visiblecolumns['addtoquizaction'])) { 01119 unset($this->visiblecolumns['addtoquizaction']); 01120 } 01121 } 01122 01123 public function preview_question_url($question) { 01124 return quiz_question_preview_url($this->quiz, $question); 01125 } 01126 01127 public function add_to_quiz_url($questionid) { 01128 global $CFG; 01129 $params = $this->baseurl->params(); 01130 $params['addquestion'] = $questionid; 01131 $params['sesskey'] = sesskey(); 01132 return new moodle_url('/mod/quiz/edit.php', $params); 01133 } 01134 01135 public function display($tabname, $page, $perpage, $cat, 01136 $recurse, $showhidden, $showquestiontext) { 01137 global $OUTPUT; 01138 if ($this->process_actions_needing_ui()) { 01139 return; 01140 } 01141 01142 // Display the current category. 01143 if (!$category = $this->get_current_category($cat)) { 01144 return; 01145 } 01146 $this->print_category_info($category); 01147 01148 echo $OUTPUT->box_start('generalbox questionbank'); 01149 01150 $this->display_category_form($this->contexts->having_one_edit_tab_cap($tabname), 01151 $this->baseurl, $cat); 01152 01153 // continues with list of questions 01154 $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname), 01155 $this->baseurl, $cat, $this->cm, $recurse, $page, 01156 $perpage, $showhidden, $showquestiontext, 01157 $this->contexts->having_cap('moodle/question:add')); 01158 01159 $this->display_options($recurse, $showhidden, $showquestiontext); 01160 echo $OUTPUT->box_end(); 01161 } 01162 01163 protected function print_choose_category_message($categoryandcontext) { 01164 global $OUTPUT; 01165 echo $OUTPUT->box_start('generalbox questionbank'); 01166 $this->display_category_form($this->contexts->having_one_edit_tab_cap('edit'), 01167 $this->baseurl, $categoryandcontext); 01168 echo "<p style=\"text-align:center;\"><b>"; 01169 print_string('selectcategoryabove', 'question'); 01170 echo "</b></p>"; 01171 echo $OUTPUT->box_end(); 01172 } 01173 01174 protected function print_category_info($category) { 01175 $formatoptions = new stdClass(); 01176 $formatoptions->noclean = true; 01177 $strcategory = get_string('category', 'quiz'); 01178 echo '<div class="categoryinfo"><div class="categorynamefieldcontainer">' . 01179 $strcategory; 01180 echo ': <span class="categorynamefield">'; 01181 echo shorten_text(strip_tags(format_string($category->name)), 60); 01182 echo '</span></div><div class="categoryinfofieldcontainer">' . 01183 '<span class="categoryinfofield">'; 01184 echo shorten_text(strip_tags(format_text($category->info, $category->infoformat, 01185 $formatoptions, $this->course->id)), 200); 01186 echo '</span></div></div>'; 01187 } 01188 01189 protected function display_options($recurse, $showhidden, $showquestiontext) { 01190 echo '<form method="get" action="edit.php" id="displayoptions">'; 01191 echo "<fieldset class='invisiblefieldset'>"; 01192 echo html_writer::input_hidden_params($this->baseurl, 01193 array('recurse', 'showhidden', 'qbshowtext')); 01194 $this->display_category_form_checkbox('recurse', $recurse, 01195 get_string('includesubcategories', 'question')); 01196 $this->display_category_form_checkbox('showhidden', $showhidden, 01197 get_string('showhidden', 'question')); 01198 echo '<noscript><div class="centerpara"><input type="submit" value="' . 01199 get_string('go') . '" />'; 01200 echo '</div></noscript></fieldset></form>'; 01201 } 01202 } 01203 01214 function quiz_print_grading_form($quiz, $pageurl, $tabindex) { 01215 global $OUTPUT; 01216 $strsave = get_string('save', 'quiz'); 01217 echo '<form method="post" action="edit.php" class="quizsavegradesform"><div>'; 01218 echo '<fieldset class="invisiblefieldset" style="display: block;">'; 01219 echo "<input type=\"hidden\" name=\"sesskey\" value=\"" . sesskey() . "\" />"; 01220 echo html_writer::input_hidden_params($pageurl); 01221 $a = '<input type="text" id="inputmaxgrade" name="maxgrade" size="' . 01222 ($quiz->decimalpoints + 2) . '" tabindex="' . $tabindex 01223 . '" value="' . quiz_format_grade($quiz, $quiz->grade) . '" />'; 01224 echo '<label for="inputmaxgrade">' . get_string('maximumgradex', '', $a) . "</label>"; 01225 echo '<input type="hidden" name="savechanges" value="save" />'; 01226 echo '<input type="submit" value="' . $strsave . '" />'; 01227 echo '</fieldset>'; 01228 echo "</div></form>\n"; 01229 return $tabindex + 1; 01230 } 01231 01237 function quiz_print_status_bar($quiz) { 01238 global $CFG; 01239 01240 $bits = array(); 01241 01242 $bits[] = html_writer::tag('span', 01243 get_string('totalpointsx', 'quiz', quiz_format_grade($quiz, $quiz->sumgrades)), 01244 array('class' => 'totalpoints')); 01245 01246 $bits[] = html_writer::tag('span', 01247 get_string('numquestionsx', 'quiz', quiz_number_of_questions_in_quiz($quiz->questions)), 01248 array('class' => 'numberofquestions')); 01249 01250 $timenow = time(); 01251 01252 // Exact open and close dates for the tool-tip. 01253 $dates = array(); 01254 if ($quiz->timeopen > 0) { 01255 if ($timenow > $quiz->timeopen) { 01256 $dates[] = get_string('quizopenedon', 'quiz', userdate($quiz->timeopen)); 01257 } else { 01258 $dates[] = get_string('quizwillopen', 'quiz', userdate($quiz->timeopen)); 01259 } 01260 } 01261 if ($quiz->timeclose > 0) { 01262 if ($timenow > $quiz->timeclose) { 01263 $dates[] = get_string('quizclosed', 'quiz', userdate($quiz->timeclose)); 01264 } else { 01265 $dates[] = get_string('quizcloseson', 'quiz', userdate($quiz->timeclose)); 01266 } 01267 } 01268 if (empty($dates)) { 01269 $dates[] = get_string('alwaysavailable', 'quiz'); 01270 } 01271 $tooltip = implode(', ', $dates);; 01272 01273 // Brief summary on the page. 01274 if ($timenow < $quiz->timeopen) { 01275 $currentstatus = get_string('quizisclosedwillopen', 'quiz', 01276 userdate($quiz->timeopen, get_string('strftimedatetimeshort', 'langconfig'))); 01277 } else if ($quiz->timeclose && $timenow <= $quiz->timeclose) { 01278 $currentstatus = get_string('quizisopenwillclose', 'quiz', 01279 userdate($quiz->timeclose, get_string('strftimedatetimeshort', 'langconfig'))); 01280 } else if ($quiz->timeclose && $timenow > $quiz->timeclose) { 01281 $currentstatus = get_string('quizisclosed', 'quiz'); 01282 } else { 01283 $currentstatus = get_string('quizisopen', 'quiz'); 01284 } 01285 01286 $bits[] = html_writer::tag('span', $currentstatus, 01287 array('class' => 'quizopeningstatus', 'title' => implode(', ', $dates))); 01288 01289 echo html_writer::tag('div', implode(' | ', $bits), array('class' => 'statusbar')); 01290 }