Proyectos de Subversion Moodle

Rev

Rev 11 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 11 Rev 1441
Línea 488... Línea 488...
488
 
488
 
489
        // Ensure the module cache is current when recalculating grades.
489
        // Ensure the module cache is current when recalculating grades.
Línea 490... Línea 490...
490
        rebuild_course_cache($this->get_courseid(), true);
490
        rebuild_course_cache($this->get_courseid(), true);
491
 
491
 
492
        // Restore marks items as needing update. Update everything now.
492
        // Restore marks items as needing update. Update everything now.
Línea 493... Línea 493...
493
        grade_regrade_final_grades($this->get_courseid());
493
        grade_regrade_final_grades($this->get_courseid(), async: true);
494
    }
494
    }
495
 
495
 
Línea 1157... Línea 1157...
1157
 
1157
 
1158
        // Do not include group/groupings information if not requested.
1158
        // Do not include group/groupings information if not requested.
1159
        $groupinfo = $this->get_setting_value('groups');
1159
        $groupinfo = $this->get_setting_value('groups');
1160
        if ($groupinfo) {
1160
        if ($groupinfo) {
1161
            $paths[] = new restore_path_element('group', '/groups/group');
-
 
1162
            $paths[] = new restore_path_element('groupcustomfield', '/groups/groupcustomfields/groupcustomfield');
1161
            $paths[] = new restore_path_element('group', '/groups/group');
1163
            $paths[] = new restore_path_element('grouping', '/groups/groupings/grouping');
-
 
1164
            $paths[] = new restore_path_element('groupingcustomfield',
-
 
1165
                '/groups/groupings/groupingcustomfields/groupingcustomfield');
1162
            $paths[] = new restore_path_element('grouping', '/groups/groupings/grouping');
-
 
1163
            $paths[] = new restore_path_element('grouping_group', '/groups/groupings/grouping/grouping_groups/grouping_group');
-
 
1164
 
-
 
1165
            // Custom fields.
-
 
1166
            if ($this->get_setting_value('customfield')) {
-
 
1167
                $paths[] = new restore_path_element('groupcustomfield', '/groups/groupcustomfields/groupcustomfield');
-
 
1168
                $paths[] = new restore_path_element('groupingcustomfield',
-
 
1169
                    '/groups/groupings/groupingcustomfields/groupingcustomfield');
1166
            $paths[] = new restore_path_element('grouping_group', '/groups/groupings/grouping/grouping_groups/grouping_group');
1170
            }
1167
        }
1171
        }
1168
        return $paths;
1172
        return $paths;
Línea 1169... Línea 1173...
1169
    }
1173
    }
Línea 1233... Línea 1237...
1233
     * @param array $data data for group custom field
1237
     * @param array $data data for group custom field
1234
     * @return void
1238
     * @return void
1235
     */
1239
     */
1236
    public function process_groupcustomfield($data) {
1240
    public function process_groupcustomfield($data) {
1237
        $newgroup = $this->get_mapping('group', $data['groupid']);
1241
        $newgroup = $this->get_mapping('group', $data['groupid']);
-
 
1242
        if ($newgroup && $newgroup->newitemid) {
1238
        $data['groupid'] = $newgroup->newitemid ?? $data['groupid'];
1243
            $data['groupid'] = $newgroup->newitemid;
1239
        $handler = \core_group\customfield\group_handler::create();
1244
            $handler = \core_group\customfield\group_handler::create();
1240
        $handler->restore_instance_data_from_backup($this->task, $data);
1245
            $handler->restore_instance_data_from_backup($this->task, $data);
-
 
1246
        }
1241
    }
1247
    }
Línea 1242... Línea 1248...
1242
 
1248
 
