Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * @package    moodlecore
 * @subpackage backup-moodle2
 * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */


defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/question/type/multianswer/questiontype.php');
/**
 * restore plugin class that provides the necessary information
 * needed to restore one multianswer qtype plugin
 *
 * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class restore_qtype_multianswer_plugin extends restore_qtype_plugin {

    /**
     * Returns the paths to be handled by the plugin at question level
     */
    protected function define_question_plugin_structure() {
        $paths = array();

        // This qtype uses question_answers, add them.
        $this->add_question_question_answers($paths);

        // Add own qtype stuff.
        $elename = 'multianswer';
        $elepath = $this->get_pathfor('/multianswer');
        $paths[] = new restore_path_element($elename, $elepath);

        return $paths; // And we return the interesting paths.
    }

    /**
     * Process the qtype/multianswer element
     */
    public function process_multianswer($data) {
        global $DB;

        $data = (object)$data;
        $oldid = $data->id;

        // Detect if the question is created or mapped.
        $oldquestionid   = $this->get_old_parentid('question');
        $newquestionid   = $this->get_new_parentid('question');
        $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;

        // If the question has been created by restore, we need to create its
        // question_multianswer too.
        if ($questioncreated) {
            // Adjust some columns.
            $data->question = $newquestionid;
            // Note: multianswer->sequence is a list of question->id values. We aren't
            // recoding them here (because some questions can be missing yet). Instead
            // we'll perform the recode in the {@link after_execute} method of the plugin
            // that gets executed once all questions have been created.
            // Insert record.
            $newitemid = $DB->insert_record('question_multianswer', $data);
            // Create mapping (need it for after_execute recode of sequence).
            $this->set_mapping('question_multianswer', $oldid, $newitemid);
        }
    }

    /**
     * This method is executed once the whole restore_structure_step
     * this step is part of ({@link restore_create_categories_and_questions})
     * has ended processing the whole xml structure. Its name is:
     * "after_execute_" + connectionpoint ("question")
     *
     * For multianswer qtype we use it to restore the sequence column,
     * containing one list of question ids
     */
    public function after_execute_question() {
        global $DB;
        // Now that all the questions have been restored, let's process
        // the created question_multianswer sequences (list of question ids).
        $rs = $DB->get_recordset_sql("
                SELECT qma.id, qma.sequence
                  FROM {question_multianswer} qma
                  JOIN {backup_ids_temp} bi ON bi.newitemid = qma.question
                 WHERE bi.backupid = ?
                   AND bi.itemname = 'question_created'",
                array($this->get_restoreid()));
        foreach ($rs as $rec) {
            $sequencearr = preg_split('/,/', $rec->sequence, -1, PREG_SPLIT_NO_EMPTY);
            if (substr_count($rec->sequence, ',') + 1 != count($sequencearr)) {
                $this->task->log('Invalid sequence found in restored multianswer question ' . $rec->id, backup::LOG_WARNING);
            }

            foreach ($sequencearr as $key => $question) {
                $sequencearr[$key] = $this->get_mappingid('question', $question);
            }
< $sequence = implode(',', $sequencearr);
> $sequence = implode(',', array_filter($sequencearr));
$DB->set_field('question_multianswer', 'sequence', $sequence, array('id' => $rec->id)); if (!empty($sequence)) { // Get relevant data indexed by positionkey from the multianswers table. $wrappedquestions = $DB->get_records_list('question', 'id', explode(',', $sequence), 'id ASC'); foreach ($wrappedquestions as $wrapped) { if ($wrapped->qtype == 'multichoice') { question_bank::get_qtype($wrapped->qtype)->get_question_options($wrapped); if (isset($wrapped->options->shuffleanswers)) { preg_match('/'.ANSWER_REGEX.'/s', $wrapped->questiontext, $answerregs); if (isset($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE]) && $answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE] !== '') { $wrapped->options->shuffleanswers = 0; $DB->set_field_select('qtype_multichoice_options', 'shuffleanswers', '0', "id =:select", array('select' => $wrapped->options->id) ); } } } } } } $rs->close(); } public function recode_response($questionid, $sequencenumber, array $response) { global $DB; $qtypes = $DB->get_records_menu('question', array('parent' => $questionid), '', 'id, qtype'); $sequence = $DB->get_field('question_multianswer', 'sequence', array('question' => $questionid)); $fakestep = new question_attempt_step_read_only($response); foreach (explode(',', $sequence) as $key => $subqid) { $i = $key + 1; $substep = new question_attempt_step_subquestion_adapter($fakestep, 'sub' . $i . '_'); $recodedresponse = $this->step->questions_recode_response_data($qtypes[$subqid], $subqid, $sequencenumber, $substep->get_all_data()); foreach ($recodedresponse as $name => $value) { $response[$substep->add_prefix($name)] = $value; } } return $response; } /** * Given one question_states record, return the answer * recoded pointing to all the restored stuff for multianswer questions * * answer is one comma separated list of hypen separated pairs * containing sequence (pointing to questions sequence in question_multianswer) * and mixed answers. We'll delegate * the recoding of answers to the proper qtype */ public function recode_legacy_state_answer($state) { global $DB; $answer = $state->answer; $resultarr = array(); // Get sequence of questions. $sequence = $DB->get_field('question_multianswer', 'sequence', array('question' => $state->question)); $sequencearr = explode(',', $sequence); // Let's process each pair. foreach (explode(',', $answer) as $pair) { $pairarr = explode('-', $pair); $sequenceid = $pairarr[0]; $subanswer = $pairarr[1]; // Calculate the questionid based on sequenceid. // Note it is already one *new* questionid that doesn't need mapping. $questionid = $sequencearr[$sequenceid - 1]; // Fetch qtype of the question (needed for delegation). $questionqtype = $DB->get_field('question', 'qtype', array('id' => $questionid)); // Delegate subanswer recode to proper qtype, faking one question_states record. $substate = new stdClass(); $substate->question = $questionid; $substate->answer = $subanswer; $newanswer = $this->step->restore_recode_legacy_answer($substate, $questionqtype); $resultarr[] = implode('-', array($sequenceid, $newanswer)); } return implode(',', $resultarr); } }