|
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 00027 defined('MOODLE_INTERNAL') || die(); 00028 00029 require_once($CFG->libdir . '/questionlib.php'); 00030 00031 define('DEFAULT_QUESTIONS_PER_PAGE', 20); 00032 00033 function get_module_from_cmid($cmid) { 00034 global $CFG, $DB; 00035 if (!$cmrec = $DB->get_record_sql("SELECT cm.*, md.name as modname 00036 FROM {course_modules} cm, 00037 {modules} md 00038 WHERE cm.id = ? AND 00039 md.id = cm.module", array($cmid))){ 00040 print_error('invalidcoursemodule'); 00041 } elseif (!$modrec =$DB->get_record($cmrec->modname, array('id' => $cmrec->instance))) { 00042 print_error('invalidcoursemodule'); 00043 } 00044 $modrec->instance = $modrec->id; 00045 $modrec->cmid = $cmrec->id; 00046 $cmrec->name = $modrec->name; 00047 00048 return array($modrec, $cmrec); 00049 } 00059 function get_questions_category( $category, $noparent=false, $recurse=true, $export=true ) { 00060 global $DB; 00061 00062 // questions will be added to an array 00063 $qresults = array(); 00064 00065 // build sql bit for $noparent 00066 $npsql = ''; 00067 if ($noparent) { 00068 $npsql = " and parent='0' "; 00069 } 00070 00071 // Get list of categories 00072 if ($recurse) { 00073 $categorylist = question_categorylist($category->id); 00074 } else { 00075 $categorylist = array($category->id); 00076 } 00077 00078 // get the list of questions for the category 00079 list($usql, $params) = $DB->get_in_or_equal($categorylist); 00080 if ($questions = $DB->get_records_select('question', "category $usql $npsql", $params, 'qtype, name')) { 00081 00082 // iterate through questions, getting stuff we need 00083 foreach($questions as $question) { 00084 $question->export_process = $export; 00085 question_bank::get_qtype($question->qtype)->get_question_options($question); 00086 $qresults[] = $question; 00087 } 00088 } 00089 00090 return $qresults; 00091 } 00092 00097 function question_is_only_toplevel_category_in_context($categoryid) { 00098 global $DB; 00099 return 1 == $DB->count_records_sql(" 00100 SELECT count(*) 00101 FROM {question_categories} c1, 00102 {question_categories} c2 00103 WHERE c2.id = ? 00104 AND c1.contextid = c2.contextid 00105 AND c1.parent = 0 AND c2.parent = 0", array($categoryid)); 00106 } 00107 00113 function question_can_delete_cat($todelete) { 00114 global $DB; 00115 if (question_is_only_toplevel_category_in_context($todelete)) { 00116 print_error('cannotdeletecate', 'question'); 00117 } else { 00118 $contextid = $DB->get_field('question_categories', 'contextid', array('id' => $todelete)); 00119 require_capability('moodle/question:managecategory', get_context_instance_by_id($contextid)); 00120 } 00121 } 00122 00123 00130 abstract class question_bank_column_base { 00134 protected $qbank; 00135 00140 public function __construct(question_bank_view $qbank) { 00141 $this->qbank = $qbank; 00142 $this->init(); 00143 } 00144 00149 protected function init() { 00150 } 00151 00152 public function is_extra_row() { 00153 return false; 00154 } 00155 00159 public function display_header() { 00160 echo '<th class="header ' . $this->get_classes() . '" scope="col">'; 00161 $sortable = $this->is_sortable(); 00162 $name = $this->get_name(); 00163 $title = $this->get_title(); 00164 $tip = $this->get_title_tip(); 00165 if (is_array($sortable)) { 00166 if ($title) { 00167 echo '<div class="title">' . $title . '</div>'; 00168 } 00169 $links = array(); 00170 foreach ($sortable as $subsort => $details) { 00171 $links[] = $this->make_sort_link($name . '_' . $subsort, 00172 $details['title'], '', !empty($details['reverse'])); 00173 } 00174 echo '<div class="sorters">' . implode(' / ', $links) . '</div>'; 00175 } else if ($sortable) { 00176 echo $this->make_sort_link($name, $title, $tip); 00177 } else { 00178 if ($tip) { 00179 echo '<span title="' . $tip . '">'; 00180 } 00181 echo $title; 00182 if ($tip) { 00183 echo '</span>'; 00184 } 00185 } 00186 echo "</th>\n"; 00187 } 00188 00194 protected abstract function get_title(); 00195 00200 protected function get_title_tip() { 00201 return ''; 00202 } 00203 00212 protected function make_sort_link($sort, $title, $tip, $defaultreverse = false) { 00213 $currentsort = $this->qbank->get_primary_sort_order($sort); 00214 $newsortreverse = $defaultreverse; 00215 if ($currentsort) { 00216 $newsortreverse = $currentsort > 0; 00217 } 00218 if (!$tip) { 00219 $tip = $title; 00220 } 00221 if ($newsortreverse) { 00222 $tip = get_string('sortbyxreverse', '', $tip); 00223 } else { 00224 $tip = get_string('sortbyx', '', $tip); 00225 } 00226 $link = '<a href="' . $this->qbank->new_sort_url($sort, $newsortreverse) . '" title="' . $tip . '">'; 00227 $link .= $title; 00228 if ($currentsort) { 00229 $link .= $this->get_sort_icon($currentsort < 0); 00230 } 00231 $link .= '</a>'; 00232 return $link; 00233 } 00234 00240 protected function get_sort_icon($reverse) { 00241 global $OUTPUT; 00242 if ($reverse) { 00243 return ' <img src="' . $OUTPUT->pix_url('t/up') . '" alt="' . get_string('desc') . '" />'; 00244 } else { 00245 return ' <img src="' . $OUTPUT->pix_url('t/down') . '" alt="' . get_string('asc') . '" />'; 00246 } 00247 } 00248 00254 public function display($question, $rowclasses) { 00255 $this->display_start($question, $rowclasses); 00256 $this->display_content($question, $rowclasses); 00257 $this->display_end($question, $rowclasses); 00258 } 00259 00260 protected function display_start($question, $rowclasses) { 00261 echo '<td class="' . $this->get_classes() . '">'; 00262 } 00263 00267 protected function get_classes() { 00268 $classes = $this->get_extra_classes(); 00269 $classes[] = $this->get_name(); 00270 return implode(' ', $classes); 00271 } 00272 00278 public abstract function get_name(); 00279 00283 public function get_extra_classes() { 00284 return array(); 00285 } 00286 00292 protected abstract function display_content($question, $rowclasses); 00293 00294 protected function display_end($question, $rowclasses) { 00295 echo "</td>\n"; 00296 } 00297 00312 public function get_extra_joins() { 00313 return array(); 00314 } 00315 00320 public function get_required_fields() { 00321 return array(); 00322 } 00323 00337 public function is_sortable() { 00338 return false; 00339 } 00340 00347 protected function sortorder($reverse) { 00348 if ($reverse) { 00349 return ' DESC'; 00350 } else { 00351 return ' ASC'; 00352 } 00353 } 00354 00361 public function sort_expression($reverse, $subsort) { 00362 $sortable = $this->is_sortable(); 00363 if (is_array($sortable)) { 00364 if (array_key_exists($subsort, $sortable)) { 00365 return $sortable[$subsort]['field'] . $this->sortorder($reverse, !empty($sortable[$subsort]['reverse'])); 00366 } else { 00367 throw new coding_exception('Unexpected $subsort type: ' . $subsort); 00368 } 00369 } else if ($sortable) { 00370 return $sortable . $this->sortorder($reverse); 00371 } else { 00372 throw new coding_exception('sort_expression called on a non-sortable column.'); 00373 } 00374 } 00375 } 00376 00377 00384 class question_bank_checkbox_column extends question_bank_column_base { 00385 protected $strselect; 00386 protected $firstrow = true; 00387 00388 public function init() { 00389 $this->strselect = get_string('select'); 00390 } 00391 00392 public function get_name() { 00393 return 'checkbox'; 00394 } 00395 00396 protected function get_title() { 00397 return '<input type="checkbox" disabled="disabled" id="qbheadercheckbox" />'; 00398 } 00399 00400 protected function get_title_tip() { 00401 return get_string('selectquestionsforbulk', 'question'); 00402 } 00403 00404 protected function display_content($question, $rowclasses) { 00405 global $PAGE; 00406 echo '<input title="' . $this->strselect . '" type="checkbox" name="q' . 00407 $question->id . '" id="checkq' . $question->id . '" value="1"/>'; 00408 if ($this->firstrow) { 00409 $PAGE->requires->js_function_call('question_bank.init_checkbox_column', array(get_string('selectall'), 00410 get_string('deselectall'), 'checkq' . $question->id)); 00411 $this->firstrow = false; 00412 } 00413 } 00414 00415 public function get_required_fields() { 00416 return array('q.id'); 00417 } 00418 } 00419 00420 00427 class question_bank_question_type_column extends question_bank_column_base { 00428 public function get_name() { 00429 return 'qtype'; 00430 } 00431 00432 protected function get_title() { 00433 return get_string('qtypeveryshort', 'question'); 00434 } 00435 00436 protected function get_title_tip() { 00437 return get_string('questiontype', 'question'); 00438 } 00439 00440 protected function display_content($question, $rowclasses) { 00441 echo print_question_icon($question); 00442 } 00443 00444 public function get_required_fields() { 00445 return array('q.qtype'); 00446 } 00447 00448 public function is_sortable() { 00449 return 'q.qtype'; 00450 } 00451 } 00452 00453 00460 class question_bank_question_name_column extends question_bank_column_base { 00461 protected $checkboxespresent = null; 00462 00463 public function get_name() { 00464 return 'questionname'; 00465 } 00466 00467 protected function get_title() { 00468 return get_string('question'); 00469 } 00470 00471 protected function label_for($question) { 00472 if (is_null($this->checkboxespresent)) { 00473 $this->checkboxespresent = $this->qbank->has_column('checkbox'); 00474 } 00475 if ($this->checkboxespresent) { 00476 return 'checkq' . $question->id; 00477 } else { 00478 return ''; 00479 } 00480 } 00481 00482 protected function display_content($question, $rowclasses) { 00483 $labelfor = $this->label_for($question); 00484 if ($labelfor) { 00485 echo '<label for="' . $labelfor . '">'; 00486 } 00487 echo format_string($question->name); 00488 if ($labelfor) { 00489 echo '</label>'; 00490 } 00491 } 00492 00493 public function get_required_fields() { 00494 return array('q.id', 'q.name'); 00495 } 00496 00497 public function is_sortable() { 00498 return 'q.name'; 00499 } 00500 } 00501 00502 00509 class question_bank_creator_name_column extends question_bank_column_base { 00510 public function get_name() { 00511 return 'creatorname'; 00512 } 00513 00514 protected function get_title() { 00515 return get_string('createdby', 'question'); 00516 } 00517 00518 protected function display_content($question, $rowclasses) { 00519 if (!empty($question->creatorfirstname) && !empty($question->creatorlastname)) { 00520 $u = new stdClass(); 00521 $u->firstname = $question->creatorfirstname; 00522 $u->lastname = $question->creatorlastname; 00523 echo fullname($u); 00524 } 00525 } 00526 00527 public function get_extra_joins() { 00528 return array('uc' => 'LEFT JOIN {user} uc ON uc.id = q.createdby'); 00529 } 00530 00531 public function get_required_fields() { 00532 return array('uc.firstname AS creatorfirstname', 'uc.lastname AS creatorlastname'); 00533 } 00534 00535 public function is_sortable() { 00536 return array( 00537 'firstname' => array('field' => 'uc.firstname', 'title' => get_string('firstname')), 00538 'lastname' => array('field' => 'uc.lastname', 'title' => get_string('lastname')), 00539 ); 00540 } 00541 } 00542 00543 00550 class question_bank_modifier_name_column extends question_bank_column_base { 00551 public function get_name() { 00552 return 'modifiername'; 00553 } 00554 00555 protected function get_title() { 00556 return get_string('lastmodifiedby', 'question'); 00557 } 00558 00559 protected function display_content($question, $rowclasses) { 00560 if (!empty($question->modifierfirstname) && !empty($question->modifierlastname)) { 00561 $u = new stdClass(); 00562 $u->firstname = $question->modifierfirstname; 00563 $u->lastname = $question->modifierlastname; 00564 echo fullname($u); 00565 } 00566 } 00567 00568 public function get_extra_joins() { 00569 return array('um' => 'LEFT JOIN {user} um ON um.id = q.modifiedby'); 00570 } 00571 00572 public function get_required_fields() { 00573 return array('um.firstname AS modifierfirstname', 'um.lastname AS modifierlastname'); 00574 } 00575 00576 public function is_sortable() { 00577 return array( 00578 'firstname' => array('field' => 'um.firstname', 'title' => get_string('firstname')), 00579 'lastname' => array('field' => 'um.lastname', 'title' => get_string('lastname')), 00580 ); 00581 } 00582 } 00583 00584 00591 abstract class question_bank_action_column_base extends question_bank_column_base { 00592 00593 protected function get_title() { 00594 return ' '; 00595 } 00596 00597 public function get_extra_classes() { 00598 return array('iconcol'); 00599 } 00600 00601 protected function print_icon($icon, $title, $url) { 00602 global $OUTPUT; 00603 echo '<a title="' . $title . '" href="' . $url . '"> 00604 <img src="' . $OUTPUT->pix_url($icon) . '" class="iconsmall" alt="' . $title . '" /></a>'; 00605 } 00606 00607 public function get_required_fields() { 00608 return array('q.id'); 00609 } 00610 } 00611 00612 00619 class question_bank_edit_action_column extends question_bank_action_column_base { 00620 protected $stredit; 00621 protected $strview; 00622 00623 public function init() { 00624 parent::init(); 00625 $this->stredit = get_string('edit'); 00626 $this->strview = get_string('view'); 00627 } 00628 00629 public function get_name() { 00630 return 'editaction'; 00631 } 00632 00633 protected function display_content($question, $rowclasses) { 00634 if (question_has_capability_on($question, 'edit') || 00635 question_has_capability_on($question, 'move')) { 00636 $this->print_icon('t/edit', $this->stredit, $this->qbank->edit_question_url($question->id)); 00637 } else { 00638 $this->print_icon('i/info', $this->strview, $this->qbank->edit_question_url($question->id)); 00639 } 00640 } 00641 } 00642 00643 00650 class question_bank_preview_action_column extends question_bank_action_column_base { 00651 protected $strpreview; 00652 00653 public function init() { 00654 parent::init(); 00655 $this->strpreview = get_string('preview'); 00656 } 00657 00658 public function get_name() { 00659 return 'previewaction'; 00660 } 00661 00662 protected function display_content($question, $rowclasses) { 00663 global $OUTPUT; 00664 if (question_has_capability_on($question, 'use')) { 00665 // Build the icon. 00666 $image = $OUTPUT->pix_icon('t/preview', $this->strpreview); 00667 00668 $link = $this->qbank->preview_question_url($question); 00669 $action = new popup_action('click', $link, 'questionpreview', 00670 question_preview_popup_params()); 00671 00672 echo $OUTPUT->action_link($link, $image, $action, array('title' => $this->strpreview)); 00673 } 00674 } 00675 00676 public function get_required_fields() { 00677 return array('q.id'); 00678 } 00679 } 00680 00681 00688 class question_bank_move_action_column extends question_bank_action_column_base { 00689 protected $strmove; 00690 00691 public function init() { 00692 parent::init(); 00693 $this->strmove = get_string('move'); 00694 } 00695 00696 public function get_name() { 00697 return 'moveaction'; 00698 } 00699 00700 protected function display_content($question, $rowclasses) { 00701 if (question_has_capability_on($question, 'move')) { 00702 $this->print_icon('t/move', $this->strmove, $this->qbank->move_question_url($question->id)); 00703 } 00704 } 00705 } 00706 00707 00714 class question_bank_delete_action_column extends question_bank_action_column_base { 00715 protected $strdelete; 00716 protected $strrestore; 00717 00718 public function init() { 00719 parent::init(); 00720 $this->strdelete = get_string('delete'); 00721 $this->strrestore = get_string('restore'); 00722 } 00723 00724 public function get_name() { 00725 return 'deleteaction'; 00726 } 00727 00728 protected function display_content($question, $rowclasses) { 00729 if (question_has_capability_on($question, 'edit')) { 00730 if ($question->hidden) { 00731 $url = new moodle_url($this->qbank->base_url(), array('unhide' => $question->id, 'sesskey'=>sesskey())); 00732 $this->print_icon('t/restore', $this->strrestore, $url); 00733 } else { 00734 $url = new moodle_url($this->qbank->base_url(), array('deleteselected' => $question->id, 'q' . $question->id => 1, 'sesskey'=>sesskey())); 00735 $this->print_icon('t/delete', $this->strdelete, $url); 00736 } 00737 } 00738 } 00739 00740 public function get_required_fields() { 00741 return array('q.id', 'q.hidden'); 00742 } 00743 } 00744 00751 abstract class question_bank_row_base extends question_bank_column_base { 00752 public function is_extra_row() { 00753 return true; 00754 } 00755 00756 protected function display_start($question, $rowclasses) { 00757 if ($rowclasses) { 00758 echo '<tr class="' . $rowclasses . '">' . "\n"; 00759 } else { 00760 echo "<tr>\n"; 00761 } 00762 echo '<td colspan="' . $this->qbank->get_column_count() . '" class="' . $this->get_name() . '">'; 00763 } 00764 00765 protected function display_end($question, $rowclasses) { 00766 echo "</td></tr>\n"; 00767 } 00768 } 00769 00776 class question_bank_question_text_row extends question_bank_row_base { 00777 protected $formatoptions; 00778 00779 protected function init() { 00780 $this->formatoptions = new stdClass(); 00781 $this->formatoptions->noclean = true; 00782 $this->formatoptions->para = false; 00783 } 00784 00785 public function get_name() { 00786 return 'questiontext'; 00787 } 00788 00789 protected function get_title() { 00790 return get_string('questiontext', 'question'); 00791 } 00792 00793 protected function display_content($question, $rowclasses) { 00794 $text = question_rewrite_questiontext_preview_urls($question->questiontext, 00795 $question->contextid, 'question', $question->id); 00796 $text = format_text($text, $question->questiontextformat, 00797 $this->formatoptions); 00798 if ($text == '') { 00799 $text = ' '; 00800 } 00801 echo $text; 00802 } 00803 00804 public function get_extra_joins() { 00805 return array('qc' => 'JOIN {question_categories} qc ON qc.id = q.category'); 00806 } 00807 00808 public function get_required_fields() { 00809 return array('q.id', 'q.questiontext', 'q.questiontextformat', 'qc.contextid'); 00810 } 00811 } 00812 00833 class question_bank_view { 00834 const MAX_SORTS = 3; 00835 00836 protected $baseurl; 00837 protected $editquestionurl; 00838 protected $quizorcourseid; 00839 protected $contexts; 00840 protected $cm; 00841 protected $course; 00842 protected $knowncolumntypes; 00843 protected $visiblecolumns; 00844 protected $extrarows; 00845 protected $requiredcolumns; 00846 protected $sort; 00847 protected $lastchangedid; 00848 protected $countsql; 00849 protected $loadsql; 00850 protected $sqlparams; 00851 00859 public function __construct($contexts, $pageurl, $course, $cm = null) { 00860 global $CFG, $PAGE; 00861 00862 $this->contexts = $contexts; 00863 $this->baseurl = $pageurl; 00864 $this->course = $course; 00865 $this->cm = $cm; 00866 00867 if (!empty($cm) && $cm->modname == 'quiz') { 00868 $this->quizorcourseid = '&quizid=' . $cm->instance; 00869 } else { 00870 $this->quizorcourseid = '&courseid=' .$this->course->id; 00871 } 00872 00873 // Create the url of the new question page to forward to. 00874 $returnurl = str_replace($CFG->wwwroot, '', $pageurl->out(false)); 00875 $this->editquestionurl = new moodle_url('/question/question.php', 00876 array('returnurl' => $returnurl)); 00877 if ($cm !== null){ 00878 $this->editquestionurl->param('cmid', $cm->id); 00879 } else { 00880 $this->editquestionurl->param('courseid', $this->course->id); 00881 } 00882 00883 $this->lastchangedid = optional_param('lastchanged',0,PARAM_INT); 00884 00885 $this->init_column_types(); 00886 $this->init_columns($this->wanted_columns()); 00887 $this->init_sort(); 00888 00889 $PAGE->requires->yui2_lib('container'); 00890 } 00891 00892 protected function wanted_columns() { 00893 $columns = array('checkbox', 'qtype', 'questionname', 'editaction', 00894 'previewaction', 'moveaction', 'deleteaction', 'creatorname', 00895 'modifiername'); 00896 if (optional_param('qbshowtext', false, PARAM_BOOL)) { 00897 $columns[] = 'questiontext'; 00898 } 00899 return $columns; 00900 } 00901 00902 protected function known_field_types() { 00903 return array( 00904 new question_bank_checkbox_column($this), 00905 new question_bank_question_type_column($this), 00906 new question_bank_question_name_column($this), 00907 new question_bank_creator_name_column($this), 00908 new question_bank_modifier_name_column($this), 00909 new question_bank_edit_action_column($this), 00910 new question_bank_preview_action_column($this), 00911 new question_bank_move_action_column($this), 00912 new question_bank_delete_action_column($this), 00913 new question_bank_question_text_row($this), 00914 ); 00915 } 00916 00917 protected function init_column_types() { 00918 $this->knowncolumntypes = array(); 00919 foreach ($this->known_field_types() as $col) { 00920 $this->knowncolumntypes[$col->get_name()] = $col; 00921 } 00922 } 00923 00924 protected function init_columns($wanted) { 00925 $this->visiblecolumns = array(); 00926 $this->extrarows = array(); 00927 foreach ($wanted as $colname) { 00928 if (!isset($this->knowncolumntypes[$colname])) { 00929 throw new coding_exception('Unknown column type ' . $colname . ' requested in init columns.'); 00930 } 00931 $column = $this->knowncolumntypes[$colname]; 00932 if ($column->is_extra_row()) { 00933 $this->extrarows[$colname] = $column; 00934 } else { 00935 $this->visiblecolumns[$colname] = $column; 00936 } 00937 } 00938 $this->requiredcolumns = array_merge($this->visiblecolumns, $this->extrarows); 00939 } 00940 00945 public function has_column($colname) { 00946 return isset($this->visiblecolumns[$colname]); 00947 } 00948 00952 public function get_column_count() { 00953 return count($this->visiblecolumns); 00954 } 00955 00956 public function get_courseid() { 00957 return $this->course->id; 00958 } 00959 00960 protected function init_sort() { 00961 $this->init_sort_from_params(); 00962 if (empty($this->sort)) { 00963 $this->sort = $this->default_sort(); 00964 } 00965 } 00966 00973 protected function parse_subsort($sort) { 00975 if (strpos($sort, '_') !== false) { 00976 list($colname, $subsort) = explode('_', $sort, 2); 00977 } else { 00978 $colname = $sort; 00979 $subsort = ''; 00980 } 00982 if (!isset($this->knowncolumntypes[$colname]) || !$this->knowncolumntypes[$colname]->is_sortable()) { 00983 for ($i = 1; $i <= question_bank_view::MAX_SORTS; $i++) { 00984 $this->baseurl->remove_params('qbs' . $i); 00985 } 00986 throw new moodle_exception('unknownsortcolumn', '', $link = $this->baseurl->out(), $colname); 00987 } 00989 if ($subsort) { 00990 $subsorts = $this->knowncolumntypes[$colname]->is_sortable(); 00991 if (!is_array($subsorts) || !isset($subsorts[$subsort])) { 00992 throw new moodle_exception('unknownsortcolumn', '', $link = $this->baseurl->out(), $sort); 00993 } 00994 } 00995 return array($colname, $subsort); 00996 } 00997 00998 protected function init_sort_from_params() { 00999 $this->sort = array(); 01000 for ($i = 1; $i <= question_bank_view::MAX_SORTS; $i++) { 01001 if (!$sort = optional_param('qbs' . $i, '', PARAM_ALPHAEXT)) { 01002 break; 01003 } 01004 // Work out the appropriate order. 01005 $order = 1; 01006 if ($sort[0] == '-') { 01007 $order = -1; 01008 $sort = substr($sort, 1); 01009 if (!$sort) { 01010 break; 01011 } 01012 } 01013 // Deal with subsorts. 01014 list($colname, $subsort) = $this->parse_subsort($sort); 01015 $this->requiredcolumns[$colname] = $this->knowncolumntypes[$colname]; 01016 $this->sort[$sort] = $order; 01017 } 01018 } 01019 01020 protected function sort_to_params($sorts) { 01021 $params = array(); 01022 $i = 0; 01023 foreach ($sorts as $sort => $order) { 01024 $i += 1; 01025 if ($order < 0) { 01026 $sort = '-' . $sort; 01027 } 01028 $params['qbs' . $i] = $sort; 01029 } 01030 return $params; 01031 } 01032 01033 protected function default_sort() { 01034 return array('qtype' => 1, 'questionname' => 1); 01035 } 01036 01041 public function get_primary_sort_order($sort) { 01042 $order = reset($this->sort); 01043 $primarysort = key($this->sort); 01044 if ($sort == $primarysort) { 01045 return $order; 01046 } else { 01047 return 0; 01048 } 01049 } 01050 01057 public function new_sort_url($sort, $newsortreverse) { 01058 if ($newsortreverse) { 01059 $order = -1; 01060 } else { 01061 $order = 1; 01062 } 01063 // Tricky code to add the new sort at the start, removing it from where it was before, if it was present. 01064 $newsort = array_reverse($this->sort); 01065 if (isset($newsort[$sort])) { 01066 unset($newsort[$sort]); 01067 } 01068 $newsort[$sort] = $order; 01069 $newsort = array_reverse($newsort); 01070 if (count($newsort) > question_bank_view::MAX_SORTS) { 01071 $newsort = array_slice($newsort, 0, question_bank_view::MAX_SORTS, true); 01072 } 01073 return $this->baseurl->out(true, $this->sort_to_params($newsort)); 01074 } 01075 01076 protected function build_query_sql($category, $recurse, $showhidden) { 01077 global $DB; 01078 01080 $joins = array(); 01081 foreach ($this->requiredcolumns as $column) { 01082 $extrajoins = $column->get_extra_joins(); 01083 foreach ($extrajoins as $prefix => $join) { 01084 if (isset($joins[$prefix]) && $joins[$prefix] != $join) { 01085 throw new coding_exception('Join ' . $join . ' conflicts with previous join ' . $joins[$prefix]); 01086 } 01087 $joins[$prefix] = $join; 01088 } 01089 } 01090 01092 $fields = array('q.hidden', 'q.category'); 01093 foreach ($this->visiblecolumns as $column) { 01094 $fields = array_merge($fields, $column->get_required_fields()); 01095 } 01096 foreach ($this->extrarows as $row) { 01097 $fields = array_merge($fields, $row->get_required_fields()); 01098 } 01099 $fields = array_unique($fields); 01100 01102 $sorts = array(); 01103 foreach ($this->sort as $sort => $order) { 01104 list($colname, $subsort) = $this->parse_subsort($sort); 01105 $sorts[] = $this->requiredcolumns[$colname]->sort_expression($order < 0, $subsort); 01106 } 01107 01109 $tests = array('q.parent = 0'); 01110 01111 if (!$showhidden) { 01112 $tests[] = 'q.hidden = 0'; 01113 } 01114 01115 if ($recurse) { 01116 $categoryids = question_categorylist($category->id); 01117 } else { 01118 $categoryids = array($category->id); 01119 } 01120 list($catidtest, $params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED, 'cat'); 01121 $tests[] = 'q.category ' . $catidtest; 01122 $this->sqlparams = $params; 01123 01125 $sql = ' FROM {question} q ' . implode(' ', $joins); 01126 $sql .= ' WHERE ' . implode(' AND ', $tests); 01127 $this->countsql = 'SELECT count(1)' . $sql; 01128 $this->loadsql = 'SELECT ' . implode(', ', $fields) . $sql . ' ORDER BY ' . implode(', ', $sorts); 01129 $this->sqlparams = $params; 01130 } 01131 01132 protected function get_question_count() { 01133 global $DB; 01134 return $DB->count_records_sql($this->countsql, $this->sqlparams); 01135 } 01136 01137 protected function load_page_questions($page, $perpage) { 01138 global $DB; 01139 $questions = $DB->get_recordset_sql($this->loadsql, $this->sqlparams, $page*$perpage, $perpage); 01140 if (!$questions->valid()) { 01142 $questions = $DB->get_recordset_sql($this->loadsql, $this->sqlparams, 0, $perpage); 01143 } 01144 return $questions; 01145 } 01146 01147 public function base_url() { 01148 return $this->baseurl; 01149 } 01150 01151 public function edit_question_url($questionid) { 01152 return $this->editquestionurl->out(true, array('id' => $questionid)); 01153 } 01154 01155 public function move_question_url($questionid) { 01156 return $this->editquestionurl->out(true, array('id' => $questionid, 'movecontext' => 1)); 01157 } 01158 01159 public function preview_question_url($question) { 01160 return question_preview_url($question->id, null, null, null, null, 01161 $this->contexts->lowest()); 01162 } 01163 01176 public function display($tabname, $page, $perpage, $cat, 01177 $recurse, $showhidden, $showquestiontext) { 01178 global $PAGE, $OUTPUT; 01179 01180 if ($this->process_actions_needing_ui()) { 01181 return; 01182 } 01183 01184 $PAGE->requires->js('/question/qbank.js'); 01185 01186 // Category selection form 01187 echo $OUTPUT->heading(get_string('questionbank', 'question'), 2); 01188 01189 $this->display_category_form($this->contexts->having_one_edit_tab_cap($tabname), 01190 $this->baseurl, $cat); 01191 $this->display_options($recurse, $showhidden, $showquestiontext); 01192 01193 if (!$category = $this->get_current_category($cat)) { 01194 return; 01195 } 01196 $this->print_category_info($category); 01197 01198 // continues with list of questions 01199 $this->display_question_list($this->contexts->having_one_edit_tab_cap($tabname), 01200 $this->baseurl, $cat, $this->cm, 01201 $recurse, $page, $perpage, $showhidden, $showquestiontext, 01202 $this->contexts->having_cap('moodle/question:add')); 01203 } 01204 01205 protected function print_choose_category_message($categoryandcontext) { 01206 echo "<p style=\"text-align:center;\"><b>"; 01207 print_string('selectcategoryabove', 'question'); 01208 echo "</b></p>"; 01209 } 01210 01211 protected function get_current_category($categoryandcontext) { 01212 global $DB, $OUTPUT; 01213 list($categoryid, $contextid) = explode(',', $categoryandcontext); 01214 if (!$categoryid) { 01215 $this->print_choose_category_message($categoryandcontext); 01216 return false; 01217 } 01218 01219 if (!$category = $DB->get_record('question_categories', 01220 array('id' => $categoryid, 'contextid' => $contextid))) { 01221 echo $OUTPUT->box_start('generalbox questionbank'); 01222 echo $OUTPUT->notification('Category not found!'); 01223 echo $OUTPUT->box_end(); 01224 return false; 01225 } 01226 01227 return $category; 01228 } 01229 01230 protected function print_category_info($category) { 01231 $formatoptions = new stdClass(); 01232 $formatoptions->noclean = true; 01233 $formatoptions->overflowdiv = true; 01234 echo '<div class="boxaligncenter">'; 01235 echo format_text($category->info, $category->infoformat, $formatoptions, $this->course->id); 01236 echo "</div>\n"; 01237 } 01238 01242 protected function display_category_form($contexts, $pageurl, $current) { 01243 global $CFG, $OUTPUT; 01244 01246 echo '<div class="choosecategory">'; 01247 $catmenu = question_category_options($contexts, false, 0, true); 01248 01249 $select = new single_select($this->baseurl, 'category', $catmenu, $current, null, 'catmenu'); 01250 $select->set_label(get_string('selectacategory', 'question')); 01251 echo $OUTPUT->render($select); 01252 echo "</div>\n"; 01253 } 01254 01255 protected function display_options($recurse, $showhidden, $showquestiontext) { 01256 echo '<form method="get" action="edit.php" id="displayoptions">'; 01257 echo "<fieldset class='invisiblefieldset'>"; 01258 echo html_writer::input_hidden_params($this->baseurl, array('recurse', 'showhidden', 'qbshowtext')); 01259 $this->display_category_form_checkbox('recurse', $recurse, get_string('includesubcategories', 'question')); 01260 $this->display_category_form_checkbox('showhidden', $showhidden, get_string('showhidden', 'question')); 01261 $this->display_category_form_checkbox('qbshowtext', $showquestiontext, get_string('showquestiontext', 'question')); 01262 echo '<noscript><div class="centerpara"><input type="submit" value="'. get_string('go') .'" />'; 01263 echo '</div></noscript></fieldset></form>'; 01264 } 01265 01269 protected function display_category_form_checkbox($name, $value, $label) { 01270 echo '<div><input type="hidden" id="' . $name . '_off" name="' . $name . '" value="0" />'; 01271 echo '<input type="checkbox" id="' . $name . '_on" name="' . $name . '" value="1"'; 01272 if ($value) { 01273 echo ' checked="checked"'; 01274 } 01275 echo ' onchange="getElementById(\'displayoptions\').submit(); return true;" />'; 01276 echo '<label for="' . $name . '_on">' . $label . '</label>'; 01277 echo "</div>\n"; 01278 } 01279 01280 protected function create_new_question_form($category, $canadd) { 01281 global $CFG; 01282 echo '<div class="createnewquestion">'; 01283 if ($canadd) { 01284 create_new_question_button($category->id, $this->editquestionurl->params(), 01285 get_string('createnewquestion', 'question')); 01286 } else { 01287 print_string('nopermissionadd', 'question'); 01288 } 01289 echo '</div>'; 01290 } 01291 01304 protected function display_question_list($contexts, $pageurl, $categoryandcontext, 01305 $cm = null, $recurse=1, $page=0, $perpage=100, $showhidden=false, 01306 $showquestiontext = false, $addcontexts = array()) { 01307 global $CFG, $DB, $OUTPUT; 01308 01309 $category = $this->get_current_category($categoryandcontext); 01310 01311 $cmoptions = new stdClass(); 01312 $cmoptions->hasattempts = !empty($this->quizhasattempts); 01313 01314 $strselectall = get_string('selectall'); 01315 $strselectnone = get_string('deselectall'); 01316 $strdelete = get_string('delete'); 01317 01318 list($categoryid, $contextid) = explode(',', $categoryandcontext); 01319 $catcontext = get_context_instance_by_id($contextid); 01320 01321 $canadd = has_capability('moodle/question:add', $catcontext); 01322 $caneditall =has_capability('moodle/question:editall', $catcontext); 01323 $canuseall =has_capability('moodle/question:useall', $catcontext); 01324 $canmoveall =has_capability('moodle/question:moveall', $catcontext); 01325 01326 $this->create_new_question_form($category, $canadd); 01327 01328 $this->build_query_sql($category, $recurse, $showhidden); 01329 $totalnumber = $this->get_question_count(); 01330 if ($totalnumber == 0) { 01331 return; 01332 } 01333 01334 $questions = $this->load_page_questions($page, $perpage); 01335 01336 echo '<div class="categorypagingbarcontainer">'; 01337 $pageing_url = new moodle_url('edit.php'); 01338 $r = $pageing_url->params($pageurl->params()); 01339 $pagingbar = new paging_bar($totalnumber, $page, $perpage, $pageing_url); 01340 $pagingbar->pagevar = 'qpage'; 01341 echo $OUTPUT->render($pagingbar); 01342 echo '</div>'; 01343 01344 echo '<form method="post" action="edit.php">'; 01345 echo '<fieldset class="invisiblefieldset" style="display: block;">'; 01346 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />'; 01347 echo html_writer::input_hidden_params($pageurl); 01348 01349 echo '<div class="categoryquestionscontainer">'; 01350 $this->start_table(); 01351 $rowcount = 0; 01352 foreach ($questions as $question) { 01353 $this->print_table_row($question, $rowcount); 01354 $rowcount += 1; 01355 } 01356 $this->end_table(); 01357 echo "</div>\n"; 01358 01359 echo '<div class="categorypagingbarcontainer pagingbottom">'; 01360 echo $OUTPUT->render($pagingbar); 01361 if ($totalnumber > DEFAULT_QUESTIONS_PER_PAGE) { 01362 if ($perpage == DEFAULT_QUESTIONS_PER_PAGE) { 01363 $url = new moodle_url('edit.php', array_merge($pageurl->params(), array('qperpage'=>1000))); 01364 $showall = '<a href="'.$url.'">'.get_string('showall', 'moodle', $totalnumber).'</a>'; 01365 } else { 01366 $url = new moodle_url('edit.php', array_merge($pageurl->params(), array('qperpage'=>DEFAULT_QUESTIONS_PER_PAGE))); 01367 $showall = '<a href="'.$url.'">'.get_string('showperpage', 'moodle', DEFAULT_QUESTIONS_PER_PAGE).'</a>'; 01368 } 01369 echo "<div class='paging'>$showall</div>"; 01370 } 01371 echo '</div>'; 01372 01373 echo '<div class="modulespecificbuttonscontainer">'; 01374 if ($caneditall || $canmoveall || $canuseall){ 01375 echo '<strong> '.get_string('withselected', 'question').':</strong><br />'; 01376 01377 if (function_exists('module_specific_buttons')) { 01378 echo module_specific_buttons($this->cm->id,$cmoptions); 01379 } 01380 01381 // print delete and move selected question 01382 if ($caneditall) { 01383 echo '<input type="submit" name="deleteselected" value="' . $strdelete . "\" />\n"; 01384 } 01385 01386 if ($canmoveall && count($addcontexts)) { 01387 echo '<input type="submit" name="move" value="'.get_string('moveto', 'question')."\" />\n"; 01388 question_category_select_menu($addcontexts, false, 0, "$category->id,$category->contextid"); 01389 } 01390 01391 if (function_exists('module_specific_controls') && $canuseall) { 01392 $modulespecific = module_specific_controls($totalnumber, $recurse, $category, $this->cm->id,$cmoptions); 01393 if(!empty($modulespecific)){ 01394 echo "<hr />$modulespecific"; 01395 } 01396 } 01397 } 01398 echo "</div>\n"; 01399 01400 echo '</fieldset>'; 01401 echo "</form>\n"; 01402 } 01403 01404 protected function start_table() { 01405 echo '<table id="categoryquestions">' . "\n"; 01406 echo "<thead>\n"; 01407 $this->print_table_headers(); 01408 echo "</thead>\n"; 01409 echo "<tbody>\n"; 01410 } 01411 01412 protected function end_table() { 01413 echo "</tbody>\n"; 01414 echo "</table>\n"; 01415 } 01416 01417 protected function print_table_headers() { 01418 echo "<tr>\n"; 01419 foreach ($this->visiblecolumns as $column) { 01420 $column->display_header(); 01421 } 01422 echo "</tr>\n"; 01423 } 01424 01425 protected function get_row_classes($question, $rowcount) { 01426 $classes = array(); 01427 if ($question->hidden) { 01428 $classes[] = 'dimmed_text'; 01429 } 01430 if ($question->id == $this->lastchangedid) { 01431 $classes[] ='highlight'; 01432 } 01433 if (!empty($this->extrarows)) { 01434 $classes[] = 'r' . ($rowcount % 2); 01435 } 01436 return $classes; 01437 } 01438 01439 protected function print_table_row($question, $rowcount) { 01440 $rowclasses = implode(' ', $this->get_row_classes($question, $rowcount)); 01441 if ($rowclasses) { 01442 echo '<tr class="' . $rowclasses . '">' . "\n"; 01443 } else { 01444 echo "<tr>\n"; 01445 } 01446 foreach ($this->visiblecolumns as $column) { 01447 $column->display($question, $rowclasses); 01448 } 01449 echo "</tr>\n"; 01450 foreach ($this->extrarows as $row) { 01451 $row->display($question, $rowclasses); 01452 } 01453 } 01454 01455 public function process_actions() { 01456 global $CFG, $DB; 01458 if (optional_param('move', false, PARAM_BOOL) and confirm_sesskey()) { 01459 // Move selected questions to new category 01460 $category = required_param('category', PARAM_SEQUENCE); 01461 list($tocategoryid, $contextid) = explode(',', $category); 01462 if (! $tocategory = $DB->get_record('question_categories', array('id' => $tocategoryid, 'contextid' => $contextid))) { 01463 print_error('cannotfindcate', 'question'); 01464 } 01465 $tocontext = get_context_instance_by_id($contextid); 01466 require_capability('moodle/question:add', $tocontext); 01467 $rawdata = (array) data_submitted(); 01468 $questionids = array(); 01469 foreach ($rawdata as $key => $value) { // Parse input for question ids 01470 if (preg_match('!^q([0-9]+)$!', $key, $matches)) { 01471 $key = $matches[1]; 01472 $questionids[] = $key; 01473 } 01474 } 01475 if ($questionids) { 01476 list($usql, $params) = $DB->get_in_or_equal($questionids); 01477 $sql = ""; 01478 $questions = $DB->get_records_sql(" 01479 SELECT q.*, c.contextid 01480 FROM {question} q 01481 JOIN {question_categories} c ON c.id = q.category 01482 WHERE q.id $usql", $params); 01483 foreach ($questions as $question){ 01484 question_require_capability_on($question, 'move'); 01485 } 01486 question_move_questions_to_category($questionids, $tocategory->id); 01487 redirect($this->baseurl->out(false, 01488 array('category' => "$tocategoryid,$contextid"))); 01489 } 01490 } 01491 01492 if (optional_param('deleteselected', false, PARAM_BOOL)) { // delete selected questions from the category 01493 if (($confirm = optional_param('confirm', '', PARAM_ALPHANUM)) and confirm_sesskey()) { // teacher has already confirmed the action 01494 $deleteselected = required_param('deleteselected', PARAM_RAW); 01495 if ($confirm == md5($deleteselected)) { 01496 if ($questionlist = explode(',', $deleteselected)) { 01497 // for each question either hide it if it is in use or delete it 01498 foreach ($questionlist as $questionid) { 01499 $questionid = (int)$questionid; 01500 question_require_capability_on($questionid, 'edit'); 01501 if (questions_in_use(array($questionid))) { 01502 $DB->set_field('question', 'hidden', 1, array('id' => $questionid)); 01503 } else { 01504 question_delete_question($questionid); 01505 } 01506 } 01507 } 01508 redirect($this->baseurl); 01509 } else { 01510 print_error('invalidconfirm', 'question'); 01511 } 01512 } 01513 } 01514 01515 // Unhide a question 01516 if(($unhide = optional_param('unhide', '', PARAM_INT)) and confirm_sesskey()) { 01517 question_require_capability_on($unhide, 'edit'); 01518 $DB->set_field('question', 'hidden', 0, array('id' => $unhide)); 01519 redirect($this->baseurl); 01520 } 01521 } 01522 01523 public function process_actions_needing_ui() { 01524 global $DB, $OUTPUT; 01525 if (optional_param('deleteselected', false, PARAM_BOOL)) { 01526 // make a list of all the questions that are selected 01527 $rawquestions = $_REQUEST; // This code is called by both POST forms and GET links, so cannot use data_submitted. 01528 $questionlist = ''; // comma separated list of ids of questions to be deleted 01529 $questionnames = ''; // string with names of questions separated by <br /> with 01530 // an asterix in front of those that are in use 01531 $inuse = false; // set to true if at least one of the questions is in use 01532 foreach ($rawquestions as $key => $value) { // Parse input for question ids 01533 if (preg_match('!^q([0-9]+)$!', $key, $matches)) { 01534 $key = $matches[1]; 01535 $questionlist .= $key.','; 01536 question_require_capability_on($key, 'edit'); 01537 if (questions_in_use(array($key))) { 01538 $questionnames .= '* '; 01539 $inuse = true; 01540 } 01541 $questionnames .= $DB->get_field('question', 'name', array('id' => $key)) . '<br />'; 01542 } 01543 } 01544 if (!$questionlist) { // no questions were selected 01545 redirect($this->baseurl); 01546 } 01547 $questionlist = rtrim($questionlist, ','); 01548 01549 // Add an explanation about questions in use 01550 if ($inuse) { 01551 $questionnames .= '<br />'.get_string('questionsinuse', 'question'); 01552 } 01553 $baseurl = new moodle_url('edit.php', $this->baseurl->params()); 01554 $deleteurl = new moodle_url($baseurl, array('deleteselected'=>$questionlist, 'confirm'=>md5($questionlist), 'sesskey'=>sesskey())); 01555 01556 echo $OUTPUT->confirm(get_string('deletequestionscheck', 'question', $questionnames), $deleteurl, $baseurl); 01557 01558 return true; 01559 } 01560 } 01561 } 01562 01571 function question_edit_setup($edittab, $baseurl, $requirecmid = false, $requirecourseid = true) { 01572 global $DB, $PAGE; 01573 01574 $thispageurl = new moodle_url($baseurl); 01575 $thispageurl->remove_all_params(); // We are going to explicity add back everything important - this avoids unwanted params from being retained. 01576 01577 if ($requirecmid){ 01578 $cmid =required_param('cmid', PARAM_INT); 01579 } else { 01580 $cmid = optional_param('cmid', 0, PARAM_INT); 01581 } 01582 if ($cmid){ 01583 list($module, $cm) = get_module_from_cmid($cmid); 01584 $courseid = $cm->course; 01585 $thispageurl->params(compact('cmid')); 01586 require_login($courseid, false, $cm); 01587 $thiscontext = get_context_instance(CONTEXT_MODULE, $cmid); 01588 } else { 01589 $module = null; 01590 $cm = null; 01591 if ($requirecourseid){ 01592 $courseid = required_param('courseid', PARAM_INT); 01593 } else { 01594 $courseid = optional_param('courseid', 0, PARAM_INT); 01595 } 01596 if ($courseid){ 01597 $thispageurl->params(compact('courseid')); 01598 require_login($courseid, false); 01599 $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid); 01600 } else { 01601 $thiscontext = null; 01602 } 01603 } 01604 01605 if ($thiscontext){ 01606 $contexts = new question_edit_contexts($thiscontext); 01607 $contexts->require_one_edit_tab_cap($edittab); 01608 01609 } else { 01610 $contexts = null; 01611 } 01612 01613 $PAGE->set_pagelayout('admin'); 01614 01615 $pagevars['qpage'] = optional_param('qpage', -1, PARAM_INT); 01616 01617 //pass 'cat' from page to page and when 'category' comes from a drop down menu 01618 //then we also reset the qpage so we go to page 1 of 01619 //a new cat. 01620 $pagevars['cat'] = optional_param('cat', 0, PARAM_SEQUENCE); // if empty will be set up later 01621 if ($category = optional_param('category', 0, PARAM_SEQUENCE)) { 01622 if ($pagevars['cat'] != $category) { // is this a move to a new category? 01623 $pagevars['cat'] = $category; 01624 $pagevars['qpage'] = 0; 01625 } 01626 } 01627 if ($pagevars['cat']){ 01628 $thispageurl->param('cat', $pagevars['cat']); 01629 } 01630 if (strpos($baseurl, '/question/') === 0) { 01631 navigation_node::override_active_url($thispageurl); 01632 } 01633 01634 if ($pagevars['qpage'] > -1) { 01635 $thispageurl->param('qpage', $pagevars['qpage']); 01636 } else { 01637 $pagevars['qpage'] = 0; 01638 } 01639 01640 $pagevars['qperpage'] = optional_param('qperpage', -1, PARAM_INT); 01641 if ($pagevars['qperpage'] > -1) { 01642 $thispageurl->param('qperpage', $pagevars['qperpage']); 01643 } else { 01644 $pagevars['qperpage'] = DEFAULT_QUESTIONS_PER_PAGE; 01645 } 01646 01647 for ($i = 1; $i <= question_bank_view::MAX_SORTS; $i++) { 01648 $param = 'qbs' . $i; 01649 if (!$sort = optional_param($param, '', PARAM_ALPHAEXT)) { 01650 break; 01651 } 01652 $thispageurl->param($param, $sort); 01653 } 01654 01655 $defaultcategory = question_make_default_categories($contexts->all()); 01656 01657 $contextlistarr = array(); 01658 foreach ($contexts->having_one_edit_tab_cap($edittab) as $context){ 01659 $contextlistarr[] = "'$context->id'"; 01660 } 01661 $contextlist = join($contextlistarr, ' ,'); 01662 if (!empty($pagevars['cat'])){ 01663 $catparts = explode(',', $pagevars['cat']); 01664 if (!$catparts[0] || (false !== array_search($catparts[1], $contextlistarr)) || 01665 !$DB->count_records_select("question_categories", "id = ? AND contextid = ?", array($catparts[0], $catparts[1]))) { 01666 print_error('invalidcategory', 'question'); 01667 } 01668 } else { 01669 $category = $defaultcategory; 01670 $pagevars['cat'] = "$category->id,$category->contextid"; 01671 } 01672 01673 if(($recurse = optional_param('recurse', -1, PARAM_BOOL)) != -1) { 01674 $pagevars['recurse'] = $recurse; 01675 $thispageurl->param('recurse', $recurse); 01676 } else { 01677 $pagevars['recurse'] = 1; 01678 } 01679 01680 if(($showhidden = optional_param('showhidden', -1, PARAM_BOOL)) != -1) { 01681 $pagevars['showhidden'] = $showhidden; 01682 $thispageurl->param('showhidden', $showhidden); 01683 } else { 01684 $pagevars['showhidden'] = 0; 01685 } 01686 01687 if(($showquestiontext = optional_param('qbshowtext', -1, PARAM_BOOL)) != -1) { 01688 $pagevars['qbshowtext'] = $showquestiontext; 01689 $thispageurl->param('qbshowtext', $showquestiontext); 01690 } else { 01691 $pagevars['qbshowtext'] = 0; 01692 } 01693 01694 //category list page 01695 $pagevars['cpage'] = optional_param('cpage', 1, PARAM_INT); 01696 if ($pagevars['cpage'] != 1){ 01697 $thispageurl->param('cpage', $pagevars['cpage']); 01698 } 01699 01700 return array($thispageurl, $contexts, $cmid, $cm, $module, $pagevars); 01701 } 01702 01707 $QUESTION_EDITTABCAPS = question_edit_contexts::$caps; 01708 01712 function require_login_in_context($contextorid = null){ 01713 global $DB, $CFG; 01714 if (!is_object($contextorid)){ 01715 $context = get_context_instance_by_id($contextorid); 01716 } else { 01717 $context = $contextorid; 01718 } 01719 if ($context && ($context->contextlevel == CONTEXT_COURSE)) { 01720 require_login($context->instanceid); 01721 } else if ($context && ($context->contextlevel == CONTEXT_MODULE)) { 01722 if ($cm = $DB->get_record('course_modules',array('id' =>$context->instanceid))) { 01723 if (!$course = $DB->get_record('course', array('id' => $cm->course))) { 01724 print_error('invalidcourseid'); 01725 } 01726 require_course_login($course, true, $cm); 01727 01728 } else { 01729 print_error('invalidcoursemodule'); 01730 } 01731 } else if ($context && ($context->contextlevel == CONTEXT_SYSTEM)) { 01732 if (!empty($CFG->forcelogin)) { 01733 require_login(); 01734 } 01735 01736 } else { 01737 require_login(); 01738 } 01739 } 01740 01747 function print_choose_qtype_to_add_form($hiddenparams) { 01748 global $CFG, $PAGE, $OUTPUT; 01749 $PAGE->requires->js('/question/qbank.js'); 01750 echo '<div id="chooseqtypehead" class="hd">' . "\n"; 01751 echo $OUTPUT->heading(get_string('chooseqtypetoadd', 'question'), 3); 01752 echo "</div>\n"; 01753 echo '<div id="chooseqtype">' . "\n"; 01754 echo '<form action="' . $CFG->wwwroot . '/question/question.php" method="get"><div id="qtypeformdiv">' . "\n"; 01755 foreach ($hiddenparams as $name => $value) { 01756 echo '<input type="hidden" name="' . s($name) . '" value="' . s($value) . '" />' . "\n"; 01757 } 01758 echo "</div>\n"; 01759 echo '<div class="qtypes">' . "\n"; 01760 echo '<div class="instruction">' . get_string('selectaqtypefordescription', 'question') . "</div>\n"; 01761 echo '<div class="realqtypes">' . "\n"; 01762 $fakeqtypes = array(); 01763 foreach (question_bank::get_creatable_qtypes() as $qtype) { 01764 if ($qtype->is_real_question_type()) { 01765 print_qtype_to_add_option($qtype); 01766 } else { 01767 $fakeqtypes[] = $qtype; 01768 } 01769 } 01770 echo "</div>\n"; 01771 echo '<div class="fakeqtypes">' . "\n"; 01772 foreach ($fakeqtypes as $qtype) { 01773 print_qtype_to_add_option($qtype); 01774 } 01775 echo "</div>\n"; 01776 echo "</div>\n"; 01777 echo '<div class="submitbuttons">' . "\n"; 01778 echo '<input type="submit" value="' . get_string('next') . '" id="chooseqtype_submit" />' . "\n"; 01779 echo '<input type="submit" id="chooseqtypecancel" name="addcancel" value="' . get_string('cancel') . '" />' . "\n"; 01780 echo "</div></form>\n"; 01781 echo "</div>\n"; 01782 $PAGE->requires->js_init_call('qtype_chooser.init', array('chooseqtype')); 01783 } 01784 01789 function print_qtype_to_add_option($qtype) { 01790 if (get_string_manager()->string_exists('pluginnamesummary', $qtype->plugin_name())) { 01791 $summary = get_string('pluginnamesummary', $qtype->plugin_name()); 01792 } else { 01793 $summary = get_string($qtype->name() . 'summary', $qtype->plugin_name()); 01794 } 01795 01796 echo '<div class="qtypeoption">' . "\n"; 01797 echo '<label for="qtype_' . $qtype->name() . '">'; 01798 echo '<input type="radio" name="qtype" id="qtype_' . $qtype->name() . '" value="' . $qtype->name() . '" />'; 01799 echo '<span class="qtypename">'; 01800 $fakequestion = new stdClass(); 01801 $fakequestion->qtype = $qtype->name(); 01802 echo print_question_icon($fakequestion); 01803 echo $qtype->menu_name() . '</span><span class="qtypesummary">' . $summary; 01804 echo "</span></label>\n"; 01805 echo "</div>\n"; 01806 } 01807 01820 function create_new_question_button($categoryid, $params, $caption, $tooltip = '', $disabled = false) { 01821 global $CFG, $PAGE, $OUTPUT; 01822 static $choiceformprinted = false; 01823 $params['category'] = $categoryid; 01824 $url = new moodle_url('/question/addquestion.php', $params); 01825 echo $OUTPUT->single_button($url, $caption, 'get', array('disabled'=>$disabled, 'title'=>$tooltip)); 01826 01827 $PAGE->requires->yui2_lib('dragdrop'); 01828 $PAGE->requires->yui2_lib('container'); 01829 if (!$choiceformprinted) { 01830 echo '<div id="qtypechoicecontainer">'; 01831 print_choose_qtype_to_add_form(array()); 01832 echo "</div>\n"; 01833 $choiceformprinted = true; 01834 } 01835 } 01836 01837