1243
    public function process_grouping($data) {
1249
    public function process_grouping($data) {
Línea 1289... Línea 1295...
1289
     * Restore grouping custom field values.
1295
     * Restore grouping custom field values.
1290
     * @param array $data data for grouping custom field
1296
     * @param array $data data for grouping custom field
1291
     * @return void
1297
     * @return void
1292
     */
1298
     */
1293
    public function process_groupingcustomfield($data) {
1299
    public function process_groupingcustomfield($data) {
1294
        $newgroup = $this->get_mapping('grouping', $data['groupingid']);
1300
        $newgrouping = $this->get_mapping('grouping', $data['groupingid']);
-
 
1301
        if ($newgrouping && $newgrouping->newitemid) {
1295
        $data['groupingid'] = $newgroup->newitemid ?? $data['groupingid'];
1302
            $data['groupingid'] = $newgrouping->newitemid;
1296
        $handler = \core_group\customfield\grouping_handler::create();
1303
            $handler = \core_group\customfield\grouping_handler::create();
1297
        $handler->restore_instance_data_from_backup($this->task, $data);
1304
            $handler->restore_instance_data_from_backup($this->task, $data);
-
 
1305
        }
1298
    }
1306
    }
Línea 1299... Línea 1307...
1299
 
1307
 
1300
    public function process_grouping_group($data) {
1308
    public function process_grouping_group($data) {
Línea 1610... Línea 1618...
1610
        // Look for the section
1618
        // Look for the section
1611
        $section = new stdclass();
1619
        $section = new stdclass();
1612
        $section->course  = $this->get_courseid();
1620
        $section->course  = $this->get_courseid();
1613
        $section->section = $data->number;
1621
        $section->section = $data->number;
1614
        $section->timemodified = $data->timemodified ?? 0;
1622
        $section->timemodified = $data->timemodified ?? 0;
-
 
1623
        $section->component = null;
-
 
1624
        $section->itemid = null;
-
 
1625
 
-
 
1626
        $secrec = $DB->get_record(
-
 
1627
            'course_sections',
-
 
1628
            ['course' => $this->get_courseid(), 'section' => $data->number, 'component' => null]
-
 
1629
        );
-
 
1630
        $createsection = empty($secrec);
-
 
1631
 
-
 
1632
        // Delegated sections are always restored as new sections.
-
 
1633
        if (!empty($data->component)) {
-
 
1634
            $section->itemid = $this->get_delegated_section_mapping($data->component, $data->itemid);
-
 
1635
            // If the delegate component does not set the mapping id, the section must be converted
-
 
1636
            // into a regular section. Otherwise, it won't be accessible.
-
 
1637
            $createsection = $createsection || $section->itemid !== null;
-
 
1638
            $section->component = ($section->itemid !== null) ? $data->component : null;
-
 
1639
            // The section number will be always the last of the course, no matter the case.
-
 
1640
            $section->section = $this->get_last_section_number($this->get_courseid()) + 1;
-
 
1641
 
-
 
1642
        }
1615
        // Section doesn't exist, create it with all the info from backup
1643
        // Section doesn't exist, create it with all the info from backup
1616
        if (!$secrec = $DB->get_record('course_sections', ['course' => $this->get_courseid(), 'section' => $data->number])) {
1644
        if ($createsection) {
1617
            $section->name = $data->name;
1645
            $section->name = $data->name;
1618
            $section->summary = $data->summary;
1646
            $section->summary = $data->summary;
1619
            $section->summaryformat = $data->summaryformat;
1647
            $section->summaryformat = $data->summaryformat;
1620
            $section->sequence = '';
1648
            $section->sequence = '';
1621
            $section->visible = $data->visible;
1649
            $section->visible = $data->visible;
Línea 1627... Línea 1655...
1627
                if (is_null($section->availability)) {
1655
                if (is_null($section->availability)) {
1628
                    $section->availability = \core_availability\info::convert_legacy_fields(
1656
                    $section->availability = \core_availability\info::convert_legacy_fields(
1629
                            $data, true);
1657
                            $data, true);
1630
                }
1658
                }
1631
            }
1659
            }
1632
            // Moodle 4.4 implement basic delegated section logic but it is not able to restore
-
 
-
 
1660
 
1633
            // them from a backup. To prevent unexpected retoration errors, all sections with
1661
            // Delegated sections should be always after the normal sections.
1634
            // a component will be restored as a normal section.
1662
            $this->displace_delegated_sections_after($section->section);
1635
            $section->component = null;
-
 
1636
            $section->itemid = null;
-
 
-
 
1663
 
1637
            $newitemid = $DB->insert_record('course_sections', $section);
1664
            $newitemid = $DB->insert_record('course_sections', $section);
1638
            $section->id = $newitemid;
1665
            $section->id = $newitemid;
Línea 1639... Línea 1666...
1639
 
1666
 
Línea 1803... Línea 1830...
1803
 
1830
 
1804
    protected function after_execute() {
1831
    protected function after_execute() {
1805
        // Add section related files, with 'course_section' itemid to match
1832
        // Add section related files, with 'course_section' itemid to match
1806
        $this->add_related_files('course', 'section', 'course_section');
1833
        $this->add_related_files('course', 'section', 'course_section');
-
 
1834
    }
-
 
1835
 
-
 
1836
    /**
-
 
1837
     * Create a delegate section mapping.
-
 
1838
     *
-
 
1839
     * @param string $component the component name (frankenstyle)
-
 
1840
     * @param int $oldsectionid The old section id.
-
 
1841
     * @return int|null The new section id or null if not found.
-
 
1842
     */
-
 
1843
    protected function get_delegated_section_mapping($component, $oldsectionid): ?int {
-
 
1844
        $result = $this->get_mappingid("course_section::$component", $oldsectionid, null);
-
 
1845
        return $result;
-
 
1846
    }
-
 
1847
 
-
 
1848
    /**
-
 
1849
     * Displace delegated sections after the given section number.
-
 
1850
     *
-
 
1851
     * @param int $sectionnum The section number.
-
 
1852
     */
-
 
1853
    protected function displace_delegated_sections_after(int $sectionnum): void {
-
 
1854
        global $DB;
-
 
1855
 
-
 
1856
        $sectionstomove = $DB->get_records_select(
-
 
1857
            'course_sections',
-
 
1858
            'course = ? AND component IS NOT NULL',
-
 
1859
            [$this->get_courseid()],
-
 
1860
            'section DESC', 'id, section'
-
 
1861
        );
-
 
1862
        // Here we add the new section to the end of the list so we make sure that all delegated sections are really
-
 
1863
        // all located after the normal sections. We can have case where delegated sections are located before the
-
 
1864
        // normal sections, so we need to move them to the end (mostly in the restore process more than in the duplicate
-
 
1865
        // process in which the order sections => delegated section is mostly there).
-
 
1866
        $sectionnum = $sectionnum + count($sectionstomove);
-
 
1867
        foreach ($sectionstomove as $section) {
-
 
1868
            $section->section = $sectionnum--;
-
 
1869
            $DB->update_record('course_sections', $section);
-
 
1870
        }
-
 
1871
    }
-
 
1872
 
-
 
1873
    /**
-
 
1874
     * Get the last section number in the course.
-
 
1875
     *
-
 
1876
     * @param int $courseid The course id.
-
 
1877
     * @param bool $includedelegated If true, include delegated sections in the count.
-
 
1878
     * @return int The last section number.
-
 
1879
     */
-
 
1880
    protected function get_last_section_number(int $courseid, bool $includedelegated = false): int {
-
 
1881
        global $DB;
-
 
1882
 
-
 
1883
        $delegtadefilter = $includedelegated ? '' : ' AND component IS NULL';
-
 
1884
 
-
 
1885
        return (int) $DB->get_field_sql(
-
 
1886
            'SELECT max(section) from {course_sections} WHERE course = ?' . $delegtadefilter,
-
 
1887
            [$courseid]
-
 
1888
        );
1807
    }
1889
    }
Línea 1808... Línea 1890...
1808
}
1890
}
1809
 
1891
 
1810
/**
1892
/**
Línea 1830... Línea 1912...
1830
     */
1912
     */
1831
    protected $legacyallowedmodules = array();
1913
    protected $legacyallowedmodules = array();
Línea 1832... Línea 1914...
1832
 
1914
 
Línea -... Línea 1915...
-
 
1915
    protected function define_structure() {
-
 
1916
 
1833
    protected function define_structure() {
1917
        $paths = [];
-
 
1918
 
1834
 
1919
        $course = new restore_path_element('course', '/course');
1835
        $course = new restore_path_element('course', '/course');
1920
        $paths[] = $course;
1836
        $category = new restore_path_element('category', '/course/category');
1921
        $paths[] = new restore_path_element('category', '/course/category');
1837
        $tag = new restore_path_element('tag', '/course/tags/tag');
1922
        $paths[] = new restore_path_element('tag', '/course/tags/tag');
-
 
1923
        $paths[] = new restore_path_element('course_format_option', '/course/courseformatoptions/courseformatoption');
-
 
1924
        $paths[] = new restore_path_element('allowed_module', '/course/allowed_modules/module');
-
 
1925
 
1838
        $customfield = new restore_path_element('customfield', '/course/customfields/customfield');
1926
        // Custom fields.
-
 
1927
        if ($this->get_setting_value('customfield')) {
Línea 1839... Línea 1928...
1839
        $courseformatoptions = new restore_path_element('course_format_option', '/course/courseformatoptions/courseformatoption');
1928
            $paths[] = new restore_path_element('customfield', '/course/customfields/customfield');
1840
        $allowedmodule = new restore_path_element('allowed_module', '/course/allowed_modules/module');
1929
        }
Línea 1841... Línea 1930...
1841
 
1930
 
Línea 1858... Línea 1947...
1858
        $this->add_plugin_structure('local', $course);
1947
        $this->add_plugin_structure('local', $course);
Línea 1859... Línea 1948...
1859
 
1948
 
1860
        // Apply for admin tool plugins optional paths at course level.
1949
        // Apply for admin tool plugins optional paths at course level.
Línea 1861... Línea 1950...
1861
        $this->add_plugin_structure('tool', $course);
1950
        $this->add_plugin_structure('tool', $course);
1862
 
1951
 
Línea 1863... Línea 1952...
1863
        return array($course, $category, $tag, $customfield, $allowedmodule, $courseformatoptions);
1952
        return $paths;
1864
    }
1953
    }
1865
 
1954
 
Línea 2702... Línea 2791...
2702
                'notification'   => $data->notification,
2791
                'notification'   => $data->notification,
2703
                'status'         => BADGE_STATUS_INACTIVE,
2792
                'status'         => BADGE_STATUS_INACTIVE,
2704
                'nextcron'       => $data->nextcron,
2793
                'nextcron'       => $data->nextcron,
2705
                'version'        => $data->version,
2794
                'version'        => $data->version,
2706
                'language'       => $data->language,
2795
                'language'       => $data->language,
2707
                'imageauthorname' => $data->imageauthorname,
-
 
2708
                'imageauthoremail' => $data->imageauthoremail,
-
 
2709
                'imageauthorurl' => $data->imageauthorurl,
-
 
2710
                'imagecaption'   => $data->imagecaption
2796
                'imagecaption'   => $data->imagecaption
2711
        );
2797
        );
