diff --git a/mod/quiz/db/upgrade.php b/mod/quiz/db/upgrade.php
index 31a05191d5..aaa42c1d0b 100644
--- a/mod/quiz/db/upgrade.php
+++ b/mod/quiz/db/upgrade.php
@@ -1,6 +1,6 @@
prefix . 'quiz SET review = ' .
sql_bitor(sql_bitand('review', sql_bitnot(QUIZ_REVIEW_OVERALLFEEDBACK)),
@@ -82,7 +82,7 @@ function xmldb_quiz_upgrade($oldversion=0) {
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN) << 14) |
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED) << 12));
}
-
+
return $result;
}
diff --git a/mod/quiz/edit.php b/mod/quiz/edit.php
index 4930d296d5..dfe341e965 100644
--- a/mod/quiz/edit.php
+++ b/mod/quiz/edit.php
@@ -30,12 +30,12 @@
require_once($CFG->dirroot.'/mod/quiz/editlib.php');
/**
- * Callback function called from question_list() function (which is called from showbank()
+ * Callback function called from question_list() function (which is called from showbank())
* Displays action icon as first action for each question.
*/
- function module_specific_actions($pageurl, $questionid, $cmid){
+ function module_specific_actions($pageurl, $questionid, $cmid, $canuse){
global $CFG;
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
+ if ($canuse){
$straddtoquiz = get_string("addtoquiz", "quiz");
$out = "get_query_string()."&addquestion=$questionid&sesskey=".sesskey()."\">pixpath/t/moveleft.gif\" alt=\"$straddtoquiz\" /> ";
@@ -45,28 +45,24 @@
}
}
/**
- * Callback function called from question_list() function (which is called from showbank()
+ * Callback function called from question_list() function (which is called from showbank())
* Displays button in form with checkboxes for each question.
*/
function module_specific_buttons($cmid){
global $THEME;
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
- $straddtoquiz = get_string("addtoquiz", "quiz");
- $out = "larrow} $straddtoquiz\" />\n";
- $out .= '
';
- return $out;
- } else {
- return '';
- }
+ $straddtoquiz = get_string("addtoquiz", "quiz");
+ $out = "larrow} $straddtoquiz\" />\n";
+ echo ' ';
+ return $out;
}
-
-
+
+
/**
- * Callback function called from question_list() function (which is called from showbank()
- * Displays button in form with checkboxes for each question.
+ * Callback function called from question_list() function (which is called from showbank())
*/
- function module_specific_controls($totalnumber, $recurse, $categoryid, $cmid){
- if (has_capability("mod/quiz:manage", get_context_instance(CONTEXT_MODULE, $cmid))){
+ function module_specific_controls($totalnumber, $recurse, $category, $cmid){
+ $catcontext = get_context_instance_by_id($category->contextid);
+ if (has_capability('moodle/question:useall', $catcontext)){
for ($i = 1;$i <= min(10, $totalnumber); $i++) {
$randomcount[$i] = $i;
}
@@ -76,16 +72,16 @@
$out = ' ';
$out .= get_string('addrandom', 'quiz', choose_from_menu($randomcount, 'randomcount', '1', '', '', '', true));
$out .= '';
- $out .= "";
+ $out .= "id\" />";
$out .= ' ';
$out .= helpbutton('random', get_string('random', 'quiz'), 'quiz', true, false, '', true);
- return $out;
} else {
- return '';
+ $out = '';
}
- }
-
- list($thispageurl, $courseid, $cmid, $cm, $quiz, $pagevars) = question_edit_setup(true);
+ return $out;
+ }
+
+ list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) = question_edit_setup('editq', true);
//these params are only passed from page request to request while we stay on this page
//otherwise they would go in question_edit_setup
@@ -99,12 +95,12 @@
if ($quiz_reordertool != 0) {
$thispageurl->param('reordertool', $quiz_reordertool);
}
-
+
$strquizzes = get_string('modulenameplural', 'quiz');
$strquiz = get_string('modulename', 'quiz');
$streditingquestions = get_string('editquestions', "quiz");
$streditingquiz = get_string('editinga', 'moodle', $strquiz);
-
+
@@ -112,15 +108,14 @@
if (! $course = get_record("course", "id", $quiz->course)) {
error("This course doesn't exist");
}
- $coursecontext = get_context_instance(CONTEXT_COURSE, $quiz->course);
- $quizcontext = get_context_instance(CONTEXT_MODULE, $quiz->cmid);
-
+
// Log this visit.
add_to_log($cm->course, 'quiz', 'editquestions',
"view.php?id=$cm->id", "$quiz->id", $cm->id);
- require_capability('mod/quiz:manage', $quizcontext);
+ //you need mod/quiz:manage in addition to question capabilities to access this page.
+ require_capability('mod/quiz:manage', $contexts->lowest());
if (isset($quiz->instance)
&& empty($quiz->grades)){ // Construct an array to hold all the grades.
@@ -187,6 +182,8 @@
if (! $category = get_record('question_categories', 'id', $categoryid)) {
error('Category ID is incorrect');
}
+ $catcontext = get_context_instance_by_id($category->contextid);
+ require_capability('moodle/question:useall', $catcontext);
$category->name = addslashes($category->name);
// find existing random questions in this category
$random = RANDOM;
@@ -214,7 +211,7 @@
if ($randomcreate > 0) {
$form->name = get_string('random', 'quiz') .' ('. $category->name .')';
- $form->category = $category->id;
+ $form->category = "$category->id,$category->contextid";
$form->questiontext = $recurse; // we use the questiontext field to store the info
// on whether to include questions in subcategories
$form->questiontextformat = 0;
@@ -246,7 +243,6 @@
error('Could not save layout');
}
}
-
if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz
quiz_delete_quiz_question($_REQUEST['delete'], $quiz);
}
@@ -267,7 +263,7 @@
$questions[$value] = $oldquestions[$key];
}
}
-
+
// If ordering info was given, reorder the questions
if ($questions) {
ksort($questions);
@@ -296,26 +292,23 @@
delete_records('quiz_attempts', 'preview', '1', 'quiz', $quiz->id);
}
-/// all commands have been dealt with, now print the page
+ question_showbank_actions($thispageurl, $cm);
- if (empty($quiz->category) or !record_exists('question_categories', 'id', $quiz->category)) {
- $category = get_default_question_category($course->id);
- $quiz->category = $category->id;
- }
+/// all commands have been dealt with, now print the page
// Print basic page layout.
if (isset($quiz->instance) and record_exists_select('quiz_attempts', "quiz = '$quiz->instance' AND preview = '0'")){
// one column layout with table of questions used in this quiz
- $strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
+ $strupdatemodule = has_capability('moodle/course:manageactivities', $contexts->lowest())
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
: "";
$navlinks = array();
- $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
- $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
+ $navlinks[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity');
+ $navlinks[] = array('name' => format_string($quiz->name), 'link' => "view.php?q=$quiz->instance", 'type' => 'activityinstance');
$navlinks[] = array('name' => $streditingquiz, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($streditingquiz, '', $navigation, "", "",
true, $strupdatemodule);
@@ -329,9 +322,7 @@
$a->attemptnum = count_records('quiz_attempts', 'quiz', $quiz->id, 'preview', 0);
$a->studentnum = count_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'", 'COUNT(DISTINCT userid)');
$a->studentstring = $course->students;
- if (! $cm = get_coursemodule_from_instance("quiz", $quiz->instance, $course->id)) {
- error("Course Module ID was incorrect");
- }
+
echo "
';
diff --git a/mod/quiz/editlib.php b/mod/quiz/editlib.php
index f75a8e95cf..bcfde9aa3f 100644
--- a/mod/quiz/editlib.php
+++ b/mod/quiz/editlib.php
@@ -141,6 +141,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
$strgrade = get_string("grade");
$strremove = get_string('remove', 'quiz');
$stredit = get_string("edit");
+ $strview = get_string("view");
$straction = get_string("action");
$strmoveup = get_string("moveup");
$strmovedown = get_string("movedown");
@@ -155,7 +156,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
return 0;
}
- if (!$questions = get_records_sql("SELECT q.*,c.course
+ if (!$questions = get_records_sql("SELECT q.*,c.contextid
FROM {$CFG->prefix}question q,
{$CFG->prefix}question_categories c
WHERE q.id in ($quiz->questions)
@@ -169,7 +170,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
$count = 0;
$qno = 1;
$sumgrade = 0;
- $order = explode(",", $quiz->questions);
+ $order = explode(',', $quiz->questions);
$lastindex = count($order)-1;
// If the list does not end with a pagebreak then add it on.
if ($order[$lastindex] != 0) {
@@ -235,12 +236,11 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
echo '
';
}
$count++;
- // missing here, if loop is broken, need to close the from line 199/201
+ // missing here, if loop is broken, need to close the
echo "";
continue;
}
$question = $questions[$qnum];
- $canedit = has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $question->course));
echo "
";
if ($count != 0) {
@@ -276,17 +276,20 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete=true, $showbreak
}
echo '
";
print_continue("edit.php?".$thispageurl->get_query_string());
- print_footer($course);
+ print_footer($COURSE);
exit;
}
- /// Display upload form
+ /// Display export form
- // get valid formats to generate dropdown list
- $fileformatnames = get_import_export_formats( 'export' );
-
- // get filename
- if (empty($exportfilename)) {
- $exportfilename = default_export_filename($course, $category);
- }
print_heading_with_help($txt->exportquestions, 'export', 'quiz');
- print_simple_box_start('center');
-?>
-
-
-
- display();
+
+ print_footer($COURSE);
?>
diff --git a/question/format.php b/question/format.php
index 0cfd17bfd4..0222c6c4b1 100644
--- a/question/format.php
+++ b/question/format.php
@@ -1,4 +1,4 @@
-course = $course;
}
+ /**
+ * set an array of contexts.
+ * @param array $contexts Moodle course variable
+ */
+ function setContexts($contexts) {
+ $this->contexts = $contexts;
+ $this->translator = new context_to_string_translator($this->contexts);
+ }
/**
* set the filename
@@ -73,14 +85,29 @@ class qformat_default {
function setCatfromfile( $catfromfile ) {
$this->catfromfile = $catfromfile;
}
-
+
+ /**
+ * set contextfromfile
+ * @param bool $contextfromfile allow contexts embedded in import file
+ */
+ function setContextfromfile($contextfromfile) {
+ $this->contextfromfile = $contextfromfile;
+ }
+
/**
* set cattofile
* @param bool cattofile exports categories within export file
*/
function setCattofile( $cattofile ) {
$this->cattofile = $cattofile;
- }
+ }
+ /**
+ * set contexttofile
+ * @param bool cattofile exports categories within export file
+ */
+ function setContexttofile($contexttofile) {
+ $this->contexttofile = $contexttofile;
+ }
/**
* set stoponerror
@@ -112,7 +139,7 @@ class qformat_default {
$this->importerrors++;
}
- /**
+ /**
* Import for questiontype plugins
* Do not override.
* @param data mixed The segment of data containing the question
@@ -135,8 +162,8 @@ class qformat_default {
return $question;
}
}
- }
- return false;
+ }
+ return false;
}
/**
@@ -153,13 +180,14 @@ class qformat_default {
* @return boolean success
*/
function importprocess() {
+ global $USER;
// reset the timer in case file upload was slow
@set_time_limit();
// STAGE 1: Parse the file
notify( get_string('parsingquestions','quiz') );
-
+
if (! $lines = $this->readdata($this->filename)) {
notify( get_string('cannotread','quiz') );
return false;
@@ -186,7 +214,7 @@ class qformat_default {
foreach ($questions as $question) { // Process and store each question
- // reset the php timeout
+ // reset the php timeout
@set_time_limit();
// check for category modifiers
@@ -194,12 +222,12 @@ class qformat_default {
if ($this->catfromfile) {
// find/create category object
$catpath = $question->category;
- $newcategory = create_category_path( $catpath, '/', $this->course->id );
+ $newcategory = $this->create_category_path( $catpath, '/');
if (!empty($newcategory)) {
$this->category = $newcategory;
}
}
- continue;
+ continue;
}
$count++;
@@ -231,6 +259,9 @@ class qformat_default {
$question->category = $this->category->id;
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
+ $question->createdby = $USER->id;
+ $question->timecreated = time();
+
if (!$question->id = insert_record("question", $question)) {
error( get_string('cannotinsert','quiz') );
}
@@ -258,7 +289,57 @@ class qformat_default {
}
return true;
}
-
+ /**
+ * find and/or create the category described by a delimited list
+ * e.g. $course$/tom/dick/harry or tom/dick/harry
+ *
+ * removes any context string no matter whether $getcontext is set
+ * but if $getcontext is set then ignore the context and use selected category context.
+ *
+ * @param string catpath delimited category path
+ * @param string delimiter path delimiting character
+ * @param int courseid course to search for categories
+ * @return mixed category object or null if fails
+ */
+ function create_category_path($catpath, $delimiter='/') {
+ $catpath = clean_param($catpath, PARAM_PATH);
+ $catnames = explode($delimiter, $catpath);
+ $parent = 0;
+ $category = null;
+ if (FALSE !== preg_match('/^\$([a-z]+)\$$/', $catnames[0], $matches)){
+ $contextid = $this->translator->string_to_context($matches[1]);
+ array_shift($catnames);
+ } else {
+ $contextid = FALSE;
+ }
+ if ($this->contextfromfile && ($contextid !== FALSE)){
+ $context = get_context_instance_by_id($contextid);
+ require_capability('moodle/question:add', $context);
+ } else {
+ $context = get_context_instance_by_id($this->category->contextid);
+ }
+ foreach ($catnames as $catname) {
+ if ($category = get_record( 'question_categories', 'name', $catname, 'contextid', $context->id, 'parent', $parent)) {
+ $parent = $category->id;
+ } else {
+ require_capability('moodle/question:managecategory', $context);
+ // create the new category
+ $category = new object;
+ $category->contextid = $context->id;
+ $category->name = $catname;
+ $category->info = '';
+ $category->parent = $parent;
+ $category->sortorder = 999;
+ $category->stamp = make_unique_id_code();
+ if (!($id = insert_record('question_categories', $category))) {
+ error( "cannot create new category - $catname" );
+ }
+ $category->id = $id;
+ $parent = $id;
+ }
+ }
+ return $category;
+ }
/**
* Return complete file within an array, one item per line
* @param string filename name of file
@@ -279,9 +360,9 @@ class qformat_default {
}
/**
- * Parses an array of lines into an array of questions,
- * where each item is a question object as defined by
- * readquestion(). Questions are defined as anything
+ * Parses an array of lines into an array of questions,
+ * where each item is a question object as defined by
+ * readquestion(). Questions are defined as anything
* between blank lines.
*
* If your format does not use blank lines as a delimiter
@@ -291,7 +372,7 @@ class qformat_default {
* @return array array of question objects
*/
function readquestions($lines) {
-
+
$questions = array();
$currentquestion = array();
@@ -325,12 +406,12 @@ class qformat_default {
* by import but are required db fields.
* This should not be overridden.
* @return object default question
- */
+ */
function defaultquestion() {
global $CFG;
-
+
$question = new stdClass();
- $question->shuffleanswers = $CFG->quiz_shuffleanswers;
+ $question->shuffleanswers = $CFG->quiz_shuffleanswers;
$question->defaultgrade = 1;
$question->image = "";
$question->usecase = 0;
@@ -351,8 +432,8 @@ class qformat_default {
}
/**
- * Given the data known to define a question in
- * this format, this function converts it into a question
+ * Given the data known to define a question in
+ * this format, this function converts it into a question
* object suitable for processing and insertion into Moodle.
*
* If your format does not use blank lines to delimit questions
@@ -396,7 +477,7 @@ class qformat_default {
check_dir_exists($destination, true, true );
// detect and fix any filename collision - get unique filename
- $newfiles = resolve_filename_collisions( $destination, array($file) );
+ $newfiles = resolve_filename_collisions( $destination, array($file) );
$newfile = $newfiles[0];
// convert and save file contents
@@ -421,11 +502,11 @@ class qformat_default {
* EXPORT FUNCTIONS
*******************/
- /**
+ /**
* Provide export functionality for plugin questiontypes
* Do not override
* @param name questiontype name
- * @param question object data to export
+ * @param question object data to export
* @param extra mixed any addition format specific data needed
* @return string the data to append to export or false if error (or unhandled)
*/
@@ -503,7 +584,7 @@ class qformat_default {
// results are first written into string (and then to a file)
// so create/initialize the string here
$expout = "";
-
+
// track which category questions are in
// if it changes we will record the category change in the output
// file if selected. 0 means that it will get printed before the 1st question
@@ -511,7 +592,7 @@ class qformat_default {
// iterate through questions
foreach($questions as $question) {
-
+
// do not export hidden questions
if (!empty($question->hidden)) {
continue;
@@ -521,13 +602,13 @@ class qformat_default {
if ($question->qtype==RANDOM) {
continue;
}
-
+
// check if we need to record category change
if ($this->cattofile) {
if ($question->category != $trackcategory) {
- $trackcategory = $question->category;
- $categoryname = get_category_path( $trackcategory );
-
+ $trackcategory = $question->category;
+ $categoryname = $this->get_category_path($trackcategory, '/', $this->contexttofile);
+
// create 'dummy' question for category export
$dummyquestion = new object;
$dummyquestion->qtype = 'category';
@@ -536,8 +617,8 @@ class qformat_default {
$dummyquestion->id = 0;
$dummyquestion->questiontextformat = '';
$expout .= $this->writequestion( $dummyquestion ) . "\n";
- }
- }
+ }
+ }
// export the question displaying message
$count++;
@@ -547,7 +628,7 @@ class qformat_default {
// final pre-process on exported data
$expout = $this->presave_process( $expout );
-
+
// write file
$filepath = $path."/".$this->filename . $this->export_file_extension();
if (!$fh=fopen($filepath,"w")) {
@@ -559,6 +640,35 @@ class qformat_default {
fclose($fh);
return true;
}
+ /**
+ * get the category as a path (e.g., tom/dick/harry)
+ * @param int id the id of the most nested catgory
+ * @param string delimiter the delimiter you want
+ * @return string the path
+ */
+ function get_category_path($id, $delimiter='/', $includecontext = true) {
+ $path = '';
+ if (!$firstcategory = get_record('question_categories','id',$id)) {
+ print_error( "Error getting category record from db - $id" );
+ }
+ $category = $firstcategory;
+ $contextstring = $this->translator->context_to_string($category->contextid);
+ do {
+ $name = $category->name;
+ $id = $category->parent;
+ if (!empty($path)) {
+ $path = "{$name}{$delimiter}{$path}";
+ }
+ else {
+ $path = $name;
+ }
+ } while ($category = get_record( 'question_categories','id',$id ));
+
+ if ($includecontext){
+ $path = '$'.$contextstring.'$'."{$delimiter}{$path}";
+ }
+ return $path;
+ }
/**
* Do an post-processing that may be required
@@ -579,12 +689,11 @@ class qformat_default {
// if not overidden, then this is an error.
$formatnotimplemented = get_string( 'formatnotimplemented','quiz' );
echo "
$formatnotimplemented
";
-
return NULL;
}
/**
- * get directory into which export is going
+ * get directory into which export is going
* @return string file path
*/
function question_get_export_dir() {
@@ -608,6 +717,8 @@ class qformat_default {
}
return format_text(stripslashes($question->questiontext), $format, $formatoptions);
}
+
+
}
?>
diff --git a/question/format/coursetestmanager/format.php b/question/format/coursetestmanager/format.php
index b80cf24d4f..e8de1dc8e5 100755
--- a/question/format/coursetestmanager/format.php
+++ b/question/format/coursetestmanager/format.php
@@ -25,8 +25,8 @@ class qformat_coursetestmanager extends qformat_default {
}
function importprocess($filename) {
- global $CFG,$strimportquestions,$form,$question_category,$category,$course,
- $hostname, $mdapath, $mdbpath;
+ global $CFG, $USER, $strimportquestions,$form,$question_category,$category,$course,
+ $hostname, $mdapath, $mdbpath;
if ((PHP_OS == "Linux") and isset($hostname)) {
$hostname = trim($hostname);
// test the ODBC socket server connection
@@ -261,6 +261,8 @@ class qformat_coursetestmanager extends qformat_default {
echo "
$count. ".stripslashes($question->questiontext)."
";
$question->category = $this->category->id;
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
+ $question->createdby = $USER->id;
+ $question->timecreated = time();
if (!$question->id = insert_record("question", $question)) {
error("Could not insert new question!");
}
diff --git a/question/import.php b/question/import.php
index 7946756fdb..153f7031d2 100644
--- a/question/import.php
+++ b/question/import.php
@@ -10,92 +10,53 @@
*/
require_once("../config.php");
- require_once("editlib.php" );
+ require_once("editlib.php");
require_once($CFG->libdir . '/uploadlib.php');
require_once($CFG->libdir . '/questionlib.php');
+ require_once("import_form.php");
- list($thispageurl, $courseid, $cmid, $cm, $module, $pagevars) = question_edit_setup(false, false);
+ list($thispageurl, $contexts, $cmid, $cm, $module, $pagevars) = question_edit_setup('import', false, false);
- // get parameters
- $params = new stdClass;
- $params->choosefile = optional_param('choosefile','',PARAM_PATH);
- $catfromfile = optional_param('catfromfile', 0, PARAM_BOOL );
- $format = optional_param('format','',PARAM_FILE);
- $params->matchgrades = optional_param('matchgrades','',PARAM_ALPHA);
- $params->stoponerror = optional_param('stoponerror', 0, PARAM_BOOL);
- $params->category = optional_param( 'category', 0, PARAM_INT );
-
- // get display strings
+ // get display strings
$txt = new stdClass();
- $txt->category = get_string('category','quiz');
- $txt->choosefile = get_string('choosefile','quiz');
- $txt->file = get_string('file');
- $txt->fileformat = get_string('fileformat','quiz');
- $txt->fromfile = get_string('fromfile','quiz');
- $txt->importcategory = get_string('importcategory','quiz');
$txt->importerror = get_string('importerror','quiz');
- $txt->importfilearea = get_string('importfilearea','quiz');
- $txt->importfileupload = get_string('importfileupload','quiz');
- $txt->importfromthisfile = get_string('importfromthisfile','quiz');
$txt->importquestions = get_string("importquestions", "quiz");
- $txt->matchgrades = get_string('matchgrades','quiz');
- $txt->matchgradeserror = get_string('matchgradeserror','quiz');
- $txt->matchgradesnearest = get_string('matchgradesnearest','quiz');
- $txt->modulename = get_string('modulename','quiz');
- $txt->modulenameplural = get_string('modulenameplural','quiz');
- $txt->onlyteachersimport = get_string('onlyteachersimport','quiz');
- $txt->questions = get_string("questions", "quiz");
- $txt->quizzes = get_string('modulenameplural', 'quiz');
- $txt->stoponerror = get_string('stoponerror', 'quiz');
- $txt->upload = get_string('upload');
- $txt->uploadproblem = get_string('uploadproblem');
- $txt->uploadthisfile = get_string('uploadthisfile');
-
- // matching options
- $matchgrades = array();
- $matchgrades['error'] = $txt->matchgradeserror;
- $matchgrades['nearest'] = $txt->matchgradesnearest;
-
- // not sure where $pagevars['cat'] comes from, but it doesn't respect
- // the user's choice on the form - so this bodge
- if (empty($params->category)) {
- $params->category = $pagevars['cat'];
- }
- if (!$category = get_record("question_categories", "id", $params->category)) {
- // if no valid category was given, use the default category
+ list($catid, $catcontext) = explode(',', $pagevars['cat']);
+ if (!$category = get_record("question_categories", "id", $catid)) {
print_error('nocategory','quiz');
}
- // check category is valid (against THIS courseid, before we change it)
- $validcats = question_category_options( $cmid, false, true );
- if (!array_key_exists( $params->category, $validcats )) {
- print_error( 'invalidcategory', 'quiz' );
- }
-
- $localcourseid = $cmid;
- $courseid = $category->course;
-
- if (!$course = get_record("course", "id", $courseid)) {
- error("Invalid course!");
+ //this page can be called without courseid or cmid in which case
+ //we get the context from the category object.
+ if ($contexts === null) { // need to get the course from the chosen category
+ $contexts = new question_edit_contexts(get_context_instance_by_id($category->contextid));
+ $thiscontext = $contexts->lowest();
+ if ($thiscontext->contextlevel == CONTEXT_COURSE){
+ require_login($thiscontext->instanceid, false);
+ } elseif ($thiscontext->contextlevel == CONTEXT_MODULE){
+ list($module, $cm) = get_module_from_cmid($thiscontext->instanceid);
+ require_login($cm->course, false, $cm);
+ }
+ $contexts->require_one_edit_tab_cap($edittab);
}
- require_login($course->id, false);
-
- $context = get_context_instance(CONTEXT_COURSE, $course->id);
- require_capability('moodle/question:import', $context);
-
// ensure the files area exists for this course
- make_upload_directory( "$course->id" );
+ make_upload_directory("$COURSE->id");
+ $import_form = new question_import_form($thispageurl, array('contexts'=>$contexts->having_one_edit_tab_cap('import'),
+ 'defaultcategory'=>$pagevars['cat']));
+ if ($import_form->is_cancelled()){
+ redirect($thispageurl);
+ }
//==========
// PAGE HEADER
//==========
if ($cm!==null) {
- $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $course->id))
- ? update_module_button($cm->id, $course->id, get_string('modulename', $cm->modname))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $COURSE->id))
+ ? update_module_button($cm->id, $COURSE->id, get_string('modulename', $cm->modname))
: "";
$navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$course->id", 'type' => 'activity');
@@ -113,7 +74,7 @@
$navlinks = array();
$navlinks[] = array('name' => $txt->importquestions, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
-
+
print_header_simple($txt->importquestions, '', $navigation);
// print tabs
$currenttab = 'import';
@@ -122,165 +83,78 @@
// file upload form sumitted
- if (!empty($format) and confirm_sesskey() ) {
+ if ($form = $import_form->get_data()) {
// file checks out ok
$fileisgood = false;
- // work out if this is an uploaded file
+ // work out if this is an uploaded file
// or one from the filesarea.
- if (!empty($params->choosefile)) {
- $importfile = "{$CFG->dataroot}/{$course->id}/{$params->choosefile}";
+ if (!empty($form->choosefile)) {
+ $importfile = "{$CFG->dataroot}/{$COURSE->id}/{$form->choosefile}";
if (file_exists($importfile)) {
$fileisgood = true;
- }
- else {
- notify($txt->uploadproblem);
+ } else {
+ error(get_string('uploadproblem', 'moodle', $form->choosefile));
}
} else {
// must be upload file
- if (empty($_FILES['newfile'])) {
- notify( $txt->uploadproblem );
- }
- else if ((!is_uploaded_file($_FILES['newfile']['tmp_name']) or $_FILES['newfile']['size'] == 0)) {
- notify( $txt->uploadproblem );
- }
- else {
- $importfile = $_FILES['newfile']['tmp_name'];
+ if (!$importfile = $import_form->get_importfile_name()) {
+ error(get_string('uploadproblem', 'moodle'));
+ }else {
$fileisgood = true;
}
}
// process if we are happy file is ok
- if ($fileisgood) {
+ if ($fileisgood) {
- if (! is_readable("format/$format/format.php")) {
- error( get_string('formatnotfound','quiz', $format) );
+ if (! is_readable("format/$form->format/format.php")) {
+ error(get_string('formatnotfound','quiz', $form->format));
}
- require("format.php"); // Parent class
- require("format/$format/format.php");
+ require_once("format.php"); // Parent class
+ require_once("format/$form->format/format.php");
- $classname = "qformat_$format";
+ $classname = "qformat_$form->format";
$qformat = new $classname();
// load data into class
- $qformat->setCategory( $category );
- $qformat->setCourse( $course );
- $qformat->setFilename( $importfile );
- $qformat->setMatchgrades( $params->matchgrades );
- $qformat->setCatfromfile( $catfromfile );
- $qformat->setStoponerror( $params->stoponerror );
+ $qformat->setCategory($category);
+ $qformat->setContexts($contexts->having_one_edit_tab_cap('import'));
+ $qformat->setCourse($COURSE);
+ $qformat->setFilename($importfile);
+ $qformat->setMatchgrades($form->matchgrades);
+ $qformat->setCatfromfile(!empty($form->catfromfile));
+ $qformat->setContextfromfile(!empty($form->contextfromfile));
+ $qformat->setStoponerror($form->stoponerror);
// Do anything before that we need to
- if (! $qformat->importpreprocess()) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ if (! $qformat->importpreprocess()) {
+ error($txt->importerror, $thispageurl->out());
}
// Process the uploaded file
- if (! $qformat->importprocess() ) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ if (! $qformat->importprocess()) {
+ error($txt->importerror, $thispageurl->out());
}
// In case anything needs to be done after
if (! $qformat->importpostprocess()) {
- error( $txt->importerror, $thispageurl->out(false, array('category'=>$category->id)));
+ error($txt->importerror, $thispageurl->out());
}
echo "";
- print_continue("edit.php?".$thispageurl->get_query_string());
- print_footer($course);
+ print_continue("edit.php?".($thispageurl->get_query_string(array('category'=>"{$qformat->category->id},{$qformat->category->contextid}"))));
+ print_footer($COURSE);
exit;
}
}
- /// Print upload form
-
- // get list of available import formats
- $fileformatnames = get_import_export_formats( 'import' );
-
print_heading_with_help($txt->importquestions, "import", "quiz");
- /// Get all the existing categories now
- $catmenu = question_category_options($course->id, false, true);
-
- //==========
- // DISPLAY
- //==========
-
- ?>
-
-
-
-
-
- display();
+ print_footer($COURSE);
?>
diff --git a/question/preview.php b/question/preview.php
index ffd5b24f2d..8922ac69d6 100644
--- a/question/preview.php
+++ b/question/preview.php
@@ -38,12 +38,7 @@
$continue = false;
}
- require_login();
- // this might break things in the future
- if (!isteacherinanycourse()) {
- error('This page is for teachers only');
- }
if (!$continue) {
// Start a new attempt; delete the old session
@@ -57,20 +52,23 @@
$url .= '&continue=1';
redirect($url);
}
-
+ // Load the question information
+ if (!$questions = get_records('question', 'id', $id)) {
+ error('Could not load question');
+ }
if (empty($quizid)) {
$quiz = new cmoptions;
$quiz->id = 0;
$quiz->review = $CFG->quiz_review;
-
+ require_login_in_context($questions[$id]->contextid);
} else if (!$quiz = get_record('quiz', 'id', $quizid)) {
error("Quiz id $quizid does not exist");
+ } else {
+ require_login($quiz->course, false, get_coursemodule_from_instance('quiz', $quizid, $quiz->course));
}
- // Load the question information
- if (!$questions = get_records('question', 'id', $id)) {
- error('Could not load question');
- }
+
+
if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) {
$questions[$id]->maxgrade = $maxgrade;
} else {
@@ -84,10 +82,12 @@
error("This question doesn't belong to a valid category!");
}
- if (!has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $category->course)) and !$category->publish) {
+ if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category)){
error("You can't preview these questions!");
}
- $quiz->course = $category->course;
+ if (isset($COURSE)){
+ $quiz->course = $COURSE->id;
+ }
// Load the question type specific information
if (!get_question_options($questions)) {
@@ -197,7 +197,7 @@
echo ' ';
-
+
echo '
';
echo "\n";
echo "\n";
diff --git a/question/question.php b/question/question.php
index 45e8ac9963..8079937c9b 100644
--- a/question/question.php
+++ b/question/question.php
@@ -13,23 +13,43 @@ require_once(dirname(__FILE__) . '/editlib.php');
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/formslib.php');
-$returnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
-
// Read URL parameters telling us which question to edit.
$id = optional_param('id', 0, PARAM_INT); // question id
-$cmid = optional_param('cmid', 0, PARAM_INT);
$qtype = optional_param('qtype', '', PARAM_FILE);
$categoryid = optional_param('category', 0, PARAM_INT);
+
+$cmid = optional_param('cmid', 0, PARAM_INT);
+$courseid = optional_param('courseid', 0, PARAM_INT);
$wizardnow = optional_param('wizardnow', '', PARAM_ALPHA);
+$movecontext = optional_param('movecontext', 0, PARAM_BOOL);//switch to make question
+ //uneditable - form is displayed to edit question only
+$returnurl = optional_param('returnurl', 0, PARAM_LOCALURL);
+
+if ($movecontext && !$id){
+ print_error('questiondoesnotexist', 'question', $returnurl);
+}
if ($cmid){
- list($module, $cm) = get_module_from_cmid($cmid);
-} else {
+ list($module, $cm) = get_module_from_cmid($cmid);
+ require_login($cm->course, false, $cm);
+ $thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
+} elseif ($courseid) {
+ require_login($courseid, false);
+ $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
$module = null;
$cm = null;
+} else {
+ error('Need to pass courseid or cmid to this script.');
}
+$contexts = new question_edit_contexts($thiscontext);
+
+
+if (!$returnurl) {
+ $returnurl = "{$CFG->wwwroot}/question/edit.php?courseid={$COURSE->id}";
+}
+
+
-// Validate the URL parameters.
if ($id) {
if (!$question = get_record('question', 'id', $id)) {
print_error('questiondoesnotexist', 'question', $returnurl);
@@ -47,79 +67,139 @@ if ($id) {
if (!$category = get_record('question_categories', 'id', $question->category)) {
print_error('categorydoesnotexist', 'question', $returnurl);
}
-if (!$returnurl) {
- $returnurl = "{$CFG->wwwroot}/question/edit.php?courseid={$category->course}";
+
+//permissions
+$question->formoptions = new object();
+$permissionstrs = array();
+
+$categorycontext = get_context_instance_by_id($category->contextid);
+$addpermission = has_capability('moodle/question:add', $categorycontext);
+
+if ($id) {
+ $canview = question_has_capability_on($question, 'view');
+ if ($movecontext){
+ $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;
}
+$question->category = "$category->id,$category->contextid";
+if ($formeditable && $id){
+ $question->categorymoveto = $question->category;
+}
// Validate the question type.
if (!isset($QTYPES[$question->qtype])) {
print_error('unknownquestiontype', 'question', $returnurl, $question->qtype);
}
$CFG->pagepath = 'question/type/' . $question->qtype;
-// Check the user is logged in and has enough premissions.
-require_login($category->course, false);
-$coursecontext = get_context_instance(CONTEXT_COURSE, $category->course);
-require_capability('moodle/question:manage', $coursecontext);
// Create the question editing form.
-if ($wizardnow!==''){
+if ($wizardnow!=='' && !$movecontext){
if (!method_exists($QTYPES[$question->qtype], 'next_wizard_form')){
print_error('missingimportantcode', 'question', $returnurl, 'wizard form definition');
} else {
- $mform = $QTYPES[$question->qtype]->next_wizard_form('question.php', $question, $wizardnow);
+ $mform = $QTYPES[$question->qtype]->next_wizard_form('question.php', $question, $wizardnow, $formeditable);
}
} else {
- $mform = $QTYPES[$question->qtype]->create_editing_form('question.php', $question);
+ $mform = $QTYPES[$question->qtype]->create_editing_form('question.php', $question, $category, $contexts, $formeditable);
}
-
if ($mform === null) {
print_error('missingimportantcode', 'question', $returnurl, 'question editing form definition for "'.$question->qtype.'"');
}
$toform = $question; // send the question object and a few more parameters to the form
$toform->returnurl = $returnurl;
+$toform->movecontext = $movecontext;
if ($cm !== null){
$toform->cmid = $cm->id;
+ $toform->courseid = $cm->course;
+} else {
+ $toform->courseid = $COURSE->id;
}
$mform->set_data($toform);
if ($mform->is_cancelled()){
redirect($returnurl);
} elseif ($data = $mform->get_data()){
+ $returnurl = new moodle_url($returnurl);
+ //select category that question has been saved in / moved to when we return to question bank
+ if (!empty($data->categorymoveto)){
+ $returnurl->param('category', $data->categorymoveto);
+ } else if (!empty($data->category)){
+ $returnurl->param('category', $data->category);
+ }
+ $returnurl = $returnurl->out();
if (!empty($data->makecopy)) {
$question->id = 0; // causes a new question to be created.
$question->hidden = 0; // Copies should not be hidden
}
+ if ($movecontext){
+ list($tocatid, $tocontextid) = explode(',', $data->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 {
+ $movecontexturl->param('courseid', $COURSE->id);
+ }
+ redirect($movecontexturl->out());
+ }
+ }
$question = $QTYPES[$question->qtype]->save_question($question, $data, $COURSE, $wizardnow);
- if ($QTYPES[$qtype]->finished_edit_wizard($data)){
+ if ($QTYPES[$qtype]->finished_edit_wizard($data) || $movecontext){
+
if (optional_param('inpopup', 0, PARAM_BOOL)) {
notify(get_string('changessaved'), '');
close_window(3);
} else {
redirect($returnurl);
}
- die;
} else {
- //useful for passing data to the next page which is not saved in the database
- $queryappend = '';
- if (isset($data->nextpageparam)){
- foreach ($data->nextpageparam as $key => $param){
- $queryappend .= "&".urlencode($key).'='.urlencode($param);
- }
- }
+ $nexturlparams = array('returnurl'=>$returnurl)
+ + $data->nextpageparam;//useful for passing data to the next page which is not saved in the database
if ($question->id) {
- $nexturl = "question.php?id=$question->id&returnurl=" . urlencode($returnurl);
+ $nexturlparams['id'] = $question->id;
} else { // only for creating new questions
- $nexturl = "question.php?category=$question->category&qtype=$question->qtype&returnurl=".urlencode($returnurl);
+ $nexturlparams['category'] = $question->category;
+ $nexturlparams['qtype'] =$question->qtype;
}
- redirect($nexturl.'&wizardnow='.$data->wizard.$queryappend, '', 20);
+ $nexturlparams['wizardnow'] = $data->wizard;
+ $nexturl = new moodle_url('question.php', $nexturlparams);
+ redirect($nexturl);
}
} else {
list($streditingquestion,) = $QTYPES[$question->qtype]->get_heading();
if ($cm !== null) {
- $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $category->course))
- ? update_module_button($cm->id, $category->course, get_string('modulename', $cm->modname))
+ $strupdatemodule = has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $COURSE->id))
+ ? update_module_button($cm->id, $cm->course, get_string('modulename', $cm->modname))
: "";
$navlinks = array();
$navlinks[] = array('name' => get_string('modulenameplural', $cm->modname), 'link' => "$CFG->wwwroot/mod/{$cm->modname}/index.php?id=$category->course", 'type' => 'activity');
@@ -128,7 +208,7 @@ if ($mform->is_cancelled()){
$navlinks[] = array('name' => $streditingquestion, 'link' => '', 'type' => 'title');
$navigation = build_navigation($navlinks);
print_header_simple($streditingquestion, '', $navigation, "", "", true, $strupdatemodule);
-
+
} else {
$navlinks = array();
$navlinks[] = array('name' => get_string('editquestions', "quiz"), 'link' => $returnurl, 'type' => 'title');
@@ -139,10 +219,10 @@ if ($mform->is_cancelled()){
print_header_simple($streditingquestion, '', $navigation);
}
+
// Display a heading, question editing form and possibly some extra content needed for
// for this question type.
$QTYPES[$question->qtype]->display_question_editing_page($mform, $question, $wizardnow);
-
print_footer($COURSE);
}
?>
diff --git a/question/restorelib.php b/question/restorelib.php
index 2c00b47e18..fa48d6ddef 100644
--- a/question/restorelib.php
+++ b/question/restorelib.php
@@ -85,82 +85,210 @@
include_once($CFG->libdir.'/questionlib.php');
- function restore_question_categories($category,$restore) {
-
- global $CFG;
+ /**
+ * Returns the best question category (id) found to restore one
+ * question category from a backup file. Works by stamp.
+ *
+ * @param object $restore preferences for restoration
+ * @param array $contextinfo fragment of decoded xml
+ * @return object best context instance for this category to be in
+ */
+ function restore_question_get_best_category_context($restore, $contextinfo) {
+ switch ($contextinfo['LEVEL'][0]['#']) {
+ case 'module':
+ $instanceinfo = backup_getid($restore->backup_unique_code, 'course_modules', $contextinfo['INSTANCE'][0]['#']);
+ $tocontext = get_context_instance(CONTEXT_MODULE, $instanceinfo->new_id);
+ break;
+ case 'course':
+ $tocontext = get_context_instance(CONTEXT_COURSE, $restore->course_id);
+ break;
+ case 'coursecategory':
+ //search COURSECATEGORYLEVEL steps up the course cat tree or
+ //to the top of the tree if steps are exhausted.
+ $catno = $contextinfo['COURSECATEGORYLEVEL'][0]['#'];
+ $catid = get_field('course', 'parent', 'id', $restore->course_id);
+ while ($catno > 1){
+ $nextcatid = get_field('course_categories', 'parent', 'id', $catid);
+ if ($nextcatid == 0){
+ break;
+ }
+ $catid == $nextcatid;
+ $catno--;
+ }
+ $tocontext = get_context_instance(CONTEXT_COURSECAT, $catid);
+ break;
+ case 'system':
+ $tocontext = get_context_instance(CONTEXT_SYSTEM);
+ break;
+ }
+ return $tocontext;
+ }
+ function restore_question_categories($info, $restore) {
$status = true;
-
- //Hook to call Moodle < 1.5 Quiz Restore
- if ($restore->backup_version < 2005043000) {
- include_once($CFG->dirroot.'/mod/quiz/restorelibpre15.php');
- return quiz_restore_pre15_question_categories($category,$restore);
+ //Iterate over each category
+ foreach ($info as $category) {
+ $status = $status && restore_question_category($category, $restore);
}
+ $status = $status && restore_recode_category_parents($restore);
+ return $status;
+ }
- //Get record from backup_ids
- $data = backup_getid($restore->backup_unique_code,"question_categories",$category->id);
-
- if ($data) {
- //Now get completed xmlized object
- $info = $data->info;
- //traverse_xmlize($info); //Debug
- //print_object ($GLOBALS['traverse_array']); //Debug
- //$GLOBALS['traverse_array']=""; //Debug
+ function restore_question_category($category, $restore){
+ $status = true;
+ //Skip empty categories (some backups can contain them)
+ if (!empty($category->id)) {
+ //Get record from backup_ids
+ $data = backup_getid($restore->backup_unique_code, "question_categories", $category->id);
+
+ if ($data) {
+ //Now get completed xmlized object
+ $info = $data->info;
+ //traverse_xmlize($info); //Debug
+ //print_object ($GLOBALS['traverse_array']); //Debug
+ //$GLOBALS['traverse_array']=""; //Debug
- //Now, build the question_categories record structure
- $question_cat = new stdClass;
- $question_cat->course = $restore->course_id;
- $question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['0']['#']);
- $question_cat->info = backup_todb($info['QUESTION_CATEGORY']['#']['INFO']['0']['#']);
- $question_cat->publish = backup_todb($info['QUESTION_CATEGORY']['#']['PUBLISH']['0']['#']);
- $question_cat->stamp = backup_todb($info['QUESTION_CATEGORY']['#']['STAMP']['0']['#']);
- $question_cat->parent = backup_todb($info['QUESTION_CATEGORY']['#']['PARENT']['0']['#']);
- $question_cat->sortorder = backup_todb($info['QUESTION_CATEGORY']['#']['SORTORDER']['0']['#']);
-
- if ($catfound = restore_get_best_question_category($question_cat, $restore->course_id)) {
- $newid = $catfound;
- } else {
+ //Now, build the question_categories record structure
+ $question_cat = new stdClass;
+ $question_cat->name = backup_todb($info['QUESTION_CATEGORY']['#']['NAME']['0']['#']);
+ $question_cat->info = backup_todb($info['QUESTION_CATEGORY']['#']['INFO']['0']['#']);
+ $question_cat->stamp = backup_todb($info['QUESTION_CATEGORY']['#']['STAMP']['0']['#']);
+ //parent is fixed after all categories are restored and we know all the new ids.
+ $question_cat->parent = backup_todb($info['QUESTION_CATEGORY']['#']['PARENT']['0']['#']);
+ $question_cat->sortorder = backup_todb($info['QUESTION_CATEGORY']['#']['SORTORDER']['0']['#']);
if (!$question_cat->stamp) {
$question_cat->stamp = make_unique_id_code();
}
- $newid = insert_record ("question_categories",$question_cat);
- }
+ if (isset($info['QUESTION_CATEGORY']['#']['PUBLISH'])) {
+ $course = $restore->course_id;
+ $publish = backup_todb($info['QUESTION_CATEGORY']['#']['PUBLISH']['0']['#']);
+ if ($publish){
+ $tocontext = get_context_instance(CONTEXT_SYSTEM);
+ } else {
+ $tocontext = get_context_instance(CONTEXT_COURSE, $course);
+ }
+ } else {
+ $tocontext = restore_question_get_best_category_context($restore, $info['QUESTION_CATEGORY']['#']['CONTEXT']['0']['#']);
+ }
+ $question_cat->contextid = $tocontext->id;
+
+ //does cat exist ?? if it does we check if the cat and questions already exist whether we have
+ //add permission or not if we have no permission to add questions to SYSTEM or COURSECAT context
+ //AND the question does not already exist then we create questions in COURSE context.
+ if (!$fcat = get_record('question_categories','contextid', $question_cat->contextid, 'stamp', $question_cat->stamp)){
+ //no preexisting cat
+ if ((($tocontext->contextlevel == CONTEXT_SYSTEM) || ($tocontext->contextlevel == CONTEXT_COURSECAT))
+ && !has_capability('moodle/question:add', $tocontext)){
+ //no preexisting cat and no permission to create questions here
+ //must restore to course.
+ $tocontext = get_context_instance(CONTEXT_COURSE, $restore->course_id);
+ }
+ $question_cat->contextid = $tocontext->id;
+ if (!$fcat = get_record('question_categories','contextid', $question_cat->contextid, 'stamp', $question_cat->stamp)){
+ $question_cat->id = insert_record ("question_categories", $question_cat);
+ } else {
+ $question_cat = $fcat;
+ }
+ //we'll be restoring all questions here.
+ backup_putid($restore->backup_unique_code, "question_categories", $category->id, $question_cat->id);
+ } else {
+ $question_cat = $fcat;
+ //we found an existing best category
+ //but later if context is above course need to check if there are questions need creating in category
+ //if we do need to create questions and permissions don't allow it create new category in course
+ }
- //Do some output
- if ($newid) {
+ //Do some output
if (!defined('RESTORE_SILENTLY')) {
echo "
".get_string('category', 'quiz')." \"".$question_cat->name."\" ";
}
- } else {
- //We must never arrive here !!
+
+ backup_flush(300);
+
+ //start with questions
+ if ($question_cat->id) {
+ //We have the newid, update backup_ids
+ //Now restore question
+ $status = restore_questions($category->id, $question_cat, $info, $restore);
+ } else {
+ $status = false;
+ }
if (!defined('RESTORE_SILENTLY')) {
- echo "
";print_r($possibledatasets);
while (ereg('\{=([^[:space:]}]*)}', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
@@ -176,9 +185,9 @@ class question_edit_calculated_form extends question_edit_form {
$errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
}else {
$errors['questiontext'] .= ' '.$formulaerrors.':'.$regs1[1];
- }
+ }
}
- }
+ }
$answers = $data['answers'];
$answercount = 0;
$maxgrade = false;
@@ -186,13 +195,13 @@ class question_edit_calculated_form extends question_edit_form {
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
- }
+ }
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){
$errors['answers['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
- }
- }
+ }
+ }
foreach ($answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
diff --git a/question/type/datasetdependent/abstractqtype.php b/question/type/datasetdependent/abstractqtype.php
index 8e2bdd9db2..54edd15fc0 100644
--- a/question/type/datasetdependent/abstractqtype.php
+++ b/question/type/datasetdependent/abstractqtype.php
@@ -197,16 +197,16 @@ class question_dataset_dependent_questiontype extends default_questiontype {
/**
* This method prepare the $datasets in a format similar to dadatesetdefinitions_form.php
- * so that they can be saved
+ * so that they can be saved
* using the function save_dataset_definitions($form)
- * when creating a new calculated question or
+ * when creating a new calculated question or
* whenediting an already existing calculated question
- * or by function save_as_new_dataset_definitions($form, $initialid)
+ * or by function save_as_new_dataset_definitions($form, $initialid)
* when saving as new an already existing calculated question
- *
+ *
* @param object $form
* @param int $questionfromid default = '0'
- */
+ */
function preparedatasets(&$form , $questionfromid='0'){
// the dataset names present in the edit_question_form and edit_calculated_form are retrieved
$possibledatasets = $this->find_dataset_names($form->questiontext);
@@ -215,7 +215,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
$mandatorydatasets += $this->find_dataset_names($answer);
}
// if there are identical datasetdefs already saved in the original question.
- // either when editing a question or saving as new
+ // either when editing a question or saving as new
// they are retrieved using $questionfromid
if ($questionfromid!='0'){
$form->id = $questionfromid ;
@@ -237,7 +237,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
// they will defined and stored with datasetdefinitions_form.php
// the $options are not used here
if ($questionfromid!='0'){
-
+
foreach ($possibledatasets as $datasetname) {
if (!isset($datasets[$datasetname])) {
list($options, $selected) =
@@ -253,15 +253,15 @@ class question_dataset_dependent_questiontype extends default_questiontype {
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
- * at the first step $wizardnow = 'question'
+ * at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
- * then the datasets are prepared and saved
- * at the second step $wizardnow = 'datasetdefinitions'
+ * then the datasets are prepared and saved
+ * at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
- * at the third step $wizardnow = 'datasetitems'
+ * at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
@@ -272,11 +272,11 @@ class question_dataset_dependent_questiontype extends default_questiontype {
function save_question($question, $form, $course) {
$wizardnow = optional_param('wizardnow', '', PARAM_ALPHA);
$id = optional_param('id', 0, PARAM_INT); // question id
- // in case 'question'
- // for a new question $form->id is empty
- // when saving as new question
- // $question->id = 0, $form is $data from question2.php
- // and $data->makecopy is defined as $data->id is the initial question id
+ // in case 'question'
+ // for a new question $form->id is empty
+ // when saving as new question
+ // $question->id = 0, $form is $data from question2.php
+ // and $data->makecopy is defined as $data->id is the initial question id
// edit case. If it is a new question we don't necessarily need to
// return a valid question object
@@ -290,23 +290,24 @@ class question_dataset_dependent_questiontype extends default_questiontype {
$this->preparedatasets($form);
$form->id = $question->id;
$this->save_dataset_definitions($form);
- } else if (!empty($form->makecopy)){
+ } else if (!empty($form->makecopy)){
$questionfromid = $form->id ;
$question = parent::save_question($question, $form, $course);
//prepare the datasets
- $this->preparedatasets($form,$questionfromid);
+ $this->preparedatasets($form,$questionfromid);
$form->id = $question->id;
$this->save_as_new_dataset_definitions($form,$questionfromid );
- } else {// editing a question
+ } else {// editing a question
$question = parent::save_question($question, $form, $course);
//prepare the datasets
$this->preparedatasets($form,$question->id);
- $form->id = $question->id;
+ $form->id = $question->id;
$this->save_dataset_definitions($form);
}
break;
case 'datasetdefinitions':
- $this->save_dataset_definitions($form);
+
+ $this->save_dataset_definitions($form);
break;
case 'datasetitems':
$this->save_dataset_items($question, $form);
@@ -369,7 +370,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
- // This dataset is not used any more, delete it
+ // This dataset is not used any more, delete it
delete_records('question_datasets',
'question', $form->id,
'datasetdefinition', $datasetdef->id);
@@ -441,12 +442,12 @@ class question_dataset_dependent_questiontype extends default_questiontype {
}
/** This function create a copy of the datasets ( definition and dataitems)
* from the preceding question if they remain in the new question
- * otherwise its create the datasets that have been added as in the
+ * otherwise its create the datasets that have been added as in the
* save_dataset_definitions()
*/
function save_as_new_dataset_definitions($form, $initialid) {
global $CFG ;
- // Get the datasets from the intial question
+ // Get the datasets from the intial question
$datasetdefinitions = $this->get_dataset_definitions($initialid, $form->dataset);
// $tmpdatasets contains those of the new question
$tmpdatasets = array_flip($form->dataset);
@@ -469,7 +470,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
'question_dataset_definitions', $datasetdef)) {
error("Unable to create dataset $defid");
}
- //copy the dataitems
+ //copy the dataitems
$olditems = get_records_sql( // Use number as key!!
" SELECT itemnumber, value
FROM {$CFG->prefix}question_dataset_items
@@ -481,15 +482,15 @@ class question_dataset_dependent_questiontype extends default_questiontype {
if (!insert_record('question_dataset_items', $item)) {
error("Unable to insert dataset item $item->itemnumber with $item->value for $datasetdef->name");
}
- $itemcount++;
+ $itemcount++;
}
//update item count
$datasetdef->itemcount =$itemcount;
- update_record('question_dataset_definitions', $datasetdef);
- } // end of copy the dataitems
+ update_record('question_dataset_definitions', $datasetdef);
+ } // end of copy the dataitems
}// end of copy the datasetdef
- // Create relation to the new question with this
- // copy as new datasetdef from the initial question
+ // Create relation to the new question with this
+ // copy as new datasetdef from the initial question
$questiondataset = new stdClass;
$questiondataset->question = $form->id;
$questiondataset->datasetdefinition = $datasetdef->id;
diff --git a/question/type/datasetdependent/datasetdefinitions_form.php b/question/type/datasetdependent/datasetdefinitions_form.php
index c2c7bc4f11..d6ae8c72a3 100644
--- a/question/type/datasetdependent/datasetdefinitions_form.php
+++ b/question/type/datasetdependent/datasetdefinitions_form.php
@@ -101,6 +101,15 @@ class question_dataset_dependent_definitions_form extends moodleform {
$mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_INT);
$mform->addElement('hidden', 'id');
+
+ $mform->addElement('hidden', 'courseid');
+ $mform->setType('courseid', PARAM_INT);
+ $mform->setDefault('courseid', 0);
+
+ $mform->addElement('hidden', 'cmid');
+ $mform->setType('cmid', PARAM_INT);
+ $mform->setDefault('cmid', 0);
+
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetitems');
$mform->setType('wizard', PARAM_ALPHA);
diff --git a/question/type/datasetdependent/datasetitems_form.php b/question/type/datasetdependent/datasetitems_form.php
index ce9853e9e5..c9defa801e 100644
--- a/question/type/datasetdependent/datasetitems_form.php
+++ b/question/type/datasetdependent/datasetitems_form.php
@@ -156,6 +156,15 @@ class question_dataset_dependent_items_form extends moodleform {
$mform->setType('qtype', PARAM_ALPHA);
$mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_INT);
+
+ $mform->addElement('hidden', 'courseid');
+ $mform->setType('courseid', PARAM_INT);
+ $mform->setDefault('courseid', 0);
+
+ $mform->addElement('hidden', 'cmid');
+ $mform->setType('cmid', PARAM_INT);
+ $mform->setDefault('cmid', 0);
+
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetitems');
diff --git a/question/type/description/questiontype.php b/question/type/description/questiontype.php
index 7244bf8698..1ba123698f 100644
--- a/question/type/description/questiontype.php
+++ b/question/type/description/questiontype.php
@@ -19,7 +19,7 @@ class description_qtype extends default_questiontype {
function name() {
return 'description';
}
-
+
function is_usable_by_random() {
return false;
}
@@ -46,7 +46,7 @@ class description_qtype extends default_questiontype {
// For editing teachers print a link to an editing popup window
$editlink = '';
- if (has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $cmoptions->course))) {
+ if (question_has_capability_on($question, 'edit')) {
$stredit = get_string('edit');
$linktext = '';
$editlink = link_to_popup_window('/question/question.php?id='.$question->id, $stredit, $linktext, 450, 550, $stredit, '', true);
diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php
index 3a08e8bb92..e105bb1679 100644
--- a/question/type/edit_question_form.php
+++ b/question/type/edit_question_form.php
@@ -14,7 +14,7 @@
* all question types need. Question types should define their own
* class that inherits from this one, and implements the definition_inner()
* method.
- *
+ *
* @package questionbank
* @subpackage questiontypes
*/
@@ -28,9 +28,25 @@ class question_edit_form extends moodleform {
*/
var $question;
- function question_edit_form($submiturl, $question){
+ var $contexts;
+ var $category;
+ var $categorycontext;
+ var $coursefilesid;
+
+ function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
+
$this->question = $question;
- parent::moodleform($submiturl);
+
+ $this->contexts = $contexts;
+
+ $this->category = $category;
+ $this->categorycontext = get_context_instance_by_id($category->contextid);
+
+ //course id or site id depending on question cat context
+ $this->coursefilesid = get_filesdir_from_context(get_context_instance_by_id($category->contextid));
+
+ parent::moodleform($submiturl, null, 'post', '', null, $formeditable);
+
}
/**
@@ -51,22 +67,53 @@ class question_edit_form extends moodleform {
// Standard fields at the start of the form.
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
- $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'), null,
- array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true));
+ if (!isset($this->question->id)){
+ //adding question
+ $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
+ array('contexts' => array($this->categorycontext)));
+ } elseif (!($this->question->formoptions->canmove || $this->question->formoptions->cansaveasnew)){
+ //editing question with no permission to move from category.
+ $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
+ array('contexts' => array($this->categorycontext)));
+ } elseif ($this->question->formoptions->movecontext){
+ //moving question to another context.
+ $mform->addElement('questioncategory', 'categorymoveto', get_string('category', 'quiz'),
+ array('contexts' => $this->contexts->having_cap('moodle/question:add')));
+
+ } else {
+ //editing question with permission to move from category or save as new q
+ $currentgrp = array();
+ $currentgrp[0] =& $mform->createElement('questioncategory', 'category', get_string('categorycurrent', 'question'),
+ array('contexts' => array($this->categorycontext)));
+ if ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew){
+ //not move only form
+ $currentgrp[1] =& $mform->createElement('checkbox', 'usecurrentcat', '', get_string('categorycurrentuse', 'question'));
+ $mform->setDefault('usecurrentcat', 1);
+ }
+ $currentgrp[0]->freeze();
+ $currentgrp[0]->setPersistantFreeze(false);
+ $mform->addGroup($currentgrp, 'currentgrp', get_string('categorycurrent', 'question'), null, false);
+
+ $mform->addElement('questioncategory', 'categorymoveto', get_string('categorymoveto', 'question'),
+ array('contexts' => array($this->categorycontext)));
+ if ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew){
+ //not move only form
+ $mform->disabledIf('categorymoveto', 'usecurrentcat', 'checked');
+ }
+ }
- $mform->addElement('text', 'name', get_string('questionname', 'quiz'),
- array('size' => 50));
+ $mform->addElement('text', 'name', get_string('questionname', 'quiz'), array('size' => 50));
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
$mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'),
- array('rows' => 15, 'course' => $COURSE->id));
+ array('rows' => 15, 'course' => $this->coursefilesid));
$mform->setType('questiontext', PARAM_RAW);
$mform->setHelpButton('questiontext', array(array('questiontext', get_string('questiontext', 'quiz'), 'quiz'), 'richtext'), false, 'editorhelpbutton');
$mform->addElement('format', 'questiontextformat', get_string('format'));
- make_upload_directory("$COURSE->id"); // Just in case
- $coursefiles = get_directory_list("$CFG->dataroot/$COURSE->id", $CFG->moddata);
+ make_upload_directory($this->coursefilesid); // Just in case
+ $coursefiles = get_directory_list("$CFG->dataroot/$this->coursefilesid", $CFG->moddata);
foreach ($coursefiles as $filename) {
if (mimeinfo("icon", $filename) == "image.gif") {
$images["$filename"] = $filename;
@@ -92,13 +139,33 @@ class question_edit_form extends moodleform {
$mform->setDefault('penalty', 0.1);
$mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
- array('rows' => 10, 'course' => $COURSE->id));
+ array('rows' => 10, 'course' => $this->coursefilesid));
$mform->setType('generalfeedback', PARAM_RAW);
$mform->setHelpButton('generalfeedback', array('generalfeedback', get_string('generalfeedback', 'quiz'), 'quiz'));
// Any questiontype specific fields.
$this->definition_inner($mform);
+
+ if (!empty($this->question->id)){
+ $mform->addElement('header', 'createdmodifiedheader', get_string('createdmodifiedheader', 'question'));
+ $a = new object();
+ if (!empty($this->question->createdby)){
+ $a->time = userdate($this->question->timecreated);
+ $a->user = fullname(get_record('user', 'id', $this->question->createdby));
+ } else {
+ $a->time = get_string('unknown', 'question');
+ $a->user = get_string('unknown', 'question');
+ }
+ $mform->addElement('static', 'created', get_string('created', 'question'), get_string('byandon', 'question', $a));
+ if (!empty($this->question->modifiedby)){
+ $a = new object();
+ $a->time = userdate($this->question->timemodified);
+ $a->user = fullname(get_record('user', 'id', $this->question->modifiedby));
+ $mform->addElement('static', 'modified', get_string('modified', 'question'), get_string('byandon', 'question', $a));
+ }
+ }
+
// Standard fields at the end of the form.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
@@ -112,22 +179,46 @@ class question_edit_form extends moodleform {
$mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL);
+ $mform->addElement('hidden', 'movecontext');
+ $mform->setType('movecontext', PARAM_BOOL);
+
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0);
+ $mform->addElement('hidden', 'courseid');
+ $mform->setType('courseid', PARAM_INT);
+ $mform->setDefault('courseid', 0);
+
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
- $mform->setDefault('returnurl', '');
+ $mform->setDefault('returnurl', 0);
$buttonarray = array();
- $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
- if (!empty($this->question->id)) {
- $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
+ if (!empty($this->question->id)){
+ //editing / moving question
+ if ($this->question->formoptions->movecontext){
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('moveq', 'question'));
+ } elseif ($this->question->formoptions->canedit || $this->question->formoptions->canmove ||$this->question->formoptions->movecontext){
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
+ }
+ if ($this->question->formoptions->cansaveasnew){
+ $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
+ }
+ $buttonarray[] = &$mform->createElement('cancel');
+ } else {
+ // adding new question
+ $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
+ $buttonarray[] = &$mform->createElement('cancel');
}
- $buttonarray[] = &$mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
+
+ if ($this->question->formoptions->movecontext){
+ $mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar'));
+ } elseif ((!empty($this->question->id)) && (!($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew))){
+ $mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar', 'currentgrp'));
+ }
}
/**
@@ -167,6 +258,7 @@ class question_edit_form extends moodleform {
function qtype() {
return '';
}
+
}
?>
\ No newline at end of file
diff --git a/question/type/essay/edit_essay_form.php b/question/type/essay/edit_essay_form.php
index 4b989c08bc..60972b11ef 100644
--- a/question/type/essay/edit_essay_form.php
+++ b/question/type/essay/edit_essay_form.php
@@ -19,7 +19,8 @@ class question_edit_essay_form extends question_edit_form {
* @param MoodleQuickForm $mform the form being built.
*/
function definition_inner(&$mform) {
- $mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"));
+ $mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"),
+ array('course' => $this->coursefilesid));
$mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0);
diff --git a/question/type/match/edit_match_form.php b/question/type/match/edit_match_form.php
index 97373b24b1..c05302993b 100644
--- a/question/type/match/edit_match_form.php
+++ b/question/type/match/edit_match_form.php
@@ -36,8 +36,12 @@ class question_edit_match_form extends question_edit_form {
} else {
$countsubquestions = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countsubquestions;
+ }
$mform->setType('subanswer', PARAM_TEXT);
$mform->setType('subquestion', PARAM_TEXT);
@@ -67,7 +71,7 @@ class question_edit_match_form extends question_edit_form {
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['subanswers'];
$questions = $data['subquestions'];
$questioncount = 0;
diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php
index 352c3c5fad..baa600b28e 100644
--- a/question/type/match/questiontype.php
+++ b/question/type/match/questiontype.php
@@ -616,7 +616,7 @@ class question_match_qtype extends default_questiontype {
/**
* Decode links in question type specific tables.
* @return bool success or failure.
- */
+ */
function decode_content_links_caller($questionids, $restore, &$i) {
$status = true;
@@ -646,6 +646,38 @@ class question_match_qtype extends default_questiontype {
return $status;
}
+
+ function find_file_links($question, $courseid){
+ // find links in the question_match_sub table.
+ $urls = array();
+ foreach ($question->options->subquestions as $subquestion) {
+ $urls += question_find_file_links_from_html($subquestion->questiontext, $courseid);
+ }
+
+ //set all the values of the array to the question object
+ if ($urls){
+ $urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
+ }
+ $urls = array_merge_recursive($urls, parent::find_file_links($question, $courseid));
+ return $urls;
+ }
+
+ function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
+ parent::replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination);
+ // replace links in the question_match_sub table.
+ if (isset($question->options->subquestions)){
+ foreach ($question->options->subquestions as $subquestion) {
+ $subquestionchanged = false;
+ $subquestion->questiontext = question_replace_file_links_in_html($subquestion->questiontext, $fromcourseid, $tocourseid, $url, $destination, $subquestionchanged);
+ if ($subquestionchanged){//need to update rec in db
+ if (!update_record('question_match_sub', addslashes_recursive($subquestion))) {
+ error('Couldn\'t update \'question_match_sub\' record '.$subquestion->id);
+ }
+
+ }
+ }
+ }
+ }
}
//// END OF CLASS ////
diff --git a/question/type/missingtype/edit_missingtype_form.php b/question/type/missingtype/edit_missingtype_form.php
index 6cef3ad894..396f0616e7 100644
--- a/question/type/missingtype/edit_missingtype_form.php
+++ b/question/type/missingtype/edit_missingtype_form.php
@@ -25,15 +25,20 @@ class question_edit_missingtype_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
- $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'));
+ $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
+ array('course' => $this->coursefilesid));
if (isset($this->question->options)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_NOTAGS);
@@ -62,7 +67,7 @@ class question_edit_missingtype_form extends question_edit_form {
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php
index 683b3df6c7..6c784480f4 100644
--- a/question/type/multianswer/edit_multianswer_form.php
+++ b/question/type/multianswer/edit_multianswer_form.php
@@ -76,7 +76,7 @@ class question_edit_multianswer_form extends question_edit_form {
function validation($data){
//TODO would be nice to parse the question text here and output some error
//messages if there is a problem with the text.
- $errors = array();
+ $errors = parent::validation($data);
//extra check to make sure there is something in the htmlarea besides a
$questiontext= trim(strip_tags($data['questiontext']));
if ($questiontext==''){
diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php
index 77bc15572b..51697a1875 100644
--- a/question/type/multianswer/questiontype.php
+++ b/question/type/multianswer/questiontype.php
@@ -671,6 +671,7 @@ function qtype_multianswer_extract_question($text) {
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) {
$wrapped->qtype = 'multichoice';
$wrapped->single = 1;
+ $wrapped->answernumbering = 0;
$wrapped->correctfeedback = '';
$wrapped->partiallycorrectfeedback = '';
$wrapped->incorrectfeedback = '';
diff --git a/question/type/multichoice/edit_multichoice_form.php b/question/type/multichoice/edit_multichoice_form.php
index 3879986a75..2e0fc3b1e5 100644
--- a/question/type/multichoice/edit_multichoice_form.php
+++ b/question/type/multichoice/edit_multichoice_form.php
@@ -20,7 +20,7 @@ class question_edit_multichoice_form extends question_edit_form {
*/
function definition_inner(&$mform) {
global $QTYPES;
-
+
$menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
$mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
$mform->setDefault('single', 1);
@@ -46,15 +46,20 @@ class question_edit_multichoice_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_multichoice', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
- $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'));
+ $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
+ array('course' => $this->coursefilesid));
if (isset($this->question->options)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_RAW);
@@ -62,13 +67,16 @@ class question_edit_multichoice_form extends question_edit_form {
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
- $mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_multichoice'));
+ $mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_multichoice'),
+ array('course' => $this->coursefilesid));
$mform->setType('correctfeedback', PARAM_RAW);
- $mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_multichoice'));
+ $mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_multichoice'),
+ array('course' => $this->coursefilesid));
$mform->setType('partiallycorrectfeedback', PARAM_RAW);
- $mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_multichoice'));
+ $mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_multichoice'),
+ array('course' => $this->coursefilesid));
$mform->setType('incorrectfeedback', PARAM_RAW);
}
@@ -101,7 +109,7 @@ class question_edit_multichoice_form extends question_edit_form {
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php
index 5dd0910d12..965921006d 100644
--- a/question/type/multichoice/questiontype.php
+++ b/question/type/multichoice/questiontype.php
@@ -395,7 +395,7 @@ class question_multichoice_qtype extends default_questiontype {
function response_summary($question, $state, $length = 80) {
return implode(',', $this->get_actual_response($question, $state));
}
-
+
/// BACKUP FUNCTIONS ////////////////////////////
/*
@@ -555,7 +555,7 @@ class question_multichoice_qtype extends default_questiontype {
/**
* Decode links in question type specific tables.
* @return bool success or failure.
- */
+ */
function decode_content_links_caller($questionids, $restore, &$i) {
$status = true;
@@ -601,7 +601,7 @@ class question_multichoice_qtype extends default_questiontype {
function get_numbering_styles() {
return array('abc', 'ABC', '123', 'none');
}
-
+
function number_html($qnum) {
return '' . $qnum . '. ';
}
@@ -625,6 +625,36 @@ class question_multichoice_qtype extends default_questiontype {
return 'ERR';
}
}
+
+ function find_file_links($question, $courseid){
+ $urls = array();
+ $urls = parent::find_file_links($question, $courseid);
+ // find links in the question_match_sub table.
+ foreach ($question->options->subquestions as $subquestion) {
+ $urls += question_find_file_links_from_html($subquestion->questiontext, $courseid);
+
+ }
+ //set all the values of the array to the question id
+ if ($urls){
+ $urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
+ }
+ $urls = array_merge_recursive($urls, parent::find_file_links($question, $courseid));
+ return $urls;
+ }
+
+ function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
+ parent::replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination);
+ // replace links in the question_match_sub table.
+ $optionschanged = false;
+ $question->options->correctfeedback = question_replace_file_links_in_html($question->options->correctfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
+ $question->options->partiallycorrectfeedback = question_replace_file_links_in_html($question->options->partiallycorrectfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
+ $question->options->incorrectfeedback = question_replace_file_links_in_html($question->options->incorrectfeedback, $fromcourseid, $tocourseid, $url, $destination, $optionschanged);
+ if ($optionschanged){
+ if (!update_record('question_multichoice', addslashes_recursive($question->options))) {
+ error('Couldn\'t update \'question_multichoice\' record '.$question->options->id);
+ }
+ }
+ }
}
// Register this question type with the question bank.
diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php
index 102369d862..3b256b0291 100644
--- a/question/type/numerical/edit_numerical_form.php
+++ b/question/type/numerical/edit_numerical_form.php
@@ -36,7 +36,8 @@ class question_edit_numerical_form extends question_edit_form {
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeatedoptions['fraction']['default'] = 0;
- $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'));
+ $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
+ array('course' => $this->coursefilesid));
$mform->setType('feedback', PARAM_RAW);
@@ -45,9 +46,12 @@ class question_edit_numerical_form extends question_edit_form {
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + 1))?
- QUESTION_NUMANS_START : ($countanswers + 1);
-
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + 1))?
+ QUESTION_NUMANS_START : ($countanswers + 1);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', 2, get_string('addmoreanswerblanks', 'qtype_numerical'));
//------------------------------------------------------------------------------------------
@@ -65,7 +69,12 @@ class question_edit_numerical_form extends question_edit_form {
} else {
$countunits = 0;
}
- $repeatsatstart = $countunits + 2;
+
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = $countunits + 2;
+ } else {
+ $repeatsatstart = $countunits;
+ }
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_numerical'));
$firstunit =& $mform->getElement('multiplier[0]');
@@ -99,7 +108,7 @@ class question_edit_numerical_form extends question_edit_form {
parent::set_data($question);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
// Check the answers.
$answercount = 0;
diff --git a/question/type/questiontype.php b/question/type/questiontype.php
index 9c6499f7c3..a7ad604cd3 100644
--- a/question/type/questiontype.php
+++ b/question/type/questiontype.php
@@ -9,7 +9,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
- *//** */
+ */
require_once($CFG->libdir . '/questionlib.php');
@@ -25,7 +25,7 @@ require_once($CFG->libdir . '/questionlib.php');
* experiences of the first few question type implementors, and improve the
* interface to meet their needs, rather the freeze the API prematurely and
* condem everyone to working round a clunky interface for ever afterwards.
- *
+ *
* @package questionbank
* @subpackage questiontypes
*/
@@ -75,7 +75,7 @@ class default_questiontype {
}
/**
- * @return whether the question_answers.answer field needs to have
+ * @return whether the question_answers.answer field needs to have
* restore_decode_content_links_worker called on it.
*/
function has_html_answers() {
@@ -114,7 +114,7 @@ class default_questiontype {
* @param string $submiturl passed on to the constructor call.
* @return object an instance of the form definition, or null if one could not be found.
*/
- function create_editing_form($submiturl, $question) {
+ function create_editing_form($submiturl, $question, $category, $contexts, $formeditable) {
global $CFG;
require_once("{$CFG->dirroot}/question/type/edit_question_form.php");
$definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php';
@@ -126,7 +126,7 @@ class default_questiontype {
if (!class_exists($classname)) {
return null;
}
- return new $classname($submiturl, $question);
+ return new $classname($submiturl, $question, $category, $contexts, $formeditable);
}
/**
@@ -154,25 +154,51 @@ class default_questiontype {
* @param string $wizardnow is '' for first page.
*/
function display_question_editing_page(&$mform, $question, $wizardnow){
- list($heading, $langmodule) = $this->get_heading();
+ list($heading, $langmodule) = $this->get_heading(empty($question->id));
print_heading_with_help($heading, $this->name(), $langmodule);
+ $permissionstrs = array();
+ if (!empty($question->id)){
+ if ($question->formoptions->canedit){
+ $permissionstrs[] = get_string('permissionedit', 'question');
+ }
+ if ($question->formoptions->canmove){
+ $permissionstrs[] = get_string('permissionmove', 'question');
+ }
+ if ($question->formoptions->cansaveasnew){
+ $permissionstrs[] = get_string('permissionsaveasnew', 'question');
+ }
+ }
+ if (!$question->formoptions->movecontext && count($permissionstrs)){
+ print_heading(get_string('permissionto', 'question'), 'center', 3);
+ $html = '
';
+ foreach ($permissionstrs as $permissionstr){
+ $html .= '
'.$permissionstr.'
';
+ }
+ $html .= '
';
+ print_box($html, 'boxwidthnarrow boxaligncenter generalbox');
+ }
$mform->display();
}
-
+
/**
* Method called by display_question_editing_page and by question.php to get heading for breadcrumbs.
- *
+ *
* @return array a string heading and the langmodule in which it was found.
*/
- function get_heading(){
+ function get_heading($adding = false){
$name = $this->name();
$langmodule = 'qtype_' . $name;
- $strheading = get_string('editing' . $name, $langmodule);
+ if (!$adding){
+ $strtoget = 'editing' . $name;
+ } else {
+ $strtoget = 'adding' . $name;
+ }
+ $strheading = get_string($strtoget, $langmodule);
if ($strheading[0] == '[') {
// Legacy behavior, if the string was not in the proper qtype_name
// language file, look it up in the quiz one.
$langmodule = 'quiz';
- $strheading = get_string('editing' . $name, $langmodule);
+ $strheading = get_string($strtoget, $langmodule);
}
return array($strheading, $langmodule);
}
@@ -204,14 +230,11 @@ class default_questiontype {
* 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
- if (!record_exists('question_categories', 'id', $form->category)) {
- print_error('categorydoesnotexist', 'question');
- }
- $question->category = $form->category;
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
@@ -247,16 +270,28 @@ class default_questiontype {
}
if (!empty($question->id)) { // Question already exists
+ if (isset($form->categorymoveto)){
+ question_require_capability_on($question, 'move');
+ list($question->categorymoveto, $movetocontextid) = explode(',', $form->categorymoveto);
+ }
+ if (isset($question->qtype) && $question->qtype != RANDOM){
+ $question->category = $question->categorymoveto;
+ }
// keep existing unique stamp code
$question->stamp = get_field('question', 'stamp', 'id', $question->id);
- if (!update_record("question", $question)) {
- error("Could not update question!");
+ $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,$contextid) = explode(',', $form->category);
$question->stamp = make_unique_id_code();
- if (!$question->id = insert_record("question", $question)) {
- error("Could not insert new question!");
+ $question->createdby = $USER->id;
+ $question->timecreated = time();
+ if (!$question->id = insert_record('question', $question)) {
+ error('Could not insert new question!');
}
}
@@ -305,7 +340,7 @@ class default_questiontype {
if (is_array($extra_question_fields)) {
$question_extension_table = array_shift($extra_question_fields);
-
+
$function = 'update_record';
$options = get_record($question_extension_table, 'questionid', $question->id);
if (!$options) {
@@ -322,7 +357,7 @@ class default_questiontype {
}
$options->$field = $question->$field;
}
-
+
if (!$function($question_extension_table, $options)) {
$result = new stdClass;
$result->error = 'Could not save question options for ' .
@@ -333,7 +368,7 @@ class default_questiontype {
$extra_answer_fields = $this->extra_answer_fields();
// TODO save the answers, with any extra data.
-
+
return null;
}
@@ -687,13 +722,13 @@ class default_questiontype {
* then this method will return an array of tags that reference
* those stylesheets. This function will also call require_js()
* from ajaxlib.php, to get any necessary JavaScript linked in too.
- *
+ *
* The two parameters match the first two parameters of print_question.
- *
+ *
* @param object $question The question object.
* @param object $state The state object.
- *
- * @return an array of bits of HTML to add to the head of pages where
+ *
+ * @return an array of bits of HTML to add to the head of pages where
* this question is print_question-ed in the body. The array should use
* integer array keys, which have no significance.
*/
@@ -703,13 +738,14 @@ class default_questiontype {
// Core question types should not use this mechanism. Their styles
// should be included in the standard theme.
- // We only do this once
+
+ // We only do this once
// for this question type, no matter how often this method is called.
if ($this->already_done) {
return array();
}
$this->already_done = true;
-
+
$plugindir = $this->plugin_dir();
$baseurl = $this->plugin_baseurl();
$stylesheets = array();
@@ -732,7 +768,7 @@ class default_questiontype {
}
return $contributions;
}
-
+
/**
* Prints the question including the number, grading details, content,
* feedback and interactions
@@ -782,7 +818,7 @@ class default_questiontype {
// For editing teachers print a link to an editing popup window
$editlink = '';
- if ($context && has_capability('moodle/question:manage', $context)) {
+ if (question_has_capability_on($question, 'edit')) {
$stredit = get_string('edit');
$linktext = '';
$editlink = link_to_popup_window('/question/question.php?inpopup=1&id='.$question->id, 'editquestion', $linktext, 450, 550, $stredit, '', true);
@@ -1040,7 +1076,7 @@ class default_questiontype {
if (($cmoptions->optionflags & QUESTION_ADAPTIVE) and !$options->readonly) {
echo 'id, "'; return true;", '" />';
}
}
@@ -1345,6 +1381,111 @@ class default_questiontype {
return format_text($text, $textformat, $formatoptions, $cmoptions === NULL ? NULL : $cmoptions->course);
}
+ /*
+ * Find all course / site files linked from a question.
+ *
+ * Need to check for links to files in question_answers.answer and feedback
+ * and in question table in generalfeedback and questiontext fields. Methods
+ * on child classes will also check extra question specific fields.
+ *
+ * Needs to be overriden for child classes that have extra fields containing
+ * html.
+ *
+ * @param string html the html to search
+ * @param int courseid search for files for courseid course or set to siteid for
+ * finding site files.
+ * @return array of url, relative url is key and array with one item = question id as value
+ * relative url is relative to course/site files directory root.
+ */
+ function find_file_links($question, $courseid){
+ $urls = array();
+ if ($question->image != ''){
+ if (substr(strtolower($question->image), 0, 7) == 'http://') {
+ $matches = array();
+
+ //support for older questions where we have a complete url in image field
+ if (preg_match('!^'.question_file_links_base_url($courseid).'(.*)!i', $question->image, $matches)){
+ if ($cleanedurl = question_url_check($urls[$matches[2]])){
+ $urls[$cleanedurl] = null;
+ }
+ }
+ } else {
+ if ($question->image != ''){
+ if ($cleanedurl = question_url_check($question->image)){
+ $urls[$cleanedurl] = null;//will be set later
+ }
+ }
+
+ }
+
+ }
+ $urls += question_find_file_links_from_html($question->questiontext, $courseid);
+ $urls += question_find_file_links_from_html($question->generalfeedback, $courseid);
+ if ($this->has_html_answers() && isset($question->options->answers)){
+ foreach ($question->options->answers as $answerkey => $answer){
+ $thisurls= question_find_file_links_from_html($answer->answer, $courseid);
+ if ($thisurls){
+ $urls += $thisurls;
+ }
+ }
+ }
+ //set all the values of the array to the question object
+ if ($urls){
+ $urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id)));
+ }
+ return $urls;
+ }
+ /*
+ * Find all course / site files linked from a question.
+ *
+ * Need to check for links to files in question_answers.answer and feedback
+ * and in question table in generalfeedback and questiontext fields. Methods
+ * on child classes will also check extra question specific fields.
+ *
+ * Needs to be overriden for child classes that have extra fields containing
+ * html.
+ *
+ * @param string html the html to search
+ * @param int course search for files for courseid course or set to siteid for
+ * finding site files.
+ * @return array of files, file name is key and array with one item = question id as value
+ */
+ function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
+ global $CFG;
+ $updateqrec = false;
+ if (!empty($question->image)){
+ //support for older questions where we have a complete url in image field
+ if (substr(strtolower($question->image), 0, 7) == 'http://') {
+ $questionimage = preg_replace('!^'.question_file_links_base_url($fromcourseid).preg_quote($url, '!').'$!i', $destination, $question->image, 1);
+ } else {
+ $questionimage = preg_replace('!^'.preg_quote($url, '!').'$!i', $destination, $question->image, 1);
+ }
+ if ($questionimage != $question->image){
+ $question->image = $questionimage;
+ $updateqrec = true;
+ }
+ }
+ $question->questiontext = question_replace_file_links_in_html($question->questiontext, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
+ $question->generalfeedback = question_replace_file_links_in_html($question->generalfeedback, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
+ if ($updateqrec){
+ if (!update_record('question', addslashes_recursive($question))){
+ error ('Couldn\'t update question '.$question->name);
+ }
+ }
+
+ if ($this->has_html_answers() && isset($question->options->answers)){
+ //answers that do not need updating have been unset
+ foreach ($question->options->answers as $answer){
+ $answerchanged = false;
+ $answer->answer = question_replace_file_links_in_html($answer->answer, $fromcourseid, $tocourseid, $url, $destination, $answerchanged);
+ if ($answerchanged){
+ if (!update_record('question_answers', addslashes_recursive($answer))){
+ error ('Couldn\'t update question ('.$question->name.') answer '.$answer->id);
+ }
+ }
+ }
+ }
+ }
/**
* @return the best link to pass to print_error.
* @param $cmoptions as passed in from outside.
diff --git a/question/type/random/edit_random_form.php b/question/type/random/edit_random_form.php
index 0da3436d25..2a078a52a5 100644
--- a/question/type/random/edit_random_form.php
+++ b/question/type/random/edit_random_form.php
@@ -31,8 +31,8 @@ class question_edit_random_form extends question_edit_form {
// Standard fields at the start of the form.
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
- $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
- array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true));
+ $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
+ array('contexts' => $this->contexts->having_cap('moodle/question:useall')));
$mform->addElement('text', 'name', get_string('questionname', 'quiz'),
array('size' => 50));
@@ -57,6 +57,18 @@ class question_edit_random_form extends question_edit_form {
$mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL);
+ $mform->addElement('hidden', 'cmid');
+ $mform->setType('cmid', PARAM_INT);
+ $mform->setDefault('cmid', 0);
+
+ $mform->addElement('hidden', 'courseid');
+ $mform->setType('courseid', PARAM_INT);
+ $mform->setDefault('courseid', 0);
+
+ $mform->addElement('hidden', 'returnurl');
+ $mform->setType('returnurl', PARAM_LOCALURL);
+ $mform->setDefault('returnurl', 0);
+
$buttonarray = array();
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
diff --git a/question/type/randomsamatch/edit_randomsamatch_form.php b/question/type/randomsamatch/edit_randomsamatch_form.php
index ce22a4d68b..6786842384 100644
--- a/question/type/randomsamatch/edit_randomsamatch_form.php
+++ b/question/type/randomsamatch/edit_randomsamatch_form.php
@@ -49,7 +49,7 @@ class question_edit_randomsamatch_form extends question_edit_form {
function validation($data){
global $QTYPES;
- $errors = array();
+ $errors = parent::validation($data);
$saquestions = $QTYPES['randomsamatch']->get_sa_candidates($data['category']);
$numberavailable = count($saquestions);
if ($saquestions === false){
diff --git a/question/type/shortanswer/edit_shortanswer_form.php b/question/type/shortanswer/edit_shortanswer_form.php
index 6fdb563e1f..89a8d181b9 100644
--- a/question/type/shortanswer/edit_shortanswer_form.php
+++ b/question/type/shortanswer/edit_shortanswer_form.php
@@ -31,15 +31,20 @@ class question_edit_shortanswer_form extends question_edit_form {
$repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerno', 'qtype_shortanswer', '{no}'));
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 54));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
- $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'));
+ $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
+ array('course' => $this->coursefilesid));
if (isset($this->question->options)){
$countanswers = count($this->question->options->answers);
} else {
$countanswers = 0;
}
- $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
- QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ if ($this->question->formoptions->repeatelements){
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countanswers + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
$repeatedoptions = array();
$mform->setType('answer', PARAM_RAW);
$repeatedoptions['fraction']['default'] = 0;
@@ -65,7 +70,7 @@ class question_edit_shortanswer_form extends question_edit_form {
parent::set_data($question);
}
function validation($data){
- $errors = array();
+ $errors = parent::validation($data);
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
diff --git a/question/type/truefalse/edit_truefalse_form.php b/question/type/truefalse/edit_truefalse_form.php
index 1e13002268..0567e9d191 100644
--- a/question/type/truefalse/edit_truefalse_form.php
+++ b/question/type/truefalse/edit_truefalse_form.php
@@ -23,10 +23,12 @@ class question_edit_truefalse_form extends question_edit_form {
$mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'),
array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse')));
- $mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'));
+ $mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'),
+ array('course' => $this->coursefilesid));;
$mform->setType('feedbacktrue', PARAM_RAW);
- $mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'));
+ $mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'),
+ array('course' => $this->coursefilesid));
$mform->setType('feedbackfalse', PARAM_RAW);
// Fix penalty factor at 1.
diff --git a/question/upgrade.php b/question/upgrade.php
index e9d1cd2b28..efcc2335dd 100644
--- a/question/upgrade.php
+++ b/question/upgrade.php
@@ -8,18 +8,18 @@
* @author T.J.Hunt@open.ac.uk
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package package_name
- *//** */
+ */
/**
* This test is becuase the RQP question type was included in core
* up to and including Moodle 1.8, and was removed before Moodle 1.9.
- *
+ *
* Therefore, we want to check whether any rqp questions exist in the database
- * before doing the upgrade. However, the check is not relevant if that
- * question type was never installed, or if the person has chosen to
+ * before doing the upgrade. However, the check is not relevant if that
+ * question type was never installed, or if the person has chosen to
* manually reinstall the rqp question type from contrib.
- *
- * @param $version the version to test.
+ *
+ * @param $result the result object that can be modified.
* @return null if the test is irrelevant, or true or false depending on whether the test passes.
*/
function question_check_no_rqp_questions($result) {
@@ -37,24 +37,24 @@ function question_remove_rqp_qtype() {
global $CFG;
$result = true;
-
+
// Only remove the question type if the code is gone.
if (!is_dir($CFG->dirroot . '/question/type/rqp')) {
$table = new XMLDBTable('question_rqp_states');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp_types');
$result = $result && drop_table($table);
-
+
$table = new XMLDBTable('question_rqp_servers');
$result = $result && drop_table($table);
-
+
$result = $result && unset_config('qtype_rqp_version');
}
-
+
return $result;
}
@@ -67,7 +67,257 @@ function question_remove_rqp_qtype_config_string() {
if (!empty($CFG->qtype_rqp_version) && !is_dir($CFG->dirroot . '/question/type/rqp')) {
$result = $result && unset_config('qtype_rqp_version');
}
-
+
+ return $result;
+}
+
+/**
+ * @param $result the result object that can be modified.
+ * @return null if the test is irrelevant, or true or false depending on whether the test passes.
+ */
+function question_random_check($result){
+ global $CFG;
+ if ($CFG->version >= 2007081000){
+ return null;//no test after upgrade seperates question cats into contexts.
+ }
+ if (!$toupdate = question_cwqpfs_to_update()){
+ $result->setStatus(true);//pass test
+ } else {
+ //set the feedback string here and not in xml file since we need something
+ //more complex than just a string picked from admin.php lang file
+ $a = new object();
+ $a->reporturl = "{$CFG->wwwroot}/{$CFG->admin}/report/question/";
+ $lang = str_replace('_utf8', '', current_language());
+ $a->docsurl = "{$CFG->docroot}/$lang/admin/report/question/index";
+ $result->feedback_str = get_string('questioncwqpfscheck', 'admin', $a);
+ $result->setStatus(false);//fail test
+ }
+ return $result;
+}
+/*
+ * Delete all 'random' questions that are not been used in a quiz.
+ */
+function question_delete_unused_random(){
+ global $CFG;
+ $tofix = array();
+ $result = true;
+ //delete all 'random' questions that are not been used in a quiz.
+ if ($qqis = get_records_sql("SELECT q.* FROM {$CFG->prefix}question as q LEFT JOIN ".
+ "({$CFG->prefix}quiz_question_instances as qqi) ".
+ "ON (q.id = qqi.question) WHERE q.qtype='random' AND qqi.question IS NULL")){
+ $qqilist = join(array_keys($qqis), ',');
+ $result = $result && delete_records_select('question', "id IN ($qqilist)");
+ }
+ return $result;
+}
+function question_cwqpfs_to_update($categories = null){
+ global $CFG;
+
+ $tofix = array();
+ $result = true;
+
+ //any cats with questions picking from subcats?
+ if (!$cwqpfs = get_records_sql_menu("SELECT DISTINCT qc.id, 1 ".
+ "FROM {$CFG->prefix}question as q, {$CFG->prefix}question_categories as qc ".
+ "WHERE q.qtype='random' AND qc.id = q.category AND q.questiontext = 1")){
+ return array();
+ } else {
+ if ($categories === null){
+ $categories = get_records('question_categories');
+ }
+ $categorychildparents = array();
+ foreach ($categories as $id => $category){
+ $categorychildparents[$category->course][$id] = $category->parent;
+ }
+ foreach ($categories as $id => $category){
+ if (FALSE !== array_key_exists($category->parent, $categorychildparents[$category->course])){
+ //this is not a top level cat
+ continue;//go to next category
+ } else{
+ $tofix += question_cwqpfs_check_children($id, $categories, $categorychildparents[$category->course], $cwqpfs);
+ }
+ }
+ }
+
+ return $tofix;
+}
+
+function question_cwqpfs_check_children($checkid, $categories, $categorychildparents, $cwqpfs){
+ $tofix = array();
+ if (array_key_exists($checkid, $cwqpfs)){//cwqpfs in this cat
+ $getchildren = array();
+ $getchildren[] = $checkid;
+ //search down tree and find all children
+ while ($nextid = array_shift($getchildren)){//repeat until $getchildren
+ //empty;
+ $childids = array_keys($categorychildparents, $nextid);
+ foreach ($childids as $childid){
+ if ($categories[$childid]->publish != $categories[$checkid]->publish){
+ $tofix[$childid] = $categories[$checkid]->publish;
+ }
+ }
+ $getchildren = array_merge($getchildren, $childids);
+ }
+ } else { // check children for cwqpfs
+ $childrentocheck = array_keys($categorychildparents, $checkid);
+ foreach ($childrentocheck as $childtocheck){
+ $tofix += question_cwqpfs_check_children($childtocheck, $categories, $categorychildparents, $cwqpfs);
+ }
+ }
+ return $tofix;
+}
+
+function question_category_next_parent_in($contextid, $question_categories, $id){
+ $nextparent = $question_categories[$id]->parent;
+ if ($nextparent == 0){
+ return 0;
+ } elseif (!array_key_exists($nextparent, $question_categories)){
+ //finished searching up the category hierarchy. For some reason
+ //the top level items is not 0. We'll return 0 though.
+ return 0;
+ } elseif ($contextid == $question_categories[$nextparent]->contextid){
+ return $nextparent;
+ } else {
+ //parent is not in the same context look further up.
+ return question_category_next_parent_in($contextid, $question_categories, $nextparent);
+ }
+}
+
+
+/**
+ * Check that either category parent is 0 or a category shared in the same context.
+ * Fix any categories to point to grand or grand grand parent etc in the same context or 0.
+ */
+function question_category_checking($question_categories){
+ //make an array that is easier to search
+ $newparents = array();
+ foreach ($question_categories as $id => $category){
+ $newparents[$id] = question_category_next_parent_in($category->contextid, $question_categories, $id);
+ }
+ foreach (array_Keys($question_categories) as $id){
+ $question_categories[$id]->parent = $newparents[$id];
+ }
+ return $question_categories;
+}
+
+function question_upgrade_context_etc(){
+ global $CFG;
+ $result = true;
+ $result = $result && question_delete_unused_random();
+
+ $question_categories = get_records('question_categories');
+
+ $tofix = question_cwqpfs_to_update($question_categories);
+ foreach ($tofix as $catid => $publish){
+ $question_categories[$catid]->publish = $publish;
+ }
+
+ foreach ($question_categories as $id => $question_category){
+ $course = $question_categories[$id]->course;
+ unset($question_categories[$id]->course);
+ if ($question_categories[$id]->publish){
+ $context = get_context_instance(CONTEXT_SYSTEM);
+ } else {
+ $context = get_context_instance(CONTEXT_COURSE, $course);
+ }
+ $question_categories[$id]->contextid = $context->id;
+ unset($question_categories[$id]->publish);
+ }
+
+ $question_categories = question_category_checking($question_categories);
+
+/// Define index course (not unique) to be dropped form question_categories
+ $table = new XMLDBTable('question_categories');
+ $index = new XMLDBIndex('course');
+ $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course'));
+
+/// Launch drop index course
+ $result = $result && drop_index($table, $index);
+
+/// Define field course to be dropped from question_categories
+ $field = new XMLDBField('course');
+
+/// Launch drop field course
+ $result = $result && drop_field($table, $field);
+
+/// Define field context to be added to question_categories
+ $field = new XMLDBField('contextid');
+ $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'name');
+ $field->comment = 'context that this category is shared in';
+
+/// Launch add field context
+ $result = $result && add_field($table, $field);
+
+/// Define index context (not unique) to be added to question_categories
+ $index = new XMLDBIndex('contextid');
+ $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('contextid'));
+ $index->comment = 'links to context table';
+
+/// Launch add index context
+ $result = $result && add_index($table, $index);
+
+ $field = new XMLDBField('publish');
+
+/// Launch drop field publish
+ $result = $result && drop_field($table, $field);
+
+
+ /// update table contents with previously calculated new contents.
+
+ foreach ($question_categories as $question_category){
+ if (!$result = update_record('question_categories', $question_category)){
+ notify('Couldn\'t update question_categories "'. $question_category->name .'"!');
+ }
+ }
+
+/// Define field timecreated to be added to question
+ $table = new XMLDBTable('question');
+ $field = new XMLDBField('timecreated');
+ $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'hidden');
+
+/// Launch add field timecreated
+ $result = $result && add_field($table, $field);
+
+/// Define field timemodified to be added to question
+ $table = new XMLDBTable('question');
+ $field = new XMLDBField('timemodified');
+ $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'timecreated');
+
+/// Launch add field timemodified
+ $result = $result && add_field($table, $field);
+
+/// Define field createdby to be added to question
+ $table = new XMLDBTable('question');
+ $field = new XMLDBField('createdby');
+ $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'timemodified');
+
+/// Launch add field createdby
+ $result = $result && add_field($table, $field);
+
+/// Define field modifiedby to be added to question
+ $table = new XMLDBTable('question');
+ $field = new XMLDBField('modifiedby');
+ $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'createdby');
+
+/// Launch add field modifiedby
+ $result = $result && add_field($table, $field);
+
+/// Define key createdby (foreign) to be added to question
+ $table = new XMLDBTable('question');
+ $key = new XMLDBKey('createdby');
+ $key->setAttributes(XMLDB_KEY_FOREIGN, array('createdby'), 'user', array('id'));
+
+/// Launch add key createdby
+ $result = $result && add_key($table, $key);
+
+/// Define key modifiedby (foreign) to be added to question
+ $table = new XMLDBTable('question');
+ $key = new XMLDBKey('modifiedby');
+ $key->setAttributes(XMLDB_KEY_FOREIGN, array('modifiedby'), 'user', array('id'));
+
+/// Launch add key modifiedby
+ $result = $result && add_key($table, $key);
+
return $result;
}
?>
diff --git a/version.php b/version.php
index 2a0609028f..577c94a3be 100644
--- a/version.php
+++ b/version.php
@@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
- $version = 2007080903; // YYYYMMDD = date
+ $version = 2007081000; // YYYYMMDD = date
// XY = increments within a single day
$release = '1.9 dev'; // Human-friendly version name
--
2.39.5