Example #1
0
/**
 * Serve question files when they are displayed in this export format.
 *
 * @param context $previewcontext the quiz context
 * @param int $questionid the question id.
 * @param context $filecontext the file (question) context
 * @param string $filecomponent the component the file belongs to.
 * @param string $filearea the file area.
 * @param array $args remaining file args.
 * @param bool $forcedownload.
 * @param array $options additional options affecting the file serving.
 */
function qformat_xhtml_questiontext_preview_pluginfile($context, $questionid,
        $args, $forcedownload, array $options = array()) {
    global $CFG;

    list($context, $course, $cm) = get_context_info_array($context->id);
    require_login($course, false, $cm);

    question_require_capability_on($questionid, 'view');

    question_send_questiontext_file($questionid, $args, $forcedownload, $options);
}
Example #2
0
/**
 * Serve question files when they are displayed in this export format.
 *
 * @param context $previewcontext the quiz context
 * @param int $questionid the question id.
 * @param context $filecontext the file (question) context
 * @param string $filecomponent the component the file belongs to.
 * @param string $filearea the file area.
 * @param array $args remaining file args.
 * @param bool $forcedownload.
 * @param array $options additional options affecting the file serving.
 */
function qformat_xhtml_question_preview_pluginfile($previewcontext, $questionid, $filecontext, $filecomponent, $filearea, $args, $forcedownload, $options = array())
{
    global $CFG;
    list($context, $course, $cm) = get_context_info_array($previewcontext->id);
    require_login($course, false, $cm);
    question_require_capability_on($questionid, 'view');
    $fs = get_file_storage();
    $relativepath = implode('/', $args);
    $fullpath = "/{$filecontext->id}/{$filecomponent}/{$filearea}/{$relativepath}";
    if (!($file = $fs->get_file_by_hash(sha1($fullpath))) or $file->is_directory()) {
        send_file_not_found();
    }
    send_stored_file($file, 0, 0, $forcedownload, $options);
}
Example #3
0
/**
 * Verify that the question exists, and the user has permission to use it.
 * Does not return. Throws an exception if the question cannot be used.
 * @param int $questionid The id of the question.
 */
function quiz_require_question_use($questionid) {
    global $DB;
    $question = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
    question_require_capability_on($question, 'use');
}
Example #4
0
 /// whence it came. (Where we are moving to is validated by the form.)
 list($newcatid, $newcontextid) = explode(',', $fromform->category);
 if (!empty($question->id) && $newcatid != $question->category) {
     question_require_capability_on($question, 'move');
 }
 // Ensure we redirect back to the category the question is being saved into.
 $returnurl->param('category', $fromform->category);
 if ($movecontext) {
     // We are just moving the question to a different context.
     list($tocatid, $tocontextid) = explode(',', $fromform->categorymoveto);
     require_capability('moodle/question:add', context::instance_by_id($tocontextid));
     question_move_questions_to_category(array($question->id), $tocatid);
 } else {
     // We are acutally saving the question.
     if (!empty($question->id)) {
         question_require_capability_on($question, 'edit');
     } else {
         require_capability('moodle/question:add', context::instance_by_id($newcontextid));
         if (!empty($fromform->makecopy) && !$question->formoptions->cansaveasnew) {
             print_error('nopermissions', '', '', 'edit');
         }
     }
     $question = $qtypeobj->save_question($question, $fromform);
     if (!empty($CFG->usetags) && isset($fromform->tags)) {
         // A wizardpage from multipe pages questiontype like calculated may not
         // allow editing the question tags, hence the isset($fromform->tags) test.
         require_once $CFG->dirroot . '/tag/lib.php';
         tag_set('question', $question->id, $fromform->tags);
     }
 }
 // Purge this question from the cache.