Línea 2712... Línea 2798...
2712
 
2798
 
2713
        $newid = $DB->insert_record('badge', $params);
2799
        $newid = $DB->insert_record('badge', $params);
Línea 4906... Línea 4992...
4906
        // Do the mapping for modulename, preparing it for files by oldcontext
4992
        // Do the mapping for modulename, preparing it for files by oldcontext
4907
        $modulename = $this->task->get_modulename();
4993
        $modulename = $this->task->get_modulename();
4908
        $oldid = $this->task->get_old_activityid();
4994
        $oldid = $this->task->get_old_activityid();
4909
        $this->set_mapping($modulename, $oldid, $newitemid, true);
4995
        $this->set_mapping($modulename, $oldid, $newitemid, true);
4910
    }
4996
    }
-
 
4997
 
-
 
4998
    /**
-
 
4999
     * Create a delegate section mapping.
-
 
5000
     *
-
 
5001
     * @param string $component The component name (frankenstyle)
-
 
5002
     * @param int $olditemid The old section id.
-
 
5003
     * @param int $newitemid The new section id.
-
 
5004
     */
-
 
5005
    protected function set_delegated_section_mapping($component, $olditemid, $newitemid) {
-
 
5006
        $this->set_mapping("course_section::$component", $olditemid, $newitemid);
-
 
5007
    }
4911
}
5008
}
Línea 4912... Línea 5009...
4912
 
5009
 
4913
/**
5010
/**
4914
 * Structure step in charge of creating/mapping all the qcats and qs
5011
 * Structure step in charge of creating/mapping all the qcats and qs
Línea 4919... Línea 5016...
4919
class restore_create_categories_and_questions extends restore_structure_step {
5016
class restore_create_categories_and_questions extends restore_structure_step {
Línea 4920... Línea 5017...
4920
 
5017
 
4921
    /** @var array $cachedcategory store a question category */
5018
    /** @var array $cachedcategory store a question category */
Línea -... Línea 5019...
-
 
5019
    protected $cachedcategory = null;
-
 
5020
 
-
 
5021
    /** @var stdClass the last question_bank_entry seen during the restore. Processed when we get to a question. */
-
 
5022
    protected $latestqbe;
-
 
5023
 
-
 
5024
    /** @var stdClass the last question_version seen during the restore. Processed when we get to a question. */
4922
    protected $cachedcategory = null;
5025
    protected $latestversion;
Línea 4923... Línea 5026...
4923
 
5026
 
4924
    protected function define_structure() {
5027
    protected function define_structure() {
4925
 
5028
 
Línea 5003... Línea 5106...
5003
        if ($mapping->info->contextlevel == CONTEXT_MODULE) {
5106
        if ($mapping->info->contextlevel == CONTEXT_MODULE) {
5004
            $mapping->parentitemid = $this->get_mappingid('context', $this->task->get_old_contextid());
5107
            $mapping->parentitemid = $this->get_mappingid('context', $this->task->get_old_contextid());
5005
        }
5108
        }
5006
        $data->contextid = $mapping->parentitemid;
5109
        $data->contextid = $mapping->parentitemid;
Línea -... Línea 5110...
-
 
5110
 
-
 
5111
        $context = \context::instance_by_id($data->contextid);
5007
 
5112
 
5008
        // Before 3.5, question categories could be created at top level.
5113
        // Before 3.5, question categories could be created at top level.
5009
        // From 3.5 onwards, all question categories should be a child of a special category called the "top" category.
5114
        // From 3.5 onwards, all question categories should be a child of a special category called the "top" category.
5010
        $restoretask = $this->get_task();
5115
        $restoretask = $this->get_task();
-
 
5116
        $before35 = $restoretask->backup_release_compare('3.5', '<') || $restoretask->backup_version_compare(20180205, '<');
-
 
5117
 
-
 
5118
        // We need a 'Top' question category for an activity module and activity modules are mapped to CONTEXT_COURSE and moved
-
 
5119
        // to the correct module context in restore_move_module_questions_categories.
-
 
5120
        // As we can't create a 'Top' category in CONTEXT_COURSE we'll make a default
5011
        $before35 = $restoretask->backup_release_compare('3.5', '<') || $restoretask->backup_version_compare(20180205, '<');
5121
        // qbank module and map it to that until they are created later.
-
 
5122
        if (empty($mapping->info->parent) && $before35) {
-
 
5123
            if ($context->contextlevel === CONTEXT_COURSE) {
-
 
5124
                $course = get_course($context->instanceid);
-
 
5125
                $defaultbank = \core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true);
-
 
5126
                $bankcontextid = $defaultbank->context->id;
-
 
5127
            } else {
-
 
5128
                $bankcontextid = $data->contextid;
5012
        if (empty($mapping->info->parent) && $before35) {
5129
            }
5013
            $top = question_get_top_category($data->contextid, true);
5130
            $top = question_get_top_category($bankcontextid, true);
5014
            $data->parent = $top->id;
5131
            $data->parent = $top->id;
Línea 5015... Línea 5132...
5015
        }
5132
        }
5016
 
-
 
5017
        if (empty($data->parent)) {
-
 
5018
            if (!$top = question_get_top_category($data->contextid)) {
-
 
5019
                $top = question_get_top_category($data->contextid, true);
-
 
5020
                $this->set_mapping('question_category_created', $oldid, $top->id, false, null, $data->contextid);
-
 
5021
            }
-
 
5022
            $this->set_mapping('question_category', $oldid, $top->id);
-
 
5023
        } else {
5133
 
5024
 
5134
        if (!empty($data->parent)) {
5025
            // Before 3.1, the 'stamp' field could be erroneously duplicated.
5135
            // Before 3.1, the 'stamp' field could be erroneously duplicated.
5026
            // From 3.1 onwards, there's a unique index of (contextid, stamp).
5136
            // From 3.1 onwards, there's a unique index of (contextid, stamp).
5027
            // If we encounter a duplicate in an old restore file, just generate a new stamp.
5137
            // If we encounter a duplicate in an old restore file, just generate a new stamp.
Línea 5044... Línea 5154...
5044
            $this->set_mapping('question_category_created', $oldid, $newitemid, false, null, $data->contextid);
5154
            $this->set_mapping('question_category_created', $oldid, $newitemid, false, null, $data->contextid);
5045
        }
5155
        }
5046
    }
5156
    }
Línea 5047... Línea 5157...
5047
 
5157
 
