Skip to content

Commit 4f855de

Browse files
author
David Monllao
committed
Merge branch 'wip-MDL-63165-master' of https://github.com/Beedell/moodle
2 parents efbbbf3 + d0a6044 commit 4f855de

File tree

4 files changed

+114
-21
lines changed

4 files changed

+114
-21
lines changed

lib/questionlib.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,30 @@ function question_categorylist($categoryid) {
14871487
return $categorylist;
14881488
}
14891489

1490+
/**
1491+
* Get all parent categories of a given question category in decending order.
1492+
* @param int $categoryid for which you want to find the parents.
1493+
* @return array of question category ids of all parents categories.
1494+
*/
1495+
function question_categorylist_parents(int $categoryid) {
1496+
global $DB;
1497+
$parent = $DB->get_field('question_categories', 'parent', array('id' => $categoryid));
1498+
if (!$parent) {
1499+
return [];
1500+
}
1501+
$categorylist = [$parent];
1502+
$currentid = $parent;
1503+
while ($currentid) {
1504+
$currentid = $DB->get_field('question_categories', 'parent', array('id' => $currentid));
1505+
if ($currentid) {
1506+
$categorylist[] = $currentid;
1507+
}
1508+
}
1509+
// Present the list in decending order (the top category at the top).
1510+
$categorylist = array_reverse($categorylist);
1511+
return $categorylist;
1512+
}
1513+
14901514
//===========================
14911515
// Import/Export Functions
14921516
//===========================

lib/tests/questionlib_test.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,4 +1986,25 @@ public function test_question_has_capability_on_wrong_param_type() {
19861986
$this->expectExceptionMessage('$questionorid parameter needs to be an integer or an object.');
19871987
question_has_capability_on('one', 'tag');
19881988
}
1989+
1990+
/**
1991+
* Test of question_categorylist_parents function.
1992+
*/
1993+
public function test_question_categorylist_parents() {
1994+
$this->resetAfterTest();
1995+
$generator = $this->getDataGenerator();
1996+
$questiongenerator = $generator->get_plugin_generator('core_question');
1997+
$category = $generator->create_category();
1998+
$context = context_coursecat::instance($category->id);
1999+
// Create a top category.
2000+
$cat0 = question_get_top_category($context->id, true);
2001+
// Add sub-categories.
2002+
$cat1 = $questiongenerator->create_question_category(['parent' => $cat0->id]);
2003+
$cat2 = $questiongenerator->create_question_category(['parent' => $cat1->id]);
2004+
// Test the 'get parents' function.
2005+
$parentcategories = question_categorylist_parents($cat2->id);
2006+
$this->assertEquals($cat0->id, $parentcategories[0]);
2007+
$this->assertEquals($cat1->id, $parentcategories[1]);
2008+
$this->assertCount(2, $parentcategories);
2009+
}
19892010
}