Example #5
0
 /// Process the combination of usecurrentcat, categorymoveto and category form
 /// fields, so the save_question method only has to consider $fromform->category
 if (!empty($fromform->usecurrentcat)) {
     // $fromform->category is the right category to save in.
 } else {
     if (!empty($fromform->categorymoveto)) {
         $fromform->category = $fromform->categorymoveto;
     } else {
         // $fromform->category is the right category to save in.
     }
 }
 /// If we are moving a question, check we have permission to move it from
 /// whence it came. (Where we are moving to is validated by the form.)
 list($newcatid) = explode(',', $fromform->category);
 if (!empty($question->id) && $newcatid != $question->category) {
     question_require_capability_on($question, 'move');
 }
 /// Ensure we redirect back to the category the question is being saved into.
 $returnurl = new moodle_url($returnurl);
 $returnurl->param('category', $fromform->category);
 $returnurl = $returnurl->out();
 /// Call the appropriate method.
 if ($movecontext) {
     list($tocatid, $tocontextid) = explode(',', $fromform->categorymoveto);
     $tocontext = get_context_instance_by_id($tocontextid);
     require_capability('moodle/question:add', $tocontext);
     if (get_filesdir_from_context($categorycontext) != get_filesdir_from_context($tocontext)) {
         $movecontexturl = new moodle_url($CFG->wwwroot . '/question/contextmoveq.php', array('returnurl' => $returnurl, 'ids' => $question->id, 'tocatid' => $tocatid));
         if ($cmid) {
             $movecontexturl->param('cmid', $cmid);
         } else {
Example #6
0
/**
 * Shows the question bank editing interface.
 *
 * The function also processes a number of actions:
 *
 * Actions affecting the question pool:
 * move           Moves a question to a different category
 * deleteselected Deletes the selected questions from the category
 * Other actions:
 * category      Chooses the category
 * displayoptions Sets display options
 *
 * @author Martin Dougiamas and many others. This has recently been extensively
 *         rewritten by Gustav Delius and other members of the Serving Mathematics project
 *         {@link http://maths.york.ac.uk/serving_maths}
 * @param moodle_url $pageurl object representing this pages url.
 */
function question_showbank($tabname, $contexts, $pageurl, $cm, $page, $perpage, $sortorder, $sortorderdecoded, $cat, $recurse, $showhidden, $showquestiontext)
{
    global $COURSE;
    if (optional_param('deleteselected', false, PARAM_BOOL)) {
        // teacher still has to confirm
        // make a list of all the questions that are selected
        $rawquestions = $_REQUEST;
        // This code is called by both POST forms and GET links, so cannot use data_submitted.
        $questionlist = '';
        // comma separated list of ids of questions to be deleted
        $questionnames = '';
        // string with names of questions separated by <br /> with
        // an asterix in front of those that are in use
        $inuse = false;
        // set to true if at least one of the questions is in use
        foreach ($rawquestions as $key => $value) {
            // Parse input for question ids
            if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
                $key = $matches[1];
                $questionlist .= $key . ',';
                question_require_capability_on($key, 'edit');
                if (record_exists('quiz_question_instances', 'question', $key)) {
                    $questionnames .= '* ';
                    $inuse = true;
                }
                $questionnames .= get_field('question', 'name', 'id', $key) . '<br />';
            }
        }
        if (!$questionlist) {
            // no questions were selected
            redirect($pageurl->out());
        }
        $questionlist = rtrim($questionlist, ',');
        // Add an explanation about questions in use
        if ($inuse) {
            $questionnames .= '<br />' . get_string('questionsinuse', 'quiz');
        }
        notice_yesno(get_string("deletequestionscheck", "quiz", $questionnames), $pageurl->out_action(), $pageurl->out(true), array('deleteselected' => $questionlist, 'confirm' => md5($questionlist)), $pageurl->params(), 'post', 'get');
        echo '</td></tr>';
        echo '</table>';
        print_footer($COURSE);
        exit;
    }
    // starts with category selection form
    print_box_start('generalbox questionbank');
    print_heading(get_string('questionbank', 'question'), '', 2);
    question_category_form($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, $recurse, $showhidden, $showquestiontext);
    // continues with list of questions
    question_list($contexts->having_one_edit_tab_cap($tabname), $pageurl, $cat, isset($cm) ? $cm : null, $recurse, $page, $perpage, $showhidden, $sortorder, $sortorderdecoded, $showquestiontext, $contexts->having_cap('moodle/question:add'));
    print_box_end();
}
Example #7
0
/**
 * Deletes question and all associated data from the database
 *
 * It will not delete a question if it is used by an activity module
 * @param object $question  The question being deleted
 */
function delete_question($questionid)
{
    global $QTYPES;
    if (!($question = get_record('question', 'id', $questionid))) {
        // In some situations, for example if this was a child of a
        // Cloze question that was previously deleted, the question may already
        // have gone. In this case, just do nothing.
        return;
    }
    // Do not delete a question if it is used by an activity module
    if (count(question_list_instances($questionid))) {
        return;
    }
    // delete questiontype-specific data
    question_require_capability_on($question, 'edit');
    if ($question) {
        if (isset($QTYPES[$question->qtype])) {
            $QTYPES[$question->qtype]->delete_question($questionid);
        }
    } else {
        echo "Question with id {$questionid} does not exist.<br />";
    }
    if ($states = get_records('question_states', 'question', $questionid)) {
        $stateslist = implode(',', array_keys($states));
        // delete questiontype-specific data
        foreach ($QTYPES as $qtype) {
            $qtype->delete_states($stateslist);
        }
    }
    // delete entries from all other question tables
    // It is important that this is done only after calling the questiontype functions
    delete_records("question_answers", "question", $questionid);
    delete_records("question_states", "question", $questionid);
    delete_records("question_sessions", "questionid", $questionid);
    // Now recursively delete all child questions
    if ($children = get_records('question', 'parent', $questionid)) {
        foreach ($children as $child) {
            if ($child->id != $questionid) {
                delete_question($child->id);
            }
        }
    }
    // Finally delete the question record itself
    delete_records('question', 'id', $questionid);
    return;
}
/**
 * Serve questiontext files in the question text when they are displayed in this report.
 *
 * @package  core_files
 * @category files
 * @param context $previewcontext the context in which the preview is happening.
 * @param int $questionid the question id.
 * @param context $filecontext the file (question) context.
 * @param string $filecomponent the component the file belongs to.
 * @param string $filearea the file area.
 * @param array $args remaining file args.
 * @param bool $forcedownload.
 * @param array $options additional options affecting the file serving.
 */
function core_question_question_preview_pluginfile($previewcontext, $questionid, $filecontext, $filecomponent, $filearea, $args, $forcedownload, $options = array())
{
    global $DB;
    // Verify that contextid matches the question.
    $question = $DB->get_record_sql('
            SELECT q.*, qc.contextid
              FROM {question} q
              JOIN {question_categories} qc ON qc.id = q.category
             WHERE q.id = :id AND qc.contextid = :contextid', array('id' => $questionid, 'contextid' => $filecontext->id), MUST_EXIST);
    // Check the capability.
    list($context, $course, $cm) = get_context_info_array($previewcontext->id);
    require_login($course, false, $cm);
    question_require_capability_on($question, 'use');
    $fs = get_file_storage();
    $relativepath = implode('/', $args);
    $fullpath = "/{$filecontext->id}/{$filecomponent}/{$filearea}/{$relativepath}";
    if (!($file = $fs->get_file_by_hash(sha1($fullpath))) or $file->is_directory()) {
        send_file_not_found();
    }
    send_stored_file($file, 0, 0, $forcedownload, $options);
}
}
if (!($tocat = get_record('question_categories', 'id', $tocatid))) {
    print_error('categorydoesnotexist', 'question', $returnurl);
}
$tocat->context = get_context_instance_by_id($tocat->contextid);
require_capability('moodle/question:add', $tocat->context);
$tocoursefilesid = get_filesdir_from_context($tocat->context);
$urls = array();
if ($tocoursefilesid == SITEID) {
    $toareaname = get_string('filesareasite', 'question');
} else {
    $toareaname = get_string('filesareacourse', 'question');
}
$fromcoursefilesid = 0;
foreach (array_keys($questions) as $id) {
    question_require_capability_on($questions[$id], 'move');
    get_question_options($questions[$id]);
    $questions[$id]->context = get_context_instance_by_id($questions[$id]->contextid);
    $thisfilesid = get_filesdir_from_context($questions[$id]->context);
    if ($fromcoursefilesid && $thisfilesid != $fromcoursefilesid) {
        error('You can\'t use this script to move questions that have files associated with them from different areas.');
    } else {
        $fromcoursefilesid = $thisfilesid;
    }
    if ($tocoursefilesid != $fromcoursefilesid) {
        $urls = array_merge_recursive($urls, $QTYPES[$questions[$id]->qtype]->find_file_links($questions[$id], $fromcoursefilesid));
    }
}
$brokenurls = array();
foreach (array_keys($urls) as $url) {
    if (!file_exists($CFG->dataroot . "/{$fromcoursefilesid}/" . $url)) {
$navlinks[] = array('name' => $strqcreates, 'link' => "index.php?id={$course->id}", 'type' => 'activity');
$navlinks[] = array('name' => format_string($qcreate->name), 'link' => '', 'type' => 'activityinstance');
$navigation = build_navigation($navlinks);
$headerargs = array(format_string($qcreate->name), "", $navigation, "", "", true, update_module_button($cm->id, $course->id, $strqcreate), navmenu($course, $cm));
if (!($cats = get_categories_for_contexts($modulecontext->id))) {
    //if it has not been made yet then make a default cat
    question_make_default_categories(array($modulecontext));
    $cats = get_categories_for_contexts($modulecontext->id);
}
$catsinctx = array();
foreach ($cats as $catinctx) {
    $catsinctx[] = $catinctx->id;
}
$catsinctxlist = join($catsinctx, ',');
$cat = array_shift($cats);
if ($delete && question_require_capability_on($delete, 'edit')) {
    if ($confirm && confirm_sesskey()) {
        if (!delete_records_select('question', "id = {$delete} AND category IN ({$catsinctxlist})")) {
            print_error('question_not_found');
        } else {
            qcreate_update_grades($qcreate, $USER->id);
            redirect($CFG->wwwroot . '/mod/qcreate/view.php?id=' . $cm->id);
        }
    } else {
        call_user_func_array('print_header_simple', $headerargs);
        print_heading(get_string('delete'));
        notice_yesno(get_string('confirmdeletequestion', 'qcreate'), 'view.php', 'view.php', array('id' => $cm->id, 'sesskey' => sesskey(), 'confirm' => 1, 'delete' => $delete), array('id' => $cm->id), 'POST', 'GET');
        print_footer('none');
        die;
    }
}
Example #11
0
/**
 * Deletes question and all associated data from the database
 *
 * It will not delete a question if it is used by an activity module
 *
 * @global object
 * @global object
 * @param object $question  The question being deleted
 */
function delete_question($questionid)
{
    global $QTYPES, $DB;
    $question = $DB->get_record_sql('
            SELECT q.*, qc.contextid
            FROM {question} q
            JOIN {question_categories} qc ON qc.id = q.category
            WHERE q.id = ?', array($questionid));
    if (!$question) {
        // In some situations, for example if this was a child of a
        // Cloze question that was previously deleted, the question may already
        // have gone. In this case, just do nothing.
        return;
    }
    // Do not delete a question if it is used by an activity module
    if (count(question_list_instances($questionid))) {
        return;
    }
    // delete questiontype-specific data
    question_require_capability_on($question, 'edit');
    if (isset($QTYPES[$question->qtype])) {
        $QTYPES[$question->qtype]->delete_question($questionid, $question->contextid);
    }
    if ($states = $DB->get_records('question_states', array('question' => $questionid))) {
        $stateslist = implode(',', array_keys($states));
        // delete questiontype-specific data
        foreach ($QTYPES as $qtype) {
            $qtype->delete_states($stateslist);
        }
    }
    // Delete entries from all other question tables
    // It is important that this is done only after calling the questiontype functions
    $DB->delete_records('question_answers', array('question' => $questionid));
    $DB->delete_records('question_states', array('question' => $questionid));
    $DB->delete_records('question_sessions', array('questionid' => $questionid));
    // Now recursively delete all child questions
    if ($children = $DB->get_records('question', array('parent' => $questionid), '', 'id,qtype')) {
        foreach ($children as $child) {
            if ($child->id != $questionid) {
                delete_question($child->id);
            }
        }
    }
    // Finally delete the question record itself
    $DB->delete_records('question', array('id' => $questionid));
}
Example #12
0
/**
 * Serve questiontext files in the question text when they are displayed in this report.
 *
 * @package  core_files
 * @category files
 * @param stdClass $context the context
 * @param int $questionid the question id
 * @param array $args remaining file args
 * @param bool $forcedownload
 */
function core_question_questiontext_preview_pluginfile($context, $questionid, $args, $forcedownload)
{
    global $DB;
    // Verify that contextid matches the question.
    $question = $DB->get_record_sql('
            SELECT q.*, qc.contextid
              FROM {question} q
              JOIN {question_categories} qc ON qc.id = q.category
             WHERE q.id = :id AND qc.contextid = :contextid', array('id' => $questionid, 'contextid' => $context->id), MUST_EXIST);
    // Check the capability.
    list($context, $course, $cm) = get_context_info_array($context->id);
    require_login($course, false, $cm);
    question_require_capability_on($question, 'use');
    question_send_questiontext_file($questionid, $args, $forcedownload);
}
Example #13
0
/**
 * Deletes question and all associated data from the database
 *
 * It will not delete a question if it is used by an activity module
 * @param object $question  The question being deleted
 */
function question_delete_question($questionid)
{
    global $DB;
    $question = $DB->get_record_sql('
            SELECT q.*, qc.contextid
            FROM {question} q
            JOIN {question_categories} qc ON qc.id = q.category
            WHERE q.id = ?', array($questionid));
    if (!$question) {
        // In some situations, for example if this was a child of a
        // Cloze question that was previously deleted, the question may already
        // have gone. In this case, just do nothing.
        return;
    }
    // Do not delete a question if it is used by an activity module
    if (questions_in_use(array($questionid))) {
        return;
    }
    // Check permissions.
    question_require_capability_on($question, 'edit');
    $dm = new question_engine_data_mapper();
    $dm->delete_previews($questionid);
    // delete questiontype-specific data
    question_bank::get_qtype($question->qtype, false)->delete_question($questionid, $question->contextid);
    // Now recursively delete all child questions
    if ($children = $DB->get_records('question', array('parent' => $questionid), '', 'id, qtype')) {
        foreach ($children as $child) {
            if ($child->id != $questionid) {
                question_delete_question($child->id);
            }
        }
    }
    // Finally delete the question record itself
    $DB->delete_records('question', array('id' => $questionid));
}
Example #14
0
    public function process_actions_needing_ui() {
        global $DB, $OUTPUT;
        if (optional_param('deleteselected', false, PARAM_BOOL)) {
            // make a list of all the questions that are selected
            $rawquestions = $_REQUEST; // This code is called by both POST forms and GET links, so cannot use data_submitted.
            $questionlist = '';  // comma separated list of ids of questions to be deleted
            $questionnames = ''; // string with names of questions separated by <br /> with
                                 // an asterix in front of those that are in use
            $inuse = false;      // set to true if at least one of the questions is in use
            foreach ($rawquestions as $key => $value) {    // Parse input for question ids
                if (preg_match('!^q([0-9]+)$!', $key, $matches)) {
                    $key = $matches[1];
                    $questionlist .= $key.',';
                    question_require_capability_on($key, 'edit');
                    if (questions_in_use(array($key))) {
                        $questionnames .= '* ';
                        $inuse = true;
                    }
                    $questionnames .= $DB->get_field('question', 'name', array('id' => $key)) . '<br />';
                }
            }
            if (!$questionlist) { // no questions were selected
                redirect($this->baseurl);
            }
            $questionlist = rtrim($questionlist, ',');

            // Add an explanation about questions in use
            if ($inuse) {
                $questionnames .= '<br />'.get_string('questionsinuse', 'question');
            }
            $baseurl = new moodle_url('edit.php', $this->baseurl->params());
            $deleteurl = new moodle_url($baseurl, array('deleteselected'=>$questionlist, 'confirm'=>md5($questionlist), 'sesskey'=>sesskey()));

            echo $OUTPUT->confirm(get_string('deletequestionscheck', 'question', $questionnames), $deleteurl, $baseurl);

            return true;
        }
    }
 /**
  * Saves or updates a question after editing by a teacher
  *
  * Given some question info and some data about the answers
  * this function parses, organises and saves the question
  * It is used by {@link question.php} when saving new data from
  * a form, and also by {@link import.php} when importing questions
  * This function in turn calls {@link save_question_options}
  * to save question-type specific options
  * @param object $question the question object which should be updated
  * @param object $form the form submitted by the teacher
  * @param object $course the course we are in
  * @return object On success, return the new question object. On failure,
  *       return an object as follows. If the error object has an errors field,
  *       display that as an error message. Otherwise, the editing form will be
  *       redisplayed with validation errors, from validation_errors field, which
  *       is itself an object, shown next to the form fields.
  */
 function save_question($question, $form, $course)
 {
     global $USER;
     // This default implementation is suitable for most
     // question types.
     // First, save the basic question itself
     $question->name = trim($form->name);
     $question->questiontext = trim($form->questiontext);
     $question->questiontextformat = $form->questiontextformat;
     $question->parent = isset($form->parent) ? $form->parent : 0;
     $question->length = $this->actual_number_of_questions($question);
     $question->penalty = isset($form->penalty) ? $form->penalty : 0;
     if (empty($form->image)) {
         $question->image = "";
     } else {
         $question->image = $form->image;
     }
     if (empty($form->generalfeedback)) {
         $question->generalfeedback = '';
     } else {
         $question->generalfeedback = trim($form->generalfeedback);
     }
     if (empty($question->name)) {
         $question->name = shorten_text(strip_tags($question->questiontext), 15);
         if (empty($question->name)) {
             $question->name = '-';
         }
     }
     if ($question->penalty > 1 or $question->penalty < 0) {
         $question->errors['penalty'] = get_string('invalidpenalty', 'quiz');
     }
     if (isset($form->defaultgrade)) {
         $question->defaultgrade = $form->defaultgrade;
     }
     if (!empty($question->id)) {
         // Question already exists
         if (isset($form->categorymoveto)) {
             question_require_capability_on($question, 'move');
             list($question->category, $movetocontextid) = explode(',', $form->categorymoveto);
             //don't need to test add permission of category we are moving question to.
             //Only categories that we have permission to add
             //a question to will get through the form cleaning code for the select box.
         }
         // keep existing unique stamp code
         $question->stamp = get_field('question', 'stamp', 'id', $question->id);
         $question->modifiedby = $USER->id;
         $question->timemodified = time();
         if (!update_record('question', $question)) {
             error('Could not update question!');
         }
     } else {
         // Question is a new one
         // Set the unique code
         list($question->category, $notused) = explode(',', $form->category);
         $question->stamp = make_unique_id_code();
         $question->createdby = $USER->id;
         $question->modifiedby = $USER->id;
         $question->timecreated = time();
         $question->timemodified = time();
         if (!($question->id = insert_record('question', $question))) {
             error('Could not insert new question!');
         }
     }
     // Now to save all the answers and type-specific options
     $form->id = $question->id;
     $form->qtype = $question->qtype;
     $form->category = $question->category;
     $form->questiontext = $question->questiontext;
     $result = $this->save_question_options($form);
     if (!empty($result->error)) {
         error($result->error);
     }
     if (!empty($result->notice)) {
         notice($result->notice, "question.php?id={$question->id}");
     }
     if (!empty($result->noticeyesno)) {
         notice_yesno($result->noticeyesno, "question.php?id={$question->id}&amp;courseid={$course->id}", "edit.php?courseid={$course->id}");
         print_footer($course);
         exit;
     }
     // Give the question a unique version stamp determined by question_hash()
     if (!set_field('question', 'version', question_hash($question), 'id', $question->id)) {
         error('Could not update question version field');
     }
     return $question;
 }
Example #16
0
        $question->formoptions->canedit = false;
        $question->formoptions->canmove = question_has_capability_on($question, 'move') && $contexts->have_cap('moodle/question:add');
        $question->formoptions->cansaveasnew = false;
        $question->formoptions->repeatelements = false;
        $question->formoptions->movecontext = true;
        $formeditable = true;
        question_require_capability_on($question, 'view');
    } else {
        $question->formoptions->canedit = question_has_capability_on($question, 'edit');
        $question->formoptions->canmove = question_has_capability_on($question, 'move') && $addpermission;
        $question->formoptions->cansaveasnew = ($canview || question_has_capability_on($question, 'edit')) && $addpermission;
        $question->formoptions->repeatelements = $question->formoptions->canedit || $question->formoptions->cansaveasnew;
        $formeditable = $question->formoptions->canedit || $question->formoptions->cansaveasnew || $question->formoptions->canmove;
        $question->formoptions->movecontext = false;
        if (!$formeditable) {
            question_require_capability_on($question, 'view');
        }
    }
} else {
    // creating a new question
    require_capability('moodle/question:add', $categorycontext);
    $formeditable = true;
    $question->formoptions->repeatelements = true;
    $question->formoptions->movecontext = false;
}
// Validate the question type.
if (!isset($QTYPES[$question->qtype])) {
    print_error('unknownquestiontype', 'question', $returnurl, $question->qtype);
}
$CFG->pagepath = 'question/type/' . $question->qtype;
// Create the question editing form.