5048
    /**
5158
    /**
5049
     * Process pre 4.0 question data where in creates the record for version and entry table.
5159
     * Set up date to allow restore of questions from pre-4.0 backups.
5050
     *
5160
     *
5051
     * @param array $data the data from the XML file.
5161
     * @param stdClass $data the data from the XML file.
5052
     */
5162
     */
-
 
5163
    protected function process_question_legacy_data($data) {
-
 
5164
        $this->latestqbe = (object) [
-
 
5165
            'id' => $data->id,
-
 
5166
            'questioncategoryid' => $data->category,
-
 
5167
            'ownerid' => $data->createdby,
5053
    protected function process_question_legacy_data($data) {
5168
            'idnumber' => $data->idnumber ?? null,
Línea 5054... Línea -...
5054
        global $DB;
-
 
5055
 
5169
        ];
5056
        $oldid = $data->id;
5170
 
5057
        // Process question bank entry.
-
 
5058
        $entrydata = new stdClass();
-
 
5059
        $entrydata->questioncategoryid = $data->category;
5171
        $this->latestversion = (object) [
5060
        $userid = $this->get_mappingid('user', $data->createdby);
-
 
5061
        if ($userid) {
-
 
5062
            $entrydata->ownerid = $userid;
-
 
5063
        } else {
-
 
5064
            if (!$this->task->is_samesite()) {
-
 
5065
                $entrydata->ownerid = $this->task->get_userid();
-
 
5066
            }
-
 
5067
        }
-
 
5068
        // The idnumber if it exists also needs to be unique within a category or reset it to null.
-
 
5069
        if (isset($data->idnumber) && !$DB->record_exists('question_bank_entries',
5172
            'id' => $data->id,
5070
                ['idnumber' => $data->idnumber, 'questioncategoryid' => $data->category])) {
-
 
5071
            $entrydata->idnumber = $data->idnumber;
-
 
5072
        }
-
 
5073
 
-
 
5074
        $newentryid = $DB->insert_record('question_bank_entries', $entrydata);
-
 
5075
        // Process question versions.
-
 
5076
        $versiondata = new stdClass();
-
 
5077
        $versiondata->questionbankentryid = $newentryid;
-
 
5078
        $versiondata->version = 1;
-
 
5079
        // Question id is updated after inserting the question.
5173
            'version' => 1,
5080
        $versiondata->questionid = 0;
-
 
5081
        $versionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
5174
            'status' => $data->hidden ?
5082
        if ((int)$data->hidden === 1) {
5175
                \core_question\local\bank\question_version_status::QUESTION_STATUS_HIDDEN :
5083
            $versionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_HIDDEN;
-
 
5084
        }
-
 
5085
        $versiondata->status = $versionstatus;
-
 
5086
        $newversionid = $DB->insert_record('question_versions', $versiondata);
5176
                \core_question\local\bank\question_version_status::QUESTION_STATUS_READY,
Línea 5087... Línea 5177...
5087
        $this->set_mapping('question_version_created', $oldid, $newversionid);
5177
        ];
5088
    }
5178
    }
5089
 
5179
 
5090
    /**
5180
    /**
5091
     * Process question bank entry data.
5181
     * Process question bank entry data.
5092
     *
5182
     *
5093
     * @param array $data the data from the XML file.
-
 
5094
     */
-
 
5095
    protected function process_question_bank_entry($data) {
-
 
5096
        global $DB;
-
 
5097
 
-
 
5098
        $data = (object)$data;
-
 
5099
        $oldid = $data->id;
-
 
5100
 
5183
     * @param array $data the data from the XML file.
5101
        $questioncreated = $this->get_mappingid('question_category_created', $data->questioncategoryid) ? true : false;
-
 
5102
        $recordexist = $DB->record_exists('question_bank_entries', ['id' => $data->id,
-
 
5103
            'questioncategoryid' => $data->questioncategoryid]);
-
 
5104
        // Check we have category created.
-
 
5105
        if (!$questioncreated && $recordexist) {
-
 
5106
            return self::SKIP_ALL_CHILDREN;
5184
     */
5107
        }
-
 
5108
 
-
 
5109
        $data->questioncategoryid = $this->get_new_parentid('question_category');
5185
    protected function process_question_bank_entry($data) {
5110
        $userid = $this->get_mappingid('user', $data->ownerid);
-
 
5111
        if ($userid) {
-
 
5112
            $data->ownerid = $userid;
-
 
5113
        } else {
-
 
5114
            if (!$this->task->is_samesite()) {
-
 
5115
                $data->ownerid = $this->task->get_userid();
-
 
5116
            }
-
 
5117
        }
-
 
5118
 
-
 
5119
        // The idnumber if it exists also needs to be unique within a category or reset it to null.
-
 
5120
        if (!empty($data->idnumber) && $DB->record_exists('question_bank_entries',
-
 
5121
                ['idnumber' => $data->idnumber, 'questioncategoryid' => $data->questioncategoryid])) {
-
 
5122
            unset($data->idnumber);
-
 
5123
        }
-
 
5124
 
5186
        // We can only determine the right way to process this once we get to
Línea 5125... Línea 5187...
5125
        $newitemid = $DB->insert_record('question_bank_entries', $data);
5187
        // process_question and have more information, so for now just store.
5126
        $this->set_mapping('question_bank_entry', $oldid, $newitemid);
5188
        $this->latestqbe = (object) $data;
5127
    }
5189
    }
5128
 
5190
 
5129
    /**
5191
    /**
5130
     * Process question versions.
5192
     * Process question versions.
5131
     *
-
 
5132
     * @param array $data the data from the XML file.
-
 
5133
     */
-
 
5134
    protected function process_question_versions($data) {
-
 
5135
        global $DB;
-
 
5136
 
5193
     *
5137
        $data = (object)$data;
5194
     * @param array $data the data from the XML file.
5138
        $oldid = $data->id;
-
 
5139
 
-
 
5140
        $data->questionbankentryid = $this->get_new_parentid('question_bank_entry');
5195
     */
5141
        // Question id is updated after inserting the question.
5196
    protected function process_question_versions($data) {
Línea 5142... Línea 5197...
5142
        $data->questionid = 0;
5197
        // We can only determine the right way to process this once we get to
5143
        $newitemid = $DB->insert_record('question_versions', $data);
5198
        // process_question and have more information, so for now just store.
5144
        $this->set_mapping('question_versions', $oldid, $newitemid);
5199
        $this->latestversion = (object) $data;
5145
    }
5200
    }
5146
 
5201
 
5147
    /**
5202
    /**
5148
     * Process the actual question.
5203
     * Process the actual question.
Línea 5149... Línea 5204...
5149
     *
5204
     *
5150
     * @param array $data the data from the XML file.
5205
     * @param array $data the data from the XML file.
Línea 5151... Línea 5206...
5151
     */
5206
     */
-
 