question/format.php

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,10 @@ public function importpreprocess() {
282282
/**
283283
* Process the file
284284
* This method should not normally be overidden
285-
* @param object $category
286285
* @return bool success
287286
*/
288-
public function importprocess($category) {
289-
global $USER, $CFG, $DB, $OUTPUT;
287+
public function importprocess() {
288+
global $USER, $DB, $OUTPUT;
290289

291290
// Raise time and memory, as importing can be quite intensive.
292291
core_php_time_limit::raise();
@@ -543,10 +542,14 @@ protected function create_category_path($catpath) {
543542
} else if ($category = $DB->get_record('question_categories',
544543
array('name' => $catname, 'contextid' => $context->id, 'parent' => $parent))) {
545544
$parent = $category->id;
546-
} else if ($parent == 0) {
547-
$category = question_get_top_category($context->id, true);
548-
$parent = $category->id;
549545
} else {
546+
if ($catname == 'top') {
547+
// Should not happen, but if it does just move on.
548+
// Occurs when there has been some import/export that has created
549+
// multiple nested 'top' categories (due to old bug solved by MDL-63165).
550+
// Not throwing an error here helps clean up old errors (silently).
551+
continue;
552+
}
550553
require_capability('moodle/question:managecategory', $context);
551554
// create the new category
552555
$category = new stdClass();
@@ -556,9 +559,8 @@ protected function create_category_path($catpath) {
556559
$category->parent = $parent;
557560
$category->sortorder = 999;
558561
$category->stamp = make_unique_id_code();
559-
$id = $DB->insert_record('question_categories', $category);
560-
$category->id = $id;
561-
$parent = $id;
562+
$category->id = $DB->insert_record('question_categories', $category);
563+
$parent = $category->id;
562564
}
563565
}
564566
return $category;
@@ -800,7 +802,13 @@ protected function presave_process($content) {
800802
* @return string The content of the export.
801803
*/
802804
public function exportprocess($checkcapabilities = true) {
803-
global $CFG, $OUTPUT, $DB, $USER;
805+
global $CFG, $DB;
806+
807+
// Get the parents (from database) for this category.
808+
$parents = [];
809+
if ($this->category) {
810+
$parents = question_categorylist_parents($this->category->id);
811+
}
804812

805813
// get the questions (from database) in this category
806814
// only get q's with no parents (no cloze subquestions specifically)
@@ -821,7 +829,17 @@ public function exportprocess($checkcapabilities = true) {
821829
// file if selected. 0 means that it will get printed before the 1st question
822830
$trackcategory = 0;
823831

824-
// iterate through questions
832+
// Array of categories written to file.
833+
$writtencategories = [];
834+
835+
foreach ($parents as $parent) {
836+
$categoryname = $this->get_category_path($parent, $this->contexttofile);
837+
// Create 'dummy' question for category export.
838+
$dummyquestion = $this->create_dummy_question_representing_category($categoryname);
839+
$expout .= $this->writequestion($dummyquestion) . "\n";
840+
$writtencategories[] = $parent;
841+
}
842+
825843
foreach ($questions as $question) {
826844
// used by file api
827845
$contextid = $DB->get_field('question_categories', 'contextid',
@@ -840,19 +858,33 @@ public function exportprocess($checkcapabilities = true) {
840858

841859
// check if we need to record category change
842860
if ($this->cattofile) {
861+
$addnewcat = false;
843862
if ($question->category != $trackcategory) {
863+
$addnewcat = true;
844864
$trackcategory = $question->category;
845865
$categoryname = $this->get_category_path($trackcategory, $this->contexttofile);
846-
847-
// create 'dummy' question for category export
848-
$dummyquestion = new stdClass();
849-
$dummyquestion->qtype = 'category';
850-
$dummyquestion->category = $categoryname;
851-
$dummyquestion->name = 'Switch category to ' . $categoryname;
852-
$dummyquestion->id = 0;
853-
$dummyquestion->questiontextformat = '';
854-
$dummyquestion->contextid = 0;
866+
}
867+
$trackcategoryparents = question_categorylist_parents($trackcategory);
868+
// Check if we need to record empty parents categories.
869+
foreach ($trackcategoryparents as $trackcategoryparent) {
870+
// If parent wasn't written.
871+
if (!in_array($trackcategoryparent, $writtencategories)) {
872+
// If parent is empty.
873+
if (!count($DB->get_records('question', array('category' => $trackcategoryparent)))) {
874+
$categoryname = $this->get_category_path($trackcategoryparent, $this->contexttofile);
875+
// Create 'dummy' question for parent category.
876+
$dummyquestion = $this->create_dummy_question_representing_category($categoryname);
877+
$expout .= $this->writequestion($dummyquestion) . "\n";
878+
$writtencategories[] = $trackcategoryparent;
879+
}
880+
}
881+
}
882+
if ($addnewcat && !in_array($trackcategory, $writtencategories)) {
883+
$categoryname = $this->get_category_path($trackcategory, $this->contexttofile);
884+
// Create 'dummy' question for category.
885+
$dummyquestion = $this->create_dummy_question_representing_category($categoryname);
855886
$expout .= $this->writequestion($dummyquestion) . "\n";
887+
$writtencategories[] = $trackcategory;
856888
}
857889
}
858890

@@ -878,6 +910,22 @@ public function exportprocess($checkcapabilities = true) {
878910
return $expout;
879911
}
880912

913+
/**
914+
* Create 'dummy' question for category export.
915+
* @param string $categoryname the name of the category
916+
* @return stdClass 'dummy' question for category
917+
*/
918+
protected function create_dummy_question_representing_category(string $categoryname) {
919+
$dummyquestion = new stdClass();
920+
$dummyquestion->qtype = 'category';
921+
$dummyquestion->category = $categoryname;
922+
$dummyquestion->id = 0;
923+
$dummyquestion->questiontextformat = '';
924+
$dummyquestion->contextid = 0;
925+
$dummyquestion->name = 'Switch category to ' . $categoryname;
926+
return $dummyquestion;
927+
}
928+
881929
/**
882930
* get the category as a path (e.g., tom/dick/harry)
883931
* @param int id the id of the most nested catgory

question/import.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
}
121121

122122
// Process the uploaded file
123-
if (!$qformat->importprocess($category)) {
123+
if (!$qformat->importprocess()) {
124124
print_error('cannotimport', '', $thispageurl->out());
125125
}
126126

0 commit comments

Comments
 (0)