5207
    protected function process_question($data) {
-
 
5208
        global $DB;
-
 
5209
 
-
 
5210
        $data = (object) $data;
-
 
5211
        $oldid = $data->id;
-
 
5212
 
-
 
5213
        // Check we have one mapping for this question.
5152
    protected function process_question($data) {
5214
        if (!$questionmapping = $this->get_mapping('question', $oldid)) {
5153
        global $DB;
5215
            // No mapping = this question doesn't need to be created/mapped.
5154
 
-
 
5155
        $data = (object)$data;
-
 
5156
        $oldid = $data->id;
-
 
5157
 
-
 
5158
        // Check if the backup is a pre 4.0 one.
-
 
5159
        $restoretask = $this->get_task();
5216
            return;
5160
        if ($restoretask->backup_release_compare('4.0', '<') || $restoretask->backup_version_compare(20220202, '<')) {
5217
        }
5161
            // Check we have one mapping for this question.
5218
 
5162
            if (!$questionmapping = $this->get_mapping('question', $oldid)) {
5219
        // Check if this is a pre 4.0 backup, then there will not be a question bank entry
5163
                return; // No mapping = this question doesn't need to be created/mapped.
5220
        // or question version in the file. So, we need to set up that data ready to be used below.
Línea 5204... Línea 5261...
5204
            if (!$this->task->is_samesite()) {
5261
            if (!$this->task->is_samesite()) {
5205
                $data->modifiedby = $this->task->get_userid();
5262
                $data->modifiedby = $this->task->get_userid();
5206
            }
5263
            }
5207
        }
5264
        }
Línea -... Línea 5265...
-
 
5265
 
-
 
5266
        // With newitemid = 0, let's create the question.
-
 
5267
        if (!$questionmapping->newitemid) {
-
 
5268
            // Now we know we are inserting a question, we may need to insert the questionbankentry.
-
 
5269
            if (empty($this->latestqbe->newid)) {
-
 
5270
                $this->latestqbe->oldid = $this->latestqbe->id;
-
 
5271
 
-
 
5272
                $this->latestqbe->questioncategoryid = $this->get_new_parentid('question_category');
-
 
5273
                $userid = $this->get_mappingid('user', $this->latestqbe->ownerid);
-
 
5274
                if ($userid) {
-
 
5275
                    $this->latestqbe->ownerid = $userid;
-
 
5276
                } else {
-
 
5277
                    if (!$this->task->is_samesite()) {
-
 
5278
                        $this->latestqbe->ownerid = $this->task->get_userid();
-
 
5279
                    }
-
 
5280
                }
-
 
5281
 
-
 
5282
                // The idnumber if it exists also needs to be unique within a category or reset it to null.
-
 
5283
                if (!empty($this->latestqbe->idnumber) && $DB->record_exists('question_bank_entries',
-
 
5284
                        ['idnumber' => $this->latestqbe->idnumber, 'questioncategoryid' => $this->latestqbe->questioncategoryid])) {
-
 
5285
                    unset($this->latestqbe->idnumber);
-
 
5286
                }
-
 
5287
 
-
 
5288
                $this->latestqbe->newid = $DB->insert_record('question_bank_entries', $this->latestqbe);
-
 
5289
                $this->set_mapping('question_bank_entry', $this->latestqbe->oldid, $this->latestqbe->newid);
-
 
5290
            }
-
 
5291
 
5208
 
5292
            // Now store the question.
5209
        $newitemid = $DB->insert_record('question', $data);
5293
            $newitemid = $DB->insert_record('question', $data);
5210
        $this->set_mapping('question', $oldid, $newitemid);
5294
            $this->set_mapping('question', $oldid, $newitemid);
5211
        // Also annotate them as question_created, we need
5295
            // Also annotate them as question_created, we need
5212
        // that later when remapping parents (keeping the old categoryid as parentid).
5296
            // that later when remapping parents (keeping the old categoryid as parentid).
5213
        $parentcatid = $this->get_old_parentid('question_category');
5297
            $parentcatid = $this->get_old_parentid('question_category');
-
 
5298
            $this->set_mapping('question_created', $oldid, $newitemid, false, null, $parentcatid);
5214
        $this->set_mapping('question_created', $oldid, $newitemid, false, null, $parentcatid);
5299
 
-
 
5300
            // Also insert this question_version.
5215
        // Now update the question_versions table with the new question id. we dont need to do that for random qtypes.
5301
            $oldqvid = $this->latestversion->id;
5216
        $legacyquestiondata = $this->get_mappingid('question_version_created', $oldid) ? true : false;
5302
            $this->latestversion->questionbankentryid = $this->latestqbe->newid;
-
 
5303
            $this->latestversion->questionid = $newitemid;
5217
        if ($legacyquestiondata) {
5304
            $newqvid = $DB->insert_record('question_versions', $this->latestversion);
-
 
5305
            $this->set_mapping('question_versions', $oldqvid, $newqvid);
5218
            $parentitemid = $this->get_mappingid('question_version_created', $oldid);
5306
 
-
 
5307
        } else {
-
 
5308
            // By performing this set_mapping() we make get_old/new_parentid() to work for all the
-
 
5309
            // children elements of the 'question' one (so qtype plugins will know the question they belong to).
-
 
5310
            $this->set_mapping('question', $oldid, $questionmapping->newitemid);
-
 
5311
 
-
 
5312
            // Also create the question_bank_entry and version mappings, if required.
5219
        } else {
5313
            $newquestionversion = $DB->get_record('question_versions', ['questionid' => $questionmapping->newitemid]);
-
 
5314
            $this->set_mapping('question_versions', $this->latestversion->id, $newquestionversion->id);
-
 
5315
            if (empty($this->latestqbe->newid)) {
-
 
5316
                $this->latestqbe->oldid = $this->latestqbe->id;
-
 
5317
                $this->latestqbe->newid = $newquestionversion->questionbankentryid;
-
 
5318
                $this->set_mapping('question_bank_entry', $this->latestqbe->oldid, $this->latestqbe->newid);
5220
            $parentitemid = $this->get_new_parentid('question_versions');
5319
            }
5221
        }
-
 
5222
        $version = new stdClass();
-
 
5223
        $version->id = $parentitemid;
-
 
5224
        $version->questionid = $newitemid;
-
 
Línea 5225... Línea 5320...
5225
        $DB->update_record('question_versions', $version);
5320
        }
5226
 
5321
 
5227
        // Note, we don't restore any question files yet
5322
        // Note, we don't restore any question files yet
5228
        // as far as the CONTEXT_MODULE categories still
5323
        // as far as the CONTEXT_MODULE categories still
5229
        // haven't their contexts to be restored to
5324
        // haven't their contexts to be restored to
5230
        // The {@link restore_create_question_files}, executed in the final step
5325
        // The {@see restore_create_question_files}, executed in the final
Línea 5231... Línea 5326...
5231
        // step will be in charge of restoring all the question files.
5326
        // step will be in charge of restoring all the question files.
5232
    }
5327
    }
Línea 5263... Línea 5358...
5263
            // of all the question's hints in DB as slower fallback. MDL-33863.
5358
            // of all the question's hints in DB as slower fallback. MDL-33863.
5264
            if (!$newitemid) {
5359
            if (!$newitemid) {
5265
                $potentialhints = $DB->get_records('question_hints',
5360
                $potentialhints = $DB->get_records('question_hints',
5266
                        array('questionid' => $newquestionid), '', 'id, hint');
5361
                        array('questionid' => $newquestionid), '', 'id, hint');
5267
                foreach ($potentialhints as $potentialhint) {
5362
                foreach ($potentialhints as $potentialhint) {
5268
                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
-
 
5269
                    $cleanhint = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $potentialhint->hint); // Clean CTRL chars.
5363
                    $cleanhint = core_text::trim_ctrl_chars($potentialhint->hint); // Clean CTRL chars.
5270
                    $cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
5364
                    $cleanhint = preg_replace("/\r\n|\r/", "\n", $cleanhint); // Normalize line ending.
5271
                    if ($cleanhint === $data->hint) {
5365
                    if ($cleanhint === $data->hint) {
5272
                        $newitemid = $data->id;
5366
                        $newitemid = $data->id;
5273
                    }
5367
                    }
5274
                }
5368
                }
Línea 5299... Línea 5393...
5299
            return;
5393
            return;
5300
        }
5394
        }
Línea 5301... Línea 5395...
5301
 
5395
 
5302
        if (core_tag_tag::is_enabled('core_question', 'question')) {
5396
        if (core_tag_tag::is_enabled('core_question', 'question')) {
5303
            $tagname = $data->rawname;
-
 
5304
            if (!empty($data->contextid) && $newcontextid = $this->get_mappingid('context', $data->contextid)) {
-
 
5305
                    $tagcontextid = $newcontextid;
-
 
5306
            } else {
5397
            $tagname = $data->rawname;
5307
                // Get the category, so we can then later get the context.
5398
            // Get the category, so we can then later get the context.
5308
                $categoryid = $this->get_new_parentid('question_category');
5399
            $categoryid = $this->get_new_parentid('question_category');
5309
                if (empty($this->cachedcategory) || $this->cachedcategory->id != $categoryid) {
5400
            if (empty($this->cachedcategory) || $this->cachedcategory->id != $categoryid) {
5310
                    $this->cachedcategory = $DB->get_record('question_categories', array('id' => $categoryid));
-
 
5311
                }
-
 
5312
                $tagcontextid = $this->cachedcategory->contextid;
5401
                $this->cachedcategory = $DB->get_record('question_categories', ['id' => $categoryid]);
-
 
5402
            }
5313
            }
5403
            $tagcontextid = $this->cachedcategory->contextid;
5314
            // Add the tag to the question.
5404
            // Add the tag to the question.
-
 
5405
            core_tag_tag::add_item_tag('core_question',
-
 
5406
                'question',
5315
            core_tag_tag::add_item_tag('core_question', 'question', $newquestion,
5407
                $newquestion,
5316
                    context::instance_by_id($tagcontextid),
5408
                context::instance_by_id($tagcontextid),
-
 
5409
                $tagname
5317
                    $tagname);
5410
            );
5318
        }
5411
        }
Línea 5319... Línea 5412...
5319
    }
5412
    }
5320
 
5413
 
Línea 5338... Línea 5431...
5338
                    $DB->set_field('question_categories', 'parent', $newparent, array('id' => $dbcat->id));
5431
                    $DB->set_field('question_categories', 'parent', $newparent, array('id' => $dbcat->id));
5339
                } else {
5432
                } else {
5340
                    $newparent = 0; // No ctx match for both cats, no parent relationship
5433
                    $newparent = 0; // No ctx match for both cats, no parent relationship
5341
                }
5434
                }
5342
            }
5435
            }
-
 
5436
            $context = \core\context::instance_by_id($dbcat->contextid);
5343
            // Here with $newparent empty, problem with contexts or remapping, set it to top cat
5437
            // Here with $newparent empty, problem with contexts or remapping, set it to top cat
5344
            if (!$newparent && $dbcat->parent) {
5438
            if (!$newparent && $dbcat->parent && $context->contextlevel === CONTEXT_MODULE) {
5345
                $topcat = question_get_top_category($dbcat->contextid, true);
5439
                $topcat = question_get_top_category($dbcat->contextid, true);
5346
                if ($dbcat->parent != $topcat->id) {
5440
                if ($dbcat->parent != $topcat->id) {
5347
                    $DB->set_field('question_categories', 'parent', $topcat->id, array('id' => $dbcat->id));
5441
                    $DB->set_field('question_categories', 'parent', $topcat->id, array('id' => $dbcat->id));
5348
                }
5442
                }
5349
            }
5443
            }
Línea 5384... Línea 5478...
5384
 
5478
 
Línea 5385... Línea 5479...
5385
        $after35 = $this->task->backup_release_compare('3.5', '>=') && $this->task->backup_version_compare(20180205, '>');
5479
        $after35 = $this->task->backup_release_compare('3.5', '>=') && $this->task->backup_version_compare(20180205, '>');
5386
 
5480
 
-
 
5481
        $contexts = restore_dbops::restore_get_question_banks($this->get_restoreid(), CONTEXT_MODULE);
-
 
5482
        foreach ($contexts as $contextid => $contextlevel) {
5387
        $contexts = restore_dbops::restore_get_question_banks($this->get_restoreid(), CONTEXT_MODULE);
5483
            if (!$newcontext = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'context', $contextid)) {
-
 
5484
                // The bank for the question categories required by this module was not included in the backup,
5388
        foreach ($contexts as $contextid => $contextlevel) {
5485
                // but if that context still exists on the site and the user has access then point question references
5389
            // Only if context mapping exists (i.e. the module has been restored)
5486
                // to the originals.
5390
            if ($newcontext = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'context', $contextid)) {
5487
                $originalcontext = context::instance_by_id($contextid, IGNORE_MISSING);
5391
                // Update all the qcats having their parentitemid set to the original contextid
5488
                if ($originalcontext && has_capability('mod/qbank:view', $originalcontext)) {
5392
                $modulecats = $DB->get_records_sql("SELECT itemid, newitemid, info
5489
                    $originalquestions = get_questions_category(question_get_top_category($contextid), false);
5393
                                                      FROM {backup_ids_temp}
5490
                    $targetcoursecontext = context_course::instance($this->get_courseid());
5394
                                                     WHERE backupid = ?
5491
                    foreach ($originalquestions as $originalquestion) {
-
 
5492
                        $backupids = restore_dbops::get_backup_ids_record(
5395
                                                       AND itemname = 'question_category'
5493
                            $this->get_restoreid(),
5396
                                                       AND parentitemid = ?", array($this->get_restoreid(), $contextid));
5494
                            'question',
5397
                $top = question_get_top_category($newcontext->newitemid, true);
5495
                            $originalquestion->id,
-
 
5496
                        );
5398
                $oldtopid = 0;
5497
                        if (!$backupids) {
5399
                $categoryids = [];
5498
                            continue; // This question was not included in the backup.
5400
                foreach ($modulecats as $modulecat) {
5499
                        }
-
 
5500
                        // Restored question references will point to the restored copy of the question. Select question references
5401
                    // Before 3.5, question categories could be created at top level.
5501
                        // that point to that restored copy, only if they are within the target course's context, so we can update
5402
                    // From 3.5 onwards, all question categories should be a child of a special category called the "top" category.
5502
                        // them to point to the original question.
5403
                    $info = backup_controller_dbops::decode_backup_temp_info($modulecat->info);
5503
                        $conpathlike = $DB->sql_like('con.path', '?');
5404
                    if ($after35 && empty($info->parent)) {
5504
                        $references = $DB->get_records_sql(
-
 
5505
                            "SELECT qr.id, qr.questionbankentryid
-
 
5506
                               FROM {question_references} qr
5405
                        $oldtopid = $modulecat->newitemid;
5507
                                    JOIN {context} con ON qr.usingcontextid = con.id
-
 
5508
                                    JOIN {question_versions} qv ON qv.questionbankentryid = qr.questionbankentryid
5406
                        $modulecat->newitemid = $top->id;
5509
                              WHERE qv.questionid = ?
5407
                    } else {
5510
                                    AND {$conpathlike}",
5408
                        $cat = new stdClass();
5511
                            [
-
 
5512
                                $backupids->newitemid,
-
 
5513
                                $targetcoursecontext->path . '/%',
5409
                        $cat->id = $modulecat->newitemid;
5514
                            ],
5410
                        $cat->contextid = $newcontext->newitemid;
5515
                        );
5411
                        if (empty($info->parent)) {
5516
                        if (empty($references)) {
-
 
5517
                            continue;
-
 
5518
                        }
5412
                            $cat->parent = $top->id;
5519
                        [$refin, $refparams] = $DB->get_in_or_equal(array_keys($references));
-
 
5520
                        $DB->set_field_select(
-
 
5521
                            'question_references',
5413
                        }
5522
                            'questionbankentryid',
-
 
5523
                            $DB->get_field('question_versions', 'questionbankentryid', ['questionid' => $backupids->itemid]),
-
 
5524
                            'id ' . $refin,
5414
                        $DB->update_record('question_categories', $cat);
5525
                            $refparams,
-
 
5526
                        );
-
 
5527
                    }
-
 
5528
                    continue;
-
 
5529
                }
-
 
5530
                // We have no target question bank so create a default bank for categories without a module to attach to.
-
 
5531
                // This can occur when a quiz backup contains references to a question bank module,
-
 
5532
                // that was not included in the backup and does not exist in the site being restored to.
-
 
5533
                $course = get_course($this->get_courseid());
-
 
5534
                $defaultqbank = core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true);
-
 
5535
                $context = context_module::instance($defaultqbank->id);
5415
                        $categoryids[] = (int)$cat->id;
5536
                $newcontext = new stdClass();
-
 
5537
                $newcontext->newitemid = $context->id;
-
 
5538
            }
-
 
5539
            // Only if context mapping exists (i.e. the module has been restored)
-
 
5540
            // Update all the qcats having their parentitemid set to the original contextid.
-
 
5541
            $modulecats = $DB->get_records_sql("SELECT itemid, newitemid, info
5416
                    }
5542
                                                  FROM {backup_ids_temp}
5417
 
5543
                                                 WHERE backupid = ?
-
 
5544
                                                   AND itemname = 'question_category'
-
 
5545
                                                   AND parentitemid = ?",
-
 
5546
                [$this->get_restoreid(), $contextid]
-
 
5547
            );
-
 
5548
            $top = question_get_top_category($newcontext->newitemid, true);
-
 
5549
            $oldtopid = 0;
-
 
5550
            $categoryids = [];
5418
                    // And set new contextid (and maybe update newitemid) also in question_category mapping (will be
5551
            foreach ($modulecats as $modulecat) {
-
 
5552
                // Before 3.5, question categories could be created at top level.
-
 
5553
                // From 3.5 onwards, all question categories should be a child of a special category called the "top" category.
-
 
5554
                $info = backup_controller_dbops::decode_backup_temp_info($modulecat->info);
-
 
5555
                if ($after35 && empty($info->parent)) {
-
 
5556
                    $oldtopid = $modulecat->newitemid;
-
 
5557
                    $modulecat->newitemid = $top->id;
-
 
5558
                } else {
5419
                    // used by {@link restore_create_question_files} later.
5559
                    $cat = new stdClass();
-
 
5560
                    $cat->id = $modulecat->newitemid;
-
 
5561
                    $cat->contextid = $newcontext->newitemid;
-
 
5562
                    if (empty($info->parent)) {
-
 
5563
                        $cat->parent = $top->id;
-
 
5564
                    }
5420
                    restore_dbops::set_backup_ids_record($this->get_restoreid(), 'question_category', $modulecat->itemid,
5565
                    $DB->update_record('question_categories', $cat);
Línea -... Línea 5566...
-
 
5566
                    $categoryids[] = (int) $cat->id;
-
 
5567
                }
-
 
5568
 
-
 
5569
                // And set new contextid (and maybe update newitemid) also in question_category mapping (will be
-
 
5570
                // used by {@see restore_create_question_files} later.
-
 
5571
                restore_dbops::set_backup_ids_record($this->get_restoreid(),
-
 
5572
                    'question_category',
-
 
5573
                    $modulecat->itemid,
-
 
5574
                    $modulecat->newitemid,
-
 
5575
                    $newcontext->newitemid
5421
                            $modulecat->newitemid, $newcontext->newitemid);
5576
                );
5422
                }
5577
            }
5423
 
5578
 
5424
                // Update the context id of any tags applied to any questions in these categories.
5579
            // Update the context id of any tags applied to any questions in these categories.
5425
                if ($categoryids) {
5580
            if ($categoryids) {
5426
                    [$categorysql, $categoryidparams] = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
5581
                [$categorysql, $categoryidparams] = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
5427
                    $sqlupdate = "UPDATE {tag_instance}
5582
                $sqlupdate = "UPDATE {tag_instance}
5428
                                     SET contextid = :newcontext
5583
                                 SET contextid = :newcontext
5429
                                   WHERE component = :component
5584
                               WHERE component = :component
5430
                                         AND itemtype = :itemtype
5585
                                 AND itemtype = :itemtype
5431
                                         AND itemid IN (SELECT DISTINCT bi.newitemid as questionid
5586
                                 AND itemid IN (SELECT DISTINCT bi.newitemid as questionid
5432
                                                          FROM {backup_ids_temp} bi
5587
                                FROM {backup_ids_temp} bi
5433
                                                          JOIN {question} q ON q.id = bi.newitemid
5588
                                JOIN {question} q ON q.id = bi.newitemid
5434
                                                          JOIN {question_versions} qv ON qv.questionid = q.id
5589
                                JOIN {question_versions} qv ON qv.questionid = q.id
5435
                                                          JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
5590
                                JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
5436
                                                         WHERE bi.backupid = :backupid AND bi.itemname = 'question_created'
5591
                               WHERE bi.backupid = :backupid AND bi.itemname = 'question_created'
5437
                                                               AND qbe.questioncategoryid {$categorysql}) ";
5592
                                 AND qbe.questioncategoryid {$categorysql}) ";
5438
                    $params = [
5593
                $params = [
5439
                        'newcontext' => $newcontext->newitemid,
5594
                        'newcontext' => $newcontext->newitemid,
5440
                        'component' => 'core_question',
5595
                        'component' => 'core_question',
5441
                        'itemtype' => 'question',
5596
                        'itemtype' => 'question',
5442
                        'backupid' => $this->get_restoreid(),
5597
                        'backupid' => $this->get_restoreid(),
5443
                    ];
5598
                ];
5444
                    $params += $categoryidparams;
5599
                $params += $categoryidparams;
5445
                    $DB->execute($sqlupdate, $params);
5600
                $DB->execute($sqlupdate, $params);
5446
 
5601
 
5447
                    // As explained in {@see restore_quiz_activity_structure_step::process_quiz_question_legacy_instance()}
5602
                // As explained in {@see restore_quiz_activity_structure_step::process_quiz_question_legacy_instance()}
5448
                    // question_set_references relating to random questions restored from old backups,
5603
                // question_set_references relating to random questions restored from old backups,
5449
                    // which pick from context_module question_categores, will have been restored with the wrong questioncontextid.
5604
                // which pick from context_module question_categores, will have been restored with the wrong questioncontextid.
5450
                    // So, now, we need to find those, and updated the questioncontextid.
5605
                // So, now, we need to find those, and updated the questioncontextid.
5451
                    // We can only find them by picking apart the filter conditions, and seeign which categories they refer to.
5606
                // We can only find them by picking apart the filter conditions, and seeign which categories they refer to.
5452
 
5607
 
5453
                    // We need to check all the question_set_references belonging to this context_module.
5608
                // We need to check all the question_set_references belonging to this context_module.
5454
                    $references = $DB->get_records('question_set_references', ['usingcontextid' => $newcontext->newitemid]);
5609
                $references = $DB->get_records('question_set_references', ['usingcontextid' => $newcontext->newitemid]);
5455
                    foreach ($references as $reference) {
5610
                foreach ($references as $reference) {
5456
                        $filtercondition = json_decode($reference->filtercondition);
5611
                    $filtercondition = json_decode($reference->filtercondition);
5457
                        if (!empty($filtercondition->questioncategoryid) &&
5612
                    if (!empty($filtercondition->questioncategoryid) &&
5458
                                in_array($filtercondition->questioncategoryid, $categoryids)) {
5613
                            in_array($filtercondition->questioncategoryid, $categoryids)) {
5459
                            // This is one of ours, update the questionscontextid.
5614
                        // This is one of ours, update the questionscontextid.
5460
                            $DB->set_field('question_set_references',
-
 
5461
                                'questionscontextid', $newcontext->newitemid,
5615
                        $DB->set_field('question_set_references',
5462
                                ['id' => $reference->id]);
5616
                            'questionscontextid', $newcontext->newitemid,
-
 
5617
                            ['id' => $reference->id]);
Línea 5463... Línea 5618...
5463
                        }
5618
                    }
5464
                    }
5619
                }
5465
                }
5620
            }
5466
 
5621
 
-
 
5622
            // Now set the parent id for the question categories that were in the top category in the course context
-
 
5623
            // and have been moved now.
5467
                // Now set the parent id for the question categories that were in the top category in the course context
5624
            if ($oldtopid) {
5468
                // and have been moved now.
5625
                $DB->set_field('question_categories',
5469
                if ($oldtopid) {
5626
                    'parent',
5470
                    $DB->set_field('question_categories', 'parent', $top->id,
5627
                    $top->id,
-
 
5628
                    ['contextid' => $newcontext->newitemid, 'parent' => $oldtopid]
-
 
5629
                );
-
 
5630
            }
-
 
5631
        }
-
 
5632
        // Remove any remaining course-level question categories from the restored course.
-
 
5633
        $coursecatsql = "
-
 
5634
            SELECT qc.id AS categoryid
-
 
5635
              FROM {question_categories} qc
-
 
5636
              JOIN {context} c ON c.id = qc.contextid
-
 
5637
             WHERE c.contextlevel = :courselevel AND c.instanceid = :courseid
-
 
5638
        ";
-
 
5639
        $DB->delete_records_subquery(
-
 
5640
            'question_categories',
-
 
5641
            'id',
5471
                            array('contextid' => $newcontext->newitemid, 'parent' => $oldtopid));
5642
            'categoryid',
5472
                }
5643
            $coursecatsql,
Línea 5473... Línea 5644...
5473
            }
5644
            ['courselevel' => context_course::LEVEL, 'courseid' => $this->task->get_courseid()]
5474
        }
5645
        );
Línea 6248... Línea 6419...
6248
     * @param array $data the data from the XML file.
6419
     * @param array $data the data from the XML file.
6249
     */
6420
     */
6250
    public function process_question_set_reference($data) {
6421
    public function process_question_set_reference($data) {
6251
        global $DB;
6422
        global $DB;
6252
        $data = (object) $data;
6423
        $data = (object) $data;
-
 
6424
        $owncontext = $data->usingcontextid == $data->questionscontextid;
6253
        $data->usingcontextid = $this->get_mappingid('context', $data->usingcontextid);
6425
        $data->usingcontextid = $this->get_mappingid('context', $data->usingcontextid);
6254
        $data->itemid = $this->get_new_parentid('quiz_question_instance');
6426
        $data->itemid = $this->get_new_parentid('quiz_question_instance');
6255
        $filtercondition = json_decode($data->filtercondition, true);
6427
        $filtercondition = json_decode($data->filtercondition, true);
Línea 6256... Línea 6428...
6256
 
6428
 
Línea 6260... Línea 6432...
6260
                    $filtercondition);
6432
                    $filtercondition);
6261
        }
6433
        }
Línea 6262... Línea 6434...
6262
 
6434
 
6263
        // Map category id used for category filter condition and corresponding context id.
6435
        // Map category id used for category filter condition and corresponding context id.
-
 
6436
        $oldcategoryid = $filtercondition['filter']['category']['values'][0];
-
 
6437
        // Decide if we're going to refer back to the original category, or to the new category.
-
 
6438
        // Are we restoring to a different site?
-
 
6439
        // Has the original context or category been deleted?
-
 
6440
        // Did the old category belong to the same context as the original set reference?
-
 
6441
        // Are we allowed to use its questions?
-
 
6442
        $questionscontext = context::instance_by_id($data->questionscontextid, IGNORE_MISSING);
-
 
6443
        if (
-
 
6444
            !$this->get_task()->is_samesite()
-
 
6445
            || !$questionscontext
-
 
6446
            || !$DB->record_exists('question_categories', ['id' => $oldcategoryid])
-
 
6447
            || $owncontext
-
 
6448
            || !has_capability('moodle/question:useall', $questionscontext)
6264
        $oldcategoryid = $filtercondition['filter']['category']['values'][0];
6449
        ) {
6265
        $newcategoryid = $this->get_mappingid('question_category', $oldcategoryid);
6450
            $newcategoryid = $this->get_mappingid('question_category', $oldcategoryid);
-
 
6451
            $filtercondition['filter']['category']['values'][0] = $newcategoryid;
Línea 6266... Línea 6452...
6266
        $filtercondition['filter']['category']['values'][0] = $newcategoryid;
6452
        }
6267
 
6453
 
6268
        if ($context = $this->get_mappingid('context', $data->questionscontextid)) {
6454
        if ($context = $this->get_mappingid('context', $data->questionscontextid)) {
6269
            $data->questionscontextid = $context;
6455
            $data->questionscontextid = $context;